create-fluxstack 1.0.22 → 1.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.
- package/app/server/backend-only.ts +5 -5
- package/app/server/index.ts +63 -54
- package/app/server/live/FluxStackConfig.ts +43 -39
- package/app/server/live/SystemMonitorIntegration.ts +2 -2
- package/app/server/live/register-components.ts +6 -26
- package/app/server/middleware/errorHandling.ts +6 -4
- package/app/server/routes/config.ts +145 -0
- package/app/server/routes/index.ts +5 -3
- package/config/app.config.ts +113 -0
- package/config/build.config.ts +24 -0
- package/config/database.config.ts +99 -0
- package/config/index.ts +68 -0
- package/config/logger.config.ts +27 -0
- package/config/runtime.config.ts +92 -0
- package/config/server.config.ts +46 -0
- package/config/services.config.ts +130 -0
- package/config/system.config.ts +105 -0
- package/core/build/bundler.ts +53 -5
- package/core/build/flux-plugins-generator.ts +315 -0
- package/core/build/index.ts +11 -7
- package/core/build/live-components-generator.ts +231 -0
- package/core/build/optimizer.ts +2 -54
- package/core/cli/index.ts +31 -13
- package/core/config/env.ts +38 -94
- package/core/config/runtime-config.ts +61 -58
- package/core/config/schema.ts +1 -0
- package/core/framework/server.ts +55 -11
- package/core/plugins/built-in/index.ts +7 -17
- package/core/plugins/built-in/static/index.ts +24 -10
- package/core/plugins/built-in/swagger/index.ts +228 -228
- package/core/plugins/built-in/vite/index.ts +374 -358
- package/core/plugins/dependency-manager.ts +5 -5
- package/core/plugins/manager.ts +57 -14
- package/core/plugins/registry.ts +3 -3
- package/core/server/index.ts +0 -1
- package/core/server/live/ComponentRegistry.ts +34 -8
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/websocket-plugin.ts +434 -434
- package/core/server/middleware/README.md +488 -0
- package/core/server/middleware/elysia-helpers.ts +227 -0
- package/core/server/middleware/index.ts +25 -9
- package/core/server/plugins/static-files-plugin.ts +231 -231
- package/core/utils/config-schema.ts +484 -0
- package/core/utils/env.ts +306 -0
- package/core/utils/helpers.ts +9 -3
- package/core/utils/logger/colors.ts +114 -0
- package/core/utils/logger/config.ts +35 -0
- package/core/utils/logger/formatter.ts +82 -0
- package/core/utils/logger/group-logger.ts +101 -0
- package/core/utils/logger/index.ts +199 -250
- package/core/utils/logger/stack-trace.ts +92 -0
- package/core/utils/logger/startup-banner.ts +92 -0
- package/core/utils/logger/winston-logger.ts +152 -0
- package/core/utils/version.ts +5 -0
- package/create-fluxstack.ts +1 -0
- package/fluxstack.config.ts +6 -12
- package/package.json +117 -114
- package/plugins/crypto-auth/README.md +238 -0
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +325 -0
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +190 -0
- package/plugins/crypto-auth/client/components/LoginButton.tsx +155 -0
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +109 -0
- package/plugins/crypto-auth/client/components/SessionInfo.tsx +242 -0
- package/plugins/crypto-auth/client/components/index.ts +15 -0
- package/plugins/crypto-auth/client/index.ts +12 -0
- package/plugins/crypto-auth/index.ts +230 -0
- package/plugins/crypto-auth/package.json +65 -0
- package/plugins/crypto-auth/plugin.json +29 -0
- package/plugins/crypto-auth/server/AuthMiddleware.ts +237 -0
- package/plugins/crypto-auth/server/CryptoAuthService.ts +293 -0
- package/plugins/crypto-auth/server/index.ts +9 -0
- package/vite.config.ts +16 -0
- package/core/config/env-dynamic.ts +0 -326
- package/core/plugins/built-in/logger/index.ts +0 -180
- package/core/server/plugins/logger.ts +0 -47
- package/core/utils/env-runtime-v2.ts +0 -232
- package/core/utils/env-runtime.ts +0 -259
- package/core/utils/logger/formatters.ts +0 -222
- package/core/utils/logger/middleware.ts +0 -253
- package/core/utils/logger/performance.ts +0 -384
- package/core/utils/logger/transports.ts +0 -365
- package/core/utils/logger.ts +0 -106
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚡ FluxStack Config Schema System
|
|
3
|
+
*
|
|
4
|
+
* Laravel-inspired declarative configuration system with:
|
|
5
|
+
* - Schema-based config declaration
|
|
6
|
+
* - Automatic validation
|
|
7
|
+
* - Type casting
|
|
8
|
+
* - Default values
|
|
9
|
+
* - Environment variable mapping
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const appConfig = defineConfig({
|
|
14
|
+
* name: {
|
|
15
|
+
* type: 'string',
|
|
16
|
+
* env: 'APP_NAME',
|
|
17
|
+
* default: 'MyApp',
|
|
18
|
+
* required: true
|
|
19
|
+
* },
|
|
20
|
+
* port: {
|
|
21
|
+
* type: 'number',
|
|
22
|
+
* env: 'PORT',
|
|
23
|
+
* default: 3000,
|
|
24
|
+
* validate: (value) => value > 0 && value < 65536
|
|
25
|
+
* },
|
|
26
|
+
* debug: {
|
|
27
|
+
* type: 'boolean',
|
|
28
|
+
* env: 'DEBUG',
|
|
29
|
+
* default: false
|
|
30
|
+
* }
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* // Access with full type safety
|
|
34
|
+
* appConfig.name // string
|
|
35
|
+
* appConfig.port // number
|
|
36
|
+
* appConfig.debug // boolean
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { env } from './env'
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Config field types
|
|
44
|
+
*/
|
|
45
|
+
export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'enum'
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Config field definition
|
|
49
|
+
*/
|
|
50
|
+
export interface ConfigField<T = any> {
|
|
51
|
+
/** Field type */
|
|
52
|
+
type: ConfigFieldType
|
|
53
|
+
|
|
54
|
+
/** Environment variable name */
|
|
55
|
+
env?: string
|
|
56
|
+
|
|
57
|
+
/** Default value */
|
|
58
|
+
default?: T
|
|
59
|
+
|
|
60
|
+
/** Is field required? */
|
|
61
|
+
required?: boolean
|
|
62
|
+
|
|
63
|
+
/** Custom validation function */
|
|
64
|
+
validate?: (value: T) => boolean | string
|
|
65
|
+
|
|
66
|
+
/** For enum type: allowed values */
|
|
67
|
+
values?: readonly T[]
|
|
68
|
+
|
|
69
|
+
/** Field description (for documentation) */
|
|
70
|
+
description?: string
|
|
71
|
+
|
|
72
|
+
/** Custom transformer function */
|
|
73
|
+
transform?: (value: any) => T
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Config schema definition
|
|
78
|
+
*/
|
|
79
|
+
export type ConfigSchema = Record<string, ConfigField>
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Infer TypeScript type from config schema
|
|
83
|
+
*/
|
|
84
|
+
export type InferConfig<T extends ConfigSchema> = {
|
|
85
|
+
[K in keyof T]: T[K]['default'] extends infer D
|
|
86
|
+
? D extends undefined
|
|
87
|
+
? T[K]['required'] extends true
|
|
88
|
+
? InferFieldType<T[K]>
|
|
89
|
+
: InferFieldType<T[K]> | undefined
|
|
90
|
+
: InferFieldType<T[K]>
|
|
91
|
+
: InferFieldType<T[K]>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Infer field type from field definition
|
|
96
|
+
* Uses the generic T from ConfigField<T> for better type inference
|
|
97
|
+
*/
|
|
98
|
+
type InferFieldType<F> =
|
|
99
|
+
F extends ConfigField<infer T>
|
|
100
|
+
? T extends undefined
|
|
101
|
+
? (
|
|
102
|
+
F extends { type: 'string' } ? string :
|
|
103
|
+
F extends { type: 'number' } ? number :
|
|
104
|
+
F extends { type: 'boolean' } ? boolean :
|
|
105
|
+
F extends { type: 'array' } ? string[] :
|
|
106
|
+
F extends { type: 'object' } ? Record<string, any> :
|
|
107
|
+
F extends { type: 'enum'; values: readonly (infer U)[] } ? U :
|
|
108
|
+
any
|
|
109
|
+
)
|
|
110
|
+
: T
|
|
111
|
+
: any
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validation error
|
|
115
|
+
*/
|
|
116
|
+
export interface ValidationError {
|
|
117
|
+
field: string
|
|
118
|
+
message: string
|
|
119
|
+
value?: any
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Config validation result
|
|
124
|
+
*/
|
|
125
|
+
export interface ValidationResult {
|
|
126
|
+
valid: boolean
|
|
127
|
+
errors: ValidationError[]
|
|
128
|
+
warnings?: string[]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Cast value to specific type
|
|
133
|
+
*/
|
|
134
|
+
function castValue(value: any, type: ConfigFieldType): any {
|
|
135
|
+
if (value === undefined || value === null) {
|
|
136
|
+
return undefined
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
switch (type) {
|
|
140
|
+
case 'string':
|
|
141
|
+
return String(value)
|
|
142
|
+
|
|
143
|
+
case 'number':
|
|
144
|
+
const num = Number(value)
|
|
145
|
+
return isNaN(num) ? undefined : num
|
|
146
|
+
|
|
147
|
+
case 'boolean':
|
|
148
|
+
if (typeof value === 'boolean') return value
|
|
149
|
+
if (typeof value === 'string') {
|
|
150
|
+
return ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
|
|
151
|
+
}
|
|
152
|
+
return Boolean(value)
|
|
153
|
+
|
|
154
|
+
case 'array':
|
|
155
|
+
if (Array.isArray(value)) return value
|
|
156
|
+
if (typeof value === 'string') {
|
|
157
|
+
return value.split(',').map(v => v.trim()).filter(Boolean)
|
|
158
|
+
}
|
|
159
|
+
return [value]
|
|
160
|
+
|
|
161
|
+
case 'object':
|
|
162
|
+
if (typeof value === 'object' && value !== null) return value
|
|
163
|
+
if (typeof value === 'string') {
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(value)
|
|
166
|
+
} catch {
|
|
167
|
+
return {}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {}
|
|
171
|
+
|
|
172
|
+
case 'enum':
|
|
173
|
+
return value
|
|
174
|
+
|
|
175
|
+
default:
|
|
176
|
+
return value
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Validate config value
|
|
182
|
+
*/
|
|
183
|
+
function validateField(
|
|
184
|
+
fieldName: string,
|
|
185
|
+
value: any,
|
|
186
|
+
field: ConfigField
|
|
187
|
+
): ValidationError | null {
|
|
188
|
+
// Check required
|
|
189
|
+
if (field.required && (value === undefined || value === null || value === '')) {
|
|
190
|
+
return {
|
|
191
|
+
field: fieldName,
|
|
192
|
+
message: `Field '${fieldName}' is required but not provided`
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Skip validation if value is undefined and not required
|
|
197
|
+
if (value === undefined && !field.required) {
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check enum values
|
|
202
|
+
if (field.type === 'enum' && field.values) {
|
|
203
|
+
if (!field.values.includes(value)) {
|
|
204
|
+
return {
|
|
205
|
+
field: fieldName,
|
|
206
|
+
message: `Field '${fieldName}' must be one of: ${field.values.join(', ')}`,
|
|
207
|
+
value
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Custom validation
|
|
213
|
+
if (field.validate) {
|
|
214
|
+
const result = field.validate(value)
|
|
215
|
+
if (result === false) {
|
|
216
|
+
return {
|
|
217
|
+
field: fieldName,
|
|
218
|
+
message: `Field '${fieldName}' failed validation`,
|
|
219
|
+
value
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (typeof result === 'string') {
|
|
223
|
+
return {
|
|
224
|
+
field: fieldName,
|
|
225
|
+
message: result,
|
|
226
|
+
value
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Reactive config instance that can reload in runtime
|
|
236
|
+
*/
|
|
237
|
+
export class ReactiveConfig<T extends ConfigSchema> {
|
|
238
|
+
private schema: T
|
|
239
|
+
private config: InferConfig<T>
|
|
240
|
+
private watchers: Array<(config: InferConfig<T>) => void> = []
|
|
241
|
+
|
|
242
|
+
constructor(schema: T) {
|
|
243
|
+
this.schema = schema
|
|
244
|
+
this.config = this.loadConfig()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Load config from environment
|
|
249
|
+
*/
|
|
250
|
+
private loadConfig(): InferConfig<T> {
|
|
251
|
+
const config: any = {}
|
|
252
|
+
const errors: ValidationError[] = []
|
|
253
|
+
|
|
254
|
+
for (const [fieldName, field] of Object.entries(this.schema)) {
|
|
255
|
+
let value: any
|
|
256
|
+
|
|
257
|
+
// 1. Try to get from environment variable
|
|
258
|
+
if (field.env) {
|
|
259
|
+
const envValue = env.has(field.env) ? env.all()[field.env] : undefined
|
|
260
|
+
if (envValue !== undefined && envValue !== '') {
|
|
261
|
+
value = envValue
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 2. Use default value if not found in env
|
|
266
|
+
if (value === undefined) {
|
|
267
|
+
value = field.default
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 3. Apply custom transform if provided
|
|
271
|
+
if (value !== undefined && field.transform) {
|
|
272
|
+
try {
|
|
273
|
+
value = field.transform(value)
|
|
274
|
+
} catch (error) {
|
|
275
|
+
errors.push({
|
|
276
|
+
field: fieldName,
|
|
277
|
+
message: `Transform failed: ${error}`
|
|
278
|
+
})
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 4. Cast to correct type
|
|
284
|
+
if (value !== undefined) {
|
|
285
|
+
value = castValue(value, field.type)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 5. Validate
|
|
289
|
+
const validationError = validateField(fieldName, value, field)
|
|
290
|
+
if (validationError) {
|
|
291
|
+
errors.push(validationError)
|
|
292
|
+
continue
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 6. Set value
|
|
296
|
+
config[fieldName] = value
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Throw error if validation failed
|
|
300
|
+
if (errors.length > 0) {
|
|
301
|
+
const errorMessage = errors
|
|
302
|
+
.map(e => ` - ${e.message}${e.value !== undefined ? ` (got: ${JSON.stringify(e.value)})` : ''}`)
|
|
303
|
+
.join('\n')
|
|
304
|
+
|
|
305
|
+
throw new Error(
|
|
306
|
+
`❌ Configuration validation failed:\n${errorMessage}\n\n` +
|
|
307
|
+
`Please check your environment variables or configuration.`
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return config as InferConfig<T>
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get current config values
|
|
316
|
+
*/
|
|
317
|
+
get values(): InferConfig<T> {
|
|
318
|
+
return this.config
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Reload config from environment (runtime reload)
|
|
323
|
+
*/
|
|
324
|
+
reload(): InferConfig<T> {
|
|
325
|
+
// Clear env cache to get fresh values
|
|
326
|
+
env.clearCache()
|
|
327
|
+
|
|
328
|
+
// Reload config
|
|
329
|
+
const newConfig = this.loadConfig()
|
|
330
|
+
this.config = newConfig
|
|
331
|
+
|
|
332
|
+
// Notify watchers
|
|
333
|
+
this.watchers.forEach(watcher => watcher(newConfig))
|
|
334
|
+
|
|
335
|
+
return newConfig
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Watch for config changes (called after reload)
|
|
340
|
+
*/
|
|
341
|
+
watch(callback: (config: InferConfig<T>) => void): () => void {
|
|
342
|
+
this.watchers.push(callback)
|
|
343
|
+
|
|
344
|
+
// Return unwatch function
|
|
345
|
+
return () => {
|
|
346
|
+
const index = this.watchers.indexOf(callback)
|
|
347
|
+
if (index > -1) {
|
|
348
|
+
this.watchers.splice(index, 1)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get specific field value with runtime lookup
|
|
355
|
+
*/
|
|
356
|
+
get<K extends keyof InferConfig<T>>(key: K): InferConfig<T>[K] {
|
|
357
|
+
return this.config[key]
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Check if field exists
|
|
362
|
+
*/
|
|
363
|
+
has<K extends keyof InferConfig<T>>(key: K): boolean {
|
|
364
|
+
return this.config[key] !== undefined
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Define and load configuration from schema
|
|
370
|
+
*/
|
|
371
|
+
export function defineConfig<T extends ConfigSchema>(schema: T): InferConfig<T> {
|
|
372
|
+
const reactive = new ReactiveConfig(schema)
|
|
373
|
+
return reactive.values as InferConfig<T>
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Define reactive configuration (can be reloaded in runtime)
|
|
378
|
+
*/
|
|
379
|
+
export function defineReactiveConfig<T extends ConfigSchema>(schema: T): ReactiveConfig<T> {
|
|
380
|
+
return new ReactiveConfig(schema)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Validate configuration without throwing
|
|
385
|
+
*/
|
|
386
|
+
export function validateConfig<T extends ConfigSchema>(
|
|
387
|
+
schema: T,
|
|
388
|
+
values: Partial<InferConfig<T>>
|
|
389
|
+
): ValidationResult {
|
|
390
|
+
const errors: ValidationError[] = []
|
|
391
|
+
|
|
392
|
+
for (const [fieldName, field] of Object.entries(schema)) {
|
|
393
|
+
const value = (values as any)[fieldName]
|
|
394
|
+
const error = validateField(fieldName, value, field)
|
|
395
|
+
if (error) {
|
|
396
|
+
errors.push(error)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
valid: errors.length === 0,
|
|
402
|
+
errors
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Create nested config schema (for grouping)
|
|
408
|
+
*/
|
|
409
|
+
export function defineNestedConfig<T extends Record<string, ConfigSchema>>(
|
|
410
|
+
schemas: T
|
|
411
|
+
): { [K in keyof T]: InferConfig<T[K]> } {
|
|
412
|
+
const config: any = {}
|
|
413
|
+
|
|
414
|
+
for (const [groupName, schema] of Object.entries(schemas)) {
|
|
415
|
+
config[groupName] = defineConfig(schema)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return config
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Helper to create env field quickly
|
|
423
|
+
*/
|
|
424
|
+
export function envString(envVar: string, defaultValue?: string, required = false): ConfigField<string> {
|
|
425
|
+
return {
|
|
426
|
+
type: 'string' as const,
|
|
427
|
+
env: envVar,
|
|
428
|
+
default: defaultValue,
|
|
429
|
+
required
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function envNumber(envVar: string, defaultValue?: number, required = false): ConfigField<number> {
|
|
434
|
+
return {
|
|
435
|
+
type: 'number' as const,
|
|
436
|
+
env: envVar,
|
|
437
|
+
default: defaultValue,
|
|
438
|
+
required
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function envBoolean(envVar: string, defaultValue?: boolean, required = false): ConfigField<boolean> {
|
|
443
|
+
return {
|
|
444
|
+
type: 'boolean' as const,
|
|
445
|
+
env: envVar,
|
|
446
|
+
default: defaultValue,
|
|
447
|
+
required
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export function envArray(envVar: string, defaultValue?: string[], required = false): ConfigField<string[]> {
|
|
452
|
+
return {
|
|
453
|
+
type: 'array' as const,
|
|
454
|
+
env: envVar,
|
|
455
|
+
default: defaultValue,
|
|
456
|
+
required
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export function envEnum<T extends readonly string[]>(
|
|
461
|
+
envVar: string,
|
|
462
|
+
values: T,
|
|
463
|
+
defaultValue?: T[number],
|
|
464
|
+
required = false
|
|
465
|
+
): ConfigField<T[number]> {
|
|
466
|
+
return {
|
|
467
|
+
type: 'enum' as const,
|
|
468
|
+
env: envVar,
|
|
469
|
+
values,
|
|
470
|
+
default: defaultValue,
|
|
471
|
+
required
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Export shorthand helpers
|
|
477
|
+
*/
|
|
478
|
+
export const config = {
|
|
479
|
+
string: envString,
|
|
480
|
+
number: envNumber,
|
|
481
|
+
boolean: envBoolean,
|
|
482
|
+
array: envArray,
|
|
483
|
+
enum: envEnum
|
|
484
|
+
}
|