@buenojs/bueno 0.8.3 → 0.8.5
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/README.md +136 -16
- package/dist/cli/{index.js → bin.js} +3036 -1421
- package/dist/container/index.js +250 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +11043 -6482
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3346 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +776 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/package.json +121 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +392 -438
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +61 -0
- package/src/cli/templates/database/mysql.ts +14 -0
- package/src/cli/templates/database/none.ts +16 -0
- package/src/cli/templates/database/postgresql.ts +14 -0
- package/src/cli/templates/database/sqlite.ts +14 -0
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +63 -0
- package/src/cli/templates/frontend/none.ts +17 -0
- package/src/cli/templates/frontend/react.ts +140 -0
- package/src/cli/templates/frontend/solid.ts +134 -0
- package/src/cli/templates/frontend/svelte.ts +131 -0
- package/src/cli/templates/frontend/vue.ts +130 -0
- package/src/cli/templates/generators/index.ts +339 -0
- package/src/cli/templates/generators/types.ts +56 -0
- package/src/cli/templates/index.ts +35 -2
- package/src/cli/templates/project/api.ts +81 -0
- package/src/cli/templates/project/default.ts +140 -0
- package/src/cli/templates/project/fullstack.ts +111 -0
- package/src/cli/templates/project/index.ts +95 -0
- package/src/cli/templates/project/minimal.ts +45 -0
- package/src/cli/templates/project/types.ts +94 -0
- package/src/cli/templates/project/website.ts +263 -0
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -2
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +47 -0
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +545 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +179 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +409 -298
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
package/src/config/env.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Environment variable handling for Bueno Framework
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { setNestedValue } from "./merge";
|
|
5
6
|
import type { BuenoConfig, DeepPartial, EnvMapping } from "./types";
|
|
6
7
|
import { ENV_MAPPINGS } from "./types";
|
|
7
|
-
import { setNestedValue } from "./merge";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Environment variable source information
|
|
@@ -168,6 +168,113 @@ export async function loadEnvFiles(options?: {
|
|
|
168
168
|
return result;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
// ============= Environment Variable Validation =============
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validate environment variables before loading
|
|
175
|
+
*
|
|
176
|
+
* @param envVars - Environment variables to validate
|
|
177
|
+
* @returns Validation result with transformed values or error details
|
|
178
|
+
*/
|
|
179
|
+
export async function validateEnvVars(
|
|
180
|
+
envVars: Record<string, string>,
|
|
181
|
+
): Promise<ValidationResult> {
|
|
182
|
+
try {
|
|
183
|
+
const { validateEnvVars } = await import("./env-validation");
|
|
184
|
+
return validateEnvVars(envVars);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
issues: [
|
|
189
|
+
{
|
|
190
|
+
message: `Failed to validate environment variables: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Load and validate environment variables with detailed error reporting
|
|
199
|
+
*
|
|
200
|
+
* @param options - Options for loading and validating environment variables
|
|
201
|
+
* @returns Loaded environment data with validation result
|
|
202
|
+
*/
|
|
203
|
+
export async function loadAndValidateEnv(options?: {
|
|
204
|
+
/** Custom list of env files to load */
|
|
205
|
+
files?: string[];
|
|
206
|
+
/** Whether to also load NODE_ENV-specific file */
|
|
207
|
+
loadNodeEnv?: boolean;
|
|
208
|
+
/** Base directory for env files */
|
|
209
|
+
cwd?: string;
|
|
210
|
+
/** Whether to merge with existing Bun.env */
|
|
211
|
+
mergeWithProcess?: boolean;
|
|
212
|
+
/** Custom environment variable mappings */
|
|
213
|
+
mappings?: EnvMapping[];
|
|
214
|
+
}): Promise<{ loaded: LoadedEnv; valid: boolean; errors?: string }> {
|
|
215
|
+
try {
|
|
216
|
+
// Load environment variables from files
|
|
217
|
+
const fileVars = await loadEnvFiles(options);
|
|
218
|
+
|
|
219
|
+
// Validate the environment variables
|
|
220
|
+
const validationResult = await validateEnvVars(fileVars);
|
|
221
|
+
|
|
222
|
+
// Merge with Bun.env if requested
|
|
223
|
+
const raw: Record<string, string> =
|
|
224
|
+
options?.mergeWithProcess !== false
|
|
225
|
+
? {
|
|
226
|
+
...fileVars,
|
|
227
|
+
...Object.fromEntries(
|
|
228
|
+
Object.entries(Bun.env).filter(([, v]) => v !== undefined) as [
|
|
229
|
+
string,
|
|
230
|
+
string,
|
|
231
|
+
][],
|
|
232
|
+
),
|
|
233
|
+
}
|
|
234
|
+
: fileVars;
|
|
235
|
+
|
|
236
|
+
// Transform to config
|
|
237
|
+
const config = envToConfig(raw, options?.mappings);
|
|
238
|
+
|
|
239
|
+
// Track sources
|
|
240
|
+
const sources = new Map<string, EnvSourceInfo>();
|
|
241
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
242
|
+
sources.set(name, {
|
|
243
|
+
name,
|
|
244
|
+
value,
|
|
245
|
+
source: fileVars[name] !== undefined ? ".env file" : "process",
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Set loaded vars to Bun.env
|
|
250
|
+
for (const [key, value] of Object.entries(fileVars)) {
|
|
251
|
+
if (Bun.env[key] === undefined) {
|
|
252
|
+
Bun.env[key] = value;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const loaded: LoadedEnv = { raw, config, sources };
|
|
257
|
+
|
|
258
|
+
if (!validationResult.success) {
|
|
259
|
+
return {
|
|
260
|
+
loaded,
|
|
261
|
+
valid: false,
|
|
262
|
+
errors: validationResult.issues
|
|
263
|
+
.map((issue) => issue.message)
|
|
264
|
+
.join("\n"),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { loaded, valid: true };
|
|
269
|
+
} catch (error) {
|
|
270
|
+
return {
|
|
271
|
+
loaded: { raw: {}, config: {}, sources: new Map() },
|
|
272
|
+
valid: false,
|
|
273
|
+
errors: `Failed to load and validate environment variables: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
171
278
|
/**
|
|
172
279
|
* Get environment variable value from Bun.env
|
|
173
280
|
*/
|
|
@@ -233,36 +340,18 @@ export async function loadEnv(options?: {
|
|
|
233
340
|
/** Custom environment variable mappings */
|
|
234
341
|
mappings?: EnvMapping[];
|
|
235
342
|
}): Promise<LoadedEnv> {
|
|
236
|
-
// Load
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// Transform to config
|
|
246
|
-
const config = envToConfig(raw, options?.mappings);
|
|
247
|
-
|
|
248
|
-
// Track sources
|
|
249
|
-
const sources = new Map<string, EnvSourceInfo>();
|
|
250
|
-
for (const [name, value] of Object.entries(raw)) {
|
|
251
|
-
sources.set(name, {
|
|
252
|
-
name,
|
|
253
|
-
value,
|
|
254
|
-
source: fileVars[name] !== undefined ? ".env file" : "process",
|
|
255
|
-
});
|
|
343
|
+
// Load and validate environment variables
|
|
344
|
+
const result = await loadAndValidateEnv(options);
|
|
345
|
+
|
|
346
|
+
if (!result.valid) {
|
|
347
|
+
console.error("Environment variable validation failed:");
|
|
348
|
+
console.error(result.errors);
|
|
349
|
+
throw new Error(
|
|
350
|
+
"Environment variable validation failed. Check the error messages above.",
|
|
351
|
+
);
|
|
256
352
|
}
|
|
257
353
|
|
|
258
|
-
|
|
259
|
-
for (const [key, value] of Object.entries(fileVars)) {
|
|
260
|
-
if (Bun.env[key] === undefined) {
|
|
261
|
-
Bun.env[key] = value;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return { raw, config, sources };
|
|
354
|
+
return result.loaded;
|
|
266
355
|
}
|
|
267
356
|
|
|
268
357
|
/**
|
|
@@ -324,7 +413,7 @@ export function parseEnvBoolean(value: string): boolean {
|
|
|
324
413
|
* Parse a number environment variable
|
|
325
414
|
*/
|
|
326
415
|
export function parseEnvNumber(value: string): number {
|
|
327
|
-
const num = parseInt(value, 10);
|
|
416
|
+
const num = Number.parseInt(value, 10);
|
|
328
417
|
if (isNaN(num)) {
|
|
329
418
|
throw new Error(`Invalid number: ${value}`);
|
|
330
419
|
}
|
|
@@ -351,22 +440,58 @@ export function parseEnvArray(value: string): string[] {
|
|
|
351
440
|
export const envConfigMapping: EnvConfigMapping[] = [
|
|
352
441
|
{ envVar: "BUENO_PORT", configKey: "server.port", transform: parseEnvNumber },
|
|
353
442
|
{ envVar: "BUENO_HOST", configKey: "server.host" },
|
|
354
|
-
{
|
|
443
|
+
{
|
|
444
|
+
envVar: "BUENO_DEV",
|
|
445
|
+
configKey: "server.development",
|
|
446
|
+
transform: parseEnvBoolean,
|
|
447
|
+
},
|
|
355
448
|
{ envVar: "DATABASE_URL", configKey: "database.url" },
|
|
356
|
-
{
|
|
449
|
+
{
|
|
450
|
+
envVar: "DATABASE_POOL_SIZE",
|
|
451
|
+
configKey: "database.poolSize",
|
|
452
|
+
transform: parseEnvNumber,
|
|
453
|
+
},
|
|
357
454
|
{ envVar: "REDIS_URL", configKey: "cache.url" },
|
|
358
455
|
{ envVar: "CACHE_DRIVER", configKey: "cache.driver" },
|
|
359
456
|
{ envVar: "CACHE_TTL", configKey: "cache.ttl", transform: parseEnvNumber },
|
|
360
457
|
{ envVar: "LOG_LEVEL", configKey: "logger.level" },
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
458
|
+
{
|
|
459
|
+
envVar: "LOG_PRETTY",
|
|
460
|
+
configKey: "logger.pretty",
|
|
461
|
+
transform: parseEnvBoolean,
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
envVar: "HEALTH_ENABLED",
|
|
465
|
+
configKey: "health.enabled",
|
|
466
|
+
transform: parseEnvBoolean,
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
envVar: "METRICS_ENABLED",
|
|
470
|
+
configKey: "metrics.enabled",
|
|
471
|
+
transform: parseEnvBoolean,
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
envVar: "TELEMETRY_ENABLED",
|
|
475
|
+
configKey: "telemetry.enabled",
|
|
476
|
+
transform: parseEnvBoolean,
|
|
477
|
+
},
|
|
365
478
|
{ envVar: "TELEMETRY_SERVICE_NAME", configKey: "telemetry.serviceName" },
|
|
366
479
|
{ envVar: "TELEMETRY_ENDPOINT", configKey: "telemetry.endpoint" },
|
|
367
|
-
{
|
|
368
|
-
|
|
369
|
-
|
|
480
|
+
{
|
|
481
|
+
envVar: "FRONTEND_DEV_SERVER",
|
|
482
|
+
configKey: "frontend.devServer",
|
|
483
|
+
transform: parseEnvBoolean,
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
envVar: "FRONTEND_HMR",
|
|
487
|
+
configKey: "frontend.hmr",
|
|
488
|
+
transform: parseEnvBoolean,
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
envVar: "FRONTEND_PORT",
|
|
492
|
+
configKey: "frontend.port",
|
|
493
|
+
transform: parseEnvNumber,
|
|
494
|
+
},
|
|
370
495
|
];
|
|
371
496
|
|
|
372
497
|
/**
|
|
@@ -396,7 +521,10 @@ export function getEnvConfig(
|
|
|
396
521
|
/**
|
|
397
522
|
* Get an environment variable value
|
|
398
523
|
*/
|
|
399
|
-
export function getEnvValue(
|
|
524
|
+
export function getEnvValue(
|
|
525
|
+
key: string,
|
|
526
|
+
defaultValue?: string,
|
|
527
|
+
): string | undefined {
|
|
400
528
|
return Bun.env[key] ?? defaultValue;
|
|
401
529
|
}
|
|
402
530
|
|
|
@@ -405,4 +533,4 @@ export function getEnvValue(key: string, defaultValue?: string): string | undefi
|
|
|
405
533
|
*/
|
|
406
534
|
export function setEnvValue(key: string, value: string): void {
|
|
407
535
|
Bun.env[key] = value;
|
|
408
|
-
}
|
|
536
|
+
}
|
package/src/config/index.ts
CHANGED
|
@@ -19,6 +19,9 @@ export type {
|
|
|
19
19
|
MetricsConfig,
|
|
20
20
|
TelemetryConfig,
|
|
21
21
|
FrontendConfig,
|
|
22
|
+
JobsConfig,
|
|
23
|
+
NotificationConfig,
|
|
24
|
+
I18nConfig,
|
|
22
25
|
DeepPartial,
|
|
23
26
|
UserConfig,
|
|
24
27
|
UserConfigFn,
|
|
@@ -70,21 +73,21 @@ export {
|
|
|
70
73
|
} from "./validation";
|
|
71
74
|
|
|
72
75
|
import type { StandardSchema } from "../types";
|
|
76
|
+
import { getEnvConfig, loadEnv } from "./env";
|
|
77
|
+
import { clearConfigCache, findConfigFile, loadConfig } from "./loader";
|
|
78
|
+
import { deepMerge, mergeConfigs } from "./merge";
|
|
73
79
|
import type {
|
|
74
80
|
BuenoConfig,
|
|
75
81
|
DeepPartial,
|
|
82
|
+
InferConfig,
|
|
76
83
|
UserConfig,
|
|
77
84
|
UserConfigFn,
|
|
78
|
-
InferConfig,
|
|
79
85
|
} from "./types";
|
|
80
|
-
import { deepMerge, mergeConfigs } from "./merge";
|
|
81
|
-
import { loadEnv, getEnvConfig } from "./env";
|
|
82
|
-
import { loadConfig, findConfigFile, clearConfigCache } from "./loader";
|
|
83
86
|
import {
|
|
87
|
+
type ConfigValidationResult,
|
|
88
|
+
assertValidConfig,
|
|
84
89
|
validateConfig,
|
|
85
90
|
validateConfigSync,
|
|
86
|
-
assertValidConfig,
|
|
87
|
-
type ConfigValidationResult,
|
|
88
91
|
} from "./validation";
|
|
89
92
|
|
|
90
93
|
/**
|
|
@@ -178,9 +181,9 @@ export class ConfigManager<T extends BuenoConfig = BuenoConfig> {
|
|
|
178
181
|
|
|
179
182
|
constructor(options: ConfigManagerOptions<T> = {}) {
|
|
180
183
|
this.options = options;
|
|
181
|
-
this.config = (
|
|
182
|
-
? { ...DEFAULT_CONFIG }
|
|
183
|
-
|
|
184
|
+
this.config = (
|
|
185
|
+
options.useDefaults !== false ? { ...DEFAULT_CONFIG } : {}
|
|
186
|
+
) as T;
|
|
184
187
|
|
|
185
188
|
// Apply initial config if provided
|
|
186
189
|
if (options.config) {
|
|
@@ -212,10 +215,10 @@ export class ConfigManager<T extends BuenoConfig = BuenoConfig> {
|
|
|
212
215
|
// Merge all sources
|
|
213
216
|
const merged = mergeConfigs(
|
|
214
217
|
{} as T,
|
|
215
|
-
this.options.useDefaults !== false ? DEFAULT_CONFIG : {} as T,
|
|
218
|
+
this.options.useDefaults !== false ? DEFAULT_CONFIG : ({} as T),
|
|
216
219
|
fileConfig as T,
|
|
217
220
|
envConfig as T,
|
|
218
|
-
this.options.config as T || {} as T,
|
|
221
|
+
(this.options.config as T) || ({} as T),
|
|
219
222
|
);
|
|
220
223
|
|
|
221
224
|
this.config = merged as T;
|
|
@@ -270,7 +273,10 @@ export class ConfigManager<T extends BuenoConfig = BuenoConfig> {
|
|
|
270
273
|
set(key: string, value: unknown): void;
|
|
271
274
|
set(key: string, value: unknown): void {
|
|
272
275
|
const parts = key.split(".");
|
|
273
|
-
let current: Record<string, unknown> = this.config as Record<
|
|
276
|
+
let current: Record<string, unknown> = this.config as Record<
|
|
277
|
+
string,
|
|
278
|
+
unknown
|
|
279
|
+
>;
|
|
274
280
|
|
|
275
281
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
276
282
|
const part = parts[i];
|
|
@@ -320,7 +326,9 @@ export class ConfigManager<T extends BuenoConfig = BuenoConfig> {
|
|
|
320
326
|
* Validate synchronously (only default rules)
|
|
321
327
|
*/
|
|
322
328
|
validateSync(): ConfigValidationResult {
|
|
323
|
-
return validateConfigSync(
|
|
329
|
+
return validateConfigSync(
|
|
330
|
+
this.config as unknown as DeepPartial<BuenoConfig>,
|
|
331
|
+
);
|
|
324
332
|
}
|
|
325
333
|
|
|
326
334
|
/**
|
|
@@ -452,9 +460,9 @@ export function defineConfigFn<T extends BuenoConfig = BuenoConfig>(
|
|
|
452
460
|
* const config = await createConfigManager();
|
|
453
461
|
* const port = config.get('server.port');
|
|
454
462
|
*/
|
|
455
|
-
export async function createConfigManager<
|
|
456
|
-
T
|
|
457
|
-
|
|
463
|
+
export async function createConfigManager<T extends BuenoConfig = BuenoConfig>(
|
|
464
|
+
options?: ConfigManagerOptions<T>,
|
|
465
|
+
): Promise<ConfigManager<T>> {
|
|
458
466
|
const manager = new ConfigManager<T>(options);
|
|
459
467
|
await manager.load();
|
|
460
468
|
return manager;
|
|
@@ -478,9 +486,9 @@ export function createConfigManagerSync<T extends BuenoConfig = BuenoConfig>(
|
|
|
478
486
|
* const config = await loadConfigDirect();
|
|
479
487
|
* console.log(config.server?.port);
|
|
480
488
|
*/
|
|
481
|
-
export async function loadConfigDirect<
|
|
482
|
-
T
|
|
483
|
-
|
|
489
|
+
export async function loadConfigDirect<T extends BuenoConfig = BuenoConfig>(
|
|
490
|
+
options?: ConfigManagerOptions<T>,
|
|
491
|
+
): Promise<T> {
|
|
484
492
|
const manager = await createConfigManager<T>(options);
|
|
485
493
|
return manager.getAll();
|
|
486
494
|
}
|
|
@@ -503,4 +511,4 @@ export function getConfigManager(): ConfigManager {
|
|
|
503
511
|
*/
|
|
504
512
|
export function setConfigManager(manager: ConfigManager): void {
|
|
505
513
|
defaultManager = manager;
|
|
506
|
-
}
|
|
514
|
+
}
|
package/src/config/loader.ts
CHANGED
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
* Uses Bun's native TypeScript loader to import config files
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { BuenoConfig, DeepPartial, UserConfig, UserConfigFn } from "./types";
|
|
7
6
|
import { deepMerge } from "./merge";
|
|
7
|
+
import type {
|
|
8
|
+
BuenoConfig,
|
|
9
|
+
DeepPartial,
|
|
10
|
+
UserConfig,
|
|
11
|
+
UserConfigFn,
|
|
12
|
+
} from "./types";
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Configuration file search order
|
|
@@ -74,7 +79,9 @@ export function clearConfigCache(): void {
|
|
|
74
79
|
/**
|
|
75
80
|
* Get cached config
|
|
76
81
|
*/
|
|
77
|
-
export function getCachedConfig(
|
|
82
|
+
export function getCachedConfig(
|
|
83
|
+
path: string,
|
|
84
|
+
): DeepPartial<BuenoConfig> | undefined {
|
|
78
85
|
return configCache.get(path);
|
|
79
86
|
}
|
|
80
87
|
|
|
@@ -152,18 +159,18 @@ export async function loadConfigFile<T extends BuenoConfig = BuenoConfig>(
|
|
|
152
159
|
* Load configuration from file
|
|
153
160
|
* Searches for config files in order and loads the first one found
|
|
154
161
|
*/
|
|
155
|
-
export async function loadConfig<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
): Promise<LoadedConfig> {
|
|
162
|
+
export async function loadConfig<
|
|
163
|
+
T extends BuenoConfig = BuenoConfig,
|
|
164
|
+
>(options?: {
|
|
165
|
+
/** Custom config file path */
|
|
166
|
+
configPath?: string;
|
|
167
|
+
/** Working directory to search for config */
|
|
168
|
+
cwd?: string;
|
|
169
|
+
/** Whether to use cache */
|
|
170
|
+
useCache?: boolean;
|
|
171
|
+
/** Additional context to pass to config function */
|
|
172
|
+
context?: Record<string, unknown>;
|
|
173
|
+
}): Promise<LoadedConfig> {
|
|
167
174
|
// If a specific path is provided, use it
|
|
168
175
|
if (options?.configPath) {
|
|
169
176
|
return loadConfigFile<T>(options.configPath, {
|
|
@@ -306,7 +313,9 @@ export function validateConfigStructure(
|
|
|
306
313
|
/**
|
|
307
314
|
* Extract config file path from CLI args
|
|
308
315
|
*/
|
|
309
|
-
export function getConfigPathFromArgs(
|
|
316
|
+
export function getConfigPathFromArgs(
|
|
317
|
+
args: string[] = process.argv,
|
|
318
|
+
): string | undefined {
|
|
310
319
|
const configIndex = args.indexOf("--config");
|
|
311
320
|
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
312
321
|
return args[configIndex + 1];
|
|
@@ -326,4 +335,4 @@ export function getConfigPathFromArgs(args: string[] = process.argv): string | u
|
|
|
326
335
|
*/
|
|
327
336
|
export function getConfigPathFromEnv(): string | undefined {
|
|
328
337
|
return Bun.env.BUENO_CONFIG;
|
|
329
|
-
}
|
|
338
|
+
}
|
package/src/config/merge.ts
CHANGED
|
@@ -7,7 +7,9 @@ import type { BuenoConfig, DeepPartial } from "./types";
|
|
|
7
7
|
/**
|
|
8
8
|
* Check if a value is a plain object (not an array, not null, not a class instance)
|
|
9
9
|
*/
|
|
10
|
-
export function isPlainObject(
|
|
10
|
+
export function isPlainObject(
|
|
11
|
+
value: unknown,
|
|
12
|
+
): value is Record<string, unknown> {
|
|
11
13
|
if (value === null || typeof value !== "object") {
|
|
12
14
|
return false;
|
|
13
15
|
}
|
|
@@ -60,7 +62,10 @@ export function deepMerge<T>(target: T, source: DeepPartial<T>): T {
|
|
|
60
62
|
|
|
61
63
|
if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
|
|
62
64
|
// Both are objects, merge recursively
|
|
63
|
-
result[key] = deepMerge(
|
|
65
|
+
result[key] = deepMerge(
|
|
66
|
+
targetValue,
|
|
67
|
+
sourceValue as DeepPartial<typeof targetValue>,
|
|
68
|
+
);
|
|
64
69
|
} else if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
|
|
65
70
|
// Both are arrays, concatenate them
|
|
66
71
|
result[key] = [...targetValue, ...sourceValue];
|
|
@@ -80,13 +85,19 @@ export function deepMerge<T>(target: T, source: DeepPartial<T>): T {
|
|
|
80
85
|
export function mergeConfigs<T extends BuenoConfig = BuenoConfig>(
|
|
81
86
|
...configs: (DeepPartial<T> | undefined | null)[]
|
|
82
87
|
): DeepPartial<T> {
|
|
83
|
-
return configs.reduce<DeepPartial<T>>(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
return configs.reduce<DeepPartial<T>>(
|
|
89
|
+
(acc, config) => {
|
|
90
|
+
if (config === undefined || config === null) {
|
|
91
|
+
return acc;
|
|
92
|
+
}
|
|
93
|
+
// Use unknown as intermediate type to avoid recursive type issues
|
|
94
|
+
return deepMerge(
|
|
95
|
+
acc as unknown as T,
|
|
96
|
+
config as unknown as DeepPartial<T>,
|
|
97
|
+
) as unknown as DeepPartial<T>;
|
|
98
|
+
},
|
|
99
|
+
{} as DeepPartial<T>,
|
|
100
|
+
);
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
/**
|
|
@@ -282,4 +293,4 @@ export function diffConfigs(
|
|
|
282
293
|
}
|
|
283
294
|
|
|
284
295
|
return { added, removed, changed };
|
|
285
|
-
}
|
|
296
|
+
}
|