@geekmidas/envkit 0.2.0 → 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.
Files changed (73) hide show
  1. package/dist/{EnvironmentBuilder-DfmYRBm-.mjs → EnvironmentBuilder-BSuHZm0y.mjs} +2 -4
  2. package/dist/EnvironmentBuilder-BSuHZm0y.mjs.map +1 -0
  3. package/dist/EnvironmentBuilder-DHfDXJUm.d.mts.map +1 -0
  4. package/dist/{EnvironmentBuilder-W2wku49g.cjs → EnvironmentBuilder-Djr1VsWM.cjs} +2 -4
  5. package/dist/EnvironmentBuilder-Djr1VsWM.cjs.map +1 -0
  6. package/dist/EnvironmentBuilder-Xuf2Dd9u.d.cts.map +1 -0
  7. package/dist/EnvironmentBuilder.cjs +1 -1
  8. package/dist/EnvironmentBuilder.mjs +1 -1
  9. package/dist/EnvironmentParser-Bt246UeP.cjs.map +1 -1
  10. package/dist/{EnvironmentParser-CVWU1ooT.d.mts → EnvironmentParser-CY8TosTN.d.mts} +2 -1
  11. package/dist/EnvironmentParser-CY8TosTN.d.mts.map +1 -0
  12. package/dist/{EnvironmentParser-tV-JjCg7.d.cts → EnvironmentParser-DtOL86NU.d.cts} +2 -1
  13. package/dist/EnvironmentParser-DtOL86NU.d.cts.map +1 -0
  14. package/dist/EnvironmentParser-c06agx31.mjs.map +1 -1
  15. package/dist/EnvironmentParser.d.cts +1 -1
  16. package/dist/EnvironmentParser.d.mts +1 -1
  17. package/dist/SnifferEnvironmentParser.cjs.map +1 -1
  18. package/dist/SnifferEnvironmentParser.d.cts +3 -2
  19. package/dist/SnifferEnvironmentParser.d.cts.map +1 -0
  20. package/dist/SnifferEnvironmentParser.d.mts +3 -2
  21. package/dist/SnifferEnvironmentParser.d.mts.map +1 -0
  22. package/dist/SnifferEnvironmentParser.mjs.map +1 -1
  23. package/dist/{SstEnvironmentBuilder-DEa3lTUB.mjs → SstEnvironmentBuilder-BEBFSUYr.mjs} +2 -2
  24. package/dist/SstEnvironmentBuilder-BEBFSUYr.mjs.map +1 -0
  25. package/dist/SstEnvironmentBuilder-CjURMGjW.d.mts.map +1 -0
  26. package/dist/SstEnvironmentBuilder-D4oSo_KX.d.cts.map +1 -0
  27. package/dist/{SstEnvironmentBuilder-BuFw1hCe.cjs → SstEnvironmentBuilder-wFnN2M5O.cjs} +2 -2
  28. package/dist/SstEnvironmentBuilder-wFnN2M5O.cjs.map +1 -0
  29. package/dist/SstEnvironmentBuilder.cjs +2 -2
  30. package/dist/SstEnvironmentBuilder.mjs +2 -2
  31. package/dist/credentials.cjs +66 -0
  32. package/dist/credentials.cjs.map +1 -0
  33. package/dist/credentials.d.cts +31 -0
  34. package/dist/credentials.d.cts.map +1 -0
  35. package/dist/credentials.d.mts +31 -0
  36. package/dist/credentials.d.mts.map +1 -0
  37. package/dist/credentials.mjs +62 -0
  38. package/dist/credentials.mjs.map +1 -0
  39. package/dist/index.cjs +1 -1
  40. package/dist/index.d.cts +1 -1
  41. package/dist/index.d.mts +1 -1
  42. package/dist/index.mjs +1 -1
  43. package/dist/sst.cjs +2 -2
  44. package/dist/sst.cjs.map +1 -1
  45. package/dist/sst.d.cts +1 -0
  46. package/dist/sst.d.cts.map +1 -0
  47. package/dist/sst.d.mts +1 -0
  48. package/dist/sst.d.mts.map +1 -0
  49. package/dist/sst.mjs +2 -2
  50. package/dist/sst.mjs.map +1 -1
  51. package/examples/basic-usage.ts +329 -333
  52. package/package.json +6 -1
  53. package/src/EnvironmentBuilder.ts +76 -80
  54. package/src/EnvironmentParser.ts +231 -231
  55. package/src/SnifferEnvironmentParser.ts +178 -178
  56. package/src/SstEnvironmentBuilder.ts +127 -127
  57. package/src/__tests__/ConfigParser.spec.ts +388 -388
  58. package/src/__tests__/EnvironmentBuilder.spec.ts +245 -265
  59. package/src/__tests__/EnvironmentParser.spec.ts +828 -828
  60. package/src/__tests__/SnifferEnvironmentParser.spec.ts +380 -326
  61. package/src/__tests__/SstEnvironmentBuilder.spec.ts +347 -367
  62. package/src/__tests__/credentials.integration.spec.ts +239 -0
  63. package/src/__tests__/credentials.spec.ts +136 -0
  64. package/src/__tests__/sst.spec.ts +390 -413
  65. package/src/credentials.ts +99 -0
  66. package/src/index.ts +11 -11
  67. package/src/sst.ts +24 -24
  68. package/sst-env.d.ts +0 -1
  69. package/tsconfig.json +9 -0
  70. package/dist/EnvironmentBuilder-DfmYRBm-.mjs.map +0 -1
  71. package/dist/EnvironmentBuilder-W2wku49g.cjs.map +0 -1
  72. package/dist/SstEnvironmentBuilder-BuFw1hCe.cjs.map +0 -1
  73. package/dist/SstEnvironmentBuilder-DEa3lTUB.mjs.map +0 -1
