@smicolon/ai-kit 0.3.2 → 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 -369
- 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/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,346 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hono-tester
|
|
3
|
-
description: Testing specialist for Hono applications using Bun test and Vitest, covering unit tests, integration tests, and API tests.
|
|
4
|
-
model: inherit
|
|
5
|
-
skills:
|
|
6
|
-
- hono-patterns
|
|
7
|
-
- zod-validation
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Hono Tester
|
|
11
|
-
|
|
12
|
-
You are a testing specialist for Hono applications.
|
|
13
|
-
|
|
14
|
-
## Current Task
|
|
15
|
-
Write comprehensive tests for the specified Hono code.
|
|
16
|
-
|
|
17
|
-
## Testing Stack
|
|
18
|
-
- **Bun Projects**: `bun:test` (built-in)
|
|
19
|
-
- **CF Workers**: Vitest + `@cloudflare/vitest-pool-workers`
|
|
20
|
-
- **HTTP Testing**: Hono's built-in `app.request()`
|
|
21
|
-
- **Assertions**: Built-in matchers
|
|
22
|
-
|
|
23
|
-
## Testing Patterns
|
|
24
|
-
|
|
25
|
-
### Basic Route Testing (Bun)
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// tests/users.test.ts
|
|
29
|
-
import { describe, it, expect, beforeEach } from 'bun:test'
|
|
30
|
-
import app from '../src/index'
|
|
31
|
-
|
|
32
|
-
describe('Users API', () => {
|
|
33
|
-
describe('GET /api/users', () => {
|
|
34
|
-
it('returns list of users', async () => {
|
|
35
|
-
const res = await app.request('/api/users')
|
|
36
|
-
|
|
37
|
-
expect(res.status).toBe(200)
|
|
38
|
-
|
|
39
|
-
const data = await res.json()
|
|
40
|
-
expect(data).toHaveProperty('data')
|
|
41
|
-
expect(Array.isArray(data.data)).toBe(true)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('supports pagination', async () => {
|
|
45
|
-
const res = await app.request('/api/users?page=2&limit=10')
|
|
46
|
-
|
|
47
|
-
expect(res.status).toBe(200)
|
|
48
|
-
|
|
49
|
-
const data = await res.json()
|
|
50
|
-
expect(data.meta.page).toBe(2)
|
|
51
|
-
expect(data.meta.limit).toBe(10)
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe('POST /api/users', () => {
|
|
56
|
-
it('creates a new user', async () => {
|
|
57
|
-
const res = await app.request('/api/users', {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: { 'Content-Type': 'application/json' },
|
|
60
|
-
body: JSON.stringify({
|
|
61
|
-
email: 'test@example.com',
|
|
62
|
-
name: 'Test User'
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
expect(res.status).toBe(201)
|
|
67
|
-
|
|
68
|
-
const data = await res.json()
|
|
69
|
-
expect(data.email).toBe('test@example.com')
|
|
70
|
-
expect(data.name).toBe('Test User')
|
|
71
|
-
expect(data.id).toBeDefined()
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('returns 400 for invalid email', async () => {
|
|
75
|
-
const res = await app.request('/api/users', {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
headers: { 'Content-Type': 'application/json' },
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
email: 'invalid-email',
|
|
80
|
-
name: 'Test User'
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
expect(res.status).toBe(400)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('returns 400 for missing required fields', async () => {
|
|
88
|
-
const res = await app.request('/api/users', {
|
|
89
|
-
method: 'POST',
|
|
90
|
-
headers: { 'Content-Type': 'application/json' },
|
|
91
|
-
body: JSON.stringify({})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
expect(res.status).toBe(400)
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
describe('GET /api/users/:id', () => {
|
|
99
|
-
it('returns user by id', async () => {
|
|
100
|
-
const userId = 'valid-uuid-here'
|
|
101
|
-
const res = await app.request(`/api/users/${userId}`)
|
|
102
|
-
|
|
103
|
-
expect(res.status).toBe(200)
|
|
104
|
-
|
|
105
|
-
const data = await res.json()
|
|
106
|
-
expect(data.id).toBe(userId)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('returns 404 for non-existent user', async () => {
|
|
110
|
-
const res = await app.request('/api/users/non-existent-id')
|
|
111
|
-
|
|
112
|
-
expect(res.status).toBe(404)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('returns 400 for invalid uuid format', async () => {
|
|
116
|
-
const res = await app.request('/api/users/invalid-uuid')
|
|
117
|
-
|
|
118
|
-
expect(res.status).toBe(400)
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Testing with Mocked Bindings
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
// tests/with-bindings.test.ts
|
|
128
|
-
import { describe, it, expect, mock } from 'bun:test'
|
|
129
|
-
import { Hono } from 'hono'
|
|
130
|
-
import type { Env } from '../src/types/bindings'
|
|
131
|
-
import { users } from '../src/routes/users'
|
|
132
|
-
|
|
133
|
-
describe('Users with D1', () => {
|
|
134
|
-
const mockDB = {
|
|
135
|
-
prepare: mock(() => ({
|
|
136
|
-
bind: mock(() => ({
|
|
137
|
-
all: mock(() => Promise.resolve({
|
|
138
|
-
results: [
|
|
139
|
-
{ id: '1', email: 'user1@test.com', name: 'User 1' },
|
|
140
|
-
{ id: '2', email: 'user2@test.com', name: 'User 2' }
|
|
141
|
-
]
|
|
142
|
-
})),
|
|
143
|
-
first: mock(() => Promise.resolve({
|
|
144
|
-
id: '1', email: 'user1@test.com', name: 'User 1'
|
|
145
|
-
})),
|
|
146
|
-
run: mock(() => Promise.resolve({ meta: { last_row_id: 1 } }))
|
|
147
|
-
}))
|
|
148
|
-
}))
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const app = new Hono<Env>()
|
|
152
|
-
app.route('/users', users)
|
|
153
|
-
|
|
154
|
-
it('lists users from D1', async () => {
|
|
155
|
-
const res = await app.request('/users', {}, {
|
|
156
|
-
DB: mockDB as unknown as D1Database
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
expect(res.status).toBe(200)
|
|
160
|
-
|
|
161
|
-
const data = await res.json()
|
|
162
|
-
expect(data.data).toHaveLength(2)
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Testing Middleware
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// tests/middleware.test.ts
|
|
171
|
-
import { describe, it, expect } from 'bun:test'
|
|
172
|
-
import { Hono } from 'hono'
|
|
173
|
-
import { authMiddleware } from '../src/middleware/auth'
|
|
174
|
-
import type { Env } from '../src/types/bindings'
|
|
175
|
-
|
|
176
|
-
describe('Auth Middleware', () => {
|
|
177
|
-
const app = new Hono<Env>()
|
|
178
|
-
|
|
179
|
-
app.use('/protected/*', authMiddleware)
|
|
180
|
-
app.get('/protected/resource', (c) => c.json({ message: 'success' }))
|
|
181
|
-
|
|
182
|
-
it('returns 401 without token', async () => {
|
|
183
|
-
const res = await app.request('/protected/resource')
|
|
184
|
-
|
|
185
|
-
expect(res.status).toBe(401)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('returns 401 with invalid token', async () => {
|
|
189
|
-
const res = await app.request('/protected/resource', {
|
|
190
|
-
headers: { Authorization: 'Bearer invalid-token' }
|
|
191
|
-
}, {
|
|
192
|
-
JWT_SECRET: 'test-secret'
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
expect(res.status).toBe(401)
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
it('allows access with valid token', async () => {
|
|
199
|
-
// Generate valid JWT for test
|
|
200
|
-
const token = await generateTestToken()
|
|
201
|
-
|
|
202
|
-
const res = await app.request('/protected/resource', {
|
|
203
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
204
|
-
}, {
|
|
205
|
-
JWT_SECRET: 'test-secret'
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
expect(res.status).toBe(200)
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Testing with Vitest (Cloudflare Workers)
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
// tests/workers.test.ts
|
|
217
|
-
import { describe, it, expect, beforeAll } from 'vitest'
|
|
218
|
-
import { unstable_dev } from 'wrangler'
|
|
219
|
-
import type { UnstableDevWorker } from 'wrangler'
|
|
220
|
-
|
|
221
|
-
describe('Worker', () => {
|
|
222
|
-
let worker: UnstableDevWorker
|
|
223
|
-
|
|
224
|
-
beforeAll(async () => {
|
|
225
|
-
worker = await unstable_dev('src/index.ts', {
|
|
226
|
-
experimental: { disableExperimentalWarning: true }
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
afterAll(async () => {
|
|
231
|
-
await worker.stop()
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it('responds to health check', async () => {
|
|
235
|
-
const res = await worker.fetch('/health')
|
|
236
|
-
|
|
237
|
-
expect(res.status).toBe(200)
|
|
238
|
-
|
|
239
|
-
const data = await res.json()
|
|
240
|
-
expect(data.status).toBe('ok')
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Testing Zod Schemas
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
// tests/validators.test.ts
|
|
249
|
-
import { describe, it, expect } from 'bun:test'
|
|
250
|
-
import { createUserSchema, userQuerySchema } from '../src/validators/user.schema'
|
|
251
|
-
|
|
252
|
-
describe('User Schemas', () => {
|
|
253
|
-
describe('createUserSchema', () => {
|
|
254
|
-
it('validates valid user data', () => {
|
|
255
|
-
const result = createUserSchema.safeParse({
|
|
256
|
-
email: 'test@example.com',
|
|
257
|
-
name: 'Test User'
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
expect(result.success).toBe(true)
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
it('rejects invalid email', () => {
|
|
264
|
-
const result = createUserSchema.safeParse({
|
|
265
|
-
email: 'not-an-email',
|
|
266
|
-
name: 'Test User'
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
expect(result.success).toBe(false)
|
|
270
|
-
expect(result.error?.issues[0].path).toContain('email')
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('rejects empty name', () => {
|
|
274
|
-
const result = createUserSchema.safeParse({
|
|
275
|
-
email: 'test@example.com',
|
|
276
|
-
name: ''
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
expect(result.success).toBe(false)
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
it('applies default role', () => {
|
|
283
|
-
const result = createUserSchema.parse({
|
|
284
|
-
email: 'test@example.com',
|
|
285
|
-
name: 'Test User'
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
expect(result.role).toBe('user')
|
|
289
|
-
})
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
describe('userQuerySchema', () => {
|
|
293
|
-
it('coerces string numbers', () => {
|
|
294
|
-
const result = userQuerySchema.parse({
|
|
295
|
-
page: '2',
|
|
296
|
-
limit: '50'
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
expect(result.page).toBe(2)
|
|
300
|
-
expect(result.limit).toBe(50)
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
it('applies defaults', () => {
|
|
304
|
-
const result = userQuerySchema.parse({})
|
|
305
|
-
|
|
306
|
-
expect(result.page).toBe(1)
|
|
307
|
-
expect(result.limit).toBe(20)
|
|
308
|
-
})
|
|
309
|
-
})
|
|
310
|
-
})
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Test Coverage Goals
|
|
314
|
-
|
|
315
|
-
- **Unit Tests**: 90%+ coverage on validators and utilities
|
|
316
|
-
- **Integration Tests**: All routes tested with happy path + errors
|
|
317
|
-
- **Edge Cases**: Invalid inputs, missing data, auth failures
|
|
318
|
-
|
|
319
|
-
## Testing Commands
|
|
320
|
-
|
|
321
|
-
```bash
|
|
322
|
-
# Run all tests (Bun)
|
|
323
|
-
bun test
|
|
324
|
-
|
|
325
|
-
# Run with coverage
|
|
326
|
-
bun test --coverage
|
|
327
|
-
|
|
328
|
-
# Run specific test file
|
|
329
|
-
bun test tests/users.test.ts
|
|
330
|
-
|
|
331
|
-
# Watch mode
|
|
332
|
-
bun test --watch
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## Quality Checklist
|
|
336
|
-
|
|
337
|
-
- [ ] All routes have tests
|
|
338
|
-
- [ ] Happy paths covered
|
|
339
|
-
- [ ] Error cases covered
|
|
340
|
-
- [ ] Validation tested
|
|
341
|
-
- [ ] Middleware tested
|
|
342
|
-
- [ ] Edge cases handled
|
|
343
|
-
- [ ] Mocks properly typed
|
|
344
|
-
- [ ] No flaky tests
|
|
345
|
-
|
|
346
|
-
Now write tests for the specified code.
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: middleware-create
|
|
3
|
-
description: Create custom Hono middleware with proper typing
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Create Hono Middleware
|
|
7
|
-
|
|
8
|
-
Create a new custom middleware following Hono best practices.
|
|
9
|
-
|
|
10
|
-
## Workflow
|
|
11
|
-
|
|
12
|
-
### Step 1: Gather Requirements
|
|
13
|
-
|
|
14
|
-
Ask the user:
|
|
15
|
-
1. **Middleware name** (e.g., "auth", "rateLimit", "logger")
|
|
16
|
-
2. **Purpose** - What should this middleware do?
|
|
17
|
-
3. **Configuration options** - Any parameters needed?
|
|
18
|
-
4. **Variables to set** - What data to pass to handlers?
|
|
19
|
-
5. **When to apply** - Which routes need this?
|
|
20
|
-
|
|
21
|
-
## Middleware Patterns
|
|
22
|
-
|
|
23
|
-
### Basic Middleware
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// middleware/{name}.ts
|
|
27
|
-
import { createMiddleware } from 'hono/factory'
|
|
28
|
-
import type { Env } from '../types/bindings'
|
|
29
|
-
|
|
30
|
-
export const {name}Middleware = createMiddleware<Env>(async (c, next) => {
|
|
31
|
-
// Pre-handler logic
|
|
32
|
-
console.log(`[${c.req.method}] ${c.req.url}`)
|
|
33
|
-
|
|
34
|
-
await next()
|
|
35
|
-
|
|
36
|
-
// Post-handler logic (runs after handler)
|
|
37
|
-
console.log(`Response status: ${c.res.status}`)
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Middleware with Variables
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
// middleware/auth.ts
|
|
45
|
-
import { createMiddleware } from 'hono/factory'
|
|
46
|
-
import { HTTPException } from 'hono/http-exception'
|
|
47
|
-
import { verify } from 'hono/jwt'
|
|
48
|
-
import type { Env } from '../types/bindings'
|
|
49
|
-
|
|
50
|
-
// First, update types/bindings.ts to include the variable:
|
|
51
|
-
// Variables: {
|
|
52
|
-
// user: { id: string; email: string; role: string }
|
|
53
|
-
// }
|
|
54
|
-
|
|
55
|
-
export const authMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
56
|
-
const authHeader = c.req.header('Authorization')
|
|
57
|
-
|
|
58
|
-
if (!authHeader?.startsWith('Bearer ')) {
|
|
59
|
-
throw new HTTPException(401, { message: 'Missing authorization token' })
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const token = authHeader.slice(7)
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const payload = await verify(token, c.env.JWT_SECRET)
|
|
66
|
-
|
|
67
|
-
// Set variable for downstream handlers
|
|
68
|
-
c.set('user', {
|
|
69
|
-
id: payload.sub as string,
|
|
70
|
-
email: payload.email as string,
|
|
71
|
-
role: payload.role as string
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
await next()
|
|
75
|
-
} catch {
|
|
76
|
-
throw new HTTPException(401, { message: 'Invalid or expired token' })
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Configurable Middleware Factory
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// middleware/rateLimit.ts
|
|
85
|
-
import { createMiddleware } from 'hono/factory'
|
|
86
|
-
import { HTTPException } from 'hono/http-exception'
|
|
87
|
-
import type { Env } from '../types/bindings'
|
|
88
|
-
|
|
89
|
-
interface RateLimitOptions {
|
|
90
|
-
windowMs: number // Time window in milliseconds
|
|
91
|
-
max: number // Max requests per window
|
|
92
|
-
keyGenerator?: (c: Context) => string
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const rateLimit = (options: RateLimitOptions) => {
|
|
96
|
-
const { windowMs, max, keyGenerator } = options
|
|
97
|
-
|
|
98
|
-
return createMiddleware<Env>(async (c, next) => {
|
|
99
|
-
const key = keyGenerator
|
|
100
|
-
? keyGenerator(c)
|
|
101
|
-
: c.req.header('CF-Connecting-IP') || 'unknown'
|
|
102
|
-
|
|
103
|
-
const cacheKey = `ratelimit:${key}`
|
|
104
|
-
const kv = c.env.KV
|
|
105
|
-
|
|
106
|
-
const current = parseInt(await kv.get(cacheKey) || '0')
|
|
107
|
-
|
|
108
|
-
if (current >= max) {
|
|
109
|
-
throw new HTTPException(429, {
|
|
110
|
-
message: 'Too many requests, please try again later'
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
await kv.put(cacheKey, String(current + 1), {
|
|
115
|
-
expirationTtl: Math.ceil(windowMs / 1000)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
// Add rate limit headers
|
|
119
|
-
c.header('X-RateLimit-Limit', String(max))
|
|
120
|
-
c.header('X-RateLimit-Remaining', String(max - current - 1))
|
|
121
|
-
|
|
122
|
-
await next()
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Usage:
|
|
127
|
-
// app.use('/api/*', rateLimit({ windowMs: 60000, max: 100 }))
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Request Validation Middleware
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// middleware/validateRequest.ts
|
|
134
|
-
import { createMiddleware } from 'hono/factory'
|
|
135
|
-
import type { Env } from '../types/bindings'
|
|
136
|
-
|
|
137
|
-
export const requestIdMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
138
|
-
const requestId = c.req.header('X-Request-ID') || crypto.randomUUID()
|
|
139
|
-
|
|
140
|
-
c.set('requestId', requestId)
|
|
141
|
-
c.header('X-Request-ID', requestId)
|
|
142
|
-
|
|
143
|
-
await next()
|
|
144
|
-
})
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Response Timing Middleware
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
// middleware/timing.ts
|
|
151
|
-
import { createMiddleware } from 'hono/factory'
|
|
152
|
-
import type { Env } from '../types/bindings'
|
|
153
|
-
|
|
154
|
-
export const timingMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
155
|
-
const start = Date.now()
|
|
156
|
-
|
|
157
|
-
await next()
|
|
158
|
-
|
|
159
|
-
const duration = Date.now() - start
|
|
160
|
-
c.header('X-Response-Time', `${duration}ms`)
|
|
161
|
-
})
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Error Handling Middleware
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
// middleware/errorHandler.ts
|
|
168
|
-
import { createMiddleware } from 'hono/factory'
|
|
169
|
-
import { HTTPException } from 'hono/http-exception'
|
|
170
|
-
import type { Env } from '../types/bindings'
|
|
171
|
-
|
|
172
|
-
export const errorHandler = createMiddleware<Env>(async (c, next) => {
|
|
173
|
-
try {
|
|
174
|
-
await next()
|
|
175
|
-
} catch (error) {
|
|
176
|
-
if (error instanceof HTTPException) {
|
|
177
|
-
return c.json(
|
|
178
|
-
{ error: error.message, status: error.status },
|
|
179
|
-
error.status
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.error('Unhandled error:', error)
|
|
184
|
-
return c.json(
|
|
185
|
-
{ error: 'Internal server error', status: 500 },
|
|
186
|
-
500
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## Generated Files
|
|
193
|
-
|
|
194
|
-
1. `middleware/{name}.ts` - Middleware implementation
|
|
195
|
-
2. Updated `types/bindings.ts` - Add Variables if needed
|
|
196
|
-
3. Updated `middleware/index.ts` - Barrel export
|
|
197
|
-
|
|
198
|
-
## Usage Examples
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
// Apply to all routes
|
|
202
|
-
app.use('*', timingMiddleware)
|
|
203
|
-
|
|
204
|
-
// Apply to specific path
|
|
205
|
-
app.use('/api/*', authMiddleware)
|
|
206
|
-
|
|
207
|
-
// Apply to specific routes
|
|
208
|
-
app.use('/api/admin/*', roleMiddleware('admin'))
|
|
209
|
-
|
|
210
|
-
// Chain multiple middleware
|
|
211
|
-
app.use('/api/*', timingMiddleware, requestIdMiddleware, authMiddleware)
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Quality Checklist
|
|
215
|
-
|
|
216
|
-
- [ ] Uses `createMiddleware` for type safety
|
|
217
|
-
- [ ] Properly typed with `Env`
|
|
218
|
-
- [ ] Calls `await next()` appropriately
|
|
219
|
-
- [ ] Handles errors with HTTPException
|
|
220
|
-
- [ ] Variables added to bindings type
|
|
221
|
-
- [ ] Exported from barrel file
|
|
222
|
-
|
|
223
|
-
Now ask the user what middleware they want to create!
|