@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.
Files changed (218) hide show
  1. package/README.md +136 -16
  2. package/dist/cli/{index.js → bin.js} +3036 -1421
  3. package/dist/container/index.js +250 -0
  4. package/dist/context/index.js +219 -0
  5. package/dist/database/index.js +493 -0
  6. package/dist/frontend/index.js +7697 -0
  7. package/dist/health/index.js +364 -0
  8. package/dist/i18n/index.js +345 -0
  9. package/dist/index.js +11043 -6482
  10. package/dist/jobs/index.js +819 -0
  11. package/dist/lock/index.js +367 -0
  12. package/dist/logger/index.js +281 -0
  13. package/dist/metrics/index.js +289 -0
  14. package/dist/middleware/index.js +77 -0
  15. package/dist/migrations/index.js +571 -0
  16. package/dist/modules/index.js +3346 -0
  17. package/dist/notification/index.js +484 -0
  18. package/dist/observability/index.js +331 -0
  19. package/dist/openapi/index.js +776 -0
  20. package/dist/orm/index.js +1356 -0
  21. package/dist/router/index.js +886 -0
  22. package/dist/rpc/index.js +691 -0
  23. package/dist/schema/index.js +400 -0
  24. package/dist/telemetry/index.js +595 -0
  25. package/dist/template/index.js +640 -0
  26. package/dist/templates/index.js +640 -0
  27. package/dist/testing/index.js +1111 -0
  28. package/dist/types/index.js +60 -0
  29. package/package.json +121 -27
  30. package/src/cache/index.ts +2 -1
  31. package/src/cli/bin.ts +2 -2
  32. package/src/cli/commands/build.ts +183 -165
  33. package/src/cli/commands/dev.ts +96 -89
  34. package/src/cli/commands/generate.ts +142 -111
  35. package/src/cli/commands/help.ts +20 -16
  36. package/src/cli/commands/index.ts +3 -6
  37. package/src/cli/commands/migration.ts +124 -105
  38. package/src/cli/commands/new.ts +392 -438
  39. package/src/cli/commands/start.ts +81 -79
  40. package/src/cli/core/args.ts +68 -50
  41. package/src/cli/core/console.ts +89 -95
  42. package/src/cli/core/index.ts +4 -4
  43. package/src/cli/core/prompt.ts +65 -62
  44. package/src/cli/core/spinner.ts +23 -20
  45. package/src/cli/index.ts +46 -38
  46. package/src/cli/templates/database/index.ts +61 -0
  47. package/src/cli/templates/database/mysql.ts +14 -0
  48. package/src/cli/templates/database/none.ts +16 -0
  49. package/src/cli/templates/database/postgresql.ts +14 -0
  50. package/src/cli/templates/database/sqlite.ts +14 -0
  51. package/src/cli/templates/deploy.ts +29 -26
  52. package/src/cli/templates/docker.ts +41 -30
  53. package/src/cli/templates/frontend/index.ts +63 -0
  54. package/src/cli/templates/frontend/none.ts +17 -0
  55. package/src/cli/templates/frontend/react.ts +140 -0
  56. package/src/cli/templates/frontend/solid.ts +134 -0
  57. package/src/cli/templates/frontend/svelte.ts +131 -0
  58. package/src/cli/templates/frontend/vue.ts +130 -0
  59. package/src/cli/templates/generators/index.ts +339 -0
  60. package/src/cli/templates/generators/types.ts +56 -0
  61. package/src/cli/templates/index.ts +35 -2
  62. package/src/cli/templates/project/api.ts +81 -0
  63. package/src/cli/templates/project/default.ts +140 -0
  64. package/src/cli/templates/project/fullstack.ts +111 -0
  65. package/src/cli/templates/project/index.ts +95 -0
  66. package/src/cli/templates/project/minimal.ts +45 -0
  67. package/src/cli/templates/project/types.ts +94 -0
  68. package/src/cli/templates/project/website.ts +263 -0
  69. package/src/cli/utils/fs.ts +55 -41
  70. package/src/cli/utils/index.ts +3 -2
  71. package/src/cli/utils/strings.ts +47 -33
  72. package/src/cli/utils/version.ts +47 -0
  73. package/src/config/env-validation.ts +100 -0
  74. package/src/config/env.ts +169 -41
  75. package/src/config/index.ts +28 -20
  76. package/src/config/loader.ts +25 -16
  77. package/src/config/merge.ts +21 -10
  78. package/src/config/types.ts +545 -25
  79. package/src/config/validation.ts +215 -7
  80. package/src/container/forward-ref.ts +22 -22
  81. package/src/container/index.ts +34 -12
  82. package/src/context/index.ts +11 -1
  83. package/src/database/index.ts +7 -190
  84. package/src/database/orm/builder.ts +457 -0
  85. package/src/database/orm/casts/index.ts +130 -0
  86. package/src/database/orm/casts/types.ts +25 -0
  87. package/src/database/orm/compiler.ts +304 -0
  88. package/src/database/orm/hooks/index.ts +114 -0
  89. package/src/database/orm/index.ts +61 -0
  90. package/src/database/orm/model-registry.ts +59 -0
  91. package/src/database/orm/model.ts +821 -0
  92. package/src/database/orm/relationships/base.ts +146 -0
  93. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  94. package/src/database/orm/relationships/belongs-to.ts +56 -0
  95. package/src/database/orm/relationships/has-many.ts +45 -0
  96. package/src/database/orm/relationships/has-one.ts +41 -0
  97. package/src/database/orm/relationships/index.ts +11 -0
  98. package/src/database/orm/scopes/index.ts +55 -0
  99. package/src/events/__tests__/event-system.test.ts +235 -0
  100. package/src/events/config.ts +238 -0
  101. package/src/events/example-usage.ts +185 -0
  102. package/src/events/index.ts +278 -0
  103. package/src/events/manager.ts +385 -0
  104. package/src/events/registry.ts +182 -0
  105. package/src/events/types.ts +124 -0
  106. package/src/frontend/api-routes.ts +65 -23
  107. package/src/frontend/bundler.ts +76 -34
  108. package/src/frontend/console-client.ts +2 -2
  109. package/src/frontend/console-stream.ts +94 -38
  110. package/src/frontend/dev-server.ts +94 -46
  111. package/src/frontend/file-router.ts +61 -19
  112. package/src/frontend/frameworks/index.ts +37 -10
  113. package/src/frontend/frameworks/react.ts +10 -8
  114. package/src/frontend/frameworks/solid.ts +11 -9
  115. package/src/frontend/frameworks/svelte.ts +15 -9
  116. package/src/frontend/frameworks/vue.ts +13 -11
  117. package/src/frontend/hmr-client.ts +12 -10
  118. package/src/frontend/hmr.ts +146 -103
  119. package/src/frontend/index.ts +14 -5
  120. package/src/frontend/islands.ts +41 -22
  121. package/src/frontend/isr.ts +59 -37
  122. package/src/frontend/layout.ts +36 -21
  123. package/src/frontend/ssr/react.ts +74 -27
  124. package/src/frontend/ssr/solid.ts +54 -20
  125. package/src/frontend/ssr/svelte.ts +48 -14
  126. package/src/frontend/ssr/vue.ts +50 -18
  127. package/src/frontend/ssr.ts +83 -39
  128. package/src/frontend/types.ts +91 -56
  129. package/src/health/index.ts +21 -9
  130. package/src/i18n/engine.ts +305 -0
  131. package/src/i18n/index.ts +38 -0
  132. package/src/i18n/loader.ts +218 -0
  133. package/src/i18n/middleware.ts +164 -0
  134. package/src/i18n/negotiator.ts +162 -0
  135. package/src/i18n/types.ts +158 -0
  136. package/src/index.ts +179 -27
  137. package/src/jobs/drivers/memory.ts +315 -0
  138. package/src/jobs/drivers/redis.ts +459 -0
  139. package/src/jobs/index.ts +30 -0
  140. package/src/jobs/queue.ts +281 -0
  141. package/src/jobs/types.ts +295 -0
  142. package/src/jobs/worker.ts +380 -0
  143. package/src/logger/index.ts +1 -3
  144. package/src/logger/transports/index.ts +62 -22
  145. package/src/metrics/index.ts +25 -16
  146. package/src/migrations/index.ts +9 -0
  147. package/src/modules/filters.ts +13 -17
  148. package/src/modules/guards.ts +49 -26
  149. package/src/modules/index.ts +409 -298
  150. package/src/modules/interceptors.ts +58 -20
  151. package/src/modules/lazy.ts +11 -19
  152. package/src/modules/lifecycle.ts +15 -7
  153. package/src/modules/metadata.ts +15 -5
  154. package/src/modules/pipes.ts +94 -72
  155. package/src/notification/channels/base.ts +68 -0
  156. package/src/notification/channels/email.ts +105 -0
  157. package/src/notification/channels/push.ts +104 -0
  158. package/src/notification/channels/sms.ts +105 -0
  159. package/src/notification/channels/whatsapp.ts +104 -0
  160. package/src/notification/index.ts +48 -0
  161. package/src/notification/service.ts +354 -0
  162. package/src/notification/types.ts +344 -0
  163. package/src/observability/__tests__/observability.test.ts +483 -0
  164. package/src/observability/breadcrumbs.ts +114 -0
  165. package/src/observability/index.ts +136 -0
  166. package/src/observability/interceptor.ts +85 -0
  167. package/src/observability/service.ts +303 -0
  168. package/src/observability/trace.ts +37 -0
  169. package/src/observability/types.ts +196 -0
  170. package/src/openapi/__tests__/decorators.test.ts +335 -0
  171. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  172. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  173. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  174. package/src/openapi/decorators.ts +328 -0
  175. package/src/openapi/document-builder.ts +274 -0
  176. package/src/openapi/index.ts +112 -0
  177. package/src/openapi/metadata.ts +112 -0
  178. package/src/openapi/route-scanner.ts +289 -0
  179. package/src/openapi/schema-generator.ts +256 -0
  180. package/src/openapi/swagger-module.ts +166 -0
  181. package/src/openapi/types.ts +398 -0
  182. package/src/orm/index.ts +10 -0
  183. package/src/rpc/index.ts +3 -1
  184. package/src/schema/index.ts +9 -0
  185. package/src/security/index.ts +15 -6
  186. package/src/ssg/index.ts +9 -8
  187. package/src/telemetry/index.ts +76 -22
  188. package/src/template/index.ts +7 -0
  189. package/src/templates/engine.ts +224 -0
  190. package/src/templates/index.ts +9 -0
  191. package/src/templates/loader.ts +331 -0
  192. package/src/templates/renderers/markdown.ts +212 -0
  193. package/src/templates/renderers/simple.ts +269 -0
  194. package/src/templates/types.ts +154 -0
  195. package/src/testing/index.ts +100 -27
  196. package/src/types/optional-deps.d.ts +347 -187
  197. package/src/validation/index.ts +92 -2
  198. package/src/validation/schemas.ts +536 -0
  199. package/tests/integration/fullstack.test.ts +4 -4
  200. package/tests/unit/database.test.ts +2 -72
  201. package/tests/unit/env-validation.test.ts +166 -0
  202. package/tests/unit/events.test.ts +910 -0
  203. package/tests/unit/i18n.test.ts +455 -0
  204. package/tests/unit/jobs.test.ts +493 -0
  205. package/tests/unit/notification.test.ts +988 -0
  206. package/tests/unit/observability.test.ts +453 -0
  207. package/tests/unit/orm/builder.test.ts +323 -0
  208. package/tests/unit/orm/casts.test.ts +179 -0
  209. package/tests/unit/orm/compiler.test.ts +220 -0
  210. package/tests/unit/orm/eager-loading.test.ts +285 -0
  211. package/tests/unit/orm/hooks.test.ts +191 -0
  212. package/tests/unit/orm/model.test.ts +373 -0
  213. package/tests/unit/orm/relationships.test.ts +303 -0
  214. package/tests/unit/orm/scopes.test.ts +74 -0
  215. package/tests/unit/templates-simple.test.ts +53 -0
  216. package/tests/unit/templates.test.ts +454 -0
  217. package/tests/unit/validation.test.ts +18 -24
  218. 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 from .env files