@@ -4,386 +4,382 @@ import { z } from 'zod';
4
4
  const parser = new EnvironmentParser(process.env as {});
5
5
  // Example 1: Basic configuration
6
6
  export function basicExample() {
7
- const config = parser.create((get) => ({
8
- appName: get('APP_NAME').string().default('My App'),
9
- port: get('PORT').string().transform(Number).default(3000),
10
- isDevelopment: get('NODE_ENV')
11
- .string()
12
- .transform((env) => env === 'development'),
13
- }));
7
+ const config = parser.create((get) => ({
8
+ appName: get('APP_NAME').string().default('My App'),
9
+ port: get('PORT').string().transform(Number).default(3000),
10
+ isDevelopment: get('NODE_ENV')
11
+ .string()
12
+ .transform((env) => env === 'development'),
13
+ }));
14
14
 
15
- const result = config.parse();
15
+ const result = config.parse();
16
16
 
17
- return result;
17
+ return result;
18
18
  }
19
19
 
20
20
  // Example 2: Database configuration
21
21
  export function databaseExample() {
22
- const config = parser.create((get) => ({
23
- database: {
24
- host: get('DB_HOST').string().default('localhost'),
25
- port: get('DB_PORT').string().transform(Number).default(5432),
26
- name: get('DB_NAME').string(),
27
- user: get('DB_USER').string(),
28
- password: get('DB_PASSWORD').string(),
29
- ssl: get('DB_SSL')
30
- .string()
31
- .transform((v) => v === 'true')
32
- .default(false),
33
- poolSize: get('DB_POOL_SIZE')
34
- .string()
35
- .transform(Number)
36
- .int()
37
- .min(1)
38
- .max(100)
39
- .default(10),
40
- },
41
- }));
42
-
43
- return config.parse();
22
+ const config = parser.create((get) => ({
23
+ database: {
24
+ host: get('DB_HOST').string().default('localhost'),
25
+ port: get('DB_PORT').string().transform(Number).default(5432),
26
+ name: get('DB_NAME').string(),
27
+ user: get('DB_USER').string(),
28
+ password: get('DB_PASSWORD').string(),
29
+ ssl: get('DB_SSL')
30
+ .string()
31
+ .transform((v) => v === 'true')
32
+ .default(false),
33
+ poolSize: get('DB_POOL_SIZE')
34
+ .string()
35
+ .transform(Number)
36
+ .int()
37
+ .min(1)
38
+ .max(100)
39
+ .default(10),
40
+ },
41
+ }));
42
+
43
+ return config.parse();
44
44
  }
