@smicolon/ai-kit 0.3.1 → 0.4.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/README.md +73 -40
- package/dist/index.js +260 -126
- package/package.json +5 -5
- package/.claude-plugin/marketplace.json +0 -373
- package/packs/architect/CHANGELOG.md +0 -17
- package/packs/architect/README.md +0 -58
- package/packs/architect/agents/system-architect.md +0 -768
- package/packs/architect/commands/diagram-create.md +0 -300
- package/packs/better-auth/.mcp.json +0 -14
- package/packs/better-auth/CHANGELOG.md +0 -26
- package/packs/better-auth/README.md +0 -125
- package/packs/better-auth/agents/auth-architect.md +0 -278
- package/packs/better-auth/commands/auth-provider-add.md +0 -265
- package/packs/better-auth/commands/auth-setup.md +0 -298
- package/packs/better-auth/skills/auth-security/SKILL.md +0 -425
- package/packs/better-auth/skills/better-auth-patterns/SKILL.md +0 -455
- package/packs/dev-loop/CHANGELOG.md +0 -69
- package/packs/dev-loop/README.md +0 -155
- package/packs/dev-loop/commands/cancel-dev.md +0 -21
- package/packs/dev-loop/commands/dev-loop.md +0 -72
- package/packs/dev-loop/commands/dev-plan.md +0 -351
- package/packs/dev-loop/hooks/hooks.json +0 -15
- package/packs/dev-loop/hooks/stop-hook.sh +0 -178
- package/packs/dev-loop/scripts/setup-dev-loop.sh +0 -194
- package/packs/dev-loop/skills/tdd-planner/SKILL.md +0 -249
- package/packs/dev-loop/skills/tdd-planner/references/framework-patterns.md +0 -874
- package/packs/dev-loop/skills/tdd-planner/references/good-example.md +0 -260
- package/packs/dev-loop/skills/tdd-planner/references/plan-template.md +0 -275
- package/packs/django/CHANGELOG.md +0 -39
- package/packs/django/README.md +0 -92
- package/packs/django/agents/django-architect.md +0 -182
- package/packs/django/agents/django-builder.md +0 -250
- package/packs/django/agents/django-feature-based.md +0 -420
- package/packs/django/agents/django-reviewer.md +0 -253
- package/packs/django/agents/django-tester.md +0 -230
- package/packs/django/commands/api-endpoint.md +0 -285
- package/packs/django/commands/model-create.md +0 -178
- package/packs/django/commands/test-generate.md +0 -325
- package/packs/django/rules/migrations.md +0 -138
- package/packs/django/rules/models.md +0 -167
- package/packs/django/rules/serializers.md +0 -126
- package/packs/django/rules/services.md +0 -131
- package/packs/django/rules/tests.md +0 -140
- package/packs/django/rules/views.md +0 -102
- package/packs/django/skills/import-convention-enforcer/SKILL.md +0 -226
- package/packs/django/skills/import-convention-enforcer/patterns/django-imports.md +0 -343
- package/packs/django/skills/migration-safety-checker/SKILL.md +0 -375
- package/packs/django/skills/model-entity-validator/SKILL.md +0 -298
- package/packs/django/skills/performance-optimizer/SKILL.md +0 -447
- package/packs/django/skills/red-phase-verifier/SKILL.md +0 -180
- package/packs/django/skills/security-first-validator/SKILL.md +0 -435
- package/packs/django/skills/test-coverage-advisor/SKILL.md +0 -394
- package/packs/django/skills/test-validity-checker/SKILL.md +0 -194
- package/packs/failure-log/CHANGELOG.md +0 -20
- package/packs/failure-log/README.md +0 -168
- package/packs/failure-log/commands/failure-add.md +0 -106
- package/packs/failure-log/commands/failure-list.md +0 -89
- package/packs/failure-log/hooks/hooks.json +0 -16
- package/packs/failure-log/hooks/scripts/inject-failures.sh +0 -64
- package/packs/failure-log/skills/failure-log-manager/SKILL.md +0 -164
- package/packs/flutter/CHANGELOG.md +0 -19
- package/packs/flutter/README.md +0 -170
- package/packs/flutter/agents/flutter-architect.md +0 -166
- package/packs/flutter/agents/flutter-builder.md +0 -303
- package/packs/flutter/agents/release-manager.md +0 -355
- package/packs/flutter/commands/fastlane-setup.md +0 -188
- package/packs/flutter/commands/flutter-build.md +0 -90
- package/packs/flutter/commands/flutter-deploy.md +0 -133
- package/packs/flutter/commands/flutter-test.md +0 -117
- package/packs/flutter/commands/signing-setup.md +0 -209
- package/packs/flutter/hooks/hooks.json +0 -17
- package/packs/flutter/skills/fastlane-knowledge/SKILL.md +0 -193
- package/packs/flutter/skills/flutter-architecture/SKILL.md +0 -127
- package/packs/flutter/skills/store-publishing/SKILL.md +0 -163
- package/packs/hono/CHANGELOG.md +0 -19
- package/packs/hono/README.md +0 -143
- package/packs/hono/agents/hono-architect.md +0 -240
- package/packs/hono/agents/hono-builder.md +0 -285
- package/packs/hono/agents/hono-reviewer.md +0 -279
- package/packs/hono/agents/hono-tester.md +0 -346
- package/packs/hono/commands/middleware-create.md +0 -223
- package/packs/hono/commands/project-init.md +0 -306
- package/packs/hono/commands/route-create.md +0 -153
- package/packs/hono/commands/rpc-client.md +0 -263
- package/packs/hono/hooks/hooks.json +0 -4
- package/packs/hono/skills/cloudflare-bindings/SKILL.md +0 -408
- package/packs/hono/skills/hono-patterns/SKILL.md +0 -309
- package/packs/hono/skills/rpc-typesafe/SKILL.md +0 -388
- package/packs/hono/skills/zod-validation/SKILL.md +0 -332
- package/packs/nestjs/CHANGELOG.md +0 -29
- package/packs/nestjs/README.md +0 -75
- package/packs/nestjs/agents/nestjs-architect.md +0 -402
- package/packs/nestjs/agents/nestjs-builder.md +0 -301
- package/packs/nestjs/agents/nestjs-tester.md +0 -437
- package/packs/nestjs/commands/module-create.md +0 -369
- package/packs/nestjs/rules/controllers.md +0 -92
- package/packs/nestjs/rules/dto.md +0 -124
- package/packs/nestjs/rules/entities.md +0 -102
- package/packs/nestjs/rules/services.md +0 -106
- package/packs/nestjs/skills/barrel-export-manager/SKILL.md +0 -389
- package/packs/nestjs/skills/import-convention-enforcer/SKILL.md +0 -365
- package/packs/nextjs/CHANGELOG.md +0 -36
- package/packs/nextjs/README.md +0 -76
- package/packs/nextjs/agents/frontend-tester.md +0 -680
- package/packs/nextjs/agents/frontend-visual.md +0 -820
- package/packs/nextjs/agents/nextjs-architect.md +0 -331
- package/packs/nextjs/agents/nextjs-modular.md +0 -433
- package/packs/nextjs/commands/component-create.md +0 -398
- package/packs/nextjs/rules/api-routes.md +0 -129
- package/packs/nextjs/rules/components.md +0 -106
- package/packs/nextjs/rules/hooks.md +0 -132
- package/packs/nextjs/skills/accessibility-validator/SKILL.md +0 -445
- package/packs/nextjs/skills/import-convention-enforcer/SKILL.md +0 -399
- package/packs/nextjs/skills/react-form-validator/SKILL.md +0 -569
- package/packs/nuxtjs/CHANGELOG.md +0 -30
- package/packs/nuxtjs/README.md +0 -56
- package/packs/nuxtjs/agents/frontend-tester.md +0 -680
- package/packs/nuxtjs/agents/frontend-visual.md +0 -820
- package/packs/nuxtjs/agents/nuxtjs-architect.md +0 -537
- package/packs/nuxtjs/commands/component-create.md +0 -223
- package/packs/nuxtjs/rules/components.md +0 -101
- package/packs/nuxtjs/rules/composables.md +0 -118
- package/packs/nuxtjs/rules/server-routes.md +0 -127
- package/packs/nuxtjs/skills/accessibility-validator/SKILL.md +0 -183
- package/packs/nuxtjs/skills/import-convention-enforcer/SKILL.md +0 -196
- package/packs/nuxtjs/skills/veevalidate-form-validator/SKILL.md +0 -190
- package/packs/onboard/CHANGELOG.md +0 -22
- package/packs/onboard/README.md +0 -103
- package/packs/onboard/agents/onboard-guide.md +0 -118
- package/packs/onboard/commands/onboard.md +0 -313
- package/packs/onboard/skills/onboard-context-provider/SKILL.md +0 -98
- package/packs/tanstack-router/CHANGELOG.md +0 -30
- package/packs/tanstack-router/README.md +0 -113
- package/packs/tanstack-router/agents/tanstack-architect.md +0 -173
- package/packs/tanstack-router/agents/tanstack-builder.md +0 -360
- package/packs/tanstack-router/agents/tanstack-tester.md +0 -454
- package/packs/tanstack-router/commands/form-create.md +0 -313
- package/packs/tanstack-router/commands/query-create.md +0 -263
- package/packs/tanstack-router/commands/route-create.md +0 -190
- package/packs/tanstack-router/commands/table-create.md +0 -413
- package/packs/tanstack-router/skills/ai-patterns/SKILL.md +0 -370
- package/packs/tanstack-router/skills/db-patterns/SKILL.md +0 -346
- package/packs/tanstack-router/skills/devtools-patterns/SKILL.md +0 -415
- package/packs/tanstack-router/skills/form-patterns/SKILL.md +0 -425
- package/packs/tanstack-router/skills/pacer-patterns/SKILL.md +0 -341
- package/packs/tanstack-router/skills/query-patterns/SKILL.md +0 -359
- package/packs/tanstack-router/skills/router-patterns/SKILL.md +0 -285
- package/packs/tanstack-router/skills/store-patterns/SKILL.md +0 -351
- package/packs/tanstack-router/skills/table-patterns/SKILL.md +0 -531
- package/packs/tanstack-router/skills/tanstack-conventions/SKILL.md +0 -428
- package/packs/tanstack-router/skills/virtual-patterns/SKILL.md +0 -490
- package/packs/worktree/CHANGELOG.md +0 -45
- package/packs/worktree/README.md +0 -219
- package/packs/worktree/commands/wt.md +0 -93
- package/packs/worktree/scripts/wt.sh +0 -957
- package/packs/worktree/skills/worktree-manager/SKILL.md +0 -113
|
@@ -1,408 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: cloudflare-bindings
|
|
3
|
-
description: This skill activates when working with Cloudflare Workers bindings like D1, KV, R2, Durable Objects, or environment variables. It provides patterns for database access, caching, file storage, and secrets management.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Cloudflare Bindings
|
|
7
|
-
|
|
8
|
-
Patterns for Cloudflare Workers bindings in Hono.
|
|
9
|
-
|
|
10
|
-
## Type Definitions
|
|
11
|
-
|
|
12
|
-
Define all bindings in a central type:
|
|
13
|
-
|
|
14
|
-
```typescript
|
|
15
|
-
// types/bindings.ts
|
|
16
|
-
export type Env = {
|
|
17
|
-
Bindings: {
|
|
18
|
-
// D1 Database
|
|
19
|
-
DB: D1Database
|
|
20
|
-
|
|
21
|
-
// KV Namespace
|
|
22
|
-
KV: KVNamespace
|
|
23
|
-
|
|
24
|
-
// R2 Bucket
|
|
25
|
-
BUCKET: R2Bucket
|
|
26
|
-
|
|
27
|
-
// Durable Object
|
|
28
|
-
COUNTER: DurableObjectNamespace
|
|
29
|
-
|
|
30
|
-
// Environment Variables
|
|
31
|
-
ENVIRONMENT: 'development' | 'staging' | 'production'
|
|
32
|
-
API_KEY: string
|
|
33
|
-
JWT_SECRET: string
|
|
34
|
-
}
|
|
35
|
-
Variables: {
|
|
36
|
-
user: User
|
|
37
|
-
requestId: string
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## D1 Database
|
|
43
|
-
|
|
44
|
-
### Basic Queries
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
app.get('/users', async (c) => {
|
|
48
|
-
const db = c.env.DB
|
|
49
|
-
|
|
50
|
-
// Select all
|
|
51
|
-
const { results } = await db
|
|
52
|
-
.prepare('SELECT * FROM users WHERE deleted_at IS NULL')
|
|
53
|
-
.all()
|
|
54
|
-
|
|
55
|
-
return c.json({ data: results })
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
app.get('/users/:id', async (c) => {
|
|
59
|
-
const db = c.env.DB
|
|
60
|
-
const id = c.req.param('id')
|
|
61
|
-
|
|
62
|
-
// Select one
|
|
63
|
-
const user = await db
|
|
64
|
-
.prepare('SELECT * FROM users WHERE id = ?')
|
|
65
|
-
.bind(id)
|
|
66
|
-
.first()
|
|
67
|
-
|
|
68
|
-
if (!user) {
|
|
69
|
-
return c.json({ error: 'User not found' }, 404)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return c.json({ data: user })
|
|
73
|
-
})
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Insert and Update
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
app.post('/users', async (c) => {
|
|
80
|
-
const db = c.env.DB
|
|
81
|
-
const { email, name } = c.req.valid('json')
|
|
82
|
-
const id = crypto.randomUUID()
|
|
83
|
-
|
|
84
|
-
const result = await db
|
|
85
|
-
.prepare('INSERT INTO users (id, email, name) VALUES (?, ?, ?)')
|
|
86
|
-
.bind(id, email, name)
|
|
87
|
-
.run()
|
|
88
|
-
|
|
89
|
-
return c.json({ data: { id, email, name } }, 201)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
app.put('/users/:id', async (c) => {
|
|
93
|
-
const db = c.env.DB
|
|
94
|
-
const id = c.req.param('id')
|
|
95
|
-
const { name } = c.req.valid('json')
|
|
96
|
-
|
|
97
|
-
await db
|
|
98
|
-
.prepare('UPDATE users SET name = ?, updated_at = datetime("now") WHERE id = ?')
|
|
99
|
-
.bind(name, id)
|
|
100
|
-
.run()
|
|
101
|
-
|
|
102
|
-
return c.json({ data: { id, name } })
|
|
103
|
-
})
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Transactions (Batch)
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
app.post('/transfer', async (c) => {
|
|
110
|
-
const db = c.env.DB
|
|
111
|
-
const { fromId, toId, amount } = c.req.valid('json')
|
|
112
|
-
|
|
113
|
-
// D1 batch for transaction-like behavior
|
|
114
|
-
const results = await db.batch([
|
|
115
|
-
db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?')
|
|
116
|
-
.bind(amount, fromId),
|
|
117
|
-
db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?')
|
|
118
|
-
.bind(amount, toId),
|
|
119
|
-
db.prepare('INSERT INTO transfers (from_id, to_id, amount) VALUES (?, ?, ?)')
|
|
120
|
-
.bind(fromId, toId, amount)
|
|
121
|
-
])
|
|
122
|
-
|
|
123
|
-
return c.json({ success: true })
|
|
124
|
-
})
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Pagination
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
app.get('/users', async (c) => {
|
|
131
|
-
const db = c.env.DB
|
|
132
|
-
const { page, limit } = c.req.valid('query')
|
|
133
|
-
const offset = (page - 1) * limit
|
|
134
|
-
|
|
135
|
-
const [users, countResult] = await Promise.all([
|
|
136
|
-
db.prepare('SELECT * FROM users LIMIT ? OFFSET ?')
|
|
137
|
-
.bind(limit, offset)
|
|
138
|
-
.all(),
|
|
139
|
-
db.prepare('SELECT COUNT(*) as total FROM users')
|
|
140
|
-
.first<{ total: number }>()
|
|
141
|
-
])
|
|
142
|
-
|
|
143
|
-
return c.json({
|
|
144
|
-
data: users.results,
|
|
145
|
-
meta: {
|
|
146
|
-
page,
|
|
147
|
-
limit,
|
|
148
|
-
total: countResult?.total || 0
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## KV Namespace
|
|
155
|
-
|
|
156
|
-
### Basic Operations
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// Get
|
|
160
|
-
app.get('/cache/:key', async (c) => {
|
|
161
|
-
const kv = c.env.KV
|
|
162
|
-
const key = c.req.param('key')
|
|
163
|
-
|
|
164
|
-
const value = await kv.get(key)
|
|
165
|
-
|
|
166
|
-
if (!value) {
|
|
167
|
-
return c.json({ error: 'Not found' }, 404)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return c.json({ data: value })
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
// Get as JSON
|
|
174
|
-
const data = await kv.get('config', 'json')
|
|
175
|
-
|
|
176
|
-
// Get with metadata
|
|
177
|
-
const { value, metadata } = await kv.getWithMetadata('key')
|
|
178
|
-
|
|
179
|
-
// Put
|
|
180
|
-
await kv.put('key', 'value')
|
|
181
|
-
|
|
182
|
-
// Put with TTL (seconds)
|
|
183
|
-
await kv.put('session', token, { expirationTtl: 3600 })
|
|
184
|
-
|
|
185
|
-
// Put with expiration (Unix timestamp)
|
|
186
|
-
await kv.put('temp', data, { expiration: Date.now() / 1000 + 3600 })
|
|
187
|
-
|
|
188
|
-
// Put with metadata
|
|
189
|
-
await kv.put('user:123', JSON.stringify(user), {
|
|
190
|
-
metadata: { createdAt: Date.now() }
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
// Delete
|
|
194
|
-
await kv.delete('key')
|
|
195
|
-
|
|
196
|
-
// List keys
|
|
197
|
-
const { keys } = await kv.list({ prefix: 'user:' })
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Caching Pattern
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
app.get('/expensive-data', async (c) => {
|
|
204
|
-
const kv = c.env.KV
|
|
205
|
-
const cacheKey = 'expensive-data'
|
|
206
|
-
|
|
207
|
-
// Try cache first
|
|
208
|
-
const cached = await kv.get(cacheKey, 'json')
|
|
209
|
-
if (cached) {
|
|
210
|
-
return c.json({ data: cached, cached: true })
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Compute expensive data
|
|
214
|
-
const data = await computeExpensiveData()
|
|
215
|
-
|
|
216
|
-
// Cache for 5 minutes
|
|
217
|
-
await kv.put(cacheKey, JSON.stringify(data), { expirationTtl: 300 })
|
|
218
|
-
|
|
219
|
-
return c.json({ data, cached: false })
|
|
220
|
-
})
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Rate Limiting
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
const rateLimiter = createMiddleware<Env>(async (c, next) => {
|
|
227
|
-
const kv = c.env.KV
|
|
228
|
-
const ip = c.req.header('CF-Connecting-IP') || 'unknown'
|
|
229
|
-
const key = `ratelimit:${ip}`
|
|
230
|
-
|
|
231
|
-
const count = parseInt(await kv.get(key) || '0')
|
|
232
|
-
|
|
233
|
-
if (count >= 100) {
|
|
234
|
-
return c.json({ error: 'Rate limit exceeded' }, 429)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
await kv.put(key, String(count + 1), { expirationTtl: 60 })
|
|
238
|
-
|
|
239
|
-
await next()
|
|
240
|
-
})
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## R2 Bucket
|
|
244
|
-
|
|
245
|
-
### Upload Files
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
app.post('/upload', async (c) => {
|
|
249
|
-
const bucket = c.env.BUCKET
|
|
250
|
-
const body = await c.req.parseBody()
|
|
251
|
-
const file = body['file'] as File
|
|
252
|
-
|
|
253
|
-
if (!file) {
|
|
254
|
-
return c.json({ error: 'No file provided' }, 400)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const key = `uploads/${crypto.randomUUID()}-${file.name}`
|
|
258
|
-
|
|
259
|
-
await bucket.put(key, file.stream(), {
|
|
260
|
-
httpMetadata: {
|
|
261
|
-
contentType: file.type
|
|
262
|
-
},
|
|
263
|
-
customMetadata: {
|
|
264
|
-
originalName: file.name,
|
|
265
|
-
uploadedAt: new Date().toISOString()
|
|
266
|
-
}
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
return c.json({ key }, 201)
|
|
270
|
-
})
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Download Files
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
app.get('/files/:key', async (c) => {
|
|
277
|
-
const bucket = c.env.BUCKET
|
|
278
|
-
const key = c.req.param('key')
|
|
279
|
-
|
|
280
|
-
const object = await bucket.get(key)
|
|
281
|
-
|
|
282
|
-
if (!object) {
|
|
283
|
-
return c.json({ error: 'File not found' }, 404)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const headers = new Headers()
|
|
287
|
-
headers.set('Content-Type', object.httpMetadata?.contentType || 'application/octet-stream')
|
|
288
|
-
headers.set('Content-Length', String(object.size))
|
|
289
|
-
|
|
290
|
-
return new Response(object.body, { headers })
|
|
291
|
-
})
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### List Files
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
app.get('/files', async (c) => {
|
|
298
|
-
const bucket = c.env.BUCKET
|
|
299
|
-
const prefix = c.req.query('prefix') || ''
|
|
300
|
-
|
|
301
|
-
const listed = await bucket.list({
|
|
302
|
-
prefix,
|
|
303
|
-
limit: 100
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
return c.json({
|
|
307
|
-
data: listed.objects.map(obj => ({
|
|
308
|
-
key: obj.key,
|
|
309
|
-
size: obj.size,
|
|
310
|
-
uploaded: obj.uploaded
|
|
311
|
-
}))
|
|
312
|
-
})
|
|
313
|
-
})
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Delete Files
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
app.delete('/files/:key', async (c) => {
|
|
320
|
-
const bucket = c.env.BUCKET
|
|
321
|
-
const key = c.req.param('key')
|
|
322
|
-
|
|
323
|
-
await bucket.delete(key)
|
|
324
|
-
|
|
325
|
-
return c.body(null, 204)
|
|
326
|
-
})
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
## Environment Variables
|
|
330
|
-
|
|
331
|
-
### Access in Handlers
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
app.get('/config', (c) => {
|
|
335
|
-
const env = c.env.ENVIRONMENT
|
|
336
|
-
|
|
337
|
-
return c.json({
|
|
338
|
-
environment: env,
|
|
339
|
-
features: {
|
|
340
|
-
debug: env === 'development'
|
|
341
|
-
}
|
|
342
|
-
})
|
|
343
|
-
})
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### Secrets in Middleware
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
const authMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
350
|
-
const token = c.req.header('Authorization')?.replace('Bearer ', '')
|
|
351
|
-
|
|
352
|
-
if (!token) {
|
|
353
|
-
throw new HTTPException(401, { message: 'Missing token' })
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Use secret from environment
|
|
357
|
-
const payload = await verify(token, c.env.JWT_SECRET)
|
|
358
|
-
c.set('user', payload)
|
|
359
|
-
|
|
360
|
-
await next()
|
|
361
|
-
})
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## wrangler.toml Configuration
|
|
365
|
-
|
|
366
|
-
```toml
|
|
367
|
-
name = "my-app"
|
|
368
|
-
main = "src/index.ts"
|
|
369
|
-
compatibility_date = "2024-01-01"
|
|
370
|
-
|
|
371
|
-
[vars]
|
|
372
|
-
ENVIRONMENT = "production"
|
|
373
|
-
|
|
374
|
-
[[d1_databases]]
|
|
375
|
-
binding = "DB"
|
|
376
|
-
database_name = "my-app-db"
|
|
377
|
-
database_id = "xxxx-xxxx-xxxx"
|
|
378
|
-
|
|
379
|
-
[[kv_namespaces]]
|
|
380
|
-
binding = "KV"
|
|
381
|
-
id = "xxxx-xxxx-xxxx"
|
|
382
|
-
|
|
383
|
-
[[r2_buckets]]
|
|
384
|
-
binding = "BUCKET"
|
|
385
|
-
bucket_name = "my-app-bucket"
|
|
386
|
-
|
|
387
|
-
# Secrets are set via wrangler secret put
|
|
388
|
-
# wrangler secret put JWT_SECRET
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
## Local Development
|
|
392
|
-
|
|
393
|
-
**.dev.vars** (for local secrets):
|
|
394
|
-
```
|
|
395
|
-
JWT_SECRET=dev-secret-key
|
|
396
|
-
API_KEY=dev-api-key
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
```bash
|
|
400
|
-
# Run with local D1
|
|
401
|
-
wrangler d1 create my-app-db --local
|
|
402
|
-
|
|
403
|
-
# Run with local KV
|
|
404
|
-
wrangler kv:namespace create KV --local
|
|
405
|
-
|
|
406
|
-
# Start dev server
|
|
407
|
-
wrangler dev
|
|
408
|
-
```
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hono-patterns
|
|
3
|
-
description: This skill activates when writing Hono routes, handlers, middleware, or discussing Hono application architecture. It provides patterns for routing, middleware composition, error handling, and TypeScript integration.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Hono Patterns
|
|
7
|
-
|
|
8
|
-
Core patterns for building Hono applications.
|
|
9
|
-
|
|
10
|
-
## Routing Patterns
|
|
11
|
-
|
|
12
|
-
### Modular Route Organization
|
|
13
|
-
|
|
14
|
-
Organize routes in separate files and compose with `app.route()`:
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
// routes/users.ts
|
|
18
|
-
import { Hono } from 'hono'
|
|
19
|
-
import type { Env } from '../types/bindings'
|
|
20
|
-
|
|
21
|
-
const users = new Hono<Env>()
|
|
22
|
-
|
|
23
|
-
users.get('/', (c) => c.json({ users: [] }))
|
|
24
|
-
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))
|
|
25
|
-
users.post('/', (c) => c.json({ created: true }, 201))
|
|
26
|
-
|
|
27
|
-
export { users }
|
|
28
|
-
|
|
29
|
-
// index.ts
|
|
30
|
-
import { users } from './routes/users'
|
|
31
|
-
import { posts } from './routes/posts'
|
|
32
|
-
|
|
33
|
-
app.route('/api/users', users)
|
|
34
|
-
app.route('/api/posts', posts)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Route Groups with Shared Middleware
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
const api = new Hono<Env>()
|
|
41
|
-
|
|
42
|
-
// Apply auth to all /api routes
|
|
43
|
-
api.use('*', authMiddleware)
|
|
44
|
-
|
|
45
|
-
api.route('/users', users)
|
|
46
|
-
api.route('/posts', posts)
|
|
47
|
-
|
|
48
|
-
app.route('/api', api)
|
|
49
|
-
|
|
50
|
-
// Public routes remain unprotected
|
|
51
|
-
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Chained Route Definition (for RPC)
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
// Chain routes for proper type inference
|
|
58
|
-
const routes = app
|
|
59
|
-
.get('/users', (c) => c.json({ users: [] }))
|
|
60
|
-
.post('/users', (c) => c.json({ created: true }, 201))
|
|
61
|
-
.get('/users/:id', (c) => c.json({ id: c.req.param('id') }))
|
|
62
|
-
|
|
63
|
-
export type AppType = typeof routes
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Handler Patterns
|
|
67
|
-
|
|
68
|
-
### Basic Handler
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
app.get('/users', async (c) => {
|
|
72
|
-
const users = await fetchUsers()
|
|
73
|
-
return c.json(users)
|
|
74
|
-
})
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Handler with Validation
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import { zValidator } from '@hono/zod-validator'
|
|
81
|
-
import { z } from 'zod'
|
|
82
|
-
|
|
83
|
-
app.post('/users',
|
|
84
|
-
zValidator('json', z.object({
|
|
85
|
-
email: z.string().email(),
|
|
86
|
-
name: z.string().min(1)
|
|
87
|
-
})),
|
|
88
|
-
async (c) => {
|
|
89
|
-
const data = c.req.valid('json')
|
|
90
|
-
return c.json({ id: crypto.randomUUID(), ...data }, 201)
|
|
91
|
-
}
|
|
92
|
-
)
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Factory Pattern for External Handlers
|
|
96
|
-
|
|
97
|
-
Use when handlers need to be defined outside route files:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import { createFactory } from 'hono/factory'
|
|
101
|
-
import type { Env } from '../types/bindings'
|
|
102
|
-
|
|
103
|
-
const factory = createFactory<Env>()
|
|
104
|
-
|
|
105
|
-
// Define handler with proper types
|
|
106
|
-
export const listUsers = factory.createHandlers(
|
|
107
|
-
zValidator('query', paginationSchema),
|
|
108
|
-
async (c) => {
|
|
109
|
-
const { page, limit } = c.req.valid('query')
|
|
110
|
-
return c.json({ users: [], page, limit })
|
|
111
|
-
}
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
// Use in routes
|
|
115
|
-
users.get('/', ...listUsers)
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## Middleware Patterns
|
|
119
|
-
|
|
120
|
-
### Creating Typed Middleware
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import { createMiddleware } from 'hono/factory'
|
|
124
|
-
import type { Env } from '../types/bindings'
|
|
125
|
-
|
|
126
|
-
export const loggerMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
127
|
-
const start = Date.now()
|
|
128
|
-
await next()
|
|
129
|
-
console.log(`${c.req.method} ${c.req.url} - ${Date.now() - start}ms`)
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Middleware with Variables
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
// Update types/bindings.ts
|
|
137
|
-
type Env = {
|
|
138
|
-
Variables: {
|
|
139
|
-
user: { id: string; email: string }
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Middleware sets variable
|
|
144
|
-
const authMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
145
|
-
const user = await validateToken(c.req.header('Authorization'))
|
|
146
|
-
c.set('user', user) // Type-safe!
|
|
147
|
-
await next()
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
// Handler accesses variable
|
|
151
|
-
app.get('/profile', authMiddleware, (c) => {
|
|
152
|
-
const user = c.get('user') // Type-safe!
|
|
153
|
-
return c.json(user)
|
|
154
|
-
})
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Configurable Middleware Factory
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
interface CacheOptions {
|
|
161
|
-
maxAge: number
|
|
162
|
-
public?: boolean
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export const cache = (options: CacheOptions) => {
|
|
166
|
-
return createMiddleware<Env>(async (c, next) => {
|
|
167
|
-
await next()
|
|
168
|
-
|
|
169
|
-
const directive = options.public ? 'public' : 'private'
|
|
170
|
-
c.header('Cache-Control', `${directive}, max-age=${options.maxAge}`)
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Usage
|
|
175
|
-
app.get('/static/*', cache({ maxAge: 86400, public: true }))
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Error Handling
|
|
179
|
-
|
|
180
|
-
### HTTPException
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
import { HTTPException } from 'hono/http-exception'
|
|
184
|
-
|
|
185
|
-
app.get('/users/:id', async (c) => {
|
|
186
|
-
const user = await getUser(c.req.param('id'))
|
|
187
|
-
|
|
188
|
-
if (!user) {
|
|
189
|
-
throw new HTTPException(404, { message: 'User not found' })
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return c.json(user)
|
|
193
|
-
})
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Global Error Handler
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
app.onError((err, c) => {
|
|
200
|
-
if (err instanceof HTTPException) {
|
|
201
|
-
return c.json({ error: err.message }, err.status)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
console.error(err)
|
|
205
|
-
return c.json({ error: 'Internal server error' }, 500)
|
|
206
|
-
})
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Custom Error Classes
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
class NotFoundError extends HTTPException {
|
|
213
|
-
constructor(resource: string) {
|
|
214
|
-
super(404, { message: `${resource} not found` })
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
class ValidationError extends HTTPException {
|
|
219
|
-
constructor(errors: Record<string, string>) {
|
|
220
|
-
super(400, { message: 'Validation failed', cause: errors })
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## Response Patterns
|
|
226
|
-
|
|
227
|
-
### JSON Responses
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
// Success
|
|
231
|
-
c.json({ data: users })
|
|
232
|
-
c.json({ data: user }, 200)
|
|
233
|
-
c.json({ data: newUser }, 201)
|
|
234
|
-
|
|
235
|
-
// Errors
|
|
236
|
-
c.json({ error: 'Not found' }, 404)
|
|
237
|
-
c.json({ error: 'Validation failed', details: errors }, 400)
|
|
238
|
-
|
|
239
|
-
// No content
|
|
240
|
-
c.body(null, 204)
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### Consistent Response Format
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
interface ApiResponse<T> {
|
|
247
|
-
data?: T
|
|
248
|
-
error?: string
|
|
249
|
-
meta?: {
|
|
250
|
-
page?: number
|
|
251
|
-
limit?: number
|
|
252
|
-
total?: number
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Helper
|
|
257
|
-
function success<T>(c: Context, data: T, status = 200) {
|
|
258
|
-
return c.json({ data } as ApiResponse<T>, status)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function error(c: Context, message: string, status = 400) {
|
|
262
|
-
return c.json({ error: message } as ApiResponse<never>, status)
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Context Access
|
|
267
|
-
|
|
268
|
-
### Request Data
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
// Path params
|
|
272
|
-
const id = c.req.param('id')
|
|
273
|
-
const { id, slug } = c.req.param()
|
|
274
|
-
|
|
275
|
-
// Query params
|
|
276
|
-
const page = c.req.query('page')
|
|
277
|
-
const { page, limit } = c.req.query()
|
|
278
|
-
|
|
279
|
-
// Headers
|
|
280
|
-
const auth = c.req.header('Authorization')
|
|
281
|
-
|
|
282
|
-
// Body (validated)
|
|
283
|
-
const data = c.req.valid('json')
|
|
284
|
-
const form = c.req.valid('form')
|
|
285
|
-
const query = c.req.valid('query')
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Environment and Bindings
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
// Environment variables
|
|
292
|
-
const secret = c.env.JWT_SECRET
|
|
293
|
-
|
|
294
|
-
// Cloudflare bindings
|
|
295
|
-
const db = c.env.DB
|
|
296
|
-
const kv = c.env.KV
|
|
297
|
-
const bucket = c.env.BUCKET
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
## Best Practices
|
|
301
|
-
|
|
302
|
-
1. **Always type your app**: `new Hono<Env>()`
|
|
303
|
-
2. **Validate all inputs** with Zod
|
|
304
|
-
3. **Use `createMiddleware`** for type-safe middleware
|
|
305
|
-
4. **Export `AppType`** for RPC client
|
|
306
|
-
5. **Handle errors** with HTTPException
|
|
307
|
-
6. **Use proper status codes**: 200, 201, 204, 400, 401, 404, 500
|
|
308
|
-
7. **Organize routes** in separate files
|
|
309
|
-
8. **Keep handlers small** - delegate to services
|