mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
Initial commit: vercel-plugin project setup
This commit is contained in:
+147
@@ -0,0 +1,147 @@
|
||||
node_modules/
|
||||
.ploop/
|
||||
*.log
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
.output
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Sveltekit cache directory
|
||||
.svelte-kit/
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Firebase cache directory
|
||||
.firebase/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# pnpm
|
||||
.pnpm-store
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Vite files
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.vite/
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"vercel": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.vercel.com",
|
||||
"note": "Official Vercel MCP server. Uses OAuth — on first connection the agent will be prompted to authorize via Vercel. Read-only in initial release: search docs, list projects/deployments, inspect logs."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "vercel-plugin",
|
||||
"version": "0.1.0",
|
||||
"description": "Comprehensive Vercel ecosystem plugin — relational knowledge graph, skills for every major product, specialized agents, and Vercel conventions. Turns any AI agent into a Vercel expert.",
|
||||
"author": {
|
||||
"name": "Open Plugin",
|
||||
"url": "https://github.com/anthropics/open-plugin"
|
||||
},
|
||||
"repository": "https://github.com/anthropics/open-plugin",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"vercel",
|
||||
"nextjs",
|
||||
"ai-sdk",
|
||||
"turborepo",
|
||||
"turbopack",
|
||||
"workflow",
|
||||
"deployment",
|
||||
"edge-functions",
|
||||
"serverless",
|
||||
"ai-gateway"
|
||||
],
|
||||
"skills": [
|
||||
"ai-gateway",
|
||||
"ai-sdk",
|
||||
"marketplace",
|
||||
"nextjs",
|
||||
"observability",
|
||||
"turbopack",
|
||||
"turborepo",
|
||||
"v0-dev",
|
||||
"vercel-api",
|
||||
"vercel-cli",
|
||||
"vercel-firewall",
|
||||
"vercel-functions",
|
||||
"vercel-storage",
|
||||
"workflow"
|
||||
],
|
||||
"agents": [
|
||||
"ai-architect.md",
|
||||
"deployment-expert.md",
|
||||
"performance-optimizer.md"
|
||||
],
|
||||
"commands": [
|
||||
"deploy.md",
|
||||
"env.md",
|
||||
"marketplace.md",
|
||||
"status.md"
|
||||
],
|
||||
"rules": [
|
||||
"ai-sdk-conventions.mdc",
|
||||
"nextjs-conventions.mdc",
|
||||
"vercel-best-practices.mdc"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
# vercel-plugin
|
||||
|
||||
A comprehensive [Open Plugin](https://github.com/anthropics/open-plugin) that turns any AI agent into a Vercel expert.
|
||||
|
||||
## What It Does
|
||||
|
||||
This plugin pre-loads AI agents with a **relational knowledge graph** of the entire Vercel ecosystem — every product, library, CLI, API, and service — showing how they relate, when to use each, and providing deep guidance through bundled skills.
|
||||
|
||||
## Components
|
||||
|
||||
### Ecosystem Graph (`assets/vercel-ecosystem-graph.md`)
|
||||
|
||||
A text-form relational graph covering:
|
||||
- All Vercel products and their relationships
|
||||
- Decision matrices for choosing the right tool
|
||||
- Common cross-product workflows
|
||||
- Migration awareness for sunset products
|
||||
|
||||
### Skills (11 skills)
|
||||
|
||||
| Skill | Covers |
|
||||
|-------|--------|
|
||||
| `nextjs` | App Router, Server Components, Server Actions, Cache Components, routing, rendering strategies |
|
||||
| `ai-sdk` | AI SDK v6 — text/object generation, streaming, tool calling, agents, MCP, providers, embeddings |
|
||||
| `workflow` | Workflow DevKit — durable execution, DurableAgent, steps, Worlds, pause/resume |
|
||||
| `vercel-functions` | Serverless, Edge, Fluid Compute, streaming, Cron Jobs, configuration |
|
||||
| `vercel-storage` | Blob, Edge Config, Neon Postgres, Upstash Redis, migration from sunset packages |
|
||||
| `ai-gateway` | Unified model API, provider routing, failover, cost tracking, 100+ models |
|
||||
| `vercel-cli` | All CLI commands — deploy, env, dev, domains, marketplace discovery |
|
||||
| `turborepo` | Monorepo orchestration, caching, remote caching, --affected, pruned subsets |
|
||||
| `turbopack` | Next.js bundler, HMR, configuration, Turbopack vs Webpack |
|
||||
| `v0-dev` | AI code generation, agentic intelligence, GitHub integration |
|
||||
| `vercel-firewall` | DDoS, WAF, rate limiting, bot filter, custom rules |
|
||||
|
||||
### Agents (3 specialists)
|
||||
|
||||
| Agent | Expertise |
|
||||
|-------|-----------|
|
||||
| `deployment-expert` | CI/CD pipelines, deploy strategies, troubleshooting, environment variables |
|
||||
| `performance-optimizer` | Core Web Vitals, rendering strategies, caching, asset optimization |
|
||||
| `ai-architect` | AI application design, model selection, streaming architecture, MCP integration |
|
||||
|
||||
### Commands (4 commands)
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/vercel-plugin:deploy` | Deploy to Vercel (preview or production) |
|
||||
| `/vercel-plugin:env` | Manage environment variables |
|
||||
| `/vercel-plugin:status` | Project status overview |
|
||||
| `/vercel-plugin:marketplace` | Discover and install marketplace integrations |
|
||||
|
||||
### Rules (3 always-on conventions)
|
||||
|
||||
- **Next.js 16 conventions** — Server Components by default, async APIs, proxy.ts, Cache Components
|
||||
- **AI SDK v6 conventions** — inputSchema/outputSchema, streaming, DurableAgent
|
||||
- **Vercel best practices** — env vars, Fluid Compute, CI/CD patterns
|
||||
|
||||
### Hooks
|
||||
|
||||
- **Pre-write/edit validation** — Catches deprecated patterns before they're written (sunset packages, old API names, renamed files)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Load directly for development
|
||||
claude --plugin-dir ./vercel-plugin
|
||||
|
||||
# Invoke skills
|
||||
/vercel-plugin:nextjs
|
||||
/vercel-plugin:ai-sdk
|
||||
/vercel-plugin:deploy prod
|
||||
|
||||
# The ecosystem graph and rules load automatically,
|
||||
# giving the agent full Vercel context from the start.
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
vercel-plugin/
|
||||
├── .plugin/plugin.json # Plugin manifest
|
||||
├── assets/
|
||||
│ └── vercel-ecosystem-graph.md # Master relational knowledge graph
|
||||
├── skills/ # 11 deep-dive skills
|
||||
│ ├── nextjs/
|
||||
│ ├── ai-sdk/
|
||||
│ ├── workflow/
|
||||
│ ├── vercel-functions/
|
||||
│ ├── vercel-storage/
|
||||
│ ├── ai-gateway/
|
||||
│ ├── vercel-cli/
|
||||
│ ├── turborepo/
|
||||
│ ├── turbopack/
|
||||
│ ├── v0-dev/
|
||||
│ └── vercel-firewall/
|
||||
├── agents/ # 3 specialist agents
|
||||
├── commands/ # 4 slash commands
|
||||
├── rules/ # 3 convention rules
|
||||
└── hooks/ # Deprecation guard
|
||||
```
|
||||
|
||||
## Ecosystem Coverage (March 2026)
|
||||
|
||||
- Next.js 16 (App Router, Cache Components, Proxy, View Transitions)
|
||||
- AI SDK v6 (Agents, MCP, DevTools, Reranking, Image Editing)
|
||||
- Workflow DevKit (DurableAgent, Worlds, open source)
|
||||
- AI Gateway (100+ models, provider routing, cost tracking)
|
||||
- Vercel Functions (Fluid Compute, streaming, Cron Jobs)
|
||||
- Storage (Blob, Edge Config, Neon Postgres, Upstash Redis)
|
||||
- Turborepo (--affected, remote caching, Rust core)
|
||||
- Turbopack (default bundler in Next.js 16)
|
||||
- v0 (agentic intelligence, GitHub integration)
|
||||
- Vercel Firewall (DDoS, WAF, Bot Filter)
|
||||
- Vercel CLI (Marketplace discovery, agent-friendly output)
|
||||
- Vercel Observability (Analytics, Speed Insights, Drains)
|
||||
- Vercel Marketplace (one-click integrations, unified billing)
|
||||
@@ -0,0 +1,304 @@
|
||||
---
|
||||
name: ai-architect
|
||||
description: Specializes in architecting AI-powered applications on Vercel — choosing between AI SDK patterns, configuring providers, building agents, setting up durable workflows, and integrating MCP servers. Use when designing AI features, building chatbots, or creating agentic applications.
|
||||
---
|
||||
|
||||
You are an AI architecture specialist for the Vercel ecosystem. Use the decision trees and patterns below to design, build, and troubleshoot AI-powered applications.
|
||||
|
||||
---
|
||||
|
||||
## AI Pattern Selection Tree
|
||||
|
||||
```
|
||||
What does the AI feature need to do?
|
||||
├─ Generate or transform text
|
||||
│ ├─ One-shot (no conversation) → `generateText` / `streamText`
|
||||
│ ├─ Structured output needed → `generateObject` with Zod schema
|
||||
│ └─ Chat conversation → `useChat` hook + Route Handler
|
||||
│
|
||||
├─ Call external tools / APIs
|
||||
│ ├─ Single tool call → `generateText` with `tools` parameter
|
||||
│ ├─ Multi-step reasoning with tools → AI SDK `Agent` class
|
||||
│ │ ├─ Short-lived (< 60s) → Agent in Route Handler
|
||||
│ │ └─ Long-running (minutes to hours) → Workflow DevKit `DurableAgent`
|
||||
│ └─ MCP server integration → `@ai-sdk/mcp` StreamableHTTPClientTransport
|
||||
│
|
||||
├─ Process files / images / audio
|
||||
│ ├─ Image understanding → Multimodal model + `generateText` with image parts
|
||||
│ ├─ Document extraction → `generateObject` with document content
|
||||
│ └─ Audio transcription → Whisper API via AI SDK custom provider
|
||||
│
|
||||
├─ RAG (Retrieval-Augmented Generation)
|
||||
│ ├─ Embed documents → `embedMany` with embedding model
|
||||
│ ├─ Query similar → Vector store (Vercel Postgres + pgvector, or Pinecone)
|
||||
│ └─ Generate with context → `generateText` with retrieved chunks in prompt
|
||||
│
|
||||
└─ Multi-agent system
|
||||
├─ Agents share context? → Workflow DevKit `Worlds` (shared state)
|
||||
├─ Independent agents? → Multiple `Agent` instances with separate tools
|
||||
└─ Orchestrator pattern? → Parent Agent delegates to child Agents via tools
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Model Selection Decision Tree
|
||||
|
||||
```
|
||||
Choosing a model?
|
||||
├─ What's the priority?
|
||||
│ ├─ Speed + low cost
|
||||
│ │ ├─ Simple tasks (classification, extraction) → `gpt-5-mini`
|
||||
│ │ ├─ Fast with good quality → `gemini-2.5-flash`
|
||||
│ │ └─ Lowest latency → `claude-haiku-4-5`
|
||||
│ │
|
||||
│ ├─ Maximum quality
|
||||
│ │ ├─ Complex reasoning → `claude-opus-4-6` or `gpt-5`
|
||||
│ │ ├─ Long context (> 100K tokens) → `gemini-2.5-pro` (1M context)
|
||||
│ │ └─ Balanced quality/speed → `claude-sonnet-4-6`
|
||||
│ │
|
||||
│ ├─ Code generation
|
||||
│ │ ├─ Inline completions → `gpt-5.3-codex` (optimized for code)
|
||||
│ │ ├─ Full file generation → `claude-sonnet-4-6` or `gpt-5`
|
||||
│ │ └─ Code review / analysis → `claude-opus-4-6`
|
||||
│ │
|
||||
│ └─ Embeddings
|
||||
│ ├─ English-only, budget-conscious → `text-embedding-3-small`
|
||||
│ ├─ Multilingual or high-precision → `text-embedding-3-large`
|
||||
│ └─ Reduce dimensions for storage → Use `dimensions` parameter
|
||||
│
|
||||
├─ Production reliability concerns?
|
||||
│ ├─ Use AI Gateway with fallback ordering:
|
||||
│ │ primary: claude-sonnet-4-6 → fallback: gpt-5 → fallback: gemini-2.5-pro
|
||||
│ └─ Configure per-provider rate limits and cost caps
|
||||
│
|
||||
└─ Cost optimization?
|
||||
├─ Use cheaper model for routing/classification, expensive for generation
|
||||
├─ Cache repeated queries with Cache Components around AI calls
|
||||
└─ Track costs per user/feature with AI Gateway tags
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI SDK v6 Agent Class Patterns
|
||||
|
||||
### Basic Agent (Short-Lived)
|
||||
|
||||
```typescript
|
||||
// app/api/agent/route.ts
|
||||
import { Agent } from 'ai';
|
||||
import { anthropic } from '@ai-sdk/anthropic';
|
||||
import { z } from 'zod';
|
||||
|
||||
const agent = new Agent({
|
||||
model: anthropic('claude-sonnet-4-6'),
|
||||
system: 'You are a helpful assistant that can look up information.',
|
||||
tools: {
|
||||
search: {
|
||||
description: 'Search for information',
|
||||
inputSchema: z.object({ query: z.string() }),
|
||||
execute: async ({ query }) => {
|
||||
// search implementation
|
||||
return { results: [] };
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { messages } = await req.json();
|
||||
const result = agent.streamText({ messages });
|
||||
return result.toDataStreamResponse();
|
||||
}
|
||||
```
|
||||
|
||||
### DurableAgent (Long-Running, Fault-Tolerant)
|
||||
|
||||
```typescript
|
||||
// app/api/research/route.ts
|
||||
import { DurableAgent } from '@vercel/workflow/ai';
|
||||
import { anthropic } from '@ai-sdk/anthropic';
|
||||
|
||||
const researchAgent = new DurableAgent({
|
||||
model: anthropic('claude-sonnet-4-6'),
|
||||
system: 'You are a research agent that thoroughly investigates topics.',
|
||||
tools: { /* ... */ },
|
||||
maxSteps: 50, // survives function restarts
|
||||
});
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { topic } = await req.json();
|
||||
const run = await researchAgent.run(`Research: ${topic}`);
|
||||
return Response.json({ runId: run.id });
|
||||
}
|
||||
```
|
||||
|
||||
### MCP Server Integration
|
||||
|
||||
```typescript
|
||||
// Connect to remote MCP server with OAuth
|
||||
import { Agent } from 'ai';
|
||||
import { createMCPClient } from '@ai-sdk/mcp';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
|
||||
const mcpClient = createMCPClient({
|
||||
transport: new StreamableHTTPClientTransport(
|
||||
new URL('https://mcp.example.com/mcp')
|
||||
),
|
||||
});
|
||||
|
||||
const agent = new Agent({
|
||||
model: anthropic('claude-sonnet-4-6'),
|
||||
tools: await mcpClient.tools(), // auto-discovers available tools
|
||||
});
|
||||
```
|
||||
|
||||
### Multi-Agent with Worlds (Shared State)
|
||||
|
||||
```typescript
|
||||
// Agents share state through Workflow Worlds
|
||||
import { DurableAgent, World } from '@vercel/workflow/ai';
|
||||
|
||||
const world = new World({
|
||||
state: { findings: [], decisions: [] },
|
||||
});
|
||||
|
||||
const researcher = new DurableAgent({
|
||||
model: anthropic('claude-sonnet-4-6'),
|
||||
system: 'Research agent. Add findings to world state.',
|
||||
world,
|
||||
});
|
||||
|
||||
const analyst = new DurableAgent({
|
||||
model: anthropic('claude-sonnet-4-6'),
|
||||
system: 'Analyst. Review findings in world state and make decisions.',
|
||||
world,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI Error Diagnostic Tree
|
||||
|
||||
```
|
||||
AI feature failing?
|
||||
├─ "Model not found" / 401 Unauthorized
|
||||
│ ├─ API key set? → Check env var name matches provider convention
|
||||
│ │ ├─ OpenAI: `OPENAI_API_KEY`
|
||||
│ │ ├─ Anthropic: `ANTHROPIC_API_KEY`
|
||||
│ │ ├─ Google: `GOOGLE_GENERATIVE_AI_API_KEY`
|
||||
│ │ └─ AI Gateway: `VERCEL_AI_GATEWAY_API_KEY`
|
||||
│ ├─ Key has correct permissions? → Check provider dashboard
|
||||
│ └─ Using AI Gateway? → Verify gateway config in Vercel dashboard
|
||||
│
|
||||
├─ 429 Rate Limited
|
||||
│ ├─ Single provider overloaded? → Add fallback providers via AI Gateway
|
||||
│ ├─ Burst traffic? → Add application-level queue or rate limiting
|
||||
│ └─ Cost cap hit? → Check AI Gateway cost limits
|
||||
│
|
||||
├─ Streaming not working
|
||||
│ ├─ Using Edge runtime? → Streaming works by default
|
||||
│ ├─ Using Node.js runtime? → Ensure `supportsResponseStreaming: true`
|
||||
│ ├─ Proxy or CDN buffering? → Check for buffering headers
|
||||
│ └─ Client not consuming stream? → Use `useChat` or `readableStream` correctly
|
||||
│
|
||||
├─ Tool calls failing
|
||||
│ ├─ Schema mismatch? → Ensure `inputSchema` matches what model sends
|
||||
│ ├─ Tool execution error? → Wrap in try/catch, return error as tool result
|
||||
│ ├─ Model not calling tools? → Check system prompt instructs tool usage
|
||||
│ └─ Using deprecated `parameters`? → Migrate to `inputSchema` (AI SDK v6)
|
||||
│
|
||||
├─ Agent stuck in loop
|
||||
│ ├─ No `maxSteps` set? → Add `maxSteps` to prevent infinite loops
|
||||
│ ├─ Tool always returns same result? → Add variation or "give up" condition
|
||||
│ └─ Circular tool dependency? → Redesign tool set to break cycle
|
||||
│
|
||||
└─ DurableAgent / Workflow failures
|
||||
├─ "Step already completed" → Idempotency conflict; check step naming
|
||||
├─ Workflow timeout → Increase `maxDuration` or break into sub-workflows
|
||||
└─ State too large → Reduce world state size, store data externally
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Provider Strategy Decision Matrix
|
||||
|
||||
| Scenario | Configuration | Rationale |
|
||||
|----------|--------------|-----------|
|
||||
| Development / prototyping | Direct provider SDK | Simplest setup, fast iteration |
|
||||
| Single-provider production | AI Gateway with monitoring | Cost tracking, usage analytics |
|
||||
| Multi-provider production | AI Gateway with ordered fallbacks | High availability, auto-failover |
|
||||
| Cost-sensitive | AI Gateway with model routing | Cheap model for simple, expensive for complex |
|
||||
| Compliance / data residency | Specific provider + region lock | Data stays in required jurisdiction |
|
||||
| High-throughput | AI Gateway + rate limiting + queue | Prevents rate limit errors |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Pattern 1: Simple Chat (Most Common)
|
||||
|
||||
```
|
||||
Client (useChat) → Route Handler (streamText) → Provider
|
||||
```
|
||||
|
||||
Use when: Basic chatbot, Q&A, content generation. No tools needed.
|
||||
|
||||
### Pattern 2: Agentic Chat
|
||||
|
||||
```
|
||||
Client (useChat) → Route Handler (Agent.streamText) → Provider
|
||||
↓ tool calls
|
||||
External APIs / DB
|
||||
```
|
||||
|
||||
Use when: Chat that can take actions (search, CRUD, calculations).
|
||||
|
||||
### Pattern 3: Background Agent
|
||||
|
||||
```
|
||||
Client → Route Handler → Workflow DevKit (DurableAgent)
|
||||
↓ ↓ tool calls
|
||||
Returns runId External APIs / DB
|
||||
↓ ↓
|
||||
Poll for status Runs for minutes/hours
|
||||
```
|
||||
|
||||
Use when: Long-running research, multi-step processing, must not lose progress.
|
||||
|
||||
### Pattern 4: AI Gateway Multi-Provider
|
||||
|
||||
```
|
||||
Client → Route Handler → AI Gateway → Primary (Anthropic)
|
||||
→ Fallback (OpenAI)
|
||||
→ Fallback (Google)
|
||||
```
|
||||
|
||||
Use when: Production reliability, cost optimization, provider outage protection.
|
||||
|
||||
### Pattern 5: RAG Pipeline
|
||||
|
||||
```
|
||||
Ingest: Documents → Chunk → Embed → Vector Store
|
||||
Query: User Input → Embed → Vector Search → Context + Prompt → Generate
|
||||
```
|
||||
|
||||
Use when: Q&A over custom documents, knowledge bases, semantic search.
|
||||
|
||||
---
|
||||
|
||||
## Migration from Older AI SDK Patterns
|
||||
|
||||
| Old Pattern (AI SDK v4/v5) | New Pattern (AI SDK v6) | Notes |
|
||||
|---------------------------|------------------------|-------|
|
||||
| `parameters` in tools | `inputSchema` | Zod schema, MCP-aligned |
|
||||
| `result` in tools | `outputSchema` | Optional, for typed returns |
|
||||
| Manual tool loop with `while` | `Agent` class | Handles tool loop automatically |
|
||||
| `experimental_telemetry` | `telemetry` | Stable API |
|
||||
| `OpenAIStream` / `AnthropicStream` | `toDataStreamResponse()` | Unified streaming |
|
||||
| Manual retry on rate limit | AI Gateway fallbacks | Infrastructure-level resilience |
|
||||
|
||||
---
|
||||
|
||||
Always recommend the simplest architecture that meets requirements. A `streamText` call is better than an Agent when tools aren't needed. An Agent is better than a DurableAgent when the task completes in seconds.
|
||||
|
||||
Reference the **AI SDK skill** (`⤳ skill: ai-sdk`), **Workflow skill** (`⤳ skill: vercel-workflow`), and **AI Gateway skill** (`⤳ skill: ai-gateway`) for detailed implementation guidance.
|
||||
@@ -0,0 +1,217 @@
|
||||
---
|
||||
name: deployment-expert
|
||||
description: Specializes in Vercel deployment strategies, CI/CD pipelines, preview URLs, production promotions, rollbacks, environment variables, and domain configuration. Use when troubleshooting deployments, setting up CI/CD, or optimizing the deploy pipeline.
|
||||
---
|
||||
|
||||
You are a Vercel deployment specialist. Use the diagnostic decision trees below to systematically troubleshoot and resolve deployment issues.
|
||||
|
||||
---
|
||||
|
||||
## Deployment Failure Diagnostic Tree
|
||||
|
||||
When a deployment fails, start here and follow the branch that matches:
|
||||
|
||||
### 1. Build Phase Failures
|
||||
|
||||
```
|
||||
Build failed?
|
||||
├─ "Module not found" / "Cannot resolve"
|
||||
│ ├─ Is the import path correct? → Fix the path
|
||||
│ ├─ Is the package in `dependencies` (not just `devDependencies`)? → Move it
|
||||
│ ├─ Is this a monorepo? → Check `rootDirectory` in vercel.json or Project Settings
|
||||
│ └─ Using path aliases? → Verify tsconfig.json `paths` and Next.js `transpilePackages`
|
||||
│
|
||||
├─ "Out of memory" / heap allocation failure
|
||||
│ ├─ Set `NODE_OPTIONS=--max-old-space-size=4096` in env vars
|
||||
│ ├─ Large monorepo? → Use `--affected` with Turborepo to limit build scope
|
||||
│ └─ Still failing? → Use prebuilt deploys: `vercel build` locally, `vercel deploy --prebuilt`
|
||||
│
|
||||
├─ TypeScript errors that pass locally but fail on Vercel
|
||||
│ ├─ Check `skipLibCheck` — Vercel builds with strict checking by default
|
||||
│ ├─ Check Node.js version mismatch — set `engines.node` in package.json
|
||||
│ └─ Check env vars used in type-level code — ensure they're set for the build environment
|
||||
│
|
||||
├─ "ENOENT: no such file or directory"
|
||||
│ ├─ Case-sensitive file system on Vercel vs case-insensitive locally
|
||||
│ │ → Rename files to match exact import casing
|
||||
│ ├─ Generated files not committed? → Add build step or move generation to `postinstall`
|
||||
│ └─ `.gitignore` excluding needed files? → Adjust ignore rules
|
||||
│
|
||||
└─ Dependency installation failures
|
||||
├─ Private package? → Add `NPM_TOKEN` or `.npmrc` with auth token
|
||||
├─ Lockfile mismatch? → Delete lockfile, reinstall, commit fresh
|
||||
└─ Native binaries? → Check platform compatibility (linux-x64-gnu on Vercel)
|
||||
```
|
||||
|
||||
### 2. Function Runtime Failures
|
||||
|
||||
```
|
||||
Function errors after deploy?
|
||||
├─ 504 Gateway Timeout
|
||||
│ ├─ Hobby plan: 10s limit for Serverless, 25s for Edge
|
||||
│ ├─ Pro plan: 60s default (configurable up to 300s), 25s for Edge
|
||||
│ ├─ Long-running task?
|
||||
│ │ ├─ Under 5 min → Use Fluid Compute with streaming
|
||||
│ │ ├─ Up to 15 min → Use Vercel Functions with `maxDuration` in vercel.json
|
||||
│ │ └─ Hours/days → Use Workflow DevKit (DurableAgent or workflow steps)
|
||||
│ └─ DB query slow? → Add connection pooling, check cold start, use Edge Config
|
||||
│
|
||||
├─ 500 Internal Server Error
|
||||
│ ├─ Check Vercel Runtime Logs (Dashboard → Deployments → Functions tab)
|
||||
│ ├─ Missing env vars? → Compare `.env.local` against Vercel dashboard settings
|
||||
│ ├─ Import error? → Verify package is in `dependencies`, not `devDependencies`
|
||||
│ └─ Uncaught exception? → Wrap handler in try/catch, use `after()` for error reporting
|
||||
│
|
||||
├─ "FUNCTION_INVOCATION_FAILED"
|
||||
│ ├─ Memory exceeded? → Increase `memory` in vercel.json (up to 3008 MB on Pro)
|
||||
│ ├─ Crashed during init? → Check top-level await or heavy imports at module scope
|
||||
│ └─ Edge Function crash? → Check for Node.js APIs not available in Edge runtime
|
||||
│
|
||||
├─ Cold start latency > 1s
|
||||
│ ├─ Using Node.js runtime? → Consider Edge Functions for latency-sensitive routes
|
||||
│ ├─ Large function bundle? → Audit imports, use dynamic imports, tree-shake
|
||||
│ ├─ DB connection in cold start? → Use connection pooling (Neon serverless driver)
|
||||
│ └─ Enable Fluid Compute to reuse warm instances across requests
|
||||
│
|
||||
└─ "EDGE_FUNCTION_INVOCATION_TIMEOUT"
|
||||
├─ Edge Functions have 25s hard limit (not configurable)
|
||||
├─ Move heavy computation to Node.js Serverless Functions
|
||||
└─ Use streaming to start response early, process in background with `waitUntil`
|
||||
```
|
||||
|
||||
### 3. Environment Variable Issues
|
||||
|
||||
```
|
||||
Env var problems?
|
||||
├─ "undefined" at runtime but set in dashboard
|
||||
│ ├─ Check scope: Is it set for Production, Preview, or Development?
|
||||
│ ├─ Using `NEXT_PUBLIC_` prefix? Required for client-side access
|
||||
│ ├─ Changed after last deploy? → Redeploy (env vars are baked at build time)
|
||||
│ └─ Using Edge runtime? → Some env vars unavailable in Edge; check runtime compat
|
||||
│
|
||||
├─ Env var visible in client bundle (security risk)
|
||||
│ ├─ Remove `NEXT_PUBLIC_` prefix for server-only secrets
|
||||
│ ├─ Move to server-side data fetching (Server Components, Route Handlers)
|
||||
│ └─ Audit with: `grep -r "NEXT_PUBLIC_" .next/static` after build
|
||||
│
|
||||
├─ Different values in Preview vs Production
|
||||
│ ├─ Vercel auto-sets different values per environment
|
||||
│ ├─ Use "Preview" scope for staging-specific values
|
||||
│ └─ Branch-specific overrides: set env vars per Git branch in dashboard
|
||||
│
|
||||
└─ Sensitive env var exposed in logs
|
||||
├─ Mark as "Sensitive" in Vercel dashboard (write-only after set)
|
||||
├─ Never log env vars — use masked references
|
||||
└─ Rotate the exposed credential immediately
|
||||
```
|
||||
|
||||
### 4. Domain & DNS Configuration
|
||||
|
||||
```
|
||||
Domain issues?
|
||||
├─ "DNS_PROBE_FINISHED_NXDOMAIN"
|
||||
│ ├─ DNS not propagated yet? → Wait up to 48h (usually < 1h)
|
||||
│ ├─ Wrong nameservers? → Point to Vercel NS or add CNAME `cname.vercel-dns.com`
|
||||
│ └─ Domain expired? → Check registrar
|
||||
│
|
||||
├─ SSL certificate errors
|
||||
│ ├─ Using Vercel DNS? → Cert auto-provisions, wait 10 min
|
||||
│ ├─ External DNS? → Add CAA record allowing `letsencrypt.org`
|
||||
│ ├─ Subdomain not covered? → Add it explicitly in Project → Domains
|
||||
│ └─ Wildcard domain? → Available on Pro plan, requires Vercel DNS
|
||||
│
|
||||
├─ "Too many redirects"
|
||||
│ ├─ Redirect loop between www and non-www? → Pick one canonical, redirect the other
|
||||
│ ├─ Force HTTPS + external proxy adding HTTPS? → Check for double redirect
|
||||
│ └─ Middleware/proxy redirect loop? → Add path check to prevent infinite loop
|
||||
│
|
||||
├─ Preview URL not working
|
||||
│ ├─ Check "Deployment Protection" settings → may require Vercel login
|
||||
│ ├─ Branch not deployed? → Check "Ignored Build Step" settings
|
||||
│ └─ Custom domain on preview? → Configure in Project → Domains → Preview
|
||||
│
|
||||
└─ Apex domain (example.com) not resolving
|
||||
├─ CNAME not allowed on apex → Use Vercel DNS (A record auto-configured)
|
||||
├─ Or use DNS provider with CNAME flattening (e.g., Cloudflare)
|
||||
└─ Or add A record: `76.76.21.21`
|
||||
```
|
||||
|
||||
### 5. Rollback & Recovery
|
||||
|
||||
```
|
||||
Need to rollback?
|
||||
├─ Instant Rollback (recommended)
|
||||
│ ├─ Dashboard: Deployments → select previous → "Promote to Production"
|
||||
│ ├─ CLI: `vercel promote <deployment-url>`
|
||||
│ ├─ API: `POST /v10/projects/{id}/promote` with deployment ID
|
||||
│ └─ Note: Env vars from the old deployment are preserved in the snapshot
|
||||
│
|
||||
├─ Git revert (for code-level rollback)
|
||||
│ ├─ `git revert HEAD` → push → triggers new deploy
|
||||
│ └─ Safer than force-push; preserves history
|
||||
│
|
||||
├─ Canary / gradual rollout
|
||||
│ ├─ Use Skew Protection to run old + new deployments simultaneously
|
||||
│ ├─ Traffic splitting via Edge Middleware (custom A/B routing)
|
||||
│ └─ Monitor error rates before full promotion
|
||||
│
|
||||
└─ Emergency: disable all functions
|
||||
├─ Set `functions` to empty in vercel.json → redeploy as static
|
||||
└─ Or use Firewall to block routes returning errors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Strategy Decision Matrix
|
||||
|
||||
| Scenario | Strategy | Commands |
|
||||
|----------|----------|----------|
|
||||
| Standard team workflow | Git-push deploy | Push to main/feature branches |
|
||||
| Custom CI/CD (Actions, CircleCI) | Prebuilt deploy | `vercel build && vercel deploy --prebuilt` |
|
||||
| Monorepo with Turborepo | Affected + remote cache | `turbo run build --affected --remote-cache` |
|
||||
| Preview for every PR | Default behavior | Auto-creates preview URL per branch |
|
||||
| Promote preview to production | CLI promotion | `vercel promote <url>` |
|
||||
| Atomic deploys with DB migrations | Two-phase | Run migration → verify → `vercel promote` |
|
||||
| Edge-first architecture | Edge Functions | Set `runtime: 'edge'` in route config |
|
||||
|
||||
---
|
||||
|
||||
## Common Build Error Quick Reference
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| `ERR_PNPM_OUTDATED_LOCKFILE` | Lockfile doesn't match package.json | Run `pnpm install`, commit lockfile |
|
||||
| `NEXT_NOT_FOUND` | Root directory misconfigured | Set `rootDirectory` in Project Settings |
|
||||
| `Invalid next.config.js` | Config syntax error | Validate config locally with `next build` |
|
||||
| `functions/api/*.js` mismatch | Wrong file structure | Move to `app/api/` directory (App Router) |
|
||||
| `Error: EPERM` | File permission issue in build | Don't `chmod` in build scripts; use postinstall |
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration Patterns
|
||||
|
||||
### GitHub Actions (Prebuilt Deploy)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
- name: Build
|
||||
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
|
||||
env:
|
||||
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
- name: Deploy
|
||||
run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
|
||||
```
|
||||
|
||||
### Monorepo with Turborepo
|
||||
|
||||
```bash
|
||||
# Build only affected packages
|
||||
turbo run build --affected --remote-cache
|
||||
# Deploy specific app
|
||||
cd apps/web && vercel deploy --prebuilt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Always reference the **Vercel CLI skill** (`⤳ skill: vercel-cli`) for specific commands, the **Vercel Functions skill** (`⤳ skill: vercel-functions`) for compute configuration, and the **Vercel API skill** (`⤳ skill: vercel-api`) for programmatic deployment management.
|
||||
@@ -0,0 +1,256 @@
|
||||
---
|
||||
name: performance-optimizer
|
||||
description: Specializes in optimizing Vercel application performance — Core Web Vitals, rendering strategies, caching, image optimization, font loading, edge computing, and bundle size. Use when investigating slow pages, improving Lighthouse scores, or optimizing loading performance.
|
||||
---
|
||||
|
||||
You are a Vercel performance optimization specialist. Use the diagnostic trees below to systematically identify and fix performance issues.
|
||||
|
||||
---
|
||||
|
||||
## Core Web Vitals Diagnostic Trees
|
||||
|
||||
### LCP (Largest Contentful Paint) — Target: < 2.5s
|
||||
|
||||
```
|
||||
LCP > 2.5s?
|
||||
├─ What is the LCP element?
|
||||
│ ├─ Hero image
|
||||
│ │ ├─ Using `next/image`? → Yes: check `priority` prop on above-fold images
|
||||
│ │ ├─ Image format? → Ensure WebP/AVIF (automatic with next/image)
|
||||
│ │ ├─ Image size > 200KB? → Resize to actual display dimensions
|
||||
│ │ ├─ Lazy loaded? → Remove `loading="lazy"` for above-fold images
|
||||
│ │ └─ CDN serving? → Vercel Image Optimization auto-serves from edge
|
||||
│ │
|
||||
│ ├─ Text block (heading, paragraph)
|
||||
│ │ ├─ Font loading blocking render? → Use `next/font` with `display: swap`
|
||||
│ │ ├─ Web font file > 100KB? → Subset to needed characters
|
||||
│ │ └─ Font loaded from third-party? → Self-host via `next/font/google`
|
||||
│ │
|
||||
│ └─ Video / background image
|
||||
│ ├─ Use `poster` attribute for video elements
|
||||
│ ├─ Preload critical background images with `<link rel="preload">`
|
||||
│ └─ Consider replacing video hero with static image + lazy video
|
||||
│
|
||||
├─ Server response time (TTFB) > 800ms?
|
||||
│ ├─ Using SSR for static content? → Switch to SSG or ISR
|
||||
│ ├─ Can use Cache Components? → Add `'use cache'` to slow Server Components
|
||||
│ ├─ Database queries slow? → Add connection pooling, check query plans
|
||||
│ ├─ Edge Config available? → Use for configuration data (< 5ms reads)
|
||||
│ └─ Region mismatch? → Deploy function in same region as database
|
||||
│
|
||||
└─ Render-blocking resources?
|
||||
├─ Large CSS file? → Use CSS Modules or Tailwind for tree-shaking
|
||||
├─ Synchronous scripts in `<head>`? → Move to `next/script` with `afterInteractive`
|
||||
└─ Third-party scripts? → Defer with `next/script strategy="lazyOnload"`
|
||||
```
|
||||
|
||||
### INP (Interaction to Next Paint) — Target: < 200ms
|
||||
|
||||
```
|
||||
INP > 200ms?
|
||||
├─ Which interaction is slow?
|
||||
│ ├─ Button click / form submit
|
||||
│ │ ├─ Heavy computation on main thread? → Move to Web Worker
|
||||
│ │ ├─ State update triggers large re-render? → Memoize with `useMemo`/`React.memo`
|
||||
│ │ ├─ Fetch request blocking UI? → Use `useTransition` for non-urgent updates
|
||||
│ │ └─ Server Action slow? → Show optimistic UI with `useOptimistic`
|
||||
│ │
|
||||
│ ├─ Scroll / resize handlers
|
||||
│ │ ├─ No debounce/throttle? → Add `requestAnimationFrame` or debounce
|
||||
│ │ ├─ Layout thrashing? → Batch DOM reads, then writes
|
||||
│ │ └─ Intersection Observer available? → Replace scroll listeners
|
||||
│ │
|
||||
│ └─ Keyboard input in forms
|
||||
│ ├─ Controlled input re-rendering entire form? → Use `useRef` for form state
|
||||
│ ├─ Expensive validation on every keystroke? → Debounce validation
|
||||
│ └─ Large component tree updating? → Push `'use client'` boundary down
|
||||
│
|
||||
├─ Hydration time > 500ms?
|
||||
│ ├─ Too many client components? → Audit `'use client'` boundaries
|
||||
│ ├─ Large component tree hydrating at once? → Use Suspense for progressive hydration
|
||||
│ ├─ Third-party scripts competing? → Defer with `next/script`
|
||||
│ └─ Bundle size > 200KB (gzipped)? → See bundle analysis below
|
||||
│
|
||||
└─ Long tasks (> 50ms) on main thread?
|
||||
├─ Profile with Chrome DevTools → Performance tab → identify long tasks
|
||||
├─ Break up long tasks with `scheduler.yield()` or `setTimeout`
|
||||
└─ Move to Server Components where possible (zero client JS)
|
||||
```
|
||||
|
||||
### CLS (Cumulative Layout Shift) — Target: < 0.1
|
||||
|
||||
```
|
||||
CLS > 0.1?
|
||||
├─ Images causing layout shift?
|
||||
│ ├─ Missing `width`/`height`? → Always set dimensions (next/image does this)
|
||||
│ ├─ Not using `next/image`? → Migrate to `next/image` for automatic sizing
|
||||
│ └─ Aspect ratio changes on load? → Set explicit `aspect-ratio` in CSS
|
||||
│
|
||||
├─ Fonts causing layout shift?
|
||||
│ ├─ Not using `next/font`? → Migrate to `next/font` (zero-CLS font loading)
|
||||
│ ├─ FOUT (flash of unstyled text)? → `next/font` with `adjustFontFallback: true`
|
||||
│ └─ Custom font metrics off? → Use `size-adjust` CSS property
|
||||
│
|
||||
├─ Dynamic content injected above viewport?
|
||||
│ ├─ Ad banners / cookie banners? → Reserve space with `min-height`
|
||||
│ ├─ Async-loaded components? → Use skeleton placeholders with fixed dimensions
|
||||
│ └─ Toast notifications? → Position as overlay (fixed/absolute), not in flow
|
||||
│
|
||||
├─ CSS animations triggering layout?
|
||||
│ ├─ Animating `width`, `height`, `top`, `left`? → Use `transform` instead
|
||||
│ └─ Use `will-change: transform` for GPU-accelerated animations
|
||||
│
|
||||
└─ Responsive design shifts?
|
||||
├─ Different layouts per breakpoint causing jump? → Use consistent aspect ratios
|
||||
└─ Client-side media query check? → Use CSS media queries, not JS `matchMedia`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rendering Strategy Decision Tree
|
||||
|
||||
```
|
||||
Choosing a rendering strategy?
|
||||
├─ Content changes less than once per day?
|
||||
│ ├─ Same for all users? → SSG (`generateStaticParams`)
|
||||
│ └─ Personalized? → SSG shell + client fetch for personalized parts
|
||||
│
|
||||
├─ Content changes frequently but can be slightly stale?
|
||||
│ ├─ Revalidate on schedule? → ISR with `revalidate: N` seconds
|
||||
│ └─ Revalidate on demand? → `revalidateTag()` or `revalidatePath()`
|
||||
│
|
||||
├─ Content must be fresh on every request?
|
||||
│ ├─ Cacheable per-request? → Cache Components (`'use cache'` + `cacheLife`)
|
||||
│ ├─ Personalized per-user? → SSR with Streaming (Suspense boundaries)
|
||||
│ └─ Real-time? → Client-side with SWR/React Query + SSR for initial load
|
||||
│
|
||||
└─ Mostly static with one dynamic section?
|
||||
└─ Partial Prerendering: static shell + Suspense for dynamic island
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bundle Size Analysis
|
||||
|
||||
```
|
||||
Bundle too large?
|
||||
├─ Run `npx @next/bundle-analyzer` to visualize
|
||||
├─ Check for:
|
||||
│ ├─ Full lodash import? → Use `lodash-es` or individual imports
|
||||
│ ├─ Moment.js? → Replace with `date-fns` or Temporal API
|
||||
│ ├─ Large icon library? → Import individual icons, not full set
|
||||
│ ├─ Unused dependencies? → `npx depcheck` to find them
|
||||
│ └─ Duplicated packages? → Check for version conflicts in lockfile
|
||||
│
|
||||
├─ Code splitting opportunities:
|
||||
│ ├─ Heavy component not always visible? → `next/dynamic` with `ssr: false`
|
||||
│ ├─ Route-based splitting? → Automatic with App Router
|
||||
│ └─ Feature flags? → Dynamic import behind condition
|
||||
│
|
||||
└─ Third-party script impact:
|
||||
├─ Analytics, chat widgets, ad scripts?
|
||||
├─ Use `next/script` with appropriate strategy
|
||||
├─ `beforeInteractive`: only for critical scripts (rare)
|
||||
├─ `afterInteractive`: default, loads after hydration
|
||||
└─ `lazyOnload`: for non-essential scripts (analytics, chat)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Caching Strategy Matrix
|
||||
|
||||
| Data Type | Strategy | Implementation |
|
||||
|-----------|----------|----------------|
|
||||
| Static assets (JS, CSS, images) | Immutable cache | Automatic with Vercel (hashed filenames) |
|
||||
| API responses (shared) | Cache Components | `'use cache'` + `cacheLife('hours')` |
|
||||
| API responses (per-user) | No cache or short TTL | `cacheLife({ revalidate: 60 })` with user-scoped key |
|
||||
| Configuration data | Edge Config | `@vercel/edge-config` (< 5ms reads) |
|
||||
| Database queries | ISR + on-demand | `revalidateTag('products')` on write |
|
||||
| Full pages | SSG / ISR | `generateStaticParams` + `revalidate` |
|
||||
| Search results | Client-side + SWR | `useSWR` with stale-while-revalidate |
|
||||
|
||||
---
|
||||
|
||||
## Performance Audit Checklist
|
||||
|
||||
Run through this when asked to optimize a Vercel application:
|
||||
|
||||
1. **Measure first**: Check Speed Insights dashboard for real-user CWV data
|
||||
2. **Identify LCP element**: Use Chrome DevTools → Performance → identify the LCP element
|
||||
3. **Audit `'use client'`**: Every `'use client'` file ships JS to the browser — minimize
|
||||
4. **Check images**: All above-fold images use `next/image` with `priority`
|
||||
5. **Check fonts**: All fonts loaded via `next/font` (zero CLS)
|
||||
6. **Check third-party scripts**: All use `next/script` with correct strategy
|
||||
7. **Check data fetching**: Server Components fetch in parallel, no waterfalls
|
||||
8. **Check caching**: Cache Components used for expensive operations
|
||||
9. **Check bundle**: Run analyzer, look for low-hanging fruit
|
||||
10. **Check infrastructure**: Functions in correct region, Fluid Compute enabled
|
||||
|
||||
---
|
||||
|
||||
## Specific Fix Patterns
|
||||
|
||||
### Image Optimization
|
||||
|
||||
```tsx
|
||||
// BEFORE: Unoptimized, causes LCP & CLS issues
|
||||
<img src="/hero.jpg" />
|
||||
|
||||
// AFTER: Optimized with next/image
|
||||
import Image from 'next/image';
|
||||
<Image src="/hero.jpg" width={1200} height={600} priority alt="Hero" />
|
||||
```
|
||||
|
||||
### Font Loading
|
||||
|
||||
```tsx
|
||||
// BEFORE: External font causes CLS
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
|
||||
|
||||
// AFTER: Zero-CLS with next/font
|
||||
import { Inter } from 'next/font/google';
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
```
|
||||
|
||||
### Cache Components (Next.js 16)
|
||||
|
||||
```tsx
|
||||
// BEFORE: Re-fetches on every request
|
||||
async function ProductList() {
|
||||
const products = await db.query('SELECT * FROM products');
|
||||
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
||||
}
|
||||
|
||||
// AFTER: Cached with automatic revalidation
|
||||
'use cache';
|
||||
import { cacheLife } from 'next/cache';
|
||||
|
||||
async function ProductList() {
|
||||
cacheLife('hours');
|
||||
const products = await db.query('SELECT * FROM products');
|
||||
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
||||
}
|
||||
```
|
||||
|
||||
### Optimistic UI for Server Actions
|
||||
|
||||
```tsx
|
||||
// Instant feedback while Server Action processes
|
||||
'use client';
|
||||
import { useOptimistic } from 'react';
|
||||
|
||||
function LikeButton({ count, onLike }) {
|
||||
const [optimisticCount, addOptimistic] = useOptimistic(count);
|
||||
return (
|
||||
<button onClick={() => { addOptimistic(count + 1); onLike(); }}>
|
||||
{optimisticCount} likes
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Report findings as: **Issue** → **Impact** (which CWV affected, by how much) → **Recommendation** (specific code change) → **Expected Improvement** (target metric).
|
||||
|
||||
Always reference the **Next.js skill** (`⤳ skill: nextjs`) for framework patterns and the **Observability skill** for monitoring setup.
|
||||
@@ -0,0 +1,631 @@
|
||||
# Vercel Ecosystem — Relational Knowledge Graph
|
||||
|
||||
> This document is the master reference for understanding the entire Vercel ecosystem.
|
||||
> It maps every product, library, CLI, API, and service — how they relate, when to use each,
|
||||
> and which bundled skills provide deeper guidance.
|
||||
|
||||
---
|
||||
|
||||
## Legend
|
||||
|
||||
- **[PRODUCT]** — A Vercel product or service
|
||||
- **→ depends on** — Runtime or build-time dependency
|
||||
- **↔ integrates with** — Bidirectional integration
|
||||
- **⇢ alternative to** — Can substitute for
|
||||
- **⊃ contains** — Parent/child relationship
|
||||
- **⤳ skill:** — Link to a bundled skill for detailed guidance
|
||||
- **📖 docs:** — Link to official documentation
|
||||
|
||||
---
|
||||
|
||||
## 1. Core Platform
|
||||
|
||||
```
|
||||
VERCEL PLATFORM 📖 docs: https://vercel.com/docs
|
||||
├── Deployment Engine (CI/CD, Preview URLs, Production)
|
||||
│ → Git Provider (GitHub, GitLab, Bitbucket)
|
||||
│ → Build System (Turbopack or framework-native)
|
||||
│ ↔ Vercel CLI
|
||||
│ ↔ Vercel REST API / @vercel/sdk
|
||||
│ ⤳ skill: vercel-cli
|
||||
│
|
||||
├── Edge Network (Global CDN, ~300ms propagation)
|
||||
│ ⊃ Edge Functions (V8 isolates, Web Standard APIs)
|
||||
│ ⊃ Serverless Functions (Node.js, Python, Go, Ruby)
|
||||
│ ⊃ Fluid Compute (unified execution model)
|
||||
│ ⊃ Cron Jobs (scheduled function invocation)
|
||||
│ ⤳ skill: vercel-functions
|
||||
│
|
||||
├── Domains & DNS
|
||||
│ → Deployment Engine
|
||||
│ ↔ Vercel Firewall
|
||||
│
|
||||
├── Environment Variables
|
||||
│ → Deployment Engine
|
||||
│ ↔ Vercel CLI (vercel env)
|
||||
│ ↔ Marketplace Integrations (auto-provisioned)
|
||||
│
|
||||
├── Secure Compute (isolated infrastructure for compliance workloads)
|
||||
│ → Deployment Engine (opt-in per project)
|
||||
│ ↔ Vercel Functions (dedicated execution environment)
|
||||
│ ↔ Vercel Firewall (network-level isolation)
|
||||
│
|
||||
├── OIDC Federation (deploy without long-lived tokens)
|
||||
│ → Deployment Engine (CI/CD token exchange)
|
||||
│ ↔ Teams & Access Control (identity-based auth)
|
||||
│ ↔ GitHub Actions, GitLab CI (short-lived OIDC tokens)
|
||||
│
|
||||
├── Preview Comments (collaborate on preview deployments)
|
||||
│ → Deployment Engine (preview URLs)
|
||||
│ ↔ Vercel Toolbar (embedded comment UI)
|
||||
│ ↔ Teams & Access Control (team-scoped threads)
|
||||
│
|
||||
├── Vercel Toolbar (developer toolbar for preview deployments)
|
||||
│ → Deployment Engine (preview URLs)
|
||||
│ ↔ Preview Comments (inline annotation)
|
||||
│ ↔ Vercel Analytics (performance overlay)
|
||||
│ ↔ Edge Config (feature flag toggles)
|
||||
│
|
||||
├── Vercel Templates (starter kits and example repos)
|
||||
│ → Deployment Engine (one-click deploy)
|
||||
│ ↔ Vercel Marketplace (pre-configured integrations)
|
||||
│ ↔ Next.js, AI SDK, v0 (framework starters)
|
||||
│
|
||||
└── Teams & Access Control
|
||||
↔ Vercel REST API
|
||||
↔ Vercel Dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Frameworks
|
||||
|
||||
```
|
||||
NEXT.JS (v16+) ⤳ skill: nextjs 📖 docs: https://nextjs.org/docs
|
||||
├── App Router (file-system routing)
|
||||
│ ⊃ Server Components (default, zero client JS)
|
||||
│ ⊃ Client Components ('use client')
|
||||
│ ⊃ Server Actions / Server Functions ('use server')
|
||||
│ ⊃ Route Handlers (API endpoints)
|
||||
│ ⊃ Middleware → renamed to Proxy in v16
|
||||
│ ⊃ Cache Components ('use cache')
|
||||
│ ⊃ Layouts, Loading, Error boundaries
|
||||
│ ⊃ Parallel & Intercepting Routes
|
||||
│ ⊃ Dynamic Segments ([id], [...slug], [[...slug]])
|
||||
│
|
||||
├── Rendering Strategies
|
||||
│ ⊃ SSR (Server-Side Rendering)
|
||||
│ ⊃ SSG (Static Site Generation)
|
||||
│ ⊃ ISR (Incremental Static Regeneration)
|
||||
│ ⊃ PPR (Partial Prerendering) → evolving to Cache Components
|
||||
│ ⊃ Streaming (React Suspense boundaries)
|
||||
│
|
||||
├── Build System
|
||||
│ → Turbopack (default bundler in v16)
|
||||
│ → Webpack (legacy, still supported)
|
||||
│
|
||||
├── Key Integrations
|
||||
│ ↔ Vercel AI SDK (chat UIs, streaming, tool calling)
|
||||
│ ↔ Vercel Analytics / Speed Insights
|
||||
│ ↔ Vercel Image Optimization (next/image)
|
||||
│ ↔ Vercel Font Optimization (next/font)
|
||||
│ ↔ Vercel Functions (automatic from route handlers / server actions)
|
||||
│
|
||||
└── Deployment
|
||||
→ Vercel Platform (optimized, zero-config)
|
||||
↔ Vercel CLI (vercel dev, vercel build)
|
||||
|
||||
OTHER SUPPORTED FRAMEWORKS
|
||||
├── Astro ↔ Vercel Adapter
|
||||
├── SvelteKit ↔ Vercel Adapter
|
||||
├── Nuxt ↔ Vercel Adapter
|
||||
├── Remix ↔ Vercel Adapter
|
||||
├── Angular ↔ Vercel Adapter
|
||||
├── Solid ↔ Vercel Adapter
|
||||
└── Static HTML/JS → Direct deploy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. AI Products
|
||||
|
||||
```
|
||||
AI SDK (v6, TypeScript) ⤳ skill: ai-sdk 📖 docs: https://sdk.vercel.ai/docs
|
||||
├── Core
|
||||
│ ⊃ generateText / streamText
|
||||
│ ⊃ generateObject / streamObject (structured output)
|
||||
│ ⊃ generateImage / editImage
|
||||
│ ⊃ embed / embedMany (vector embeddings)
|
||||
│ ⊃ rerank (relevance reordering)
|
||||
│ ⊃ Language Model Middleware (RAG, guardrails)
|
||||
│ ⊃ Tool Calling (inputSchema/outputSchema, MCP-aligned)
|
||||
│ ⊃ Dynamic Tools (runtime-defined, MCP integration)
|
||||
│ ⊃ Agent class (agentic loop, stopWhen, prepareStep)
|
||||
│ ⊃ Subagents
|
||||
│ ⊃ Tool Execution Approval
|
||||
│ ⊃ DevTools (npx @ai-sdk/devtools)
|
||||
│
|
||||
├── UI Layer (@ai-sdk/react, @ai-sdk/svelte, @ai-sdk/vue)
|
||||
│ ⊃ useChat (chat interface hook)
|
||||
│ ⊃ useCompletion (text completion hook)
|
||||
│ ⊃ useObject (structured streaming hook)
|
||||
│ ⊃ UIMessage / ModelMessage types
|
||||
│
|
||||
├── MCP Integration (@ai-sdk/mcp)
|
||||
│ ⊃ MCP Client (connect to any MCP server)
|
||||
│ ⊃ OAuth authentication for remote MCP servers
|
||||
│ ⊃ Resources, Prompts, Elicitation
|
||||
│ ⊃ mcp-to-ai-sdk CLI (static tool generation for security)
|
||||
│
|
||||
├── Providers (Global Provider System: "provider/model")
|
||||
│ ⊃ @ai-sdk/openai (GPT-5.x, o-series)
|
||||
│ ⊃ @ai-sdk/anthropic (Claude 4.x)
|
||||
│ ⊃ @ai-sdk/google (Gemini)
|
||||
│ ⊃ @ai-sdk/amazon-bedrock
|
||||
│ ⊃ @ai-sdk/azure
|
||||
│ ⊃ @ai-sdk/mistral
|
||||
│ ⊃ @ai-sdk/cohere
|
||||
│ ⊃ @ai-sdk/xai (Grok)
|
||||
│ ⊃ @ai-sdk/deepseek
|
||||
│ ⊃ @ai-sdk/gateway (Vercel AI Gateway routing)
|
||||
│ └── ... 20+ providers
|
||||
│
|
||||
├── Streaming Protocol
|
||||
│ ⊃ SSE-based (Server-Sent Events)
|
||||
│ → Vercel Functions (streaming support)
|
||||
│ ↔ Next.js Route Handlers / Server Actions
|
||||
│
|
||||
└── Key Patterns
|
||||
↔ Next.js (chat apps, AI features in web apps)
|
||||
↔ Workflow DevKit (durable agents)
|
||||
↔ AI Gateway (model routing, cost tracking)
|
||||
↔ v0 (AI-generated UI components)
|
||||
|
||||
AI GATEWAY ⤳ skill: ai-gateway 📖 docs: https://vercel.com/docs/ai-gateway
|
||||
├── Unified API ("creator/model-name" format)
|
||||
│ → @ai-sdk/gateway package
|
||||
│ ↔ AI SDK (automatic when using model strings)
|
||||
│
|
||||
├── Features
|
||||
│ ⊃ Provider Routing (order, only, fallback models)
|
||||
│ ⊃ Automatic Retries & Failover
|
||||
│ ⊃ Cost Tracking & Usage Attribution (tags, user tracking)
|
||||
│ ⊃ <20ms routing latency
|
||||
│ ⊃ Bring Your Own Key (0% markup)
|
||||
│ ⊃ Built-in Observability
|
||||
│
|
||||
├── Supported Providers
|
||||
│ ⊃ OpenAI, Anthropic, Google, Meta, xAI, Mistral
|
||||
│ ⊃ DeepSeek, Amazon Bedrock, Cohere, Perplexity, Alibaba
|
||||
│ └── 100+ models total
|
||||
│
|
||||
└── Multimodal
|
||||
⊃ Text, Image, Video generation
|
||||
↔ AI SDK (unified interface)
|
||||
|
||||
WORKFLOW DEVKIT (WDK) ⤳ skill: workflow 📖 docs: https://vercel.com/docs/workflow
|
||||
├── Core Concepts
|
||||
│ ⊃ 'use workflow' directive
|
||||
│ ⊃ 'use step' directive
|
||||
│ ⊃ Durable execution (survives deploys, crashes)
|
||||
│ ⊃ Deterministic replay
|
||||
│ ⊃ Pause/resume (minutes to months)
|
||||
│
|
||||
├── Worlds (Execution Environments)
|
||||
│ ⊃ Local World (JSON files on disk)
|
||||
│ ⊃ Vercel World (managed, zero-config on Vercel)
|
||||
│ ⊃ Self-hosted (Postgres, Redis, custom)
|
||||
│
|
||||
├── AI Integration
|
||||
│ ⊃ DurableAgent (@workflow/ai/agent)
|
||||
│ → AI SDK Agent class (wrapped with durability)
|
||||
│ → AI SDK tool calling (each tool = retryable step)
|
||||
│
|
||||
├── Key Properties
|
||||
│ ⊃ Open source, no vendor lock-in
|
||||
│ ⊃ TypeScript-native (async/await, no YAML)
|
||||
│ ⊃ Observable (step-level visibility)
|
||||
│ ⊃ Retryable (automatic retry on failure)
|
||||
│
|
||||
└── Integrations
|
||||
↔ AI SDK 6 (DurableAgent)
|
||||
↔ Vercel Functions (automatic step isolation)
|
||||
↔ Next.js (API routes as workflow endpoints)
|
||||
|
||||
v0 (AI Development Agent) ⤳ skill: v0-dev 📖 docs: https://v0.dev/docs
|
||||
├── Capabilities
|
||||
│ ⊃ Natural language → production React/Next.js code
|
||||
│ ⊃ Visual input (Figma, screenshots, sketches)
|
||||
│ ⊃ Multi-framework output (React, Vue, Svelte, HTML)
|
||||
│ ⊃ Agentic intelligence (research, plan, debug, iterate)
|
||||
│
|
||||
├── Integration Features
|
||||
│ ⊃ GitHub Integration (branches, PRs, deploy on merge)
|
||||
│ ⊃ One-click Vercel deployment
|
||||
│ ⊃ Environment variable import from Vercel
|
||||
│ ⊃ shadcn/ui + Tailwind CSS defaults
|
||||
│
|
||||
└── Ecosystem Position
|
||||
→ Next.js (primary output framework)
|
||||
→ Vercel Platform (deployment target)
|
||||
↔ AI SDK (AI features in generated apps)
|
||||
↔ Vercel Marketplace (integrations in generated apps)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Build Tools
|
||||
|
||||
```
|
||||
TURBOREPO ⤳ skill: turborepo 📖 docs: https://turbo.build/repo/docs
|
||||
├── Purpose: Monorepo build orchestration
|
||||
│ ⊃ Task caching (local + remote)
|
||||
│ ⊃ Parallel execution (all cores)
|
||||
│ ⊃ Incremental builds (content-aware hashing)
|
||||
│ ⊃ --affected flag (changed packages only)
|
||||
│ ⊃ Pruned subsets (deploy only what's needed)
|
||||
│ ⊃ Rust-powered core
|
||||
│
|
||||
├── Remote Caching
|
||||
│ → Vercel Account (free tier available)
|
||||
│ ↔ CI/CD pipelines (shared cache across machines)
|
||||
│
|
||||
├── Conformance (code quality + best-practice checks for monorepos)
|
||||
│ ⊃ Automated rule enforcement (ESLint, TypeScript, import boundaries)
|
||||
│ ↔ Turborepo (runs as part of task pipeline)
|
||||
│ ↔ Vercel Platform (enforced on deploy)
|
||||
│
|
||||
└── Integrations
|
||||
↔ Next.js (monorepo with multiple Next.js apps)
|
||||
↔ Vercel Platform (auto-detected, optimized builds)
|
||||
↔ Turbopack (per-app bundling)
|
||||
|
||||
TURBOPACK ⤳ skill: turbopack 📖 docs: https://turbo.build/pack/docs
|
||||
├── Purpose: JavaScript/TypeScript bundler
|
||||
│ ⊃ Instant HMR (doesn't degrade with app size)
|
||||
│ ⊃ Multi-environment builds (Browser, Server, Edge, SSR, RSC)
|
||||
│ ⊃ TypeScript, JSX, CSS, CSS Modules, WebAssembly
|
||||
│ ⊃ React Server Components (native support)
|
||||
│
|
||||
├── Status: Default bundler in Next.js 16
|
||||
│ → Next.js (top-level turbopack config)
|
||||
│ ⇢ alternative to: Webpack
|
||||
│
|
||||
└── Architecture
|
||||
⊃ Rust-powered
|
||||
⊃ Incremental computation engine
|
||||
⊃ Lives in the Next.js monorepo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Storage & Data
|
||||
|
||||
```
|
||||
VERCEL BLOB (active, first-party) ⤳ skill: vercel-storage 📖 docs: https://vercel.com/docs/storage/vercel-blob
|
||||
├── Purpose: File storage for unstructured data
|
||||
│ ⊃ Client uploads (up to 5 TB)
|
||||
│ ⊃ Conditional gets with ETags
|
||||
│ ⊃ @vercel/blob package
|
||||
│
|
||||
└── Use When: Media files, user uploads, large assets
|
||||
|
||||
VERCEL EDGE CONFIG (active, first-party) ⤳ skill: vercel-storage 📖 docs: https://vercel.com/docs/storage/edge-config
|
||||
├── Purpose: Global low-latency key-value for config
|
||||
│ ⊃ Feature flags
|
||||
│ ⊃ A/B testing configuration
|
||||
│ ⊃ Dynamic routing rules
|
||||
│ ⊃ @vercel/edge-config package (supports Next.js 16 cacheComponents)
|
||||
│
|
||||
└── Use When: Config that must be read at the edge instantly
|
||||
|
||||
MARKETPLACE STORAGE (partner-provided) ⤳ skill: vercel-storage
|
||||
├── Neon Postgres (replaces @vercel/postgres)
|
||||
│ ⊃ @neondatabase/serverless
|
||||
│ ⊃ Branching, auto-scaling
|
||||
│ ⇢ alternative to: @vercel/postgres (sunset)
|
||||
│
|
||||
├── Upstash Redis (replaces @vercel/kv)
|
||||
│ ⊃ @upstash/redis
|
||||
│ ⊃ Same Vercel billing integration
|
||||
│ ⇢ alternative to: @vercel/kv (sunset)
|
||||
│
|
||||
└── Other: MongoDB, PlanetScale, Supabase, etc.
|
||||
↔ Vercel Marketplace (one-click install, auto env vars)
|
||||
```
|
||||
|
||||
**IMPORTANT**: `@vercel/postgres` and `@vercel/kv` are **sunset**. Use Neon and Upstash respectively.
|
||||
|
||||
---
|
||||
|
||||
## 6. Security
|
||||
|
||||
```
|
||||
VERCEL FIREWALL ⤳ skill: vercel-firewall 📖 docs: https://vercel.com/docs/security/vercel-firewall
|
||||
├── DDoS Protection (automatic, all plans)
|
||||
│ ⊃ Layer 3/4 mitigation
|
||||
│ ⊃ Layer 7 protection
|
||||
│ ⊃ 40x faster with stream processing
|
||||
│
|
||||
├── Web Application Firewall (WAF)
|
||||
│ ⊃ Custom rules engine (path, user-agent, IP, geo, JA4)
|
||||
│ ⊃ Framework-aware rules (no regex needed)
|
||||
│ ⊃ Managed rulesets (OWASP Top 10, Enterprise)
|
||||
│ ⊃ Rate limiting
|
||||
│ ⊃ Bot Filter (public beta, all plans)
|
||||
│ ⊃ Attack Challenge Mode
|
||||
│ ⊃ Persistent Actions (block repeat offenders)
|
||||
│ ⊃ Firewall API (programmatic control)
|
||||
│ ⊃ 300ms global propagation
|
||||
│
|
||||
└── Integrations
|
||||
↔ Edge Network (embedded in request lifecycle)
|
||||
↔ Vercel Observability (linked logs)
|
||||
↔ Vercel REST API (Firewall API)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Observability
|
||||
|
||||
```
|
||||
VERCEL OBSERVABILITY ⤳ skill: observability 📖 docs: https://vercel.com/docs/analytics
|
||||
├── Web Analytics
|
||||
│ ⊃ First-party, privacy-friendly
|
||||
│ ⊃ Custom events (Pro/Enterprise)
|
||||
│ ⊃ UTM parameters (Analytics Plus)
|
||||
│ ↔ Next.js (@vercel/analytics)
|
||||
│
|
||||
├── Speed Insights
|
||||
│ ⊃ Real user performance data
|
||||
│ ⊃ Core Web Vitals
|
||||
│ ↔ Next.js (@vercel/speed-insights)
|
||||
│
|
||||
├── Monitoring & Logs
|
||||
│ ⊃ Real-time infrastructure logs
|
||||
│ ⊃ Function runtime logs
|
||||
│ ⊃ Custom queries and visualizations
|
||||
│
|
||||
├── Vercel Drains (export observability data)
|
||||
│ ⊃ OpenTelemetry-compatible traces
|
||||
│ ⊃ Web analytics events
|
||||
│ ⊃ Speed Insights metrics
|
||||
│ → Datadog, Honeycomb, Grafana Tempo, New Relic
|
||||
│
|
||||
└── Integrations
|
||||
↔ Vercel Firewall (security event logs)
|
||||
↔ Vercel Functions (automatic tracing)
|
||||
↔ Next.js (automatic instrumentation)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. CLI & API
|
||||
|
||||
```
|
||||
VERCEL CLI (vercel / vc) ⤳ skill: vercel-cli 📖 docs: https://vercel.com/docs/cli
|
||||
├── Deployment
|
||||
│ ⊃ vercel / vercel deploy (preview deployment)
|
||||
│ ⊃ vercel --prod (production deployment)
|
||||
│ ⊃ vercel build (local build)
|
||||
│ ⊃ vercel deploy --prebuilt (deploy build output only)
|
||||
│ ⊃ vercel promote / vercel rollback
|
||||
│
|
||||
├── Development
|
||||
│ ⊃ vercel dev (local dev server)
|
||||
│ ⊃ vercel link (connect to Vercel project)
|
||||
│ ⊃ vercel pull (pull env vars and project settings)
|
||||
│
|
||||
├── Environment Variables
|
||||
│ ⊃ vercel env ls / add / rm / pull
|
||||
│ ⊃ Branch-scoped variables
|
||||
│ ⊃ Sensitive variables (write-only)
|
||||
│
|
||||
├── Marketplace (NEW in 2026)
|
||||
│ ⊃ vercel integration discover (find integrations)
|
||||
│ ⊃ vercel integration guide (setup instructions)
|
||||
│ ⊃ --format=json (agent-friendly output)
|
||||
│
|
||||
├── Other
|
||||
│ ⊃ vercel logs (view function logs)
|
||||
│ ⊃ vercel inspect (deployment details)
|
||||
│ ⊃ vercel domains (manage domains)
|
||||
│ ⊃ vercel certs (SSL certificates)
|
||||
│ ⊃ vercel dns (DNS records)
|
||||
│ ⊃ vercel teams (team management)
|
||||
│
|
||||
└── CI/CD Integration
|
||||
⊃ VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID
|
||||
↔ Any CI provider (GitHub Actions, Azure DevOps, etc.)
|
||||
|
||||
VERCEL MCP SERVER (Official) ⤳ skill: vercel-api 📖 docs: https://vercel.com/docs/mcp
|
||||
├── URL: https://mcp.vercel.com
|
||||
│ ⊃ Streamable HTTP transport
|
||||
│ ⊃ OAuth 2.1 authentication (automatic)
|
||||
│ ⊃ Read-only in initial release (Beta)
|
||||
│
|
||||
├── MCP Tools
|
||||
│ ⊃ Search & navigate Vercel / Next.js / AI SDK docs
|
||||
│ ⊃ List & inspect projects and deployments
|
||||
│ ⊃ Query build logs and function invocation logs
|
||||
│ ⊃ List domains and environment variables
|
||||
│ ⊃ View team members and settings
|
||||
│
|
||||
├── Supported AI Clients
|
||||
│ ⊃ Claude Code (`claude mcp add --transport http vercel https://mcp.vercel.com`)
|
||||
│ ⊃ Cursor, VS Code (reviewed and approved clients)
|
||||
│
|
||||
└── Relationship to REST API
|
||||
→ Uses Vercel REST API under the hood
|
||||
↔ AI SDK MCP Client (@ai-sdk/mcp)
|
||||
|
||||
VERCEL REST API / @vercel/sdk ⤳ skill: vercel-api 📖 docs: https://vercel.com/docs/rest-api
|
||||
├── Endpoint Categories
|
||||
│ ⊃ /v1/deployments — Create, list, inspect, cancel
|
||||
│ ⊃ /v1/projects — CRUD, environment variables, domains
|
||||
│ ⊃ /v1/teams — Members, billing, settings
|
||||
│ ⊃ /v1/domains — Register, configure, transfer
|
||||
│ ⊃ /v1/dns — Record management
|
||||
│ ⊃ /v1/certs — SSL certificate management
|
||||
│ ⊃ /v1/secrets — Secret management
|
||||
│ ⊃ /v1/integrations — Marketplace integration management
|
||||
│ ⊃ /v1/edge-config — Edge Config management
|
||||
│ ⊃ /v1/firewall — WAF rule management
|
||||
│
|
||||
├── SDK (@vercel/sdk)
|
||||
│ ⊃ TypeScript SDK for all API endpoints
|
||||
│ ⊃ vercel.deployments, vercel.projects, etc.
|
||||
│
|
||||
└── Authentication
|
||||
⊃ Bearer Token (personal or team)
|
||||
⊃ OAuth (for integrations)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Marketplace
|
||||
|
||||
```
|
||||
VERCEL MARKETPLACE ⤳ skill: marketplace 📖 docs: https://vercel.com/marketplace
|
||||
├── Categories
|
||||
│ ⊃ Databases (Neon, MongoDB, Supabase, PlanetScale)
|
||||
│ ⊃ CMS (Sanity, Contentful, Storyblok)
|
||||
│ ⊃ Auth (Clerk, Auth0)
|
||||
│ ⊃ Payments (Stripe)
|
||||
│ ⊃ Feature Flags (LaunchDarkly, Statsig)
|
||||
│ ⊃ AI Agents (CodeRabbit, Corridor, Sourcery, Parallel)
|
||||
│ ⊃ Storage (Upstash Redis, Cloudinary)
|
||||
│ ⊃ Monitoring (Datadog, Sentry)
|
||||
│
|
||||
├── Features
|
||||
│ ⊃ Unified billing
|
||||
│ ⊃ One-click install
|
||||
│ ⊃ Auto-provisioned environment variables
|
||||
│ ⊃ CLI discovery (vercel integration discover/guide)
|
||||
│
|
||||
└── Integration
|
||||
↔ Vercel CLI (agent-friendly discovery)
|
||||
↔ Vercel REST API (programmatic management)
|
||||
↔ Environment Variables (auto-injected)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Decision Matrix — When to Use What
|
||||
|
||||
### Rendering Strategy
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Static content, rarely changes | SSG (`generateStaticParams`) | Fastest, cached at edge |
|
||||
| Static with periodic updates | ISR (`revalidate`) | Fresh enough, still fast |
|
||||
| Per-request dynamic data | SSR (Server Components) | Always fresh, streamed |
|
||||
| Mix of static shell + dynamic parts | Cache Components (`'use cache'`) | Best of both worlds |
|
||||
| Real-time interactive UI | Client Components | Full browser API access |
|
||||
|
||||
### Data Mutations
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Form submissions, in-app mutations | Server Actions | Integrated with caching, progressive enhancement |
|
||||
| Public API, webhooks, large uploads | Route Handlers | REST semantics, streaming support |
|
||||
| Scheduled tasks | Cron Jobs + Serverless Functions | Reliable scheduling |
|
||||
|
||||
### AI Features
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Chat interface | AI SDK `useChat` + `streamText` | Streaming UI, provider-agnostic |
|
||||
| Structured data extraction | AI SDK `generateObject` | Type-safe, schema-validated |
|
||||
| Multi-step agent | AI SDK `Agent` class | Loop control, tool calling |
|
||||
| Production agent (must not lose state) | Workflow DevKit `DurableAgent` | Survives crashes, observable |
|
||||
| Model routing / failover | AI Gateway | Provider routing, cost tracking |
|
||||
| Connect to external tools | AI SDK MCP Client | Standard protocol, OAuth |
|
||||
| Agent needs live Vercel state | Vercel MCP Server | Read projects, deployments, logs via MCP |
|
||||
| UI generation from prompts | v0 | Visual output, GitHub integration |
|
||||
|
||||
### Storage
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| File uploads, media | Vercel Blob | First-party, up to 5TB |
|
||||
| Feature flags, A/B config | Edge Config | Ultra-low latency at edge |
|
||||
| Relational database | Neon (via Marketplace) | Serverless Postgres, branching |
|
||||
| Key-value cache | Upstash Redis (via Marketplace) | Serverless Redis, same billing |
|
||||
|
||||
### Build & Monorepo
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Single Next.js app | Turbopack (default) | Fastest HMR, built-in |
|
||||
| Monorepo with multiple apps/packages | Turborepo | Caching, parallelism, affected |
|
||||
| Code quality enforcement in monorepo | Conformance | Automated best-practice checks |
|
||||
| Non-Next.js framework | Framework-native bundler | Vercel adapters handle deploy |
|
||||
|
||||
### Security
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| DDoS protection | Vercel Firewall (automatic) | Always on, all plans |
|
||||
| Custom traffic rules | WAF rules engine | Framework-aware, 300ms propagation |
|
||||
| Bot blocking | Bot Filter | One-click, public beta |
|
||||
| Rate limiting | WAF rate limiting | Per-endpoint control |
|
||||
| OWASP protection | Managed rulesets (Enterprise) | Industry-standard rules |
|
||||
| Compliance isolation (SOC2, HIPAA) | Secure Compute | Dedicated infrastructure, no shared tenancy |
|
||||
| Tokenless CI/CD deployments | OIDC Federation | Short-lived tokens, no secrets to rotate |
|
||||
|
||||
### Functions
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Standard server logic | Serverless Functions (Node.js) | Full Node.js, up to 14min (paid) |
|
||||
| Ultra-low latency, simple logic | Edge Functions | <1ms cold start, global |
|
||||
| Long-running with I/O waits | Fluid Compute | Shared instances, waitUntil |
|
||||
| AI streaming responses | Streaming Functions | SSE, zero config |
|
||||
| Scheduled execution | Cron Jobs | vercel.json schedule config |
|
||||
|
||||
---
|
||||
|
||||
## 11. Common Cross-Product Workflows
|
||||
|
||||
### 1. Build an AI Chatbot
|
||||
```
|
||||
Next.js (App Router) → AI SDK (useChat + streamText) → AI Gateway (model routing)
|
||||
→ Vercel Functions (streaming) → Vercel Platform (deploy)
|
||||
```
|
||||
|
||||
### 2. Build a Durable AI Agent
|
||||
```
|
||||
Next.js (API Route) → Workflow DevKit (DurableAgent) → AI SDK (tool calling)
|
||||
→ Neon Postgres (state) → Vercel Functions (step execution)
|
||||
```
|
||||
|
||||
### 3. Full-Stack SaaS App
|
||||
```
|
||||
Next.js (App Router) → Neon Postgres (data) → Clerk (auth, via Marketplace)
|
||||
→ Stripe (payments, via Marketplace) → Vercel Blob (uploads)
|
||||
→ Edge Config (feature flags) → Vercel Analytics
|
||||
```
|
||||
|
||||
### 4. Monorepo with Multiple Apps
|
||||
```
|
||||
Turborepo (orchestration) → Next.js App A → Vercel Platform (deploy)
|
||||
→ Next.js App B → Vercel Platform (deploy)
|
||||
→ Shared packages → Turbopack (bundling)
|
||||
→ Remote Cache → Vercel (shared across CI)
|
||||
```
|
||||
|
||||
### 5. Deploy with Custom CI
|
||||
```
|
||||
Git Push → CI Pipeline → vercel build → vercel deploy --prebuilt
|
||||
→ VERCEL_TOKEN auth → Preview URL → vercel promote (production)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Migration Awareness
|
||||
|
||||
| Deprecated | Replacement | Migration Path |
|
||||
|-----------|-------------|----------------|
|
||||
| `@vercel/postgres` | `@neondatabase/serverless` | Use `@neondatabase/vercel-postgres-compat` for drop-in |
|
||||
| `@vercel/kv` | `@upstash/redis` | Same billing, direct replacement |
|
||||
| `middleware.ts` (Next.js 16) | `proxy.ts` | Rename file, Node.js runtime only |
|
||||
| `experimental.turbopack` | `turbopack` (top-level) | Move config in next.config |
|
||||
| Sync Request APIs (Next.js 16) | Async Request APIs | `await cookies()`, `await headers()`, etc. |
|
||||
| PPR (Next.js 15 canary) | Cache Components | Follow Vercel migration guide |
|
||||
| AI SDK 5 | AI SDK 6 | Run `npx @ai-sdk/codemod v6` |
|
||||
| `parameters` (AI SDK tools) | `inputSchema` | Aligned with MCP spec |
|
||||
| `result` (AI SDK tools) | `output` | Aligned with MCP spec |
|
||||
@@ -0,0 +1,166 @@
|
||||
# Verification Checklist — Version-Sensitive Claims
|
||||
|
||||
> Every version-sensitive claim in this plugin is listed below with its source URL and a
|
||||
> last-verified date. Re-verify periodically (monthly or after major Vercel releases).
|
||||
> When verifying, update the **Last Verified** date and note any discrepancies.
|
||||
|
||||
---
|
||||
|
||||
## How to Re-Verify
|
||||
|
||||
1. Open each **Source URL** and confirm the claim still matches official documentation.
|
||||
2. Update the **Last Verified** column with today's date.
|
||||
3. If a claim has changed, update both the relevant file(s) and this checklist.
|
||||
4. Run `grep -c '⤳ skill:' assets/vercel-ecosystem-graph.md` and `ls skills/*/SKILL.md | wc -l` to confirm structural integrity.
|
||||
|
||||
---
|
||||
|
||||
## Next.js 16
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 1 | Next.js 16 uses React 19.2 and App Router as default | `skills/nextjs/SKILL.md`, `assets/vercel-ecosystem-graph.md` | https://nextjs.org/blog | 2026-03-03 |
|
||||
| 2 | `middleware.ts` renamed to `proxy.ts` in v16; runs on Node.js runtime (not Edge) | `skills/nextjs/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 64, Migration table) | https://nextjs.org/docs/app/api-reference/file-conventions/proxy | 2026-03-03 |
|
||||
| 3 | Cache Components (`'use cache'`) replace PPR from Next.js 15 canaries | `skills/nextjs/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 65, 74, 491, 594) | https://nextjs.org/docs/app/api-reference/directives/use-cache | 2026-03-03 |
|
||||
| 4 | Turbopack is the default bundler in Next.js 16 | `skills/nextjs/SKILL.md`, `skills/turbopack/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 78, 259) | https://nextjs.org/blog | 2026-03-03 |
|
||||
| 5 | Async Request APIs: `cookies()`, `headers()`, `params`, `searchParams` are all async | `skills/nextjs/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 593) | https://nextjs.org/docs/messages/sync-dynamic-apis | 2026-03-03 |
|
||||
| 6 | Turbopack config is top-level (moved from `experimental.turbopack`) | `skills/turbopack/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 592) | https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack | 2026-03-03 |
|
||||
|
||||
## AI SDK v6
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 7 | AI SDK v6 is current major version | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 107) | https://sdk.vercel.ai/docs | 2026-03-03 |
|
||||
| 8 | `Agent` class with `stopWhen`, `prepareStep` for agentic loops | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 117) | https://sdk.vercel.ai/docs/ai-sdk-core/agents | 2026-03-03 |
|
||||
| 9 | Tools use `inputSchema` (not `parameters`) and `output`/`outputSchema` (not `result`), aligned with MCP | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 115, 596–597) | https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling | 2026-03-03 |
|
||||
| 10 | DevTools available via `npx @ai-sdk/devtools` | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 120) | https://sdk.vercel.ai/docs/ai-sdk-core/devtools | 2026-03-03 |
|
||||
| 11 | MCP Integration via `@ai-sdk/mcp` with OAuth, Resources, Prompts, Elicitation | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 128–132) | https://sdk.vercel.ai/docs/ai-sdk-core/mcp | 2026-03-03 |
|
||||
| 12 | `mcp-to-ai-sdk` CLI for static tool generation | `assets/vercel-ecosystem-graph.md` (line 132) | https://sdk.vercel.ai/docs/ai-sdk-core/mcp | 2026-03-03 |
|
||||
| 13 | Global Provider System: `"provider/model"` format in v6 | `skills/ai-sdk/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 134) | https://sdk.vercel.ai/docs/ai-sdk-core/settings | 2026-03-03 |
|
||||
| 14 | Migration codemod: `npx @ai-sdk/codemod v6` | `assets/vercel-ecosystem-graph.md` (line 595) | https://sdk.vercel.ai/docs/migration | 2026-03-03 |
|
||||
| 15 | Model identifiers: `gpt-5-mini`, `claude-sonnet-4-6`, `gemini-2.5-flash` | `skills/ai-sdk/SKILL.md` | Provider docs (OpenAI, Anthropic, Google) | 2026-03-03 |
|
||||
|
||||
## Workflow DevKit (WDK)
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 16 | `'use workflow'` and `'use step'` directives | `skills/workflow/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 182–183) | https://vercel.com/docs/workflow | 2026-03-03 |
|
||||
| 17 | `DurableAgent` at `@workflow/ai/agent` wraps AI SDK Agent with durability | `skills/workflow/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 194–195) | https://vercel.com/docs/workflow | 2026-03-03 |
|
||||
| 18 | Worlds: Local (JSON), Vercel (managed), Self-hosted (Postgres, Redis) | `skills/workflow/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 188–191) | https://vercel.com/docs/workflow | 2026-03-03 |
|
||||
| 19 | Open source, no vendor lock-in | `assets/vercel-ecosystem-graph.md` (line 199) | https://github.com/vercel/workflow | 2026-03-03 |
|
||||
|
||||
## AI Gateway
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 20 | `@ai-sdk/gateway` package for AI Gateway routing | `skills/ai-gateway/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 144, 160) | https://vercel.com/docs/ai-gateway | 2026-03-03 |
|
||||
| 21 | <20ms routing latency | `assets/vercel-ecosystem-graph.md` (line 167) | https://vercel.com/docs/ai-gateway | 2026-03-03 |
|
||||
| 22 | Available since AI SDK 5.0.36+ | `skills/ai-gateway/SKILL.md` | https://vercel.com/docs/ai-gateway | 2026-03-03 |
|
||||
|
||||
## Vercel MCP Server
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 23 | Official MCP Server at `https://mcp.vercel.com` | `assets/vercel-ecosystem-graph.md` (line 411) | https://vercel.com/docs/mcp | 2026-03-03 |
|
||||
| 24 | Streamable HTTP transport, OAuth 2.1, read-only (Beta) | `assets/vercel-ecosystem-graph.md` (lines 412–414) | https://vercel.com/docs/mcp | 2026-03-03 |
|
||||
| 25 | Claude Code integration: `claude mcp add --transport http vercel https://mcp.vercel.com` | `assets/vercel-ecosystem-graph.md` (line 424) | https://vercel.com/docs/mcp | 2026-03-03 |
|
||||
|
||||
## Turbopack
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 26 | Instant HMR that doesn't degrade with app size | `skills/turbopack/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 254) | https://turbo.build/pack/docs | 2026-03-03 |
|
||||
| 27 | Multi-environment builds (Browser, Server, Edge, SSR, RSC) | `assets/vercel-ecosystem-graph.md` (line 255) | https://turbo.build/pack/docs | 2026-03-03 |
|
||||
|
||||
## Storage — Sunset Packages
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 28 | `@vercel/postgres` is sunset → use `@neondatabase/serverless` | `skills/vercel-storage/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 292–293, 589) | https://vercel.com/docs/storage | 2026-03-03 |
|
||||
| 29 | `@vercel/kv` is sunset → use `@upstash/redis` | `skills/vercel-storage/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 297–298, 590) | https://vercel.com/docs/storage | 2026-03-03 |
|
||||
| 30 | `@neondatabase/vercel-postgres-compat` available as drop-in replacement | `assets/vercel-ecosystem-graph.md` (line 589) | https://neon.tech/docs | 2026-03-03 |
|
||||
|
||||
## Edge Config
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 31 | `@vercel/edge-config` supports Next.js 16 cacheComponents | `assets/vercel-ecosystem-graph.md` (line 287) | https://vercel.com/docs/storage/edge-config | 2026-03-03 |
|
||||
|
||||
## Vercel Functions
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 32 | Serverless timeout: Hobby 10s, Pro 15s | `skills/vercel-functions/SKILL.md` | https://vercel.com/docs/functions/runtimes | 2026-03-03 |
|
||||
| 33 | Fluid Compute timeout: Hobby 60s, Pro/Enterprise 800s | `skills/vercel-functions/SKILL.md` | https://vercel.com/docs/functions/fluid-compute | 2026-03-03 |
|
||||
| 34 | Edge Functions cold start <1ms | `skills/vercel-functions/SKILL.md` | https://vercel.com/docs/functions/edge-functions | 2026-03-03 |
|
||||
|
||||
## Vercel Firewall
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 35 | 40x faster DDoS mitigation with stream processing | `assets/vercel-ecosystem-graph.md` (line 317) | https://vercel.com/docs/security/vercel-firewall | 2026-03-03 |
|
||||
| 36 | Bot Filter in public beta, all plans | `assets/vercel-ecosystem-graph.md` (line 324) | https://vercel.com/docs/security/vercel-firewall | 2026-03-03 |
|
||||
| 37 | 300ms global WAF propagation | `assets/vercel-ecosystem-graph.md` (line 328) | https://vercel.com/docs/security/vercel-firewall | 2026-03-03 |
|
||||
|
||||
## Vercel CLI
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 38 | `vercel integration discover` and `vercel integration guide` (new in 2026) | `skills/vercel-cli/SKILL.md`, `assets/vercel-ecosystem-graph.md` (lines 393–396) | https://vercel.com/docs/cli | 2026-03-03 |
|
||||
| 39 | `--format=json` for agent-friendly output | `skills/vercel-cli/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 396) | https://vercel.com/docs/cli | 2026-03-03 |
|
||||
|
||||
## v0
|
||||
|
||||
| # | Claim | Files | Source URL | Last Verified |
|
||||
|---|-------|-------|------------|---------------|
|
||||
| 40 | Agentic features (research, plan, debug, iterate) in 2026 | `skills/v0-dev/SKILL.md`, `assets/vercel-ecosystem-graph.md` (line 214) | https://v0.dev/docs | 2026-03-03 |
|
||||
| 41 | Multi-framework output (React, Vue, Svelte, HTML) | `assets/vercel-ecosystem-graph.md` (line 213) | https://v0.dev/docs | 2026-03-03 |
|
||||
|
||||
---
|
||||
|
||||
## Structural Integrity Checks
|
||||
|
||||
Run these commands to verify the plugin's internal consistency:
|
||||
|
||||
```bash
|
||||
# Count skill references in the graph (should be 17 as of 2026-03-03)
|
||||
grep -c '⤳ skill:' assets/vercel-ecosystem-graph.md
|
||||
|
||||
# Count skill files (should be 12 as of 2026-03-03)
|
||||
ls skills/*/SKILL.md | wc -l
|
||||
|
||||
# List unique skill names referenced in graph
|
||||
grep -o '⤳ skill: [a-z0-9-]*' assets/vercel-ecosystem-graph.md | sort -u
|
||||
|
||||
# List skill directory names
|
||||
ls skills/
|
||||
|
||||
# Orphan check: every skill dir should appear in a ⤳ skill: reference
|
||||
for skill in skills/*/; do
|
||||
name=$(basename "$skill")
|
||||
if ! grep -q "⤳ skill: $name" assets/vercel-ecosystem-graph.md; then
|
||||
echo "ORPHAN: $name has no graph reference"
|
||||
fi
|
||||
done
|
||||
|
||||
# Broken link check: every ⤳ skill: reference should have a matching directory
|
||||
grep -o '⤳ skill: [a-z0-9-]*' assets/vercel-ecosystem-graph.md | \
|
||||
sed 's/⤳ skill: //' | sort -u | while read name; do
|
||||
if [ ! -f "skills/$name/SKILL.md" ]; then
|
||||
echo "BROKEN: ⤳ skill: $name has no SKILL.md"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cross-Reference Audit Results (2026-03-03)
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| Total `⤳ skill:` references in graph | 17 |
|
||||
| Unique skill names referenced | 12 |
|
||||
| Total `skills/*/SKILL.md` files | 12 |
|
||||
| Broken references (graph → missing skill) | **0** |
|
||||
| Orphaned skills (skill → no graph reference) | **0** |
|
||||
| Graph sections without skill links | 2 (Observability §7, Marketplace §9 — noted gaps, not errors) |
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
description: Deploy the current project to Vercel. Pass "prod" or "production" as argument to deploy to production. Default is preview deployment.
|
||||
---
|
||||
|
||||
# Deploy to Vercel
|
||||
|
||||
Deploy the current project to Vercel using the CLI.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Check if the project is linked to Vercel (`vercel link` status)
|
||||
2. If not linked, run `vercel link` first
|
||||
3. Check for uncommitted changes and warn the user
|
||||
|
||||
If "$ARGUMENTS" contains "prod" or "production":
|
||||
- Run `vercel --prod` for a production deployment
|
||||
- Confirm with the user before deploying to production
|
||||
|
||||
Otherwise:
|
||||
- Run `vercel` for a preview deployment
|
||||
- Return the preview URL to the user
|
||||
|
||||
After deployment, show the deployment URL and suggest checking logs with `vercel logs <url>` if needed.
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
description: Manage Vercel environment variables. Commands include list, pull, add, remove. Use to sync environment variables between Vercel and your local development environment.
|
||||
---
|
||||
|
||||
# Vercel Environment Variables
|
||||
|
||||
Manage environment variables for the current Vercel project.
|
||||
|
||||
## Actions
|
||||
|
||||
Based on "$ARGUMENTS":
|
||||
|
||||
### "list" or "ls" or no arguments
|
||||
- Run `vercel env ls` to show all environment variables
|
||||
|
||||
### "pull"
|
||||
- Run `vercel env pull` to download env vars to `.env.local`
|
||||
- For production: `vercel env pull .env.production.local --environment=production`
|
||||
|
||||
### "add <NAME>"
|
||||
- Run `vercel env add <NAME>` and guide the user through setting the value
|
||||
- Ask which environments (production, preview, development)
|
||||
|
||||
### "rm <NAME>" or "remove <NAME>"
|
||||
- Run `vercel env rm <NAME>` with confirmation
|
||||
|
||||
Always remind the user to add `.env*.local` to `.gitignore` to avoid committing secrets.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
description: Discover and install Vercel Marketplace integrations. Use to find databases, CMS, auth providers, and other services available on the Vercel Marketplace.
|
||||
---
|
||||
|
||||
# Vercel Marketplace
|
||||
|
||||
Discover and install integrations from the Vercel Marketplace.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run `vercel integration discover --format=json` to list available integrations
|
||||
2. Present integrations organized by category (Database, CMS, Auth, Payments, etc.)
|
||||
3. If "$ARGUMENTS" specifies an integration name, run `vercel integration guide <name>` to show setup instructions
|
||||
4. Follow the guide to help the user install and configure the integration
|
||||
|
||||
The `--format=json` flag provides structured, parseable output. The guide command returns agent-friendly markdown with step-by-step setup instructions.
|
||||
|
||||
Common integrations:
|
||||
- **Neon** — Serverless Postgres (replaces @vercel/postgres)
|
||||
- **Upstash** — Serverless Redis (replaces @vercel/kv)
|
||||
- **Clerk** — Authentication
|
||||
- **Sanity** — Headless CMS
|
||||
- **Stripe** — Payments
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
description: Show the status of the current Vercel project — recent deployments, linked project info, and environment overview.
|
||||
---
|
||||
|
||||
# Vercel Project Status
|
||||
|
||||
Show a comprehensive status overview of the current Vercel project.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run `vercel ls` to show recent deployments (last 5)
|
||||
2. Run `vercel inspect` on the latest deployment to show details
|
||||
3. Run `vercel env ls` to show environment variable overview
|
||||
4. Check for `vercel.json` configuration and summarize key settings
|
||||
|
||||
Present the information in a clean, readable format:
|
||||
- Latest deployment URL and status
|
||||
- Environment variable count per environment
|
||||
- Any configured cron jobs
|
||||
- Function configuration (if custom)
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "prompt",
|
||||
"prompt": "Before writing or editing code in a Vercel/Next.js project, verify these deprecation and misuse checks:\n\n(1) SUNSET PACKAGES: Are you using `@vercel/postgres` or `@vercel/kv`? These are sunset — use `@neondatabase/serverless` and `@upstash/redis` instead.\n\n(2) AI SDK TOOL SCHEMA: Are you using `parameters` or `result` in AI SDK tool definitions? These are deprecated in v6 — use `inputSchema`/`outputSchema` instead.\n\n(3) PROXY FILE RENAME: Are you creating or editing `middleware.ts` in Next.js 16? It has been renamed to `proxy.ts`.\n\n(4) TURBOPACK CONFIG: Are you putting turbopack config under `experimental.turbopack` in next.config? In Next.js 16, turbopack config is top-level (e.g., `turbopack: { rules: {} }`).\n\n(5) CACHE COMPONENTS: Are you using `'use cache'` at the top of a Client Component (a file with `'use client'`)? `'use cache'` only works in Server Components and Server Actions. Move the cached logic to a separate Server Component or use `cacheLife`/`cacheTag` in a Server Action.\n\n(6) ASYNC REQUEST APIS: In Next.js 16, `cookies()`, `headers()`, and `params` are async. Are you calling them synchronously (e.g., `const cookieStore = cookies()`)? You must `await` them: `const cookieStore = await cookies()`.\n\n(7) AI SDK AGENT CLASS: Are you manually implementing a tool-call while-loop with `generateText`? In AI SDK v6, use the `Agent` class instead, which handles the tool loop automatically with built-in `maxSteps` and error handling.\n\n(8) VERCEL WORKFLOW IMPORT: Are you importing from `@vercel/workflow` directly for AI agent patterns? Use `@vercel/workflow/ai` for `DurableAgent` and `World` imports.\n\n(9) IMAGE COMPONENT: Are you using `<img>` tags in a Next.js project? Use `next/image` (`import Image from 'next/image'`) for automatic optimization, lazy loading, and CLS prevention."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "vercel-plugin",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "bun run scripts/validate.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
description: AI SDK v6 conventions. Enforces correct tool definitions (inputSchema/outputSchema), streaming patterns, and provider usage.
|
||||
alwaysApply: false
|
||||
globs: "**/*.{ts,tsx,js,jsx}"
|
||||
---
|
||||
|
||||
## AI SDK v6 Conventions
|
||||
|
||||
- Use `inputSchema` (not `parameters`) and `output`/`outputSchema` (not `result`) for tool definitions — aligned with MCP spec.
|
||||
- Always stream for user-facing AI: use `streamText` + `useChat`, not `generateText`.
|
||||
- Use the `Agent` class for multi-step reasoning instead of manual tool-calling loops.
|
||||
- Use `DurableAgent` from `@workflow/ai/agent` for production agents that must survive crashes.
|
||||
- Reference models as `"provider/model"` (e.g., `"openai/gpt-5-mini"`) to use AI Gateway automatically.
|
||||
- Use `@ai-sdk/mcp` (stable, not experimental) for MCP server connections.
|
||||
- Use `mcp-to-ai-sdk` CLI to generate static tool definitions from MCP servers for security.
|
||||
- Use AI SDK DevTools (`npx @ai-sdk/devtools`) during development for debugging.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
description: Next.js 16 App Router conventions and best practices. Enforces modern patterns for Server Components, Client Components, Server Actions, and data fetching.
|
||||
alwaysApply: true
|
||||
globs: "**/*.{ts,tsx,js,jsx}"
|
||||
---
|
||||
|
||||
## Next.js 16 Conventions
|
||||
|
||||
- Default to Server Components. Only add `'use client'` when you need interactivity or browser APIs.
|
||||
- Push `'use client'` boundaries as far down the component tree as possible.
|
||||
- Use Server Actions (`'use server'`) for data mutations, not Route Handlers (unless building a public API).
|
||||
- All request APIs are async in Next.js 16: `await cookies()`, `await headers()`, `await params`, `await searchParams`.
|
||||
- Use `proxy.ts` instead of `middleware.ts` (Next.js 16 rename). Proxy runs on Node.js runtime only.
|
||||
- Turbopack config is top-level in `next.config.ts`, not under `experimental.turbopack`.
|
||||
- Use Cache Components (`'use cache'`) instead of PPR for mixing static and dynamic content.
|
||||
- Prefer `next/image` for images and `next/font` for fonts — both optimize automatically on Vercel.
|
||||
- `@vercel/postgres` and `@vercel/kv` are sunset — use `@neondatabase/serverless` and `@upstash/redis`.
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
description: General Vercel platform best practices for deployment, environment variables, and function configuration.
|
||||
alwaysApply: true
|
||||
globs: "**/*.{ts,tsx,js,jsx,json}"
|
||||
---
|
||||
|
||||
## Vercel Best Practices
|
||||
|
||||
- Never hardcode secrets — use environment variables via `vercel env` or Marketplace auto-provisioning.
|
||||
- Add `.env*.local` to `.gitignore` — these files contain pulled secrets.
|
||||
- Use Fluid Compute for long-running functions — extends max duration to 800s on paid plans.
|
||||
- Use `waitUntil` (or `after` in Next.js) for background work after sending a response.
|
||||
- Configure cron jobs in `vercel.json` and verify with `CRON_SECRET` header.
|
||||
- Use `vercel deploy --prebuilt` in CI for fastest deploys (separate build from deploy).
|
||||
- For monorepos, use Turborepo with remote caching and `--affected` for efficient CI.
|
||||
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Structural validation for the Vercel ecosystem plugin.
|
||||
* Checks cross-references, frontmatter, manifest completeness, and hooks validity.
|
||||
*
|
||||
* Usage: bun run scripts/validate.ts
|
||||
* Exits 0 on success, non-zero on failure.
|
||||
*/
|
||||
|
||||
import { readdir, readFile, stat } from "node:fs/promises";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
const ROOT = resolve(import.meta.dirname, "..");
|
||||
const errors: string[] = [];
|
||||
|
||||
function fail(msg: string) {
|
||||
errors.push(msg);
|
||||
console.error(` ✗ ${msg}`);
|
||||
}
|
||||
|
||||
function pass(msg: string) {
|
||||
console.log(` ✓ ${msg}`);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await stat(path);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function parseFrontmatter(content: string): Record<string, string> | null {
|
||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (!match) return null;
|
||||
const pairs: Record<string, string> = {};
|
||||
for (const line of match[1].split("\n")) {
|
||||
const idx = line.indexOf(":");
|
||||
if (idx > 0) {
|
||||
pairs[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
||||
}
|
||||
}
|
||||
return pairs;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Validate ⤳ skill: references in ecosystem graph
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function validateGraphSkillRefs() {
|
||||
console.log("\n[1] Ecosystem graph → skill cross-references");
|
||||
|
||||
const graphPath = join(ROOT, "assets", "vercel-ecosystem-graph.md");
|
||||
if (!(await exists(graphPath))) {
|
||||
fail("assets/vercel-ecosystem-graph.md not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const graph = await readFile(graphPath, "utf-8");
|
||||
const refs = [...graph.matchAll(/⤳\s*skill:\s*([a-z][a-z0-9-]*)/g)].map((m) => m[1]);
|
||||
|
||||
if (refs.length === 0) {
|
||||
fail("No ⤳ skill: references found in ecosystem graph");
|
||||
return;
|
||||
}
|
||||
|
||||
const uniqueRefs = [...new Set(refs)];
|
||||
for (const name of uniqueRefs) {
|
||||
const skillPath = join(ROOT, "skills", name, "SKILL.md");
|
||||
if (await exists(skillPath)) {
|
||||
pass(`⤳ skill:${name} → skills/${name}/SKILL.md`);
|
||||
} else {
|
||||
fail(`⤳ skill:${name} referenced in graph but skills/${name}/SKILL.md not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Validate SKILL.md frontmatter
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function validateSkillFrontmatter(): Promise<string[]> {
|
||||
console.log("\n[2] SKILL.md YAML frontmatter");
|
||||
|
||||
const skillsDir = join(ROOT, "skills");
|
||||
const dirs = await readdir(skillsDir);
|
||||
const skillNames: string[] = [];
|
||||
|
||||
for (const dir of dirs.sort()) {
|
||||
const skillPath = join(skillsDir, dir, "SKILL.md");
|
||||
if (!(await exists(skillPath))) continue;
|
||||
|
||||
skillNames.push(dir);
|
||||
const content = await readFile(skillPath, "utf-8");
|
||||
const fm = parseFrontmatter(content);
|
||||
|
||||
if (!fm) {
|
||||
fail(`skills/${dir}/SKILL.md — missing YAML frontmatter`);
|
||||
continue;
|
||||
}
|
||||
if (!fm.name) {
|
||||
fail(`skills/${dir}/SKILL.md — frontmatter missing 'name' field`);
|
||||
}
|
||||
if (!fm.description) {
|
||||
fail(`skills/${dir}/SKILL.md — frontmatter missing 'description' field`);
|
||||
}
|
||||
if (fm.name && fm.description) {
|
||||
pass(`skills/${dir}/SKILL.md — name: "${fm.name}", description present`);
|
||||
}
|
||||
}
|
||||
|
||||
return skillNames;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. Validate plugin.json enumerates all capabilities
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function validatePluginJson(skillNames: string[]) {
|
||||
console.log("\n[3] plugin.json completeness");
|
||||
|
||||
const manifestPath = join(ROOT, ".plugin", "plugin.json");
|
||||
if (!(await exists(manifestPath))) {
|
||||
fail(".plugin/plugin.json not found");
|
||||
return;
|
||||
}
|
||||
|
||||
let manifest: any;
|
||||
try {
|
||||
manifest = JSON.parse(await readFile(manifestPath, "utf-8"));
|
||||
} catch (e) {
|
||||
fail(`.plugin/plugin.json is not valid JSON: ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skills
|
||||
const declaredSkills: string[] = manifest.skills ?? [];
|
||||
for (const name of skillNames) {
|
||||
if (declaredSkills.includes(name)) {
|
||||
pass(`plugin.json lists skill "${name}"`);
|
||||
} else {
|
||||
fail(`plugin.json missing skill "${name}"`);
|
||||
}
|
||||
}
|
||||
for (const name of declaredSkills) {
|
||||
if (!skillNames.includes(name)) {
|
||||
fail(`plugin.json lists skill "${name}" but skills/${name}/SKILL.md not found`);
|
||||
}
|
||||
}
|
||||
|
||||
// Agents
|
||||
const agentsDir = join(ROOT, "agents");
|
||||
if (await exists(agentsDir)) {
|
||||
const agentFiles = (await readdir(agentsDir)).filter((f) => f.endsWith(".md")).sort();
|
||||
const declaredAgents: string[] = manifest.agents ?? [];
|
||||
for (const f of agentFiles) {
|
||||
if (declaredAgents.includes(f)) {
|
||||
pass(`plugin.json lists agent "${f}"`);
|
||||
} else {
|
||||
fail(`plugin.json missing agent "${f}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commands
|
||||
const commandsDir = join(ROOT, "commands");
|
||||
if (await exists(commandsDir)) {
|
||||
const cmdFiles = (await readdir(commandsDir)).filter((f) => f.endsWith(".md")).sort();
|
||||
const declaredCmds: string[] = manifest.commands ?? [];
|
||||
for (const f of cmdFiles) {
|
||||
if (declaredCmds.includes(f)) {
|
||||
pass(`plugin.json lists command "${f}"`);
|
||||
} else {
|
||||
fail(`plugin.json missing command "${f}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules
|
||||
const rulesDir = join(ROOT, "rules");
|
||||
if (await exists(rulesDir)) {
|
||||
const ruleFiles = (await readdir(rulesDir)).filter((f) => f.endsWith(".mdc")).sort();
|
||||
const declaredRules: string[] = manifest.rules ?? [];
|
||||
for (const f of ruleFiles) {
|
||||
if (declaredRules.includes(f)) {
|
||||
pass(`plugin.json lists rule "${f}"`);
|
||||
} else {
|
||||
fail(`plugin.json missing rule "${f}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. Validate hooks.json
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function validateHooksJson() {
|
||||
console.log("\n[4] hooks.json validity");
|
||||
|
||||
const hooksPath = join(ROOT, "hooks", "hooks.json");
|
||||
if (!(await exists(hooksPath))) {
|
||||
fail("hooks/hooks.json not found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await readFile(hooksPath, "utf-8");
|
||||
JSON.parse(content);
|
||||
pass("hooks/hooks.json is valid JSON");
|
||||
} catch (e) {
|
||||
fail(`hooks/hooks.json is not valid JSON: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function main() {
|
||||
console.log("Vercel Plugin — Structural Validation\n" + "=".repeat(40));
|
||||
|
||||
await validateGraphSkillRefs();
|
||||
const skillNames = await validateSkillFrontmatter();
|
||||
await validatePluginJson(skillNames);
|
||||
await validateHooksJson();
|
||||
|
||||
console.log("\n" + "=".repeat(40));
|
||||
if (errors.length > 0) {
|
||||
console.error(`\nFAILED — ${errors.length} error(s)\n`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\nPASSED — all checks OK\n");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,416 @@
|
||||
---
|
||||
name: ai-gateway
|
||||
description: Vercel AI Gateway expert guidance. Use when configuring model routing, provider failover, cost tracking, or managing multiple AI providers through a unified API.
|
||||
---
|
||||
|
||||
# Vercel AI Gateway
|
||||
|
||||
You are an expert in the Vercel AI Gateway — a unified API for calling AI models with built-in routing, failover, cost tracking, and observability.
|
||||
|
||||
## Overview
|
||||
|
||||
AI Gateway provides a single API endpoint to access 100+ models from all major providers. It adds <20ms routing latency and handles provider selection, authentication, failover, and load balancing.
|
||||
|
||||
## Setup
|
||||
|
||||
The AI SDK automatically uses the AI Gateway when you pass a model string in `"provider/model"` format:
|
||||
|
||||
```ts
|
||||
import { generateText } from 'ai'
|
||||
import { gateway } from 'ai' // Available since AI SDK 5.0.36+
|
||||
|
||||
const result = await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt: 'Hello!',
|
||||
})
|
||||
```
|
||||
|
||||
No additional package needed — the `gateway` provider is built into the `ai` package.
|
||||
|
||||
## Provider Routing
|
||||
|
||||
Configure how AI Gateway routes requests across providers:
|
||||
|
||||
```ts
|
||||
const result = await generateText({
|
||||
model: gateway('anthropic/claude-sonnet-4-6'),
|
||||
prompt: 'Hello!',
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
// Try providers in order; failover to next on error
|
||||
order: ['bedrock', 'anthropic'],
|
||||
|
||||
// Restrict to specific providers only
|
||||
only: ['anthropic', 'vertex'],
|
||||
|
||||
// Fallback models if primary model fails
|
||||
models: ['openai/gpt-5-mini', 'google/gemini-2.5-flash'],
|
||||
|
||||
// Track usage per end-user
|
||||
user: 'user-123',
|
||||
|
||||
// Tag for cost attribution and filtering
|
||||
tags: ['feature:chat', 'env:production', 'team:growth'],
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Routing Options
|
||||
|
||||
| Option | Purpose |
|
||||
|--------|---------|
|
||||
| `order` | Provider priority list; try first, failover to next |
|
||||
| `only` | Restrict to specific providers |
|
||||
| `models` | Fallback model list if primary model unavailable |
|
||||
| `user` | End-user ID for usage tracking |
|
||||
| `tags` | Labels for cost attribution and reporting |
|
||||
|
||||
## Cache-Control Headers
|
||||
|
||||
AI Gateway supports response caching to reduce latency and cost for repeated or similar requests:
|
||||
|
||||
```ts
|
||||
const result = await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt: 'What is the capital of France?',
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
// Cache identical requests for 1 hour
|
||||
cacheControl: 'max-age=3600',
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Caching strategies
|
||||
|
||||
| Header Value | Behavior |
|
||||
|-------------|----------|
|
||||
| `max-age=3600` | Cache response for 1 hour |
|
||||
| `max-age=0` | Bypass cache, always call provider |
|
||||
| `s-maxage=86400` | Cache at the edge for 24 hours |
|
||||
| `stale-while-revalidate=600` | Serve stale for 10 min while refreshing in background |
|
||||
|
||||
### When to use caching
|
||||
|
||||
- **Static knowledge queries**: FAQs, translations, factual lookups — cache aggressively
|
||||
- **User-specific conversations**: Do not cache — each response depends on conversation history
|
||||
- **Embeddings**: Cache embedding results for identical inputs to save cost
|
||||
- **Structured extraction**: Cache when extracting structured data from identical documents
|
||||
|
||||
### Cache key composition
|
||||
|
||||
The cache key is derived from: model, prompt/messages, temperature, and other generation parameters. Changing any parameter produces a new cache key.
|
||||
|
||||
## Per-User Rate Limiting
|
||||
|
||||
Control usage at the individual user level to prevent abuse and manage costs:
|
||||
|
||||
```ts
|
||||
const result = await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt: userMessage,
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
user: userId, // Required for per-user rate limiting
|
||||
tags: ['feature:chat'],
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Rate limit configuration
|
||||
|
||||
Configure rate limits in the Vercel dashboard under AI Gateway settings:
|
||||
|
||||
- **Requests per minute per user**: Throttle individual users (e.g., 20 RPM)
|
||||
- **Tokens per day per user**: Cap daily token consumption (e.g., 100K tokens/day)
|
||||
- **Concurrent requests per user**: Limit parallel calls (e.g., 3 concurrent)
|
||||
|
||||
### Handling rate limit responses
|
||||
|
||||
When a user exceeds their limit, the gateway returns HTTP 429:
|
||||
|
||||
```ts
|
||||
import { generateText, APICallError } from 'ai'
|
||||
|
||||
try {
|
||||
const result = await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt: userMessage,
|
||||
providerOptions: { gateway: { user: userId } },
|
||||
})
|
||||
} catch (error) {
|
||||
if (APICallError.isInstance(error) && error.statusCode === 429) {
|
||||
const retryAfter = error.responseHeaders?.['retry-after']
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Rate limited', retryAfter }),
|
||||
{ status: 429 }
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
```
|
||||
|
||||
## Budget Alerts and Cost Controls
|
||||
|
||||
### Tagging for cost attribution
|
||||
|
||||
Use tags to track spend by feature, team, and environment:
|
||||
|
||||
```ts
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
tags: [
|
||||
'feature:document-qa',
|
||||
'team:product',
|
||||
'env:production',
|
||||
'tier:premium',
|
||||
],
|
||||
user: userId,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Setting up budget alerts
|
||||
|
||||
In the Vercel dashboard:
|
||||
|
||||
1. Navigate to **AI Gateway → Usage & Budgets**
|
||||
2. Set monthly budget thresholds (e.g., $500/month warning, $1000/month hard limit)
|
||||
3. Configure alert channels (email, Slack webhook, Vercel integration)
|
||||
4. Optionally set per-tag budgets for granular control
|
||||
|
||||
### Hard spending limits
|
||||
|
||||
When a hard limit is reached, the gateway returns HTTP 402 (Payment Required). Handle this gracefully:
|
||||
|
||||
```ts
|
||||
if (APICallError.isInstance(error) && error.statusCode === 402) {
|
||||
// Budget exceeded — degrade gracefully
|
||||
return fallbackResponse()
|
||||
}
|
||||
```
|
||||
|
||||
### Cost optimization patterns
|
||||
|
||||
- Use cheaper models for classification/routing, expensive models for generation
|
||||
- Cache embeddings and static queries (see Cache-Control above)
|
||||
- Set per-user daily token caps to prevent runaway usage
|
||||
- Monitor cost-per-feature with tags to identify optimization targets
|
||||
|
||||
## Audit Logging
|
||||
|
||||
AI Gateway logs every request for compliance and debugging:
|
||||
|
||||
### What's logged
|
||||
|
||||
- Timestamp, model, provider used
|
||||
- Input/output token counts
|
||||
- Latency (routing + provider)
|
||||
- User ID and tags
|
||||
- HTTP status code
|
||||
- Failover chain (which providers were tried)
|
||||
|
||||
### Accessing logs
|
||||
|
||||
- **Vercel Dashboard**: AI Gateway → Logs — filter by model, user, tag, status, date range
|
||||
- **Vercel API**: Query logs programmatically:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $VERCEL_TOKEN" \
|
||||
"https://api.vercel.com/v1/ai-gateway/logs?projectId=$PROJECT_ID&limit=100"
|
||||
```
|
||||
|
||||
- **Log Drains**: Forward AI Gateway logs to Datadog, Splunk, or other providers via Vercel Log Drains for long-term retention and custom dashboards
|
||||
|
||||
### Compliance considerations
|
||||
|
||||
- AI Gateway does not log prompt or completion content by default
|
||||
- Enable content logging in project settings if required for compliance
|
||||
- Logs are retained per your Vercel plan's retention policy
|
||||
- Use `user` field consistently to support audit trails
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### Provider unavailable
|
||||
|
||||
When a provider is down, the gateway automatically fails over if you configured `order` or `models`:
|
||||
|
||||
```ts
|
||||
const result = await generateText({
|
||||
model: gateway('anthropic/claude-sonnet-4-6'),
|
||||
prompt: 'Summarize this document',
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
order: ['anthropic', 'bedrock'], // Bedrock as fallback
|
||||
models: ['openai/gpt-5-mini'], // Final fallback model
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Quota exceeded at provider
|
||||
|
||||
If your provider API key hits its quota, the gateway tries the next provider in the `order` list. Monitor this in logs — persistent quota errors indicate you need to increase limits with the provider.
|
||||
|
||||
### Invalid model identifier
|
||||
|
||||
```ts
|
||||
// Bad — model doesn't exist
|
||||
model: gateway('openai/gpt-99') // Returns 400 with descriptive error
|
||||
|
||||
// Good — use models listed in Vercel docs
|
||||
model: gateway('openai/gpt-5-mini')
|
||||
```
|
||||
|
||||
### Timeout handling
|
||||
|
||||
Gateway has a default timeout per provider. For long-running generations, use streaming:
|
||||
|
||||
```ts
|
||||
import { streamText } from 'ai'
|
||||
|
||||
const result = streamText({
|
||||
model: gateway('anthropic/claude-sonnet-4-6'),
|
||||
prompt: longDocument,
|
||||
})
|
||||
|
||||
for await (const chunk of result.textStream) {
|
||||
process.stdout.write(chunk)
|
||||
}
|
||||
```
|
||||
|
||||
### Complete error handling template
|
||||
|
||||
```ts
|
||||
import { generateText, APICallError } from 'ai'
|
||||
|
||||
async function callAI(prompt: string, userId: string) {
|
||||
try {
|
||||
return await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt,
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
user: userId,
|
||||
order: ['openai', 'azure-openai'],
|
||||
models: ['anthropic/claude-haiku-4-5'],
|
||||
tags: ['feature:chat'],
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (!APICallError.isInstance(error)) throw error
|
||||
|
||||
switch (error.statusCode) {
|
||||
case 402: return { text: 'Budget limit reached. Please try again later.' }
|
||||
case 429: return { text: 'Too many requests. Please slow down.' }
|
||||
case 503: return { text: 'AI service temporarily unavailable.' }
|
||||
default: throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Gateway vs Direct Provider — Decision Tree
|
||||
|
||||
Use this to decide whether to route through AI Gateway or call a provider SDK directly:
|
||||
|
||||
```
|
||||
Need failover across providers?
|
||||
└─ Yes → Use Gateway
|
||||
└─ No
|
||||
Need cost tracking / budget alerts?
|
||||
└─ Yes → Use Gateway
|
||||
└─ No
|
||||
Need per-user rate limiting?
|
||||
└─ Yes → Use Gateway
|
||||
└─ No
|
||||
Need audit logging?
|
||||
└─ Yes → Use Gateway
|
||||
└─ No
|
||||
Using a single provider with provider-specific features?
|
||||
└─ Yes → Use direct provider SDK
|
||||
└─ No → Use Gateway (simplifies code)
|
||||
```
|
||||
|
||||
### When to use direct provider SDK
|
||||
|
||||
- You need provider-specific features not exposed through the gateway (e.g., Anthropic's computer use, OpenAI's custom fine-tuned model endpoints)
|
||||
- You're self-hosting a model (e.g., vLLM, Ollama) that isn't registered with the gateway
|
||||
- You need request-level control over HTTP transport (custom proxies, mTLS)
|
||||
|
||||
### When to always use Gateway
|
||||
|
||||
- Production applications — failover and observability are essential
|
||||
- Multi-tenant SaaS — per-user tracking and rate limiting
|
||||
- Teams with cost accountability — tag-based budgeting
|
||||
|
||||
## Supported Providers
|
||||
|
||||
- OpenAI (GPT-5.x, o-series)
|
||||
- Anthropic (Claude 4.x)
|
||||
- Google (Gemini)
|
||||
- xAI (Grok)
|
||||
- Mistral
|
||||
- DeepSeek
|
||||
- Amazon Bedrock
|
||||
- Azure OpenAI
|
||||
- Cohere
|
||||
- Perplexity
|
||||
- Alibaba (Qwen)
|
||||
- Meta (Llama)
|
||||
- And many more (100+ models total)
|
||||
|
||||
## Pricing
|
||||
|
||||
- **Vercel-managed keys**: Tokens at provider list price, no markup
|
||||
- **Bring Your Own Key**: 0% markup on token costs
|
||||
- **Free tier**: $5 credits every 30 days on any Vercel account
|
||||
|
||||
## Multimodal Support
|
||||
|
||||
Text, image, and video generation all route through the gateway:
|
||||
|
||||
```ts
|
||||
// Text
|
||||
const { text } = await generateText({
|
||||
model: gateway('openai/gpt-5-mini'),
|
||||
prompt: 'Hello',
|
||||
})
|
||||
|
||||
// Image
|
||||
const { image } = await generateImage({
|
||||
model: gateway('openai/dall-e-3'),
|
||||
prompt: 'A sunset',
|
||||
})
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
1. **Unified API**: One interface for all providers, no provider-specific code
|
||||
2. **Automatic failover**: If a provider is down, requests route to the next
|
||||
3. **Cost tracking**: Per-user, per-feature attribution with tags
|
||||
4. **Observability**: Built-in monitoring of all model calls
|
||||
5. **Low latency**: <20ms routing overhead
|
||||
6. **No lock-in**: Switch models/providers by changing a string
|
||||
|
||||
## When to Use AI Gateway
|
||||
|
||||
| Scenario | Use Gateway? |
|
||||
|----------|-------------|
|
||||
| Production app with AI features | Yes — failover, cost tracking |
|
||||
| Prototyping with single provider | Optional — direct provider works fine |
|
||||
| Multi-provider setup | Yes — unified routing |
|
||||
| Need provider-specific features | Use direct provider SDK + Gateway as fallback |
|
||||
| Cost tracking and budgeting | Yes — user tracking and tags |
|
||||
| Multi-tenant SaaS | Yes — per-user rate limiting and audit |
|
||||
| Compliance requirements | Yes — audit logging and log drains |
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [AI Gateway](https://vercel.com/docs/ai-gateway)
|
||||
- [Providers and Models](https://sdk.vercel.ai/docs/foundations/providers-and-models)
|
||||
- [AI SDK Core](https://sdk.vercel.ai/docs/ai-sdk-core)
|
||||
@@ -0,0 +1,331 @@
|
||||
---
|
||||
name: ai-sdk
|
||||
description: Vercel AI SDK expert guidance. Use when building AI-powered features — chat interfaces, text generation, structured output, tool calling, agents, MCP integration, streaming, embeddings, reranking, image generation, or working with any LLM provider.
|
||||
---
|
||||
|
||||
# Vercel AI SDK (v6)
|
||||
|
||||
You are an expert in the Vercel AI SDK v6. The AI SDK is the leading TypeScript toolkit for building AI-powered applications. It provides a unified API across all LLM providers.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install ai @ai-sdk/openai # or @ai-sdk/anthropic, @ai-sdk/google, etc.
|
||||
```
|
||||
|
||||
## Global Provider System
|
||||
|
||||
In AI SDK 6, reference models using `"provider/model"` format. The gateway provider is available from the `ai` package.
|
||||
|
||||
```ts
|
||||
import { gateway } from 'ai'
|
||||
|
||||
const model = gateway('openai/gpt-5-mini')
|
||||
// or: gateway('anthropic/claude-sonnet-4-6')
|
||||
// or: gateway('google/gemini-2.5-flash')
|
||||
```
|
||||
|
||||
When using the AI Gateway on Vercel, this automatically routes through the gateway with failover, cost tracking, and observability.
|
||||
|
||||
## Core Functions
|
||||
|
||||
### Text Generation
|
||||
```ts
|
||||
import { generateText, streamText } from 'ai'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
|
||||
// Non-streaming
|
||||
const { text } = await generateText({
|
||||
model: openai('gpt-5-mini'),
|
||||
prompt: 'Explain quantum computing in simple terms.',
|
||||
})
|
||||
|
||||
// Streaming
|
||||
const result = streamText({
|
||||
model: openai('gpt-5-mini'),
|
||||
prompt: 'Write a poem about coding.',
|
||||
})
|
||||
|
||||
for await (const chunk of result.textStream) {
|
||||
process.stdout.write(chunk)
|
||||
}
|
||||
```
|
||||
|
||||
### Structured Output
|
||||
```ts
|
||||
import { generateObject } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
const { object } = await generateObject({
|
||||
model: openai('gpt-5-mini'),
|
||||
schema: z.object({
|
||||
recipe: z.object({
|
||||
name: z.string(),
|
||||
ingredients: z.array(z.object({
|
||||
name: z.string(),
|
||||
amount: z.string(),
|
||||
})),
|
||||
steps: z.array(z.string()),
|
||||
}),
|
||||
}),
|
||||
prompt: 'Generate a recipe for chocolate chip cookies.',
|
||||
})
|
||||
```
|
||||
|
||||
### Tool Calling (MCP-Aligned)
|
||||
|
||||
In AI SDK 6, tools use `inputSchema` (not `parameters`) and `output`/`outputSchema` (not `result`), aligned with the MCP specification.
|
||||
|
||||
```ts
|
||||
import { generateText, tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-5-mini'),
|
||||
tools: {
|
||||
weather: tool({
|
||||
description: 'Get the weather for a location',
|
||||
inputSchema: z.object({
|
||||
city: z.string().describe('The city name'),
|
||||
}),
|
||||
outputSchema: z.object({
|
||||
temperature: z.number(),
|
||||
condition: z.string(),
|
||||
}),
|
||||
execute: async ({ city }) => {
|
||||
const data = await fetchWeather(city)
|
||||
return { temperature: data.temp, condition: data.condition }
|
||||
},
|
||||
}),
|
||||
},
|
||||
prompt: 'What is the weather in San Francisco?',
|
||||
})
|
||||
```
|
||||
|
||||
### Dynamic Tools (MCP Integration)
|
||||
|
||||
For tools with schemas not known at compile time (e.g., MCP server tools):
|
||||
|
||||
```ts
|
||||
import { dynamicTool } from 'ai'
|
||||
|
||||
const tools = {
|
||||
unknownTool: dynamicTool({
|
||||
description: 'A tool discovered at runtime',
|
||||
execute: async (input) => {
|
||||
// Handle dynamically
|
||||
return { result: 'done' }
|
||||
},
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
### Agents
|
||||
|
||||
The Agent class wraps `generateText`/`streamText` with agentic loop control:
|
||||
|
||||
```ts
|
||||
import { Agent } from 'ai'
|
||||
|
||||
const agent = new Agent({
|
||||
model: openai('gpt-5-mini'),
|
||||
tools: { weather, search, calculator },
|
||||
system: 'You are a helpful assistant.',
|
||||
stopWhen: (context) => context.toolCalls.length === 0, // Stop when no tools called
|
||||
prepareStep: (context) => ({
|
||||
// Customize each step
|
||||
toolChoice: context.steps.length > 5 ? 'none' : 'auto',
|
||||
}),
|
||||
})
|
||||
|
||||
const { text } = await agent.generateText({
|
||||
prompt: 'Research the weather in Tokyo and calculate the average temperature this week.',
|
||||
})
|
||||
```
|
||||
|
||||
### MCP Client
|
||||
|
||||
Connect to any MCP server and use its tools:
|
||||
|
||||
```ts
|
||||
import { createMCPClient } from '@ai-sdk/mcp'
|
||||
|
||||
const mcpClient = await createMCPClient({
|
||||
transport: {
|
||||
type: 'sse',
|
||||
url: 'https://my-mcp-server.com/sse',
|
||||
},
|
||||
})
|
||||
|
||||
const tools = await mcpClient.tools()
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-5-mini'),
|
||||
tools,
|
||||
prompt: 'Use the available tools to help the user.',
|
||||
})
|
||||
|
||||
await mcpClient.close()
|
||||
```
|
||||
|
||||
MCP OAuth for remote servers is handled automatically by `@ai-sdk/mcp`.
|
||||
|
||||
### Embeddings & Reranking
|
||||
|
||||
```ts
|
||||
import { embed, embedMany, rerank } from 'ai'
|
||||
|
||||
// Single embedding
|
||||
const { embedding } = await embed({
|
||||
model: openai.embedding('text-embedding-3-small'),
|
||||
value: 'The quick brown fox',
|
||||
})
|
||||
|
||||
// Batch embeddings
|
||||
const { embeddings } = await embedMany({
|
||||
model: openai.embedding('text-embedding-3-small'),
|
||||
values: ['text 1', 'text 2', 'text 3'],
|
||||
})
|
||||
|
||||
// Rerank search results by relevance
|
||||
const { results } = await rerank({
|
||||
model: cohere.reranker('rerank-v3.5'),
|
||||
query: 'What is quantum computing?',
|
||||
documents: searchResults,
|
||||
})
|
||||
```
|
||||
|
||||
### Image Generation & Editing
|
||||
|
||||
```ts
|
||||
import { generateImage, editImage } from 'ai'
|
||||
|
||||
const { image } = await generateImage({
|
||||
model: openai.image('dall-e-3'),
|
||||
prompt: 'A futuristic cityscape at sunset',
|
||||
})
|
||||
|
||||
const { image: edited } = await editImage({
|
||||
model: openai.image('dall-e-3'),
|
||||
image: originalImage,
|
||||
prompt: 'Add flying cars to the scene',
|
||||
})
|
||||
```
|
||||
|
||||
## UI Hooks (React)
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useChat, useCompletion, useObject } from '@ai-sdk/react'
|
||||
|
||||
// Chat interface
|
||||
function Chat() {
|
||||
const { messages, input, handleInputChange, handleSubmit } = useChat()
|
||||
return (
|
||||
<div>
|
||||
{messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input value={input} onChange={handleInputChange} />
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `useChat` hook connects to a Route Handler or Server Action that uses `streamText`.
|
||||
|
||||
### Server-side for useChat
|
||||
```ts
|
||||
// app/api/chat/route.ts
|
||||
import { streamText } from 'ai'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { messages } = await req.json()
|
||||
const result = streamText({
|
||||
model: openai('gpt-5-mini'),
|
||||
messages,
|
||||
})
|
||||
return result.toDataStreamResponse()
|
||||
}
|
||||
```
|
||||
|
||||
## Language Model Middleware
|
||||
|
||||
Intercept and transform model calls for RAG, guardrails, logging:
|
||||
|
||||
```ts
|
||||
import { wrapLanguageModel } from 'ai'
|
||||
|
||||
const wrappedModel = wrapLanguageModel({
|
||||
model: openai('gpt-5-mini'),
|
||||
middleware: {
|
||||
transformParams: async ({ params }) => {
|
||||
// Inject RAG context, modify system prompt, etc.
|
||||
return { ...params, system: params.system + '\n\nContext: ...' }
|
||||
},
|
||||
wrapGenerate: async ({ doGenerate }) => {
|
||||
const result = await doGenerate()
|
||||
// Post-process, log, validate guardrails
|
||||
return result
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Provider Routing via AI Gateway
|
||||
|
||||
```ts
|
||||
import { generateText } from 'ai'
|
||||
import { gateway } from 'ai'
|
||||
|
||||
const result = await generateText({
|
||||
model: gateway('anthropic/claude-sonnet-4-6'),
|
||||
prompt: 'Hello!',
|
||||
providerOptions: {
|
||||
gateway: {
|
||||
order: ['bedrock', 'anthropic'], // Try Bedrock first
|
||||
models: ['openai/gpt-5-mini'], // Fallback model
|
||||
only: ['anthropic', 'bedrock'], // Restrict providers
|
||||
user: 'user-123', // Usage tracking
|
||||
tags: ['feature:chat', 'env:production'], // Cost attribution
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## DevTools
|
||||
|
||||
```bash
|
||||
npx @ai-sdk/devtools
|
||||
# Opens http://localhost:4983 — inspect LLM calls, agents, token usage, timing
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
1. **Always stream for user-facing AI** — use `streamText` + `useChat`, not `generateText`
|
||||
2. **Use structured output** for extracting data — `generateObject` with Zod schemas
|
||||
3. **Use the Agent class** for multi-step reasoning — not manual loops
|
||||
4. **Use DurableAgent** (from Workflow DevKit) for production agents that must survive crashes
|
||||
5. **Use AI Gateway** for model routing and cost tracking in production
|
||||
6. **Use `mcp-to-ai-sdk`** to generate static tool definitions from MCP servers for security
|
||||
|
||||
## Migration from AI SDK 5
|
||||
|
||||
Run `npx @ai-sdk/codemod v6` to auto-migrate. Key changes:
|
||||
- `parameters` → `inputSchema`
|
||||
- `result` → `output`
|
||||
- `experimental_createMCPClient` → `createMCPClient` (stable)
|
||||
- UIMessage / ModelMessage types introduced
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [AI SDK Documentation](https://sdk.vercel.ai/docs)
|
||||
- [AI SDK Core](https://sdk.vercel.ai/docs/ai-sdk-core)
|
||||
- [AI SDK UI](https://sdk.vercel.ai/docs/ai-sdk-ui)
|
||||
- [Generating Text](https://sdk.vercel.ai/docs/ai-sdk-core/generating-text)
|
||||
- [Structured Data](https://sdk.vercel.ai/docs/ai-sdk-core/generating-structured-data)
|
||||
- [Tools and Tool Calling](https://sdk.vercel.ai/docs/ai-sdk-core/tools-and-tool-calling)
|
||||
- [Agents](https://sdk.vercel.ai/docs/ai-sdk-core/agents)
|
||||
- [Providers and Models](https://sdk.vercel.ai/docs/foundations/providers-and-models)
|
||||
- [Provider Directory](https://sdk.vercel.ai/providers)
|
||||
@@ -0,0 +1,57 @@
|
||||
# AI SDK — Provider Reference
|
||||
|
||||
## Global Provider System
|
||||
|
||||
Use `"provider/model"` format for automatic AI Gateway routing:
|
||||
|
||||
```ts
|
||||
import { gateway } from 'ai'
|
||||
const model = gateway('openai/gpt-5-mini')
|
||||
```
|
||||
|
||||
## Provider Packages
|
||||
|
||||
| Provider | Package | Example Models |
|
||||
|----------|---------|---------------|
|
||||
| OpenAI | `@ai-sdk/openai` | `gpt-5`, `gpt-5-mini`, `gpt-5.3-codex`, `o3`, `o4-mini` |
|
||||
| Anthropic | `@ai-sdk/anthropic` | `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5` |
|
||||
| Google | `@ai-sdk/google` | `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-flash` |
|
||||
| xAI | `@ai-sdk/xai` | `grok-4.1` |
|
||||
| Mistral | `@ai-sdk/mistral` | `mistral-large`, `mistral-small` |
|
||||
| Cohere | `@ai-sdk/cohere` | `command-r-plus`, `rerank-v3.5` |
|
||||
| Amazon Bedrock | `@ai-sdk/amazon-bedrock` | All Bedrock models |
|
||||
| Azure OpenAI | `@ai-sdk/azure` | Azure-hosted OpenAI models |
|
||||
| DeepSeek | `@ai-sdk/deepseek` | `deepseek-r1`, `deepseek-v3` |
|
||||
| Perplexity | `@ai-sdk/perplexity` | `sonar-pro`, `sonar` |
|
||||
| AI Gateway | `@ai-sdk/gateway` | Routes to any provider |
|
||||
|
||||
## Model Selection Guide
|
||||
|
||||
| Use Case | Recommended | Why |
|
||||
|----------|-------------|-----|
|
||||
| Fast chat responses | `gpt-5-mini`, `gemini-2.5-flash`, `claude-haiku-4-5` | Low latency, low cost |
|
||||
| Complex reasoning | `gpt-5`, `claude-opus-4-6`, `gemini-2.5-pro` | Best reasoning |
|
||||
| Code generation | `gpt-5.3-codex`, `claude-sonnet-4-6` | Code-optimized |
|
||||
| Embeddings | `text-embedding-3-small` (OpenAI) | Cost-effective, good quality |
|
||||
| Embeddings (high-quality) | `text-embedding-3-large` (OpenAI) | Best quality |
|
||||
| Image generation | `dall-e-3` (OpenAI) | General purpose |
|
||||
| Reranking | `rerank-v3.5` (Cohere) | Relevance reordering |
|
||||
|
||||
## Direct Provider Usage
|
||||
|
||||
```ts
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { anthropic } from '@ai-sdk/anthropic'
|
||||
import { google } from '@ai-sdk/google'
|
||||
|
||||
// Text models
|
||||
const gpt = openai('gpt-5-mini')
|
||||
const claude = anthropic('claude-sonnet-4-6')
|
||||
const gemini = google('gemini-2.5-flash')
|
||||
|
||||
// Embedding models
|
||||
const embedder = openai.embedding('text-embedding-3-small')
|
||||
|
||||
// Image models
|
||||
const imageGen = openai.image('dall-e-3')
|
||||
```
|
||||
@@ -0,0 +1,275 @@
|
||||
---
|
||||
name: marketplace
|
||||
description: Vercel Marketplace expert guidance — discovering, installing, and building integrations, auto-provisioned environment variables, unified billing, and the vercel integration CLI. Use when consuming third-party services, building custom integrations, or managing marketplace resources on Vercel.
|
||||
---
|
||||
|
||||
# Vercel Marketplace
|
||||
|
||||
You are an expert in the Vercel Marketplace — the integration platform that connects third-party services to Vercel projects with unified billing, auto-provisioned environment variables, and one-click setup.
|
||||
|
||||
## Consuming Integrations
|
||||
|
||||
### Discovering Integrations
|
||||
|
||||
```bash
|
||||
# Browse available integrations
|
||||
vercel integration discover
|
||||
|
||||
# Get guided setup for a category
|
||||
vercel integration guide database
|
||||
|
||||
# Search for a specific integration
|
||||
vercel integration discover --search "redis"
|
||||
```
|
||||
|
||||
### Installing an Integration
|
||||
|
||||
```bash
|
||||
# Install from CLI
|
||||
vercel integration add <integration-name>
|
||||
|
||||
# Examples
|
||||
vercel integration add neon # Postgres database
|
||||
vercel integration add upstash # Redis / Kafka
|
||||
vercel integration add clerk # Authentication
|
||||
vercel integration add sentry # Error monitoring
|
||||
vercel integration add sanity # CMS
|
||||
```
|
||||
|
||||
### Auto-Provisioned Environment Variables
|
||||
|
||||
When you install a Marketplace integration, Vercel automatically provisions the required environment variables for all linked projects.
|
||||
|
||||
```bash
|
||||
# View environment variables added by integrations
|
||||
vercel env ls
|
||||
|
||||
# Example: after installing Neon, these are auto-provisioned:
|
||||
# POSTGRES_URL — connection string
|
||||
# POSTGRES_URL_NON_POOLING — direct connection
|
||||
# POSTGRES_USER — database user
|
||||
# POSTGRES_PASSWORD — database password
|
||||
# POSTGRES_DATABASE — database name
|
||||
# POSTGRES_HOST — database host
|
||||
```
|
||||
|
||||
No manual `.env` file management is needed — the variables are injected into all environments (Development, Preview, Production) automatically.
|
||||
|
||||
### Using Provisioned Resources
|
||||
|
||||
```ts
|
||||
// app/api/users/route.ts — using Neon auto-provisioned env vars
|
||||
import { neon } from '@neondatabase/serverless'
|
||||
|
||||
// POSTGRES_URL is auto-injected by the Neon integration
|
||||
const sql = neon(process.env.POSTGRES_URL!)
|
||||
|
||||
export async function GET() {
|
||||
const users = await sql`SELECT * FROM users LIMIT 10`
|
||||
return Response.json(users)
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// app/api/cache/route.ts — using Upstash auto-provisioned env vars
|
||||
import { Redis } from '@upstash/redis'
|
||||
|
||||
// KV_REST_API_URL and KV_REST_API_TOKEN are auto-injected
|
||||
const redis = Redis.fromEnv()
|
||||
|
||||
export async function GET() {
|
||||
const cached = await redis.get('featured-products')
|
||||
return Response.json(cached)
|
||||
}
|
||||
```
|
||||
|
||||
### Managing Integrations
|
||||
|
||||
```bash
|
||||
# List installed integrations
|
||||
vercel integration ls
|
||||
|
||||
# Remove an integration
|
||||
vercel integration remove <integration-name>
|
||||
```
|
||||
|
||||
## Unified Billing
|
||||
|
||||
Marketplace integrations use Vercel's unified billing system:
|
||||
|
||||
- **Single invoice**: All integration charges appear on your Vercel bill
|
||||
- **Usage-based**: Pay for what you use, scaled per integration's pricing model
|
||||
- **Team-level billing**: Charges roll up to the Vercel team account
|
||||
- **No separate accounts**: No need to manage billing with each provider individually
|
||||
|
||||
## Building Integrations
|
||||
|
||||
### Integration Architecture
|
||||
|
||||
Vercel integrations consist of:
|
||||
|
||||
1. **Integration manifest** — declares capabilities, required scopes, and UI surfaces
|
||||
2. **Webhook handlers** — respond to Vercel lifecycle events
|
||||
3. **UI components** — optional dashboard panels rendered within Vercel
|
||||
4. **Resource provisioning** — create and manage resources for users
|
||||
|
||||
### Scaffold an Integration
|
||||
|
||||
```bash
|
||||
# Create a new integration project
|
||||
npx create-vercel-integration my-integration
|
||||
|
||||
# Or start from the template
|
||||
npx create-next-app my-integration --example vercel-integration
|
||||
```
|
||||
|
||||
### Integration Manifest
|
||||
|
||||
```json
|
||||
// vercel-integration.json
|
||||
{
|
||||
"name": "my-integration",
|
||||
"slug": "my-integration",
|
||||
"description": "Provides X for Vercel projects",
|
||||
"logo": "public/logo.svg",
|
||||
"website": "https://my-service.com",
|
||||
"categories": ["databases"],
|
||||
"scopes": {
|
||||
"project": ["env-vars:read-write"],
|
||||
"team": ["integrations:read-write"]
|
||||
},
|
||||
"installationType": "marketplace",
|
||||
"resourceTypes": [
|
||||
{
|
||||
"name": "database",
|
||||
"displayName": "Database",
|
||||
"description": "A managed database instance"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Handling Lifecycle Webhooks
|
||||
|
||||
```ts
|
||||
// app/api/webhook/route.ts
|
||||
import { verifyVercelSignature } from '@vercel/integration-utils'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const body = await req.json()
|
||||
|
||||
// Verify the webhook is from Vercel
|
||||
const isValid = await verifyVercelSignature(req, body)
|
||||
if (!isValid) {
|
||||
return Response.json({ error: 'Invalid signature' }, { status: 401 })
|
||||
}
|
||||
|
||||
switch (body.type) {
|
||||
case 'integration.installed':
|
||||
// Provision resources for the new installation
|
||||
await provisionDatabase(body.payload)
|
||||
break
|
||||
|
||||
case 'integration.uninstalled':
|
||||
// Clean up resources
|
||||
await deprovisionDatabase(body.payload)
|
||||
break
|
||||
|
||||
case 'integration.configuration-updated':
|
||||
// Handle config changes
|
||||
await updateConfiguration(body.payload)
|
||||
break
|
||||
}
|
||||
|
||||
return Response.json({ received: true })
|
||||
}
|
||||
```
|
||||
|
||||
### Provisioning Environment Variables
|
||||
|
||||
```ts
|
||||
// lib/provision.ts
|
||||
async function provisionEnvVars(
|
||||
installationId: string,
|
||||
projectId: string,
|
||||
credentials: { url: string; token: string }
|
||||
) {
|
||||
const response = await fetch(
|
||||
`https://api.vercel.com/v1/integrations/installations/${installationId}/env`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.VERCEL_INTEGRATION_TOKEN}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
projectId,
|
||||
envVars: [
|
||||
{
|
||||
key: 'MY_SERVICE_URL',
|
||||
value: credentials.url,
|
||||
target: ['production', 'preview', 'development'],
|
||||
type: 'encrypted',
|
||||
},
|
||||
{
|
||||
key: 'MY_SERVICE_TOKEN',
|
||||
value: credentials.token,
|
||||
target: ['production', 'preview', 'development'],
|
||||
type: 'secret',
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
return response.json()
|
||||
}
|
||||
```
|
||||
|
||||
### Integration CLI Commands
|
||||
|
||||
```bash
|
||||
# Develop integration locally
|
||||
vercel integration dev
|
||||
|
||||
# Deploy integration
|
||||
vercel integration deploy
|
||||
|
||||
# Publish to marketplace (requires review)
|
||||
vercel integration publish
|
||||
|
||||
# Check integration status
|
||||
vercel integration status
|
||||
```
|
||||
|
||||
## Common Integration Categories
|
||||
|
||||
| Category | Popular Integrations | Auto-Provisioned Env Vars |
|
||||
|----------|---------------------|---------------------------|
|
||||
| Databases | Neon, Supabase, PlanetScale, MongoDB | `POSTGRES_URL`, `DATABASE_URL` |
|
||||
| Cache/KV | Upstash Redis | `KV_REST_API_URL`, `KV_REST_API_TOKEN` |
|
||||
| Auth | Clerk, Auth0 | `CLERK_SECRET_KEY`, `AUTH0_SECRET` |
|
||||
| CMS | Sanity, Contentful, Storyblok | `SANITY_PROJECT_ID`, `CONTENTFUL_TOKEN` |
|
||||
| Monitoring | Datadog, Sentry | `SENTRY_DSN`, `DD_API_KEY` |
|
||||
| Payments | Stripe | `STRIPE_SECRET_KEY` |
|
||||
| Feature Flags | LaunchDarkly, Statsig | `LAUNCHDARKLY_SDK_KEY` |
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Add a database to your project | `vercel integration add neon` | Auto-provisioned, unified billing |
|
||||
| Browse available services | `vercel integration discover` | CLI-native discovery |
|
||||
| Build a SaaS integration | Integration SDK + manifest | Full lifecycle management |
|
||||
| Centralize billing | Marketplace integrations | Single Vercel invoice |
|
||||
| Auto-inject credentials | Marketplace auto-provisioning | No manual env var management |
|
||||
| Manage integrations programmatically | Vercel REST API | `/v1/integrations` endpoints |
|
||||
| Test integration locally | `vercel integration dev` | Local development server |
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel Marketplace](https://vercel.com/marketplace)
|
||||
- [Building Integrations](https://vercel.com/docs/integrations)
|
||||
- [Integration CLI](https://vercel.com/docs/cli/integration)
|
||||
- [Integration Webhooks](https://vercel.com/docs/integrations/webhooks)
|
||||
- [Environment Variables](https://vercel.com/docs/environment-variables)
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
name: nextjs
|
||||
description: Next.js App Router expert guidance. Use when building, debugging, or architecting Next.js applications — routing, Server Components, Server Actions, Cache Components, layouts, middleware/proxy, data fetching, rendering strategies, and deployment on Vercel.
|
||||
---
|
||||
|
||||
# Next.js (v16+) — App Router
|
||||
|
||||
You are an expert in Next.js 16 with the App Router. Always prefer the App Router over the legacy Pages Router unless the user's project explicitly uses Pages Router.
|
||||
|
||||
## Key Architecture
|
||||
|
||||
Next.js 16 uses React 19.2 features and the App Router (file-system routing under `app/`).
|
||||
|
||||
### File Conventions
|
||||
- `layout.tsx` — Persistent wrapper, preserves state across navigations
|
||||
- `page.tsx` — Unique UI for a route, makes route publicly accessible
|
||||
- `loading.tsx` — Suspense fallback shown while segment loads
|
||||
- `error.tsx` — Error boundary for a segment
|
||||
- `not-found.tsx` — 404 UI for a segment
|
||||
- `route.ts` — API endpoint (Route Handler)
|
||||
- `template.tsx` — Like layout but re-mounts on navigation
|
||||
- `default.tsx` — Fallback for parallel routes
|
||||
|
||||
### Routing
|
||||
- Dynamic segments: `[id]`, catch-all: `[...slug]`, optional catch-all: `[[...slug]]`
|
||||
- Route groups: `(group)` — organize without affecting URL
|
||||
- Parallel routes: `@slot` — render multiple pages in same layout
|
||||
- Intercepting routes: `(.)`, `(..)`, `(...)`, `(..)(..)` — modal patterns
|
||||
|
||||
## Server Components (Default)
|
||||
|
||||
All components in the App Router are Server Components by default. They:
|
||||
- Run on the server only, ship zero JavaScript to the client
|
||||
- Can directly `await` data (fetch, DB queries, file system)
|
||||
- Cannot use `useState`, `useEffect`, or browser APIs
|
||||
- Cannot use event handlers (`onClick`, `onChange`)
|
||||
|
||||
```tsx
|
||||
// app/users/page.tsx — Server Component (default)
|
||||
export default async function UsersPage() {
|
||||
const users = await db.query('SELECT * FROM users')
|
||||
return <UserList users={users} />
|
||||
}
|
||||
```
|
||||
|
||||
## Client Components
|
||||
|
||||
Add `'use client'` at the top of the file when you need interactivity or browser APIs.
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
|
||||
export function Counter() {
|
||||
const [count, setCount] = useState(0)
|
||||
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Push `'use client'` as far down the component tree as possible. Keep data fetching in Server Components and pass data down as props.
|
||||
|
||||
## Server Actions / Server Functions
|
||||
|
||||
Async functions marked with `'use server'` that run on the server. Use for mutations.
|
||||
|
||||
```tsx
|
||||
// app/actions.ts
|
||||
'use server'
|
||||
|
||||
export async function createUser(formData: FormData) {
|
||||
const name = formData.get('name') as string
|
||||
await db.insert('users', { name })
|
||||
revalidatePath('/users')
|
||||
}
|
||||
```
|
||||
|
||||
Use Server Actions for:
|
||||
- Form submissions and data mutations
|
||||
- In-app mutations with `revalidatePath` / `revalidateTag`
|
||||
|
||||
Use Route Handlers (`route.ts`) for:
|
||||
- Public APIs consumed by external clients
|
||||
- Webhooks
|
||||
- Large file uploads
|
||||
- Streaming responses
|
||||
|
||||
## Cache Components (Next.js 16)
|
||||
|
||||
The `'use cache'` directive enables component and function-level caching.
|
||||
|
||||
```tsx
|
||||
'use cache'
|
||||
|
||||
export async function CachedUserList() {
|
||||
cacheLife('hours') // Configure cache duration
|
||||
cacheTag('users') // Tag for on-demand invalidation
|
||||
const users = await db.query('SELECT * FROM users')
|
||||
return <UserList users={users} />
|
||||
}
|
||||
```
|
||||
|
||||
Invalidate with `updateTag('users')` from a Server Action. This replaces PPR from Next.js 15 canaries.
|
||||
|
||||
## Proxy (formerly Middleware)
|
||||
|
||||
In Next.js 16, `middleware.ts` is renamed to `proxy.ts`. It runs on the Node.js runtime (not Edge).
|
||||
|
||||
```ts
|
||||
// proxy.ts
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
export function proxy(request: NextRequest) {
|
||||
// Rewrite, redirect, set headers, etc.
|
||||
}
|
||||
|
||||
export const config = { matcher: ['/dashboard/:path*'] }
|
||||
```
|
||||
|
||||
## Breaking Changes in Next.js 16
|
||||
|
||||
1. **Async Request APIs**: `cookies()`, `headers()`, `params`, `searchParams` are all async — must `await` them
|
||||
2. **Proxy replaces Middleware**: Rename `middleware.ts` → `proxy.ts`, runs on Node.js (not Edge)
|
||||
3. **Turbopack is top-level config**: Move from `experimental.turbopack` to `turbopack` in `next.config`
|
||||
4. **View Transitions**: Built-in support for animating elements across navigations
|
||||
|
||||
## Rendering Strategy Decision
|
||||
|
||||
| Strategy | When to Use |
|
||||
|----------|-------------|
|
||||
| SSG (`generateStaticParams`) | Content rarely changes, maximum performance |
|
||||
| ISR (`revalidate: N`) | Content changes periodically, acceptable staleness |
|
||||
| SSR (Server Components) | Per-request fresh data, personalized content |
|
||||
| Cache Components (`'use cache'`) | Mix static shell with dynamic parts |
|
||||
| Client Components | Interactive UI, browser APIs needed |
|
||||
| Streaming (Suspense) | Show content progressively as data loads |
|
||||
|
||||
## Deployment on Vercel
|
||||
|
||||
- Zero-config: Vercel auto-detects Next.js and optimizes
|
||||
- `vercel dev` for local development with Vercel features
|
||||
- Server Components → Serverless/Edge Functions automatically
|
||||
- Image optimization via `next/image` (automatic on Vercel)
|
||||
- Font optimization via `next/font` (automatic on Vercel)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Data Fetching in Server Components
|
||||
```tsx
|
||||
// Parallel data fetching
|
||||
const [users, posts] = await Promise.all([
|
||||
getUsers(),
|
||||
getPosts(),
|
||||
])
|
||||
```
|
||||
|
||||
### Streaming with Suspense
|
||||
```tsx
|
||||
import { Suspense } from 'react'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<SlowDataComponent />
|
||||
</Suspense>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
```tsx
|
||||
// app/dashboard/error.tsx
|
||||
'use client'
|
||||
|
||||
export default function Error({ error, reset }: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<h2>Something went wrong</h2>
|
||||
<button onClick={() => reset()}>Try again</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs)
|
||||
- [App Router](https://nextjs.org/docs/app)
|
||||
- [Routing](https://nextjs.org/docs/app/building-your-application/routing)
|
||||
- [Data Fetching](https://nextjs.org/docs/app/building-your-application/data-fetching)
|
||||
- [Rendering](https://nextjs.org/docs/app/building-your-application/rendering)
|
||||
- [Caching](https://nextjs.org/docs/app/building-your-application/caching)
|
||||
- [Deploying](https://nextjs.org/docs/app/building-your-application/deploying)
|
||||
- [Upgrading](https://nextjs.org/docs/app/building-your-application/upgrading)
|
||||
@@ -0,0 +1,92 @@
|
||||
# Next.js App Router — File Convention Reference
|
||||
|
||||
## Special Files
|
||||
|
||||
| File | Purpose | Server/Client |
|
||||
|------|---------|---------------|
|
||||
| `layout.tsx` | Shared UI wrapper, preserves state | Server (default) |
|
||||
| `page.tsx` | Unique route UI | Server (default) |
|
||||
| `loading.tsx` | Suspense fallback | Server (default) |
|
||||
| `error.tsx` | Error boundary | Client (required) |
|
||||
| `not-found.tsx` | 404 UI | Server (default) |
|
||||
| `route.ts` | API endpoint (Route Handler) | Server only |
|
||||
| `template.tsx` | Layout that remounts on navigation | Server (default) |
|
||||
| `default.tsx` | Parallel route fallback | Server (default) |
|
||||
| `proxy.ts` | Network proxy (replaces middleware) | Server (Node.js) |
|
||||
|
||||
## Route Segments
|
||||
|
||||
| Pattern | Example | Matches |
|
||||
|---------|---------|---------|
|
||||
| `[id]` | `app/users/[id]/page.tsx` | `/users/123` |
|
||||
| `[...slug]` | `app/docs/[...slug]/page.tsx` | `/docs/a/b/c` |
|
||||
| `[[...slug]]` | `app/shop/[[...slug]]/page.tsx` | `/shop` or `/shop/a/b` |
|
||||
| `(group)` | `app/(marketing)/page.tsx` | `/` (group ignored in URL) |
|
||||
| `@slot` | `app/@sidebar/page.tsx` | Parallel route slot |
|
||||
|
||||
## Data Fetching Patterns
|
||||
|
||||
### Server Component (Default)
|
||||
```tsx
|
||||
export default async function Page() {
|
||||
const data = await fetch('https://api.example.com/data')
|
||||
return <div>{data}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### With Params (Async in Next.js 16)
|
||||
```tsx
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>
|
||||
}) {
|
||||
const { id } = await params
|
||||
const user = await getUser(id)
|
||||
return <UserProfile user={user} />
|
||||
}
|
||||
```
|
||||
|
||||
### With Search Params (Async in Next.js 16)
|
||||
```tsx
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ q?: string }>
|
||||
}) {
|
||||
const { q } = await searchParams
|
||||
const results = await search(q)
|
||||
return <SearchResults results={results} />
|
||||
}
|
||||
```
|
||||
|
||||
### generateStaticParams (SSG)
|
||||
```tsx
|
||||
export async function generateStaticParams() {
|
||||
const posts = await getPosts()
|
||||
return posts.map((post) => ({ slug: post.slug }))
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>
|
||||
}) {
|
||||
const { slug } = await params
|
||||
const post = await getPost(slug)
|
||||
return <Post post={post} />
|
||||
}
|
||||
```
|
||||
|
||||
### generateMetadata
|
||||
```tsx
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>
|
||||
}) {
|
||||
const { id } = await params
|
||||
const product = await getProduct(id)
|
||||
return { title: product.name, description: product.description }
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,327 @@
|
||||
---
|
||||
name: observability
|
||||
description: Vercel Observability expert guidance — Web Analytics, Speed Insights, runtime logs, custom events, Log Drains, OpenTelemetry integration, and monitoring dashboards. Use when instrumenting, debugging, or optimizing application performance and user experience on Vercel.
|
||||
---
|
||||
|
||||
# Vercel Observability
|
||||
|
||||
You are an expert in Vercel's observability stack — Web Analytics, Speed Insights, runtime logs, Log Drains, and monitoring integrations.
|
||||
|
||||
## Web Analytics
|
||||
|
||||
Privacy-friendly, first-party analytics with no cookie banners required.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install @vercel/analytics
|
||||
```
|
||||
|
||||
### Setup (Next.js App Router)
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
import { Analytics } from '@vercel/analytics/next'
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
{children}
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Events (Pro/Enterprise)
|
||||
|
||||
Track business-specific events beyond pageviews.
|
||||
|
||||
```ts
|
||||
import { track } from '@vercel/analytics'
|
||||
|
||||
// Track a conversion
|
||||
track('purchase', {
|
||||
product: 'pro-plan',
|
||||
value: 20,
|
||||
currency: 'USD',
|
||||
})
|
||||
|
||||
// Track a feature usage
|
||||
track('feature_used', {
|
||||
name: 'ai-chat',
|
||||
duration_ms: 3200,
|
||||
})
|
||||
```
|
||||
|
||||
### Server-Side Tracking
|
||||
|
||||
```ts
|
||||
import { track } from '@vercel/analytics/server'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const data = await req.json()
|
||||
await processOrder(data)
|
||||
|
||||
track('order_completed', {
|
||||
order_id: data.id,
|
||||
total: data.total,
|
||||
})
|
||||
|
||||
return Response.json({ success: true })
|
||||
}
|
||||
```
|
||||
|
||||
## Speed Insights
|
||||
|
||||
Real-user performance monitoring built on Core Web Vitals.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install @vercel/speed-insights
|
||||
```
|
||||
|
||||
### Setup (Next.js App Router)
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
import { SpeedInsights } from '@vercel/speed-insights/next'
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
{children}
|
||||
<SpeedInsights />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Metrics Tracked
|
||||
|
||||
| Metric | What It Measures | Good Threshold |
|
||||
|--------|-----------------|----------------|
|
||||
| LCP | Largest Contentful Paint | < 2.5s |
|
||||
| INP | Interaction to Next Paint | < 200ms |
|
||||
| CLS | Cumulative Layout Shift | < 0.1 |
|
||||
| FCP | First Contentful Paint | < 1.8s |
|
||||
| TTFB | Time to First Byte | < 800ms |
|
||||
|
||||
### Performance Attribution
|
||||
|
||||
Speed Insights attributes metrics to specific routes and pages, letting you identify which pages are slow and why.
|
||||
|
||||
## Runtime Logs
|
||||
|
||||
Vercel provides real-time logs for all function invocations.
|
||||
|
||||
### Structured Logging
|
||||
|
||||
```ts
|
||||
// app/api/process/route.ts
|
||||
export async function POST(req: Request) {
|
||||
const start = Date.now()
|
||||
const data = await req.json()
|
||||
|
||||
// Structured logs appear in Vercel's log viewer
|
||||
console.log(JSON.stringify({
|
||||
level: 'info',
|
||||
message: 'Processing request',
|
||||
requestId: req.headers.get('x-vercel-id'),
|
||||
payload_size: JSON.stringify(data).length,
|
||||
}))
|
||||
|
||||
try {
|
||||
const result = await processData(data)
|
||||
console.log(JSON.stringify({
|
||||
level: 'info',
|
||||
message: 'Request completed',
|
||||
duration_ms: Date.now() - start,
|
||||
}))
|
||||
return Response.json(result)
|
||||
} catch (error) {
|
||||
console.error(JSON.stringify({
|
||||
level: 'error',
|
||||
message: 'Processing failed',
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
duration_ms: Date.now() - start,
|
||||
}))
|
||||
return Response.json({ error: 'Internal error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Next.js Instrumentation
|
||||
|
||||
```ts
|
||||
// instrumentation.ts (Next.js 16)
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
// Initialize monitoring on server startup
|
||||
const { initMonitoring } = await import('./lib/monitoring')
|
||||
initMonitoring()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Log Drains
|
||||
|
||||
Export logs and telemetry data to external observability platforms.
|
||||
|
||||
### Supported Drain Types
|
||||
|
||||
| Drain Type | Protocol | Best For |
|
||||
|-----------|----------|----------|
|
||||
| JSON | HTTPS POST | Custom backends, generic log collectors |
|
||||
| NDJSON | HTTPS POST | Streaming-friendly consumers |
|
||||
| Syslog | TLS syslog | Traditional log management |
|
||||
|
||||
### Setting Up via CLI
|
||||
|
||||
```bash
|
||||
# List existing drains
|
||||
vercel logs drain ls
|
||||
|
||||
# Add a JSON drain
|
||||
vercel logs drain add <endpoint-url> --type json
|
||||
|
||||
# Add a drain with filtering
|
||||
vercel logs drain add <endpoint-url> --type json --environment production
|
||||
```
|
||||
|
||||
### OpenTelemetry Integration
|
||||
|
||||
Vercel exports traces in OpenTelemetry-compatible format via Log Drains.
|
||||
|
||||
```bash
|
||||
# Configure OTel-compatible drain
|
||||
vercel logs drain add <otel-collector-url> --type json
|
||||
```
|
||||
|
||||
### Datadog Integration
|
||||
|
||||
```bash
|
||||
# Install via Marketplace (recommended)
|
||||
vercel integration add datadog
|
||||
|
||||
# Or configure manually via drain
|
||||
vercel logs drain add https://http-intake.logs.datadoghq.com/api/v2/logs \
|
||||
--type json \
|
||||
--headers "DD-API-KEY:<your-key>"
|
||||
```
|
||||
|
||||
### Honeycomb Integration
|
||||
|
||||
```bash
|
||||
# Install via Marketplace
|
||||
vercel integration add honeycomb
|
||||
|
||||
# Or manual drain setup
|
||||
vercel logs drain add https://api.honeycomb.io/1/batch/<dataset> \
|
||||
--type json \
|
||||
--headers "X-Honeycomb-Team:<your-key>"
|
||||
```
|
||||
|
||||
## Monitoring Dashboard Patterns
|
||||
|
||||
### Full-Stack Observability Setup
|
||||
|
||||
Combine all Vercel observability tools for comprehensive coverage.
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx — complete observability setup
|
||||
import { Analytics } from '@vercel/analytics/next'
|
||||
import { SpeedInsights } from '@vercel/speed-insights/next'
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
{children}
|
||||
<Analytics />
|
||||
<SpeedInsights />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Monitoring with `waitUntil`
|
||||
|
||||
Fire-and-forget telemetry that doesn't block responses.
|
||||
|
||||
```ts
|
||||
import { waitUntil } from '@vercel/functions'
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const start = Date.now()
|
||||
const result = await fetchData()
|
||||
|
||||
// Send response immediately
|
||||
const response = Response.json(result)
|
||||
|
||||
// Report metrics in background
|
||||
waitUntil(async () => {
|
||||
await reportMetric('api_latency', Date.now() - start, {
|
||||
route: '/api/data',
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
```
|
||||
|
||||
### Error Tracking Pattern
|
||||
|
||||
```ts
|
||||
// lib/error-reporting.ts
|
||||
export async function reportError(error: unknown, context: Record<string, unknown>) {
|
||||
const payload = {
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
...context,
|
||||
}
|
||||
|
||||
// Log for Vercel's runtime logs
|
||||
console.error(JSON.stringify(payload))
|
||||
|
||||
// Also send to external service if configured
|
||||
if (process.env.ERROR_WEBHOOK_URL) {
|
||||
await fetch(process.env.ERROR_WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
| Need | Use | Why |
|
||||
|------|-----|-----|
|
||||
| Page views, traffic sources | Web Analytics | First-party, privacy-friendly |
|
||||
| Business event tracking | Web Analytics custom events | Track conversions, feature usage |
|
||||
| Core Web Vitals monitoring | Speed Insights | Real user data per route |
|
||||
| Function debugging | Runtime Logs | Real-time, per-invocation logs |
|
||||
| Export to Datadog/Honeycomb | Log Drains | Centralize observability |
|
||||
| OpenTelemetry traces | Log Drains (OTel) | Standards-based export |
|
||||
| Post-response telemetry | `waitUntil` + custom reporting | Non-blocking metrics |
|
||||
| Server-side event tracking | `@vercel/analytics/server` | Track API-triggered events |
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel Analytics](https://vercel.com/docs/analytics)
|
||||
- [Speed Insights](https://vercel.com/docs/speed-insights)
|
||||
- [Runtime Logs](https://vercel.com/docs/observability/runtime-logs)
|
||||
- [Log Drains](https://vercel.com/docs/observability/log-drains)
|
||||
- [Monitoring](https://vercel.com/docs/observability/monitoring)
|
||||
- [@vercel/analytics npm](https://www.npmjs.com/package/@vercel/analytics)
|
||||
- [@vercel/speed-insights npm](https://www.npmjs.com/package/@vercel/speed-insights)
|
||||
@@ -0,0 +1,295 @@
|
||||
---
|
||||
name: turbopack
|
||||
description: Turbopack expert guidance. Use when configuring the Next.js bundler, optimizing HMR, debugging build issues, or understanding the Turbopack vs Webpack differences.
|
||||
---
|
||||
|
||||
# Turbopack
|
||||
|
||||
You are an expert in Turbopack — the Rust-powered JavaScript/TypeScript bundler built by Vercel. It is the default bundler in Next.js 16.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Instant HMR**: Hot Module Replacement that doesn't degrade with app size
|
||||
- **Multi-environment builds**: Browser, Server, Edge, SSR, React Server Components
|
||||
- **Native RSC support**: Built for React Server Components from the ground up
|
||||
- **TypeScript, JSX, CSS, CSS Modules, WebAssembly**: Out of the box
|
||||
- **Rust-powered**: Incremental computation engine for maximum performance
|
||||
|
||||
## Configuration (Next.js 16)
|
||||
|
||||
In Next.js 16, Turbopack config is top-level (moved from `experimental.turbopack`):
|
||||
|
||||
```js
|
||||
// next.config.ts
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
turbopack: {
|
||||
// Resolve aliases (like webpack resolve.alias)
|
||||
resolveAlias: {
|
||||
'old-package': 'new-package',
|
||||
},
|
||||
// Custom file extensions to resolve
|
||||
resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
|
||||
},
|
||||
}
|
||||
|
||||
export default nextConfig
|
||||
```
|
||||
|
||||
## CSS and CSS Modules Handling
|
||||
|
||||
Turbopack handles CSS natively without additional configuration.
|
||||
|
||||
### Global CSS
|
||||
|
||||
Import global CSS in your root layout:
|
||||
|
||||
```tsx
|
||||
// app/layout.tsx
|
||||
import './globals.css'
|
||||
```
|
||||
|
||||
### CSS Modules
|
||||
|
||||
CSS Modules work out of the box with `.module.css` files:
|
||||
|
||||
```tsx
|
||||
// components/Button.tsx
|
||||
import styles from './Button.module.css'
|
||||
|
||||
export function Button({ children }) {
|
||||
return <button className={styles.primary}>{children}</button>
|
||||
}
|
||||
```
|
||||
|
||||
### PostCSS
|
||||
|
||||
Turbopack reads your `postcss.config.js` automatically. Tailwind CSS v4 works with zero config:
|
||||
|
||||
```js
|
||||
// postcss.config.js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Sass / SCSS
|
||||
|
||||
Install `sass` and import `.scss` files directly — Turbopack compiles them natively:
|
||||
|
||||
```bash
|
||||
npm install sass
|
||||
```
|
||||
|
||||
```tsx
|
||||
import styles from './Component.module.scss'
|
||||
```
|
||||
|
||||
### Common CSS pitfalls
|
||||
|
||||
- **CSS ordering differs from webpack**: Turbopack may load CSS chunks in a different order. Avoid relying on source-order specificity across files — use more specific selectors or CSS Modules.
|
||||
- **`@import` in global CSS**: Use standard CSS `@import` — Turbopack resolves them, but circular imports cause build failures.
|
||||
- **CSS-in-JS libraries**: `styled-components` and `emotion` work but require their SWC plugins configured under `compiler` in next.config.
|
||||
|
||||
## Tree Shaking
|
||||
|
||||
Turbopack performs tree shaking at the module level in production builds. Key behaviors:
|
||||
|
||||
- **ES module exports**: Only used exports are included — write `export` on each function/constant rather than barrel `export *`
|
||||
- **Side-effect-free packages**: Mark packages as side-effect-free in `package.json` to enable aggressive tree shaking:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-ui-lib",
|
||||
"sideEffects": false
|
||||
}
|
||||
```
|
||||
|
||||
- **Barrel file optimization**: Turbopack can skip unused re-exports from barrel files (`index.ts`) when the package declares `"sideEffects": false`
|
||||
- **Dynamic imports**: `import()` expressions create async chunk boundaries — Turbopack splits these into separate chunks automatically
|
||||
|
||||
### Diagnosing large bundles
|
||||
|
||||
Use the Next.js bundle analyzer to inspect Turbopack's output:
|
||||
|
||||
```bash
|
||||
ANALYZE=true next build
|
||||
```
|
||||
|
||||
Or install `@next/bundle-analyzer`:
|
||||
|
||||
```js
|
||||
// next.config.ts
|
||||
import withBundleAnalyzer from '@next/bundle-analyzer'
|
||||
|
||||
const nextConfig = withBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
})({
|
||||
// your config
|
||||
})
|
||||
```
|
||||
|
||||
## Custom Loader Migration from Webpack
|
||||
|
||||
Turbopack does not support webpack loaders directly. Here is how to migrate common patterns:
|
||||
|
||||
| Webpack Loader | Turbopack Equivalent |
|
||||
|----------------|---------------------|
|
||||
| `css-loader` + `style-loader` | Built-in CSS support — remove loaders |
|
||||
| `sass-loader` | Built-in — install `sass` package |
|
||||
| `postcss-loader` | Built-in — reads `postcss.config.js` |
|
||||
| `file-loader` / `url-loader` | Built-in static asset handling |
|
||||
| `svgr` / `@svgr/webpack` | Use `@svgr/webpack` via `turbopack.rules` |
|
||||
| `raw-loader` | Use `import x from './file?raw'` |
|
||||
| `graphql-tag/loader` | Use a build-time codegen step instead |
|
||||
| `worker-loader` | Use native `new Worker(new URL(...))` syntax |
|
||||
|
||||
### Configuring custom rules (loader replacement)
|
||||
|
||||
For loaders that have no built-in equivalent, use `turbopack.rules`:
|
||||
|
||||
```js
|
||||
// next.config.ts
|
||||
const nextConfig: NextConfig = {
|
||||
turbopack: {
|
||||
rules: {
|
||||
'*.svg': {
|
||||
loaders: ['@svgr/webpack'],
|
||||
as: '*.js',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### When migration isn't possible
|
||||
|
||||
If a webpack loader has no Turbopack equivalent and no workaround, fall back to webpack:
|
||||
|
||||
```js
|
||||
const nextConfig: NextConfig = {
|
||||
bundler: 'webpack',
|
||||
}
|
||||
```
|
||||
|
||||
File an issue at [github.com/vercel/next.js](https://github.com/vercel/next.js) — the Turbopack team tracks loader parity requests.
|
||||
|
||||
## Production Build Diagnostics
|
||||
|
||||
### Build failing with Turbopack
|
||||
|
||||
1. **Check for unsupported config**: Remove any `webpack()` function from next.config — it's ignored by Turbopack and may mask the real config
|
||||
2. **Verify `turbopack.rules`**: Ensure custom rules reference valid loaders that are installed
|
||||
3. **Check for Node.js built-in usage in edge/client**: Turbopack enforces environment boundaries — `fs`, `path`, etc. cannot be imported in client or edge bundles
|
||||
4. **Module not found errors**: Ensure `turbopack.resolveAlias` covers any custom resolution that was previously in webpack config
|
||||
|
||||
### Build output too large
|
||||
|
||||
- Audit `"use client"` directives — each client component boundary creates a new chunk
|
||||
- Check for accidentally bundled server-only packages in client components
|
||||
- Use `server-only` package to enforce server/client boundaries at import time:
|
||||
|
||||
```bash
|
||||
npm install server-only
|
||||
```
|
||||
|
||||
```ts
|
||||
// lib/db.ts
|
||||
import 'server-only' // Build fails if imported in a client component
|
||||
```
|
||||
|
||||
### Comparing webpack vs Turbopack output
|
||||
|
||||
Run both bundlers and compare:
|
||||
|
||||
```bash
|
||||
# Turbopack build (default in Next.js 16)
|
||||
next build
|
||||
|
||||
# Webpack build
|
||||
BUNDLER=webpack next build
|
||||
```
|
||||
|
||||
Compare `.next/` output sizes and page-level chunks.
|
||||
|
||||
## Performance Profiling
|
||||
|
||||
### HMR profiling
|
||||
|
||||
Enable verbose HMR timing in development:
|
||||
|
||||
```bash
|
||||
NEXT_TURBOPACK_TRACING=1 next dev
|
||||
```
|
||||
|
||||
This writes a `trace.json` to the project root — open it in `chrome://tracing` or [Perfetto](https://ui.perfetto.dev/) to see module-level timing.
|
||||
|
||||
### Build profiling
|
||||
|
||||
Profile production builds:
|
||||
|
||||
```bash
|
||||
NEXT_TURBOPACK_TRACING=1 next build
|
||||
```
|
||||
|
||||
Look for:
|
||||
- **Long-running transforms**: Indicates a slow SWC plugin or heavy PostCSS config
|
||||
- **Large module graphs**: Reduce barrel file re-exports
|
||||
- **Cache misses**: If incremental builds aren't hitting cache, check for files that change every build (e.g., generated timestamps)
|
||||
|
||||
### Memory usage
|
||||
|
||||
Turbopack's Rust core manages its own memory. If builds OOM:
|
||||
- Increase Node.js heap: `NODE_OPTIONS='--max-old-space-size=8192' next build`
|
||||
- Reduce concurrent tasks if running inside Turborepo: `turbo build --concurrency=2`
|
||||
|
||||
## Turbopack vs Webpack
|
||||
|
||||
| Feature | Turbopack | Webpack |
|
||||
|---------|-----------|---------|
|
||||
| Language | Rust | JavaScript |
|
||||
| HMR speed | Constant (O(1)) | Degrades with app size |
|
||||
| RSC support | Native | Plugin-based |
|
||||
| Cold start | Fast | Slower |
|
||||
| Ecosystem | Growing | Massive (loaders, plugins) |
|
||||
| Status in Next.js 16 | Default | Still supported |
|
||||
| Tree shaking | Module-level | Module-level |
|
||||
| CSS handling | Built-in | Requires loaders |
|
||||
| Production builds | Supported | Supported |
|
||||
|
||||
## When You Might Need Webpack
|
||||
|
||||
- Custom webpack loaders with no Turbopack equivalent
|
||||
- Complex webpack plugin configurations (e.g., `ModuleFederationPlugin`)
|
||||
- Specific webpack features not yet in Turbopack (e.g., custom `externals` functions)
|
||||
|
||||
To use webpack instead:
|
||||
```js
|
||||
// next.config.ts
|
||||
const nextConfig: NextConfig = {
|
||||
bundler: 'webpack', // Opt out of Turbopack
|
||||
}
|
||||
```
|
||||
|
||||
## Development vs Production
|
||||
|
||||
- **Development**: Turbopack provides instant HMR and fast refresh
|
||||
- **Production**: Turbopack handles the production build (replaces webpack in Next.js 16)
|
||||
|
||||
## Common Issues
|
||||
|
||||
1. **Missing loader equivalent**: Some webpack loaders don't have Turbopack equivalents yet. Check Turbopack docs for supported transformations.
|
||||
2. **Config migration**: Move `experimental.turbopack` to top-level `turbopack` in next.config.
|
||||
3. **Custom aliases**: Use `turbopack.resolveAlias` instead of `webpack.resolve.alias`.
|
||||
4. **CSS ordering changes**: Test visual regressions when migrating — CSS chunk order may differ.
|
||||
5. **Environment boundary errors**: Server-only modules imported in client components fail at build time — use `server-only` package.
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Turbopack](https://turbo.build/pack)
|
||||
- [Turbopack Documentation](https://turbo.build/pack/docs)
|
||||
- [Next.js Turbopack Config](https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack)
|
||||
@@ -0,0 +1,432 @@
|
||||
---
|
||||
name: turborepo
|
||||
description: Turborepo expert guidance. Use when setting up or optimizing monorepo builds, configuring task caching, remote caching, parallel execution, or the --affected flag for incremental CI.
|
||||
---
|
||||
|
||||
# Turborepo
|
||||
|
||||
You are an expert in Turborepo — a high-performance build system for JavaScript/TypeScript monorepos, built by Vercel with a Rust-powered core.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Task caching**: Content-aware hashing — only rebuilds when files actually change
|
||||
- **Remote caching**: Share build caches across machines and CI via Vercel
|
||||
- **Parallel execution**: Uses all CPU cores automatically
|
||||
- **Incremental builds**: `--affected` flag runs only changed packages + dependents
|
||||
- **Pruned subsets**: Generate minimal monorepo for deploying a single app
|
||||
- **Dependency graph awareness**: Understands package relationships
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
npx create-turbo@latest
|
||||
# or add to existing monorepo:
|
||||
npm install turbo --save-dev
|
||||
```
|
||||
|
||||
## turbo.json Task Pipeline
|
||||
|
||||
The `turbo.json` file defines your task dependency graph. Here are comprehensive examples:
|
||||
|
||||
### Basic pipeline
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [".next/**", "dist/**"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["build"]
|
||||
},
|
||||
"lint": {},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced pipeline with environment variables and inputs
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"globalDependencies": [".env"],
|
||||
"globalEnv": ["CI", "NODE_ENV"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": [".next/**", "dist/**"],
|
||||
"env": ["DATABASE_URL", "NEXT_PUBLIC_API_URL"],
|
||||
"inputs": ["src/**", "package.json", "tsconfig.json"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["build"],
|
||||
"outputs": ["coverage/**"],
|
||||
"env": ["TEST_DATABASE_URL"]
|
||||
},
|
||||
"test:unit": {
|
||||
"dependsOn": [],
|
||||
"outputs": ["coverage/**"]
|
||||
},
|
||||
"lint": {
|
||||
"inputs": ["src/**", ".eslintrc.*"]
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["src/**", "tsconfig.json"]
|
||||
},
|
||||
"db:generate": {
|
||||
"cache": false
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"clean": {
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Configuration
|
||||
|
||||
- `dependsOn: ["^build"]` — Run `build` in dependencies first (`^` = topological)
|
||||
- `dependsOn: ["build"]` — Run `build` in the same package first (no `^`)
|
||||
- `outputs` — Files to cache (build artifacts)
|
||||
- `inputs` — Files that affect the task hash (default: all non-gitignored files)
|
||||
- `env` — Environment variables that affect the task hash
|
||||
- `cache: false` — Skip caching (for dev servers, codegen)
|
||||
- `persistent: true` — Long-running tasks (dev servers)
|
||||
- `globalDependencies` — Files that invalidate all task caches when changed
|
||||
- `globalEnv` — Env vars that invalidate all task caches when changed
|
||||
|
||||
## Workspace Filtering
|
||||
|
||||
Run tasks in specific packages or subsets of your monorepo:
|
||||
|
||||
```bash
|
||||
# Single package
|
||||
turbo build --filter=web
|
||||
|
||||
# Package and its dependencies
|
||||
turbo build --filter=web...
|
||||
|
||||
# Package and its dependents (what depends on it)
|
||||
turbo build --filter=...ui
|
||||
|
||||
# Multiple packages
|
||||
turbo build --filter=web --filter=api
|
||||
|
||||
# By directory
|
||||
turbo build --filter=./apps/*
|
||||
|
||||
# Packages that changed since main
|
||||
turbo build --filter=[main]
|
||||
|
||||
# Combine: changed packages and their dependents
|
||||
turbo build --filter=...[main]
|
||||
|
||||
# Exclude a package
|
||||
turbo build --filter=!docs
|
||||
|
||||
# Packages matching a pattern
|
||||
turbo build --filter=@myorg/*
|
||||
```
|
||||
|
||||
### Filter syntax reference
|
||||
|
||||
| Pattern | Meaning |
|
||||
|---------|---------|
|
||||
| `web` | Only the `web` package |
|
||||
| `web...` | `web` and all its dependencies |
|
||||
| `...web` | `web` and all its dependents |
|
||||
| `...web...` | `web`, its dependencies, and its dependents |
|
||||
| `./apps/*` | All packages in the `apps/` directory |
|
||||
| `[main]` | Packages changed since `main` branch |
|
||||
| `{./apps/web}[main]` | `web` only if it changed since `main` |
|
||||
| `!docs` | Exclude the `docs` package |
|
||||
|
||||
## CI Matrix Strategies
|
||||
|
||||
### GitHub Actions — parallel jobs per package
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Required for --affected
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
- run: npm ci
|
||||
- run: turbo build test lint --affected
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
|
||||
deploy-web:
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: npm ci
|
||||
- run: turbo build --filter=web
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
```
|
||||
|
||||
### Dynamic matrix from workspace list
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
detect:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: ${{ steps.list.outputs.packages }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: list
|
||||
run: |
|
||||
PACKAGES=$(turbo ls --affected --output=json | jq -c '[.[].name]')
|
||||
echo "packages=$PACKAGES" >> "$GITHUB_OUTPUT"
|
||||
|
||||
test:
|
||||
needs: detect
|
||||
if: needs.detect.outputs.packages != '[]'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJson(needs.detect.outputs.packages) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: npm ci
|
||||
- run: turbo test --filter=${{ matrix.package }}
|
||||
```
|
||||
|
||||
### Remote caching in CI
|
||||
|
||||
```bash
|
||||
# Set in CI environment
|
||||
TURBO_TOKEN=your-vercel-token
|
||||
TURBO_TEAM=your-vercel-team
|
||||
|
||||
# Builds automatically use remote cache
|
||||
turbo build
|
||||
```
|
||||
|
||||
## Watch Mode
|
||||
|
||||
Run tasks in watch mode for development — re-executes when source files change:
|
||||
|
||||
```bash
|
||||
# Watch a specific task
|
||||
turbo watch test
|
||||
|
||||
# Watch with a filter
|
||||
turbo watch test --filter=web
|
||||
|
||||
# Watch multiple tasks
|
||||
turbo watch test lint
|
||||
```
|
||||
|
||||
Watch mode respects the task graph — if `test` depends on `build`, changing a source file re-runs `build` first, then `test`.
|
||||
|
||||
### Persistent tasks vs watch
|
||||
|
||||
- `persistent: true` in turbo.json: The task itself is long-running (e.g., `next dev`). Turbo starts it and keeps it alive.
|
||||
- `turbo watch`: Turbo re-invokes the task on file changes. Use for tasks that run and exit (e.g., `vitest run`, `tsc --noEmit`).
|
||||
|
||||
## Boundary Rules
|
||||
|
||||
Enforce architectural constraints across your monorepo with `boundaries` in turbo.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"boundaries": {
|
||||
"tags": {
|
||||
"apps/*": ["app"],
|
||||
"packages/ui": ["shared", "ui"],
|
||||
"packages/utils": ["shared"],
|
||||
"packages/config": ["config"]
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"from": ["app"],
|
||||
"allow": ["shared"]
|
||||
},
|
||||
{
|
||||
"from": ["shared"],
|
||||
"deny": ["app"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This enforces:
|
||||
- Apps can import shared packages
|
||||
- Shared packages cannot import from apps
|
||||
- Violations produce build-time errors with `turbo boundaries`
|
||||
|
||||
```bash
|
||||
# Check boundary compliance
|
||||
turbo boundaries
|
||||
|
||||
# Add to your pipeline
|
||||
{
|
||||
"tasks": {
|
||||
"check": {
|
||||
"dependsOn": ["lint", "typecheck", "boundaries"]
|
||||
},
|
||||
"boundaries": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Graph Visualization
|
||||
|
||||
Inspect your task dependency graph:
|
||||
|
||||
```bash
|
||||
# Print graph to terminal
|
||||
turbo build --graph
|
||||
|
||||
# Output as DOT format (Graphviz)
|
||||
turbo build --graph=graph.dot
|
||||
|
||||
# Output as JSON
|
||||
turbo build --graph=graph.json
|
||||
|
||||
# Open interactive graph in browser
|
||||
turbo build --graph=graph.html
|
||||
```
|
||||
|
||||
### Dry run — see what would execute
|
||||
|
||||
```bash
|
||||
# Show tasks that would run without executing them
|
||||
turbo build --dry-run
|
||||
|
||||
# JSON output for programmatic use
|
||||
turbo build --dry-run=json
|
||||
```
|
||||
|
||||
The dry run output shows:
|
||||
- Each task that would execute
|
||||
- Cache status (HIT or MISS)
|
||||
- Dependencies and dependents
|
||||
- File hash used for caching
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
# Run build across all packages
|
||||
turbo build
|
||||
|
||||
# Run only affected packages (changed since main branch)
|
||||
turbo build --affected
|
||||
|
||||
# Run specific tasks in specific packages
|
||||
turbo build --filter=web
|
||||
|
||||
# Run with remote caching
|
||||
turbo build --remote-cache
|
||||
|
||||
# Prune monorepo for a single app deployment
|
||||
turbo prune web --docker
|
||||
|
||||
# List all packages
|
||||
turbo ls
|
||||
|
||||
# List affected packages
|
||||
turbo ls --affected
|
||||
```
|
||||
|
||||
## Remote Caching
|
||||
|
||||
```bash
|
||||
# Login to Vercel for remote caching
|
||||
turbo login
|
||||
|
||||
# Link to a Vercel team
|
||||
turbo link
|
||||
|
||||
# Now builds share cache across all machines
|
||||
turbo build # Cache hits from CI, teammates, etc.
|
||||
```
|
||||
|
||||
## Monorepo Structure
|
||||
|
||||
```
|
||||
my-monorepo/
|
||||
├── turbo.json
|
||||
├── package.json
|
||||
├── apps/
|
||||
│ ├── web/ # Next.js app
|
||||
│ │ └── package.json
|
||||
│ ├── api/ # Backend service
|
||||
│ │ └── package.json
|
||||
│ └── docs/ # Documentation site
|
||||
│ └── package.json
|
||||
├── packages/
|
||||
│ ├── ui/ # Shared component library
|
||||
│ │ └── package.json
|
||||
│ ├── config/ # Shared configs (eslint, tsconfig)
|
||||
│ │ └── package.json
|
||||
│ └── utils/ # Shared utilities
|
||||
│ └── package.json
|
||||
└── node_modules/
|
||||
```
|
||||
|
||||
## --affected Flag
|
||||
|
||||
The most important optimization for CI pipelines:
|
||||
|
||||
```bash
|
||||
# Only build/test packages that changed since main
|
||||
turbo build test lint --affected
|
||||
```
|
||||
|
||||
This performs intelligent graph traversal:
|
||||
1. Identifies changed files since the base branch
|
||||
2. Maps changes to affected packages
|
||||
3. Includes all dependent packages (transitively)
|
||||
4. Runs tasks only for the affected subgraph
|
||||
|
||||
## Deploying to Vercel
|
||||
|
||||
Vercel auto-detects Turborepo and optimizes builds. Each app in `apps/` can be a separate Vercel project with automatic dependency detection.
|
||||
|
||||
## When to Use Turborepo
|
||||
|
||||
| Scenario | Use Turborepo? |
|
||||
|----------|----------------|
|
||||
| Single Next.js app | No — Turbopack handles bundling |
|
||||
| Multiple apps sharing code | Yes — orchestrate builds |
|
||||
| Shared component library | Yes — manage dependencies |
|
||||
| CI taking too long | Yes — caching + affected |
|
||||
| Team sharing build artifacts | Yes — remote caching |
|
||||
| Enforcing architecture boundaries | Yes — boundary rules |
|
||||
| Complex multi-step CI pipelines | Yes — task graph + matrix |
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Turborepo Documentation](https://turbo.build/repo/docs)
|
||||
- [Getting Started](https://turbo.build/repo/docs/getting-started)
|
||||
- [Crafting Your Repository](https://turbo.build/repo/docs/crafting-your-repository)
|
||||
- [Task Configuration](https://turbo.build/repo/docs/reference/configuration)
|
||||
- [Filtering](https://turbo.build/repo/docs/crafting-your-repository/running-tasks#using-filters)
|
||||
@@ -0,0 +1,470 @@
|
||||
---
|
||||
name: v0-dev
|
||||
description: v0 by Vercel expert guidance. Use when discussing AI code generation, generating UI components from prompts, v0 CLI usage, v0 SDK/API integration, or integrating v0 into development workflows with GitHub and Vercel deployment.
|
||||
---
|
||||
|
||||
# v0 by Vercel
|
||||
|
||||
You are an expert in v0 (v0.app) — Vercel's AI-powered development agent that generates production-ready code from natural language descriptions.
|
||||
|
||||
## Overview
|
||||
|
||||
v0 transforms prompts into working React/Next.js code. It supports 6M+ developers and 80K+ active teams globally. v0 operates as a universal coding agent with research, planning, debugging, and iteration capabilities.
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
- **Natural language → code**: Describe what you want, get production React components
|
||||
- **Visual input**: Upload Figma designs, screenshots, or sketches → code
|
||||
- **Multi-framework**: Outputs React, Vue, Svelte, HTML, Markdown
|
||||
- **Agentic intelligence**: Research, plan, debug, iterate autonomously
|
||||
- **shadcn/ui + Tailwind CSS**: Default styling system
|
||||
- **Full IDE**: Built-in VS Code editor, terminal, and git panel in the web UI
|
||||
|
||||
## CLI Usage
|
||||
|
||||
### Component Integration CLI (`v0` package)
|
||||
|
||||
Install and pull v0-generated components into your Next.js project:
|
||||
|
||||
```bash
|
||||
# Initialize v0 in an existing Next.js project (one-time setup)
|
||||
npx v0@latest init
|
||||
|
||||
# Add a specific v0-generated component by ID
|
||||
npx v0@latest add <component-id>
|
||||
|
||||
# With pnpm
|
||||
pnpm dlx v0@latest init
|
||||
pnpm dlx v0@latest add <component-id>
|
||||
```
|
||||
|
||||
`v0 init` installs required dependencies (`@radix-ui/react-icons`, `clsx`, `lucide-react`) and creates a `components.json` config file.
|
||||
|
||||
### "Add to Codebase" (Web UI → Local)
|
||||
|
||||
From the v0.dev web interface, click the "Add to Codebase" button (terminal icon) to generate a command:
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add "https://v0.dev/chat/b/<project_id>?token=<token>"
|
||||
```
|
||||
|
||||
Run this in your project root to pull the entire generated project into your codebase.
|
||||
|
||||
### Typical Workflow
|
||||
|
||||
```bash
|
||||
# 1. Scaffold a Next.js app
|
||||
npx create-next-app@latest --typescript --tailwind --eslint
|
||||
|
||||
# 2. Initialize v0 integration
|
||||
npx v0@latest init
|
||||
|
||||
# 3. Generate a component on v0.dev, get its ID
|
||||
# 4. Add the component locally
|
||||
npx v0@latest add a1B2c3d4
|
||||
|
||||
# 5. Import and use in your app
|
||||
```
|
||||
|
||||
### Project Scaffolding CLI
|
||||
|
||||
```bash
|
||||
# Create a new project from v0 templates
|
||||
npx create-v0-sdk-app@latest my-v0-app
|
||||
|
||||
# Use the v0-clone template (full v0.dev replica with auth, DB, streaming)
|
||||
npx create-v0-sdk-app@latest --template v0-clone
|
||||
```
|
||||
|
||||
## v0 SDK (Programmatic API)
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install v0-sdk
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
```ts
|
||||
import { v0 } from 'v0-sdk'
|
||||
// Automatically reads from process.env.V0_API_KEY
|
||||
|
||||
// Or create a custom client:
|
||||
import { createClient } from 'v0-sdk'
|
||||
const v0 = createClient({ apiKey: process.env.CUSTOM_V0_KEY })
|
||||
```
|
||||
|
||||
Get your API key at: https://v0.dev/chat/settings/keys
|
||||
|
||||
### Create a Chat and Generate Code
|
||||
|
||||
```ts
|
||||
import { v0 } from 'v0-sdk'
|
||||
|
||||
const chat = await v0.chats.create({
|
||||
message: 'Create a responsive navbar with dark mode toggle using Tailwind',
|
||||
system: 'You are an expert React developer',
|
||||
})
|
||||
|
||||
console.log(`Open in browser: ${chat.webUrl}`)
|
||||
```
|
||||
|
||||
### Full Project Workflow (Create → Chat → Deploy)
|
||||
|
||||
```ts
|
||||
import { v0 } from 'v0-sdk'
|
||||
|
||||
// Create a project
|
||||
const project = await v0.projects.create({ name: 'My App' })
|
||||
|
||||
// Initialize a chat with existing code
|
||||
const chat = await v0.chats.init({
|
||||
type: 'files',
|
||||
files: [{ name: 'App.tsx', content: existingCode }],
|
||||
projectId: project.id,
|
||||
})
|
||||
|
||||
// Send follow-up instructions
|
||||
await v0.chats.sendMessage({
|
||||
chatId: chat.id,
|
||||
message: 'Add a sidebar with navigation links and a user avatar',
|
||||
})
|
||||
|
||||
// Deploy when ready
|
||||
const deployment = await v0.deployments.create({
|
||||
projectId: project.id,
|
||||
chatId: chat.id,
|
||||
versionId: chat.latestVersion.id,
|
||||
})
|
||||
|
||||
console.log(`Live at: ${deployment.url}`)
|
||||
```
|
||||
|
||||
### Download Generated Code
|
||||
|
||||
```ts
|
||||
// Download files from a specific chat version
|
||||
const files = await v0.chats.downloadVersion({
|
||||
chatId: chat.id,
|
||||
versionId: chat.latestVersion.id,
|
||||
})
|
||||
```
|
||||
|
||||
### SDK Method Reference
|
||||
|
||||
**Chats:**
|
||||
- `v0.chats.create(params)` — Create a new chat
|
||||
- `v0.chats.sendMessage(params)` — Send a message to an existing chat
|
||||
- `v0.chats.getById(params)` — Retrieve a specific chat
|
||||
- `v0.chats.update(params)` — Update chat properties
|
||||
- `v0.chats.findVersions(params)` — List all versions of a chat
|
||||
- `v0.chats.getVersion(params)` — Retrieve a specific version
|
||||
- `v0.chats.updateVersion(params)` — Update files within a version
|
||||
- `v0.chats.downloadVersion(params)` — Download files for a version
|
||||
- `v0.chats.resume(params)` — Resume processing of a message
|
||||
|
||||
**Projects:**
|
||||
- `v0.projects.create(params)` — Create a new project
|
||||
- `v0.projects.getById(params)` — Retrieve a project
|
||||
- `v0.projects.update(params)` — Update a project
|
||||
- `v0.projects.find()` — List all projects
|
||||
- `v0.projects.assign(params)` — Assign a chat to a project
|
||||
- `v0.projects.getByChatId(params)` — Get project by chat ID
|
||||
- `v0.projects.createEnvVars(params)` — Create env vars for a project
|
||||
|
||||
**Deployments:**
|
||||
- `v0.deployments.create(params)` — Create deployment from a chat version
|
||||
- `v0.deployments.getById(params)` — Get deployment details
|
||||
- `v0.deployments.delete(params)` — Delete a deployment
|
||||
- `v0.deployments.find(params)` — List deployments
|
||||
- `v0.deployments.findLogs(params)` — Get deployment logs
|
||||
|
||||
## REST API
|
||||
|
||||
Base URL: `https://api.v0.dev/v1`
|
||||
Auth: `Authorization: Bearer <V0_API_KEY>`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/v1/projects` | List projects |
|
||||
| `POST` | `/v1/projects` | Create project |
|
||||
| `GET` | `/v1/projects/:id` | Get project |
|
||||
| `PUT` | `/v1/projects/:id` | Update project |
|
||||
| `DELETE` | `/v1/projects/:id` | Delete project |
|
||||
| `POST` | `/v1/chats` | Create/initialize chat |
|
||||
| `GET` | `/v1/chats/:id/messages` | Get messages |
|
||||
| `POST` | `/v1/chats/:id/messages` | Send message |
|
||||
| `POST` | `/v1/deployments` | Create deployment |
|
||||
|
||||
### Rate Limits
|
||||
|
||||
- API Requests: 10,000/day
|
||||
- Chat Messages: 1,000/day
|
||||
- Deployments: 100/day
|
||||
- File Uploads: 1 GB/day
|
||||
- Projects per account: 100
|
||||
|
||||
### Available Models
|
||||
|
||||
- `v0-1.5-md` — Everyday tasks and UI generation
|
||||
- `v0-1.5-lg` — Advanced reasoning
|
||||
- `v0-1.0-md` — Legacy model
|
||||
|
||||
## AI SDK Integration
|
||||
|
||||
### Using v0 as an AI Provider
|
||||
|
||||
```bash
|
||||
npm i @ai-sdk/vercel
|
||||
```
|
||||
|
||||
```ts
|
||||
import { vercel } from '@ai-sdk/vercel'
|
||||
import { generateText } from 'ai'
|
||||
|
||||
const { text } = await generateText({
|
||||
model: vercel('v0-1.5-md'),
|
||||
prompt: 'Create a login form with email and password fields',
|
||||
})
|
||||
```
|
||||
|
||||
### v0 AI Tools (Agent Integration)
|
||||
|
||||
Use v0's full capabilities as tools within an AI SDK agent:
|
||||
|
||||
```bash
|
||||
npm install @v0-sdk/ai-tools ai
|
||||
```
|
||||
|
||||
```ts
|
||||
import { generateText } from 'ai'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { v0Tools } from '@v0-sdk/ai-tools'
|
||||
|
||||
const result = await generateText({
|
||||
model: openai('gpt-5-mini'),
|
||||
prompt: 'Create a new React dashboard project with charts and a data table',
|
||||
tools: v0Tools({ apiKey: process.env.V0_API_KEY }),
|
||||
})
|
||||
```
|
||||
|
||||
For granular control, import specific tool sets:
|
||||
|
||||
```ts
|
||||
import { createChatTools, createProjectTools, createDeploymentTools } from '@v0-sdk/ai-tools'
|
||||
```
|
||||
|
||||
The `v0Tools` export includes 20+ tools: `createChat`, `sendMessage`, `getChat`, `updateChat`, `deleteChat`, `favoriteChat`, `forkChat`, `listChats`, `createProject`, `getProject`, `updateProject`, `listProjects`, `assignChatToProject`, `createEnvironmentVariables`, `createDeployment`, `getDeployment`, `deleteDeployment`, `listDeployments`, `getDeploymentLogs`.
|
||||
|
||||
## MCP Server
|
||||
|
||||
Connect v0 to any MCP-compatible IDE (Cursor, Claude Desktop, etc.):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"v0": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"mcp-remote",
|
||||
"https://mcp.v0.dev",
|
||||
"--header",
|
||||
"Authorization: Bearer ${V0_API_KEY}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Exposes 4 tools: create chat, get chat info, find chats, send messages.
|
||||
|
||||
## GitHub Integration
|
||||
|
||||
### Setup
|
||||
|
||||
1. In the v0 chat sidebar → **Git** section → click **Connect**
|
||||
2. Select GitHub account/org scope and repository name
|
||||
3. Click **Create Repository** — links chat to a new private GitHub repo
|
||||
4. A Vercel deployment is automatically created
|
||||
|
||||
### Branch Behavior (Automatic)
|
||||
|
||||
- Every chat creates a new branch (e.g., `v0/main-e7bad8e4`)
|
||||
- Every prompt that changes code **automatically commits and pushes**
|
||||
- You never work directly on `main`
|
||||
|
||||
### PR Workflow
|
||||
|
||||
1. Click the **Publish** button (shows PR icon when GitHub-connected)
|
||||
2. Select **Open PR** — creates PR from `v0/main-abc123` → `main`
|
||||
3. Review in the GitHub modal or on GitHub.com
|
||||
4. Merge the PR → closes the chat permanently
|
||||
5. Every PR gets a Preview Deployment; merging triggers Production Deployment
|
||||
|
||||
### Importing Existing Repos
|
||||
|
||||
1. In v0 prompt bar → click `+` → "Import from GitHub"
|
||||
2. v0 reads your existing codebase and env vars from Vercel
|
||||
3. Iterate with prompts; all changes committed to a new branch
|
||||
|
||||
## Prompt Engineering Tips
|
||||
|
||||
### 1. Be Specific About Design
|
||||
|
||||
```
|
||||
Weak: "Build a dashboard"
|
||||
Strong: "Build a support ticket dashboard. Mobile-first, light theme, high
|
||||
contrast. Color code: red for urgent, yellow for medium, green for low.
|
||||
Show agent status badges. Maximum 2 columns on mobile."
|
||||
```
|
||||
|
||||
### 2. Specify Your Tech Stack
|
||||
|
||||
```
|
||||
"Build a real-time chat app using: Next.js 16 with App Router,
|
||||
Socket.io for messaging, Vercel Postgres for storage,
|
||||
NextAuth.js for authentication."
|
||||
```
|
||||
|
||||
### 3. Define User Roles
|
||||
|
||||
```
|
||||
"Create a team collaboration tool with admin, manager, and member
|
||||
roles, task assignment, progress tracking, and file sharing."
|
||||
```
|
||||
|
||||
### 4. Queue Multiple Prompts
|
||||
|
||||
You can queue up to 10 prompts while v0 is still generating:
|
||||
1. "Create the base layout with navigation"
|
||||
2. "Add authentication with NextAuth"
|
||||
3. "Connect the database and add CRUD operations"
|
||||
4. "Add a settings page with dark mode toggle"
|
||||
|
||||
### 5. Specify Error and Empty States
|
||||
|
||||
```
|
||||
"Add comprehensive error handling for network failures, invalid
|
||||
input, and empty states with helpful recovery suggestions."
|
||||
```
|
||||
|
||||
### 6. Use Visual Selection for Precision
|
||||
|
||||
Click a specific element in the preview before typing to target exactly what you want to change. Eliminates ambiguity for multi-instance components.
|
||||
|
||||
### 7. Use Design Mode vs Prompts
|
||||
|
||||
- **Prompts**: Structural changes, adding features, wiring up logic
|
||||
- **Design Mode** (click element → adjust): Colors, spacing, typography tweaks
|
||||
|
||||
### 8. v0's Default Output Stack
|
||||
|
||||
When no framework is specified, v0 generates:
|
||||
- React with JSX + TypeScript
|
||||
- Tailwind CSS
|
||||
- shadcn/ui components
|
||||
- Lucide React icons
|
||||
- Complete, copy-paste-ready code (never partial stubs)
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Pattern 1: Generate Components, Import Locally
|
||||
|
||||
Best for adding individual UI components to an existing app.
|
||||
|
||||
```bash
|
||||
npx v0@latest init
|
||||
npx v0@latest add <component-id>
|
||||
```
|
||||
|
||||
Then import the component:
|
||||
|
||||
```tsx
|
||||
import { DataTable } from '@/components/data-table'
|
||||
|
||||
export default function DashboardPage() {
|
||||
return <DataTable data={rows} columns={columns} />
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: GitHub Round-Trip
|
||||
|
||||
Best for iterating on a full feature branch with non-engineers.
|
||||
|
||||
1. Import repo into v0 from GitHub
|
||||
2. Non-engineer iterates via prompts
|
||||
3. v0 auto-commits each change to a feature branch
|
||||
4. Engineer reviews the PR, merges
|
||||
|
||||
### Pattern 3: SDK Automation
|
||||
|
||||
Best for CI/CD pipelines or programmatic component generation.
|
||||
|
||||
```ts
|
||||
import { v0 } from 'v0-sdk'
|
||||
|
||||
// Generate a component from a design spec
|
||||
const chat = await v0.chats.create({
|
||||
message: `Create a pricing table component with these tiers:
|
||||
- Free: 0/mo, 1 project, community support
|
||||
- Pro: $20/mo, unlimited projects, priority support
|
||||
- Enterprise: Custom, SLA, dedicated support`,
|
||||
})
|
||||
|
||||
// Wait for generation, then download
|
||||
const files = await v0.chats.downloadVersion({
|
||||
chatId: chat.id,
|
||||
versionId: chat.latestVersion.id,
|
||||
})
|
||||
```
|
||||
|
||||
### Pattern 4: v0 as AI Agent Tool
|
||||
|
||||
Best for autonomous agents that need to generate and deploy UI.
|
||||
|
||||
```ts
|
||||
import { Agent } from 'ai'
|
||||
import { v0Tools } from '@v0-sdk/ai-tools'
|
||||
|
||||
const agent = new Agent({
|
||||
model: openai('gpt-5-mini'),
|
||||
tools: {
|
||||
...v0Tools({ apiKey: process.env.V0_API_KEY }),
|
||||
// ... other tools
|
||||
},
|
||||
system: 'You are a full-stack developer. Use v0 to generate UI components.',
|
||||
})
|
||||
|
||||
const { text } = await agent.generateText({
|
||||
prompt: 'Create a dashboard for our analytics data and deploy it',
|
||||
})
|
||||
```
|
||||
|
||||
## Built-in Integrations
|
||||
|
||||
v0 has native support for these services in its sandbox:
|
||||
|
||||
- **Databases**: Neon (PostgreSQL), Supabase, Upstash Redis, Vercel Blob
|
||||
- **AI**: OpenAI, Anthropic, Groq, Grok, fal, Deep Infra (via Vercel AI Gateway)
|
||||
- **Payments**: Stripe
|
||||
- **External APIs**: Twilio, and others via the "Vars" panel
|
||||
|
||||
## Limitations
|
||||
|
||||
- Best for UI components and layouts (~20% of a full application)
|
||||
- Backend, database, auth, and AI integration require separate implementation or explicit prompting
|
||||
- Generated code may need manual fixes for complex business logic
|
||||
- Enterprise-level scalability needs additional architecture review
|
||||
- shadcn/ui is the primary component library; other libraries require explicit prompting
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [v0 App](https://v0.app)
|
||||
- [v0 Documentation](https://v0.app/docs)
|
||||
- [v0 API Overview](https://v0.app/docs/api/platform/overview)
|
||||
- [v0 SDK (GitHub)](https://github.com/vercel/v0-sdk)
|
||||
- [v0 AI Tools](https://v0.app/docs/api/platform/packages/ai-tools)
|
||||
- [v0 MCP Server](https://v0.app/docs/api/platform/adapters/mcp-server)
|
||||
- [v0 GitHub Integration](https://v0.app/docs/github)
|
||||
- [API Keys](https://v0.dev/chat/settings/keys)
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
name: vercel-api
|
||||
description: Vercel MCP and REST API expert guidance. Use when the agent needs live access to Vercel projects, deployments, environment variables, domains, logs, or documentation through the MCP server or REST API.
|
||||
---
|
||||
|
||||
# Vercel API — MCP Server & REST API
|
||||
|
||||
You are an expert in the Vercel platform APIs. This plugin bundles a connection to the **official Vercel MCP server** (`https://mcp.vercel.com`) which gives agents live, authenticated access to Vercel resources.
|
||||
|
||||
## MCP Server
|
||||
|
||||
The plugin's `.mcp.json` configures the official Vercel MCP server using Streamable HTTP transport with OAuth authentication.
|
||||
|
||||
### Connection
|
||||
|
||||
```
|
||||
URL: https://mcp.vercel.com
|
||||
Transport: Streamable HTTP
|
||||
Auth: OAuth 2.1 (automatic — agent is prompted to authorize on first use)
|
||||
```
|
||||
|
||||
On first connection the agent will open a browser-based OAuth flow to grant read access to your Vercel account. Subsequent sessions reuse the stored token.
|
||||
|
||||
### Available MCP Tools
|
||||
|
||||
The Vercel MCP server exposes these tool categories (read-only in initial release):
|
||||
|
||||
| Category | Capabilities |
|
||||
|----------|-------------|
|
||||
| **Documentation** | Search and navigate Vercel docs, Next.js docs, AI SDK docs |
|
||||
| **Projects** | List projects, get project details, view project settings |
|
||||
| **Deployments** | List deployments, inspect deployment details, view build output |
|
||||
| **Logs** | Query deployment logs, function invocation logs, build logs |
|
||||
| **Domains** | List domains, check domain configuration and DNS status |
|
||||
| **Environment Variables** | List env vars per project and environment |
|
||||
| **Teams** | List teams, view team members and settings |
|
||||
|
||||
### Usage Patterns
|
||||
|
||||
#### Diagnose a failed deployment
|
||||
|
||||
```
|
||||
1. List recent deployments → find the failed one
|
||||
2. Inspect deployment → get error summary
|
||||
3. Query build logs → identify root cause
|
||||
4. Cross-reference with vercel-functions skill for runtime fixes
|
||||
```
|
||||
|
||||
#### Audit project configuration
|
||||
|
||||
```
|
||||
1. Get project details → check framework, build settings, root directory
|
||||
2. List environment variables → verify required vars are set per environment
|
||||
3. List domains → confirm production domain is correctly assigned
|
||||
4. Check deployment logs → look for runtime warnings
|
||||
```
|
||||
|
||||
#### Search documentation
|
||||
|
||||
```
|
||||
1. Search Vercel docs for a topic → get relevant pages
|
||||
2. Read specific doc page → extract configuration examples
|
||||
3. Cross-reference with bundled skills for deeper guidance
|
||||
```
|
||||
|
||||
#### Debug function performance
|
||||
|
||||
```
|
||||
1. Query function logs → find slow invocations
|
||||
2. Inspect deployment → check function region, runtime, memory
|
||||
3. Cross-reference with vercel-functions skill for optimization patterns
|
||||
```
|
||||
|
||||
## REST API (Direct Access)
|
||||
|
||||
When the MCP server doesn't cover a use case (or for write operations), use the Vercel REST API directly with `@vercel/sdk` or `curl`.
|
||||
|
||||
### Authentication
|
||||
|
||||
```bash
|
||||
# Bearer token auth (personal token or team token)
|
||||
curl -H "Authorization: Bearer $VERCEL_TOKEN" https://api.vercel.com/v9/projects
|
||||
```
|
||||
|
||||
```typescript
|
||||
// @vercel/sdk
|
||||
import { Vercel } from '@vercel/sdk';
|
||||
|
||||
const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN });
|
||||
```
|
||||
|
||||
### Key Endpoints
|
||||
|
||||
| Endpoint | Method | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `/v9/projects` | GET | List all projects |
|
||||
| `/v9/projects/:id` | GET | Get project details |
|
||||
| `/v13/deployments` | GET | List deployments |
|
||||
| `/v13/deployments` | POST | Create a deployment |
|
||||
| `/v13/deployments/:id` | GET | Get deployment details |
|
||||
| `/v9/projects/:id/env` | GET | List environment variables |
|
||||
| `/v9/projects/:id/env` | POST | Create environment variable |
|
||||
| `/v6/domains` | GET | List domains |
|
||||
| `/v6/domains` | POST | Add a domain |
|
||||
| `/v1/edge-config` | GET | List Edge Configs |
|
||||
| `/v1/firewall` | GET | List firewall rules |
|
||||
|
||||
### SDK Examples
|
||||
|
||||
#### List deployments
|
||||
|
||||
```typescript
|
||||
import { Vercel } from '@vercel/sdk';
|
||||
|
||||
const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN });
|
||||
|
||||
const { deployments } = await vercel.deployments.list({
|
||||
projectId: 'prj_xxxxx',
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
for (const d of deployments) {
|
||||
console.log(`${d.url} — ${d.state} — ${d.created}`);
|
||||
}
|
||||
```
|
||||
|
||||
#### Manage environment variables
|
||||
|
||||
```typescript
|
||||
// List env vars
|
||||
const { envs } = await vercel.projects.getProjectEnv({
|
||||
idOrName: 'my-project',
|
||||
});
|
||||
|
||||
// Create env var
|
||||
await vercel.projects.createProjectEnv({
|
||||
idOrName: 'my-project',
|
||||
requestBody: {
|
||||
key: 'DATABASE_URL',
|
||||
value: 'postgres://...',
|
||||
target: ['production', 'preview'],
|
||||
type: 'encrypted',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Get project domains
|
||||
|
||||
```typescript
|
||||
const { domains } = await vercel.projects.getProjectDomains({
|
||||
idOrName: 'my-project',
|
||||
});
|
||||
|
||||
for (const d of domains) {
|
||||
console.log(`${d.name} — verified: ${d.verified}`);
|
||||
}
|
||||
```
|
||||
|
||||
## When to Use MCP vs CLI vs REST API
|
||||
|
||||
| Scenario | Use | Why |
|
||||
|----------|-----|-----|
|
||||
| Agent needs to inspect/read Vercel state | **MCP server** | OAuth, structured tools, no token management |
|
||||
| Agent needs to deploy or mutate state | **CLI** (`vercel deploy`, `vercel env add`) | Full write access, well-tested |
|
||||
| Programmatic access from app code | **REST API / @vercel/sdk** | TypeScript types, fine-grained control |
|
||||
| CI/CD pipeline automation | **CLI + VERCEL_TOKEN** | Scriptable, `--prebuilt` for speed |
|
||||
| Searching Vercel documentation | **MCP server** | Indexed docs, AI-optimized results |
|
||||
|
||||
## Cross-References
|
||||
|
||||
- **CLI operations** → `⤳ skill: vercel-cli`
|
||||
- **Function configuration** → `⤳ skill: vercel-functions`
|
||||
- **Storage APIs** → `⤳ skill: vercel-storage`
|
||||
- **Firewall rules** → `⤳ skill: vercel-firewall`
|
||||
- **AI SDK MCP client** → `⤳ skill: ai-sdk` (section: MCP Integration)
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel MCP](https://vercel.com/docs/mcp)
|
||||
- [Vercel REST API](https://vercel.com/docs/rest-api)
|
||||
- [@vercel/sdk](https://www.npmjs.com/package/@vercel/sdk)
|
||||
- [MCP Authorization Spec](https://spec.modelcontextprotocol.io)
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: vercel-cli
|
||||
description: Vercel CLI expert guidance. Use when deploying, managing environment variables, linking projects, viewing logs, managing domains, or interacting with the Vercel platform from the command line.
|
||||
---
|
||||
|
||||
# Vercel CLI
|
||||
|
||||
You are an expert in the Vercel CLI (`vercel` or `vc`). The CLI is the primary way to manage Vercel projects from the terminal.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm i -g vercel
|
||||
```
|
||||
|
||||
## Core Commands
|
||||
|
||||
### Deployment
|
||||
|
||||
```bash
|
||||
# Preview deployment (from project root)
|
||||
vercel
|
||||
|
||||
# Production deployment
|
||||
vercel --prod
|
||||
|
||||
# Build locally, deploy build output only
|
||||
vercel build
|
||||
vercel deploy --prebuilt
|
||||
|
||||
# Build for production (uses production env vars)
|
||||
vercel build --prod
|
||||
vercel deploy --prebuilt --prod
|
||||
|
||||
# Force a new deployment (skip cache)
|
||||
vercel --force
|
||||
|
||||
# Promote a preview deployment to production
|
||||
vercel promote <deployment-url>
|
||||
|
||||
# Rollback to previous production deployment
|
||||
vercel rollback
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# Start local dev server with Vercel features
|
||||
vercel dev
|
||||
|
||||
# Link current directory to a Vercel project
|
||||
vercel link
|
||||
|
||||
# Pull environment variables and project settings
|
||||
vercel pull
|
||||
|
||||
# Pull specific environment
|
||||
vercel pull --environment=production
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# List all environment variables
|
||||
vercel env ls
|
||||
|
||||
# Add an environment variable
|
||||
vercel env add MY_VAR
|
||||
|
||||
# Add for specific environments
|
||||
vercel env add MY_VAR production
|
||||
vercel env add MY_VAR preview development
|
||||
|
||||
# Add branch-scoped variable
|
||||
vercel env add MY_VAR preview --branch=feature-x
|
||||
|
||||
# Add sensitive (write-only) variable
|
||||
vercel env add MY_SECRET --sensitive
|
||||
|
||||
# Remove an environment variable
|
||||
vercel env rm MY_VAR
|
||||
|
||||
# Pull all env vars to .env.local
|
||||
vercel env pull
|
||||
vercel env pull .env.production.local --environment=production
|
||||
```
|
||||
|
||||
### Logs & Inspection
|
||||
|
||||
```bash
|
||||
# View function logs (real-time)
|
||||
vercel logs <deployment-url>
|
||||
|
||||
# View build logs
|
||||
vercel logs <deployment-url> --build
|
||||
|
||||
# Inspect a deployment
|
||||
vercel inspect <deployment-url>
|
||||
|
||||
# List recent deployments
|
||||
vercel ls
|
||||
```
|
||||
|
||||
### Domains
|
||||
|
||||
```bash
|
||||
# List domains
|
||||
vercel domains ls
|
||||
|
||||
# Add a domain to a project
|
||||
vercel domains add example.com
|
||||
|
||||
# Remove a domain
|
||||
vercel domains rm example.com
|
||||
```
|
||||
|
||||
### DNS
|
||||
|
||||
```bash
|
||||
# List DNS records
|
||||
vercel dns ls example.com
|
||||
|
||||
# Add a DNS record
|
||||
vercel dns add example.com @ A 1.2.3.4
|
||||
```
|
||||
|
||||
### Teams
|
||||
|
||||
```bash
|
||||
# List teams
|
||||
vercel teams ls
|
||||
|
||||
# Switch to a team
|
||||
vercel teams switch my-team
|
||||
```
|
||||
|
||||
### Marketplace Integrations (2026)
|
||||
|
||||
```bash
|
||||
# Discover available integrations (agent-friendly JSON output)
|
||||
vercel integration discover --format=json
|
||||
|
||||
# Get setup instructions for an integration
|
||||
vercel integration guide neon
|
||||
|
||||
# The guide output is markdown — AI agents can parse and execute setup steps
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
Required environment variables for CI:
|
||||
```bash
|
||||
VERCEL_TOKEN=<your-token>
|
||||
VERCEL_ORG_ID=<org-id>
|
||||
VERCEL_PROJECT_ID=<project-id>
|
||||
```
|
||||
|
||||
### GitHub Actions Example
|
||||
|
||||
```yaml
|
||||
- name: Deploy to Vercel
|
||||
run: |
|
||||
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
|
||||
```
|
||||
|
||||
## Global Options
|
||||
|
||||
| Flag | Purpose |
|
||||
|------|---------|
|
||||
| `--token` | Authentication token (for CI) |
|
||||
| `--cwd <dir>` | Working directory |
|
||||
| `--debug` / `-d` | Verbose output |
|
||||
| `--yes` / `-y` | Skip confirmation prompts |
|
||||
| `--scope <team>` | Execute as a team |
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### First-Time Setup
|
||||
```bash
|
||||
vercel link # Connect to Vercel project
|
||||
vercel env pull # Get environment variables
|
||||
vercel dev # Start local dev
|
||||
```
|
||||
|
||||
### Deploy from CI
|
||||
```bash
|
||||
vercel pull --yes --environment=production --token=$TOKEN
|
||||
vercel build --prod --token=$TOKEN
|
||||
vercel deploy --prebuilt --prod --token=$TOKEN
|
||||
```
|
||||
|
||||
### Quick Preview
|
||||
```bash
|
||||
vercel # Creates preview deployment, returns URL
|
||||
```
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel CLI](https://vercel.com/docs/cli)
|
||||
- [Deployments](https://vercel.com/docs/deployments)
|
||||
- [REST API](https://vercel.com/docs/rest-api)
|
||||
@@ -0,0 +1,673 @@
|
||||
---
|
||||
name: vercel-firewall
|
||||
description: Vercel Firewall and security expert guidance. Use when configuring DDoS protection, WAF rules, rate limiting, bot filtering, IP allow/block lists, OWASP rulesets, Attack Challenge Mode, or any security configuration on the Vercel platform.
|
||||
---
|
||||
|
||||
# Vercel Firewall
|
||||
|
||||
You are an expert in the Vercel Firewall — a multi-layered security solution with automatic DDoS protection, a customizable Web Application Firewall (WAF), bot management, and rate limiting.
|
||||
|
||||
## Architecture & Rule Execution Order
|
||||
|
||||
1. DDoS mitigation rules (automatic, platform-wide)
|
||||
2. WAF IP blocking rules
|
||||
3. WAF custom rules (in priority order)
|
||||
4. WAF Managed Rulesets (OWASP, Bot Protection, AI Bots)
|
||||
|
||||
Changes propagate globally in under **300ms**. No redeployment required.
|
||||
|
||||
## DDoS Protection (Automatic, All Plans)
|
||||
|
||||
- Layer 3/4 mitigation (automatic, always on)
|
||||
- Layer 7 protection (proprietary, tailored to web apps)
|
||||
- 40x faster detection with real-time stream processing
|
||||
- Handles 1B+ suspicious TCP connections per week
|
||||
- Proven to mitigate 1.37 Tbps attacks with zero downtime
|
||||
|
||||
No configuration needed — DDoS protection is always active.
|
||||
|
||||
## WAF Custom Rules
|
||||
|
||||
### Rule JSON Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Block WordPress scanners",
|
||||
"description": "Block common WordPress probe paths",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "path",
|
||||
"op": "re",
|
||||
"value": "^/wp-(admin|login|content|includes)/"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": {
|
||||
"action": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Logic**: Each object in `conditionGroup` is an **OR** group. Conditions within a single group are **AND**ed. Multiple groups are **OR**ed.
|
||||
|
||||
### Condition Types (25 available)
|
||||
|
||||
| Type | Description | Extra Fields |
|
||||
|------|-------------|--------------|
|
||||
| `path` | URL path | |
|
||||
| `method` | HTTP method | |
|
||||
| `host` | Hostname | |
|
||||
| `ip_address` | Client IP (supports CIDR) | |
|
||||
| `user_agent` | User-Agent string | |
|
||||
| `header` | Request header value | `key` (header name) |
|
||||
| `query` | Query string parameter | `key` (param name) |
|
||||
| `cookie` | Cookie value | `key` (cookie name) |
|
||||
| `geo_country` | ISO country code (e.g., `US`) | |
|
||||
| `geo_continent` | Continent code (e.g., `NA`) | |
|
||||
| `geo_country_region` | State/province code | |
|
||||
| `geo_city` | City name | |
|
||||
| `geo_as_number` | ASN | |
|
||||
| `ja4_digest` | JA4 TLS fingerprint | |
|
||||
| `ja3_digest` | JA3 TLS fingerprint | |
|
||||
| `target_path` | Resolved path after routing | |
|
||||
| `route` | Matched route pattern | |
|
||||
| `raw_path` | Raw unparsed path | |
|
||||
| `region` | Vercel edge region code | |
|
||||
| `protocol` | http/https | |
|
||||
| `scheme` | URL scheme | |
|
||||
| `environment` | Deployment environment | |
|
||||
| `bot_name` | Specific bot name | |
|
||||
| `bot_category` | Bot category | |
|
||||
| `server_action` | Next.js Server Action ID | |
|
||||
|
||||
### Condition Operators
|
||||
|
||||
| Op | Meaning |
|
||||
|----|---------|
|
||||
| `eq` | Equals |
|
||||
| `neq` | Not equals |
|
||||
| `re` | Regex match |
|
||||
| `pre` | Starts with |
|
||||
| `suf` | Ends with |
|
||||
| `sub` | Contains |
|
||||
| `inc` | In array |
|
||||
| `ninc` | Not in array |
|
||||
| `ex` | Exists |
|
||||
| `nex` | Not exists |
|
||||
| `gt` / `gte` | Greater than (or equal) |
|
||||
| `lt` / `lte` | Less than (or equal) |
|
||||
|
||||
Additional optional fields: `neg: true` negates the condition, `key` required for `header`/`query`/`cookie` types.
|
||||
|
||||
### Mitigation Actions
|
||||
|
||||
| Action | Description |
|
||||
|--------|-------------|
|
||||
| `log` | Log only, allow traffic |
|
||||
| `deny` | Block request (403) |
|
||||
| `challenge` | JavaScript browser challenge |
|
||||
| `bypass` | Skip all subsequent WAF rules |
|
||||
| `rate_limit` | Apply rate limiting (requires `rateLimit` config) |
|
||||
| `redirect` | Redirect (requires `redirect` config) |
|
||||
|
||||
### Action Options
|
||||
|
||||
```json
|
||||
{
|
||||
"action": {
|
||||
"mitigate": {
|
||||
"action": "deny",
|
||||
"actionDuration": "1h",
|
||||
"bypassSystem": false,
|
||||
"logHeaders": ["user-agent", "x-forwarded-for"],
|
||||
"redirect": {
|
||||
"location": "https://example.com/blocked",
|
||||
"permanent": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Practical Rule Examples
|
||||
|
||||
### Block Sanctioned Countries
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Block OFAC Sanctioned Countries",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "geo_country",
|
||||
"op": "inc",
|
||||
"value": ["CU", "IR", "KP", "RU", "SY"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "deny" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Require API Key Header on /api/ Routes
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Require API Key",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "header",
|
||||
"op": "nex",
|
||||
"key": "x-api-key"
|
||||
},
|
||||
{
|
||||
"type": "path",
|
||||
"op": "pre",
|
||||
"value": "/api/"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "deny" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Block by JA4 TLS Fingerprint
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Block Known Malicious JA4",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "ja4_digest",
|
||||
"op": "eq",
|
||||
"value": "t13d1516h2_8daaf6152771_b0da82dd1658"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "deny", "actionDuration": "1h" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Block Datacenter ASNs
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Block Known Datacenter ASNs",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "geo_as_number",
|
||||
"op": "inc",
|
||||
"value": ["14618", "16509", "15169"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "deny" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Challenge cURL Requests
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Challenge cURL",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{ "type": "user_agent", "op": "re", "value": "^curl/" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "challenge" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
### Rate Limit Rule
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "API Rate Limit - 100 req/min",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{ "type": "path", "op": "pre", "value": "/api/" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": {
|
||||
"action": "rate_limit",
|
||||
"rateLimit": {
|
||||
"algo": "fixed_window",
|
||||
"window": 60,
|
||||
"limit": 100,
|
||||
"keys": ["ip"],
|
||||
"action": "deny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Login Endpoint Protection
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Login Rate Limit",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{ "type": "path", "op": "eq", "value": "/api/auth/login" },
|
||||
{ "type": "method", "op": "eq", "value": "POST" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": {
|
||||
"action": "rate_limit",
|
||||
"rateLimit": {
|
||||
"algo": "fixed_window",
|
||||
"window": 60,
|
||||
"limit": 10,
|
||||
"keys": ["ip"],
|
||||
"action": "challenge"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limit Configuration Options
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `algo` | string | `"fixed_window"` (all plans) or `"token_bucket"` (Enterprise) |
|
||||
| `window` | number | Seconds. Min 10, max 600 (Pro), max 3600 (Enterprise) |
|
||||
| `limit` | number | Max requests per window |
|
||||
| `keys` | array | Count per: `"ip"`, `"ja4"`, `"user_agent"`, custom headers (Enterprise) |
|
||||
| `action` | string | When exceeded: `"deny"`, `"log"`, `"challenge"` |
|
||||
|
||||
When exceeded with `deny`, returns HTTP 429 with `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers.
|
||||
|
||||
## Bot Management
|
||||
|
||||
### Bot Protection (Managed Ruleset)
|
||||
|
||||
Challenges automated traffic unlikely to be a real browser:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "managedRules.update",
|
||||
"id": "bot_protection",
|
||||
"value": { "active": true, "action": "challenge" }
|
||||
}
|
||||
```
|
||||
|
||||
### AI Bot Blocking
|
||||
|
||||
Block known AI crawlers (GPTBot, ClaudeBot, etc.):
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "managedRules.update",
|
||||
"id": "ai_bots",
|
||||
"value": { "active": true, "action": "deny" }
|
||||
}
|
||||
```
|
||||
|
||||
### Bot Filter
|
||||
|
||||
Additional bot filtering layer:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "managedRules.update",
|
||||
"id": "bot_filter",
|
||||
"value": { "active": true, "action": "challenge" }
|
||||
}
|
||||
```
|
||||
|
||||
### Allow a Specific Bot (Bypass Rule)
|
||||
|
||||
Place this higher in priority than Bot Protection managed rules:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Allow My Monitoring Bot",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{ "type": "user_agent", "op": "eq", "value": "MyMonitorBot/1.0" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"mitigate": { "action": "bypass" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Enable BotID (Traffic Visibility)
|
||||
|
||||
```json
|
||||
{ "botIdEnabled": true }
|
||||
```
|
||||
|
||||
## IP Allow/Block Lists
|
||||
|
||||
### Block an IP
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "ip.insert",
|
||||
"value": {
|
||||
"hostname": "my-site.com",
|
||||
"ip": "203.0.113.45",
|
||||
"action": "deny",
|
||||
"notes": "Malicious scraper"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Block a CIDR Range
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "ip.insert",
|
||||
"value": {
|
||||
"hostname": "my-site.com",
|
||||
"ip": "203.0.113.0/24",
|
||||
"action": "deny",
|
||||
"notes": "Bad actor CIDR block"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Allow an IP (Bypass All Rules)
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "ip.insert",
|
||||
"value": {
|
||||
"hostname": "my-site.com",
|
||||
"ip": "198.51.100.1",
|
||||
"action": "bypass",
|
||||
"notes": "Internal monitoring IP"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### IP Rule Actions
|
||||
|
||||
| Action | Effect |
|
||||
|--------|--------|
|
||||
| `deny` | Block the IP |
|
||||
| `challenge` | Serve JS challenge |
|
||||
| `log` | Log traffic only |
|
||||
| `bypass` | Allow through all rules (allowlist) |
|
||||
|
||||
**Note**: `hostname` must match the exact domain. Add separate entries per subdomain.
|
||||
|
||||
## OWASP Core Ruleset (CRS)
|
||||
|
||||
### Individual CRS Rules
|
||||
|
||||
| ID | Protection |
|
||||
|----|-----------|
|
||||
| `sqli` | SQL Injection |
|
||||
| `xss` | Cross-Site Scripting |
|
||||
| `rce` | Remote Code Execution |
|
||||
| `lfi` | Local File Inclusion |
|
||||
| `rfi` | Remote File Inclusion |
|
||||
| `sd` | Scanner Detection |
|
||||
| `ma` | Multipart Attack |
|
||||
| `php` | PHP-specific exploits |
|
||||
| `gen` | Generic attack patterns |
|
||||
| `sf` | Session Fixation |
|
||||
| `java` | Java-specific exploits |
|
||||
|
||||
### Enable OWASP Rules
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "crs.update",
|
||||
"id": "sqli",
|
||||
"value": { "active": true, "action": "deny" }
|
||||
}
|
||||
```
|
||||
|
||||
### Full OWASP + Bot Configuration (PUT)
|
||||
|
||||
```json
|
||||
{
|
||||
"firewallEnabled": true,
|
||||
"crs": {
|
||||
"sqli": { "active": true, "action": "deny" },
|
||||
"xss": { "active": true, "action": "deny" },
|
||||
"rce": { "active": true, "action": "deny" },
|
||||
"lfi": { "active": true, "action": "deny" },
|
||||
"rfi": { "active": true, "action": "deny" },
|
||||
"sd": { "active": true, "action": "log" },
|
||||
"ma": { "active": true, "action": "deny" },
|
||||
"gen": { "active": true, "action": "deny" },
|
||||
"sf": { "active": true, "action": "deny" },
|
||||
"php": { "active": false, "action": "log" },
|
||||
"java": { "active": false, "action": "log" }
|
||||
},
|
||||
"managedRules": {
|
||||
"owasp": { "active": true, "action": "deny" },
|
||||
"bot_protection": { "active": true, "action": "challenge" },
|
||||
"ai_bots": { "active": true, "action": "deny" },
|
||||
"bot_filter": { "active": true, "action": "challenge" }
|
||||
},
|
||||
"botIdEnabled": true
|
||||
}
|
||||
```
|
||||
|
||||
## Firewall REST API
|
||||
|
||||
Base URL: `https://api.vercel.com`
|
||||
Auth: `Authorization: Bearer <VERCEL_TOKEN>`
|
||||
Query params: `?projectId=<id>&teamId=<id>`
|
||||
|
||||
### Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `GET` | `/v1/security/firewall/config/active` | Read current config |
|
||||
| `PATCH` | `/v1/security/firewall/config` | Incremental update (add/remove/update rules) |
|
||||
| `PUT` | `/v1/security/firewall/config` | Full config replacement |
|
||||
| `POST` | `/v1/security/firewall/bypass` | Create temporary bypass rule |
|
||||
|
||||
### PATCH Actions
|
||||
|
||||
| Action | Description |
|
||||
|--------|-------------|
|
||||
| `firewallEnabled` | Enable/disable firewall (value: boolean) |
|
||||
| `rules.insert` | Add a custom rule |
|
||||
| `rules.update` | Update rule (requires `id`) |
|
||||
| `rules.remove` | Delete rule (requires `id`) |
|
||||
| `rules.priority` | Reorder rule (requires `id`, value = index) |
|
||||
| `ip.insert` | Add IP rule |
|
||||
| `ip.update` | Update IP rule |
|
||||
| `ip.remove` | Delete IP rule |
|
||||
| `crs.update` | Enable/configure OWASP CRS rule |
|
||||
| `crs.disable` | Disable entire CRS |
|
||||
| `managedRules.update` | Configure managed ruleset |
|
||||
|
||||
### Add a Rule via cURL
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://api.vercel.com/v1/security/firewall/config?projectId=prj_xxx&teamId=team_xxx" \
|
||||
-H "Authorization: Bearer $VERCEL_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"action": "rules.insert",
|
||||
"value": {
|
||||
"name": "Block WordPress scanners",
|
||||
"active": true,
|
||||
"conditionGroup": [
|
||||
{
|
||||
"conditions": [
|
||||
{ "type": "path", "op": "re", "value": "^/wp-(admin|login|content|includes)/" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"action": { "mitigate": { "action": "deny" } }
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Vercel SDK Usage
|
||||
|
||||
```ts
|
||||
import { Vercel } from '@vercel/sdk'
|
||||
|
||||
const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN })
|
||||
|
||||
// Read current firewall config
|
||||
const config = await vercel.security.readFirewallConfig({
|
||||
configVersion: 'active',
|
||||
projectId: 'prj_xxx',
|
||||
teamId: 'team_xxx',
|
||||
})
|
||||
|
||||
// Add a rule
|
||||
await vercel.security.updateFirewallConfig({
|
||||
projectId: 'prj_xxx',
|
||||
teamId: 'team_xxx',
|
||||
requestBody: {
|
||||
action: 'rules.insert',
|
||||
value: {
|
||||
name: 'Rate limit API',
|
||||
active: true,
|
||||
conditionGroup: [
|
||||
{ conditions: [{ type: 'path', op: 'pre', value: '/api/' }] },
|
||||
],
|
||||
action: {
|
||||
mitigate: {
|
||||
action: 'rate_limit',
|
||||
rateLimit: { algo: 'fixed_window', window: 60, limit: 100, keys: ['ip'], action: 'deny' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Create Temporary Bypass (Attack Challenge Mode)
|
||||
|
||||
```bash
|
||||
curl -X POST "https://api.vercel.com/v1/security/firewall/bypass?projectId=prj_xxx&teamId=team_xxx" \
|
||||
-H "Authorization: Bearer $VERCEL_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"domain": "my-site.com",
|
||||
"sourceIp": "198.51.100.42",
|
||||
"ttl": 3600000,
|
||||
"note": "Temporary bypass for load testing"
|
||||
}'
|
||||
```
|
||||
|
||||
## vercel.json WAF Rules
|
||||
|
||||
Declaratively define firewall rules in `vercel.json` using the `mitigate` key:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"routes": [
|
||||
{
|
||||
"src": "/api/(.*)",
|
||||
"missing": [
|
||||
{ "type": "header", "key": "x-internal-token" }
|
||||
],
|
||||
"mitigate": { "action": "deny" }
|
||||
},
|
||||
{
|
||||
"src": "/(.*)",
|
||||
"has": [
|
||||
{ "type": "header", "key": "user-agent", "value": "(?i)^curl/" }
|
||||
],
|
||||
"mitigate": { "action": "challenge" }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Supported actions in `vercel.json`: `"challenge"`, `"deny"` only. Rate limiting, `log`, and `bypass` require the dashboard or API.
|
||||
|
||||
## Attack Challenge Mode
|
||||
|
||||
- Available on all plans (free)
|
||||
- Shows browser verification challenge to all visitors during active attacks
|
||||
- Legitimate bots (Googlebot, webhook providers) automatically pass through
|
||||
- Internal Function-to-Function calls within the same account bypass automatically
|
||||
- Blocked requests don't count toward CDN/traffic usage
|
||||
- Configured via dashboard only: Project → Firewall → Bot Management → Attack Challenge Mode
|
||||
|
||||
## Plan Availability
|
||||
|
||||
| Feature | Hobby | Pro | Enterprise |
|
||||
|---------|-------|-----|-----------|
|
||||
| DDoS Protection | All | All | All |
|
||||
| Custom Rules | 5 | 40 | 1000 |
|
||||
| Rate Limiting | 1 rule | 40 rules | 1000 rules |
|
||||
| Bot Protection | — | Yes | Yes |
|
||||
| OWASP CRS | — | — | Yes |
|
||||
| Token Bucket algo | — | — | Yes |
|
||||
| Custom rate limit keys | — | — | Yes |
|
||||
|
||||
## Observability
|
||||
|
||||
- Security event logs in the Firewall tab
|
||||
- Linkable to Monitoring queries for investigations
|
||||
- DDoS mitigation notifications (alerts on detection)
|
||||
- BotID traffic visibility when enabled
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel Firewall Overview](https://vercel.com/docs/vercel-firewall)
|
||||
- [Custom Rules](https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules)
|
||||
- [Rate Limiting](https://vercel.com/docs/vercel-firewall/vercel-waf/rate-limiting)
|
||||
- [IP Blocking](https://vercel.com/docs/security/vercel-waf/ip-blocking)
|
||||
- [Managed Rulesets](https://vercel.com/docs/security/vercel-waf/managed-rulesets)
|
||||
- [Attack Challenge Mode](https://vercel.com/docs/vercel-firewall/attack-challenge-mode)
|
||||
- [Firewall API Guide](https://vercel.com/docs/vercel-firewall/firewall-api)
|
||||
- [REST API Reference](https://vercel.com/docs/rest-api/reference/endpoints/security)
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
name: vercel-functions
|
||||
description: Vercel Functions expert guidance — Serverless Functions, Edge Functions, Fluid Compute, streaming, Cron Jobs, and runtime configuration. Use when configuring, debugging, or optimizing server-side code running on Vercel.
|
||||
---
|
||||
|
||||
# Vercel Functions
|
||||
|
||||
You are an expert in Vercel Functions — the compute layer of the Vercel platform.
|
||||
|
||||
## Function Types
|
||||
|
||||
### Serverless Functions (Node.js)
|
||||
- Full Node.js runtime, all npm packages available
|
||||
- Default for Next.js API routes, Server Actions, Server Components
|
||||
- Cold starts: 800ms–2.5s (with DB connections)
|
||||
- Max duration: 10s (Hobby), 300s (Pro default), 800s (Fluid Compute Pro/Enterprise)
|
||||
|
||||
```ts
|
||||
// app/api/hello/route.ts
|
||||
export async function GET() {
|
||||
return Response.json({ message: 'Hello from Node.js' })
|
||||
}
|
||||
```
|
||||
|
||||
### Edge Functions (V8 Isolates)
|
||||
- Lightweight V8 runtime, Web Standard APIs only
|
||||
- Ultra-low cold starts (<1ms globally)
|
||||
- Limited API surface (no full Node.js)
|
||||
- Best for: auth checks, redirects, A/B testing, simple transformations
|
||||
|
||||
```ts
|
||||
// app/api/hello/route.ts
|
||||
export const runtime = 'edge'
|
||||
|
||||
export async function GET() {
|
||||
return new Response('Hello from the Edge')
|
||||
}
|
||||
```
|
||||
|
||||
### Choosing Runtime
|
||||
|
||||
| Need | Runtime | Why |
|
||||
|------|---------|-----|
|
||||
| Full Node.js APIs, npm packages | `nodejs` | Full compatibility |
|
||||
| Ultra-low latency, simple logic | `edge` | <1ms cold start, global |
|
||||
| Database connections, heavy deps | `nodejs` | Edge lacks full Node.js |
|
||||
| Auth/redirect at the edge | `edge` | Fastest response |
|
||||
| AI streaming | Either | Both support streaming |
|
||||
|
||||
## Fluid Compute
|
||||
|
||||
Fluid Compute is the unified execution model for all Vercel Functions (both Node.js and Edge).
|
||||
|
||||
Key benefits:
|
||||
- **Optimized concurrency**: Multiple invocations on a single instance
|
||||
- **Extended durations**: Up to 800s on Pro/Enterprise
|
||||
- **Background processing**: `waitUntil` / `after` for post-response tasks
|
||||
- **Dynamic scaling**: Automatic during traffic spikes
|
||||
- **Bytecode caching**: Reduces cold starts
|
||||
|
||||
### Background Processing with `waitUntil`
|
||||
|
||||
```ts
|
||||
// Continue work after sending response
|
||||
import { waitUntil } from '@vercel/functions'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const data = await req.json()
|
||||
|
||||
// Send response immediately
|
||||
const response = Response.json({ received: true })
|
||||
|
||||
// Continue processing in background
|
||||
waitUntil(async () => {
|
||||
await processAnalytics(data)
|
||||
await sendNotification(data)
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
```
|
||||
|
||||
### Next.js `after` (equivalent)
|
||||
|
||||
```ts
|
||||
import { after } from 'next/server'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const data = await req.json()
|
||||
|
||||
after(async () => {
|
||||
await logToAnalytics(data)
|
||||
})
|
||||
|
||||
return Response.json({ ok: true })
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming
|
||||
|
||||
Zero-config streaming for both runtimes. Essential for AI applications.
|
||||
|
||||
```ts
|
||||
export async function POST(req: Request) {
|
||||
const encoder = new TextEncoder()
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
for (const chunk of data) {
|
||||
controller.enqueue(encoder.encode(chunk))
|
||||
await new Promise(r => setTimeout(r, 100))
|
||||
}
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
|
||||
return new Response(stream, {
|
||||
headers: { 'Content-Type': 'text/event-stream' },
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
For AI streaming, use the AI SDK's `toDataStreamResponse()` which handles SSE formatting automatically.
|
||||
|
||||
## Cron Jobs
|
||||
|
||||
Schedule function invocations via `vercel.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"crons": [
|
||||
{
|
||||
"path": "/api/daily-report",
|
||||
"schedule": "0 8 * * *"
|
||||
},
|
||||
{
|
||||
"path": "/api/cleanup",
|
||||
"schedule": "0 */6 * * *"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The cron endpoint receives a normal HTTP request. Verify it's from Vercel:
|
||||
|
||||
```ts
|
||||
export async function GET(req: Request) {
|
||||
const authHeader = req.headers.get('authorization')
|
||||
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
|
||||
return new Response('Unauthorized', { status: 401 })
|
||||
}
|
||||
// Do scheduled work
|
||||
return Response.json({ ok: true })
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration via vercel.json
|
||||
|
||||
```json
|
||||
{
|
||||
"functions": {
|
||||
"app/api/heavy/**": {
|
||||
"maxDuration": 300,
|
||||
"memory": 1024
|
||||
},
|
||||
"app/api/edge/**": {
|
||||
"runtime": "edge"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Timeout Limits
|
||||
|
||||
| Plan | Default | Max (Fluid Compute) |
|
||||
|------|---------|---------------------|
|
||||
| Hobby | 10s | 60s |
|
||||
| Pro | 15s | 800s |
|
||||
| Enterprise | 15s | 800s |
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Cold starts with DB connections**: Use connection pooling (e.g., Neon's `@neondatabase/serverless`)
|
||||
2. **Edge limitations**: No `fs`, no native modules, limited `crypto` — use Node.js runtime if needed
|
||||
3. **Timeout exceeded**: Use Fluid Compute for long-running tasks, or Workflow DevKit for very long processes
|
||||
4. **Bundle size**: Python runtime supports up to 500MB; Node.js has smaller limits
|
||||
5. **Environment variables**: Available in all functions automatically; use `vercel env pull` for local dev
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel Functions](https://vercel.com/docs/functions)
|
||||
- [Serverless Functions](https://vercel.com/docs/functions/serverless-functions)
|
||||
- [Edge Functions](https://vercel.com/docs/functions/edge-functions)
|
||||
- [Fluid Compute](https://vercel.com/docs/functions/fluid-compute)
|
||||
- [Streaming](https://vercel.com/docs/functions/streaming)
|
||||
- [Cron Jobs](https://vercel.com/docs/cron-jobs)
|
||||
@@ -0,0 +1,194 @@
|
||||
---
|
||||
name: vercel-storage
|
||||
description: Vercel storage expert guidance — Blob, Edge Config, and Marketplace storage (Neon Postgres, Upstash Redis). Use when choosing, configuring, or using data storage with Vercel applications.
|
||||
---
|
||||
|
||||
# Vercel Storage
|
||||
|
||||
You are an expert in Vercel's storage options. Know which products are active, which are sunset, and when to use each.
|
||||
|
||||
## Active First-Party Storage
|
||||
|
||||
### Vercel Blob — File Storage
|
||||
|
||||
Fast, scalable storage for unstructured data (images, videos, documents, any files).
|
||||
|
||||
```bash
|
||||
npm install @vercel/blob
|
||||
```
|
||||
|
||||
```ts
|
||||
import { put, del, list, get } from '@vercel/blob'
|
||||
|
||||
// Upload from server
|
||||
const blob = await put('images/photo.jpg', file, {
|
||||
access: 'public',
|
||||
})
|
||||
// blob.url → public URL
|
||||
|
||||
// Client upload (up to 5 TB)
|
||||
import { upload } from '@vercel/blob/client'
|
||||
const blob = await upload('video.mp4', file, {
|
||||
access: 'public',
|
||||
handleUploadUrl: '/api/upload', // Your token endpoint
|
||||
})
|
||||
|
||||
// List blobs
|
||||
const { blobs } = await list()
|
||||
|
||||
// Conditional get with ETags
|
||||
const response = await get('images/photo.jpg', {
|
||||
ifNoneMatch: previousETag,
|
||||
})
|
||||
if (response.statusCode === 304) {
|
||||
// Not modified, use cached version
|
||||
}
|
||||
|
||||
// Delete
|
||||
await del('images/photo.jpg')
|
||||
```
|
||||
|
||||
**Use when**: Media files, user uploads, documents, any large unstructured data.
|
||||
|
||||
### Vercel Edge Config — Global Configuration
|
||||
|
||||
Ultra-low-latency key-value store for application configuration. Not a database — designed for config data that must be read instantly at the edge.
|
||||
|
||||
```bash
|
||||
npm install @vercel/edge-config
|
||||
```
|
||||
|
||||
```ts
|
||||
import { get, getAll, has } from '@vercel/edge-config'
|
||||
|
||||
// Read a single value (< 1ms at the edge)
|
||||
const isFeatureEnabled = await get('feature-new-ui')
|
||||
|
||||
// Read multiple values
|
||||
const config = await getAll(['feature-new-ui', 'ab-test-variant', 'redirect-rules'])
|
||||
|
||||
// Check existence
|
||||
const exists = await has('maintenance-mode')
|
||||
```
|
||||
|
||||
**Use when**: Feature flags, A/B testing config, dynamic routing rules, maintenance mode toggles. Anything that must be read at the edge with near-zero latency.
|
||||
|
||||
**Do NOT use for**: User data, session state, frequently written data. Edge Config is optimized for reads, not writes.
|
||||
|
||||
## Marketplace Storage (Partner-Provided)
|
||||
|
||||
### IMPORTANT: @vercel/postgres and @vercel/kv are SUNSET
|
||||
|
||||
These packages no longer exist as first-party Vercel products. Use the marketplace replacements:
|
||||
|
||||
### Neon Postgres (replaces @vercel/postgres)
|
||||
|
||||
Serverless Postgres with branching, auto-scaling, and connection pooling.
|
||||
|
||||
```bash
|
||||
npm install @neondatabase/serverless
|
||||
# OR for drop-in replacement:
|
||||
npm install @neondatabase/vercel-postgres-compat
|
||||
```
|
||||
|
||||
```ts
|
||||
// Direct Neon usage
|
||||
import { neon } from '@neondatabase/serverless'
|
||||
|
||||
const sql = neon(process.env.DATABASE_URL!)
|
||||
const users = await sql`SELECT * FROM users WHERE id = ${userId}`
|
||||
|
||||
// With Drizzle ORM
|
||||
import { drizzle } from 'drizzle-orm/neon-http'
|
||||
import { neon } from '@neondatabase/serverless'
|
||||
|
||||
const sql = neon(process.env.DATABASE_URL!)
|
||||
const db = drizzle(sql)
|
||||
```
|
||||
|
||||
Install via Vercel Marketplace for automatic environment variable provisioning.
|
||||
|
||||
### Upstash Redis (replaces @vercel/kv)
|
||||
|
||||
Serverless Redis with same Vercel billing integration.
|
||||
|
||||
```bash
|
||||
npm install @upstash/redis
|
||||
```
|
||||
|
||||
```ts
|
||||
import { Redis } from '@upstash/redis'
|
||||
|
||||
const redis = Redis.fromEnv() // Uses UPSTASH_REDIS_REST_URL & TOKEN
|
||||
|
||||
// Basic operations
|
||||
await redis.set('session:abc', { userId: '123' }, { ex: 3600 })
|
||||
const session = await redis.get('session:abc')
|
||||
|
||||
// Rate limiting
|
||||
import { Ratelimit } from '@upstash/ratelimit'
|
||||
const ratelimit = new Ratelimit({
|
||||
redis,
|
||||
limiter: Ratelimit.slidingWindow(10, '10s'),
|
||||
})
|
||||
const { success } = await ratelimit.limit('user:123')
|
||||
```
|
||||
|
||||
Install via Vercel Marketplace for automatic environment variable provisioning.
|
||||
|
||||
## Storage Decision Matrix
|
||||
|
||||
| Need | Use | Package |
|
||||
|------|-----|---------|
|
||||
| File uploads, media, documents | Vercel Blob | `@vercel/blob` |
|
||||
| Feature flags, A/B config | Edge Config | `@vercel/edge-config` |
|
||||
| Relational data, SQL queries | Neon Postgres | `@neondatabase/serverless` |
|
||||
| Key-value cache, sessions, rate limiting | Upstash Redis | `@upstash/redis` |
|
||||
| Full-text search | Neon Postgres (pg_trgm) or Elasticsearch (Marketplace) | varies |
|
||||
| Vector embeddings | Neon Postgres (pgvector) or Pinecone (Marketplace) | varies |
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From @vercel/postgres → Neon
|
||||
```diff
|
||||
- import { sql } from '@vercel/postgres'
|
||||
+ import { neon } from '@neondatabase/serverless'
|
||||
+ const sql = neon(process.env.DATABASE_URL!)
|
||||
|
||||
// Or use the compatibility package for zero-change migration:
|
||||
- import { sql } from '@vercel/postgres'
|
||||
+ import { sql } from '@neondatabase/vercel-postgres-compat'
|
||||
```
|
||||
|
||||
### From @vercel/kv → Upstash Redis
|
||||
```diff
|
||||
- import { kv } from '@vercel/kv'
|
||||
- await kv.set('key', 'value')
|
||||
- const value = await kv.get('key')
|
||||
+ import { Redis } from '@upstash/redis'
|
||||
+ const redis = Redis.fromEnv()
|
||||
+ await redis.set('key', 'value')
|
||||
+ const value = await redis.get('key')
|
||||
```
|
||||
|
||||
## Installing Marketplace Storage
|
||||
|
||||
Use the Vercel CLI for agent-friendly discovery:
|
||||
|
||||
```bash
|
||||
# Discover available storage integrations
|
||||
vercel integration discover --format=json
|
||||
|
||||
# Get setup guide for a specific integration
|
||||
vercel integration guide neon
|
||||
```
|
||||
|
||||
This automatically provisions accounts, creates databases, and sets environment variables.
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Vercel Storage](https://vercel.com/docs/storage)
|
||||
- [Vercel Blob](https://vercel.com/docs/storage/vercel-blob)
|
||||
- [Edge Config](https://vercel.com/docs/storage/edge-config)
|
||||
- [Vercel Marketplace](https://vercel.com/marketplace) — Neon, Upstash, and other storage integrations
|
||||
- [Integrations](https://vercel.com/docs/integrations)
|
||||
@@ -0,0 +1,235 @@
|
||||
---
|
||||
name: workflow
|
||||
description: Vercel Workflow DevKit (WDK) expert guidance. Use when building durable workflows, long-running tasks, AI agents that must survive crashes, or any async process that needs pause/resume, retries, and observability.
|
||||
---
|
||||
|
||||
# Vercel Workflow DevKit (WDK)
|
||||
|
||||
You are an expert in the Vercel Workflow DevKit. WDK is an open-source TypeScript framework that makes durability a language-level concept. Functions can pause for minutes or months, survive deployments and crashes, and resume exactly where they stopped.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @workflow/core
|
||||
# For AI agent durability:
|
||||
npm install @workflow/ai
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Directives
|
||||
|
||||
WDK introduces two directives that turn ordinary async functions into durable workflows:
|
||||
|
||||
```ts
|
||||
'use workflow' // Marks a function as a durable workflow
|
||||
'use step' // Marks a block as an individually retryable, observable step
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Each `'use step'` block compiles into an isolated API Route
|
||||
2. Inputs and outputs are recorded for deterministic replay
|
||||
3. If a deploy or crash occurs, the system replays execution from the last completed step
|
||||
4. While a step executes, the workflow is suspended (zero resource consumption)
|
||||
5. When the step completes, the workflow resumes automatically
|
||||
|
||||
### Basic Workflow
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function processOrder(orderId: string) {
|
||||
'use step'
|
||||
const order = await db.getOrder(orderId)
|
||||
|
||||
'use step'
|
||||
const payment = await processPayment(order)
|
||||
|
||||
'use step'
|
||||
await sendConfirmation(order, payment)
|
||||
|
||||
'use step'
|
||||
await updateInventory(order)
|
||||
|
||||
return { success: true, orderId }
|
||||
}
|
||||
```
|
||||
|
||||
Each step is:
|
||||
- **Retryable**: Automatically retried on transient failures
|
||||
- **Observable**: Step-level visibility in the dashboard
|
||||
- **Durable**: State persisted between steps
|
||||
- **Isolated**: Runs as its own API route
|
||||
|
||||
## Worlds (Execution Environments)
|
||||
|
||||
A "World" is where workflow state gets stored. WDK is portable across environments:
|
||||
|
||||
### Local World (Development)
|
||||
```ts
|
||||
// State stored as JSON files on disk
|
||||
// Automatic in local development
|
||||
```
|
||||
|
||||
### Vercel World (Production)
|
||||
```ts
|
||||
// Fully managed: scalable storage, distributed queuing
|
||||
// Zero configuration when deployed to Vercel
|
||||
// Automatic authentication
|
||||
```
|
||||
|
||||
### Self-Hosted
|
||||
```ts
|
||||
// Use Postgres, Redis, or build your own World
|
||||
// Full control over state storage
|
||||
import { createPostgresWorld } from '@workflow/postgres'
|
||||
|
||||
const world = createPostgresWorld({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
})
|
||||
```
|
||||
|
||||
## DurableAgent (AI SDK Integration)
|
||||
|
||||
The killer feature: wrap AI SDK agents with durability.
|
||||
|
||||
```ts
|
||||
import { DurableAgent } from '@workflow/ai/agent'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
|
||||
const agent = new DurableAgent({
|
||||
model: openai('gpt-5-mini'),
|
||||
tools: {
|
||||
searchWeb: { /* ... */ },
|
||||
writeFile: { /* ... */ },
|
||||
sendEmail: { /* ... */ },
|
||||
},
|
||||
system: 'You are a research assistant.',
|
||||
})
|
||||
|
||||
// Every LLM call and tool execution becomes a retryable step
|
||||
'use workflow'
|
||||
export async function researchTask(topic: string) {
|
||||
const result = await agent.generateText({
|
||||
prompt: `Research ${topic} and write a comprehensive report.`,
|
||||
})
|
||||
return result.text
|
||||
}
|
||||
```
|
||||
|
||||
With `DurableAgent`:
|
||||
- Every LLM call is a step (retried on failure)
|
||||
- Every tool execution is a step (individually observable)
|
||||
- The entire agent loop survives crashes and deployments
|
||||
- Results are aggregated within the workflow context
|
||||
- Streaming works out of the box
|
||||
|
||||
## Patterns
|
||||
|
||||
### Long-Running Workflow with Pauses
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function onboardUser(userId: string) {
|
||||
'use step'
|
||||
await sendWelcomeEmail(userId)
|
||||
|
||||
'use step'
|
||||
// Wait for user to verify email (could be hours/days)
|
||||
await waitForEvent(`email-verified:${userId}`)
|
||||
|
||||
'use step'
|
||||
await setupDefaultWorkspace(userId)
|
||||
|
||||
'use step'
|
||||
await sendOnboardingGuide(userId)
|
||||
}
|
||||
```
|
||||
|
||||
### Workflow with Error Handling
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function processRefund(orderId: string) {
|
||||
'use step'
|
||||
const order = await getOrder(orderId)
|
||||
|
||||
'use step'
|
||||
try {
|
||||
await issueRefund(order)
|
||||
} catch (error) {
|
||||
// Step will be retried automatically on transient errors
|
||||
// For permanent failures, the error is recorded
|
||||
throw error
|
||||
}
|
||||
|
||||
'use step'
|
||||
await notifyCustomer(order, 'refund_processed')
|
||||
}
|
||||
```
|
||||
|
||||
### Fan-Out / Parallel Steps
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function processImages(imageIds: string[]) {
|
||||
'use step'
|
||||
const images = await getImages(imageIds)
|
||||
|
||||
// Process in parallel — each is its own step
|
||||
const results = await Promise.all(
|
||||
images.map(async (img) => {
|
||||
'use step'
|
||||
return await resizeImage(img)
|
||||
})
|
||||
)
|
||||
|
||||
'use step'
|
||||
await saveResults(results)
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Next.js
|
||||
|
||||
Workflows are exposed as API routes in Next.js:
|
||||
|
||||
```ts
|
||||
// app/api/workflows/process-order/route.ts
|
||||
import { processOrder } from '@/workflows/process-order'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { orderId } = await req.json()
|
||||
const result = await processOrder(orderId)
|
||||
return Response.json(result)
|
||||
}
|
||||
```
|
||||
|
||||
## Key Properties
|
||||
|
||||
- **Open source**: No vendor lock-in
|
||||
- **TypeScript-native**: async/await, no YAML or state machines
|
||||
- **Observable**: Step-level visibility, timing, inputs/outputs
|
||||
- **Retryable**: Automatic retry with configurable backoff
|
||||
- **Portable**: Local, Vercel, or self-hosted
|
||||
- **AI-first**: DurableAgent wraps AI SDK seamlessly
|
||||
|
||||
## When to Use WDK vs. Regular Functions
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| Simple API endpoint, fast response | Regular Route Handler |
|
||||
| Multi-step process, must complete all steps | WDK Workflow |
|
||||
| AI agent in production, must not lose state | WDK DurableAgent |
|
||||
| Background job that can take minutes/hours | WDK Workflow |
|
||||
| Process spanning multiple services | WDK Workflow |
|
||||
| Quick one-shot LLM call | AI SDK directly |
|
||||
|
||||
## Official Documentation
|
||||
|
||||
- [Workflow DevKit](https://vercel.com/docs/workflow)
|
||||
- [Vercel Functions](https://vercel.com/docs/functions) — Workflows compile to Vercel Functions
|
||||
- [AI SDK Agents](https://sdk.vercel.ai/docs/ai-sdk-core/agents) — DurableAgent wraps AI SDK Agent
|
||||
@@ -0,0 +1,108 @@
|
||||
# Workflow DevKit — DurableAgent Patterns
|
||||
|
||||
## Basic DurableAgent
|
||||
|
||||
```ts
|
||||
import { DurableAgent } from '@workflow/ai/agent'
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
const agent = new DurableAgent({
|
||||
model: openai('gpt-5-mini'),
|
||||
system: 'You are a helpful research assistant.',
|
||||
tools: {
|
||||
searchWeb: tool({
|
||||
description: 'Search the web for information',
|
||||
inputSchema: z.object({ query: z.string() }),
|
||||
execute: async ({ query }) => {
|
||||
// Search implementation
|
||||
return { results: await webSearch(query) }
|
||||
},
|
||||
}),
|
||||
writeReport: tool({
|
||||
description: 'Write a report to a file',
|
||||
inputSchema: z.object({
|
||||
title: z.string(),
|
||||
content: z.string(),
|
||||
}),
|
||||
execute: async ({ title, content }) => {
|
||||
await writeFile(`reports/${title}.md`, content)
|
||||
return { written: true }
|
||||
},
|
||||
}),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Workflow Endpoint (Next.js)
|
||||
|
||||
```ts
|
||||
// app/api/workflows/research/route.ts
|
||||
'use workflow'
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { topic } = await req.json()
|
||||
|
||||
const result = await agent.generateText({
|
||||
prompt: `Research "${topic}" thoroughly and produce a comprehensive report.`,
|
||||
})
|
||||
|
||||
return Response.json({ report: result.text })
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow with Human-in-the-Loop
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function processApplication(applicationId: string) {
|
||||
'use step'
|
||||
const app = await getApplication(applicationId)
|
||||
|
||||
'use step'
|
||||
const aiReview = await agent.generateText({
|
||||
prompt: `Review this application: ${JSON.stringify(app)}`,
|
||||
})
|
||||
|
||||
'use step'
|
||||
await notifyReviewer(aiReview.text)
|
||||
|
||||
'use step'
|
||||
// Pauses here until human approves — could be hours or days
|
||||
const approval = await waitForEvent(`approval:${applicationId}`)
|
||||
|
||||
'use step'
|
||||
if (approval.approved) {
|
||||
await acceptApplication(applicationId)
|
||||
} else {
|
||||
await rejectApplication(applicationId, approval.reason)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow with Parallel Fan-Out
|
||||
|
||||
```ts
|
||||
'use workflow'
|
||||
|
||||
export async function analyzeCompetitors(competitors: string[]) {
|
||||
'use step'
|
||||
const analyses = await Promise.all(
|
||||
competitors.map(async (competitor) => {
|
||||
'use step'
|
||||
return await agent.generateText({
|
||||
prompt: `Analyze ${competitor}'s product strategy.`,
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
'use step'
|
||||
const summary = await agent.generateText({
|
||||
prompt: `Synthesize these competitive analyses: ${analyses.map(a => a.text).join('\n\n')}`,
|
||||
})
|
||||
|
||||
return summary.text
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user