45
45
 
46
46
  // Example 3: API configuration with validation
47
47
  export function apiConfigExample() {
48
- const config = parser.create((get) => ({
49
- api: {
50
- baseUrl: get('API_BASE_URL').url(),
51
- key: get('API_KEY').string().min(32),
52
- secret: get('API_SECRET').string().min(64),
53
- timeout: get('API_TIMEOUT').string().transform(Number).default(5000),
54
- retries: get('API_RETRIES').string().transform(Number).default(3),
55
- endpoints: {
56
- users: get('API_ENDPOINT_USERS').string().default('/api/v1/users'),
57
- auth: get('API_ENDPOINT_AUTH').string().default('/api/v1/auth'),
58
- products: get('API_ENDPOINT_PRODUCTS')
59
- .string()
60
- .default('/api/v1/products'),
61
- },
62
- },
63
- }));
64
-
65
- return config.parse();
48
+ const config = parser.create((get) => ({
49
+ api: {
50
+ baseUrl: get('API_BASE_URL').url(),
51
+ key: get('API_KEY').string().min(32),
52
+ secret: get('API_SECRET').string().min(64),
53
+ timeout: get('API_TIMEOUT').string().transform(Number).default(5000),
54
+ retries: get('API_RETRIES').string().transform(Number).default(3),
55
+ endpoints: {
56
+ users: get('API_ENDPOINT_USERS').string().default('/api/v1/users'),
57
+ auth: get('API_ENDPOINT_AUTH').string().default('/api/v1/auth'),
58
+ products: get('API_ENDPOINT_PRODUCTS')
59
+ .string()
60
+ .default('/api/v1/products'),
61
+ },
62
+ },
63
+ }));
64
+
65
+ return config.parse();
66
66
  }
67
67
 
68
68
  // Example 4: Feature flags and complex validation
69
69
  export function featureFlagsExample() {
70
- const config = parser.create((get) => ({
71
- features: {
72
- authentication: get('FEATURE_AUTH')
73
- .string()
74
- .transform((v) => v === 'true')
75
- .default(true),
76
- rateLimit: get('FEATURE_RATE_LIMIT')
77
- .string()
78
- .transform((v) => v === 'true')
79
- .default(true),
80
- cache: get('FEATURE_CACHE')
81
- .string()
82
- .transform((v) => v === 'true')
83
- .default(false),
84
- beta: {
85
- enabled: get('FEATURE_BETA')
86
- .string()
87
- .transform((v) => v === 'true')
88
- .default(false),
89
- allowedUsers: get('FEATURE_BETA_USERS')
90
- .string()
91
- .transform((users) =>
92
- users ? users.split(',').map((u) => u.trim()) : [],
93
- )
94
- .default([]),
95
- },
96
- },
97
- rateLimit: {
98
- windowMs: get('RATE_LIMIT_WINDOW_MS')
99
- .string()
100
- .transform(Number)
101
- .default(60000),
102
- maxRequests: get('RATE_LIMIT_MAX_REQUESTS')
103
- .string()
104
- .transform(Number)
105
- .default(100),
106
- },
107
- }));
108
-
109
- return config.parse();
70
+ const config = parser.create((get) => ({
71
+ features: {
72
+ authentication: get('FEATURE_AUTH')
73
+ .string()
74
+ .transform((v) => v === 'true')
75
+ .default(true),
76
+ rateLimit: get('FEATURE_RATE_LIMIT')
77
+ .string()
78
+ .transform((v) => v === 'true')
79
+ .default(true),
80
+ cache: get('FEATURE_CACHE')
81
+ .string()
82
+ .transform((v) => v === 'true')
83
+ .default(false),
84
+ beta: {
85
+ enabled: get('FEATURE_BETA')
86
+ .string()
87
+ .transform((v) => v === 'true')
88
+ .default(false),
89
+ allowedUsers: get('FEATURE_BETA_USERS')
90
+ .string()
91
+ .transform((users) =>
92
+ users ? users.split(',').map((u) => u.trim()) : [],
93
+ )
94
+ .default([]),
95
+ },
96
+ },
97
+ rateLimit: {
98
+ windowMs: get('RATE_LIMIT_WINDOW_MS')
99
+ .string()
100
+ .transform(Number)
101
+ .default(60000),
102
+ maxRequests: get('RATE_LIMIT_MAX_REQUESTS')
103
+ .string()
104
+ .transform(Number)
105
+ .default(100),
106
+ },
107
+ }));
108
+
109
+ return config.parse();
110
110
  }
