@kood/claude-code 0.1.6 → 0.1.9

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.
Files changed (107) hide show
  1. package/dist/index.js +109 -216
  2. package/package.json +8 -2
  3. package/templates/hono/CLAUDE.md +59 -328
  4. package/templates/hono/docs/architecture/architecture.md +93 -747
  5. package/templates/hono/docs/deployment/cloudflare.md +59 -513
  6. package/templates/hono/docs/deployment/docker.md +41 -356
  7. package/templates/hono/docs/deployment/index.md +54 -190
  8. package/templates/hono/docs/deployment/railway.md +36 -306
  9. package/templates/hono/docs/deployment/vercel.md +49 -434
  10. package/templates/hono/docs/library/ai-sdk/index.md +53 -290
  11. package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
  12. package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
  13. package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
  14. package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
  15. package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
  16. package/templates/hono/docs/library/hono/env-setup.md +24 -313
  17. package/templates/hono/docs/library/hono/error-handling.md +34 -295
  18. package/templates/hono/docs/library/hono/index.md +29 -121
  19. package/templates/hono/docs/library/hono/middleware.md +21 -188
  20. package/templates/hono/docs/library/hono/rpc.md +40 -341
  21. package/templates/hono/docs/library/hono/validation.md +35 -195
  22. package/templates/hono/docs/library/pino/index.md +42 -333
  23. package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
  24. package/templates/hono/docs/library/prisma/config.md +19 -260
  25. package/templates/hono/docs/library/prisma/index.md +67 -320
  26. package/templates/hono/docs/library/zod/index.md +53 -257
  27. package/templates/npx/CLAUDE.md +62 -274
  28. package/templates/npx/docs/references/patterns.md +160 -0
  29. package/templates/tanstack-start/CLAUDE.md +100 -256
  30. package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
  31. package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
  32. package/templates/tanstack-start/docs/deployment/index.md +57 -286
  33. package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
  34. package/templates/tanstack-start/docs/deployment/railway.md +40 -409
  35. package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
  36. package/templates/tanstack-start/docs/design/components.md +77 -311
  37. package/templates/tanstack-start/docs/design/index.md +113 -69
  38. package/templates/tanstack-start/docs/design/safe-area.md +51 -250
  39. package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
  40. package/templates/tanstack-start/docs/guides/conventions.md +103 -0
  41. package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
  42. package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
  43. package/templates/tanstack-start/docs/guides/hooks.md +166 -0
  44. package/templates/tanstack-start/docs/guides/routes.md +166 -0
  45. package/templates/tanstack-start/docs/guides/services.md +143 -0
  46. package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
  47. package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
  48. package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
  49. package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
  50. package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
  51. package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
  52. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
  53. package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
  54. package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
  55. package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
  56. package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
  57. package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
  58. package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
  59. package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
  60. package/templates/tanstack-start/docs/library/tanstack-query/index.md +26 -97
  61. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
  62. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
  63. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
  64. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
  65. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
  66. package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
  67. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
  68. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
  69. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
  70. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
  71. package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
  72. package/templates/tanstack-start/docs/library/zod/index.md +31 -144
  73. package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
  74. package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
  75. package/templates/hono/docs/commands/git.md +0 -145
  76. package/templates/hono/docs/mcp/context7.md +0 -106
  77. package/templates/hono/docs/mcp/index.md +0 -176
  78. package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
  79. package/templates/hono/docs/mcp/serena.md +0 -269
  80. package/templates/hono/docs/mcp/sgrep.md +0 -105
  81. package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
  82. package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
  83. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
  84. package/templates/npx/docs/commands/git.md +0 -145
  85. package/templates/npx/docs/mcp/index.md +0 -60
  86. package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
  87. package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
  88. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
  89. package/templates/tanstack-start/docs/commands/git.md +0 -145
  90. package/templates/tanstack-start/docs/design/accessibility.md +0 -433
  91. package/templates/tanstack-start/docs/design/color.md +0 -235
  92. package/templates/tanstack-start/docs/design/spacing.md +0 -341
  93. package/templates/tanstack-start/docs/design/typography.md +0 -324
  94. package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
  95. package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
  96. package/templates/tanstack-start/docs/guides/prettier.md +0 -189
  97. package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
  98. package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -107
  99. package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -186
  100. package/templates/tanstack-start/docs/mcp/context7.md +0 -204
  101. package/templates/tanstack-start/docs/mcp/index.md +0 -177
  102. package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
  103. package/templates/tanstack-start/docs/mcp/serena.md +0 -269
  104. package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
  105. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
  106. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
  107. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
