create-baton 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/constants.js +3 -3
- package/templates/BATON_v3.1.md +53 -15
- package/templates/skills/domains/api/SKILL.md +318 -0
- package/templates/skills/domains/ecommerce/SKILL.md +277 -0
- package/templates/skills/domains/portfolio/SKILL.md +213 -0
- package/templates/skills/domains/saas/SKILL.md +252 -0
- package/templates/skills/patterns/authentication/SKILL.md +245 -0
- package/templates/skills/patterns/database-design/SKILL.md +230 -0
- package/templates/skills/patterns/email/SKILL.md +236 -0
- package/templates/skills/patterns/file-uploads/SKILL.md +216 -0
- package/templates/skills/patterns/payments/SKILL.md +246 -0
- package/templates/skills/patterns/seo/SKILL.md +219 -0
- package/templates/skills/stacks/prisma/SKILL.md +281 -0
- package/templates/skills/stacks/shadcn/SKILL.md +270 -0
- package/templates/skills/stacks/tailwind/SKILL.md +242 -0
- package/templates/skills/stacks/typescript/SKILL.md +241 -0
- package/templates/skills/stacks/vercel/SKILL.md +232 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript
|
|
3
|
+
description: >-
|
|
4
|
+
TypeScript patterns — type safety without over-engineering, practical typing
|
|
5
|
+
strategies, common patterns, and when to use strict vs pragmatic approaches.
|
|
6
|
+
Load at Session 0-1 when using TypeScript. Use when discussing types or
|
|
7
|
+
fixing type errors.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# TypeScript Skill
|
|
11
|
+
|
|
12
|
+
> Types prevent bugs. Over-typing prevents progress. Find the balance.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Pragmatic TypeScript Rules
|
|
17
|
+
|
|
18
|
+
### Type What Matters
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// DO type: function signatures, API responses, props
|
|
22
|
+
function createUser(data: CreateUserInput): Promise<User> { ... }
|
|
23
|
+
|
|
24
|
+
// DON'T over-type: obvious local variables
|
|
25
|
+
const count = items.length; // TypeScript infers `number`
|
|
26
|
+
const name = 'hello'; // TypeScript infers `string`
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### When to Use `any` (Seriously)
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Temporary during prototyping — add a TODO
|
|
33
|
+
const data: any = await weirdApi.call(); // TODO: type this
|
|
34
|
+
|
|
35
|
+
// Third-party library without types
|
|
36
|
+
declare module 'untyped-lib' {
|
|
37
|
+
const lib: any;
|
|
38
|
+
export default lib;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Rule:** `any` is a tool, not a sin. Use it sparingly, mark it with TODO, fix later.
|
|
43
|
+
|
|
44
|
+
### Prefer `unknown` Over `any` for Safety
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// BAD — any lets you do anything without checking
|
|
48
|
+
function process(data: any) {
|
|
49
|
+
data.whatever(); // No error, crashes at runtime
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// GOOD — unknown forces you to check first
|
|
53
|
+
function process(data: unknown) {
|
|
54
|
+
if (typeof data === 'string') {
|
|
55
|
+
data.toUpperCase(); // Safe
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Essential Patterns
|
|
63
|
+
|
|
64
|
+
### API Response Types
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Define once, use everywhere
|
|
68
|
+
interface User {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
name: string;
|
|
72
|
+
createdAt: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// For create/update, make fields optional
|
|
76
|
+
type CreateUserInput = Pick<User, 'email' | 'name'>;
|
|
77
|
+
type UpdateUserInput = Partial<Pick<User, 'name'>>;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Component Props
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Simple props
|
|
84
|
+
interface ButtonProps {
|
|
85
|
+
label: string;
|
|
86
|
+
onClick: () => void;
|
|
87
|
+
variant?: 'primary' | 'secondary';
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Props that extend HTML elements
|
|
92
|
+
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
93
|
+
label: string;
|
|
94
|
+
error?: string;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Server Action Return Types
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// Consistent return type for all server actions
|
|
102
|
+
type ActionResult<T = void> =
|
|
103
|
+
| { success: true; data: T }
|
|
104
|
+
| { success: false; error: string };
|
|
105
|
+
|
|
106
|
+
// Usage
|
|
107
|
+
export async function createItem(input: CreateItemInput): Promise<ActionResult<Item>> {
|
|
108
|
+
try {
|
|
109
|
+
const item = await db.insert(items).values(input).returning();
|
|
110
|
+
return { success: true, data: item };
|
|
111
|
+
} catch {
|
|
112
|
+
return { success: false, error: 'Failed to create item' };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Zod for Runtime Validation
|
|
120
|
+
|
|
121
|
+
Types only exist at compile time. Use Zod for runtime:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { z } from 'zod';
|
|
125
|
+
|
|
126
|
+
// Define schema (runtime validation)
|
|
127
|
+
const createItemSchema = z.object({
|
|
128
|
+
name: z.string().min(1).max(255),
|
|
129
|
+
price: z.number().positive(),
|
|
130
|
+
category: z.enum(['electronics', 'clothing', 'food']),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Infer TypeScript type from schema (compile-time type)
|
|
134
|
+
type CreateItemInput = z.infer<typeof createItemSchema>;
|
|
135
|
+
|
|
136
|
+
// Validate at runtime
|
|
137
|
+
const parsed = createItemSchema.safeParse(userInput);
|
|
138
|
+
if (!parsed.success) {
|
|
139
|
+
// Handle validation error
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Rule:** Use Zod at system boundaries (API routes, server actions, form submissions). Don't validate internal function calls.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Common Type Utilities
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Make some fields required
|
|
151
|
+
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
|
152
|
+
|
|
153
|
+
// Database row type (from Supabase or Prisma)
|
|
154
|
+
type Item = Database['public']['Tables']['items']['Row'];
|
|
155
|
+
|
|
156
|
+
// Omit fields you don't want to expose
|
|
157
|
+
type PublicUser = Omit<User, 'passwordHash' | 'internalNotes'>;
|
|
158
|
+
|
|
159
|
+
// Record for key-value maps
|
|
160
|
+
const featureFlags: Record<string, boolean> = {
|
|
161
|
+
darkMode: true,
|
|
162
|
+
betaFeatures: false,
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## tsconfig Best Practices
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"compilerOptions": {
|
|
173
|
+
"strict": true,
|
|
174
|
+
"noUncheckedIndexedAccess": true,
|
|
175
|
+
"forceConsistentCasingInFileNames": true,
|
|
176
|
+
"skipLibCheck": true
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- **strict: true** — enables all strict checks (do this from day 1)
|
|
182
|
+
- **noUncheckedIndexedAccess** — catches `array[0]` potentially being undefined
|
|
183
|
+
- **skipLibCheck** — faster compilation, skip checking node_modules types
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Fixing Common Type Errors
|
|
188
|
+
|
|
189
|
+
### "Type X is not assignable to type Y"
|
|
190
|
+
|
|
191
|
+
Usually means you're passing the wrong shape. Check:
|
|
192
|
+
1. Are you passing all required fields?
|
|
193
|
+
2. Are field names spelled correctly?
|
|
194
|
+
3. Is the data coming from an untyped source (API, form)?
|
|
195
|
+
|
|
196
|
+
### "Object is possibly undefined"
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// BAD
|
|
200
|
+
const name = user.name; // Error if user might be null
|
|
201
|
+
|
|
202
|
+
// GOOD — optional chaining
|
|
203
|
+
const name = user?.name;
|
|
204
|
+
|
|
205
|
+
// GOOD — early return
|
|
206
|
+
if (!user) return null;
|
|
207
|
+
const name = user.name; // TypeScript knows user exists here
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### "Property does not exist on type"
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Usually from untyped API responses
|
|
214
|
+
const data = await fetch('/api').then(r => r.json());
|
|
215
|
+
// TypeScript doesn't know what `data` is
|
|
216
|
+
|
|
217
|
+
// Fix: type the response
|
|
218
|
+
const data: ApiResponse = await fetch('/api').then(r => r.json());
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Don'ts
|
|
224
|
+
|
|
225
|
+
| Don't | Do Instead |
|
|
226
|
+
|-------|-----------|
|
|
227
|
+
| Type every variable | Let TypeScript infer obvious types |
|
|
228
|
+
| Create types for one-time objects | Inline the type or use `typeof` |
|
|
229
|
+
| Use `as` to silence errors | Fix the actual type mismatch |
|
|
230
|
+
| Create separate type files for everything | Co-locate types with the code that uses them |
|
|
231
|
+
| Use `enum` | Use `as const` objects or union types |
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// Instead of enum
|
|
235
|
+
const Status = { ACTIVE: 'active', ARCHIVED: 'archived' } as const;
|
|
236
|
+
type Status = typeof Status[keyof typeof Status]; // 'active' | 'archived'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
*Last updated: Baton Protocol v3.1*
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vercel
|
|
3
|
+
description: >-
|
|
4
|
+
Vercel deployment patterns — project setup, environment variables, domains,
|
|
5
|
+
preview deployments, and edge functions. Load at Session 3+ when deploying
|
|
6
|
+
to Vercel. Use when setting up deployment or troubleshooting builds.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Vercel Skill
|
|
10
|
+
|
|
11
|
+
> Deploy first, polish later. A live URL changes everything.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## First Deployment (Session 3)
|
|
16
|
+
|
|
17
|
+
### Connect Repository
|
|
18
|
+
|
|
19
|
+
1. Go to vercel.com → New Project
|
|
20
|
+
2. Import your GitHub repo
|
|
21
|
+
3. Vercel auto-detects Next.js (or other frameworks)
|
|
22
|
+
4. Click Deploy
|
|
23
|
+
|
|
24
|
+
That's it. Every push to `main` auto-deploys.
|
|
25
|
+
|
|
26
|
+
### Or Use CLI
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm i -g vercel
|
|
30
|
+
vercel # Deploy preview
|
|
31
|
+
vercel --prod # Deploy to production
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Environment Variables
|
|
37
|
+
|
|
38
|
+
### Setting Them
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Vercel Dashboard → Project → Settings → Environment Variables
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Environment Scoping
|
|
45
|
+
|
|
46
|
+
| Scope | When Applied |
|
|
47
|
+
|-------|-------------|
|
|
48
|
+
| Production | `vercel.com` (main branch) |
|
|
49
|
+
| Preview | PR deployments, branches |
|
|
50
|
+
| Development | `vercel dev` locally |
|
|
51
|
+
|
|
52
|
+
**Rule:** Always set variables for all three environments. Missing preview env vars = broken PR previews.
|
|
53
|
+
|
|
54
|
+
### Naming
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
DATABASE_URL=... # Server only
|
|
58
|
+
STRIPE_SECRET_KEY=... # Server only
|
|
59
|
+
NEXT_PUBLIC_APP_URL=... # Client + server (public)
|
|
60
|
+
NEXT_PUBLIC_SUPABASE_URL=... # Client + server (public)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**`NEXT_PUBLIC_` prefix = exposed to browser.** Never put secrets in `NEXT_PUBLIC_` variables.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Custom Domain
|
|
68
|
+
|
|
69
|
+
### Setup
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Vercel Dashboard → Project → Settings → Domains → Add
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### DNS Configuration
|
|
76
|
+
|
|
77
|
+
| Type | Name | Value |
|
|
78
|
+
|------|------|-------|
|
|
79
|
+
| A | @ | 76.76.21.21 |
|
|
80
|
+
| CNAME | www | cname.vercel-dns.com |
|
|
81
|
+
|
|
82
|
+
### SSL
|
|
83
|
+
|
|
84
|
+
Automatic. Vercel provisions Let's Encrypt certificates. Nothing to configure.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Preview Deployments
|
|
89
|
+
|
|
90
|
+
Every PR gets its own URL: `project-git-branch-name-username.vercel.app`
|
|
91
|
+
|
|
92
|
+
### Uses
|
|
93
|
+
|
|
94
|
+
- Test features before merging
|
|
95
|
+
- Share with teammates for review
|
|
96
|
+
- QA on a real URL
|
|
97
|
+
- Test with preview environment variables
|
|
98
|
+
|
|
99
|
+
### Preview Comments
|
|
100
|
+
|
|
101
|
+
Enable in project settings. Team members can comment directly on preview deployments.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Build Configuration
|
|
106
|
+
|
|
107
|
+
### Next.js Defaults (Usually No Config Needed)
|
|
108
|
+
|
|
109
|
+
Vercel auto-detects:
|
|
110
|
+
- Build command: `next build`
|
|
111
|
+
- Output directory: `.next`
|
|
112
|
+
- Install command: `npm install`
|
|
113
|
+
|
|
114
|
+
### Custom Build Settings
|
|
115
|
+
|
|
116
|
+
Only change if needed:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Vercel Dashboard → Project → Settings → General → Build & Development Settings
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Build Troubleshooting
|
|
123
|
+
|
|
124
|
+
| Problem | Fix |
|
|
125
|
+
|---------|-----|
|
|
126
|
+
| Build fails locally works | Check Node.js version matches (Settings → General) |
|
|
127
|
+
| Missing env vars | Add to Vercel dashboard, not just .env.local |
|
|
128
|
+
| Type errors in build | Run `npx tsc --noEmit` locally first |
|
|
129
|
+
| Module not found | Check imports, case sensitivity (Linux is case-sensitive) |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Serverless Functions
|
|
134
|
+
|
|
135
|
+
### API Routes (Automatic)
|
|
136
|
+
|
|
137
|
+
Next.js API routes automatically become serverless functions:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// app/api/hello/route.ts → deployed as serverless function
|
|
141
|
+
export async function GET() {
|
|
142
|
+
return Response.json({ message: 'hello' });
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Limits
|
|
147
|
+
|
|
148
|
+
| Limit | Free | Pro |
|
|
149
|
+
|-------|------|-----|
|
|
150
|
+
| Duration | 10s | 60s |
|
|
151
|
+
| Memory | 1024MB | 3008MB |
|
|
152
|
+
| Size | 4.5MB | 4.5MB |
|
|
153
|
+
|
|
154
|
+
**If your function times out:** Optimize the query, add caching, or upgrade to Pro.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Edge Functions
|
|
159
|
+
|
|
160
|
+
### When to Use
|
|
161
|
+
|
|
162
|
+
- Middleware (auth checks, redirects)
|
|
163
|
+
- Geolocation-based responses
|
|
164
|
+
- A/B testing
|
|
165
|
+
- When you need <50ms response times globally
|
|
166
|
+
|
|
167
|
+
### When NOT to Use
|
|
168
|
+
|
|
169
|
+
- Database queries (most databases aren't at the edge)
|
|
170
|
+
- Heavy computation
|
|
171
|
+
- Node.js-specific APIs
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// middleware.ts runs on the edge by default
|
|
175
|
+
export function middleware(request: NextRequest) {
|
|
176
|
+
// Fast, runs in 50+ global locations
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Caching
|
|
183
|
+
|
|
184
|
+
### Static Pages (Automatic)
|
|
185
|
+
|
|
186
|
+
Pages without dynamic data are statically generated at build time. Served from CDN. Instant.
|
|
187
|
+
|
|
188
|
+
### ISR (Incremental Static Regeneration)
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Revalidate every 60 seconds
|
|
192
|
+
export const revalidate = 60;
|
|
193
|
+
|
|
194
|
+
export default async function Page() {
|
|
195
|
+
const data = await getData();
|
|
196
|
+
return <div>{data}</div>;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### On-Demand Revalidation
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// app/api/revalidate/route.ts
|
|
204
|
+
import { revalidatePath } from 'next/cache';
|
|
205
|
+
|
|
206
|
+
export async function POST(request: Request) {
|
|
207
|
+
const { path } = await request.json();
|
|
208
|
+
revalidatePath(path);
|
|
209
|
+
return Response.json({ revalidated: true });
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Deployment Checklist
|
|
216
|
+
|
|
217
|
+
### Before First Deploy
|
|
218
|
+
- [ ] Build passes locally (`npm run build`)
|
|
219
|
+
- [ ] No TypeScript errors (`npx tsc --noEmit`)
|
|
220
|
+
- [ ] Environment variables set in Vercel dashboard
|
|
221
|
+
- [ ] `.env.local` is in `.gitignore`
|
|
222
|
+
|
|
223
|
+
### Before Going Live
|
|
224
|
+
- [ ] Custom domain configured
|
|
225
|
+
- [ ] Production environment variables set
|
|
226
|
+
- [ ] Preview deployments working
|
|
227
|
+
- [ ] Test the production URL (not just preview)
|
|
228
|
+
- [ ] Error tracking installed (Sentry)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
*Last updated: Baton Protocol v3.1*
|