@kood/claude-code 0.3.9 → 0.3.11
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 +1 -1
- package/package.json +1 -1
- package/templates/tanstack-start/docs/library/better-auth/index.md +225 -185
- package/templates/tanstack-start/docs/library/prisma/index.md +1025 -41
- package/templates/tanstack-start/docs/library/t3-env/index.md +207 -40
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +878 -42
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +602 -54
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +1334 -33
- package/templates/tanstack-start/docs/library/zod/index.md +674 -31
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
# t3-env
|
|
1
|
+
# t3-env
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> v1.x | Type-Safe Environment Variables
|
|
4
4
|
|
|
5
5
|
<context>
|
|
6
6
|
|
|
7
|
-
**Purpose:**
|
|
8
|
-
|
|
9
|
-
**Features:**
|
|
10
|
-
- Zod schema validation + type inference
|
|
11
|
-
- Prevent server variables from being exposed to client
|
|
12
|
-
- Transform & default value support
|
|
13
|
-
- Framework-agnostic
|
|
7
|
+
**Purpose:** Zod 기반 타입 안전 환경 변수 관리
|
|
8
|
+
**Features:** Server/Client 분리, 런타임 검증, Transform & Defaults, 프레임워크 무관
|
|
14
9
|
|
|
15
10
|
</context>
|
|
16
11
|
|
|
@@ -18,12 +13,12 @@
|
|
|
18
13
|
|
|
19
14
|
<forbidden>
|
|
20
15
|
|
|
21
|
-
|
|
|
16
|
+
| 분류 | 금지 |
|
|
22
17
|
|------|------|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
18
|
+
| **노출** | Server 변수를 클라이언트에 노출 |
|
|
19
|
+
| **접두사** | Client 변수에 `PUBLIC_` 없이 사용 |
|
|
20
|
+
| **직접 접근** | `process.env` 직접 사용 (env 객체 필수) |
|
|
21
|
+
| **타입** | any 타입으로 env 변수 접근 |
|
|
27
22
|
|
|
28
23
|
</forbidden>
|
|
29
24
|
|
|
@@ -31,18 +26,19 @@
|
|
|
31
26
|
|
|
32
27
|
<required>
|
|
33
28
|
|
|
34
|
-
|
|
|
29
|
+
| 분류 | 필수 |
|
|
35
30
|
|------|------|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
31
|
+
| **설치** | `@t3-oss/env-core zod` |
|
|
32
|
+
| **구조** | `src/env.ts` 파일 생성 |
|
|
33
|
+
| **접두사** | Client 변수: `PUBLIC_` 시작 |
|
|
39
34
|
| **Import** | `import { env } from '@/env'` |
|
|
35
|
+
| **검증** | Zod 스키마로 서버/클라이언트 변수 검증 |
|
|
40
36
|
|
|
41
37
|
</required>
|
|
42
38
|
|
|
43
39
|
---
|
|
44
40
|
|
|
45
|
-
<
|
|
41
|
+
<installation>
|
|
46
42
|
|
|
47
43
|
## Installation
|
|
48
44
|
|
|
@@ -50,6 +46,12 @@
|
|
|
50
46
|
npm install @t3-oss/env-core zod
|
|
51
47
|
```
|
|
52
48
|
|
|
49
|
+
</installation>
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<setup>
|
|
54
|
+
|
|
53
55
|
## Basic Setup
|
|
54
56
|
|
|
55
57
|
`src/env.ts`:
|
|
@@ -59,21 +61,21 @@ import { createEnv } from '@t3-oss/env-core'
|
|
|
59
61
|
import { z } from 'zod'
|
|
60
62
|
|
|
61
63
|
export const env = createEnv({
|
|
62
|
-
// Server-only variables
|
|
64
|
+
// ✅ Server-only variables
|
|
63
65
|
server: {
|
|
64
66
|
DATABASE_URL: z.url(),
|
|
65
67
|
CLERK_SECRET_KEY: z.string().min(1),
|
|
66
68
|
OPENAI_API_KEY: z.string().min(1),
|
|
67
69
|
},
|
|
68
70
|
|
|
69
|
-
// Client-exposed variables (PUBLIC_ prefix required)
|
|
71
|
+
// ✅ Client-exposed variables (PUBLIC_ prefix required)
|
|
70
72
|
clientPrefix: 'PUBLIC_',
|
|
71
73
|
client: {
|
|
72
74
|
PUBLIC_API_URL: z.url().default('http://localhost:3000'),
|
|
73
75
|
PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
74
76
|
},
|
|
75
77
|
|
|
76
|
-
// Runtime environment
|
|
78
|
+
// ✅ Runtime environment
|
|
77
79
|
runtimeEnv: {
|
|
78
80
|
// Server
|
|
79
81
|
DATABASE_URL: process.env.DATABASE_URL,
|
|
@@ -88,15 +90,23 @@ export const env = createEnv({
|
|
|
88
90
|
})
|
|
89
91
|
```
|
|
90
92
|
|
|
93
|
+
## Config Options
|
|
94
|
+
|
|
95
|
+
| 옵션 | 타입 | 설명 |
|
|
96
|
+
|------|------|------|
|
|
97
|
+
| `server` | `Record<string, ZodSchema>` | 서버 전용 변수 스키마 |
|
|
98
|
+
| `client` | `Record<string, ZodSchema>` | 클라이언트 공개 변수 스키마 |
|
|
99
|
+
| `clientPrefix` | `string` | 클라이언트 변수 접두사 (기본: `PUBLIC_`) |
|
|
100
|
+
| `runtimeEnv` | `Record<string, any>` | 실제 환경 변수 매핑 |
|
|
101
|
+
| `emptyStringAsUndefined` | `boolean` | 빈 문자열 → undefined 처리 |
|
|
102
|
+
|
|
91
103
|
</setup>
|
|
92
104
|
|
|
93
105
|
---
|
|
94
106
|
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
## Common Patterns
|
|
107
|
+
<usage>
|
|
98
108
|
|
|
99
|
-
|
|
109
|
+
## Server Function
|
|
100
110
|
|
|
101
111
|
```typescript
|
|
102
112
|
import { createServerFn } from '@tanstack/start'
|
|
@@ -104,44 +114,185 @@ import { env } from '@/env'
|
|
|
104
114
|
|
|
105
115
|
export const getUsers = createServerFn({ method: 'GET' }).handler(async () => {
|
|
106
116
|
const db = await prisma.$connect(env.DATABASE_URL)
|
|
107
|
-
// ^? string (
|
|
117
|
+
// ^? string (타입 안전)
|
|
108
118
|
return db.user.findMany()
|
|
109
119
|
})
|
|
110
120
|
```
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
## Client Component
|
|
113
123
|
|
|
114
124
|
```typescript
|
|
115
125
|
import { env } from '@/env'
|
|
116
126
|
|
|
117
127
|
export const ApiClient = () => {
|
|
118
128
|
const apiUrl = env.PUBLIC_API_URL
|
|
119
|
-
// ^? string (
|
|
129
|
+
// ^? string (타입 안전)
|
|
120
130
|
|
|
121
|
-
// ❌ Error: server
|
|
131
|
+
// ❌ Error: server 변수는 클라이언트에서 접근 불가
|
|
122
132
|
// const dbUrl = env.DATABASE_URL
|
|
123
133
|
}
|
|
124
134
|
```
|
|
125
135
|
|
|
126
|
-
|
|
136
|
+
</usage>
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
<validation>
|
|
141
|
+
|
|
142
|
+
## Validation Patterns
|
|
143
|
+
|
|
144
|
+
### Email
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
server: {
|
|
148
|
+
ADMIN_EMAIL: z.email(),
|
|
149
|
+
SUPPORT_EMAIL: z.email().default('support@example.com'),
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### URL
|
|
127
154
|
|
|
128
155
|
```typescript
|
|
129
156
|
server: {
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
API_URL: z.url(),
|
|
158
|
+
WEBHOOK_URL: z.url().optional(),
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Enum
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
server: {
|
|
166
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']),
|
|
167
|
+
DATABASE_PROVIDER: z.enum(['postgresql', 'mysql', 'sqlite']),
|
|
132
168
|
}
|
|
133
169
|
```
|
|
134
170
|
|
|
135
|
-
###
|
|
171
|
+
### Number
|
|
136
172
|
|
|
137
173
|
```typescript
|
|
138
174
|
server: {
|
|
175
|
+
PORT: z.coerce.number().positive().default(3000),
|
|
176
|
+
MAX_UPLOAD_SIZE: z.coerce.number().max(10485760), // 10MB
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Boolean
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
server: {
|
|
184
|
+
ENABLE_CACHE: z.coerce.boolean().default(true),
|
|
185
|
+
DEBUG_MODE: z.coerce.boolean().default(false),
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
</validation>
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
<examples>
|
|
194
|
+
|
|
195
|
+
## Database + Auth
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// src/env.ts
|
|
199
|
+
export const env = createEnv({
|
|
200
|
+
server: {
|
|
201
|
+
DATABASE_URL: z.url(),
|
|
202
|
+
DIRECT_URL: z.url().optional(), // Prisma connection pooling
|
|
203
|
+
CLERK_SECRET_KEY: z.string().min(1),
|
|
204
|
+
RESEND_API_KEY: z.string().min(1),
|
|
205
|
+
},
|
|
206
|
+
clientPrefix: 'PUBLIC_',
|
|
207
|
+
client: {
|
|
208
|
+
PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
|
|
209
|
+
},
|
|
210
|
+
runtimeEnv: {
|
|
211
|
+
DATABASE_URL: process.env.DATABASE_URL,
|
|
212
|
+
DIRECT_URL: process.env.DIRECT_URL,
|
|
213
|
+
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,
|
|
214
|
+
RESEND_API_KEY: process.env.RESEND_API_KEY,
|
|
215
|
+
PUBLIC_CLERK_PUBLISHABLE_KEY: import.meta.env.PUBLIC_CLERK_PUBLISHABLE_KEY,
|
|
216
|
+
},
|
|
217
|
+
emptyStringAsUndefined: true,
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## API Integration
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// src/env.ts
|
|
225
|
+
export const env = createEnv({
|
|
226
|
+
server: {
|
|
227
|
+
OPENAI_API_KEY: z.string().min(1),
|
|
228
|
+
STRIPE_SECRET_KEY: z.string().min(1),
|
|
229
|
+
STRIPE_WEBHOOK_SECRET: z.string().min(1),
|
|
230
|
+
},
|
|
231
|
+
clientPrefix: 'PUBLIC_',
|
|
232
|
+
client: {
|
|
233
|
+
PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
|
|
234
|
+
PUBLIC_APP_URL: z.url(),
|
|
235
|
+
},
|
|
236
|
+
runtimeEnv: {
|
|
237
|
+
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
|
238
|
+
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
|
|
239
|
+
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
|
|
240
|
+
PUBLIC_STRIPE_PUBLISHABLE_KEY: import.meta.env.PUBLIC_STRIPE_PUBLISHABLE_KEY,
|
|
241
|
+
PUBLIC_APP_URL: import.meta.env.PUBLIC_APP_URL,
|
|
242
|
+
},
|
|
243
|
+
emptyStringAsUndefined: true,
|
|
244
|
+
})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Multi-Environment
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// src/env.ts
|
|
251
|
+
export const env = createEnv({
|
|
252
|
+
server: {
|
|
253
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
254
|
+
DATABASE_URL: z.url(),
|
|
255
|
+
REDIS_URL: z.url().optional(), // Production only
|
|
256
|
+
},
|
|
257
|
+
clientPrefix: 'PUBLIC_',
|
|
258
|
+
client: {
|
|
259
|
+
PUBLIC_API_URL: z.url(),
|
|
260
|
+
PUBLIC_SENTRY_DSN: z.string().optional(), // Production only
|
|
261
|
+
},
|
|
262
|
+
runtimeEnv: {
|
|
263
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
264
|
+
DATABASE_URL: process.env.DATABASE_URL,
|
|
265
|
+
REDIS_URL: process.env.REDIS_URL,
|
|
266
|
+
PUBLIC_API_URL: import.meta.env.PUBLIC_API_URL,
|
|
267
|
+
PUBLIC_SENTRY_DSN: import.meta.env.PUBLIC_SENTRY_DSN,
|
|
268
|
+
},
|
|
269
|
+
emptyStringAsUndefined: true,
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
</examples>
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
<transforms>
|
|
278
|
+
|
|
279
|
+
## Transform Values
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
server: {
|
|
283
|
+
// ✅ String → Number
|
|
139
284
|
PORT: z.string().transform((val) => parseInt(val, 10)).pipe(z.number().positive()),
|
|
285
|
+
|
|
286
|
+
// ✅ Coerce number
|
|
140
287
|
MAX_CONNECTIONS: z.coerce.number().default(10),
|
|
288
|
+
|
|
289
|
+
// ✅ Environment-specific defaults
|
|
290
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
291
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
141
292
|
}
|
|
142
293
|
```
|
|
143
294
|
|
|
144
|
-
|
|
295
|
+
## Optional with Fallback
|
|
145
296
|
|
|
146
297
|
```typescript
|
|
147
298
|
client: {
|
|
@@ -150,19 +301,35 @@ client: {
|
|
|
150
301
|
}
|
|
151
302
|
```
|
|
152
303
|
|
|
153
|
-
</
|
|
304
|
+
</transforms>
|
|
154
305
|
|
|
155
306
|
---
|
|
156
307
|
|
|
157
308
|
<tips>
|
|
158
309
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
| Situation | Method |
|
|
310
|
+
| 상황 | 방법 |
|
|
162
311
|
|------|------|
|
|
163
312
|
| **Vercel** | `process.env.VERCEL_URL` → PUBLIC_APP_URL |
|
|
164
|
-
| **Monorepo** |
|
|
313
|
+
| **Monorepo** | 각 패키지마다 별도 `env.ts` |
|
|
165
314
|
| **Testing** | `.env.test` + `NODE_ENV=test` |
|
|
166
315
|
| **CI/CD** | GitHub Secrets → Environment Variables |
|
|
316
|
+
| **빌드 시점** | 빌드 시 모든 env 검증 완료 |
|
|
317
|
+
| **런타임 에러** | 잘못된 env 즉시 에러 발생 (앱 시작 전) |
|
|
167
318
|
|
|
168
319
|
</tips>
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
<patterns>
|
|
324
|
+
|
|
325
|
+
## Do's & Don'ts
|
|
326
|
+
|
|
327
|
+
| ✅ Do | ❌ Don't |
|
|
328
|
+
|-------|----------|
|
|
329
|
+
| `env.DATABASE_URL` 사용 | `process.env.DATABASE_URL` 직접 접근 |
|
|
330
|
+
| `PUBLIC_` 접두사 클라이언트 변수 | 접두사 없이 클라이언트 변수 |
|
|
331
|
+
| Zod 스키마로 타입 정의 | any 타입 사용 |
|
|
332
|
+
| 서버 변수는 서버에서만 | 클라이언트에 서버 변수 노출 |
|
|
333
|
+
| `emptyStringAsUndefined: true` | 빈 문자열 허용 |
|
|
334
|
+
|
|
335
|
+
</patterns>
|