@@ -1,710 +0,0 @@
1
- # Project Templates
2
-
3
- TanStack Start 프로젝트 템플릿 및 구조 가이드입니다.
4
-
5
- ## TanStack Start Project Structure
6
-
7
- ### Base Structure
8
-
9
- ```
10
- my-app/
11
- ├── src/
12
- │ ├── routes/ # 파일 기반 라우팅
13
- │ │ ├── __root.tsx # Root layout
14
- │ │ ├── index.tsx # Home (/)
15
- │ │ ├── about.tsx # /about
16
- │ │ ├── users/
17
- │ │ │ ├── index.tsx # /users
18
- │ │ │ ├── $id.tsx # /users/:id
19
- │ │ │ ├── -components/ # 페이지 전용 컴포넌트
20
- │ │ │ │ └── user-card.tsx
21
- │ │ │ ├── -sections/ # 섹션 분리
22
- │ │ │ │ ├── user-list-section.tsx
23
- │ │ │ │ └── user-form-section.tsx
24
- │ │ │ └── -hooks/ # 페이지 전용 훅
25
- │ │ │ └── use-users.ts
26
- │ │ └── api/
27
- │ │ └── health.ts # API route
28
- │ ├── components/ # 공통 컴포넌트
29
- │ │ └── ui/
30
- │ │ ├── button.tsx
31
- │ │ ├── input.tsx
32
- │ │ └── modal.tsx
33
- │ ├── database/ # 데이터베이스 관련
34
- │ │ ├── prisma.ts # Prisma Client 인스턴스
35
- │ │ └── seed.ts # 시드 데이터 (선택)
36
- │ ├── services/ # 도메인별 SDK/서비스 레이어
37
- │ │ ├── user/
38
- │ │ │ ├── index.ts # 진입점 (re-export)
39
- │ │ │ ├── schemas.ts # Zod 스키마
40
- │ │ │ ├── queries.ts # GET 요청
41
- │ │ │ └── mutations.ts # POST 요청
42
- │ │ ├── auth/
43
- │ │ │ ├── index.ts
44
- │ │ │ ├── schemas.ts
45
- │ │ │ ├── queries.ts
46
- │ │ │ └── mutations.ts
47
- │ │ └── post/
48
- │ │ ├── index.ts
49
- │ │ ├── schemas.ts
50
- │ │ ├── queries.ts
51
- │ │ └── mutations.ts
52
- │ ├── lib/ # 공통 유틸리티
53
- │ │ ├── query-client.ts
54
- │ │ └── utils.ts
55
- │ ├── hooks/ # 공통 훅
56
- │ │ ├── use-auth.ts
57
- │ │ └── use-media-query.ts
58
- │ ├── types/ # 타입 정의
59
- │ │ └── index.ts
60
- │ └── styles/
61
- │ └── app.css
62
- ├── generated/
63
- │ └── prisma/ # Prisma Client 출력
64
- ├── prisma/
65
- │ └── schema.prisma
66
- ├── public/
67
- ├── app.config.ts
68
- ├── package.json
69
- ├── tsconfig.json
70
- └── tailwind.config.ts
71
- ```
72
-
73
- ## Route Folder Convention
74
-
75
- ### `-` 접두사 규칙
76
-
77
- TanStack Start에서 `-` 접두사가 있는 폴더는 라우트에서 제외됩니다:
78
-
79
- ```
80
- routes/users/
81
- ├── index.tsx # /users ✅ 라우트
82
- ├── $id.tsx # /users/:id ✅ 라우트
83
- ├── route.tsx # route 설정 (선택)
84
- ├── -components/ # ❌ 라우트 아님, 컴포넌트
85
- ├── -sections/ # ❌ 라우트 아님, 섹션
86
- └── -hooks/ # ❌ 라우트 아님, 훅
87
- ```
88
-
89
- ### Route with Sections
90
-
91
- 복잡한 페이지는 섹션으로 분리:
92
-
93
- ```
94
- routes/dashboard/
95
- ├── index.tsx
96
- ├── -sections/
97
- │ ├── stats-section.tsx
98
- │ ├── recent-activity-section.tsx
99
- │ └── quick-actions-section.tsx
100
- ├── -components/
101
- │ ├── stat-card.tsx
102
- │ └── activity-item.tsx
103
- └── -hooks/
104
- ├── use-dashboard-stats.ts
105
- └── use-recent-activity.ts
106
- ```
107
-
108
- ## Route Patterns
109
-
110
- ### Basic Route
111
-
112
- ```tsx
113
- // routes/about.tsx
114
- import { createFileRoute } from '@tanstack/react-router'
115
-
116
- export const Route = createFileRoute('/about')({
117
- component: AboutPage,
118
- })
119
-
120
- const AboutPage = (): JSX.Element => {
121
- return <h1>About</h1>
122
- }
123
- ```
124
-
125
- ### Route with Sections and Hooks
126
-
127
- ```tsx
128
- // routes/users/index.tsx
129
- import { createFileRoute } from '@tanstack/react-router'
130
- import { UserListSection } from './-sections/user-list-section'
131
- import { UserFilterSection } from './-sections/user-filter-section'
132
-
133
- export const Route = createFileRoute('/users/')({
134
- component: UsersPage,
135
- })
136
-
137
- const UsersPage = (): JSX.Element => {
138
- return (
139
- <div className="container mx-auto p-4">
140
- <h1 className="text-2xl font-bold mb-4">Users</h1>
141
- <UserFilterSection />
142
- <UserListSection />
143
- </div>
144
- )
145
- }
146
- ```
147
-
148
- ### Page Hook
149
-
150
- ```typescript
151
- // routes/users/-hooks/use-users.ts
152
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
153
- import { getUsers, createUser, deleteUser } from '@/services/user'
154
- import type { User } from '@/types'
155
-
156
- interface UseUsersReturn {
157
- users: User[] | undefined
158
- isLoading: boolean
159
- error: Error | null
160
- createUser: (data: { email: string; name: string }) => void
161
- deleteUser: (id: string) => void
162
- isCreating: boolean
163
- isDeleting: boolean
164
- }
165
-
166
- export const useUsers = (): UseUsersReturn => {
167
- const queryClient = useQueryClient()
168
-
169
- const { data: users, isLoading, error } = useQuery({
170
- queryKey: ['users'],
171
- queryFn: () => getUsers(),
172
- })
173
-
174
- const createMutation = useMutation({
175
- mutationFn: createUser,
176
- onSuccess: () => {
177
- queryClient.invalidateQueries({ queryKey: ['users'] })
178
- },
179
- })
180
-
181
- const deleteMutation = useMutation({
182
- mutationFn: deleteUser,
183
- onSuccess: () => {
184
- queryClient.invalidateQueries({ queryKey: ['users'] })
185
- },
186
- })
187
-
188
- return {
189
- users,
190
- isLoading,
191
- error,
192
- createUser: createMutation.mutate,
193
- deleteUser: deleteMutation.mutate,
194
- isCreating: createMutation.isPending,
195
- isDeleting: deleteMutation.isPending,
196
- }
197
- }
198
- ```
199
-
200
- ### Section Component
201
-
202
- ```tsx
203
- // routes/users/-sections/user-list-section.tsx
204
- import { useUsers } from '../-hooks/use-users'
205
- import { UserCard } from '../-components/user-card'
206
-
207
- export const UserListSection = (): JSX.Element => {
208
- const { users, isLoading, error, deleteUser, isDeleting } = useUsers()
209
-
210
- if (isLoading) {
211
- return <div className="text-center py-8">Loading...</div>
212
- }
213
-
214
- if (error) {
215
- return <div className="text-red-600 py-8">Error: {error.message}</div>
216
- }
217
-
218
- if (!users?.length) {
219
- return <div className="text-gray-500 py-8">No users found</div>
220
- }
221
-
222
- return (
223
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
224
- {users.map((user) => (
225
- <UserCard
226
- key={user.id}
227
- user={user}
228
- onDelete={deleteUser}
229
- isDeleting={isDeleting}
230
- />
231
- ))}
232
- </div>
233
- )
234
- }
235
- ```
236
-
237
- ### Page Component
238
-
239
- ```tsx
240
- // routes/users/-components/user-card.tsx
241
- import type { User } from '@/types'
242
- import { Button } from '@/components/ui/button'
243
-
244
- interface UserCardProps {
245
- user: User
246
- onDelete?: (id: string) => void
247
- isDeleting?: boolean
248
- }
249
-
250
- export const UserCard = ({
251
- user,
252
- onDelete,
253
- isDeleting,
254
- }: UserCardProps): JSX.Element => {
255
- return (
256
- <div className="rounded-lg border p-4 shadow-sm">
257
- <div className="flex items-center gap-4">
258
- <div className="h-12 w-12 rounded-full bg-gray-200" />
259
- <div>
260
- <h3 className="font-semibold">{user.name}</h3>
261
- <p className="text-sm text-gray-600">{user.email}</p>
262
- </div>
263
- </div>
264
-
265
- {onDelete && (
266
- <div className="mt-4">
267
- <Button
268
- variant="outline"
269
- size="sm"
270
- onClick={() => onDelete(user.id)}
271
- disabled={isDeleting}
272
- >
273
- {isDeleting ? 'Deleting...' : 'Delete'}
274
- </Button>
275
- </div>
276
- )}
277
- </div>
278
- )
279
- }
280
- ```
281
-
282
- ### Dynamic Route
283
-
284
- ```tsx
285
- // routes/users/$id.tsx
286
- import { createFileRoute } from '@tanstack/react-router'
287
- import { UserDetailSection } from './-sections/user-detail-section'
288
- import { UserPostsSection } from './-sections/user-posts-section'
289
-
290
- export const Route = createFileRoute('/users/$id')({
291
- component: UserDetailPage,
292
- })
293
-
294
- const UserDetailPage = (): JSX.Element => {
295
- const { id } = Route.useParams()
296
-
297
- return (
298
- <div className="container mx-auto p-4">
299
- <UserDetailSection userId={id} />
300
- <UserPostsSection userId={id} />
301
- </div>
302
- )
303
- }
304
- ```
305
-
306
- ### Layout Route
307
-
308
- ```tsx
309
- // routes/dashboard/_layout.tsx
310
- import { createFileRoute, Outlet } from '@tanstack/react-router'
311
- import { DashboardSidebar } from './-components/dashboard-sidebar'
312
-
313
- export const Route = createFileRoute('/dashboard/_layout')({
314
- component: DashboardLayout,
315
- })
316
-
317
- const DashboardLayout = (): JSX.Element => {
318
- return (
319
- <div className="flex">
320
- <DashboardSidebar />
321
- <main className="flex-1 p-4">
322
- <Outlet />
323
- </main>
324
- </div>
325
- )
326
- }
327
- ```
328
-
329
- ## Component Templates
330
-
331
- ### Common UI Component
332
-
333
- ```tsx
334
- // components/ui/button.tsx
335
- interface ButtonProps {
336
- children: React.ReactNode
337
- variant?: 'primary' | 'secondary' | 'outline'
338
- size?: 'sm' | 'md' | 'lg'
339
- onClick?: () => void
340
- disabled?: boolean
341
- type?: 'button' | 'submit' | 'reset'
342
- }
343
-
344
- export const Button = ({
345
- children,
346
- variant = 'primary',
347
- size = 'md',
348
- onClick,
349
- disabled,
350
- type = 'button',
351
- }: ButtonProps): JSX.Element => {
352
- const baseStyles = 'rounded font-medium transition-colors disabled:opacity-50'
353
-
354
- const variants = {
355
- primary: 'bg-blue-600 text-white hover:bg-blue-700',
356
- secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
357
- outline: 'border border-gray-300 hover:bg-gray-50',
358
- }
359
-
360
- const sizes = {
361
- sm: 'px-3 py-1.5 text-sm',
362
- md: 'px-4 py-2',
363
- lg: 'px-6 py-3 text-lg',
364
- }
365
-
366
- return (
367
- <button
368
- type={type}
369
- onClick={onClick}
370
- disabled={disabled}
371
- className={`${baseStyles} ${variants[variant]} ${sizes[size]}`}
372
- >
373
- {children}
374
- </button>
375
- )
376
- }
377
- ```
378
-
379
- ### Modal Component
380
-
381
- ```tsx
382
- // components/ui/modal.tsx
383
- import { useCallback, useEffect } from 'react'
384
-
385
- interface ModalProps {
386
- isOpen: boolean
387
- onClose: () => void
388
- title: string
389
- children: React.ReactNode
390
- }
391
-
392
- export const Modal = ({
393
- isOpen,
394
- onClose,
395
- title,
396
- children,
397
- }: ModalProps): JSX.Element | null => {
398
- const handleEscape = useCallback(
399
- (e: KeyboardEvent) => {
400
- if (e.key === 'Escape') onClose()
401
- },
402
- [onClose]
403
- )
404
-
405
- useEffect(() => {
406
- document.addEventListener('keydown', handleEscape)
407
- return () => document.removeEventListener('keydown', handleEscape)
408
- }, [handleEscape])
409
-
410
- if (!isOpen) return null
411
-
412
- return (
413
- <div className="fixed inset-0 z-50 flex items-center justify-center">
414
- <div
415
- className="absolute inset-0 bg-black/50"
416
- onClick={onClose}
417
- />
418
- <div className="relative z-10 w-full max-w-md rounded-lg bg-white p-6 shadow-lg">
419
- <h2 className="text-xl font-semibold mb-4">{title}</h2>
420
- {children}
421
- </div>
422
- </div>
423
- )
424
- }
425
- ```
426
-
427
- ## Service Templates
428
-
429
- ### Database Setup
430
-
431
- ```typescript
432
- // database/prisma.ts
433
- import { PrismaClient } from '../../generated/prisma'
434
-
435
- const globalForPrisma = globalThis as unknown as {
436
- prisma: PrismaClient | undefined
437
- }
438
-
439
- export const prisma =
440
- globalForPrisma.prisma ??
441
- new PrismaClient({
442
- log: process.env.NODE_ENV === 'development' ? ['query'] : [],
443
- })
444
-
445
- if (process.env.NODE_ENV !== 'production') {
446
- globalForPrisma.prisma = prisma
447
- }
448
- ```
449
-
450
- ### Service 폴더 구조
451
-
452
- ```
453
- services/
454
- ├── user/
455
- │ ├── index.ts # 진입점 (re-export)
456
- │ ├── schemas.ts # Zod 스키마
457
- │ ├── queries.ts # GET 요청 (읽기)
458
- │ └── mutations.ts # POST 요청 (쓰기)
459
- ├── auth/
460
- │ ├── index.ts
461
- │ ├── schemas.ts
462
- │ ├── queries.ts
463
- │ └── mutations.ts
464
- └── post/
465
- ├── index.ts
466
- ├── schemas.ts
467
- ├── queries.ts
468
- └── mutations.ts
469
- ```
470
-
471
- ### Schemas 파일
472
-
473
- ```typescript
474
- // services/user/schemas.ts
475
- import { z } from 'zod'
476
-
477
- export const createUserSchema = z.object({
478
- email: z.email(),
479
- name: z.string().min(1).max(100).trim(),
480
- })
481
-
482
- export const updateUserSchema = z.object({
483
- id: z.string(),
484
- email: z.email().optional(),
485
- name: z.string().min(1).max(100).trim().optional(),
486
- })
487
-
488
- export type CreateUserInput = z.infer<typeof createUserSchema>
489
- export type UpdateUserInput = z.infer<typeof updateUserSchema>
490
- ```
491
-
492
- ### Queries 파일
493
-
494
- ```typescript
495
- // services/user/queries.ts
496
- import { createServerFn } from '@tanstack/react-start'
497
- import { prisma } from '@/database/prisma'
498
-
499
- export const getUsers = createServerFn({ method: 'GET' })
500
- .handler(async () => {
501
- return prisma.user.findMany({
502
- orderBy: { createdAt: 'desc' },
503
- })
504
- })
505
-
506
- export const getUserById = createServerFn({ method: 'GET' })
507
- .handler(async ({ data: id }: { data: string }) => {
508
- const user = await prisma.user.findUnique({ where: { id } })
509
- if (!user) throw new Error('User not found')
510
- return user
511
- })
512
-
513
- export const getUserByEmail = createServerFn({ method: 'GET' })
514
- .handler(async ({ data: email }: { data: string }) => {
515
- return prisma.user.findUnique({ where: { email } })
516
- })
517
- ```
518
-
519
- ### Mutations 파일
520
-
521
- ```typescript
522
- // services/user/mutations.ts
523
- import { createServerFn } from '@tanstack/react-start'
524
- import { prisma } from '@/database/prisma'
525
- import { createUserSchema, updateUserSchema } from './schemas'
526
-
527
- export const createUser = createServerFn({ method: 'POST' })
528
- .inputValidator(createUserSchema)
529
- .handler(async ({ data }) => {
530
- return prisma.user.create({ data })
531
- })
532
-
533
- export const updateUser = createServerFn({ method: 'POST' })
534
- .inputValidator(updateUserSchema)
535
- .handler(async ({ data }) => {
536
- const { id, ...updateData } = data
537
- return prisma.user.update({ where: { id }, data: updateData })
538
- })
539
-
540
- export const deleteUser = createServerFn({ method: 'POST' })
541
- .handler(async ({ data: id }: { data: string }) => {
542
- return prisma.user.delete({ where: { id } })
543
- })
544
- ```
545
-
546
- ### Service 진입점
547
-
548
- ```typescript
549
- // services/user/index.ts
550
- export * from './schemas'
551
- export * from './queries'
552
- export * from './mutations'
553
- ```
554
-
555
- ## Common Hook Templates
556
-
557
- ### Auth Hook
558
-
559
- ```typescript
560
- // hooks/use-auth.ts
561
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
562
- import { getCurrentUser, login, logout } from '@/services/auth'
563
- import type { User, LoginInput } from '@/types'
564
-
565
- interface UseAuthReturn {
566
- user: User | null | undefined
567
- isLoading: boolean
568
- isAuthenticated: boolean
569
- login: (data: LoginInput) => void
570
- logout: () => void
571
- isLoggingIn: boolean
572
- }
573
-
574
- export const useAuth = (): UseAuthReturn => {
575
- const queryClient = useQueryClient()
576
-
577
- const { data: user, isLoading } = useQuery({
578
- queryKey: ['auth', 'user'],
579
- queryFn: () => getCurrentUser(),
580
- retry: false,
581
- })
582
-
583
- const loginMutation = useMutation({
584
- mutationFn: login,
585
- onSuccess: () => {
586
- queryClient.invalidateQueries({ queryKey: ['auth'] })
587
- },
588
- })
589
-
590
- const logoutMutation = useMutation({
591
- mutationFn: logout,
592
- onSuccess: () => {
593
- queryClient.setQueryData(['auth', 'user'], null)
594
- queryClient.invalidateQueries({ queryKey: ['auth'] })
595
- },
596
- })
597
-
598
- return {
599
- user,
600
- isLoading,
601
- isAuthenticated: !!user,
602
- login: loginMutation.mutate,
603
- logout: logoutMutation.mutate,
604
- isLoggingIn: loginMutation.isPending,
605
- }
606
- }
607
- ```
608
-
609
- ## Database Schema Template
610
-
611
- ```prisma
612
- // prisma/schema.prisma
613
- datasource db {
614
- provider = "postgresql"
615
- url = env("DATABASE_URL")
616
- }
617
-
618
- generator client {
619
- provider = "prisma-client"
620
- output = "../generated/prisma"
621
- }
622
-
623
- model User {
624
- id String @id @default(cuid())
625
- email String @unique
626
- name String?
627
- role Role @default(USER)
628
- posts Post[]
629
- createdAt DateTime @default(now())
630
- updatedAt DateTime @updatedAt
631
- }
632
-
633
- model Post {
634
- id String @id @default(cuid())
635
- title String
636
- content String?
637
- published Boolean @default(false)
638
- author User @relation(fields: [authorId], references: [id])
639
- authorId String
640
- createdAt DateTime @default(now())
641
- updatedAt DateTime @updatedAt
642
- }
643
-
644
- enum Role {
645
- USER
646
- ADMIN
647
- }
648
- ```
649
-
650
- ## Configuration Templates
651
-
652
- ### Tailwind Config
653
-
654
- ```typescript
655
- // tailwind.config.ts
656
- import type { Config } from 'tailwindcss'
657
-
658
- export default {
659
- content: ['./src/**/*.{js,ts,jsx,tsx}'],
660
- theme: {
661
- extend: {
662
- colors: {
663
- primary: {
664
- 50: '#eff6ff',
665
- 500: '#3b82f6',
666
- 600: '#2563eb',
667
- 700: '#1d4ed8',
668
- },
669
- },
670
- },
671
- },
672
- plugins: [],
673
- } satisfies Config
674
- ```
675
-
676
- ### TypeScript Config
677
-
678
- ```json
679
- {
680
- "compilerOptions": {
681
- "target": "ES2022",
682
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
683
- "module": "ESNext",
684
- "moduleResolution": "bundler",
685
- "jsx": "react-jsx",
686
- "strict": true,
687
- "noUnusedLocals": true,
688
- "noUnusedParameters": true,
689
- "paths": {
690
- "@/*": ["./src/*"]
691
- }
692
- },
693
- "include": ["src/**/*", "*.config.ts"]
694
- }
695
- ```
696
-
697
- ### Environment Schema
698
-
699
- ```typescript
700
- // lib/env.ts
701
- import { z } from 'zod'
702
-
703
- const envSchema = z.object({
704
- NODE_ENV: z.enum(['development', 'production', 'test']),
705
- DATABASE_URL: z.url(),
706
- API_SECRET: z.string().min(32),
707
- })
708
-
709
- export const env = envSchema.parse(process.env)
710
- ```