111
111
 
112
112
  // Example 5: Email configuration with refinements
113
113
  export function emailConfigExample() {
114
- const config = parser.create((get) => ({
115
- email: {
116
- provider: get('EMAIL_PROVIDER').enum(['sendgrid', 'mailgun', 'ses']),
117
- apiKey: get('EMAIL_API_KEY').string().min(20),
118
- from: {
119
- name: get('EMAIL_FROM_NAME').string().default('Support Team'),
120
- address: get('EMAIL_FROM_ADDRESS').string().email(),
121
- },
122
- replyTo: get('EMAIL_REPLY_TO').string().email().optional(),
123
- templates: {
124
- welcome: get('EMAIL_TEMPLATE_WELCOME').string().uuid(),
125
- resetPassword: get('EMAIL_TEMPLATE_RESET_PASSWORD').string().uuid(),
126
- invoice: get('EMAIL_TEMPLATE_INVOICE').string().uuid().optional(),
127
- },
128
- smtp: {
129
- host: get('SMTP_HOST').string().optional(),
130
- port: get('SMTP_PORT').string().transform(Number).optional(),
131
- secure: get('SMTP_SECURE')
132
- .string()
133
- .transform((v) => v === 'true')
134
- .default(true),
135
- },
136
- },
137
- }));
138
-
139
- return config.parse();
114
+ const config = parser.create((get) => ({
115
+ email: {
116
+ provider: get('EMAIL_PROVIDER').enum(['sendgrid', 'mailgun', 'ses']),
117
+ apiKey: get('EMAIL_API_KEY').string().min(20),
118
+ from: {
119
+ name: get('EMAIL_FROM_NAME').string().default('Support Team'),
120
+ address: get('EMAIL_FROM_ADDRESS').string().email(),
121
+ },
122
+ replyTo: get('EMAIL_REPLY_TO').string().email().optional(),
123
+ templates: {
124
+ welcome: get('EMAIL_TEMPLATE_WELCOME').string().uuid(),
125
+ resetPassword: get('EMAIL_TEMPLATE_RESET_PASSWORD').string().uuid(),
126
+ invoice: get('EMAIL_TEMPLATE_INVOICE').string().uuid().optional(),
127
+ },
128
+ smtp: {
129
+ host: get('SMTP_HOST').string().optional(),
130
+ port: get('SMTP_PORT').string().transform(Number).optional(),
131
+ secure: get('SMTP_SECURE')
132
+ .string()
133
+ .transform((v) => v === 'true')
134
+ .default(true),
135
+ },
136
+ },
137
+ }));
138
+
139
+ return config.parse();
140
140
  }
141
141
 
142
142
  // Example 6: Multi-environment configuration
