@kood/claude-code 0.2.5 → 0.3.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/dist/index.js +13 -8
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +371 -19
- package/templates/.claude/agents/dependency-manager.md +197 -0
- package/templates/.claude/agents/deployment-validator.md +136 -0
- package/templates/.claude/agents/git-operator.md +147 -0
- package/templates/.claude/agents/implementation-executor.md +202 -0
- package/templates/.claude/agents/lint-fixer.md +155 -0
- package/templates/.claude/agents/refactor-advisor.md +339 -29
- package/templates/.claude/commands/agent-creator.md +355 -0
- package/templates/.claude/commands/docs-creator.md +404 -163
- package/templates/.claude/commands/docs-refactor.md +400 -113
- package/templates/.claude/commands/execute.md +357 -185
- package/templates/.claude/commands/git-all.md +16 -70
- package/templates/.claude/commands/git-session.md +36 -68
- package/templates/.claude/commands/git.md +20 -69
- package/templates/.claude/commands/lint-fix.md +164 -107
- package/templates/.claude/commands/lint-init.md +142 -168
- package/templates/.claude/commands/plan.md +300 -84
- package/templates/.claude/commands/prd.md +497 -214
- package/templates/.claude/commands/pre-deploy.md +242 -0
- package/templates/.claude/commands/subagent-creator.md +118 -0
- package/templates/.claude/commands/version-update.md +45 -57
- package/templates/hono/CLAUDE.md +99 -54
- package/templates/hono/docs/guides/conventions.md +352 -0
- package/templates/hono/docs/guides/env-setup.md +347 -0
- package/templates/hono/docs/guides/getting-started.md +239 -0
- package/templates/hono/docs/library/hono/error-handling.md +20 -29
- package/templates/hono/docs/library/hono/index.md +25 -52
- package/templates/hono/docs/library/hono/middleware.md +16 -75
- package/templates/hono/docs/library/hono/rpc.md +7 -35
- package/templates/hono/docs/library/hono/validation.md +25 -45
- package/templates/hono/docs/library/t3-env/index.md +374 -0
- package/templates/npx/CLAUDE.md +165 -65
- package/templates/npx/docs/library/commander/index.md +10 -73
- package/templates/npx/docs/library/fs-extra/index.md +21 -113
- package/templates/npx/docs/library/prompts/index.md +30 -176
- package/templates/npx/docs/references/patterns.md +75 -48
- package/templates/tanstack-start/CLAUDE.md +101 -77
- package/templates/tanstack-start/docs/architecture.md +427 -0
- package/templates/tanstack-start/docs/design.md +558 -0
- package/templates/tanstack-start/docs/guides/conventions.md +132 -32
- package/templates/tanstack-start/docs/guides/env-setup.md +127 -62
- package/templates/tanstack-start/docs/guides/getting-started.md +81 -20
- package/templates/tanstack-start/docs/guides/hooks.md +241 -36
- package/templates/tanstack-start/docs/guides/routes.md +213 -61
- package/templates/tanstack-start/docs/guides/services.md +260 -24
- package/templates/tanstack-start/docs/library/better-auth/index.md +469 -16
- package/templates/tanstack-start/docs/library/t3-env/index.md +307 -0
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -21
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +22 -35
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +7 -24
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +26 -39
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +23 -26
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +32 -147
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +25 -167
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +39 -74
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +46 -116
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +35 -154
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +32 -171
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +7 -15
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +16 -23
- package/templates/tanstack-start/docs/library/zod/complex-types.md +12 -31
- package/templates/tanstack-start/docs/library/zod/index.md +18 -35
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -25
- package/templates/tanstack-start/docs/library/zod/validation.md +12 -34
- package/templates/.claude/agents/debug-detective.md +0 -37
- package/templates/.claude/agents/test-writer.md +0 -41
- package/templates/.claude/commands/feedback.md +0 -199
- package/templates/.claude/commands/ts-fix.md +0 -176
- package/templates/.claude/skills/command-creator/LICENSE.txt +0 -202
- package/templates/.claude/skills/command-creator/SKILL.md +0 -245
- package/templates/.claude/skills/command-creator/scripts/init_command.py +0 -244
- package/templates/.claude/skills/command-creator/scripts/package_command.py +0 -125
- package/templates/.claude/skills/command-creator/scripts/quick_validate.py +0 -143
- package/templates/.claude/skills/frontend-design/SKILL.md +0 -310
- package/templates/.claude/skills/frontend-design/references/animation-patterns.md +0 -446
- package/templates/.claude/skills/frontend-design/references/colors-2026.md +0 -244
- package/templates/.claude/skills/frontend-design/references/typography-2026.md +0 -302
- package/templates/.claude/skills/gemini-review/SKILL.md +0 -118
- package/templates/.claude/skills/gemini-review/references/checklists.md +0 -129
- package/templates/.claude/skills/gemini-review/references/prompt-templates.md +0 -274
- package/templates/.claude/skills/skill-creator/LICENSE.txt +0 -202
- package/templates/.claude/skills/skill-creator/SKILL.md +0 -184
- package/templates/.claude/skills/skill-creator/scripts/init_skill.py +0 -303
- package/templates/.claude/skills/skill-creator/scripts/package_skill.py +0 -110
- package/templates/.claude/skills/skill-creator/scripts/quick_validate.py +0 -65
- package/templates/hono/docs/library/ai-sdk/index.md +0 -190
- package/templates/hono/docs/library/ai-sdk/openrouter.md +0 -111
- package/templates/hono/docs/library/ai-sdk/providers.md +0 -102
- package/templates/hono/docs/library/ai-sdk/streaming.md +0 -146
- package/templates/hono/docs/library/ai-sdk/structured-output.md +0 -161
- package/templates/hono/docs/library/ai-sdk/tools.md +0 -144
- package/templates/hono/docs/library/drizzle/cloudflare-d1.md +0 -247
- package/templates/hono/docs/library/drizzle/config.md +0 -167
- package/templates/hono/docs/library/drizzle/index.md +0 -259
- package/templates/hono/docs/library/hono/env-setup.md +0 -169
- package/templates/hono/docs/library/pino/index.md +0 -146
- package/templates/tanstack-start/docs/architecture/architecture.md +0 -243
- package/templates/tanstack-start/docs/deployment/cloudflare.md +0 -132
- package/templates/tanstack-start/docs/deployment/index.md +0 -163
- package/templates/tanstack-start/docs/deployment/nitro.md +0 -110
- package/templates/tanstack-start/docs/deployment/railway.md +0 -147
- package/templates/tanstack-start/docs/deployment/vercel.md +0 -135
- package/templates/tanstack-start/docs/design/components.md +0 -175
- package/templates/tanstack-start/docs/design/index.md +0 -151
- package/templates/tanstack-start/docs/design/safe-area.md +0 -118
- package/templates/tanstack-start/docs/design/tailwind-setup.md +0 -156
- package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +0 -472
- package/templates/tanstack-start/docs/library/ai-sdk/index.md +0 -264
- package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +0 -371
- package/templates/tanstack-start/docs/library/ai-sdk/providers.md +0 -403
- package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +0 -320
- package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +0 -454
- package/templates/tanstack-start/docs/library/ai-sdk/tools.md +0 -473
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +0 -48
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +0 -55
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +0 -34
- package/templates/tanstack-start/docs/library/better-auth/session.md +0 -47
- package/templates/tanstack-start/docs/library/better-auth/setup.md +0 -41
- package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +0 -147
- package/templates/tanstack-start/docs/library/drizzle/config.md +0 -118
- package/templates/tanstack-start/docs/library/drizzle/crud.md +0 -205
- package/templates/tanstack-start/docs/library/drizzle/index.md +0 -79
- package/templates/tanstack-start/docs/library/drizzle/relations.md +0 -202
- package/templates/tanstack-start/docs/library/drizzle/schema.md +0 -154
- package/templates/tanstack-start/docs/library/drizzle/setup.md +0 -96
- package/templates/tanstack-start/docs/library/drizzle/transactions.md +0 -127
- package/templates/tanstack-start/docs/library/pino/index.md +0 -320
- /package/templates/hono/docs/{architecture/architecture.md → architecture.md} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TanStack Start - 인증 패턴
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<patterns>
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
6
|
// 로그인
|
|
@@ -9,7 +9,6 @@ export const loginFn = createServerFn({ method: 'POST' })
|
|
|
9
9
|
.handler(async ({ data }) => {
|
|
10
10
|
const user = await authenticateUser(data.email, data.password)
|
|
11
11
|
if (!user) return { error: 'Invalid credentials' }
|
|
12
|
-
|
|
13
12
|
const session = await useAppSession()
|
|
14
13
|
await session.update({ userId: user.id, email: user.email })
|
|
15
14
|
throw redirect({ to: '/dashboard' })
|
|
@@ -30,11 +29,8 @@ export const getCurrentUserFn = createServerFn({ method: 'GET' })
|
|
|
30
29
|
if (!session.data.userId) return null
|
|
31
30
|
return getUserById(session.data.userId)
|
|
32
31
|
})
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## 인증 미들웨어
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
// 인증 미들웨어
|
|
38
34
|
export const authMiddleware = createMiddleware({ type: 'function' })
|
|
39
35
|
.server(async ({ next }) => {
|
|
40
36
|
const session = await useAppSession()
|
|
@@ -43,15 +39,12 @@ export const authMiddleware = createMiddleware({ type: 'function' })
|
|
|
43
39
|
return next({ context: { user } })
|
|
44
40
|
})
|
|
45
41
|
|
|
46
|
-
//
|
|
42
|
+
// Server Function에 적용
|
|
47
43
|
export const protectedFn = createServerFn({ method: 'GET' })
|
|
48
44
|
.middleware([authMiddleware])
|
|
49
45
|
.handler(async ({ context }) => ({ user: context.user }))
|
|
50
|
-
```
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
47
|
+
// 보호된 라우트
|
|
55
48
|
export const Route = createFileRoute('/dashboard')({
|
|
56
49
|
beforeLoad: async () => {
|
|
57
50
|
const user = await getCurrentUserFn()
|
|
@@ -63,11 +56,8 @@ export const Route = createFileRoute('/dashboard')({
|
|
|
63
56
|
return <h1>Welcome, {user.name}!</h1>
|
|
64
57
|
},
|
|
65
58
|
})
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Better Auth 통합
|
|
69
59
|
|
|
70
|
-
|
|
60
|
+
// Better Auth 통합
|
|
71
61
|
export const auth = betterAuth({
|
|
72
62
|
database: prismaAdapter(prisma),
|
|
73
63
|
emailAndPassword: { enabled: true },
|
|
@@ -76,3 +66,5 @@ export const auth = betterAuth({
|
|
|
76
66
|
export const getSession = createServerFn({ method: 'GET' })
|
|
77
67
|
.handler(async ({ request }) => auth.api.getSession({ headers: request.headers }))
|
|
78
68
|
```
|
|
69
|
+
|
|
70
|
+
</patterns>
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
# TanStack Start - Routing
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 기본 패턴
|
|
3
|
+
<patterns>
|
|
6
4
|
|
|
7
5
|
```tsx
|
|
8
|
-
//
|
|
9
|
-
export const Route = createFileRoute('/about')({
|
|
10
|
-
component: AboutPage,
|
|
11
|
-
})
|
|
6
|
+
// 기본
|
|
7
|
+
export const Route = createFileRoute('/about')({ component: AboutPage })
|
|
12
8
|
|
|
13
9
|
// Loader
|
|
14
10
|
export const Route = createFileRoute('/')({
|
|
15
11
|
component: Page,
|
|
16
12
|
loader: async () => fetch('/api/posts').then(r => r.json()),
|
|
17
13
|
})
|
|
18
|
-
|
|
19
|
-
function Page() {
|
|
14
|
+
const Page = () => {
|
|
20
15
|
const posts = Route.useLoaderData()
|
|
21
16
|
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
|
|
22
17
|
}
|
|
@@ -29,19 +24,13 @@ export const Route = createFileRoute('/users/$id')({
|
|
|
29
24
|
return <h1>{user.name}</h1>
|
|
30
25
|
},
|
|
31
26
|
})
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## SSR 옵션
|
|
35
27
|
|
|
36
|
-
|
|
28
|
+
// SSR 옵션
|
|
37
29
|
ssr: true // 전체 SSR (기본값)
|
|
38
30
|
ssr: false // 클라이언트만
|
|
39
|
-
ssr: 'data-only' // 데이터만
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Server Routes (API)
|
|
31
|
+
ssr: 'data-only' // 데이터만 서버
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
// Server Routes (API)
|
|
45
34
|
export const Route = createFileRoute('/api/hello')({
|
|
46
35
|
server: {
|
|
47
36
|
handlers: {
|
|
@@ -55,12 +44,16 @@ export const Route = createFileRoute('/api/hello')({
|
|
|
55
44
|
})
|
|
56
45
|
```
|
|
57
46
|
|
|
58
|
-
|
|
47
|
+
</patterns>
|
|
48
|
+
|
|
49
|
+
<structure>
|
|
59
50
|
|
|
60
51
|
```
|
|
61
52
|
routes/
|
|
62
|
-
├── __root.tsx
|
|
63
|
-
├── index.tsx
|
|
64
|
-
├── users/$id.tsx
|
|
65
|
-
├── $.tsx
|
|
53
|
+
├── __root.tsx # Root layout
|
|
54
|
+
├── index.tsx # /
|
|
55
|
+
├── users/$id.tsx # /users/:id
|
|
56
|
+
├── $.tsx # Catch-all (404)
|
|
66
57
|
```
|
|
58
|
+
|
|
59
|
+
</structure>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Zod - 복합 타입
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<patterns>
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
+
// 객체
|
|
6
7
|
const UserSchema = z.object({
|
|
7
8
|
name: z.string(),
|
|
8
9
|
email: z.email(),
|
|
9
10
|
age: z.number().optional(),
|
|
10
11
|
})
|
|
11
12
|
|
|
12
|
-
// 메서드
|
|
13
13
|
UserSchema.partial() // 모든 필드 optional
|
|
14
14
|
UserSchema.required() // 모든 필드 required
|
|
15
15
|
UserSchema.pick({ name: true }) // 특정 필드만
|
|
@@ -17,57 +17,36 @@ UserSchema.omit({ email: true }) // 특정 필드 제외
|
|
|
17
17
|
UserSchema.extend({ role: z.enum(['admin', 'user']) })
|
|
18
18
|
UserSchema.merge(AnotherSchema)
|
|
19
19
|
|
|
20
|
-
// v4
|
|
21
|
-
z.
|
|
22
|
-
z.looseObject({ name: z.string() }) // 추가 키 통과
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 배열/튜플
|
|
20
|
+
z.strictObject({ name: z.string() }) // v4: 추가 키 에러
|
|
21
|
+
z.looseObject({ name: z.string() }) // v4: 추가 키 통과
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
// 배열/튜플
|
|
28
24
|
z.array(z.string())
|
|
29
25
|
z.array(z.number()).min(1).max(10).length(5).nonempty()
|
|
30
|
-
|
|
31
26
|
z.tuple([z.string(), z.number()]) // [string, number]
|
|
32
|
-
```
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
28
|
+
// 유니온
|
|
37
29
|
z.union([z.string(), z.number()])
|
|
38
30
|
z.string().or(z.number())
|
|
39
31
|
|
|
40
|
-
// Discriminated Union
|
|
41
32
|
z.discriminatedUnion('type', [
|
|
42
33
|
z.object({ type: z.literal('circle'), radius: z.number() }),
|
|
43
34
|
z.object({ type: z.literal('rectangle'), width: z.number(), height: z.number() }),
|
|
44
35
|
])
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Enum
|
|
48
36
|
|
|
49
|
-
|
|
37
|
+
// Enum
|
|
50
38
|
const Status = z.enum(['pending', 'done', 'cancelled'])
|
|
51
39
|
type Status = z.infer<typeof Status> // 'pending' | 'done' | 'cancelled'
|
|
52
40
|
|
|
53
|
-
// Native enum
|
|
54
41
|
enum Fruits { Apple, Banana }
|
|
55
42
|
z.nativeEnum(Fruits)
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Record/Map/Set
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
z.record(z.string(), z.object({ name: z.string() }))
|
|
62
|
-
// { [key: string]: { name: string } }
|
|
63
43
|
|
|
44
|
+
// Record/Map/Set
|
|
45
|
+
z.record(z.string(), z.object({ name: z.string() })) // { [key: string]: { name: string } }
|
|
64
46
|
z.map(z.string(), z.number()) // Map<string, number>
|
|
65
47
|
z.set(z.number()) // Set<number>
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 재귀 스키마
|
|
69
48
|
|
|
70
|
-
|
|
49
|
+
// 재귀
|
|
71
50
|
type Json = string | number | boolean | null | { [key: string]: Json } | Json[]
|
|
72
51
|
|
|
73
52
|
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
|
@@ -78,3 +57,5 @@ const jsonSchema: z.ZodType<Json> = z.lazy(() =>
|
|
|
78
57
|
])
|
|
79
58
|
)
|
|
80
59
|
```
|
|
60
|
+
|
|
61
|
+
</patterns>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Zod
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> v4 | TypeScript Schema Validation
|
|
4
4
|
|
|
5
5
|
@complex-types.md
|
|
6
6
|
@transforms.md
|
|
@@ -8,66 +8,49 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<quick_reference>
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
// 기본
|
|
14
|
+
// 기본
|
|
15
15
|
const schema = z.object({
|
|
16
|
-
email: z.email(), // v4!
|
|
16
|
+
email: z.email(), // v4!
|
|
17
17
|
name: z.string().min(1).trim(),
|
|
18
|
-
website: z.url().optional(), // v4!
|
|
18
|
+
website: z.url().optional(), // v4!
|
|
19
19
|
age: z.number().int().positive(),
|
|
20
20
|
})
|
|
21
|
-
|
|
22
21
|
type Input = z.infer<typeof schema>
|
|
23
22
|
|
|
24
|
-
// 파싱
|
|
25
23
|
schema.parse(data) // 실패 시 throw
|
|
26
24
|
schema.safeParse(data) // { success, data/error }
|
|
27
25
|
|
|
28
|
-
// TanStack Start
|
|
26
|
+
// TanStack Start
|
|
29
27
|
export const createUser = createServerFn({ method: 'POST' })
|
|
30
28
|
.inputValidator(schema)
|
|
31
29
|
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
</quick_reference>
|
|
33
|
+
|
|
34
|
+
<v4_changes>
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
|
-
// ✅ v4
|
|
38
|
-
z.email()
|
|
39
|
-
z.iso.date()
|
|
37
|
+
// ✅ v4 새 API
|
|
38
|
+
z.email() z.url() z.uuid()
|
|
39
|
+
z.iso.date() z.iso.datetime() z.iso.duration()
|
|
40
|
+
z.stringbool() // "true"/"yes"/"1" → true
|
|
40
41
|
|
|
41
42
|
// ❌ v3 deprecated
|
|
42
43
|
z.string().email() z.string().url()
|
|
43
|
-
```
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
// 에러 커스텀: message → error
|
|
49
|
-
z.string().min(5, { error: "Too short." })
|
|
50
|
-
|
|
51
|
-
// Strict/Loose 객체
|
|
45
|
+
// 변경사항
|
|
46
|
+
z.string().min(5, { error: "Too short." }) // message → error
|
|
52
47
|
z.strictObject({ name: z.string() }) // 추가 키 에러
|
|
53
48
|
z.looseObject({ name: z.string() }) // 추가 키 통과
|
|
54
|
-
|
|
55
|
-
// Refinement 체이닝 가능
|
|
56
|
-
z.string().refine(val => val.includes("@")).min(5) // ✅ v4
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### v4 추가 API
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
// 문자열 불리언 (환경변수용)
|
|
63
|
-
z.stringbool() // "true"/"yes"/"1" → true
|
|
64
|
-
|
|
65
|
-
// 날짜/시간 ISO 포맷
|
|
66
|
-
z.iso.date() // 2024-01-15
|
|
67
|
-
z.iso.datetime() // ISO 날짜시간
|
|
68
|
-
z.iso.duration() // P1D, PT1H
|
|
49
|
+
z.string().refine(val => val.includes("@")).min(5) // refinement 체이닝
|
|
69
50
|
|
|
70
51
|
// 템플릿 리터럴
|
|
71
52
|
const css = z.templateLiteral([z.number(), z.enum(["px", "em", "rem"])])
|
|
72
53
|
// `${number}px` | `${number}em` | `${number}rem`
|
|
73
54
|
```
|
|
55
|
+
|
|
56
|
+
</v4_changes>
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
# Zod - 변환
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<patterns>
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
+
// Transform
|
|
6
7
|
const stringToLength = z.string().transform((val) => val.length)
|
|
7
8
|
stringToLength.parse('hello') // => 5
|
|
8
9
|
|
|
9
10
|
type In = z.input<typeof stringToLength> // string
|
|
10
11
|
type Out = z.output<typeof stringToLength> // number
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Pipe (검증 후 변환)
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
// Pipe (검증 후 변환)
|
|
16
14
|
const stringToNumber = z.string()
|
|
17
15
|
.transform((val) => parseInt(val, 10))
|
|
18
16
|
.pipe(z.number().min(0).max(100))
|
|
19
17
|
|
|
20
18
|
stringToNumber.parse('50') // => 50
|
|
21
19
|
stringToNumber.parse('150') // throws
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Coerce (강제 변환)
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
// Coerce (강제 변환)
|
|
27
22
|
z.coerce.string() // 모든 값 → 문자열
|
|
28
23
|
z.coerce.number() // 모든 값 → 숫자
|
|
29
24
|
z.coerce.boolean() // 모든 값 → 불리언
|
|
@@ -33,33 +28,24 @@ z.coerce.bigint() // 모든 값 → BigInt
|
|
|
33
28
|
z.coerce.number().parse("42") // => 42
|
|
34
29
|
z.coerce.date().parse("2021-01-01") // => Date
|
|
35
30
|
|
|
36
|
-
//
|
|
31
|
+
// v4: 입력 타입이 unknown으로 변경
|
|
37
32
|
type In = z.input<typeof z.coerce.string()> // unknown
|
|
38
|
-
```
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
// v4 환경변수 불리언
|
|
35
|
+
z.stringbool() // "true"/"yes"/"1" → true, "false"/"no"/"0" → false
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
z.stringbool() // "true"/"yes"/"1" → true
|
|
44
|
-
// "false"/"no"/"0" → false
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Preprocess
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
37
|
+
// Preprocess
|
|
50
38
|
const trimmed = z.preprocess(
|
|
51
39
|
(val) => typeof val === 'string' ? val.trim() : val,
|
|
52
40
|
z.string()
|
|
53
41
|
)
|
|
54
|
-
```
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
43
|
+
// 입력/출력 타입 분리
|
|
59
44
|
const Schema = z.object({
|
|
60
45
|
createdAt: z.string().transform((str) => new Date(str)),
|
|
61
46
|
})
|
|
62
|
-
|
|
63
47
|
type SchemaInput = z.input<typeof Schema> // { createdAt: string }
|
|
64
48
|
type SchemaOutput = z.output<typeof Schema> // { createdAt: Date }
|
|
65
49
|
```
|
|
50
|
+
|
|
51
|
+
</patterns>
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
# Zod - 검증
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<patterns>
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
// v4: message → error
|
|
6
|
+
// Refinement (v4: message → error)
|
|
7
7
|
const PasswordSchema = z.string()
|
|
8
8
|
.min(8)
|
|
9
9
|
.refine((val) => /[A-Z]/.test(val), { error: '대문자 필수' })
|
|
10
10
|
.refine((val) => /[0-9]/.test(val), { error: '숫자 필수' })
|
|
11
11
|
|
|
12
|
-
// v4: refinement 후 체이닝
|
|
13
|
-
z.string().refine(val => val.includes("@")).min(5) // ✅
|
|
12
|
+
z.string().refine(val => val.includes("@")).min(5) // v4: refinement 후 체이닝
|
|
14
13
|
|
|
15
14
|
// Async
|
|
16
15
|
const schema = z.string().refine(async (val) => val.length <= 8)
|
|
17
16
|
await schema.parseAsync('hello')
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Superrefine (고급)
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
// Superrefine
|
|
23
19
|
z.object({
|
|
24
20
|
password: z.string(),
|
|
25
21
|
confirmPassword: z.string(),
|
|
@@ -31,41 +27,25 @@ z.object({
|
|
|
31
27
|
path: ['confirmPassword'],
|
|
32
28
|
})
|
|
33
29
|
}
|
|
34
|
-
})
|
|
35
|
-
// ⚠️ v4: ctx.path 사용 불가
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## 커스텀 스키마
|
|
30
|
+
}) // v4: ctx.path 사용 불가
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
// 커스텀
|
|
41
33
|
const px = z.custom<`${number}px`>((val) =>
|
|
42
34
|
typeof val === 'string' && /^\d+px$/.test(val)
|
|
43
35
|
)
|
|
44
|
-
|
|
45
36
|
px.parse('42px') // ✅
|
|
46
37
|
px.parse('42vw') // throws
|
|
47
|
-
```
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
39
|
+
// 에러 처리
|
|
52
40
|
const result = schema.safeParse(data)
|
|
53
|
-
|
|
54
41
|
if (!result.success) {
|
|
55
42
|
result.error.errors.forEach((err) => {
|
|
56
43
|
console.log(err.path, err.message, err.code)
|
|
57
44
|
})
|
|
58
|
-
|
|
59
|
-
// 평면화
|
|
60
|
-
const flat = result.error.flatten()
|
|
61
|
-
// { fieldErrors: { email: ['Invalid email'] } }
|
|
45
|
+
const flat = result.error.flatten() // { fieldErrors: { email: ['Invalid email'] } }
|
|
62
46
|
}
|
|
63
|
-
```
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
// Server Function
|
|
48
|
+
// TanStack Start
|
|
69
49
|
export const createUser = createServerFn({ method: 'POST' })
|
|
70
50
|
.inputValidator(zodValidator(createUserSchema))
|
|
71
51
|
.handler(async ({ data }) => prisma.user.create({ data }))
|
|
@@ -81,12 +61,10 @@ const env = z.object({
|
|
|
81
61
|
const workspaceMiddleware = createMiddleware({ type: 'function' })
|
|
82
62
|
.inputValidator(zodValidator(z.object({ workspaceId: z.string() })))
|
|
83
63
|
.server(({ next, data }) => next())
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Zod Mini (v4 경량)
|
|
87
64
|
|
|
88
|
-
|
|
65
|
+
// Zod Mini (v4)
|
|
89
66
|
import * as z from "zod/mini"
|
|
90
|
-
|
|
91
67
|
z.string().check(z.minLength(5), z.maxLength(10), z.trim())
|
|
92
68
|
```
|
|
69
|
+
|
|
70
|
+
</patterns>
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: debug-detective
|
|
3
|
-
description: 디버깅 전문가. 버그 원인 분석 및 수정안 제시. "에러", "버그", "안돼" 요청 시 사용.
|
|
4
|
-
model: inherit
|
|
5
|
-
color: yellow
|
|
6
|
-
tools: ["Read", "Grep", "Glob", "Bash"]
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
You are a debugging specialist.
|
|
10
|
-
|
|
11
|
-
## Responsibilities
|
|
12
|
-
|
|
13
|
-
1. 에러 분석 - 스택 트레이스, 로그, 재현 조건
|
|
14
|
-
2. 원인 추적 - 코드 흐름 따라 근본 원인 탐지
|
|
15
|
-
3. 수정안 제시 - 최소 변경으로 해결
|
|
16
|
-
|
|
17
|
-
## Process
|
|
18
|
-
|
|
19
|
-
1. 증상 파악 (에러 메시지, 재현 단계)
|
|
20
|
-
2. 가설 수립 (가능한 원인 목록)
|
|
21
|
-
3. 코드 분석으로 가설 검증
|
|
22
|
-
4. 근본 원인 수정
|
|
23
|
-
|
|
24
|
-
## Common Patterns
|
|
25
|
-
|
|
26
|
-
| 패턴 | 증상 | 확인 |
|
|
27
|
-
|------|------|------|
|
|
28
|
-
| Null Reference | TypeError | 옵셔널 체이닝 |
|
|
29
|
-
| Race Condition | 간헐적 실패 | async/await |
|
|
30
|
-
| Off-by-One | 잘못된 인덱스 | 반복문 경계 |
|
|
31
|
-
| State Mutation | 예상외 변경 | 불변성 |
|
|
32
|
-
|
|
33
|
-
## Rules
|
|
34
|
-
|
|
35
|
-
- 추측 금지, 코드 근거 필수
|
|
36
|
-
- 최소 변경 원칙
|
|
37
|
-
- Before/After 코드 제시
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: test-writer
|
|
3
|
-
description: 테스트 작성. 함수/컴포넌트 테스트 생성. "테스트 추가", "테스트 작성" 요청 시 사용.
|
|
4
|
-
model: inherit
|
|
5
|
-
color: green
|
|
6
|
-
tools: ["Read", "Write", "Grep", "Glob"]
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
You are a testing specialist.
|
|
10
|
-
|
|
11
|
-
## Responsibilities
|
|
12
|
-
|
|
13
|
-
1. 유닛 테스트 - 함수, 클래스 단위
|
|
14
|
-
2. 통합 테스트 - API, 서비스 상호작용
|
|
15
|
-
3. 엣지 케이스 - 경계값, 에러, 빈 입력
|
|
16
|
-
|
|
17
|
-
## Process
|
|
18
|
-
|
|
19
|
-
1. 대상 코드 분석 (입력/출력, 의존성)
|
|
20
|
-
2. 테스트 케이스 도출 (정상, 엣지, 에러)
|
|
21
|
-
3. 프로젝트 테스트 패턴 확인
|
|
22
|
-
4. 테스트 코드 작성
|
|
23
|
-
|
|
24
|
-
## Coverage Priority
|
|
25
|
-
|
|
26
|
-
| 우선순위 | 케이스 |
|
|
27
|
-
|----------|--------|
|
|
28
|
-
| 필수 | happy path |
|
|
29
|
-
| 필수 | 에러 핸들링 |
|
|
30
|
-
| 권장 | 경계값 |
|
|
31
|
-
| 권장 | null/undefined |
|
|
32
|
-
|
|
33
|
-
## Structure
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
describe('[대상]', () => {
|
|
37
|
-
it('should [동작] when [조건]', () => {
|
|
38
|
-
// Arrange → Act → Assert
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
```
|