237
- const fileVars = await loadEnvFiles(options);
238
-
239
- // Merge with Bun.env if requested
240
- const raw: Record<string, string> =
241
- options?.mergeWithProcess !== false
242
- ? { ...fileVars, ...Object.fromEntries(Object.entries(Bun.env).filter(([, v]) => v !== undefined) as [string, string][]) }
243
- : fileVars;
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
- // Set loaded vars to Bun.env
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
- { envVar: "BUENO_DEV", configKey: "server.development", transform: parseEnvBoolean },
443
+ {
444
+ envVar: "BUENO_DEV",
445
+ configKey: "server.development",
446
+ transform: parseEnvBoolean,
447
+ },
355
448
  { envVar: "DATABASE_URL", configKey: "database.url" },
356
- { envVar: "DATABASE_POOL_SIZE", configKey: "database.poolSize", transform: parseEnvNumber },
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
- { envVar: "LOG_PRETTY", configKey: "logger.pretty", transform: parseEnvBoolean },
362
- { envVar: "HEALTH_ENABLED", configKey: "health.enabled", transform: parseEnvBoolean },
363
- { envVar: "METRICS_ENABLED", configKey: "metrics.enabled", transform: parseEnvBoolean },
364
- { envVar: "TELEMETRY_ENABLED", configKey: "telemetry.enabled", transform: parseEnvBoolean },
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
- { envVar: "FRONTEND_DEV_SERVER", configKey: "frontend.devServer", transform: parseEnvBoolean },
368
- { envVar: "FRONTEND_HMR", configKey: "frontend.hmr", transform: parseEnvBoolean },
369
- { envVar: "FRONTEND_PORT", configKey: "frontend.port", transform: parseEnvNumber },
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(key: string, defaultValue?: string): string | undefined {
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
+ }
@@ -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 = (options.useDefaults !== false
182
- ? { ...DEFAULT_CONFIG }
183
- : {}) as T;
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<string, unknown>;
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(this.config as unknown as DeepPartial<BuenoConfig>);
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 extends BuenoConfig = BuenoConfig,
457
- >(options?: ConfigManagerOptions<T>): Promise<ConfigManager<T>> {
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 extends BuenoConfig = BuenoConfig,
483
- >(options?: ConfigManagerOptions<T>): Promise<T> {
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
+ }
@@ -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(path: string): DeepPartial<BuenoConfig> | undefined {
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<T extends BuenoConfig = BuenoConfig>(
156
- options?: {
157
- /** Custom config file path */
158
- configPath?: string;
159
- /** Working directory to search for config */
160
- cwd?: string;
161
- /** Whether to use cache */
162
- useCache?: boolean;
163
- /** Additional context to pass to config function */
164
- context?: Record<string, unknown>;
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(args: string[] = process.argv): string | undefined {
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
+ }
@@ -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(value: unknown): value is Record<string, unknown> {
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(targetValue, sourceValue as DeepPartial<typeof targetValue>);
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>>((acc, config) => {
84
- if (config === undefined || config === null) {
85
- return acc;
86
- }
87
- // Use unknown as intermediate type to avoid recursive type issues
88
- return deepMerge(acc as unknown as T, config as unknown as DeepPartial<T>) as unknown as DeepPartial<T>;
89
- }, {} as DeepPartial<T>);
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
+ }