143
143
  export function multiEnvironmentExample() {
144
- const env = process.env.NODE_ENV || 'development';
145
-
146
- // Different config sources based on environment
147
- const configSource = {
148
- ...process.env,
149
- // Override with environment-specific values
150
- ...(env === 'production'
151
- ? {
152
- LOG_LEVEL: 'error',
153
- DEBUG: 'false',
154
- }
155
- : {
156
- LOG_LEVEL: 'debug',
157
- DEBUG: 'true',
158
- }),
159
- };
160
-
161
- const parser = new EnvironmentParser(configSource);
162
-
163
- const config = parser.create((get) => ({
164
- env: get('NODE_ENV')
165
- .enum(['development', 'staging', 'production'])
166
- .default('development'),
167
- logging: {
168
- level: get('LOG_LEVEL').enum([
169
- 'trace',
170
- 'debug',
171
- 'info',
172
- 'warn',
173
- 'error',
174
- 'fatal',
175
- ]),
176
- pretty: get('LOG_PRETTY')
177
- .string()
178
- .transform((v) => v === 'true')
179
- .default(env !== 'production'),
180
- debug: get('DEBUG')
181
- .string()
182
- .transform((v) => v === 'true'),
183
- },
184
- server: {
185
- host: get('HOST').string().default('0.0.0.0'),
186
- port: get('PORT')
187
- .string()
188
- .transform(Number)
189
- .default(env === 'production' ? 80 : 3000),
190
- cors: {
191
- enabled: get('CORS_ENABLED')
192
- .string()
193
- .transform((v) => v === 'true')
194
- .default(true),
195
- origins: get('CORS_ORIGINS')
196
- .string()
197
- .transform((origins) => origins.split(',').map((o) => o.trim()))
198
- .refine((origins) => origins.every((o) => o.startsWith('http')), {
199
- message: 'All CORS origins must be valid URLs',
200
- })
201
- .default(['http://localhost:3000']),
202
- },
203
- },
204
- }));
205
-
206
- return config.parse();
144
+ const env = process.env.NODE_ENV || 'development';
145
+
146
+ // Different config sources based on environment
147
+ const configSource = {
148
+ ...process.env,
149
+ // Override with environment-specific values
150
+ ...(env === 'production'
151
+ ? {
152
+ LOG_LEVEL: 'error',
153
+ DEBUG: 'false',
154
+ }
155
+ : {
156
+ LOG_LEVEL: 'debug',
157
+ DEBUG: 'true',
158
+ }),
159
+ };
160
+
161
+ const parser = new EnvironmentParser(configSource);
162
+
163
+ const config = parser.create((get) => ({
164
+ env: get('NODE_ENV')
165
+ .enum(['development', 'staging', 'production'])
166
+ .default('development'),
167
+ logging: {
168
+ level: get('LOG_LEVEL').enum([
169
+ 'trace',
170
+ 'debug',
171
+ 'info',
172
+ 'warn',
173
+ 'error',
174
+ 'fatal',
175
+ ]),
176
+ pretty: get('LOG_PRETTY')
177
+ .string()
178
+ .transform((v) => v === 'true')
179
+ .default(env !== 'production'),
180
+ debug: get('DEBUG')
181
+ .string()
182
+ .transform((v) => v === 'true'),
183
+ },
184
+ server: {
185
+ host: get('HOST').string().default('0.0.0.0'),
186
+ port: get('PORT')
187
+ .string()
188
+ .transform(Number)
189
+ .default(env === 'production' ? 80 : 3000),
190
+ cors: {
191
+ enabled: get('CORS_ENABLED')
192
+ .string()
193
+ .transform((v) => v === 'true')
194
+ .default(true),
195
+ origins: get('CORS_ORIGINS')
196
+ .string()
197
+ .transform((origins) => origins.split(',').map((o) => o.trim()))
198
+ .refine((origins) => origins.every((o) => o.startsWith('http')), {
199
+ message: 'All CORS origins must be valid URLs',
200
+ })
201
+ .default(['http://localhost:3000']),
202
+ },
203
+ },
204
+ }));
205
+
206
+ return config.parse();
207
207
  }
208
208
 
209
209
  // Example 7: Error handling
