@kood/claude-code 0.3.9 → 0.3.10

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 CHANGED
@@ -409,7 +409,7 @@ var init = async (options) => {
409
409
 
410
410
  // src/index.ts
411
411
  var program = new Command();
412
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.3.9");
412
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.3.10");
413
413
  program.option(
414
414
  "-t, --template <names>",
415
415
  "template names (comma-separated: tanstack-start,hono)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -1,10 +1,43 @@
1
1
  # Better Auth
2
2
 
3
- > TypeScript Authentication Library
3
+ > v1.x | TypeScript Authentication Framework
4
+
5
+ <context>
6
+
7
+ **Purpose:** Framework-agnostic authentication and authorization for TypeScript
8
+ **Features:** Email/Password, Social OAuth, 2FA, Passkeys, Multi-session, SSO
9
+
10
+ </context>
11
+
12
+ ---
13
+
14
+ <forbidden>
15
+
16
+ | 분류 | 금지 |
17
+ |------|------|
18
+ | **보안** | 하드코딩된 시크릿, HTTP 프로덕션 배포 |
19
+ | **클라이언트** | 직접 세션 조작, 토큰 localStorage 저장 |
20
+ | **서버** | `authClient` 사용 (서버는 `auth.api.*`) |
21
+ | **설정** | 절대 경로 하드코딩 (환경변수 사용) |
22
+
23
+ </forbidden>
4
24
 
5
25
  ---
6
26
 
7
- <quick_start>
27
+ <required>
28
+
29
+ | 분류 | 필수 |
30
+ |------|------|
31
+ | **환경변수** | `BETTER_AUTH_SECRET` (32+ chars), `BETTER_AUTH_URL` |
32
+ | **서버** | `auth.api.getSession()` 사용 |
33
+ | **클라이언트** | `authClient.*` 메서드 사용 |
34
+ | **프로덕션** | HTTPS, 환경별 `baseURL` 설정 |
35
+
36
+ </required>
37
+
38
+ ---
39
+
40
+ <installation>
8
41
 
9
42
  ## Installation
10
43
 
@@ -12,6 +45,20 @@
12
45
  npm install better-auth
13
46
  ```
14
47
 
48
+ ## Environment Variables
49
+
50
+ ```bash
51
+ # .env
52
+ BETTER_AUTH_SECRET="$(openssl rand -base64 32)"
53
+ BETTER_AUTH_URL="http://localhost:3000"
54
+ ```
55
+
56
+ </installation>
57
+
58
+ ---
59
+
60
+ <setup>
61
+
15
62
  ## Minimal Setup
16
63
 
17
64
  ```typescript
@@ -43,35 +90,71 @@ export const GET = async ({ request }: { request: Request }) => auth.handler(req
43
90
  export const POST = async ({ request }: { request: Request }) => auth.handler(request)
44
91
  ```
45
92
 
46
- </quick_start>
93
+ ## Database Adapters
94
+
95
+ | Adapter | Import | Provider |
96
+ |---------|--------|----------|
97
+ | **Prisma** | `better-auth/adapters/prisma` | `postgresql`, `mysql`, `sqlite` |
98
+ | **Drizzle** | `better-auth/adapters/drizzle` | `pg`, `mysql2`, `better-sqlite3` |
99
+ | **Kysely** | `better-auth/adapters/kysely` | dialect 기반 |
100
+
101
+ ## Config Options
102
+
103
+ | 옵션 | 타입 | 기본값 | 설명 |
104
+ |------|------|--------|------|
105
+ | `database` | `Adapter` | 필수 | DB 어댑터 |
106
+ | `baseURL` | `string` | `http://localhost:3000` | 앱 URL |
107
+ | `basePath` | `string` | `/api/auth` | Auth 경로 |
108
+ | `secret` | `string` | 환경변수 | JWT 시크릿 |
109
+ | `trustedOrigins` | `string[]` | `[]` | CORS 허용 오리진 |
110
+
111
+ </setup>
47
112
 
48
113
  ---
49
114
 
50
- <setup>
115
+ <auth_methods>
51
116
 
52
- ## Database Adapters
117
+ ## Email & Password
53
118
 
54
- | Adapter | Import | Provider |
55
- |---------|--------|----------|
56
- | Prisma | `better-auth/adapters/prisma` | `postgresql`, `mysql`, `sqlite` |
57
- | Drizzle | `better-auth/adapters/drizzle` | `pg`, `mysql2`, `better-sqlite3` |
58
- | Kysely | `better-auth/adapters/kysely` | dialect-based |
119
+ ```typescript
120
+ // ✅ 서버 설정
121
+ export const auth = betterAuth({
122
+ emailAndPassword: {
123
+ enabled: true,
124
+ requireEmailVerification: false,
125
+ sendResetPassword: async ({ user, url }) => {
126
+ await sendEmail({
127
+ to: user.email,
128
+ subject: 'Reset Password',
129
+ html: `<a href="${url}">Reset</a>`,
130
+ })
131
+ },
132
+ },
133
+ })
59
134
 
60
- ## Auth Config
135
+ // 회원가입
136
+ await authClient.signUp.email({
137
+ email: 'user@example.com',
138
+ password: 'password123',
139
+ name: 'User Name',
140
+ })
61
141
 
62
- | Option | Type | Default | Description |
63
- |--------|------|---------|-------------|
64
- | `database` | `Adapter` | Required | DB adapter |
65
- | `baseURL` | `string` | `http://localhost:3000` | App URL |
66
- | `basePath` | `string` | `/api/auth` | Auth path |
67
- | `secret` | `string` | Environment variable | JWT secret |
68
- | `trustedOrigins` | `string[]` | `[]` | CORS allowed origins |
142
+ // 로그인
143
+ await authClient.signIn.email({
144
+ email: 'user@example.com',
145
+ password: 'password123',
146
+ })
69
147
 
70
- ## Social Providers
148
+ // 비밀번호 재설정
149
+ await authClient.forgetPassword({ email: 'user@example.com' })
150
+ await authClient.resetPassword({ token, password: 'newpassword' })
151
+ ```
152
+
153
+ ## Social OAuth
71
154
 
72
155
  ```typescript
156
+ // ✅ 서버 설정
73
157
  export const auth = betterAuth({
74
- database: prismaAdapter(prisma),
75
158
  socialProviders: {
76
159
  google: {
77
160
  clientId: process.env.GOOGLE_CLIENT_ID!,
@@ -83,23 +166,70 @@ export const auth = betterAuth({
83
166
  },
84
167
  },
85
168
  })
169
+
170
+ // ✅ 클라이언트 로그인
171
+ await authClient.signIn.social({ provider: 'google', callbackURL: '/dashboard' })
172
+ await authClient.signIn.social({ provider: 'github', callbackURL: '/dashboard' })
86
173
  ```
87
174
 
88
- ## Email & Password
175
+ ## Two-Factor Authentication
89
176
 
90
177
  ```typescript
178
+ // ✅ 서버 플러그인
179
+ import { twoFactor } from 'better-auth/plugins'
180
+
91
181
  export const auth = betterAuth({
92
- emailAndPassword: {
93
- enabled: true,
94
- requireEmailVerification: false,
95
- sendResetPassword: async ({ user, url }) => {
96
- await sendEmail({ to: user.email, subject: 'Reset Password', html: `<a href="${url}">Reset</a>` })
97
- },
98
- },
182
+ plugins: [
183
+ twoFactor({
184
+ issuer: 'My App',
185
+ otpOptions: {
186
+ async sendOTP({ user, otp }) {
187
+ await sendEmail({
188
+ to: user.email,
189
+ subject: 'Your OTP Code',
190
+ html: `Your code: ${otp}`,
191
+ })
192
+ },
193
+ period: 300, // 5분
194
+ length: 6, // 6자리
195
+ },
196
+ backupCodeLength: 10,
197
+ backupCodeCount: 10,
198
+ }),
199
+ ],
200
+ })
201
+
202
+ // ✅ 클라이언트 플러그인
203
+ import { twoFactorClient } from 'better-auth/client/plugins'
204
+
205
+ export const authClient = createAuthClient({
206
+ plugins: [
207
+ twoFactorClient({
208
+ twoFactorPage: '/two-factor',
209
+ }),
210
+ ],
99
211
  })
212
+
213
+ // ✅ TOTP 활성화
214
+ const { data } = await authClient.twoFactor.enable({ password: 'current-password' })
215
+ // → { totpURI: 'otpauth://...', backupCodes: ['ABCD-1234', ...] }
216
+
217
+ // ✅ TOTP 검증
218
+ await authClient.twoFactor.verifyTotp({ code: '123456' })
219
+
220
+ // ✅ OTP 전송/검증
221
+ await authClient.twoFactor.sendOtp()
222
+ await authClient.twoFactor.verifyOtp({ code: '123456' })
223
+
224
+ // ✅ 백업 코드
225
+ await authClient.twoFactor.useBackupCode({ code: 'ABCD-1234' })
226
+ await authClient.twoFactor.regenerateBackupCodes({ password: 'current-password' })
227
+
228
+ // ✅ 2FA 비활성화
229
+ await authClient.twoFactor.disable({ password: 'current-password' })
100
230
  ```
101
231
 
102
- </setup>
232
+ </auth_methods>
103
233
 
104
234
  ---
105
235
 
@@ -107,22 +237,22 @@ export const auth = betterAuth({
107
237
 
108
238
  ## Session Config
109
239
 
110
- | Option | Type | Default | Description |
111
- |--------|------|---------|-------------|
112
- | `expiresIn` | `number` | `604800` (7 days) | Session expiration time (seconds) |
113
- | `updateAge` | `number` | `86400` (1 day) | Session renewal period (seconds) |
114
- | `cookieCache.enabled` | `boolean` | `true` | Enable cookie cache |
115
- | `cookieCache.maxAge` | `number` | `300` (5 min) | Cache validity time (seconds) |
116
- | `cookieCache.strategy` | `'compact' \| 'jwt' \| 'jwe'` | `'compact'` | Cache strategy |
240
+ | 옵션 | 타입 | 기본값 | 설명 |
241
+ |------|------|--------|------|
242
+ | `expiresIn` | `number` | `604800` (7) | 세션 만료 () |
243
+ | `updateAge` | `number` | `86400` (1) | 세션 갱신 주기 () |
244
+ | `cookieCache.enabled` | `boolean` | `true` | 쿠키 캐시 활성화 |
245
+ | `cookieCache.maxAge` | `number` | `300` (5) | 캐시 유효 시간 () |
246
+ | `cookieCache.strategy` | `'compact' \| 'jwt' \| 'jwe'` | `'compact'` | 캐시 전략 |
117
247
 
118
248
  ```typescript
119
249
  export const auth = betterAuth({
120
250
  session: {
121
- expiresIn: 604800, // 7 days
122
- updateAge: 86400, // Renew every day
251
+ expiresIn: 604800,
252
+ updateAge: 86400,
123
253
  cookieCache: {
124
254
  enabled: true,
125
- maxAge: 300, // 5 minutes
255
+ maxAge: 300,
126
256
  strategy: 'compact',
127
257
  },
128
258
  },
@@ -131,22 +261,27 @@ export const auth = betterAuth({
131
261
 
132
262
  ## Session Methods
133
263
 
264
+ | 환경 | 메서드 |
265
+ |------|--------|
266
+ | **클라이언트** | `authClient.getSession()` |
267
+ | **서버** | `auth.api.getSession({ headers })` |
268
+
134
269
  ```typescript
135
- // ✅ Client
270
+ // ✅ 클라이언트
136
271
  const session = await authClient.getSession()
137
272
  const session = await authClient.getSession({ query: { disableCookieCache: true } })
138
273
 
139
- // ✅ Server (TanStack Start)
274
+ // ✅ 서버 (TanStack Start)
140
275
  export const getSession = createServerFn({ method: 'GET' })
141
276
  .handler(async ({ request }) => {
142
277
  const session = await auth.api.getSession({ headers: request.headers })
143
278
  return session
144
279
  })
145
280
 
146
- // ✅ Update session
281
+ // ✅ 세션 업데이트
147
282
  await authClient.updateUser({ name: 'New Name' })
148
283
 
149
- // ✅ Sign out
284
+ // ✅ 로그아웃
150
285
  await authClient.signOut()
151
286
  ```
152
287
 
@@ -183,148 +318,39 @@ export const auth = betterAuth({
183
318
 
184
319
  ---
185
320
 
186
- <auth_methods>
187
-
188
- ## Email/Password
189
-
190
- ```typescript
191
- // ✅ Sign up
192
- await authClient.signUp.email({
193
- email: 'user@example.com',
194
- password: 'password123',
195
- name: 'User Name',
196
- })
197
-
198
- // ✅ Sign in
199
- await authClient.signIn.email({
200
- email: 'user@example.com',
201
- password: 'password123',
202
- })
203
-
204
- // ✅ Request password reset
205
- await authClient.forgetPassword({ email: 'user@example.com' })
206
-
207
- // ✅ Reset password
208
- await authClient.resetPassword({ token, password: 'newpassword' })
209
- ```
210
-
211
- ## Social Login
212
-
213
- ```typescript
214
- // ✅ Social sign in
215
- await authClient.signIn.social({ provider: 'google', callbackURL: '/dashboard' })
216
- await authClient.signIn.social({ provider: 'github', callbackURL: '/dashboard' })
217
-
218
- // ✅ SSO
219
- await authClient.signIn.sso({ providerId: 'provider-id', callbackURL: '/dashboard' })
220
- ```
221
-
222
- ## Two-Factor Authentication
223
-
224
- ### Server Setup
225
-
226
- ```typescript
227
- import { twoFactor } from 'better-auth/plugins'
228
-
229
- export const auth = betterAuth({
230
- plugins: [
231
- twoFactor({
232
- issuer: 'My App',
233
- otpOptions: {
234
- async sendOTP({ user, otp }) {
235
- await sendEmail({
236
- to: user.email,
237
- subject: 'Your OTP Code',
238
- html: `Your code: ${otp}`,
239
- })
240
- },
241
- period: 300, // 5분
242
- length: 6, // 6자리
243
- },
244
- backupCodeLength: 10,
245
- backupCodeCount: 10,
246
- }),
247
- ],
248
- })
249
- ```
250
-
251
- ### Client Setup
252
-
253
- ```typescript
254
- import { twoFactorClient } from 'better-auth/client/plugins'
255
-
256
- export const authClient = createAuthClient({
257
- plugins: [
258
- twoFactorClient({
259
- twoFactorPage: '/two-factor',
260
- }),
261
- ],
262
- })
263
- ```
264
-
265
- ### 2FA Usage
266
-
267
- ```typescript
268
- // ✅ Enable TOTP
269
- const { data } = await authClient.twoFactor.enable({ password: 'current-password' })
270
- // data: { totpURI: 'otpauth://...', backupCodes: ['ABCD-1234', ...] }
271
-
272
- // ✅ Verify TOTP
273
- await authClient.twoFactor.verifyTotp({ code: '123456' })
274
-
275
- // ✅ Send OTP
276
- await authClient.twoFactor.sendOtp()
277
-
278
- // ✅ Verify OTP
279
- await authClient.twoFactor.verifyOtp({ code: '123456' })
280
-
281
- // ✅ Use backup code
282
- await authClient.twoFactor.useBackupCode({ code: 'ABCD-1234' })
283
-
284
- // ✅ Regenerate backup codes
285
- const { data } = await authClient.twoFactor.regenerateBackupCodes({ password: 'current-password' })
286
-
287
- // ✅ Disable 2FA
288
- await authClient.twoFactor.disable({ password: 'current-password' })
289
- ```
290
-
291
- </auth_methods>
292
-
293
- ---
294
-
295
321
  <plugins>
296
322
 
297
323
  ## Plugin System
298
324
 
299
- | Plugin | Import | Features |
300
- |--------|--------|----------|
301
- | `multiSession` | `better-auth/plugins` | Multi-session management |
302
- | `customSession` | `better-auth/plugins` | Session field extension |
303
- | `twoFactor` | `better-auth/plugins` | Two-factor authentication |
304
- | `captcha` | `better-auth/plugins` | CAPTCHA verification |
325
+ | Plugin | 기능 |
326
+ |--------|------|
327
+ | `twoFactor` | TOTP, OTP, 백업 코드 |
328
+ | `multiSession` | 다중 세션 관리 |
329
+ | `customSession` | 세션 필드 확장 |
330
+ | `captcha` | reCAPTCHA 검증 |
305
331
 
306
332
  ## Multi-Session
307
333
 
308
334
  ```typescript
309
- // ✅ Server
335
+ // ✅ 서버
310
336
  import { multiSession } from 'better-auth/plugins'
311
337
 
312
338
  export const auth = betterAuth({
313
339
  plugins: [
314
340
  multiSession({
315
- maximumSessions: 5, // Maximum number of sessions
341
+ maximumSessions: 5,
316
342
  }),
317
343
  ],
318
344
  })
319
345
 
320
- // ✅ Client
346
+ // ✅ 클라이언트
321
347
  import { multiSessionClient } from 'better-auth/client/plugins'
322
348
 
323
349
  export const authClient = createAuthClient({
324
350
  plugins: [multiSessionClient()],
325
351
  })
326
352
 
327
- // Usage
353
+ // ✅ 사용
328
354
  const sessions = await authClient.multiSession.listSessions()
329
355
  await authClient.multiSession.revokeSession({ sessionId: 'session-id' })
330
356
  await authClient.multiSession.revokeOtherSessions()
@@ -333,7 +359,7 @@ await authClient.multiSession.revokeOtherSessions()
333
359
  ## CAPTCHA
334
360
 
335
361
  ```typescript
336
- // ✅ Server
362
+ // ✅ 서버
337
363
  import { captcha } from 'better-auth/plugins'
338
364
 
339
365
  export const auth = betterAuth({
@@ -347,7 +373,7 @@ export const auth = betterAuth({
347
373
  ],
348
374
  })
349
375
 
350
- // ✅ Client
376
+ // ✅ 클라이언트
351
377
  import { captchaClient } from 'better-auth/client/plugins'
352
378
 
353
379
  export const authClient = createAuthClient({
@@ -368,10 +394,24 @@ export const authClient = createAuthClient({
368
394
  ## SIWE (Ethereum)
369
395
 
370
396
  ```typescript
371
- // Server: siwe({ domain, uri })
372
- // Client:
397
+ // 서버
398
+ import { siwe } from 'better-auth/plugins'
399
+
400
+ export const auth = betterAuth({
401
+ plugins: [
402
+ siwe({
403
+ domain: 'example.com',
404
+ uri: 'https://example.com',
405
+ }),
406
+ ],
407
+ })
408
+
409
+ // ✅ 클라이언트
373
410
  const { data } = await authClient.siwe.getNonce()
374
- const message = await authClient.siwe.prepareMessage({ address: '0x...', nonce: data.nonce })
411
+ const message = await authClient.siwe.prepareMessage({
412
+ address: '0x...',
413
+ nonce: data.nonce,
414
+ })
375
415
  const signature = await signer.signMessage(message)
376
416
  await authClient.siwe.signIn({ message, signature })
377
417
  ```
@@ -379,7 +419,7 @@ await authClient.siwe.signIn({ message, signature })
379
419
  ## Stateless Mode
380
420
 
381
421
  ```typescript
382
- // ✅ Social login without DB
422
+ // ✅ DB 없이 소셜 로그인만
383
423
  export const auth = betterAuth({
384
424
  socialProviders: {
385
425
  google: {
@@ -422,8 +462,8 @@ export const auth = betterAuth({
422
462
 
423
463
  ## TanStack Start Integration
424
464
 
425
- | Pattern | Purpose |
426
- |---------|---------|
465
+ | 패턴 | 파일 |
466
+ |------|------|
427
467
  | API Handler | `src/routes/api/auth/$.ts` |
428
468
  | Server Function | `createServerFn` + `auth.api.getSession` |
429
469
  | Middleware | `auth.api.getSession` + redirect |
@@ -431,7 +471,7 @@ export const auth = betterAuth({
431
471
  ### Server Function Pattern
432
472
 
433
473
  ```typescript
434
- // ✅ Auth check Server Function
474
+ // ✅ 인증 체크
435
475
  export const requireAuth = createServerFn({ method: 'GET' })
436
476
  .handler(async ({ request }) => {
437
477
  const session = await auth.api.getSession({ headers: request.headers })
@@ -441,7 +481,7 @@ export const requireAuth = createServerFn({ method: 'GET' })
441
481
  return session.user
442
482
  })
443
483
 
444
- // ✅ Fetch protected data
484
+ // ✅ 보호된 데이터 조회
445
485
  export const getProtectedData = createServerFn({ method: 'GET' })
446
486
  .handler(async ({ request }) => {
447
487
  const user = await requireAuth()
@@ -465,23 +505,23 @@ export const Route = createFileRoute('/dashboard')({
465
505
 
466
506
  ## Common Patterns
467
507
 
468
- | Action | Pattern |
469
- |--------|---------|
470
- | Sign in | `authClient.signIn.email({ email, password })` |
471
- | Sign up | `authClient.signUp.email({ email, password, name })` |
472
- | Sign out | `authClient.signOut()` |
473
- | Get session | `authClient.getSession()` |
474
- | Update user | `authClient.updateUser({ name })` |
475
- | Reset password | `authClient.forgetPassword({ email })` → `authClient.resetPassword({ token, password })` |
508
+ | 작업 | 패턴 |
509
+ |------|------|
510
+ | 로그인 | `authClient.signIn.email({ email, password })` |
511
+ | 회원가입 | `authClient.signUp.email({ email, password, name })` |
512
+ | 로그아웃 | `authClient.signOut()` |
513
+ | 세션 조회 | `authClient.getSession()` |
514
+ | 사용자 업데이트 | `authClient.updateUser({ name })` |
515
+ | 비밀번호 재설정 | `authClient.forgetPassword({ email })` → `authClient.resetPassword({ token, password })` |
476
516
 
477
517
  ## Do's & Don'ts
478
518
 
479
519
  | ✅ Do | ❌ Don't |
480
520
  |-------|----------|
481
- | Use `auth.api.getSession()` on server | Directly manipulate sessions on client |
482
- | Use `authClient` on client | Hardcode secrets |
483
- | Manage secrets via environment variables | Store session tokens in localStorage |
484
- | HTTPS required in production | Deploy production with HTTP |
485
- | Configure `baseURL` per environment | Hardcode absolute paths |
521
+ | `auth.api.getSession()` 서버에서 사용 | 클라이언트에서 직접 세션 조작 |
522
+ | `authClient` 클라이언트에서 사용 | 하드코딩된 시크릿 |
523
+ | 환경변수로 시크릿 관리 | 세션 토큰 localStorage 저장 |
524
+ | HTTPS 프로덕션 필수 | HTTP 프로덕션 배포 |
525
+ | `baseURL` 환경별 설정 | 절대 경로 하드코딩 |
486
526
 
487
527
  </patterns>