diff --git a/examples/README.md b/examples/README.md
index e04570d09..b8aeea660 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -6,7 +6,7 @@ This directory contains comprehensive examples demonstrating TanStack AI across
Choose an example based on your use case:
-- **Want a full-stack TypeScript app?** → [TanStack Chat (ts-chat)](#tanstack-chat-ts-chat)
+- **Want a full-stack TypeScript app?** → [TanStack Chat (ts-react-chat)](#tanstack-chat-ts-react-chat)
- **Want a simple CLI tool?** → [CLI Example](#cli-example)
- **Need a vanilla JS frontend?** → [Vanilla Chat](#vanilla-chat)
- **Building a Python backend?** → [Python FastAPI Server](#python-fastapi-server)
@@ -14,7 +14,7 @@ Choose an example based on your use case:
## TypeScript Examples
-### TanStack Chat (ts-chat)
+### TanStack Chat (ts-react-chat)
A full-featured chat application built with the TanStack ecosystem.
@@ -38,14 +38,14 @@ A full-featured chat application built with the TanStack ecosystem.
**Getting Started:**
```bash
-cd examples/ts-chat
+cd examples/ts-react-chat
pnpm install
cp env.example .env
# Edit .env and add your OPENAI_API_KEY
pnpm start
```
-📖 [Full Documentation](ts-chat/README.md)
+📖 [Full Documentation](ts-react-chat/README.md)
---
@@ -245,7 +245,7 @@ Backend (TanStack Start API Route)
AI Provider (OpenAI/Anthropic/etc.)
```
-**Example:** [TanStack Chat (ts-chat)](ts-chat/README.md)
+**Example:** [TanStack Chat (ts-react-chat)](ts-react-chat/README.md)
### Multi-Language Backend
@@ -393,8 +393,8 @@ python anthropic-server.py
cd examples/vanilla-chat
pnpm dev
-# Terminal 3: Start ts-chat (full-stack)
-cd examples/ts-chat
+# Terminal 3: Start ts-react-chat (full-stack)
+cd examples/ts-react-chat
pnpm start
```
diff --git a/examples/ts-chat/.cta.json b/examples/ts-react-chat/.cta.json
similarity index 87%
rename from examples/ts-chat/.cta.json
rename to examples/ts-react-chat/.cta.json
index 8142313ea..db7fae646 100644
--- a/examples/ts-chat/.cta.json
+++ b/examples/ts-react-chat/.cta.json
@@ -1,5 +1,5 @@
{
- "projectName": "ts-chat",
+ "projectName": "ts-react-chat",
"mode": "file-router",
"typescript": true,
"tailwind": true,
diff --git a/examples/ts-chat/.gitignore b/examples/ts-react-chat/.gitignore
similarity index 100%
rename from examples/ts-chat/.gitignore
rename to examples/ts-react-chat/.gitignore
diff --git a/examples/ts-chat/README.md b/examples/ts-react-chat/README.md
similarity index 100%
rename from examples/ts-chat/README.md
rename to examples/ts-react-chat/README.md
diff --git a/examples/ts-chat/api-verification.ts b/examples/ts-react-chat/api-verification.ts
similarity index 100%
rename from examples/ts-chat/api-verification.ts
rename to examples/ts-react-chat/api-verification.ts
diff --git a/examples/ts-chat/env.example b/examples/ts-react-chat/env.example
similarity index 100%
rename from examples/ts-chat/env.example
rename to examples/ts-react-chat/env.example
diff --git a/examples/ts-chat/package.json b/examples/ts-react-chat/package.json
similarity index 93%
rename from examples/ts-chat/package.json
rename to examples/ts-react-chat/package.json
index b76671bf3..3c4ebfb46 100644
--- a/examples/ts-chat/package.json
+++ b/examples/ts-react-chat/package.json
@@ -1,5 +1,5 @@
{
- "name": "ts-chat",
+ "name": "ts-react-chat",
"private": true,
"type": "module",
"scripts": {
@@ -9,9 +9,6 @@
"test": "exit 0"
},
"dependencies": {
- "@ai-sdk/openai": "^2.0.73",
- "@ai-sdk/provider": "^2.0.0",
- "@ai-sdk/provider-utils": "^3.0.17",
"@tailwindcss/vite": "^4.1.17",
"@tanstack/ai": "workspace:*",
"@tanstack/ai-anthropic": "workspace:*",
diff --git a/examples/ts-chat/public/example-guitar-flowers.jpg b/examples/ts-react-chat/public/example-guitar-flowers.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-flowers.jpg
rename to examples/ts-react-chat/public/example-guitar-flowers.jpg
diff --git a/examples/ts-chat/public/example-guitar-motherboard.jpg b/examples/ts-react-chat/public/example-guitar-motherboard.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-motherboard.jpg
rename to examples/ts-react-chat/public/example-guitar-motherboard.jpg
diff --git a/examples/ts-chat/public/example-guitar-racing.jpg b/examples/ts-react-chat/public/example-guitar-racing.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-racing.jpg
rename to examples/ts-react-chat/public/example-guitar-racing.jpg
diff --git a/examples/ts-chat/public/example-guitar-steamer-trunk.jpg b/examples/ts-react-chat/public/example-guitar-steamer-trunk.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-steamer-trunk.jpg
rename to examples/ts-react-chat/public/example-guitar-steamer-trunk.jpg
diff --git a/examples/ts-chat/public/example-guitar-superhero.jpg b/examples/ts-react-chat/public/example-guitar-superhero.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-superhero.jpg
rename to examples/ts-react-chat/public/example-guitar-superhero.jpg
diff --git a/examples/ts-chat/public/example-guitar-traveling.jpg b/examples/ts-react-chat/public/example-guitar-traveling.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-traveling.jpg
rename to examples/ts-react-chat/public/example-guitar-traveling.jpg
diff --git a/examples/ts-chat/public/example-guitar-video-games.jpg b/examples/ts-react-chat/public/example-guitar-video-games.jpg
similarity index 100%
rename from examples/ts-chat/public/example-guitar-video-games.jpg
rename to examples/ts-react-chat/public/example-guitar-video-games.jpg
diff --git a/examples/ts-chat/public/favicon.ico b/examples/ts-react-chat/public/favicon.ico
similarity index 100%
rename from examples/ts-chat/public/favicon.ico
rename to examples/ts-react-chat/public/favicon.ico
diff --git a/examples/ts-chat/public/logo192.png b/examples/ts-react-chat/public/logo192.png
similarity index 100%
rename from examples/ts-chat/public/logo192.png
rename to examples/ts-react-chat/public/logo192.png
diff --git a/examples/ts-chat/public/logo512.png b/examples/ts-react-chat/public/logo512.png
similarity index 100%
rename from examples/ts-chat/public/logo512.png
rename to examples/ts-react-chat/public/logo512.png
diff --git a/examples/ts-chat/public/manifest.json b/examples/ts-react-chat/public/manifest.json
similarity index 100%
rename from examples/ts-chat/public/manifest.json
rename to examples/ts-react-chat/public/manifest.json
diff --git a/examples/ts-chat/public/robots.txt b/examples/ts-react-chat/public/robots.txt
similarity index 100%
rename from examples/ts-chat/public/robots.txt
rename to examples/ts-react-chat/public/robots.txt
diff --git a/examples/ts-chat/public/tanstack-circle-logo.png b/examples/ts-react-chat/public/tanstack-circle-logo.png
similarity index 100%
rename from examples/ts-chat/public/tanstack-circle-logo.png
rename to examples/ts-react-chat/public/tanstack-circle-logo.png
diff --git a/examples/ts-chat/public/tanstack-word-logo-white.svg b/examples/ts-react-chat/public/tanstack-word-logo-white.svg
similarity index 100%
rename from examples/ts-chat/public/tanstack-word-logo-white.svg
rename to examples/ts-react-chat/public/tanstack-word-logo-white.svg
diff --git a/examples/ts-chat/src/components/Approval.tsx b/examples/ts-react-chat/src/components/Approval.tsx
similarity index 100%
rename from examples/ts-chat/src/components/Approval.tsx
rename to examples/ts-react-chat/src/components/Approval.tsx
diff --git a/examples/ts-chat/src/components/Header.tsx b/examples/ts-react-chat/src/components/Header.tsx
similarity index 100%
rename from examples/ts-chat/src/components/Header.tsx
rename to examples/ts-react-chat/src/components/Header.tsx
diff --git a/examples/ts-chat/src/components/example-GuitarRecommendation.tsx b/examples/ts-react-chat/src/components/example-GuitarRecommendation.tsx
similarity index 100%
rename from examples/ts-chat/src/components/example-GuitarRecommendation.tsx
rename to examples/ts-react-chat/src/components/example-GuitarRecommendation.tsx
diff --git a/examples/ts-chat/src/data/example-guitars.ts b/examples/ts-react-chat/src/data/example-guitars.ts
similarity index 100%
rename from examples/ts-chat/src/data/example-guitars.ts
rename to examples/ts-react-chat/src/data/example-guitars.ts
diff --git a/examples/ts-chat/src/lib/guitar-tools.ts b/examples/ts-react-chat/src/lib/guitar-tools.ts
similarity index 100%
rename from examples/ts-chat/src/lib/guitar-tools.ts
rename to examples/ts-react-chat/src/lib/guitar-tools.ts
diff --git a/examples/ts-chat/src/lib/stub-adapter.ts b/examples/ts-react-chat/src/lib/stub-adapter.ts
similarity index 100%
rename from examples/ts-chat/src/lib/stub-adapter.ts
rename to examples/ts-react-chat/src/lib/stub-adapter.ts
diff --git a/examples/ts-chat/src/lib/stub-llm.ts b/examples/ts-react-chat/src/lib/stub-llm.ts
similarity index 100%
rename from examples/ts-chat/src/lib/stub-llm.ts
rename to examples/ts-react-chat/src/lib/stub-llm.ts
diff --git a/examples/ts-chat/src/logo.svg b/examples/ts-react-chat/src/logo.svg
similarity index 100%
rename from examples/ts-chat/src/logo.svg
rename to examples/ts-react-chat/src/logo.svg
diff --git a/examples/ts-chat/src/routeTree.gen.ts b/examples/ts-react-chat/src/routeTree.gen.ts
similarity index 100%
rename from examples/ts-chat/src/routeTree.gen.ts
rename to examples/ts-react-chat/src/routeTree.gen.ts
diff --git a/examples/ts-chat/src/router.tsx b/examples/ts-react-chat/src/router.tsx
similarity index 100%
rename from examples/ts-chat/src/router.tsx
rename to examples/ts-react-chat/src/router.tsx
diff --git a/examples/ts-chat/src/routes/__root.tsx b/examples/ts-react-chat/src/routes/__root.tsx
similarity index 100%
rename from examples/ts-chat/src/routes/__root.tsx
rename to examples/ts-react-chat/src/routes/__root.tsx
diff --git a/examples/ts-chat/src/routes/api.tanchat.ts b/examples/ts-react-chat/src/routes/api.tanchat.ts
similarity index 100%
rename from examples/ts-chat/src/routes/api.tanchat.ts
rename to examples/ts-react-chat/src/routes/api.tanchat.ts
diff --git a/examples/ts-chat/src/routes/api.test-chat.ts b/examples/ts-react-chat/src/routes/api.test-chat.ts
similarity index 100%
rename from examples/ts-chat/src/routes/api.test-chat.ts
rename to examples/ts-react-chat/src/routes/api.test-chat.ts
diff --git a/examples/ts-chat/src/routes/demo.tsx b/examples/ts-react-chat/src/routes/demo.tsx
similarity index 100%
rename from examples/ts-chat/src/routes/demo.tsx
rename to examples/ts-react-chat/src/routes/demo.tsx
diff --git a/examples/ts-chat/src/routes/example.guitars/$guitarId.tsx b/examples/ts-react-chat/src/routes/example.guitars/$guitarId.tsx
similarity index 100%
rename from examples/ts-chat/src/routes/example.guitars/$guitarId.tsx
rename to examples/ts-react-chat/src/routes/example.guitars/$guitarId.tsx
diff --git a/examples/ts-chat/src/routes/example.guitars/index.tsx b/examples/ts-react-chat/src/routes/example.guitars/index.tsx
similarity index 100%
rename from examples/ts-chat/src/routes/example.guitars/index.tsx
rename to examples/ts-react-chat/src/routes/example.guitars/index.tsx
diff --git a/examples/ts-chat/src/routes/index.tsx b/examples/ts-react-chat/src/routes/index.tsx
similarity index 100%
rename from examples/ts-chat/src/routes/index.tsx
rename to examples/ts-react-chat/src/routes/index.tsx
diff --git a/examples/ts-chat/src/routes/tanchat.css b/examples/ts-react-chat/src/routes/tanchat.css
similarity index 100%
rename from examples/ts-chat/src/routes/tanchat.css
rename to examples/ts-react-chat/src/routes/tanchat.css
diff --git a/examples/ts-chat/src/styles.css b/examples/ts-react-chat/src/styles.css
similarity index 100%
rename from examples/ts-chat/src/styles.css
rename to examples/ts-react-chat/src/styles.css
diff --git a/examples/ts-chat/src/utils/demo.tools.ts b/examples/ts-react-chat/src/utils/demo.tools.ts
similarity index 100%
rename from examples/ts-chat/src/utils/demo.tools.ts
rename to examples/ts-react-chat/src/utils/demo.tools.ts
diff --git a/examples/ts-chat/tsconfig.json b/examples/ts-react-chat/tsconfig.json
similarity index 100%
rename from examples/ts-chat/tsconfig.json
rename to examples/ts-react-chat/tsconfig.json
diff --git a/examples/ts-chat/vite.config.ts b/examples/ts-react-chat/vite.config.ts
similarity index 100%
rename from examples/ts-chat/vite.config.ts
rename to examples/ts-react-chat/vite.config.ts
diff --git a/examples/ts-solid-chat/.cta.json b/examples/ts-solid-chat/.cta.json
new file mode 100644
index 000000000..506626021
--- /dev/null
+++ b/examples/ts-solid-chat/.cta.json
@@ -0,0 +1,12 @@
+{
+ "projectName": "ts-solid-chat",
+ "mode": "file-router",
+ "typescript": true,
+ "tailwind": true,
+ "packageManager": "pnpm",
+ "addOnOptions": {},
+ "git": true,
+ "version": 1,
+ "framework": "solid",
+ "chosenAddOns": ["nitro", "start", "tanchat", "store"]
+}
diff --git a/examples/ts-solid-chat/.gitignore b/examples/ts-solid-chat/.gitignore
new file mode 100644
index 000000000..029f7fba9
--- /dev/null
+++ b/examples/ts-solid-chat/.gitignore
@@ -0,0 +1,12 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+count.txt
+.env
+.nitro
+.tanstack
+.output
+.vinxi
+todos.json
diff --git a/examples/ts-solid-chat/README.md b/examples/ts-solid-chat/README.md
new file mode 100644
index 000000000..a2f8e5146
--- /dev/null
+++ b/examples/ts-solid-chat/README.md
@@ -0,0 +1,325 @@
+Welcome to your new TanStack app!
+
+# Getting Started
+
+To run this application:
+
+```bash
+pnpm install
+pnpm start
+```
+
+# Building For Production
+
+To build this application for production:
+
+```bash
+pnpm build
+```
+
+## Testing
+
+This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
+
+```bash
+pnpm test
+```
+
+## Styling
+
+This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
+
+# TanStack Chat Application
+
+An example chat application built with TanStack Start, TanStack Store, and **TanStack AI** (our open-source AI SDK).
+
+## .env Updates
+
+```env
+OPENAI_API_KEY=your_openai_api_key
+```
+
+## ✨ Features
+
+### AI Capabilities
+
+- 🤖 Powered by **TanStack AI** with OpenAI GPT-4o
+- 📝 Rich markdown formatting with syntax highlighting
+- 🎯 Customizable system prompts for tailored AI behavior
+- 🔄 Real-time streaming responses with Server-Sent Events
+- 🔌 **Connection adapters** - flexible streaming architecture
+- 🛠️ **Automatic tool execution loop** - tools are executed automatically by the SDK
+- 🎸 Tool/function calling with guitar recommendations
+
+### User Experience
+
+- 🎨 Modern UI with Tailwind CSS and Lucide icons
+- 🔍 Conversation management and history
+- 🔐 Secure API key management
+- 📋 Markdown rendering with code highlighting
+
+### Technical Features
+
+- 📦 Centralized state management with TanStack Store
+- 🔌 Extensible architecture for multiple AI providers
+- 🛠️ TypeScript for type safety
+
+## Architecture
+
+### Tech Stack
+
+- **Frontend Framework**: TanStack Start
+- **Routing**: TanStack Router
+- **State Management**: TanStack Store
+- **Styling**: Tailwind CSS
+- **AI Integration**: TanStack AI with OpenAI GPT-4o
+- **Chat Client**: `@tanstack/ai-solid` with connection adapters
+- **Streaming**: Server-Sent Events via `fetchServerSentEvents`
+- **Tool Execution**: Automatic loop with `ToolCallManager`
+
+## Routing
+
+This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
+
+### Adding A Route
+
+To add a new route to your application just add another a new file in the `./src/routes` directory.
+
+TanStack will automatically generate the content of the route file for you.
+
+Now that you have two routes you can use a `Link` component to navigate between them.
+
+### Adding Links
+
+To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/solid-router`.
+
+```tsx
+import { Link } from '@tanstack/solid-router'
+```
+
+Then anywhere in your JSX you can use it like so:
+
+```tsx
+ About
+```
+
+This will create a link that will navigate to the `/about` route.
+
+More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/solid/api/router/linkComponent).
+
+### Using A Layout
+
+In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the ` ` component.
+
+Here is an example layout that includes a header:
+
+```tsx
+import { Outlet, createRootRoute } from '@tanstack/solid-router'
+import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
+
+import { Link } from '@tanstack/solid-router'
+
+export const Route = createRootRoute({
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+})
+```
+
+The ` ` component is not required so you can remove it if you don't want it in your layout.
+
+More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/routing-concepts#layouts).
+
+## Data Fetching
+
+There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
+
+For example:
+
+```tsx
+const peopleRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/people',
+ loader: async () => {
+ const response = await fetch('https://swapi.dev/api/people')
+ return response.json() as Promise<{
+ results: {
+ name: string
+ }[]
+ }>
+ },
+ component: () => {
+ const data = peopleRoute.useLoaderData()
+ return (
+
+ {data.results.map((person) => (
+ {person.name}
+ ))}
+
+ )
+ },
+})
+```
+
+Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/data-loading#loader-parameters).
+
+### Solid-Query
+
+Solid-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
+
+First add your dependencies:
+
+```bash
+pnpm add @tanstack/solid-query @tanstack/solid-query-devtools
+```
+
+Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
+
+```tsx
+import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'
+
+// ...
+
+const queryClient = new QueryClient()
+
+// ...
+```
+
+You can also add TanStack Query Devtools to the root route (optional).
+
+```tsx
+import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
+
+const rootRoute = createRootRoute({
+ component: () => (
+ <>
+
+
+
+ >
+ ),
+})
+```
+
+Now you can use `useQuery` to fetch your data.
+
+```tsx
+import { useQuery } from '@tanstack/solid-query'
+
+import './App.css'
+
+function App() {
+ const { data } = useQuery({
+ queryKey: ['people'],
+ queryFn: () =>
+ fetch('https://swapi.dev/api/people')
+ .then((res) => res.json())
+ .then((data) => data.results as { name: string }[]),
+ initialData: [],
+ })
+
+ return (
+
+
+ {data.map((person) => (
+ {person.name}
+ ))}
+
+
+ )
+}
+
+export default App
+```
+
+You can find out everything you need to know on how to use Solid-Query in the [Solid-Query documentation](https://tanstack.com/query/latest/docs/framework/solid/overview).
+
+## State Management
+
+Another common requirement for Solid applications is state management. There are many options for state management in Solid. TanStack Store provides a great starting point for your project.
+
+First you need to add TanStack Store as a dependency:
+
+```bash
+pnpm add @tanstack/store
+```
+
+Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
+
+```tsx
+import { useStore } from '@tanstack/solid-store'
+import { Store } from '@tanstack/store'
+import './App.css'
+
+const countStore = new Store(0)
+
+function App() {
+ const count = useStore(countStore)
+ return (
+
+ countStore.setState((n) => n + 1)}>
+ Increment - {count}
+
+
+ )
+}
+
+export default App
+```
+
+One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
+
+Let's check this out by doubling the count using derived state.
+
+```tsx
+import { useStore } from '@tanstack/solid-store'
+import { Store, Derived } from '@tanstack/store'
+import './App.css'
+
+const countStore = new Store(0)
+
+const doubledStore = new Derived({
+ fn: () => countStore.state * 2,
+ deps: [countStore],
+})
+doubledStore.mount()
+
+function App() {
+ const count = useStore(countStore)
+ const doubledCount = useStore(doubledStore)
+
+ return (
+
+
countStore.setState((n) => n + 1)}>
+ Increment - {count}
+
+
Doubled - {doubledCount}
+
+ )
+}
+
+export default App
+```
+
+We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
+
+Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
+
+You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
+
+# Demo files
+
+Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
+
+# Learn More
+
+You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).
diff --git a/examples/ts-solid-chat/api-verification.ts b/examples/ts-solid-chat/api-verification.ts
new file mode 100644
index 000000000..336ce12bb
--- /dev/null
+++ b/examples/ts-solid-chat/api-verification.ts
@@ -0,0 +1 @@
+export {}
diff --git a/examples/ts-solid-chat/env.example b/examples/ts-solid-chat/env.example
new file mode 100644
index 000000000..613cb664b
--- /dev/null
+++ b/examples/ts-solid-chat/env.example
@@ -0,0 +1,3 @@
+# OpenAI API Key
+# Get yours at: https://platform.openai.com/api-keys
+OPENAI_API_KEY=sk-...
\ No newline at end of file
diff --git a/examples/ts-solid-chat/package.json b/examples/ts-solid-chat/package.json
new file mode 100644
index 000000000..a5fbb4076
--- /dev/null
+++ b/examples/ts-solid-chat/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "ts-solid-chat",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev --port 3000",
+ "build": "vite build",
+ "serve": "vite preview",
+ "test": "exit 0"
+ },
+ "dependencies": {
+ "@tailwindcss/vite": "^4.1.17",
+ "@tanstack/ai": "workspace:*",
+ "@tanstack/ai-anthropic": "workspace:*",
+ "@tanstack/ai-client": "workspace:*",
+ "@tanstack/ai-devtools-core": "workspace:*",
+ "@tanstack/ai-gemini": "workspace:*",
+ "@tanstack/ai-ollama": "workspace:*",
+ "@tanstack/ai-openai": "workspace:*",
+ "@tanstack/ai-solid": "workspace:*",
+ "@tanstack/nitro-v2-vite-plugin": "^1.139.0",
+ "@tanstack/router-plugin": "^1.139.7",
+ "@tanstack/solid-ai-devtools": "workspace:*",
+ "@tanstack/solid-devtools": "^0.7.15",
+ "@tanstack/solid-router": "^1.132.0",
+ "@tanstack/solid-router-devtools": "^1.132.0",
+ "@tanstack/solid-router-ssr-query": "^1.131.7",
+ "@tanstack/solid-start": "^1.132.0",
+ "@tanstack/solid-store": "^0.7.0",
+ "@tanstack/store": "^0.8.0",
+ "highlight.js": "^11.11.1",
+ "lucide-solid": "^0.554.0",
+ "rehype-highlight": "^7.0.2",
+ "rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
+ "remark-gfm": "^4.0.1",
+ "solid-js": "^1.9.10",
+ "solid-markdown": "^2.0.14",
+ "tailwindcss": "^4.1.17",
+ "vite-tsconfig-paths": "^5.1.4",
+ "zod": "^4.1.13"
+ },
+ "devDependencies": {
+ "@solidjs/testing-library": "^0.8.10",
+ "@tanstack/devtools-event-client": "^0.3.5",
+ "@tanstack/devtools-vite": "^0.3.11",
+ "@testing-library/dom": "^10.4.1",
+ "@types/node": "^24.10.1",
+ "jsdom": "^27.2.0",
+ "typescript": "5.9.3",
+ "vite": "^7.2.4",
+ "vite-plugin-solid": "^2.11.10",
+ "vitest": "^4.0.14",
+ "web-vitals": "^5.1.0"
+ }
+}
diff --git a/examples/ts-solid-chat/public/example-guitar-flowers.jpg b/examples/ts-solid-chat/public/example-guitar-flowers.jpg
new file mode 100644
index 000000000..debe785ef
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-flowers.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-motherboard.jpg b/examples/ts-solid-chat/public/example-guitar-motherboard.jpg
new file mode 100644
index 000000000..8f1a8d72f
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-motherboard.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-racing.jpg b/examples/ts-solid-chat/public/example-guitar-racing.jpg
new file mode 100644
index 000000000..44555574c
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-racing.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-steamer-trunk.jpg b/examples/ts-solid-chat/public/example-guitar-steamer-trunk.jpg
new file mode 100644
index 000000000..7b9318939
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-steamer-trunk.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-superhero.jpg b/examples/ts-solid-chat/public/example-guitar-superhero.jpg
new file mode 100644
index 000000000..3bbea2743
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-superhero.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-traveling.jpg b/examples/ts-solid-chat/public/example-guitar-traveling.jpg
new file mode 100644
index 000000000..285647be5
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-traveling.jpg differ
diff --git a/examples/ts-solid-chat/public/example-guitar-video-games.jpg b/examples/ts-solid-chat/public/example-guitar-video-games.jpg
new file mode 100644
index 000000000..4987f77e7
Binary files /dev/null and b/examples/ts-solid-chat/public/example-guitar-video-games.jpg differ
diff --git a/examples/ts-solid-chat/public/favicon.ico b/examples/ts-solid-chat/public/favicon.ico
new file mode 100644
index 000000000..a11777cc4
Binary files /dev/null and b/examples/ts-solid-chat/public/favicon.ico differ
diff --git a/examples/ts-solid-chat/public/logo192.png b/examples/ts-solid-chat/public/logo192.png
new file mode 100644
index 000000000..fc44b0a37
Binary files /dev/null and b/examples/ts-solid-chat/public/logo192.png differ
diff --git a/examples/ts-solid-chat/public/logo512.png b/examples/ts-solid-chat/public/logo512.png
new file mode 100644
index 000000000..a4e47a654
Binary files /dev/null and b/examples/ts-solid-chat/public/logo512.png differ
diff --git a/examples/ts-solid-chat/public/manifest.json b/examples/ts-solid-chat/public/manifest.json
new file mode 100644
index 000000000..078ef5011
--- /dev/null
+++ b/examples/ts-solid-chat/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "TanStack App",
+ "name": "Create TanStack App Sample",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/examples/ts-solid-chat/public/robots.txt b/examples/ts-solid-chat/public/robots.txt
new file mode 100644
index 000000000..e9e57dc4d
--- /dev/null
+++ b/examples/ts-solid-chat/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/examples/ts-solid-chat/public/tanstack-circle-logo.png b/examples/ts-solid-chat/public/tanstack-circle-logo.png
new file mode 100644
index 000000000..9db3e67ba
Binary files /dev/null and b/examples/ts-solid-chat/public/tanstack-circle-logo.png differ
diff --git a/examples/ts-solid-chat/public/tanstack-word-logo-white.svg b/examples/ts-solid-chat/public/tanstack-word-logo-white.svg
new file mode 100644
index 000000000..b6ec5086c
--- /dev/null
+++ b/examples/ts-solid-chat/public/tanstack-word-logo-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/ts-solid-chat/src/components/Approval.tsx b/examples/ts-solid-chat/src/components/Approval.tsx
new file mode 100644
index 000000000..6d9708290
--- /dev/null
+++ b/examples/ts-solid-chat/src/components/Approval.tsx
@@ -0,0 +1,38 @@
+export interface ApprovalProps {
+ toolName: string
+ input: any
+ onApprove: () => void
+ onDeny: () => void
+}
+
+export default function Approval({
+ toolName,
+ input,
+ onApprove,
+ onDeny,
+}: ApprovalProps) {
+ return (
+
+
Approve {toolName}?
+
+
+ {JSON.stringify(input, null, 2)}
+
+
+
+
+ ✓ Approve
+
+
+ ✗ Deny
+
+
+
+ )
+}
diff --git a/examples/ts-solid-chat/src/components/Header.tsx b/examples/ts-solid-chat/src/components/Header.tsx
new file mode 100644
index 000000000..67e226d83
--- /dev/null
+++ b/examples/ts-solid-chat/src/components/Header.tsx
@@ -0,0 +1,80 @@
+import { Link } from '@tanstack/solid-router'
+
+import { Guitar, Home, Menu, X } from 'lucide-solid'
+import { createSignal } from 'solid-js'
+
+export default function Header() {
+ const [isOpen, setIsOpen] = createSignal(false)
+
+ return (
+ <>
+
+ {
+ setIsOpen(true)
+ }}
+ class="p-2 hover:bg-gray-700 rounded-lg transition-colors"
+ aria-label="Open menu"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
Navigation
+ setIsOpen(false)}
+ class="p-2 hover:bg-gray-800 rounded-lg transition-colors"
+ aria-label="Close menu"
+ >
+
+
+
+
+
+ setIsOpen(false)}
+ class="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
+ activeProps={{
+ class:
+ 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',
+ }}
+ >
+
+ Home
+
+
+
+
+ setIsOpen(false)}
+ class="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2"
+ activeProps={{
+ class:
+ 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',
+ }}
+ >
+
+ Guitar Demo
+
+
+
+ >
+ )
+}
diff --git a/examples/ts-solid-chat/src/components/example-GuitarRecommendation.tsx b/examples/ts-solid-chat/src/components/example-GuitarRecommendation.tsx
new file mode 100644
index 000000000..34e18c0c1
--- /dev/null
+++ b/examples/ts-solid-chat/src/components/example-GuitarRecommendation.tsx
@@ -0,0 +1,42 @@
+import { useNavigate } from '@tanstack/solid-router'
+
+import guitars from '../data/example-guitars'
+
+export default function GuitarRecommendation({ id }: { id: string }) {
+ const navigate = useNavigate()
+ const guitar = guitars.find((guitar) => guitar.id === +id)
+ if (!guitar) {
+ return null
+ }
+ return (
+
+
+
+
+
+
{guitar.name}
+
+ {guitar.shortDescription}
+
+
+
${guitar.price}
+
{
+ navigate({
+ to: '/example/guitars/$guitarId',
+ params: { guitarId: guitar.id.toString() },
+ })
+ }}
+ class="bg-linear-to-r from-orange-500 to-red-600 text-white px-4 py-1.5 rounded-lg text-sm hover:opacity-90 transition-opacity"
+ >
+ View Details
+
+
+
+
+ )
+}
diff --git a/examples/ts-solid-chat/src/data/example-guitars.ts b/examples/ts-solid-chat/src/data/example-guitars.ts
new file mode 100644
index 000000000..16b6764f7
--- /dev/null
+++ b/examples/ts-solid-chat/src/data/example-guitars.ts
@@ -0,0 +1,83 @@
+export interface Guitar {
+ id: number
+ name: string
+ image: string
+ description: string
+ shortDescription: string
+ price: number
+}
+
+const guitars: Array = [
+ {
+ id: 1,
+ name: 'Video Game Guitar',
+ image: '/example-guitar-video-games.jpg',
+ description:
+ "The Video Game Guitar is a unique acoustic guitar that features a design inspired by video games. It has a sleek, high-gloss finish and a comfortable playability. The guitar's ergonomic body and fast neck profile ensure comfortable playability for hours on end.",
+ shortDescription:
+ 'A unique electric guitar with a video game design, high-gloss finish, and comfortable playability.',
+ price: 699,
+ },
+ {
+ id: 2,
+ name: 'Superhero Guitar',
+ image: '/example-guitar-superhero.jpg',
+ description:
+ "The Superhero Guitar is a bold black electric guitar that stands out with its unique superhero logo design. Its sleek, high-gloss finish and powerful pickups make it perfect for high-energy performances. The guitar's ergonomic body and fast neck profile ensure comfortable playability for hours on end.",
+ shortDescription:
+ 'A bold black electric guitar with a unique superhero logo, high-gloss finish, and powerful pickups.',
+ price: 699,
+ },
+ {
+ id: 3,
+ name: 'Motherboard Guitar',
+ image: '/example-guitar-motherboard.jpg',
+ description:
+ "This guitar is a tribute to the motherboard of a computer. It's a unique and stylish instrument that will make you feel like a hacker. The intricate circuit-inspired design features actual LED lights that pulse with your playing intensity, while the neck is inlaid with binary code patterns that glow under stage lights. Each pickup has been custom-wound to produce tones ranging from clean digital precision to glitched-out distortion, perfect for electronic music fusion. The Motherboard Guitar seamlessly bridges the gap between traditional craftsmanship and cutting-edge technology, making it the ultimate instrument for the digital age musician.",
+ shortDescription:
+ 'A tech-inspired electric guitar featuring LED lights and binary code inlays that glow under stage lights.',
+ price: 649,
+ },
+ {
+ id: 4,
+ name: 'Racing Guitar',
+ image: '/example-guitar-racing.jpg',
+ description:
+ "Engineered for speed and precision, the Racing Guitar embodies the spirit of motorsport in every curve and contour. Its aerodynamic body, painted in classic racing stripes and high-gloss finish, is crafted from lightweight materials that allow for effortless play during extended performances. The custom low-action setup and streamlined neck profile enable lightning-fast fretwork, while specially designed pickups deliver a high-octane tone that cuts through any mix. Built with performance-grade hardware including racing-inspired control knobs and checkered flag inlays, this guitar isn't just played—it's driven to the limits of musical possibility.",
+ shortDescription:
+ 'A lightweight, aerodynamic guitar with racing stripes and a low-action setup designed for speed and precision.',
+ price: 679,
+ },
+ {
+ id: 5,
+ name: 'Steamer Trunk Guitar',
+ image: '/example-guitar-steamer-trunk.jpg',
+ description:
+ 'The Steamer Trunk Guitar is a semi-hollow body instrument that exudes vintage charm and character. Crafted from reclaimed antique luggage wood, it features brass hardware that adds a touch of elegance and durability. The fretboard is adorned with a world map inlay, making it a unique piece that tells a story of travel and adventure.',
+ shortDescription:
+ 'A semi-hollow body guitar with brass hardware and a world map inlay, crafted from reclaimed antique luggage wood.',
+ price: 629,
+ },
+ {
+ id: 6,
+ name: "Travelin' Man Guitar",
+ image: '/example-guitar-traveling.jpg',
+ description:
+ "The Travelin' Man Guitar is an acoustic masterpiece adorned with vintage postcards from around the world. Each postcard tells a story of adventure and wanderlust, making this guitar a unique piece of art. Its rich, resonant tones and comfortable playability make it perfect for musicians who love to travel and perform.",
+ shortDescription:
+ 'An acoustic guitar with vintage postcards, rich tones, and comfortable playability.',
+ price: 499,
+ },
+ {
+ id: 7,
+ name: 'Flowerly Love Guitar',
+ image: '/example-guitar-flowers.jpg',
+ description:
+ "The Flowerly Love Guitar is an acoustic masterpiece adorned with intricate floral designs on its body. Each flower is hand-painted, adding a touch of nature's beauty to the instrument. Its warm, resonant tones make it perfect for both intimate performances and larger gatherings.",
+ shortDescription:
+ 'An acoustic guitar with hand-painted floral designs and warm, resonant tones.',
+ price: 599,
+ },
+]
+
+export default guitars
diff --git a/examples/ts-solid-chat/src/lib/guitar-tools.ts b/examples/ts-solid-chat/src/lib/guitar-tools.ts
new file mode 100644
index 000000000..41a9bcf1e
--- /dev/null
+++ b/examples/ts-solid-chat/src/lib/guitar-tools.ts
@@ -0,0 +1,104 @@
+import { tool } from '@tanstack/ai'
+import guitars from '@/data/example-guitars'
+
+export const getGuitarsTool = tool({
+ type: 'function',
+ function: {
+ name: 'getGuitars',
+ description: 'Get all products from the database',
+ parameters: {
+ type: 'object',
+ properties: {},
+ required: [],
+ },
+ },
+ execute: async () => {
+ return JSON.stringify(guitars)
+ },
+})
+
+export const recommendGuitarTool = tool({
+ type: 'function',
+ function: {
+ name: 'recommendGuitar',
+ description:
+ 'REQUIRED tool to display a guitar recommendation to the user. This tool MUST be used whenever recommending a guitar - do NOT write recommendations yourself. This displays the guitar in a special appealing format with a buy button.',
+ parameters: {
+ type: 'object',
+ properties: {
+ id: {
+ type: 'string',
+ description:
+ 'The ID of the guitar to recommend (from the getGuitars results)',
+ },
+ },
+ required: ['id'],
+ },
+ },
+ // No execute = client-side tool
+})
+
+export const getPersonalGuitarPreferenceTool = tool({
+ type: 'function',
+ function: {
+ name: 'getPersonalGuitarPreference',
+ description:
+ "Get the user's guitar preference from their local browser storage",
+ parameters: {
+ type: 'object',
+ properties: {},
+ },
+ },
+ // No execute = client-side tool
+})
+
+export const addToWishListTool = tool({
+ type: 'function',
+ function: {
+ name: 'addToWishList',
+ description: "Add a guitar to the user's wish list (requires approval)",
+ parameters: {
+ type: 'object',
+ properties: {
+ guitarId: { type: 'string' },
+ },
+ required: ['guitarId'],
+ },
+ },
+ needsApproval: true,
+ // No execute = client-side but needs approval
+})
+
+export const addToCartTool = tool({
+ type: 'function',
+ function: {
+ name: 'addToCart',
+ description: 'Add a guitar to the shopping cart (requires approval)',
+ parameters: {
+ type: 'object',
+ properties: {
+ guitarId: { type: 'string' },
+ quantity: { type: 'number' },
+ },
+ required: ['guitarId', 'quantity'],
+ },
+ },
+ needsApproval: true,
+ execute: async (args) => {
+ return JSON.stringify({
+ success: true,
+ cartId: 'CART_' + Date.now(),
+ guitarId: args.guitarId,
+ quantity: args.quantity,
+ totalItems: args.quantity,
+ })
+ },
+})
+
+export const allTools = [
+ getGuitarsTool,
+ recommendGuitarTool,
+ getPersonalGuitarPreferenceTool,
+ addToWishListTool,
+ addToCartTool,
+]
diff --git a/examples/ts-solid-chat/src/lib/stub-adapter.ts b/examples/ts-solid-chat/src/lib/stub-adapter.ts
new file mode 100644
index 000000000..0d519594c
--- /dev/null
+++ b/examples/ts-solid-chat/src/lib/stub-adapter.ts
@@ -0,0 +1,33 @@
+import type {
+ AIAdapter,
+ ChatCompletionOptions,
+ StreamChunk,
+} from '@tanstack/ai'
+import { stubLLM } from './stub-llm'
+
+/**
+ * Stub adapter for testing without using real LLM tokens
+ * Returns canned tool call responses based on user input
+ */
+export function stubAdapter(): AIAdapter<
+ string,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any,
+ any
+> {
+ return {
+ name: 'stub',
+ async *chatStream(
+ options: ChatCompletionOptions,
+ ): AsyncIterable {
+ // Use stub LLM instead of real API
+ yield* stubLLM(options.messages)
+ },
+ } as any
+}
diff --git a/examples/ts-solid-chat/src/lib/stub-llm.ts b/examples/ts-solid-chat/src/lib/stub-llm.ts
new file mode 100644
index 000000000..064851151
--- /dev/null
+++ b/examples/ts-solid-chat/src/lib/stub-llm.ts
@@ -0,0 +1,399 @@
+import type { ModelMessage, StreamChunk } from '@tanstack/ai'
+
+/**
+ * Stub LLM for testing tool calls without burning tokens
+ * Detects which tool to call from user message keywords
+ */
+export async function* stubLLM(
+ messages: ModelMessage[],
+): AsyncIterable {
+ const lastMessage = messages[messages.length - 1]
+ const userMessage = lastMessage?.content?.toLowerCase() || ''
+
+ const baseId = `stub_${Date.now()}`
+ const timestamp = Date.now()
+
+ // Check if we have any assistant messages with tool calls already
+ // If so, this is a continuation after approval/execution, not a new request
+ // Handle both ModelMessage (toolCalls) and UIMessage (parts) formats
+ const hasExistingToolCalls = messages.some((m) => {
+ if (m.role !== 'assistant') return false
+ // Check ModelMessage format
+ if (m.toolCalls && m.toolCalls.length > 0) return true
+ // Check UIMessage format
+ if ((m as any).parts) {
+ const parts = (m as any).parts
+ return parts.some((p: any) => p.type === 'tool-call')
+ }
+ return false
+ })
+
+ if (hasExistingToolCalls && lastMessage?.role === 'assistant') {
+ // This means we're being called after an approval/tool execution
+ // Check if the user approved or denied by looking at tool results
+ let wasApproved = false
+ let wasDenied = false
+
+ // Check for tool results
+ const toolResults = messages.filter((m) => m.role === 'tool')
+ if (toolResults.length > 0) {
+ // Check if any were successful or had errors
+ for (const result of toolResults) {
+ try {
+ const parsed = JSON.parse(result.content || '{}')
+ if (parsed.error && parsed.error.includes('declined')) {
+ wasDenied = true
+ } else if (parsed.success || !parsed.error) {
+ wasApproved = true
+ }
+ } catch {
+ // If we can't parse, assume success
+ wasApproved = true
+ }
+ }
+ } else {
+ // No tool results yet, must have just gotten approval response
+ // Check the assistant message for approval status in UIMessage format
+ if ((lastMessage as any).parts) {
+ const parts = (lastMessage as any).parts
+ for (const part of parts) {
+ if (part.type === 'tool-call' && part.approval) {
+ if (part.approval.approved === true) {
+ wasApproved = true
+ } else if (part.approval.approved === false) {
+ wasDenied = true
+ }
+ }
+ }
+ } else if (lastMessage.toolCalls) {
+ // This is a ModelMessage, check if it has approval info in content
+ // (won't have it here, but we can infer from lack of tool results)
+ // Default to approved for now
+ wasApproved = true
+ }
+ }
+
+ let response = ''
+ if (wasDenied) {
+ response =
+ 'No worries! Maybe another time. Let me know if you need anything else.'
+ } else if (wasApproved) {
+ response = 'All set! Let me know if you need anything else.'
+ } else {
+ response = 'Let me know if you need anything else!'
+ }
+
+ for (let i = 0; i < response.length; i++) {
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: response[i],
+ content: response.substring(0, i + 1),
+ role: 'assistant',
+ }
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'stop',
+ }
+ return
+ }
+
+ // Check if this is a follow-up after tool execution
+ const hasToolResults = messages.some((m) => m.role === 'tool')
+
+ if (hasToolResults) {
+ // Check if user approved or denied
+ const lastAssistant = [...messages]
+ .reverse()
+ .find((m) => m.role === 'assistant' && m.toolCalls)
+
+ if (lastAssistant) {
+ // Look for approval in tool results
+ const approvedTools = messages
+ .filter((m) => m.role === 'tool')
+ .filter((m) => {
+ try {
+ const result = JSON.parse(m.content || '{}')
+ return !result.error
+ } catch {
+ return true
+ }
+ })
+
+ const deniedTools = messages
+ .filter((m) => m.role === 'tool')
+ .filter((m) => {
+ try {
+ const result = JSON.parse(m.content || '{}')
+ return result.error?.includes('declined')
+ } catch {
+ return false
+ }
+ })
+
+ let responseText = ''
+ if (approvedTools.length > 0) {
+ responseText = "Good for you! I've processed that request."
+ } else if (deniedTools.length > 0) {
+ responseText = 'Bummer! Maybe another time.'
+ } else {
+ responseText = 'Complete! If you need anything else, feel free to ask.'
+ }
+
+ // Send final response
+ for (const char of responseText) {
+ const accumulated = responseText.substring(
+ 0,
+ responseText.indexOf(char) + 1,
+ )
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: char,
+ content: accumulated,
+ role: 'assistant',
+ }
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'stop',
+ }
+ return
+ }
+ }
+
+ // Detect which tool to call based on user message
+ if (userMessage.includes('preference')) {
+ // Send initial text
+ const initText = 'Let me check your preferences...'
+ for (let i = 0; i < initText.length; i++) {
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: initText[i],
+ content: initText.substring(0, i + 1),
+ role: 'assistant',
+ }
+ }
+
+ // Call getPersonalGuitarPreference
+ yield {
+ type: 'tool_call',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ toolCall: {
+ id: `call_${Date.now()}`,
+ type: 'function',
+ function: {
+ name: 'getPersonalGuitarPreference',
+ arguments: '{}',
+ },
+ },
+ index: 0,
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'tool_calls',
+ }
+ return
+ }
+
+ if (userMessage.includes('recommend') || userMessage.includes('acoustic')) {
+ // Send initial text
+ const initText = 'Let me find the perfect guitar for you!'
+ for (let i = 0; i < initText.length; i++) {
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: initText[i],
+ content: initText.substring(0, i + 1),
+ role: 'assistant',
+ }
+ }
+
+ // Call getGuitars then recommendGuitar
+ const getGuitarsId = `call_${Date.now()}_1`
+ yield {
+ type: 'tool_call',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ toolCall: {
+ id: getGuitarsId,
+ type: 'function',
+ function: {
+ name: 'getGuitars',
+ arguments: '{}',
+ },
+ },
+ index: 0,
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'tool_calls',
+ }
+
+ // After getGuitars result, call recommendGuitar
+ const recommendId = `call_${Date.now()}_2`
+ yield {
+ type: 'tool_call',
+ id: baseId + '_2',
+ model: 'stub-llm',
+ timestamp: timestamp + 100,
+ toolCall: {
+ id: recommendId,
+ type: 'function',
+ function: {
+ name: 'recommendGuitar',
+ arguments: JSON.stringify({ id: '6' }),
+ },
+ },
+ index: 0,
+ }
+
+ yield {
+ type: 'done',
+ id: baseId + '_2',
+ model: 'stub-llm',
+ timestamp: timestamp + 100,
+ finishReason: 'tool_calls',
+ }
+ return
+ }
+
+ if (userMessage.includes('wish list')) {
+ // Send initial text
+ const initText =
+ "I'll add that to your wish list. Just need your approval first!"
+ for (let i = 0; i < initText.length; i++) {
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: initText[i],
+ content: initText.substring(0, i + 1),
+ role: 'assistant',
+ }
+ }
+
+ // Call addToWishList (needs approval)
+ yield {
+ type: 'tool_call',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ toolCall: {
+ id: `call_${Date.now()}`,
+ type: 'function',
+ function: {
+ name: 'addToWishList',
+ arguments: JSON.stringify({ guitarId: '6' }),
+ },
+ },
+ index: 0,
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'tool_calls',
+ }
+ return
+ }
+
+ if (userMessage.includes('cart')) {
+ // Send initial text
+ const initText =
+ "Ready to add to your cart! I'll need your approval to proceed."
+ for (let i = 0; i < initText.length; i++) {
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: initText[i],
+ content: initText.substring(0, i + 1),
+ role: 'assistant',
+ }
+ }
+
+ // Call addToCart (needs approval)
+ yield {
+ type: 'tool_call',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ toolCall: {
+ id: `call_${Date.now()}`,
+ type: 'function',
+ function: {
+ name: 'addToCart',
+ arguments: JSON.stringify({ guitarId: '6', quantity: 1 }),
+ },
+ },
+ index: 0,
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'tool_calls',
+ }
+ return
+ }
+
+ // Default response
+ const response =
+ 'I can help with guitar preferences, recommendations, wish lists, and cart!'
+ for (const char of response) {
+ const accumulated = response.substring(0, response.indexOf(char) + 1)
+ yield {
+ type: 'content',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ delta: char,
+ content: accumulated,
+ role: 'assistant',
+ }
+ }
+
+ yield {
+ type: 'done',
+ id: baseId,
+ model: 'stub-llm',
+ timestamp,
+ finishReason: 'stop',
+ }
+}
diff --git a/examples/ts-solid-chat/src/logo.svg b/examples/ts-solid-chat/src/logo.svg
new file mode 100644
index 000000000..fe53fe8d0
--- /dev/null
+++ b/examples/ts-solid-chat/src/logo.svg
@@ -0,0 +1,12 @@
+
+
+ logo
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/ts-solid-chat/src/routeTree.gen.ts b/examples/ts-solid-chat/src/routeTree.gen.ts
new file mode 100644
index 000000000..667067058
--- /dev/null
+++ b/examples/ts-solid-chat/src/routeTree.gen.ts
@@ -0,0 +1,156 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { Route as rootRouteImport } from './routes/__root'
+import { Route as IndexRouteImport } from './routes/index'
+import { Route as ApiTestChatRouteImport } from './routes/api.test-chat'
+import { Route as ApiTanchatRouteImport } from './routes/api.tanchat'
+import { Route as ExampleGuitarsIndexRouteImport } from './routes/example.guitars/index'
+import { Route as ExampleGuitarsGuitarIdRouteImport } from './routes/example.guitars/$guitarId'
+
+const IndexRoute = IndexRouteImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const ApiTestChatRoute = ApiTestChatRouteImport.update({
+ id: '/api/test-chat',
+ path: '/api/test-chat',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const ApiTanchatRoute = ApiTanchatRouteImport.update({
+ id: '/api/tanchat',
+ path: '/api/tanchat',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const ExampleGuitarsIndexRoute = ExampleGuitarsIndexRouteImport.update({
+ id: '/example/guitars/',
+ path: '/example/guitars/',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const ExampleGuitarsGuitarIdRoute = ExampleGuitarsGuitarIdRouteImport.update({
+ id: '/example/guitars/$guitarId',
+ path: '/example/guitars/$guitarId',
+ getParentRoute: () => rootRouteImport,
+} as any)
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/api/tanchat': typeof ApiTanchatRoute
+ '/api/test-chat': typeof ApiTestChatRoute
+ '/example/guitars/$guitarId': typeof ExampleGuitarsGuitarIdRoute
+ '/example/guitars': typeof ExampleGuitarsIndexRoute
+}
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/api/tanchat': typeof ApiTanchatRoute
+ '/api/test-chat': typeof ApiTestChatRoute
+ '/example/guitars/$guitarId': typeof ExampleGuitarsGuitarIdRoute
+ '/example/guitars': typeof ExampleGuitarsIndexRoute
+}
+export interface FileRoutesById {
+ __root__: typeof rootRouteImport
+ '/': typeof IndexRoute
+ '/api/tanchat': typeof ApiTanchatRoute
+ '/api/test-chat': typeof ApiTestChatRoute
+ '/example/guitars/$guitarId': typeof ExampleGuitarsGuitarIdRoute
+ '/example/guitars/': typeof ExampleGuitarsIndexRoute
+}
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths:
+ | '/'
+ | '/api/tanchat'
+ | '/api/test-chat'
+ | '/example/guitars/$guitarId'
+ | '/example/guitars'
+ fileRoutesByTo: FileRoutesByTo
+ to:
+ | '/'
+ | '/api/tanchat'
+ | '/api/test-chat'
+ | '/example/guitars/$guitarId'
+ | '/example/guitars'
+ id:
+ | '__root__'
+ | '/'
+ | '/api/tanchat'
+ | '/api/test-chat'
+ | '/example/guitars/$guitarId'
+ | '/example/guitars/'
+ fileRoutesById: FileRoutesById
+}
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ ApiTanchatRoute: typeof ApiTanchatRoute
+ ApiTestChatRoute: typeof ApiTestChatRoute
+ ExampleGuitarsGuitarIdRoute: typeof ExampleGuitarsGuitarIdRoute
+ ExampleGuitarsIndexRoute: typeof ExampleGuitarsIndexRoute
+}
+
+declare module '@tanstack/solid-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/api/test-chat': {
+ id: '/api/test-chat'
+ path: '/api/test-chat'
+ fullPath: '/api/test-chat'
+ preLoaderRoute: typeof ApiTestChatRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/api/tanchat': {
+ id: '/api/tanchat'
+ path: '/api/tanchat'
+ fullPath: '/api/tanchat'
+ preLoaderRoute: typeof ApiTanchatRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/example/guitars/': {
+ id: '/example/guitars/'
+ path: '/example/guitars'
+ fullPath: '/example/guitars'
+ preLoaderRoute: typeof ExampleGuitarsIndexRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/example/guitars/$guitarId': {
+ id: '/example/guitars/$guitarId'
+ path: '/example/guitars/$guitarId'
+ fullPath: '/example/guitars/$guitarId'
+ preLoaderRoute: typeof ExampleGuitarsGuitarIdRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ }
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ ApiTanchatRoute: ApiTanchatRoute,
+ ApiTestChatRoute: ApiTestChatRoute,
+ ExampleGuitarsGuitarIdRoute: ExampleGuitarsGuitarIdRoute,
+ ExampleGuitarsIndexRoute: ExampleGuitarsIndexRoute,
+}
+export const routeTree = rootRouteImport
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+import type { getRouter } from './router.tsx'
+import type { createStart } from '@tanstack/solid-start'
+declare module '@tanstack/solid-start' {
+ interface Register {
+ ssr: true
+ router: Awaited>
+ }
+}
diff --git a/examples/ts-solid-chat/src/router.tsx b/examples/ts-solid-chat/src/router.tsx
new file mode 100644
index 000000000..d9d23a5d8
--- /dev/null
+++ b/examples/ts-solid-chat/src/router.tsx
@@ -0,0 +1,13 @@
+import { createRouter } from '@tanstack/solid-router'
+
+// Import the generated route tree
+import { routeTree } from './routeTree.gen'
+
+// Create a new router instance
+export const getRouter = () => {
+ return createRouter({
+ routeTree,
+ scrollRestoration: true,
+ defaultPreloadStaleTime: 0,
+ })
+}
diff --git a/examples/ts-solid-chat/src/routes/__root.tsx b/examples/ts-solid-chat/src/routes/__root.tsx
new file mode 100644
index 000000000..9aad43ea5
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/__root.tsx
@@ -0,0 +1,64 @@
+import { HeadContent, Scripts, createRootRoute } from '@tanstack/solid-router'
+import { TanStackRouterDevtoolsPanel } from '@tanstack/solid-router-devtools'
+import { TanStackDevtools } from '@tanstack/solid-devtools'
+// import { aiDevtoolsPlugin } from "@tanstack/react-ai-devtools";
+import { HydrationScript } from 'solid-js/web'
+import appCss from '../styles.css?url'
+import Header from '../components/Header'
+import type { JSXElement } from 'solid-js'
+
+export const Route = createRootRoute({
+ head: () => ({
+ meta: [
+ {
+ charSet: 'utf-8',
+ },
+ {
+ name: 'viewport',
+ content: 'width=device-width, initial-scale=1',
+ },
+ {
+ title: 'TanStack Start Starter',
+ },
+ ],
+ links: [
+ {
+ rel: 'stylesheet',
+ href: appCss,
+ },
+ ],
+ }),
+
+ shellComponent: RootDocument,
+})
+
+function RootDocument({ children }: { children: JSXElement }) {
+ return (
+
+
+
+
+
+
+
+ {children}
+ ,
+ },
+ // aiDevtoolsPlugin(),
+ ]}
+ eventBusConfig={{
+ connectToServerBus: true,
+ }}
+ />
+
+
+
+ )
+}
diff --git a/examples/ts-solid-chat/src/routes/api.tanchat.ts b/examples/ts-solid-chat/src/routes/api.tanchat.ts
new file mode 100644
index 000000000..cf863a105
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/api.tanchat.ts
@@ -0,0 +1,102 @@
+import { createFileRoute } from '@tanstack/solid-router'
+import {
+ chat,
+ toStreamResponse,
+ maxIterations,
+ chatOptions,
+} from '@tanstack/ai'
+import { openai } from '@tanstack/ai-openai'
+// import { ollama } from "@tanstack/ai-ollama";
+// import { anthropic } from "@tanstack/ai-anthropic";
+// import { gemini } from "@tanstack/ai-gemini";
+import { allTools } from '@/lib/guitar-tools'
+
+const SYSTEM_PROMPT = `You are a helpful assistant for a guitar store.
+
+CRITICAL INSTRUCTIONS - YOU MUST FOLLOW THIS EXACT WORKFLOW:
+
+When a user asks for a guitar recommendation:
+1. FIRST: Use the getGuitars tool (no parameters needed)
+2. SECOND: Use the recommendGuitar tool with the ID of the guitar you want to recommend
+3. NEVER write a recommendation directly - ALWAYS use the recommendGuitar tool
+
+IMPORTANT:
+- The recommendGuitar tool will display the guitar in a special, appealing format
+- You MUST use recommendGuitar for ANY guitar recommendation
+- ONLY recommend guitars from our inventory (use getGuitars first)
+- The recommendGuitar tool has a buy button - this is how customers purchase
+- Do NOT describe the guitar yourself - let the recommendGuitar tool do it
+
+Example workflow:
+User: "I want an acoustic guitar"
+Step 1: Call getGuitars()
+Step 2: Call recommendGuitar(id: "6")
+Step 3: Done - do NOT add any text after calling recommendGuitar
+`
+
+export const Route = createFileRoute('/api/tanchat')({
+ server: {
+ handlers: {
+ POST: async ({ request }) => {
+ if (!process.env.OPENAI_API_KEY) {
+ return new Response(
+ JSON.stringify({
+ error:
+ 'OPENAI_API_KEY not configured. Please add it to .env or .env.local',
+ }),
+ {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ },
+ )
+ }
+
+ // Capture request signal before reading body (it may be aborted after body is consumed)
+ const requestSignal = request.signal
+
+ // If request is already aborted, return early
+ if (requestSignal?.aborted) {
+ return new Response(null, { status: 499 }) // 499 = Client Closed Request
+ }
+
+ const abortController = new AbortController()
+
+ const { messages } = await request.json()
+ try {
+ // Use the stream abort signal for proper cancellation handling
+ const stream = chat({
+ adapter: openai(),
+ model: 'gpt-4-turbo',
+ // model: "claude-sonnet-4-5-20250929",
+ // model: "smollm",
+ // model: "gemini-2.5-flash",
+ tools: allTools,
+ systemPrompts: [SYSTEM_PROMPT],
+ agentLoopStrategy: maxIterations(20),
+ messages,
+ providerOptions: {
+ tool_choice: 'auto',
+ },
+ abortController,
+ })
+
+ return toStreamResponse(stream, { abortController })
+ } catch (error: any) {
+ // If request was aborted, return early (don't send error response)
+ if (error.name === 'AbortError' || abortController.signal.aborted) {
+ return new Response(null, { status: 499 }) // 499 = Client Closed Request
+ }
+ return new Response(
+ JSON.stringify({
+ error: error.message || 'An error occurred',
+ }),
+ {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ },
+ )
+ }
+ },
+ },
+ },
+})
diff --git a/examples/ts-solid-chat/src/routes/api.test-chat.ts b/examples/ts-solid-chat/src/routes/api.test-chat.ts
new file mode 100644
index 000000000..f5d19607d
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/api.test-chat.ts
@@ -0,0 +1,47 @@
+import { createFileRoute } from '@tanstack/solid-router'
+import { chat, maxIterations, toStreamResponse } from '@tanstack/ai'
+import { stubAdapter } from '@/lib/stub-adapter'
+import { allTools } from '@/lib/guitar-tools'
+
+export const Route = createFileRoute('/api/test-chat')({
+ server: {
+ handlers: {
+ POST: async ({ request }) => {
+ const { messages } = await request.json()
+
+ try {
+ const stream = chat({
+ adapter: stubAdapter(),
+ messages,
+ model: 'gpt-4.1-nano', // Doesn't matter for stub
+ tools: allTools,
+ systemPrompts: [],
+ options: {
+ temperature: 0.7,
+ topP: 1,
+ frequencyPenalty: 0,
+ presencePenalty: 0,
+ maxTokens: 1000,
+ stream: true,
+ seed: 331423424,
+ },
+ agentLoopStrategy: maxIterations(20),
+ providerOptions: {},
+ })
+
+ return toStreamResponse(stream)
+ } catch (error: any) {
+ return new Response(
+ JSON.stringify({
+ error: error.message || 'An error occurred',
+ }),
+ {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ },
+ )
+ }
+ },
+ },
+ },
+})
diff --git a/examples/ts-solid-chat/src/routes/example.guitars/$guitarId.tsx b/examples/ts-solid-chat/src/routes/example.guitars/$guitarId.tsx
new file mode 100644
index 000000000..39da94fd8
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/example.guitars/$guitarId.tsx
@@ -0,0 +1,50 @@
+import { Link, createFileRoute } from '@tanstack/solid-router'
+import guitars from '../../data/example-guitars'
+
+export const Route = createFileRoute('/example/guitars/$guitarId')({
+ component: RouteComponent,
+ loader: async ({ params }) => {
+ const guitar = guitars.find((guitar) => guitar.id === +params.guitarId)
+ if (!guitar) {
+ throw new Error('Guitar not found')
+ }
+ return guitar
+ },
+})
+
+function RouteComponent() {
+ const guitar = Route.useLoaderData()
+
+ return (
+
+
+
+ ← Back to all guitars
+
+
{guitar().name}
+
{guitar().description}
+
+
+ ${guitar().price}
+
+
+ Add to Cart
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/examples/ts-solid-chat/src/routes/example.guitars/index.tsx b/examples/ts-solid-chat/src/routes/example.guitars/index.tsx
new file mode 100644
index 000000000..259dfe326
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/example.guitars/index.tsx
@@ -0,0 +1,51 @@
+import { Link, createFileRoute } from '@tanstack/solid-router'
+import guitars from '../../data/example-guitars'
+
+export const Route = createFileRoute('/example/guitars/')({
+ component: GuitarsIndex,
+})
+
+function GuitarsIndex() {
+ return (
+
+
Featured Guitars
+
+ {guitars.map((guitar) => (
+
+
+
+
+
+
+
+
+
+ View Details
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/examples/ts-solid-chat/src/routes/index.tsx b/examples/ts-solid-chat/src/routes/index.tsx
new file mode 100644
index 000000000..d33a1e133
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/index.tsx
@@ -0,0 +1,403 @@
+import { createFileRoute } from '@tanstack/solid-router'
+import { Send, Square } from 'lucide-solid'
+import { fetchServerSentEvents, useChat } from '@tanstack/ai-solid'
+import { createSignal, For, Show } from 'solid-js'
+import type { UIMessage } from '@tanstack/ai-solid'
+
+import type { JSXElement } from 'solid-js'
+import GuitarRecommendation from '@/components/example-GuitarRecommendation'
+
+function ChatInputArea({ children }: { children: JSXElement }) {
+ return (
+
+ )
+}
+
+function Messages(props: {
+ messages: Array
+ addToolApprovalResponse: (response: {
+ id: string
+ approved: boolean
+ }) => Promise
+}) {
+ return (
+
+
+ {({ role, parts }) => (
+
+
+ {role === 'assistant' ? (
+
+ AI
+
+ ) : (
+
+ U
+
+ )}
+
+ {/* Render parts in order */}
+
+ {(part, index) => {
+ if (part.type === 'text' && part.content) {
+ return (
+
+ {part.content}
+
+ )
+ }
+
+ // Approval UI
+ if (
+ part.type === 'tool-call' &&
+ part.state === 'approval-requested' &&
+ part.approval
+ ) {
+ return (
+
+
+ 🔒 Approval Required: {part.name}
+
+
+
+ {JSON.stringify(
+ JSON.parse(part.arguments),
+ null,
+ 2,
+ )}
+
+
+
+
+ props.addToolApprovalResponse({
+ id: part.approval!.id,
+ approved: true,
+ })
+ }
+ class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg text-sm font-medium transition-colors"
+ >
+ ✓ Approve
+
+
+ props.addToolApprovalResponse({
+ id: part.approval!.id,
+ approved: false,
+ })
+ }
+ class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-sm font-medium transition-colors"
+ >
+ ✗ Deny
+
+
+
+ )
+ }
+
+ // Guitar recommendation card
+ if (
+ part.type === 'tool-call' &&
+ part.name === 'recommendGuitar' &&
+ part.output
+ ) {
+ try {
+ return (
+
+
+
+ )
+ } catch {
+ return null
+ }
+ }
+
+ return null
+ }}
+
+
+
+
+ )}
+
+
+ )
+}
+
+function DebugPanel(props: {
+ messages: Array
+ chunks: Array
+ onClearChunks: () => void
+}) {
+ const [activeTab, setActiveTab] = createSignal<'messages' | 'chunks'>(
+ 'messages',
+ )
+
+ const exportToTypeScript = () => {
+ const tsCode = `const rawChunks = ${JSON.stringify(props.chunks, null, 2)};`
+ navigator.clipboard.writeText(tsCode)
+ alert('TypeScript code copied to clipboard!')
+ }
+
+ return (
+
+
+
Debug Panel
+
+ View messages and raw stream chunks
+
+
+ {/* Tabs */}
+
+ setActiveTab('messages')}
+ class={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
+ activeTab() === 'messages'
+ ? 'bg-orange-500 text-white'
+ : 'bg-gray-800 text-gray-400 hover:text-white'
+ }`}
+ >
+ Messages
+
+ setActiveTab('chunks')}
+ class={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
+ activeTab() === 'chunks'
+ ? 'bg-orange-500 text-white'
+ : 'bg-gray-800 text-gray-400 hover:text-white'
+ }`}
+ >
+ Raw Chunks ({props.chunks.length})
+
+
+
+
+
+ {activeTab() === 'messages' && (
+
+
+ {JSON.stringify(props.messages, null, 2)}
+
+
+ )}
+
+ {activeTab() === 'chunks' && (
+
+
+
+ 📋 Export to TypeScript
+
+
+ 🗑️ Clear Chunks
+
+
+
+ {/* Chunks Table */}
+
+
+
+
+ Type
+ Role
+ Tool Type
+ Tool Name
+ Detail
+
+
+
+
+ {(chunk, idx) => {
+ const role = chunk.role || '-'
+ const toolType = chunk.toolCall?.type || '-'
+ const toolName = chunk.toolCall?.function?.name || '-'
+
+ let detail = '-'
+ if (chunk.type === 'content' && chunk.content) {
+ detail = chunk.content
+ } else if (
+ chunk.type === 'tool_call' &&
+ chunk.toolCall?.function?.arguments
+ ) {
+ detail = chunk.toolCall.function.arguments
+ } else if (
+ chunk.type === 'tool_result' &&
+ chunk.content
+ ) {
+ detail = chunk.content
+ } else if (chunk.type === 'done') {
+ detail = `Finish: ${chunk.finishReason || 'unknown'}`
+ }
+
+ // Truncate at 200 chars
+ if (detail.length > 200) {
+ detail = detail.substring(0, 200) + '...'
+ }
+
+ return (
+
+ {chunk.type}
+ {role}
+ {toolType}
+ {toolName}
+
+ {detail}
+
+
+ )
+ }}
+
+
+
+
+
+ )}
+
+
+ )
+}
+
+function ChatPage() {
+ const [chunks, setChunks] = createSignal>([])
+
+ const { messages, sendMessage, isLoading, addToolApprovalResponse, stop } =
+ useChat({
+ connection: fetchServerSentEvents('/api/tanchat'),
+ onChunk: (chunk: any) => {
+ setChunks((prev) => [...prev, chunk])
+ },
+ onToolCall: async ({ toolName, input }) => {
+ // Handle client-side tool execution
+ switch (toolName) {
+ case 'getPersonalGuitarPreference':
+ // Pure client tool - executes immediately
+ return { preference: 'acoustic' }
+
+ case 'recommendGuitar':
+ // Client tool for UI display
+ return { id: input.id }
+
+ case 'addToWishList':
+ // Hybrid: client execution AFTER approval
+ // Only runs after user approves
+ const wishList = JSON.parse(
+ localStorage.getItem('wishList') || '[]',
+ )
+ wishList.push(input.guitarId)
+ localStorage.setItem('wishList', JSON.stringify(wishList))
+ return {
+ success: true,
+ guitarId: input.guitarId,
+ totalItems: wishList.length,
+ }
+
+ default:
+ throw new Error(`Unknown client tool: ${toolName}`)
+ }
+ },
+ })
+ const [input, setInput] = createSignal('')
+
+ const clearChunks = () => setChunks([])
+
+ return (
+
+ {/* Left side - Chat (1/4 width) */}
+
+
+
+ TanStack Chat
+
+
+ Parts-based UIMessages with tool states
+
+
+
+
+
+
+
+ {isLoading() && (
+
+
+
+ Stop
+
+
+ )}
+
+
+
+
+
+
+ {/* Right side - Debug Panel (3/4 width) */}
+
+
+
+
+ )
+}
+
+export const Route = createFileRoute('/')({
+ component: ChatPage,
+})
diff --git a/examples/ts-solid-chat/src/routes/tanchat.css b/examples/ts-solid-chat/src/routes/tanchat.css
new file mode 100644
index 000000000..a74144fea
--- /dev/null
+++ b/examples/ts-solid-chat/src/routes/tanchat.css
@@ -0,0 +1,227 @@
+@import 'tailwindcss';
+@import 'highlight.js/styles/github-dark.css';
+
+/* Custom scrollbar styles */
+::-webkit-scrollbar {
+ width: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background-color: rgba(156, 163, 175, 0.5);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background-color: rgba(156, 163, 175, 0.7);
+}
+
+/* Smooth transitions for dark mode */
+html {
+ transition: background-color 0.3s ease;
+}
+
+/* Markdown content styles */
+.prose {
+ max-width: none;
+ color: #e5e7eb; /* text-gray-200 */
+}
+
+/* .prose p {
+ margin-top: 1.25em;
+ margin-bottom: 1.25em;
+} */
+
+.prose code {
+ color: #e5e7eb;
+ background-color: rgba(31, 41, 55, 0.5);
+ padding: 0.2em 0.4em;
+ border-radius: 0.375rem;
+ font-size: 0.875em;
+}
+
+.prose pre {
+ background-color: rgba(31, 41, 55, 0.5);
+ border-radius: 0.5rem;
+ padding: 1rem;
+ margin: 1.25em 0;
+ overflow-x: auto;
+}
+
+.prose pre code {
+ background-color: transparent;
+ padding: 0;
+ border-radius: 0;
+ color: inherit;
+}
+
+.prose h1,
+.prose h2,
+.prose h3,
+.prose h4 {
+ color: #f9fafb; /* text-gray-50 */
+ /* margin-top: 2em; */
+ /* margin-bottom: 1em; */
+}
+
+.prose ul,
+.prose ol {
+ margin-top: 1.25em;
+ margin-bottom: 1.25em;
+ padding-left: 1.625em;
+}
+
+.prose li {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+.prose blockquote {
+ border-left-color: #f97316; /* orange-500 */
+ background-color: rgba(249, 115, 22, 0.1);
+ padding: 1em;
+ margin: 1.25em 0;
+ border-radius: 0.5rem;
+}
+
+.prose hr {
+ border-color: rgba(249, 115, 22, 0.2);
+ margin: 2em 0;
+}
+
+.prose a {
+ color: #f97316; /* orange-500 */
+ text-decoration: underline;
+ text-decoration-thickness: 0.1em;
+ text-underline-offset: 0.2em;
+}
+
+.prose a:hover {
+ color: #fb923c; /* orange-400 */
+}
+
+.prose table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1.25em 0;
+}
+
+.prose th,
+.prose td {
+ padding: 0.75em;
+ border: 1px solid rgba(249, 115, 22, 0.2);
+}
+
+.prose th {
+ background-color: rgba(249, 115, 22, 0.1);
+ font-weight: 600;
+}
+
+/* Message transition animations */
+.message-enter {
+ opacity: 0;
+ transform: translateY(10px);
+}
+
+.message-enter-active {
+ opacity: 1;
+ transform: translateY(0);
+ transition:
+ opacity 300ms,
+ transform 300ms;
+}
+
+.message-exit {
+ opacity: 1;
+}
+
+.message-exit-active {
+ opacity: 0;
+ transition: opacity 300ms;
+}
+
+/* Add/update these styles to match AI formatting capabilities */
+.prose h1 {
+ font-size: 2em;
+ /* margin-top: 1em; */
+ margin-bottom: 0.5em;
+}
+
+.prose h2 {
+ font-size: 1.5em;
+ margin-top: 1em;
+ margin-bottom: 0.5em;
+}
+
+.prose h3 {
+ font-size: 1.25em;
+ margin-top: 1em;
+ margin-bottom: 0.5em;
+}
+
+.prose ul {
+ list-style-type: disc;
+ padding-left: 1.5em;
+}
+
+.prose ol {
+ list-style-type: decimal;
+ padding-left: 1.5em;
+}
+
+.prose table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 1em 0;
+}
+
+.prose th,
+.prose td {
+ border: 1px solid rgba(249, 115, 22, 0.2);
+ padding: 0.5em;
+}
+
+.prose th {
+ background-color: rgba(249, 115, 22, 0.1);
+}
+
+.prose strong {
+ color: #f9fafb; /* text-gray-50 */
+ font-weight: 600;
+}
+
+.prose em {
+ font-style: italic;
+}
+
+.prose blockquote {
+ border-left: 4px solid #f97316; /* orange-500 */
+ padding-left: 1em;
+ margin: 1em 0;
+ color: #d1d5db; /* text-gray-300 */
+}
+
+/* Ensure code blocks match the AI's formatting */
+.prose code {
+ color: #e5e7eb;
+ background-color: rgba(31, 41, 55, 0.5);
+ padding: 0.2em 0.4em;
+ border-radius: 0.375rem;
+ font-size: 0.875em;
+}
+
+.prose pre {
+ background-color: rgba(31, 41, 55, 0.5);
+ border-radius: 0.5rem;
+ padding: 1rem;
+ margin: 1em 0;
+}
+
+.prose pre code {
+ background-color: transparent;
+ padding: 0;
+ border-radius: 0;
+}
diff --git a/examples/ts-solid-chat/src/styles.css b/examples/ts-solid-chat/src/styles.css
new file mode 100644
index 000000000..06f1bca4b
--- /dev/null
+++ b/examples/ts-solid-chat/src/styles.css
@@ -0,0 +1,15 @@
+@import 'tailwindcss';
+
+body {
+ @apply m-0;
+ font-family:
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
+ 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family:
+ source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
+}
diff --git a/examples/ts-solid-chat/src/utils/demo.tools.ts b/examples/ts-solid-chat/src/utils/demo.tools.ts
new file mode 100644
index 000000000..e9671ea7a
--- /dev/null
+++ b/examples/ts-solid-chat/src/utils/demo.tools.ts
@@ -0,0 +1,53 @@
+import { experimental_createMCPClient, tool } from 'ai'
+//import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
+import { z } from 'zod'
+
+import guitars from '../data/example-guitars'
+
+// Example of using an SSE MCP server
+// const mcpClient = await experimental_createMCPClient({
+// transport: {
+// type: "sse",
+// url: "http://localhost:8081/sse",
+// },
+// name: "Demo Service",
+// });
+
+// Example of using an STDIO MCP server
+// const mcpClient = await experimental_createMCPClient({
+// transport: new StdioClientTransport({
+// command: "node",
+// args: [
+// "stdio-server.js",
+// ],
+// }),
+// });
+
+const getGuitars = tool({
+ description: 'Get all products from the database',
+ inputSchema: z.object({}),
+ execute: async () => {
+ return Promise.resolve(guitars)
+ },
+})
+
+const recommendGuitar = tool({
+ description: 'Use this tool to recommend a guitar to the user',
+ inputSchema: z.object({
+ id: z.string().describe('The id of the guitar to recommend'),
+ }),
+ execute: async ({ id }) => {
+ return {
+ id,
+ }
+ },
+})
+
+export default async function getTools() {
+ // const mcpTools = await mcpCient.tools()
+ return {
+ // ...mcpTools,
+ getGuitars,
+ recommendGuitar,
+ }
+}
diff --git a/examples/ts-solid-chat/tsconfig.json b/examples/ts-solid-chat/tsconfig.json
new file mode 100644
index 000000000..6c306e5f6
--- /dev/null
+++ b/examples/ts-solid-chat/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "include": ["**/*.ts", "**/*.tsx"],
+ "compilerOptions": {
+ "target": "ES2022",
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js",
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "types": ["vite/client"],
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": false,
+ "noEmit": true,
+
+ /* Linting */
+ "skipLibCheck": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/examples/ts-solid-chat/vite.config.ts b/examples/ts-solid-chat/vite.config.ts
new file mode 100644
index 000000000..4f56dc96a
--- /dev/null
+++ b/examples/ts-solid-chat/vite.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite'
+import { tanstackStart } from '@tanstack/solid-start/plugin/vite'
+import viteSolid from 'vite-plugin-solid'
+import viteTsConfigPaths from 'vite-tsconfig-paths'
+import tailwindcss from '@tailwindcss/vite'
+import { nitroV2Plugin } from '@tanstack/nitro-v2-vite-plugin'
+import { devtools } from '@tanstack/devtools-vite'
+const config = defineConfig({
+ plugins: [
+ devtools(),
+ nitroV2Plugin(),
+ // this is the plugin that enables path aliases
+ viteTsConfigPaths({
+ projects: ['./tsconfig.json'],
+ }),
+ tailwindcss(),
+ tanstackStart(),
+ viteSolid({ ssr: true }),
+ ],
+})
+
+export default config
diff --git a/packages/typescript/ai-solid/README.md b/packages/typescript/ai-solid/README.md
new file mode 100644
index 000000000..119e23a2f
--- /dev/null
+++ b/packages/typescript/ai-solid/README.md
@@ -0,0 +1,437 @@
+# @tanstack/ai-react
+
+React hooks for building AI chat interfaces with TanStack AI.
+
+## Installation
+
+```bash
+npm install @tanstack/ai-react @tanstack/ai-client
+```
+
+## useChat Hook
+
+The `useChat` hook manages chat state, handles streaming responses, and provides a complete chat interface in a single hook.
+
+**Design Philosophy (v5 API):**
+
+- You control input state
+- Just call `sendMessage()` when ready
+- No form-centric API - use buttons, keyboard events, or any trigger
+- More flexible and less opinionated
+
+### Basic Usage
+
+```typescript
+import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
+import { useState } from "react";
+
+function ChatComponent() {
+ const { messages, sendMessage, isLoading } = useChat({
+ connection: fetchServerSentEvents("/api/chat"),
+ });
+
+ const [input, setInput] = useState("");
+
+ const handleSend = () => {
+ sendMessage(input);
+ setInput("");
+ };
+
+ return (
+
+ {messages.map((m) => (
+
+ {m.role}: {m.content}
+
+ ))}
+
+
setInput(e.target.value)}
+ onKeyDown={(e) => e.key === "Enter" && handleSend()}
+ disabled={isLoading}
+ />
+
+ Send
+
+
+ );
+}
+```
+
+### API
+
+#### Options
+
+```typescript
+interface UseChatOptions {
+ // Connection adapter (required)
+ connection: ConnectionAdapter
+
+ // Configuration
+ initialMessages?: UIMessage[] // Starting messages
+ id?: string // Unique chat ID
+ body?: Record // Extra data to send
+
+ // Callbacks
+ onResponse?: (response?: Response) => void
+ onChunk?: (chunk: StreamChunk) => void
+ onFinish?: (message: UIMessage) => void
+ onError?: (error: Error) => void
+}
+```
+
+#### Return Value
+
+```typescript
+interface UseChatReturn {
+ messages: UIMessage[] // Current conversation
+ sendMessage: (content: string) => Promise // Send a message
+ append: (message) => Promise // Add message programmatically
+ reload: () => Promise // Reload last response
+ stop: () => void // Stop current generation
+ isLoading: boolean // Is generating a response
+ error: Error | undefined // Current error
+ setMessages: (messages) => void // Set messages manually
+ clear: () => void // Clear all messages
+}
+```
+
+## Connection Adapters
+
+Connection adapters provide flexible streaming for different scenarios. See the complete guides:
+
+- 📖 [Connection Adapters Guide](../../docs/CONNECTION_ADAPTERS_GUIDE.md) - Complete guide with examples
+- 📖 [Connection Adapters API](../ai-client/CONNECTION_ADAPTERS.md) - API reference
+
+### Quick Examples
+
+**SSE (Most Common):**
+
+```typescript
+import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
+
+const chat = useChat({
+ connection: fetchServerSentEvents('/api/chat'),
+})
+```
+
+**Server Functions:**
+
+```typescript
+import { useChat, stream } from '@tanstack/ai-react'
+
+const chat = useChat({
+ connection: stream((messages) => serverChatFunction({ messages })),
+})
+```
+
+**Custom (e.g., WebSockets):**
+
+```typescript
+import { useChat } from '@tanstack/ai-react'
+import type { ConnectionAdapter } from '@tanstack/ai-client'
+
+const wsAdapter: ConnectionAdapter = {
+ async *connect(messages) {
+ // Your WebSocket logic
+ },
+}
+
+const chat = useChat({ connection: wsAdapter })
+```
+
+### Backend Endpoint
+
+Your backend should use the `chat()` method which **automatically handles tool execution in a loop**:
+
+1. Receive POST requests with this body:
+
+```typescript
+{
+ messages: Message[];
+ data?: Record;
+}
+```
+
+2. Use `chat()` to stream responses (with automatic tool execution):
+
+```typescript
+import { chat, toStreamResponse } from '@tanstack/ai'
+import { openai } from '@tanstack/ai-openai'
+
+export async function POST(request: Request) {
+ const { messages } = await request.json()
+
+ const stream = chat({
+ adapter: openai(),
+ model: 'gpt-4o',
+ messages,
+ tools: [weatherTool], // Optional: auto-executed in loop
+ agentLoopStrategy: maxIterations(5), // Optional: control loop
+ })
+
+ // Convert to HTTP streaming response with SSE headers
+ return toStreamResponse(stream)
+}
+```
+
+The response streams `StreamChunk` objects as Server-Sent Events:
+
+```
+data: {"type":"content","delta":"Hello","content":"Hello",...}
+data: {"type":"tool_call","toolCall":{...},...}
+data: {"type":"tool_result","toolCallId":"...","content":"...",...}
+data: {"type":"content","delta":" world","content":"Hello world",...}
+data: {"type":"done","finishReason":"stop","usage":{...}}
+```
+
+**Note:** The `chat()` method automatically executes tools and emits `tool_result` chunks - you don't need to handle tool execution manually!
+
+### Advanced Usage
+
+#### With Callbacks
+
+```typescript
+import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
+
+const { messages, sendMessage } = useChat({
+ connection: fetchServerSentEvents('/api/chat'),
+ onChunk: (chunk) => {
+ if (chunk.type === 'content') {
+ console.log('New token:', chunk.delta)
+ }
+ },
+ onFinish: (message) => {
+ console.log('Final message:', message)
+ // Save to database, log analytics, etc.
+ },
+ onError: (error) => {
+ console.error('Chat error:', error)
+ // Show toast notification, log error, etc.
+ },
+})
+
+// Send messages programmatically
+await sendMessage('Tell me a joke')
+```
+
+#### Flexible Triggering
+
+```typescript
+import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
+
+const { sendMessage, isLoading } = useChat({
+ connection: fetchServerSentEvents("/api/chat")
+});
+const [input, setInput] = useState("");
+
+// Button click
+ sendMessage(input)}>Send
+
+// Enter key
+ e.key === "Enter" && sendMessage(input)} />
+
+// Voice input
+ {
+ const transcript = await voiceToText();
+ sendMessage(transcript);
+}}>🎤 Speak
+
+// Predefined prompts
+ sendMessage("Explain quantum computing")}>
+ Ask about quantum computing
+
+```
+
+#### With Custom Headers
+
+```typescript
+import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
+
+const chat = useChat({
+ connection: fetchServerSentEvents('/api/chat', {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ 'X-Custom-Header': 'value',
+ },
+ }),
+ body: {
+ userId: '123',
+ sessionId: 'abc',
+ },
+})
+```
+
+#### Programmatic Control
+
+```typescript
+const { messages, sendMessage, append, reload, stop, clear } = useChat()
+
+// Send a simple message
+await sendMessage('Hello!')
+
+// Add a message with more control
+await append({
+ role: 'user',
+ content: 'Hello!',
+ id: 'custom-id',
+})
+
+// Reload the last AI response
+await reload()
+
+// Stop the current generation
+stop()
+
+// Clear all messages
+clear()
+```
+
+#### Multiple Chats
+
+```typescript
+import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
+
+function App() {
+ const chat1 = useChat({
+ id: 'chat-1',
+ connection: fetchServerSentEvents('/api/chat'),
+ })
+ const chat2 = useChat({
+ id: 'chat-2',
+ connection: fetchServerSentEvents('/api/chat'),
+ })
+
+ // Each hook manages independent state
+}
+```
+
+## Example Backend (Node.js/Express)
+
+```typescript
+import express from 'express'
+import { AI, toStreamResponse } from '@tanstack/ai'
+import { OpenAIAdapter } from '@tanstack/ai-openai'
+
+const app = express()
+app.use(express.json())
+
+const ai = new AI(new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY }))
+
+app.post('/api/chat', async (req, res) => {
+ const { messages } = req.body
+
+ // One line to create streaming response!
+ const stream = ai.streamChat({
+ model: 'gpt-3.5-turbo',
+ messages,
+ })
+
+ const response = toStreamResponse(stream)
+
+ // Copy headers and stream to Express response
+ response.headers.forEach((value, key) => {
+ res.setHeader(key, value)
+ })
+
+ const reader = response.body?.getReader()
+ if (reader) {
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+ res.write(value)
+ }
+ }
+ res.end()
+})
+
+app.listen(3000)
+```
+
+## Example Backend (Next.js App Router)
+
+```typescript
+// app/api/chat/route.ts
+import { AI, toStreamResponse } from '@tanstack/ai'
+import { OpenAIAdapter } from '@tanstack/ai-openai'
+
+export const runtime = 'edge'
+
+const ai = new AI(new OpenAIAdapter({ apiKey: process.env.OPENAI_API_KEY }))
+
+export async function POST(req: Request) {
+ const { messages } = await req.json()
+
+ // One line!
+ return toStreamResponse(
+ ai.streamChat({
+ model: 'gpt-3.5-turbo',
+ messages,
+ }),
+ )
+}
+```
+
+## Example Backend (TanStack Start)
+
+```typescript
+import { createFileRoute } from '@tanstack/react-router'
+import { AI, toStreamResponse } from '@tanstack/ai'
+import { AnthropicAdapter } from '@tanstack/ai-anthropic'
+
+const ai = new AI(
+ new AnthropicAdapter({ apiKey: process.env.ANTHROPIC_API_KEY }),
+)
+
+export const Route = createFileRoute('/api/chat')({
+ server: {
+ handlers: {
+ POST: async ({ request }) => {
+ const { messages } = await request.json()
+
+ // One line with automatic tool execution!
+ return toStreamResponse(
+ ai.streamChat({
+ model: 'claude-3-5-sonnet-20241022',
+ messages,
+ tools, // Tools with execute functions
+ }),
+ )
+ },
+ },
+ },
+})
+```
+
+## TypeScript Types
+
+All types are fully exported:
+
+```typescript
+import type {
+ UIMessage,
+ UseChatOptions,
+ UseChatReturn,
+ ChatRequestBody,
+} from '@tanstack/ai-react'
+```
+
+## Features
+
+- ✅ Automatic message state management
+- ✅ Streaming response handling
+- ✅ Loading and error states
+- ✅ Simple `sendMessage()` API (v5 style)
+- ✅ You control input state (flexible)
+- ✅ Abort/stop generation
+- ✅ Reload last response
+- ✅ Clear conversation
+- ✅ Custom headers and body data (via connection adapter options)
+- ✅ Callback hooks for lifecycle events
+- ✅ Multiple concurrent chats
+- ✅ Full TypeScript support
+
+## License
+
+MIT
diff --git a/packages/typescript/ai-solid/package.json b/packages/typescript/ai-solid/package.json
new file mode 100644
index 000000000..0c016b6d3
--- /dev/null
+++ b/packages/typescript/ai-solid/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@tanstack/ai-solid",
+ "version": "0.1.0",
+ "description": "Solid hooks for TanStack AI",
+ "author": "",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/TanStack/ai.git",
+ "directory": "packages/typescript/ai-solid"
+ },
+ "type": "module",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist",
+ "src"
+ ],
+ "scripts": {
+ "build": "tsdown",
+ "dev": "tsdown --watch",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:coverage": "vitest run --coverage",
+ "clean": "rm -rf dist node_modules",
+ "typecheck": "tsc --noEmit",
+ "lint": "tsc --noEmit"
+ },
+ "keywords": [
+ "ai",
+ "solidjs",
+ "hooks",
+ "tanstack",
+ "chat",
+ "streaming"
+ ],
+ "dependencies": {
+ "@tanstack/ai": "workspace:*",
+ "@tanstack/ai-client": "workspace:*"
+ },
+ "devDependencies": {
+ "@solidjs/testing-library": "^0.8.10",
+ "@types/node": "^24.10.1",
+ "@vitest/coverage-v8": "4.0.14",
+ "jsdom": "^27.2.0",
+ "solid-js": "^1.9.10",
+ "tsdown": "^0.15.9",
+ "typescript": "5.9.3",
+ "vitest": "^4.0.14"
+ },
+ "peerDependencies": {
+ "@tanstack/ai": "workspace:*",
+ "@tanstack/ai-client": "workspace:*",
+ "solid-js": ">=1.9.10"
+ }
+}
diff --git a/packages/typescript/ai-solid/src/index.ts b/packages/typescript/ai-solid/src/index.ts
new file mode 100644
index 000000000..231c18251
--- /dev/null
+++ b/packages/typescript/ai-solid/src/index.ts
@@ -0,0 +1,16 @@
+export { useChat } from './use-chat'
+export type {
+ UseChatOptions,
+ UseChatReturn,
+ UIMessage,
+ ChatRequestBody,
+} from './types'
+
+// Re-export connection adapters from ai-client for convenience
+export {
+ fetchServerSentEvents,
+ fetchHttpStream,
+ stream,
+ type ConnectionAdapter,
+ type FetchConnectionOptions,
+} from '@tanstack/ai-client'
diff --git a/packages/typescript/ai-solid/src/types.ts b/packages/typescript/ai-solid/src/types.ts
new file mode 100644
index 000000000..516205250
--- /dev/null
+++ b/packages/typescript/ai-solid/src/types.ts
@@ -0,0 +1,96 @@
+import type { ModelMessage } from '@tanstack/ai'
+import type {
+ ChatClientOptions,
+ UIMessage,
+ ChatRequestBody,
+} from '@tanstack/ai-client'
+import { Accessor } from 'solid-js'
+
+// Re-export types from ai-client
+export type { UIMessage, ChatRequestBody }
+
+/**
+ * Options for the useChat hook.
+ *
+ * This extends ChatClientOptions but omits the state change callbacks that are
+ * managed internally by React state:
+ * - `onMessagesChange` - Managed by React state (exposed as `messages`)
+ * - `onLoadingChange` - Managed by React state (exposed as `isLoading`)
+ * - `onErrorChange` - Managed by React state (exposed as `error`)
+ *
+ * All other callbacks (onResponse, onChunk, onFinish, onError, onToolCall) are
+ * passed through to the underlying ChatClient and can be used for side effects.
+ *
+ * Note: Connection and body changes will recreate the ChatClient instance.
+ * To update these options, remount the component or use a key prop.
+ */
+export type UseChatOptions = Omit<
+ ChatClientOptions,
+ 'onMessagesChange' | 'onLoadingChange' | 'onErrorChange'
+>
+
+export interface UseChatReturn {
+ /**
+ * Current messages in the conversation
+ */
+ messages: Accessor
+
+ /**
+ * Send a message and get a response
+ */
+ sendMessage: (content: string) => Promise
+
+ /**
+ * Append a message to the conversation
+ */
+ append: (message: ModelMessage | UIMessage) => Promise
+
+ /**
+ * Add the result of a client-side tool execution
+ */
+ addToolResult: (result: {
+ toolCallId: string
+ tool: string
+ output: any
+ state?: 'output-available' | 'output-error'
+ errorText?: string
+ }) => Promise
+
+ /**
+ * Respond to a tool approval request
+ */
+ addToolApprovalResponse: (response: {
+ id: string // approval.id, not toolCallId
+ approved: boolean
+ }) => Promise
+
+ /**
+ * Reload the last assistant message
+ */
+ reload: () => Promise
+
+ /**
+ * Stop the current response generation
+ */
+ stop: () => void
+
+ /**
+ * Whether a response is currently being generated
+ */
+ isLoading: Accessor
+
+ /**
+ * Current error, if any
+ */
+ error: Accessor
+
+ /**
+ * Set messages manually
+ */
+ setMessages: (messages: UIMessage[]) => void
+
+ /**
+ * Clear all messages
+ */
+ clear: () => void
+}
diff --git a/packages/typescript/ai-solid/src/use-chat.ts b/packages/typescript/ai-solid/src/use-chat.ts
new file mode 100644
index 000000000..c634e04da
--- /dev/null
+++ b/packages/typescript/ai-solid/src/use-chat.ts
@@ -0,0 +1,132 @@
+import { ChatClient } from '@tanstack/ai-client'
+import type { ModelMessage } from '@tanstack/ai'
+import type { UseChatOptions, UseChatReturn, UIMessage } from './types'
+import {
+ createEffect,
+ createMemo,
+ createSignal,
+ createUniqueId,
+} from 'solid-js'
+
+export function useChat(options: UseChatOptions = {}): UseChatReturn {
+ const hookId = createUniqueId()
+ const clientId = options.id || hookId
+
+ const [messages, setMessages] = createSignal(
+ options.initialMessages || [],
+ )
+ const [isLoading, setIsLoading] = createSignal(false)
+ const [error, setError] = createSignal(undefined)
+
+ // Create ChatClient instance with callbacks to sync state
+ // Note: Connection changes will recreate the client and reset state.
+ // Body and other options are captured at client creation time.
+ // To update connection/body, remount the component or use a key prop.
+ const client = createMemo(() => {
+ return new ChatClient({
+ connection: options.connection,
+ id: clientId,
+ initialMessages: options.initialMessages,
+ body: options.body,
+ onResponse: options.onResponse,
+ onChunk: options.onChunk,
+ onFinish: options.onFinish,
+ onError: options.onError,
+ onToolCall: options.onToolCall,
+ streamProcessor: options.streamProcessor,
+ onMessagesChange: (newMessages: UIMessage[]) => {
+ setMessages(newMessages)
+ },
+ onLoadingChange: (newIsLoading: boolean) => {
+ setIsLoading(newIsLoading)
+ },
+ onErrorChange: (newError: Error | undefined) => {
+ setError(newError)
+ },
+ })
+ // Only recreate when connection changes (most critical option)
+ // Other options are captured at creation time
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [clientId, options.connection])
+
+ // Sync initial messages on mount only
+ // Note: initialMessages are passed to ChatClient constructor, but we also
+ // set them here to ensure React state is in sync
+ createEffect(() => {
+ if (options.initialMessages && options.initialMessages.length > 0) {
+ // Only set if current messages are empty (initial state)
+ if (messages.length === 0) {
+ client().setMessagesManually(options.initialMessages)
+ }
+ }
+ }) // Only run on mount - initialMessages are handled by ChatClient constructor
+
+ // Cleanup on unmount: stop any in-flight requests
+ createEffect(() => {
+ return () => {
+ // Stop any active generation when component unmounts
+ if (isLoading()) {
+ client().stop()
+ }
+ }
+ }, [client, isLoading])
+
+ // Note: Callback options (onResponse, onChunk, onFinish, onError, onToolCall)
+ // are captured at client creation time. Changes to these callbacks require
+ // remounting the component or changing the connection to recreate the client.
+
+ const sendMessage = async (content: string) => {
+ await client().sendMessage(content)
+ }
+
+ const append = async (message: ModelMessage | UIMessage) => {
+ await client().append(message)
+ }
+
+ const reload = async () => {
+ await client().reload()
+ }
+
+ const stop = () => {
+ client().stop()
+ }
+
+ const clear = () => {
+ client().clear()
+ }
+
+ const setMessagesManually = (newMessages: UIMessage[]) => {
+ client().setMessagesManually(newMessages)
+ }
+
+ const addToolResult = async (result: {
+ toolCallId: string
+ tool: string
+ output: any
+ state?: 'output-available' | 'output-error'
+ errorText?: string
+ }) => {
+ await client().addToolResult(result)
+ }
+
+ const addToolApprovalResponse = async (response: {
+ id: string
+ approved: boolean
+ }) => {
+ await client().addToolApprovalResponse(response)
+ }
+
+ return {
+ messages,
+ sendMessage,
+ append,
+ reload,
+ stop,
+ isLoading,
+ error,
+ setMessages: setMessagesManually,
+ clear,
+ addToolResult,
+ addToolApprovalResponse,
+ }
+}
diff --git a/packages/typescript/ai-solid/tests/test-utils.ts b/packages/typescript/ai-solid/tests/test-utils.ts
new file mode 100644
index 000000000..ff1295d5f
--- /dev/null
+++ b/packages/typescript/ai-solid/tests/test-utils.ts
@@ -0,0 +1,29 @@
+// Re-export test utilities from ai-client
+export {
+ createMockConnectionAdapter,
+ createTextChunks,
+ createToolCallChunks,
+ type MockConnectionAdapterOptions,
+} from '@tanstack/ai-client/tests/test-utils'
+
+import { renderHook, type RenderHookResult } from '@testing-library/react'
+import type { UseChatOptions, UseChatReturn } from '../src/types'
+import { useChat } from '../src/use-chat'
+
+/**
+ * Render the useChat hook with testing utilities
+ *
+ * @example
+ * ```typescript
+ * const { result } = renderUseChat({
+ * connection: createMockConnectionAdapter({ chunks: [...] })
+ * });
+ *
+ * await result.current.sendMessage("Hello");
+ * ```
+ */
+export function renderUseChat(
+ options?: UseChatOptions,
+): RenderHookResult {
+ return renderHook(() => useChat(options))
+}
diff --git a/packages/typescript/ai-solid/tests/use-chat.test.ts b/packages/typescript/ai-solid/tests/use-chat.test.ts
new file mode 100644
index 000000000..e5a009a34
--- /dev/null
+++ b/packages/typescript/ai-solid/tests/use-chat.test.ts
@@ -0,0 +1,1185 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest'
+import { waitFor } from '@testing-library/react'
+import { useChat } from '../src/use-chat'
+import {
+ renderUseChat,
+ createMockConnectionAdapter,
+ createTextChunks,
+ createToolCallChunks,
+} from './test-utils'
+import type { UIMessage } from '../src/types'
+import type { ModelMessage } from '@tanstack/ai'
+
+describe('useChat', () => {
+ describe('initialization', () => {
+ it('should initialize with default state', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result } = renderUseChat({ connection: adapter })
+
+ expect(result.current.messages).toEqual([])
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.error).toBeUndefined()
+ })
+
+ it('should initialize with provided messages', () => {
+ const adapter = createMockConnectionAdapter()
+ const initialMessages: UIMessage[] = [
+ {
+ id: 'msg-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Hello' }],
+ createdAt: new Date(),
+ },
+ ]
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ initialMessages,
+ })
+
+ expect(result.current.messages).toEqual(initialMessages)
+ })
+
+ it('should use provided id', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ id: 'custom-id',
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Message IDs should start with the custom ID
+ const messageId = result.current.messages[0].id
+ expect(messageId).toMatch(/^custom-id-/)
+ })
+
+ it('should generate id if not provided', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Message IDs should have a generated prefix (not "custom-id-")
+ const messageId = result.current.messages[0].id
+ expect(messageId).toBeTruthy()
+ expect(messageId).not.toMatch(/^custom-id-/)
+ })
+
+ it('should maintain client instance across re-renders', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result, rerender } = renderUseChat({ connection: adapter })
+
+ const initialMessages = result.current.messages
+
+ rerender()
+
+ // Client should be the same instance, state should persist
+ expect(result.current.messages).toBe(initialMessages)
+ })
+ })
+
+ describe('state synchronization', () => {
+ it('should update messages via onMessagesChange callback', async () => {
+ const chunks = createTextChunks('Hello, world!')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThanOrEqual(2)
+ })
+
+ const userMessage = result.current.messages.find((m) => m.role === 'user')
+ expect(userMessage).toBeDefined()
+ if (userMessage) {
+ expect(userMessage.parts[0]).toEqual({
+ type: 'text',
+ content: 'Hello',
+ })
+ }
+ })
+
+ it('should update loading state via onLoadingChange callback', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ expect(result.current.isLoading).toBe(false)
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ // Should be loading during send
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ await sendPromise
+
+ // Should not be loading after completion
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false)
+ })
+ })
+
+ it('should update error state via onErrorChange callback', async () => {
+ const error = new Error('Connection failed')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ expect(result.current.error?.message).toBe('Connection failed')
+ })
+
+ it('should persist state across re-renders', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result, rerender } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ const messageCount = result.current.messages.length
+
+ rerender()
+
+ // State should persist after re-render
+ expect(result.current.messages.length).toBe(messageCount)
+ })
+ })
+
+ describe('sendMessage', () => {
+ it('should send a message and append it', async () => {
+ const chunks = createTextChunks('Hello, world!')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ const userMessage = result.current.messages.find((m) => m.role === 'user')
+ expect(userMessage).toBeDefined()
+ if (userMessage) {
+ expect(userMessage.parts[0]).toEqual({
+ type: 'text',
+ content: 'Hello',
+ })
+ }
+ })
+
+ it('should create assistant message from stream chunks', async () => {
+ const chunks = createTextChunks('Hello, world!')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+
+ await waitFor(() => {
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ })
+
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ if (assistantMessage) {
+ const textPart = assistantMessage.parts.find((p) => p.type === 'text')
+ expect(textPart).toBeDefined()
+ if (textPart && textPart.type === 'text') {
+ expect(textPart.content).toBe('Hello, world!')
+ }
+ }
+ })
+
+ it('should not send empty messages', async () => {
+ const adapter = createMockConnectionAdapter()
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('')
+ await result.current.sendMessage(' ')
+
+ expect(result.current.messages.length).toBe(0)
+ })
+
+ it('should not send message while loading', async () => {
+ const adapter = createMockConnectionAdapter({
+ chunks: createTextChunks('Response'),
+ chunkDelay: 100,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const promise1 = result.current.sendMessage('First')
+ const promise2 = result.current.sendMessage('Second')
+
+ await Promise.all([promise1, promise2])
+
+ // Should only have one user message since second was blocked
+ const userMessages = result.current.messages.filter(
+ (m) => m.role === 'user',
+ )
+ expect(userMessages.length).toBe(1)
+ })
+
+ it('should handle errors during sendMessage', async () => {
+ const error = new Error('Network error')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ expect(result.current.error?.message).toBe('Network error')
+ expect(result.current.isLoading).toBe(false)
+ })
+ })
+
+ describe('append', () => {
+ it('should append a UIMessage', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const message: UIMessage = {
+ id: 'user-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Hello' }],
+ createdAt: new Date(),
+ }
+
+ await result.current.append(message)
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ expect(result.current.messages[0].id).toBe('user-1')
+ })
+
+ it('should convert and append a ModelMessage', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const modelMessage: ModelMessage = {
+ role: 'user',
+ content: 'Hello from model',
+ }
+
+ await result.current.append(modelMessage)
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ expect(result.current.messages[0].role).toBe('user')
+ expect(result.current.messages[0].parts[0]).toEqual({
+ type: 'text',
+ content: 'Hello from model',
+ })
+ })
+
+ it('should handle errors during append', async () => {
+ const error = new Error('Append failed')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const message: UIMessage = {
+ id: 'msg-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Hello' }],
+ createdAt: new Date(),
+ }
+
+ await result.current.append(message)
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ expect(result.current.error?.message).toBe('Append failed')
+ })
+ })
+
+ describe('reload', () => {
+ it('should reload the last assistant message', async () => {
+ const chunks1 = createTextChunks('First response')
+ const chunks2 = createTextChunks('Second response')
+ let callCount = 0
+
+ const adapter = createMockConnectionAdapter({
+ chunks: chunks1,
+ onConnect: () => {
+ callCount++
+ // Return different chunks on second call
+ if (callCount === 2) {
+ return chunks2
+ }
+ },
+ })
+
+ // Create a new adapter for the second call
+ const adapter2 = createMockConnectionAdapter({ chunks: chunks2 })
+ const { result, rerender } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+
+ await waitFor(() => {
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ })
+
+ const firstAssistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ const firstContent =
+ firstAssistantMessage?.parts.find((p) => p.type === 'text')?.type ===
+ 'text'
+ ? (firstAssistantMessage.parts.find((p) => p.type === 'text') as any)
+ .content
+ : ''
+
+ // Reload with new adapter
+ rerender({ connection: adapter2 })
+ await result.current.reload()
+
+ await waitFor(() => {
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ })
+
+ // Should have reloaded (though content might be same if adapter doesn't change)
+ const messagesAfterReload = result.current.messages
+ expect(messagesAfterReload.length).toBeGreaterThan(0)
+ })
+
+ it('should maintain conversation history after reload', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('First')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThanOrEqual(2)
+ })
+
+ const messageCountBeforeReload = result.current.messages.length
+
+ await result.current.reload()
+
+ await waitFor(() => {
+ // Should still have the same number of messages (user + assistant)
+ expect(result.current.messages.length).toBeGreaterThanOrEqual(2)
+ })
+
+ // History should be maintained
+ expect(result.current.messages.length).toBeGreaterThanOrEqual(
+ messageCountBeforeReload,
+ )
+ })
+
+ it('should handle errors during reload', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThanOrEqual(2)
+ })
+
+ // Create error adapter for reload
+ const errorAdapter = createMockConnectionAdapter({
+ shouldError: true,
+ error: new Error('Reload failed'),
+ })
+
+ // Note: We can't easily change the adapter after creation,
+ // so this test verifies error handling in general
+ // The actual error would come from the connection adapter
+ expect(result.current.reload).toBeDefined()
+ })
+ })
+
+ describe('stop', () => {
+ it('should stop current generation', async () => {
+ const chunks = createTextChunks('Long response that will be stopped')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ // Wait for loading to start
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ // Stop the generation
+ result.current.stop()
+
+ await sendPromise
+
+ // Should eventually stop loading
+ await waitFor(
+ () => {
+ expect(result.current.isLoading).toBe(false)
+ },
+ { timeout: 1000 },
+ )
+ })
+
+ it('should be safe to call multiple times', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result } = renderUseChat({ connection: adapter })
+
+ // Should not throw
+ result.current.stop()
+ result.current.stop()
+ result.current.stop()
+
+ expect(result.current.isLoading).toBe(false)
+ })
+
+ it('should clear loading state when stopped', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ result.current.stop()
+
+ await waitFor(
+ () => {
+ expect(result.current.isLoading).toBe(false)
+ },
+ { timeout: 1000 },
+ )
+
+ await sendPromise.catch(() => {
+ // Ignore errors from stopped request
+ })
+ })
+ })
+
+ describe('clear', () => {
+ it('should clear all messages', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ result.current.clear()
+
+ expect(result.current.messages).toEqual([])
+ })
+
+ it('should reset to initial state', async () => {
+ const initialMessages: UIMessage[] = [
+ {
+ id: 'msg-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Initial' }],
+ createdAt: new Date(),
+ },
+ ]
+
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({
+ connection: adapter,
+ initialMessages,
+ })
+
+ await result.current.sendMessage('Hello')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(
+ initialMessages.length,
+ )
+ })
+
+ result.current.clear()
+
+ // Should clear all messages, not reset to initial
+ expect(result.current.messages).toEqual([])
+ })
+
+ it('should maintain client instance after clear', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ result.current.clear()
+
+ // Should still be able to send messages
+ await result.current.sendMessage('New message')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+ })
+ })
+
+ describe('setMessages', () => {
+ it('should manually set messages', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result } = renderUseChat({ connection: adapter })
+
+ const newMessages: UIMessage[] = [
+ {
+ id: 'msg-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Manual' }],
+ createdAt: new Date(),
+ },
+ ]
+
+ result.current.setMessages(newMessages)
+
+ expect(result.current.messages).toEqual(newMessages)
+ })
+
+ it('should update state immediately', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result } = renderUseChat({ connection: adapter })
+
+ expect(result.current.messages).toEqual([])
+
+ const newMessages: UIMessage[] = [
+ {
+ id: 'msg-1',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Immediate' }],
+ createdAt: new Date(),
+ },
+ ]
+
+ result.current.setMessages(newMessages)
+
+ // Should be updated synchronously
+ expect(result.current.messages).toEqual(newMessages)
+ })
+
+ it('should replace all existing messages', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Hello')
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ const originalCount = result.current.messages.length
+
+ const newMessages: UIMessage[] = [
+ {
+ id: 'msg-new',
+ role: 'user',
+ parts: [{ type: 'text', content: 'Replaced' }],
+ createdAt: new Date(),
+ },
+ ]
+
+ result.current.setMessages(newMessages)
+
+ expect(result.current.messages).toEqual(newMessages)
+ expect(result.current.messages.length).toBe(1)
+ expect(result.current.messages.length).not.toBe(originalCount)
+ })
+ })
+
+ describe('callbacks', () => {
+ it('should call onChunk callback when chunks are received', async () => {
+ const chunks = createTextChunks('Hello')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const onChunk = vi.fn()
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ onChunk,
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(onChunk).toHaveBeenCalled()
+ })
+
+ // Should have been called for each chunk
+ expect(onChunk.mock.calls.length).toBeGreaterThan(0)
+ })
+
+ it('should call onFinish callback when response finishes', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const onFinish = vi.fn()
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ onFinish,
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(onFinish).toHaveBeenCalled()
+ })
+
+ const finishedMessage = onFinish.mock.calls[0][0]
+ expect(finishedMessage).toBeDefined()
+ expect(finishedMessage.role).toBe('assistant')
+ })
+
+ it('should call onError callback when error occurs', async () => {
+ const error = new Error('Test error')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const onError = vi.fn()
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ onError,
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(onError).toHaveBeenCalled()
+ })
+
+ expect(onError.mock.calls[0][0].message).toBe('Test error')
+ })
+
+ it('should call onResponse callback when response is received', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({ chunks })
+ const onResponse = vi.fn()
+
+ const { result } = renderUseChat({
+ connection: adapter,
+ onResponse,
+ })
+
+ await result.current.sendMessage('Test')
+
+ // onResponse may or may not be called depending on adapter implementation
+ // This test verifies the callback is passed through
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+ })
+ })
+
+ describe('edge cases and error handling', () => {
+ describe('options changes', () => {
+ it('should maintain client instance when options change', () => {
+ const adapter1 = createMockConnectionAdapter()
+ const { result, rerender } = renderUseChat({ connection: adapter1 })
+
+ const initialMessages = result.current.messages
+
+ const adapter2 = createMockConnectionAdapter()
+ rerender({ connection: adapter2 })
+
+ // Client instance should persist (current implementation doesn't update)
+ // This documents current behavior - options changes don't update client
+ expect(result.current.messages).toBe(initialMessages)
+ })
+
+ it('should handle body changes', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result, rerender } = renderUseChat({
+ connection: adapter,
+ body: { userId: '123' },
+ })
+
+ rerender({
+ connection: adapter,
+ body: { userId: '456' },
+ })
+
+ // Should not throw
+ expect(result.current).toBeDefined()
+ })
+
+ it('should handle callback changes', () => {
+ const adapter = createMockConnectionAdapter()
+ const onChunk1 = vi.fn()
+ const { result, rerender } = renderUseChat({
+ connection: adapter,
+ onChunk: onChunk1,
+ })
+
+ const onChunk2 = vi.fn()
+ rerender({
+ connection: adapter,
+ onChunk: onChunk2,
+ })
+
+ // Should not throw
+ expect(result.current).toBeDefined()
+ })
+ })
+
+ describe('unmount behavior', () => {
+ it('should not update state after unmount', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 100,
+ })
+ const { result, unmount } = renderUseChat({ connection: adapter })
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ // Unmount before completion
+ unmount()
+
+ await sendPromise.catch(() => {
+ // Ignore errors
+ })
+
+ // State updates after unmount should be ignored (React handles this)
+ // This test documents the expected behavior
+ expect(result.current).toBeDefined()
+ })
+
+ it('should stop loading on unmount if active', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 100,
+ })
+ const { result, unmount } = renderUseChat({ connection: adapter })
+
+ result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ unmount()
+
+ // After unmount, React will clean up
+ // The actual cleanup is handled by React's lifecycle
+ expect(result.current.isLoading).toBe(true) // Still true in test, but component is unmounted
+ })
+ })
+
+ describe('concurrent operations', () => {
+ it('should handle multiple sendMessage calls', async () => {
+ const adapter = createMockConnectionAdapter({
+ chunks: createTextChunks('Response'),
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const promise1 = result.current.sendMessage('First')
+ const promise2 = result.current.sendMessage('Second')
+
+ await Promise.all([promise1, promise2])
+
+ // Should only have one user message (second should be blocked)
+ const userMessages = result.current.messages.filter(
+ (m) => m.role === 'user',
+ )
+ expect(userMessages.length).toBe(1)
+ })
+
+ it('should handle stop during sendMessage', async () => {
+ const chunks = createTextChunks('Long response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ result.current.stop()
+
+ await waitFor(
+ () => {
+ expect(result.current.isLoading).toBe(false)
+ },
+ { timeout: 1000 },
+ )
+
+ await sendPromise.catch(() => {
+ // Ignore errors from stopped request
+ })
+ })
+
+ it('should handle reload during active stream', async () => {
+ const chunks = createTextChunks('Response')
+ const adapter = createMockConnectionAdapter({
+ chunks,
+ chunkDelay: 50,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ const sendPromise = result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(true)
+ })
+
+ // Try to reload while sending
+ const reloadPromise = result.current.reload()
+
+ await Promise.allSettled([sendPromise, reloadPromise])
+
+ // Should eventually complete
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false)
+ })
+ })
+ })
+
+ describe('error scenarios', () => {
+ it('should handle network errors', async () => {
+ const error = new Error('Network request failed')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ expect(result.current.error?.message).toBe('Network request failed')
+ expect(result.current.isLoading).toBe(false)
+ })
+
+ it('should handle stream errors', async () => {
+ const error = new Error('Stream error')
+ const adapter = createMockConnectionAdapter({
+ shouldError: true,
+ error,
+ })
+ const { result } = renderUseChat({ connection: adapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ expect(result.current.error?.message).toBe('Stream error')
+ })
+
+ it('should clear error on successful operation', async () => {
+ const errorAdapter = createMockConnectionAdapter({
+ shouldError: true,
+ error: new Error('Initial error'),
+ })
+ const { result, rerender } = renderUseChat({
+ connection: errorAdapter,
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.error).toBeDefined()
+ })
+
+ // Switch to working adapter
+ const workingAdapter = createMockConnectionAdapter({
+ chunks: createTextChunks('Success'),
+ })
+ rerender({ connection: workingAdapter })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ // Error should be cleared on success
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+ })
+
+ it('should handle tool execution errors', async () => {
+ const toolCalls = createToolCallChunks([
+ { id: 'tool-1', name: 'testTool', arguments: '{"param": "value"}' },
+ ])
+ const adapter = createMockConnectionAdapter({ chunks: toolCalls })
+ const { result } = renderUseChat({
+ connection: adapter,
+ onToolCall: async () => {
+ throw new Error('Tool execution failed')
+ },
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ expect(result.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Tool error should be handled
+ expect(result.current.error).toBeDefined()
+ })
+ })
+
+ describe('multiple hook instances', () => {
+ it('should maintain independent state per instance', async () => {
+ const adapter1 = createMockConnectionAdapter({
+ chunks: createTextChunks('Response 1'),
+ })
+ const adapter2 = createMockConnectionAdapter({
+ chunks: createTextChunks('Response 2'),
+ })
+
+ const { result: result1 } = renderUseChat({
+ connection: adapter1,
+ id: 'chat-1',
+ })
+ const { result: result2 } = renderUseChat({
+ connection: adapter2,
+ id: 'chat-2',
+ })
+
+ await result1.current.sendMessage('Hello 1')
+ await result2.current.sendMessage('Hello 2')
+
+ await waitFor(() => {
+ expect(result1.current.messages.length).toBeGreaterThan(0)
+ expect(result2.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Each instance should have its own messages
+ expect(result1.current.messages.length).toBe(
+ result2.current.messages.length,
+ )
+ expect(result1.current.messages[0].parts[0]).not.toEqual(
+ result2.current.messages[0].parts[0],
+ )
+ })
+
+ it('should handle different IDs correctly', () => {
+ const adapter = createMockConnectionAdapter()
+ const { result: result1 } = renderUseChat({
+ connection: adapter,
+ id: 'chat-1',
+ })
+ const { result: result2 } = renderUseChat({
+ connection: adapter,
+ id: 'chat-2',
+ })
+
+ // Should not interfere with each other
+ expect(result1.current.messages).toEqual([])
+ expect(result2.current.messages).toEqual([])
+ })
+
+ it('should not have cross-contamination', async () => {
+ const adapter1 = createMockConnectionAdapter({
+ chunks: createTextChunks('One'),
+ })
+ const adapter2 = createMockConnectionAdapter({
+ chunks: createTextChunks('Two'),
+ })
+
+ const { result: result1 } = renderUseChat({
+ connection: adapter1,
+ })
+ const { result: result2 } = renderUseChat({
+ connection: adapter2,
+ })
+
+ await result1.current.sendMessage('Message 1')
+ await waitFor(() => {
+ expect(result1.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Second instance should still be empty
+ expect(result2.current.messages.length).toBe(0)
+
+ await result2.current.sendMessage('Message 2')
+ await waitFor(() => {
+ expect(result2.current.messages.length).toBeGreaterThan(0)
+ })
+
+ // Both should have messages, but different ones
+ expect(result1.current.messages.length).toBeGreaterThan(0)
+ expect(result2.current.messages.length).toBeGreaterThan(0)
+ expect(result1.current.messages[0].parts[0]).not.toEqual(
+ result2.current.messages[0].parts[0],
+ )
+ })
+ })
+
+ describe('tool operations', () => {
+ it('should handle addToolResult', async () => {
+ const toolCalls = createToolCallChunks([
+ { id: 'tool-1', name: 'testTool', arguments: '{"param": "value"}' },
+ ])
+ const adapter = createMockConnectionAdapter({ chunks: toolCalls })
+ const { result } = renderUseChat({
+ connection: adapter,
+ onToolCall: async () => ({ result: 'success' }),
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ })
+
+ // Find tool call
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ const toolCallPart = assistantMessage?.parts.find(
+ (p) => p.type === 'tool-call',
+ )
+
+ if (toolCallPart && toolCallPart.type === 'tool-call') {
+ await result.current.addToolResult({
+ toolCallId: toolCallPart.id,
+ tool: toolCallPart.name,
+ output: { result: 'manual' },
+ })
+
+ // Should update the tool call
+ await waitFor(() => {
+ const updatedMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ const updatedToolCall = updatedMessage?.parts.find(
+ (p) => p.type === 'tool-call' && p.id === toolCallPart.id,
+ )
+ expect(updatedToolCall).toBeDefined()
+ })
+ }
+ })
+
+ it('should handle addToolApprovalResponse', async () => {
+ const toolCalls = createToolCallChunks([
+ { id: 'tool-1', name: 'testTool', arguments: '{"param": "value"}' },
+ ])
+ const adapter = createMockConnectionAdapter({ chunks: toolCalls })
+ const { result } = renderUseChat({
+ connection: adapter,
+ })
+
+ await result.current.sendMessage('Test')
+
+ await waitFor(() => {
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ expect(assistantMessage).toBeDefined()
+ })
+
+ // Find tool call with approval
+ const assistantMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ const toolCallPart = assistantMessage?.parts.find(
+ (p) => p.type === 'tool-call' && p.approval,
+ )
+
+ if (
+ toolCallPart &&
+ toolCallPart.type === 'tool-call' &&
+ toolCallPart.approval
+ ) {
+ await result.current.addToolApprovalResponse({
+ id: toolCallPart.approval.id,
+ approved: true,
+ })
+
+ // Should update approval state
+ await waitFor(() => {
+ const updatedMessage = result.current.messages.find(
+ (m) => m.role === 'assistant',
+ )
+ const updatedToolCall = updatedMessage?.parts.find(
+ (p) => p.type === 'tool-call' && p.id === toolCallPart.id,
+ )
+ if (updatedToolCall && updatedToolCall.type === 'tool-call') {
+ expect(updatedToolCall.approval?.approved).toBe(true)
+ }
+ })
+ }
+ })
+ })
+ })
+})
diff --git a/packages/typescript/ai-solid/tsconfig.json b/packages/typescript/ai-solid/tsconfig.json
new file mode 100644
index 000000000..e8b948011
--- /dev/null
+++ b/packages/typescript/ai-solid/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src",
+ "jsx": "react-jsx",
+ "lib": ["ES2022", "DOM"]
+ },
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "exclude": ["node_modules", "dist", "**/*.config.ts"],
+ "references": [{ "path": "../ai" }, { "path": "../ai-client" }]
+}
diff --git a/packages/typescript/ai-solid/tsdown.config.ts b/packages/typescript/ai-solid/tsdown.config.ts
new file mode 100644
index 000000000..6d458495b
--- /dev/null
+++ b/packages/typescript/ai-solid/tsdown.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'tsdown'
+
+export default defineConfig({
+ entry: ['./src/index.ts'],
+ format: ['esm'],
+ unbundle: true,
+ dts: true,
+ sourcemap: true,
+ clean: true,
+ minify: false,
+ external: ['react'],
+})
diff --git a/packages/typescript/ai-solid/vitest.config.ts b/packages/typescript/ai-solid/vitest.config.ts
new file mode 100644
index 000000000..6733c18ab
--- /dev/null
+++ b/packages/typescript/ai-solid/vitest.config.ts
@@ -0,0 +1,35 @@
+import { defineConfig } from 'vitest/config'
+import { resolve } from 'path'
+import { fileURLToPath } from 'url'
+
+const __dirname = fileURLToPath(new URL('.', import.meta.url))
+
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ include: ['tests/**/*.test.ts', 'tests/**/*.test.tsx'],
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'json', 'html', 'lcov'],
+ exclude: [
+ 'node_modules/',
+ 'dist/',
+ 'tests/',
+ '**/*.test.ts',
+ '**/*.test.tsx',
+ '**/*.config.ts',
+ '**/types.ts',
+ ],
+ include: ['src/**/*.ts'],
+ },
+ },
+ resolve: {
+ alias: {
+ '@tanstack/ai/event-client': resolve(
+ __dirname,
+ '../ai/src/event-client.ts',
+ ),
+ },
+ },
+})
diff --git a/packages/typescript/smoke-tests/e2e/package.json b/packages/typescript/smoke-tests/e2e/package.json
index 17f705df7..9aec070c5 100644
--- a/packages/typescript/smoke-tests/e2e/package.json
+++ b/packages/typescript/smoke-tests/e2e/package.json
@@ -13,9 +13,6 @@
"postinstall": "npx playwright install --with-deps chromium"
},
"dependencies": {
- "@ai-sdk/openai": "^2.0.52",
- "@ai-sdk/provider": "^2.0.0",
- "@ai-sdk/provider-utils": "^3.0.12",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/ai": "workspace:*",
"@tanstack/ai-client": "workspace:*",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 06198bfba..ba3766f72 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -79,17 +79,99 @@ importers:
examples/python-fastapi: {}
- examples/ts-chat:
- dependencies:
- '@ai-sdk/openai':
- specifier: ^2.0.73
- version: 2.0.73(zod@4.1.13)
- '@ai-sdk/provider':
- specifier: ^2.0.0
- version: 2.0.0
- '@ai-sdk/provider-utils':
- specifier: ^3.0.17
- version: 3.0.17(zod@4.1.13)
+ examples/ts-group-chat:
+ dependencies:
+ '@tailwindcss/vite':
+ specifier: ^4.1.17
+ version: 4.1.17(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/ai':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai
+ '@tanstack/ai-anthropic':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-anthropic
+ '@tanstack/ai-client':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-client
+ '@tanstack/ai-react':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-react
+ '@tanstack/react-devtools':
+ specifier: ^0.8.2
+ version: 0.8.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(csstype@3.2.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)
+ '@tanstack/react-router':
+ specifier: ^1.139.7
+ version: 1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@tanstack/react-router-devtools':
+ specifier: ^1.139.7
+ version: 1.139.7(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.139.10)(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ '@tanstack/react-router-ssr-query':
+ specifier: ^1.139.7
+ version: 1.139.7(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.5(react@19.2.0))(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.139.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@tanstack/react-start':
+ specifier: ^1.139.8
+ version: 1.139.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/router-plugin':
+ specifier: ^1.139.7
+ version: 1.139.7(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ capnweb:
+ specifier: ^0.1.0
+ version: 0.1.0
+ react:
+ specifier: ^19.2.0
+ version: 19.2.0
+ react-dom:
+ specifier: ^19.2.0
+ version: 19.2.0(react@19.2.0)
+ tailwindcss:
+ specifier: ^4.1.17
+ version: 4.1.17
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.9.3)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ ws:
+ specifier: ^8.18.3
+ version: 8.18.3
+ devDependencies:
+ '@testing-library/dom':
+ specifier: ^10.4.1
+ version: 10.4.1
+ '@testing-library/react':
+ specifier: ^16.3.0
+ version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@types/node':
+ specifier: ^24.10.1
+ version: 24.10.1
+ '@types/react':
+ specifier: ^19.2.7
+ version: 19.2.7
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.7)
+ '@types/ws':
+ specifier: ^8.18.1
+ version: 8.18.1
+ '@vitejs/plugin-react':
+ specifier: ^5.1.1
+ version: 5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ jsdom:
+ specifier: ^27.2.0
+ version: 27.2.0(postcss@8.5.6)
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^7.2.4
+ version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vitest:
+ specifier: ^4.0.14
+ version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.2.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ web-vitals:
+ specifier: ^5.1.0
+ version: 5.1.0
+
+ examples/ts-react-chat:
+ dependencies:
'@tailwindcss/vite':
specifier: ^4.1.17
version: 4.1.17(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
@@ -221,7 +303,7 @@ importers:
specifier: ^5.1.0
version: 5.1.0
- examples/ts-group-chat:
+ examples/ts-solid-chat:
dependencies:
'@tailwindcss/vite':
specifier: ^4.1.17
@@ -235,67 +317,100 @@ importers:
'@tanstack/ai-client':
specifier: workspace:*
version: link:../../packages/typescript/ai-client
- '@tanstack/ai-react':
+ '@tanstack/ai-devtools-core':
specifier: workspace:*
- version: link:../../packages/typescript/ai-react
- '@tanstack/react-devtools':
- specifier: ^0.8.2
- version: 0.8.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(csstype@3.2.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)
- '@tanstack/react-router':
- specifier: ^1.139.7
- version: 1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
- '@tanstack/react-router-devtools':
- specifier: ^1.139.7
- version: 1.139.7(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.139.10)(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
- '@tanstack/react-router-ssr-query':
- specifier: ^1.139.7
- version: 1.139.7(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.5(react@19.2.0))(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.139.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
- '@tanstack/react-start':
- specifier: ^1.139.8
- version: 1.139.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ version: link:../../packages/typescript/ai-devtools
+ '@tanstack/ai-gemini':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-gemini
+ '@tanstack/ai-ollama':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-ollama
+ '@tanstack/ai-openai':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-openai
+ '@tanstack/ai-solid':
+ specifier: workspace:*
+ version: link:../../packages/typescript/ai-solid
+ '@tanstack/nitro-v2-vite-plugin':
+ specifier: ^1.139.0
+ version: 1.139.0(rolldown@1.0.0-beta.51)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
'@tanstack/router-plugin':
specifier: ^1.139.7
- version: 1.139.7(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
- capnweb:
- specifier: ^0.1.0
- version: 0.1.0
- react:
- specifier: ^19.2.0
- version: 19.2.0
- react-dom:
- specifier: ^19.2.0
- version: 19.2.0(react@19.2.0)
+ version: 1.139.10(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/solid-ai-devtools':
+ specifier: workspace:*
+ version: link:../../packages/typescript/solid-ai-devtools
+ '@tanstack/solid-devtools':
+ specifier: ^0.7.15
+ version: 0.7.15(csstype@3.2.3)(solid-js@1.9.10)
+ '@tanstack/solid-router':
+ specifier: ^1.132.0
+ version: 1.139.10(solid-js@1.9.10)
+ '@tanstack/solid-router-devtools':
+ specifier: ^1.132.0
+ version: 1.139.10(@tanstack/router-core@1.139.10)(@tanstack/solid-router@1.139.10(solid-js@1.9.10))(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ '@tanstack/solid-router-ssr-query':
+ specifier: ^1.131.7
+ version: 1.139.10(@tanstack/query-core@5.90.11)(@tanstack/router-core@1.139.10)(@tanstack/solid-query@5.90.14(solid-js@1.9.10))(@tanstack/solid-router@1.139.10(solid-js@1.9.10))(eslint@9.39.1(jiti@2.6.1))(solid-js@1.9.10)(typescript@5.9.3)
+ '@tanstack/solid-start':
+ specifier: ^1.132.0
+ version: 1.139.10(solid-js@1.9.10)(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/solid-store':
+ specifier: ^0.7.0
+ version: 0.7.7(solid-js@1.9.10)
+ '@tanstack/store':
+ specifier: ^0.8.0
+ version: 0.8.0
+ highlight.js:
+ specifier: ^11.11.1
+ version: 11.11.1
+ lucide-solid:
+ specifier: ^0.554.0
+ version: 0.554.0(solid-js@1.9.10)
+ rehype-highlight:
+ specifier: ^7.0.2
+ version: 7.0.2
+ rehype-raw:
+ specifier: ^7.0.0
+ version: 7.0.0
+ rehype-sanitize:
+ specifier: ^6.0.0
+ version: 6.0.0
+ remark-gfm:
+ specifier: ^4.0.1
+ version: 4.0.1
+ solid-js:
+ specifier: ^1.9.10
+ version: 1.9.10
+ solid-markdown:
+ specifier: ^2.0.14
+ version: 2.0.14(solid-js@1.9.10)
tailwindcss:
specifier: ^4.1.17
version: 4.1.17
vite-tsconfig-paths:
specifier: ^5.1.4
version: 5.1.4(typescript@5.9.3)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
- ws:
- specifier: ^8.18.3
- version: 8.18.3
+ zod:
+ specifier: ^4.1.13
+ version: 4.1.13
devDependencies:
+ '@solidjs/testing-library':
+ specifier: ^0.8.10
+ version: 0.8.10(solid-js@1.9.10)
+ '@tanstack/devtools-event-client':
+ specifier: ^0.3.5
+ version: 0.3.5
+ '@tanstack/devtools-vite':
+ specifier: ^0.3.11
+ version: 0.3.11(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
'@testing-library/dom':
specifier: ^10.4.1
version: 10.4.1
- '@testing-library/react':
- specifier: ^16.3.0
- version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@types/node':
specifier: ^24.10.1
version: 24.10.1
- '@types/react':
- specifier: ^19.2.7
- version: 19.2.7
- '@types/react-dom':
- specifier: ^19.2.3
- version: 19.2.3(@types/react@19.2.7)
- '@types/ws':
- specifier: ^8.18.1
- version: 8.18.1
- '@vitejs/plugin-react':
- specifier: ^5.1.1
- version: 5.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
jsdom:
specifier: ^27.2.0
version: 27.2.0(postcss@8.5.6)
@@ -305,6 +420,9 @@ importers:
vite:
specifier: ^7.2.4
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vite-plugin-solid:
+ specifier: ^2.11.10
+ version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
vitest:
specifier: ^4.0.14
version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.2.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
@@ -511,6 +629,40 @@ importers:
specifier: ^7.2.4
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ packages/typescript/ai-solid:
+ dependencies:
+ '@tanstack/ai':
+ specifier: workspace:*
+ version: link:../ai
+ '@tanstack/ai-client':
+ specifier: workspace:*
+ version: link:../ai-client
+ devDependencies:
+ '@solidjs/testing-library':
+ specifier: ^0.8.10
+ version: 0.8.10(solid-js@1.9.10)
+ '@types/node':
+ specifier: ^24.10.1
+ version: 24.10.1
+ '@vitest/coverage-v8':
+ specifier: 4.0.14
+ version: 4.0.14(vitest@4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.2.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ jsdom:
+ specifier: ^27.2.0
+ version: 27.2.0(postcss@8.5.6)
+ solid-js:
+ specifier: ^1.9.10
+ version: 1.9.10
+ tsdown:
+ specifier: ^0.15.9
+ version: 0.15.12(oxc-resolver@11.14.0)(publint@0.3.15)(typescript@5.9.3)
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
+ vitest:
+ specifier: ^4.0.14
+ version: 4.0.14(@types/node@24.10.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.2.0(postcss@8.5.6))(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+
packages/typescript/react-ai-devtools:
dependencies:
'@tanstack/ai-devtools-core':
@@ -533,37 +685,6 @@ importers:
specifier: ^7.2.4
version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
- packages/typescript/smoke-tests/adapters:
- dependencies:
- '@tanstack/ai':
- specifier: workspace:*
- version: link:../../ai
- '@tanstack/ai-anthropic':
- specifier: workspace:*
- version: link:../../ai-anthropic
- '@tanstack/ai-gemini':
- specifier: workspace:*
- version: link:../../ai-gemini
- '@tanstack/ai-ollama':
- specifier: workspace:*
- version: link:../../ai-ollama
- '@tanstack/ai-openai':
- specifier: workspace:*
- version: link:../../ai-openai
- devDependencies:
- '@types/node':
- specifier: ^24.10.1
- version: 24.10.1
- dotenv:
- specifier: ^17.2.3
- version: 17.2.3
- tsx:
- specifier: ^4.20.6
- version: 4.20.6
- typescript:
- specifier: 5.9.3
- version: 5.9.3
-
packages/typescript/solid-ai-devtools:
dependencies:
'@tanstack/ai-devtools-core':
@@ -594,22 +715,6 @@ packages:
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
- '@ai-sdk/openai@2.0.73':
- resolution: {integrity: sha512-TOGoxkeJMgcxq80ZfyCAqKcfvGPgFsEQEK8SgI/w3He0fqi16KlnZVMexOlg8HOLumF6szweg/YrOmQixsPVGw==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.25.76 || ^4.1.8
-
- '@ai-sdk/provider-utils@3.0.17':
- resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==}
- engines: {node: '>=18'}
- peerDependencies:
- zod: ^3.25.76 || ^4.1.8
-
- '@ai-sdk/provider@2.0.0':
- resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
- engines: {node: '>=18'}
-
'@anthropic-ai/sdk@0.71.0':
resolution: {integrity: sha512-go1XeWXmpxuiTkosSXpb8tokLk2ZLkIRcXpbWVwJM6gH5OBtHOVsfPfGuqI1oW7RRt4qc59EmYbrXRZ0Ng06Jw==}
hasBin: true
@@ -1230,6 +1335,9 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
+ '@nothing-but/utils@0.17.0':
+ resolution: {integrity: sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ==}
+
'@nx/nx-darwin-arm64@22.1.2':
resolution: {integrity: sha512-xT6U9oRjze9QTLp8ieoNOno6GHA5S2R36tzergMfTevCTnpJBE0GX8vtI6fmcK3NkVmbdPI9Vb/FmBPcvD9eEQ==}
cpu: [arm64]
@@ -1296,6 +1404,9 @@ packages:
resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==}
engines: {node: '>=20.0'}
+ '@oxc-project/types@0.95.0':
+ resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==}
+
'@oxc-project/types@0.98.0':
resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==}
@@ -1499,83 +1610,169 @@ packages:
resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==}
engines: {node: '>=18'}
+ '@quansync/fs@0.1.5':
+ resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==}
+
+ '@rolldown/binding-android-arm64@1.0.0-beta.45':
+ resolution: {integrity: sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
'@rolldown/binding-android-arm64@1.0.0-beta.51':
resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.45':
+ resolution: {integrity: sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
'@rolldown/binding-darwin-arm64@1.0.0-beta.51':
resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
+ '@rolldown/binding-darwin-x64@1.0.0-beta.45':
+ resolution: {integrity: sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
'@rolldown/binding-darwin-x64@1.0.0-beta.51':
resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.45':
+ resolution: {integrity: sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
'@rolldown/binding-freebsd-x64@1.0.0-beta.51':
resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45':
+ resolution: {integrity: sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45':
+ resolution: {integrity: sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45':
+ resolution: {integrity: sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45':
+ resolution: {integrity: sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.45':
+ resolution: {integrity: sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+
'@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.45':
+ resolution: {integrity: sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
'@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.45':
+ resolution: {integrity: sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
'@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45':
+ resolution: {integrity: sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45':
+ resolution: {integrity: sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45':
+ resolution: {integrity: sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1585,6 +1782,9 @@ packages:
'@rolldown/pluginutils@1.0.0-beta.40':
resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==}
+ '@rolldown/pluginutils@1.0.0-beta.45':
+ resolution: {integrity: sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==}
+
'@rolldown/pluginutils@1.0.0-beta.47':
resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==}
@@ -1838,6 +2038,26 @@ packages:
peerDependencies:
size-limit: 11.2.0
+ '@solid-devtools/debugger@0.28.1':
+ resolution: {integrity: sha512-6qIUI6VYkXoRnL8oF5bvh2KgH71qlJ18hNw/mwSyY6v48eb80ZR48/5PDXufUa3q+MBSuYa1uqTMwLewpay9eg==}
+ peerDependencies:
+ solid-js: ^1.9.0
+
+ '@solid-devtools/logger@0.9.11':
+ resolution: {integrity: sha512-THbiY1iQlieL6vdgJc4FIsLe7V8a57hod/Thm8zdKrTkWL88UPZjkBBfM+mVNGusd4OCnAN20tIFBhNnuT1Dew==}
+ peerDependencies:
+ solid-js: ^1.9.0
+
+ '@solid-devtools/shared@0.20.0':
+ resolution: {integrity: sha512-o5TACmUOQsxpzpOKCjbQqGk8wL8PMi+frXG9WNu4Lh3PQVUB6hs95Kl/S8xc++zwcMguUKZJn8h5URUiMOca6Q==}
+ peerDependencies:
+ solid-js: ^1.9.0
+
+ '@solid-primitives/bounds@0.1.3':
+ resolution: {integrity: sha512-UbiyKMdSPmtijcEDnYLQL3zzaejpwWDAJJ4Gt5P0hgVs6A72piov0GyNw7V2SroH7NZFwxlYS22YmOr8A5xc1Q==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solid-primitives/event-listener@2.4.3':
resolution: {integrity: sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg==}
peerDependencies:
@@ -1848,6 +2068,16 @@ packages:
peerDependencies:
solid-js: ^1.6.12
+ '@solid-primitives/media@2.3.3':
+ resolution: {integrity: sha512-hQ4hLOGvfbugQi5Eu1BFWAIJGIAzztq9x0h02xgBGl2l0Jaa3h7tg6bz5tV1NSuNYVGio4rPoa7zVQQLkkx9dA==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
+ '@solid-primitives/refs@1.1.2':
+ resolution: {integrity: sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solid-primitives/resize-observer@2.1.3':
resolution: {integrity: sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ==}
peerDependencies:
@@ -1858,16 +2088,41 @@ packages:
peerDependencies:
solid-js: ^1.6.12
+ '@solid-primitives/scheduled@1.5.2':
+ resolution: {integrity: sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solid-primitives/static-store@0.1.2':
resolution: {integrity: sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw==}
peerDependencies:
solid-js: ^1.6.12
+ '@solid-primitives/styles@0.1.2':
+ resolution: {integrity: sha512-7iX5K+J5b1PRrbgw3Ki92uvU2LgQ0Kd/QMsrAZxDg5dpUBwMyTijZkA3bbs1ikZsT1oQhS41bTyKbjrXeU0Awg==}
+ peerDependencies:
+ solid-js: ^1.6.12
+
'@solid-primitives/utils@6.3.2':
resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==}
peerDependencies:
solid-js: ^1.6.12
+ '@solidjs/meta@0.29.4':
+ resolution: {integrity: sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g==}
+ peerDependencies:
+ solid-js: '>=1.8.4'
+
+ '@solidjs/testing-library@0.8.10':
+ resolution: {integrity: sha512-qdeuIerwyq7oQTIrrKvV0aL9aFeuwTd86VYD3afdq5HYEwoox1OBTJy4y8A3TFZr8oAR0nujYgCzY/8wgHGfeQ==}
+ engines: {node: '>= 14'}
+ peerDependencies:
+ '@solidjs/router': '>=0.9.0'
+ solid-js: '>=1.0.0'
+ peerDependenciesMeta:
+ '@solidjs/router':
+ optional: true
+
'@speed-highlight/core@1.2.8':
resolution: {integrity: sha512-IGytNtnUnPIobIbOq5Y6LIlqiHNX+vnToQIS7lj6L5819C+rA8TXRDkkG8vePsiBOGcoW9R6i+dp2YBUKdB09Q==}
@@ -2140,6 +2395,17 @@ packages:
resolution: {integrity: sha512-mqgsJi4/B2Jo6PXRUs1AsWA+06nqiqVZe1aXioA3vR6PesNeKUSXWfmIoYF6wOx3osiV0BnwB1JCBrInCOQSWA==}
engines: {node: '>=12'}
+ '@tanstack/router-devtools-core@1.139.10':
+ resolution: {integrity: sha512-rAUAhTvwivA49dkYR4bRUPRxqShO9dTD1+r3tZsnt23XlpmGtFvxBw8FYY2C9BvqaRLu+2RxACJDaXETVfm3OA==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@tanstack/router-core': ^1.139.10
+ csstype: ^3.0.10
+ solid-js: '>=1.9.5'
+ peerDependenciesMeta:
+ csstype:
+ optional: true
+
'@tanstack/router-devtools-core@1.139.7':
resolution: {integrity: sha512-Tx6+rCyjthlH7KS9Jz6YdT2KQ6rZQ66F+XJOj7Rel8zGAvyqx8USzcqTRvC+QjaU1jIJq+mNPWpMdKkkxPSOVA==}
engines: {node: '>=12'}
@@ -2201,6 +2467,13 @@ packages:
webpack:
optional: true
+ '@tanstack/router-ssr-query-core@1.139.10':
+ resolution: {integrity: sha512-cTNjPzDOVY8nbSCqgqLcPJ2dpMWrgo5VDFDal5c6xb38rU84mTzocXOX8a/9fjWMn4wzxv5p1S8HPN6RXfKD4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/router-core': '>=1.127.0'
+
'@tanstack/router-ssr-query-core@1.139.7':
resolution: {integrity: sha512-Ei4P2g/7xNO99OgvBOAAeVLI6VnqXYcSTI1Q6b1NYBzsb4aIo8Ne38cgVnanDlnIRrUJjIDQdZTAtu0AdANiyg==}
engines: {node: '>=12'}
@@ -2216,24 +2489,111 @@ packages:
resolution: {integrity: sha512-IpNFiCoy2YU6gY/4lCKIVlFyU67ltlcUMGcdnrevqOgq20AbMyeLbbBVo9tAA3TkHK9F+9Hd7DqGXsup2pmBLg==}
engines: {node: '>=12'}
+ '@tanstack/solid-devtools@0.7.15':
+ resolution: {integrity: sha512-c9QqNwBiWSXTytodgiwu93jkV7VhaktyckbvX8TytqdWxkTSUiqTgbyQ+RjXvG1M3IDTwymXj2YkRhr0krb4Ug==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ solid-js: '>=1.9.7'
+
+ '@tanstack/solid-query@5.90.14':
+ resolution: {integrity: sha512-e/TP+92mOFoQtInffcYKvAExgbQoBSDrarKnGwnindQBItp0ne1VdIg88K2U8rNwwM/wdj15V5azpXIkZkNQDw==}
+ peerDependencies:
+ solid-js: ^1.6.0
+
+ '@tanstack/solid-router-devtools@1.139.10':
+ resolution: {integrity: sha512-ZrGkenyRs4tjxj39ihOE14W2Uj6+19wF58V+neBE0goR75ueeKSwgVqvz4GtIE6p6EhwaoebS4lOuFTtwSSQcg==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@tanstack/router-core': ^1.139.10
+ '@tanstack/solid-router': ^1.139.10
+ solid-js: ^1.9.10
+ peerDependenciesMeta:
+ '@tanstack/router-core':
+ optional: true
+
+ '@tanstack/solid-router-ssr-query@1.139.10':
+ resolution: {integrity: sha512-NXB8m1ndNhzc2fd0INUeDHqSlUDGhuBjvJoBf0tYjtwne3CHXJVADNN6vCxCgjOYmS+dXrqlK0kcOY7/4TsclQ==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@tanstack/query-core': '>=5.90.0'
+ '@tanstack/solid-query': '>=5.90.0'
+ '@tanstack/solid-router': '>=1.127.0'
+ solid-js: ^1.9.10
+
+ '@tanstack/solid-router@1.139.10':
+ resolution: {integrity: sha512-zEq1cM92ORD21OOvmhX9cbCR1PDqP5H7u2s3WxOea0cwxQCgGGePrcD/wxKLnSrhQlx3XXCZPgCFTXxXHbsdCg==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ solid-js: ^1.9.10
+
+ '@tanstack/solid-start-client@1.139.10':
+ resolution: {integrity: sha512-zfygCE+lXNixaMFVaiPYQwTzmilfmgPhyHgU6zUvSg9Tb9odTvQDYx2/RMHkyaoZY6oAtJx5GGsI+INe1wxCCA==}
+ engines: {node: '>=22.12.0'}
+ peerDependencies:
+ solid-js: '>=1.0.0'
+
+ '@tanstack/solid-start-server@1.139.10':
+ resolution: {integrity: sha512-Jlb27HgEKmA2vS7ikC6lsPFCCQY/EQiIR41TUrp466GTNVETQuMZ3G5L3hc+7f8L+Kwt4lsfFUgRNp4Ars8EqQ==}
+ engines: {node: '>=22.12.0'}
+ peerDependencies:
+ solid-js: ^1.0.0
+
+ '@tanstack/solid-start@1.139.10':
+ resolution: {integrity: sha512-ZgCW+tSrt6nTJSzb5UvLQf5VWxhWy0Y/EeOrxe46UZ+1If96wyBKB8PoxtQDb31SuxM7rI9gx1ZYseXQd8Jbuw==}
+ engines: {node: '>=22.12.0'}
+ peerDependencies:
+ solid-js: '>=1.0.0'
+ vite: '>=7.0.0'
+
+ '@tanstack/solid-store@0.7.7':
+ resolution: {integrity: sha512-DnEZbqQ+pg68BguHz17VFukfp+6JaTk8nE2MhdVliU8bhsOFlTMsmVHp/4gMoQ1AkmAOMFiBsSliROCaaeJzvg==}
+ peerDependencies:
+ solid-js: ^1.6.0
+
+ '@tanstack/solid-store@0.8.0':
+ resolution: {integrity: sha512-JwqTedbxyOGw7mfmdGkB0RGgefRCw/tNauc8tlMcaS1mV5wTFT8c1KIB3LgttuHaanMJEBeqQJ7bc/R0WTP1fA==}
+ peerDependencies:
+ solid-js: ^1.6.0
+
+ '@tanstack/start-client-core@1.139.10':
+ resolution: {integrity: sha512-eF6z4Ag/nmhfduGHRm2DCwowUUweFTyIJqQ6Vo0fbDyi23eKRNcf4uzewR9tE8EHlFqIV987pgMVbWaaZQqmjQ==}
+ engines: {node: '>=22.12.0'}
+
'@tanstack/start-client-core@1.139.7':
resolution: {integrity: sha512-omG032CeYUWlwQt6s7VFqhc9dGHKWNJ0C5PoIckL+G/HcV+0/RxYkiKzx/HTTzWt+K+LpsBDFFNnrTUUyTE5sw==}
engines: {node: '>=22.12.0'}
+ '@tanstack/start-plugin-core@1.139.10':
+ resolution: {integrity: sha512-NzvaBFmODpqLqkN/1iaD7ikF3TDXDvXh4IkhhyVzI4lY5YAxErdQ61wxOnieSxHrFpyz/P/iJI8ipsFO65gj2g==}
+ engines: {node: '>=22.12.0'}
+ peerDependencies:
+ vite: '>=7.0.0'
+
'@tanstack/start-plugin-core@1.139.8':
resolution: {integrity: sha512-u1+rof/1vNHzFVR0yPWWSVwzbCtvndQsfjBR104xSTLCLB0oGvFvkCU0xLLyKtxhqsrYZFrqudg5B8aVH2plOg==}
engines: {node: '>=22.12.0'}
peerDependencies:
vite: '>=7.0.0'
+ '@tanstack/start-server-core@1.139.10':
+ resolution: {integrity: sha512-7xMuHFVyqGf++87CrGrhlHE4AO1ltbhf0Cyrj5oe2c0i5c9YYRW95lo+av72IN+xgysyC/yBm8/RyP8UEaTSEA==}
+ engines: {node: '>=22.12.0'}
+
'@tanstack/start-server-core@1.139.8':
resolution: {integrity: sha512-jKC83uMS2kgCHoqlHmxh9hAK1pN9Wd8l+Lhkibwp9PKKMW4Z1bxy5xCx6sr3TD2yJEOP25SRhYMrtAKmrLmYGA==}
engines: {node: '>=22.12.0'}
+ '@tanstack/start-storage-context@1.139.10':
+ resolution: {integrity: sha512-nBAH4QAIdVhxfrbiEU8wuIQfozfPPhb989yAhLgntE8lkHhLLAmyHPGt6oWEWbVNZ1eka5QDm2J0d7Z7K7b2Og==}
+ engines: {node: '>=22.12.0'}
+
'@tanstack/start-storage-context@1.139.7':
resolution: {integrity: sha512-l2utb0CXLE+wfj1wlAUPHohiq7n5nOBMl3pflhl3JzCXt+6D9AAogkfrysyxOAvx3KnLh3oG+qwf1KHWIDB9HA==}
engines: {node: '>=22.12.0'}
+ '@tanstack/store@0.7.7':
+ resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==}
+
'@tanstack/store@0.8.0':
resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==}
@@ -2761,6 +3121,10 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
+ ast-kit@2.2.0:
+ resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==}
+ engines: {node: '>=20.19.0'}
+
ast-types@0.16.1:
resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
engines: {node: '>=4'}
@@ -2847,6 +3211,9 @@ packages:
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+ birpc@2.8.0:
+ resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==}
+
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -2896,6 +3263,10 @@ packages:
magicast:
optional: true
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
@@ -3274,6 +3645,15 @@ packages:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'}
+ dts-resolver@2.1.3:
+ resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ oxc-resolver: '>=11.0.0'
+ peerDependenciesMeta:
+ oxc-resolver:
+ optional: true
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -3299,6 +3679,10 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ empathic@2.0.0:
+ resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==}
+ engines: {node: '>=14'}
+
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
@@ -3415,6 +3799,13 @@ packages:
peerDependencies:
eslint: '>=8.23.0'
+ eslint-plugin-solid@0.14.5:
+ resolution: {integrity: sha512-nfuYK09ah5aJG/oEN6P1qziy1zLgW4PDWe75VNPi4CEFYk1x2AEqwFeQfEPR7gNn0F2jOeqKhx2E+5oNCOBYWQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+ typescript: '>=4.8.4'
+
eslint-plugin-unused-imports@4.3.0:
resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==}
peerDependencies:
@@ -3501,10 +3892,6 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
- eventsource-parser@3.0.6:
- resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
- engines: {node: '>=18.0.0'}
-
execa@8.0.1:
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
engines: {node: '>=16.17'}
@@ -3836,6 +4223,10 @@ packages:
html-link-extractor@1.0.5:
resolution: {integrity: sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==}
+ html-tags@3.3.1:
+ resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
+ engines: {node: '>=8'}
+
html-url-attributes@3.0.1:
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
@@ -3910,6 +4301,9 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ inline-style-parser@0.1.1:
+ resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
+
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
@@ -3962,6 +4356,10 @@ packages:
is-hexadecimal@2.0.1:
resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+ is-html@2.0.0:
+ resolution: {integrity: sha512-S+OpgB5i7wzIue/YSE5hg0e5ZYfG3hhpNh9KGl6ayJ38p7ED6wxQLd1TV91xHpcTvw90KMJ9EwN3F/iNflHBVg==}
+ engines: {node: '>=8'}
+
is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'}
@@ -4119,9 +4517,6 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
- json-schema@0.4.0:
- resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
-
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -4149,6 +4544,9 @@ packages:
jws@4.0.0:
resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
+ kebab-case@1.0.2:
+ resolution: {integrity: sha512-7n6wXq4gNgBELfDCpzKc+mRrZFs7D+wgfF5WRFLNAr4DA/qtr9Js8uOAVAfHhuLMfAcQ0pRKqbpjx+TcJVdE1Q==}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -4171,6 +4569,9 @@ packages:
knitwork@1.2.0:
resolution: {integrity: sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==}
+ known-css-properties@0.30.0:
+ resolution: {integrity: sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==}
+
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
@@ -4333,6 +4734,11 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ lucide-solid@0.554.0:
+ resolution: {integrity: sha512-pt0KcZB7N/jVoLKng+kLpGTSoL3x03ByxbDVasMuTLvaZGVtio7rN946zlK9TrMZVwtAHuCvDn11+bEfKnMR3A==}
+ peerDependencies:
+ solid-js: ^1.4.7
+
lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
@@ -5109,6 +5515,30 @@ packages:
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
hasBin: true
+ rolldown-plugin-dts@0.17.8:
+ resolution: {integrity: sha512-76EEBlhF00yeY6M7VpMkWKI4r9WjuoMiOGey7j4D6zf3m0BR+ZrrY9hvSXdueJ3ljxSLq4DJBKFpX/X9+L7EKw==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@ts-macro/tsc': ^0.3.6
+ '@typescript/native-preview': '>=7.0.0-dev.20250601.1'
+ rolldown: ^1.0.0-beta.44
+ typescript: ^5.0.0
+ vue-tsc: ~3.1.0
+ peerDependenciesMeta:
+ '@ts-macro/tsc':
+ optional: true
+ '@typescript/native-preview':
+ optional: true
+ typescript:
+ optional: true
+ vue-tsc:
+ optional: true
+
+ rolldown@1.0.0-beta.45:
+ resolution: {integrity: sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
rolldown@1.0.0-beta.51:
resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -5309,6 +5739,12 @@ packages:
solid-js@1.9.10:
resolution: {integrity: sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==}
+ solid-markdown@2.0.14:
+ resolution: {integrity: sha512-Ln8R4TsNWySXvKkS80OHV+CSR/mwjk5XfGvC5UjZo/y/rAbbkBoxt6FXoWsfCkTW6GH9yxYvahSMXsUJU/ov4Q==}
+ engines: {node: '>=18', pnpm: '>=8.6.0'}
+ peerDependencies:
+ solid-js: ^1.6.0
+
solid-refresh@0.6.3:
resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==}
peerDependencies:
@@ -5426,6 +5862,9 @@ packages:
style-to-js@1.1.18:
resolution: {integrity: sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==}
+ style-to-object@0.3.0:
+ resolution: {integrity: sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==}
+
style-to-object@1.0.11:
resolution: {integrity: sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==}
@@ -5588,6 +6027,31 @@ packages:
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
engines: {node: '>=6'}
+ tsdown@0.15.12:
+ resolution: {integrity: sha512-c8VLlQm8/lFrOAg5VMVeN4NAbejZyVQkzd+ErjuaQgJFI/9MhR9ivr0H/CM7UlOF1+ELlF6YaI7sU/4itgGQ8w==}
+ engines: {node: '>=20.19.0'}
+ hasBin: true
+ peerDependencies:
+ '@arethetypeswrong/core': ^0.18.1
+ publint: ^0.3.0
+ typescript: ^5.0.0
+ unplugin-lightningcss: ^0.4.0
+ unplugin-unused: ^0.5.0
+ unrun: ^0.2.1
+ peerDependenciesMeta:
+ '@arethetypeswrong/core':
+ optional: true
+ publint:
+ optional: true
+ typescript:
+ optional: true
+ unplugin-lightningcss:
+ optional: true
+ unplugin-unused:
+ optional: true
+ unrun:
+ optional: true
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -5648,6 +6112,12 @@ packages:
ultrahtml@1.6.0:
resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==}
+ unconfig-core@7.4.1:
+ resolution: {integrity: sha512-Bp/bPZjV2Vl/fofoA2OYLSnw1Z0MOhCX7zHnVCYrazpfZvseBbGhwcNQMxsg185Mqh7VZQqK3C8hFG/Dyng+yA==}
+
+ unconfig@7.4.1:
+ resolution: {integrity: sha512-uyQ7LElcGizrOGZyIq9KU+xkuEjcRf9IpmDTkCSYv5mEeZzrXSj6rb51C0L+WTedsmAoVxW9WKrLWhSwebIM9Q==}
+
uncrypto@0.1.3:
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
@@ -5681,6 +6151,9 @@ packages:
unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
+ unist-util-is@5.2.1:
+ resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
+
unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
@@ -5690,9 +6163,15 @@ packages:
unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+ unist-util-visit-parents@5.1.3:
+ resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
+
unist-util-visit-parents@6.0.2:
resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
+ unist-util-visit@4.1.2:
+ resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
+
unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
@@ -6102,23 +6581,6 @@ snapshots:
'@adobe/css-tools@4.4.4':
optional: true
- '@ai-sdk/openai@2.0.73(zod@4.1.13)':
- dependencies:
- '@ai-sdk/provider': 2.0.0
- '@ai-sdk/provider-utils': 3.0.17(zod@4.1.13)
- zod: 4.1.13
-
- '@ai-sdk/provider-utils@3.0.17(zod@4.1.13)':
- dependencies:
- '@ai-sdk/provider': 2.0.0
- '@standard-schema/spec': 1.0.0
- eventsource-parser: 3.0.6
- zod: 4.1.13
-
- '@ai-sdk/provider@2.0.0':
- dependencies:
- json-schema: 0.4.0
-
'@anthropic-ai/sdk@0.71.0(zod@4.1.13)':
dependencies:
json-schema-to-ts: 3.1.1
@@ -6870,6 +7332,8 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
+ '@nothing-but/utils@0.17.0': {}
+
'@nx/nx-darwin-arm64@22.1.2':
optional: true
@@ -6917,6 +7381,8 @@ snapshots:
'@oozcitak/util@10.0.0': {}
+ '@oxc-project/types@0.95.0': {}
+
'@oxc-project/types@0.98.0':
optional: true
@@ -7061,52 +7527,102 @@ snapshots:
'@publint/pack@0.1.2': {}
+ '@quansync/fs@0.1.5':
+ dependencies:
+ quansync: 0.2.11
+
+ '@rolldown/binding-android-arm64@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-android-arm64@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-darwin-arm64@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-darwin-x64@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-darwin-x64@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-freebsd-x64@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.45':
+ dependencies:
+ '@napi-rs/wasm-runtime': 1.0.7
+ optional: true
+
'@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
optional: true
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
optional: true
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45':
+ optional: true
+
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
optional: true
'@rolldown/pluginutils@1.0.0-beta.40': {}
+ '@rolldown/pluginutils@1.0.0-beta.45': {}
+
'@rolldown/pluginutils@1.0.0-beta.47': {}
'@rolldown/pluginutils@1.0.0-beta.51':
@@ -7317,6 +7833,48 @@ snapshots:
'@size-limit/file': 11.2.0(size-limit@11.2.0)
size-limit: 11.2.0
+ '@solid-devtools/debugger@0.28.1(solid-js@1.9.10)':
+ dependencies:
+ '@nothing-but/utils': 0.17.0
+ '@solid-devtools/shared': 0.20.0(solid-js@1.9.10)
+ '@solid-primitives/bounds': 0.1.3(solid-js@1.9.10)
+ '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10)
+ '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.10)
+ '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/scheduled': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-devtools/logger@0.9.11(solid-js@1.9.10)':
+ dependencies:
+ '@nothing-but/utils': 0.17.0
+ '@solid-devtools/debugger': 0.28.1(solid-js@1.9.10)
+ '@solid-devtools/shared': 0.20.0(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-devtools/shared@0.20.0(solid-js@1.9.10)':
+ dependencies:
+ '@nothing-but/utils': 0.17.0
+ '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10)
+ '@solid-primitives/media': 2.3.3(solid-js@1.9.10)
+ '@solid-primitives/refs': 1.1.2(solid-js@1.9.10)
+ '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/scheduled': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10)
+ '@solid-primitives/styles': 0.1.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-primitives/bounds@0.1.3(solid-js@1.9.10)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10)
+ '@solid-primitives/resize-observer': 2.1.3(solid-js@1.9.10)
+ '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
'@solid-primitives/event-listener@2.4.3(solid-js@1.9.10)':
dependencies:
'@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
@@ -7329,6 +7887,19 @@ snapshots:
'@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
solid-js: 1.9.10
+ '@solid-primitives/media@2.3.3(solid-js@1.9.10)':
+ dependencies:
+ '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10)
+ '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/static-store': 0.1.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-primitives/refs@1.1.2(solid-js@1.9.10)':
+ dependencies:
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
'@solid-primitives/resize-observer@2.1.3(solid-js@1.9.10)':
dependencies:
'@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10)
@@ -7339,16 +7910,35 @@ snapshots:
'@solid-primitives/rootless@1.5.2(solid-js@1.9.10)':
dependencies:
- '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-primitives/scheduled@1.5.2(solid-js@1.9.10)':
+ dependencies:
+ solid-js: 1.9.10
+
+ '@solid-primitives/static-store@0.1.2(solid-js@1.9.10)':
+ dependencies:
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-primitives/styles@0.1.2(solid-js@1.9.10)':
+ dependencies:
+ '@solid-primitives/rootless': 1.5.2(solid-js@1.9.10)
+ '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
+ solid-js: 1.9.10
+
+ '@solid-primitives/utils@6.3.2(solid-js@1.9.10)':
+ dependencies:
solid-js: 1.9.10
- '@solid-primitives/static-store@0.1.2(solid-js@1.9.10)':
+ '@solidjs/meta@0.29.4(solid-js@1.9.10)':
dependencies:
- '@solid-primitives/utils': 6.3.2(solid-js@1.9.10)
solid-js: 1.9.10
- '@solid-primitives/utils@6.3.2(solid-js@1.9.10)':
+ '@solidjs/testing-library@0.8.10(solid-js@1.9.10)':
dependencies:
+ '@testing-library/dom': 10.4.1
solid-js: 1.9.10
'@speed-highlight/core@1.2.8': {}
@@ -7739,6 +8329,29 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
+ '@tanstack/router-devtools-core@1.139.10(@tanstack/router-core@1.139.10)(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)':
+ dependencies:
+ '@tanstack/router-core': 1.139.10
+ clsx: 2.1.1
+ goober: 2.1.18(csstype@3.2.3)
+ solid-js: 1.9.10
+ tiny-invariant: 1.3.3
+ vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ optionalDependencies:
+ csstype: 3.2.3
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
+
'@tanstack/router-devtools-core@1.139.7(@tanstack/router-core@1.139.10)(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)':
dependencies:
'@tanstack/router-core': 1.139.10
@@ -7834,6 +8447,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@tanstack/router-ssr-query-core@1.139.10(@tanstack/query-core@5.90.11)(@tanstack/router-core@1.139.10)':
+ dependencies:
+ '@tanstack/query-core': 5.90.11
+ '@tanstack/router-core': 1.139.10
+
'@tanstack/router-ssr-query-core@1.139.7(@tanstack/query-core@5.90.11)(@tanstack/router-core@1.139.10)':
dependencies:
'@tanstack/query-core': 5.90.11
@@ -7868,6 +8486,127 @@ snapshots:
- supports-color
- vite
+ '@tanstack/solid-devtools@0.7.15(csstype@3.2.3)(solid-js@1.9.10)':
+ dependencies:
+ '@tanstack/devtools': 0.8.2(csstype@3.2.3)(solid-js@1.9.10)
+ solid-js: 1.9.10
+ transitivePeerDependencies:
+ - bufferutil
+ - csstype
+ - utf-8-validate
+
+ '@tanstack/solid-query@5.90.14(solid-js@1.9.10)':
+ dependencies:
+ '@tanstack/query-core': 5.90.11
+ solid-js: 1.9.10
+
+ '@tanstack/solid-router-devtools@1.139.10(@tanstack/router-core@1.139.10)(@tanstack/solid-router@1.139.10(solid-js@1.9.10))(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)':
+ dependencies:
+ '@tanstack/router-devtools-core': 1.139.10(@tanstack/router-core@1.139.10)(@types/node@24.10.1)(csstype@3.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(solid-js@1.9.10)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ '@tanstack/solid-router': 1.139.10(solid-js@1.9.10)
+ solid-js: 1.9.10
+ vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ optionalDependencies:
+ '@tanstack/router-core': 1.139.10
+ transitivePeerDependencies:
+ - '@types/node'
+ - csstype
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
+
+ '@tanstack/solid-router-ssr-query@1.139.10(@tanstack/query-core@5.90.11)(@tanstack/router-core@1.139.10)(@tanstack/solid-query@5.90.14(solid-js@1.9.10))(@tanstack/solid-router@1.139.10(solid-js@1.9.10))(eslint@9.39.1(jiti@2.6.1))(solid-js@1.9.10)(typescript@5.9.3)':
+ dependencies:
+ '@tanstack/query-core': 5.90.11
+ '@tanstack/router-ssr-query-core': 1.139.10(@tanstack/query-core@5.90.11)(@tanstack/router-core@1.139.10)
+ '@tanstack/solid-query': 5.90.14(solid-js@1.9.10)
+ '@tanstack/solid-router': 1.139.10(solid-js@1.9.10)
+ eslint-plugin-solid: 0.14.5(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ solid-js: 1.9.10
+ transitivePeerDependencies:
+ - '@tanstack/router-core'
+ - eslint
+ - supports-color
+ - typescript
+
+ '@tanstack/solid-router@1.139.10(solid-js@1.9.10)':
+ dependencies:
+ '@solid-devtools/logger': 0.9.11(solid-js@1.9.10)
+ '@solid-primitives/refs': 1.1.2(solid-js@1.9.10)
+ '@solidjs/meta': 0.29.4(solid-js@1.9.10)
+ '@tanstack/history': 1.139.0
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/solid-store': 0.8.0(solid-js@1.9.10)
+ isbot: 5.1.31
+ solid-js: 1.9.10
+ tiny-invariant: 1.3.3
+ tiny-warning: 1.0.3
+
+ '@tanstack/solid-start-client@1.139.10(solid-js@1.9.10)':
+ dependencies:
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/solid-router': 1.139.10(solid-js@1.9.10)
+ '@tanstack/start-client-core': 1.139.10
+ solid-js: 1.9.10
+ tiny-invariant: 1.3.3
+ tiny-warning: 1.0.3
+
+ '@tanstack/solid-start-server@1.139.10(solid-js@1.9.10)':
+ dependencies:
+ '@solidjs/meta': 0.29.4(solid-js@1.9.10)
+ '@tanstack/history': 1.139.0
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/solid-router': 1.139.10(solid-js@1.9.10)
+ '@tanstack/start-client-core': 1.139.10
+ '@tanstack/start-server-core': 1.139.10
+ solid-js: 1.9.10
+ transitivePeerDependencies:
+ - crossws
+
+ '@tanstack/solid-start@1.139.10(solid-js@1.9.10)(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
+ dependencies:
+ '@tanstack/solid-router': 1.139.10(solid-js@1.9.10)
+ '@tanstack/solid-start-client': 1.139.10(solid-js@1.9.10)
+ '@tanstack/solid-start-server': 1.139.10(solid-js@1.9.10)
+ '@tanstack/start-client-core': 1.139.10
+ '@tanstack/start-plugin-core': 1.139.10(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/start-server-core': 1.139.10
+ pathe: 2.0.3
+ solid-js: 1.9.10
+ vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - '@rsbuild/core'
+ - '@tanstack/react-router'
+ - crossws
+ - supports-color
+ - vite-plugin-solid
+ - webpack
+
+ '@tanstack/solid-store@0.7.7(solid-js@1.9.10)':
+ dependencies:
+ '@tanstack/store': 0.7.7
+ solid-js: 1.9.10
+
+ '@tanstack/solid-store@0.8.0(solid-js@1.9.10)':
+ dependencies:
+ '@tanstack/store': 0.8.0
+ solid-js: 1.9.10
+
+ '@tanstack/start-client-core@1.139.10':
+ dependencies:
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/start-storage-context': 1.139.10
+ seroval: 1.4.0
+ tiny-invariant: 1.3.3
+ tiny-warning: 1.0.3
+
'@tanstack/start-client-core@1.139.7':
dependencies:
'@tanstack/router-core': 1.139.7
@@ -7876,6 +8615,38 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
+ '@tanstack/start-plugin-core@1.139.10(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/core': 7.28.5
+ '@babel/types': 7.28.5
+ '@rolldown/pluginutils': 1.0.0-beta.40
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/router-generator': 1.139.10
+ '@tanstack/router-plugin': 1.139.10(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/router-utils': 1.139.0
+ '@tanstack/server-functions-plugin': 1.139.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@tanstack/start-client-core': 1.139.10
+ '@tanstack/start-server-core': 1.139.10
+ babel-dead-code-elimination: 1.0.10
+ cheerio: 1.1.2
+ exsolve: 1.0.7
+ pathe: 2.0.3
+ srvx: 0.8.16
+ tinyglobby: 0.2.15
+ ufo: 1.6.1
+ vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vitefu: 1.1.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ xmlbuilder2: 4.0.1
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - '@rsbuild/core'
+ - '@tanstack/react-router'
+ - crossws
+ - supports-color
+ - vite-plugin-solid
+ - webpack
+
'@tanstack/start-plugin-core@1.139.8(@tanstack/react-router@1.139.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
dependencies:
'@babel/code-frame': 7.26.2
@@ -7908,6 +8679,18 @@ snapshots:
- vite-plugin-solid
- webpack
+ '@tanstack/start-server-core@1.139.10':
+ dependencies:
+ '@tanstack/history': 1.139.0
+ '@tanstack/router-core': 1.139.10
+ '@tanstack/start-client-core': 1.139.10
+ '@tanstack/start-storage-context': 1.139.10
+ h3-v2: h3@2.0.0-beta.5
+ seroval: 1.4.0
+ tiny-invariant: 1.3.3
+ transitivePeerDependencies:
+ - crossws
+
'@tanstack/start-server-core@1.139.8':
dependencies:
'@tanstack/history': 1.139.0
@@ -7920,10 +8703,16 @@ snapshots:
transitivePeerDependencies:
- crossws
+ '@tanstack/start-storage-context@1.139.10':
+ dependencies:
+ '@tanstack/router-core': 1.139.10
+
'@tanstack/start-storage-context@1.139.7':
dependencies:
'@tanstack/router-core': 1.139.7
+ '@tanstack/store@0.7.7': {}
+
'@tanstack/store@0.8.0': {}
'@tanstack/typedoc-config@0.3.1(typescript@5.9.3)':
@@ -8123,7 +8912,6 @@ snapshots:
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- optional: true
'@typescript-eslint/scope-manager@8.46.3':
dependencies:
@@ -8134,7 +8922,6 @@ snapshots:
dependencies:
'@typescript-eslint/types': 8.48.0
'@typescript-eslint/visitor-keys': 8.48.0
- optional: true
'@typescript-eslint/tsconfig-utils@8.46.3(typescript@5.9.3)':
dependencies:
@@ -8143,7 +8930,6 @@ snapshots:
'@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
- optional: true
'@typescript-eslint/type-utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
@@ -8159,8 +8945,7 @@ snapshots:
'@typescript-eslint/types@8.46.3': {}
- '@typescript-eslint/types@8.48.0':
- optional: true
+ '@typescript-eslint/types@8.48.0': {}
'@typescript-eslint/typescript-estree@8.46.3(typescript@5.9.3)':
dependencies:
@@ -8192,7 +8977,6 @@ snapshots:
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- optional: true
'@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
@@ -8215,7 +8999,6 @@ snapshots:
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- optional: true
'@typescript-eslint/visitor-keys@8.46.3':
dependencies:
@@ -8226,7 +9009,6 @@ snapshots:
dependencies:
'@typescript-eslint/types': 8.48.0
eslint-visitor-keys: 4.2.1
- optional: true
'@ungap/structured-clone@1.3.0': {}
@@ -8546,6 +9328,11 @@ snapshots:
assertion-error@2.0.1: {}
+ ast-kit@2.2.0:
+ dependencies:
+ '@babel/parser': 7.28.5
+ pathe: 2.0.3
+
ast-types@0.16.1:
dependencies:
tslib: 2.8.1
@@ -8625,6 +9412,8 @@ snapshots:
dependencies:
file-uri-to-path: 1.0.0
+ birpc@2.8.0: {}
+
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -8689,6 +9478,8 @@ snapshots:
optionalDependencies:
magicast: 0.5.1
+ cac@6.7.14: {}
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -9011,6 +9802,10 @@ snapshots:
dotenv@17.2.3: {}
+ dts-resolver@2.1.3(oxc-resolver@11.14.0):
+ optionalDependencies:
+ oxc-resolver: 11.14.0
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -9033,6 +9828,8 @@ snapshots:
emoji-regex@9.2.2: {}
+ empathic@2.0.0: {}
+
encodeurl@2.0.0: {}
encoding-sniffer@0.2.1:
@@ -9171,6 +9968,19 @@ snapshots:
transitivePeerDependencies:
- typescript
+ eslint-plugin-solid@0.14.5(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
+ dependencies:
+ '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 9.39.1(jiti@2.6.1)
+ estraverse: 5.3.0
+ is-html: 2.0.0
+ kebab-case: 1.0.2
+ known-css-properties: 0.30.0
+ style-to-object: 1.0.11
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - supports-color
+
eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)):
dependencies:
eslint: 9.39.1(jiti@2.6.1)
@@ -9273,8 +10083,6 @@ snapshots:
events@3.3.0: {}
- eventsource-parser@3.0.6: {}
-
execa@8.0.1:
dependencies:
cross-spawn: 7.0.6
@@ -9690,6 +10498,8 @@ snapshots:
dependencies:
cheerio: 1.1.2
+ html-tags@3.3.1: {}
+
html-url-attributes@3.0.1: {}
html-void-elements@3.0.0: {}
@@ -9759,6 +10569,8 @@ snapshots:
inherits@2.0.4: {}
+ inline-style-parser@0.1.1: {}
+
inline-style-parser@0.2.4: {}
ioredis@5.8.2:
@@ -9808,6 +10620,10 @@ snapshots:
is-hexadecimal@2.0.1: {}
+ is-html@2.0.0:
+ dependencies:
+ html-tags: 3.3.1
+
is-inside-container@1.0.0:
dependencies:
is-docker: 3.0.0
@@ -9964,8 +10780,6 @@ snapshots:
json-schema-traverse@1.0.0: {}
- json-schema@0.4.0: {}
-
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
@@ -9995,6 +10809,8 @@ snapshots:
jwa: 2.0.1
safe-buffer: 5.2.1
+ kebab-case@1.0.2: {}
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -10022,6 +10838,8 @@ snapshots:
knitwork@1.2.0: {}
+ known-css-properties@0.30.0: {}
+
kolorist@1.8.0: {}
launch-editor@2.12.0:
@@ -10176,6 +10994,10 @@ snapshots:
dependencies:
react: 19.2.0
+ lucide-solid@0.554.0(solid-js@1.9.10):
+ dependencies:
+ solid-js: 1.9.10
+
lunr@2.3.9: {}
lz-string@1.5.0: {}
@@ -11300,6 +12122,43 @@ snapshots:
dependencies:
glob: 10.4.5
+ rolldown-plugin-dts@0.17.8(oxc-resolver@11.14.0)(rolldown@1.0.0-beta.45)(typescript@5.9.3):
+ dependencies:
+ '@babel/generator': 7.28.5
+ '@babel/parser': 7.28.5
+ '@babel/types': 7.28.5
+ ast-kit: 2.2.0
+ birpc: 2.8.0
+ dts-resolver: 2.1.3(oxc-resolver@11.14.0)
+ get-tsconfig: 4.13.0
+ magic-string: 0.30.21
+ obug: 2.1.1
+ rolldown: 1.0.0-beta.45
+ optionalDependencies:
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - oxc-resolver
+
+ rolldown@1.0.0-beta.45:
+ dependencies:
+ '@oxc-project/types': 0.95.0
+ '@rolldown/pluginutils': 1.0.0-beta.45
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.0-beta.45
+ '@rolldown/binding-darwin-arm64': 1.0.0-beta.45
+ '@rolldown/binding-darwin-x64': 1.0.0-beta.45
+ '@rolldown/binding-freebsd-x64': 1.0.0-beta.45
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.45
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.45
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.45
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45
+ '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45
+ '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45
+ '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45
+ '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45
+
rolldown@1.0.0-beta.51:
dependencies:
'@oxc-project/types': 0.98.0
@@ -11525,6 +12384,21 @@ snapshots:
seroval: 1.3.2
seroval-plugins: 1.3.3(seroval@1.3.2)
+ solid-markdown@2.0.14(solid-js@1.9.10):
+ dependencies:
+ comma-separated-tokens: 2.0.3
+ property-information: 6.5.0
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.2
+ solid-js: 1.9.10
+ space-separated-tokens: 2.0.2
+ style-to-object: 0.3.0
+ unified: 11.0.5
+ unist-util-visit: 4.1.2
+ vfile: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
solid-refresh@0.6.3(solid-js@1.9.10):
dependencies:
'@babel/generator': 7.28.5
@@ -11635,6 +12509,10 @@ snapshots:
dependencies:
style-to-object: 1.0.11
+ style-to-object@0.3.0:
+ dependencies:
+ inline-style-parser: 0.1.1
+
style-to-object@1.0.11:
dependencies:
inline-style-parser: 0.2.4
@@ -11791,6 +12669,32 @@ snapshots:
minimist: 1.2.8
strip-bom: 3.0.0
+ tsdown@0.15.12(oxc-resolver@11.14.0)(publint@0.3.15)(typescript@5.9.3):
+ dependencies:
+ ansis: 4.2.0
+ cac: 6.7.14
+ chokidar: 4.0.3
+ debug: 4.4.3
+ diff: 8.0.2
+ empathic: 2.0.0
+ hookable: 5.5.3
+ rolldown: 1.0.0-beta.45
+ rolldown-plugin-dts: 0.17.8(oxc-resolver@11.14.0)(rolldown@1.0.0-beta.45)(typescript@5.9.3)
+ semver: 7.7.3
+ tinyexec: 1.0.1
+ tinyglobby: 0.2.15
+ tree-kill: 1.2.2
+ unconfig: 7.4.1
+ optionalDependencies:
+ publint: 0.3.15
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - '@ts-macro/tsc'
+ - '@typescript/native-preview'
+ - oxc-resolver
+ - supports-color
+ - vue-tsc
+
tslib@2.8.1: {}
tsx@4.20.6:
@@ -11847,6 +12751,19 @@ snapshots:
ultrahtml@1.6.0: {}
+ unconfig-core@7.4.1:
+ dependencies:
+ '@quansync/fs': 0.1.5
+ quansync: 0.2.11
+
+ unconfig@7.4.1:
+ dependencies:
+ '@quansync/fs': 0.1.5
+ defu: 6.1.4
+ jiti: 2.6.1
+ quansync: 0.2.11
+ unconfig-core: 7.4.1
+
uncrypto@0.1.3: {}
unctx@2.4.1:
@@ -11900,6 +12817,10 @@ snapshots:
'@types/unist': 3.0.3
unist-util-is: 6.0.1
+ unist-util-is@5.2.1:
+ dependencies:
+ '@types/unist': 2.0.11
+
unist-util-is@6.0.1:
dependencies:
'@types/unist': 3.0.3
@@ -11912,11 +12833,22 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ unist-util-visit-parents@5.1.3:
+ dependencies:
+ '@types/unist': 2.0.11
+ unist-util-is: 5.2.1
+
unist-util-visit-parents@6.0.2:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.1
+ unist-util-visit@4.1.2:
+ dependencies:
+ '@types/unist': 2.0.11
+ unist-util-is: 5.2.1
+ unist-util-visit-parents: 5.1.3
+
unist-util-visit@5.0.0:
dependencies:
'@types/unist': 3.0.3