210
210
  export function errorHandlingExample() {
211
- try {
212
- const config = parser
213
- .create((get) => ({
214
- required: {
215
- apiKey: get('API_KEY').string().min(32),
216
- databaseUrl: get('DATABASE_URL').string().url(),
217
- adminEmail: get('ADMIN_EMAIL').string().email(),
218
- },
219
- optional: {
220
- sentryDsn: get('SENTRY_DSN').string().url().optional(),
221
- slackWebhook: get('SLACK_WEBHOOK').string().url().optional(),
222
- },
223
- }))
224
- .parse();
225
-
226
- return config;
227
- } catch (error) {
228
- if (error instanceof z.ZodError) {
229
- console.error('Configuration validation failed:');
230
- console.error('Missing or invalid environment variables:');
231
-
232
- error.errors.forEach((err) => {
233
- const path = err.path.join('.');
234
- console.error(` ${path}: ${err.message}`);
235
- });
236
-
237
- // In a real app, you might want to exit
238
- process.exit(1);
239
- }
240
- throw error;
241
- }
211
+ try {
212
+ const config = parser
213
+ .create((get) => ({
214
+ required: {
215
+ apiKey: get('API_KEY').string().min(32),
216
+ databaseUrl: get('DATABASE_URL').string().url(),
217
+ adminEmail: get('ADMIN_EMAIL').string().email(),
218
+ },
219
+ optional: {
220
+ sentryDsn: get('SENTRY_DSN').string().url().optional(),
221
+ slackWebhook: get('SLACK_WEBHOOK').string().url().optional(),
222
+ },
223
+ }))
224
+ .parse();
225
+
226
+ return config;
227
+ } catch (error) {
228
+ if (error instanceof z.ZodError) {
229
+ error.errors.forEach((err) => {
230
+ const _path = err.path.join('.');
231
+ });
232
+
233
+ // In a real app, you might want to exit
234
+ process.exit(1);
235
+ }
236
+ throw error;
237
+ }
242
238
  }
243
239
 
244
240
  // Example 8: Using with dotenv
245
241
  export function dotenvExample() {
246
- // Load .env file
247
- require('dotenv').config();
248
-
249
- const config = parser.create((get) => ({
250
- app: {
251
- name: get('APP_NAME').string(),
252
- version: get('APP_VERSION')
253
- .string()
254
- .regex(/^\d+\.\d+\.\d+$/),
255
- description: get('APP_DESCRIPTION').string().optional(),
256
- },
257
- secrets: {
258
- jwtSecret: get('JWT_SECRET').string().min(64),
259
- encryptionKey: get('ENCRYPTION_KEY').string().length(32),
260
- apiKeys: get('API_KEYS')
261
- .string()
262
- .transform((keys) => keys.split(',').map((k) => k.trim()))
263
- .pipe(z.array(z.string().min(32))),
264
- },
265
- }));
266
-
267
- return config.parse();
242
+ // Load .env file
243
+ require('dotenv').config();
244
+
245
+ const config = parser.create((get) => ({
246
+ app: {
247
+ name: get('APP_NAME').string(),
248
+ version: get('APP_VERSION')
249
+ .string()
250
+ .regex(/^\d+\.\d+\.\d+$/),
251
+ description: get('APP_DESCRIPTION').string().optional(),
252
+ },
253
+ secrets: {
254
+ jwtSecret: get('JWT_SECRET').string().min(64),
255
+ encryptionKey: get('ENCRYPTION_KEY').string().length(32),
256
+ apiKeys: get('API_KEYS')
257
+ .string()
258
+ .transform((keys) => keys.split(',').map((k) => k.trim()))
259
+ .pipe(z.array(z.string().min(32))),
260
+ },
261
+ }));
262
+
263
+ return config.parse();
268
264
  }
269
265
 
270
266
  // Example 9: Custom transformations
271
267
  export function customTransformationsExample() {
272
- const config = parser.create((get) => ({
273
- // Parse JSON
274
- features: get('FEATURES_JSON')
275
- .string()
276
- .transform((str) => JSON.parse(str))
277
- .pipe(z.record(z.boolean())),
278
-
279
- // Parse duration strings
280
- timeouts: {
281
- request: get('TIMEOUT_REQUEST')
282
- .string()
283
- .transform(parseDuration)
284
- .default('30s'),
285
- idle: get('TIMEOUT_IDLE').string().transform(parseDuration).default('5m'),
286
- },
287
-
288
- // Parse memory sizes
289
- limits: {
290
- memory: get('MEMORY_LIMIT')
291
- .string()
292
- .transform(parseMemorySize)
293
- .default('512MB'),
294
- upload: get('UPLOAD_LIMIT')
295
- .string()
296
- .transform(parseMemorySize)
297
- .default('10MB'),
298
- },
299
-
300
- // Complex array parsing
301
- allowedDomains: get('ALLOWED_DOMAINS')
302
- .string()
303
- .transform((domains) =>
304
- domains
305
- .split(',')
306
- .map((d) => d.trim())
307
- .filter(Boolean),
308
- )
309
- .pipe(z.array(z.string().regex(/^[a-z0-9.-]+$/i))),
310
- }));
311
-
312
- return config.parse();
268
+ const config = parser.create((get) => ({
269
+ // Parse JSON
270
+ features: get('FEATURES_JSON')
271
+ .string()
272
+ .transform((str) => JSON.parse(str))
273
+ .pipe(z.record(z.boolean())),
274
+
275
+ // Parse duration strings
276
+ timeouts: {
277
+ request: get('TIMEOUT_REQUEST')
278
+ .string()
279
+ .transform(parseDuration)
280
+ .default('30s'),
281
+ idle: get('TIMEOUT_IDLE').string().transform(parseDuration).default('5m'),
282
+ },
283
+
284
+ // Parse memory sizes
285
+ limits: {
286
+ memory: get('MEMORY_LIMIT')
287
+ .string()
288
+ .transform(parseMemorySize)
289
+ .default('512MB'),
290
+ upload: get('UPLOAD_LIMIT')
291
+ .string()
292
+ .transform(parseMemorySize)
293
+ .default('10MB'),
294
+ },
295
+
296
+ // Complex array parsing
297
+ allowedDomains: get('ALLOWED_DOMAINS')
298
+ .string()
299
+ .transform((domains) =>
300
+ domains
301
+ .split(',')
302
+ .map((d) => d.trim())
303
+ .filter(Boolean),
304
+ )
305
+ .pipe(z.array(z.string().regex(/^[a-z0-9.-]+$/i))),
306
+ }));
307
+
308
+ return config.parse();
313
309
  }
314
310
 
315
311
  // Helper functions for custom transformations
316
312
  function parseDuration(duration: string): number {
317
- const match = duration.match(/^(\d+)(ms|s|m|h)$/);
318
- if (!match) throw new Error(`Invalid duration: ${duration}`);
313
+ const match = duration.match(/^(\d+)(ms|s|m|h)$/);
314
+ if (!match) throw new Error(`Invalid duration: ${duration}`);
319
315
 
320
- const [, value, unit] = match;
321
- const multipliers = { ms: 1, s: 1000, m: 60000, h: 3600000 };
322
- return parseInt(value) * multipliers[unit as keyof typeof multipliers];
316
+ const [, value, unit] = match;
317
+ const multipliers = { ms: 1, s: 1000, m: 60000, h: 3600000 };
318
+ return parseInt(value, 10) * multipliers[unit as keyof typeof multipliers];
323
319
  }
324
320
 
325
321
  function parseMemorySize(size: string): number {
326
- const match = size.match(/^(\d+)(B|KB|MB|GB)$/i);
327
- if (!match) throw new Error(`Invalid memory size: ${size}`);
328
-
329
- const [, value, unit] = match;
330
- const multipliers = { B: 1, KB: 1024, MB: 1048576, GB: 1073741824 };
331
- return (
332
- parseInt(value) *
333
- multipliers[unit.toUpperCase() as keyof typeof multipliers]
334
- );
322
+ const match = size.match(/^(\d+)(B|KB|MB|GB)$/i);
323
+ if (!match) throw new Error(`Invalid memory size: ${size}`);
324
+
325
+ const [, value, unit] = match;
326
+ const multipliers = { B: 1, KB: 1024, MB: 1048576, GB: 1073741824 };
327
+ return (
328
+ parseInt(value, 10) *
329
+ multipliers[unit.toUpperCase() as keyof typeof multipliers]
330
+ );
335
331
  }
336
332
 
337
333
  // Example 10: Type-safe configuration module
338
334
  // config.ts - This is how you'd typically use it in a real app
339
335
  export const loadConfig = () => {
340
- const parser = new EnvironmentParser(process.env as any);
341
-
342
- return parser
343
- .create((get) => ({
344
- app: {
345
- name: get('APP_NAME').string().default('My Application'),
346
- env: get('NODE_ENV')
347
- .enum(['development', 'staging', 'production'])
348
- .default('development'),
349
- port: get('PORT').string().transform(Number).default(3000),
350
- host: get('HOST').string().default('localhost'),
351
- },
352
- database: {
353
- url: get('DATABASE_URL').string().url(),
354
- maxConnections: get('DB_MAX_CONNECTIONS')
355
- .string()
356
- .transform(Number)
357
- .default(10),
358
- ssl: get('DB_SSL')
359
- .string()
360
- .transform((v) => v === 'true')
361
- .default(false),
362
- },
363
- redis: {
364
- url: get('REDIS_URL').string().url().optional(),
365
- ttl: get('REDIS_TTL').string().transform(Number).default(3600),
366
- },
367
- auth: {
368
- jwtSecret: get('JWT_SECRET').string().min(32),
369
- jwtExpiry: get('JWT_EXPIRY').string().default('7d'),
370
- bcryptRounds: get('BCRYPT_ROUNDS')
371
- .string()
372
- .transform(Number)
373
- .default(10),
374
- },
375
- features: {
376
- signups: get('FEATURE_SIGNUPS')
377
- .string()
378
- .transform((v) => v === 'true')
379
- .default(true),
380
- subscriptions: get('FEATURE_SUBSCRIPTIONS')
381
- .string()
382
- .transform((v) => v === 'true')
383
- .default(false),
384
- },
385
- }))
386
- .parse();
336
+ const parser = new EnvironmentParser(process.env as any);
337
+
338
+ return parser
339
+ .create((get) => ({
340
+ app: {
341
+ name: get('APP_NAME').string().default('My Application'),
342
+ env: get('NODE_ENV')
343
+ .enum(['development', 'staging', 'production'])
344
+ .default('development'),
345
+ port: get('PORT').string().transform(Number).default(3000),
346
+ host: get('HOST').string().default('localhost'),
347
+ },
348
+ database: {
349
+ url: get('DATABASE_URL').string().url(),
350
+ maxConnections: get('DB_MAX_CONNECTIONS')
351
+ .string()
352
+ .transform(Number)
353
+ .default(10),
354
+ ssl: get('DB_SSL')
355
+ .string()
356
+ .transform((v) => v === 'true')
357
+ .default(false),
358
+ },
359
+ redis: {
360
+ url: get('REDIS_URL').string().url().optional(),
361
+ ttl: get('REDIS_TTL').string().transform(Number).default(3600),
362
+ },
363
+ auth: {
364
+ jwtSecret: get('JWT_SECRET').string().min(32),
365
+ jwtExpiry: get('JWT_EXPIRY').string().default('7d'),
366
+ bcryptRounds: get('BCRYPT_ROUNDS')
367
+ .string()
368
+ .transform(Number)
369
+ .default(10),
370
+ },
371
+ features: {
372
+ signups: get('FEATURE_SIGNUPS')
373
+ .string()
374
+ .transform((v) => v === 'true')
375
+ .default(true),
376
+ subscriptions: get('FEATURE_SUBSCRIPTIONS')
377
+ .string()
378
+ .transform((v) => v === 'true')
379
+ .default(false),
380
+ },
381
+ }))
382
+ .parse();
387
383
  };
388
384
 
389
385
  // Export the config for use throughout the app