@buenojs/bueno 0.8.4 → 0.8.6

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 (234) hide show
  1. package/README.md +264 -17
  2. package/dist/cli/{index.js → bin.js} +413 -332
  3. package/dist/container/index.js +273 -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/graphql/index.js +2156 -0
  8. package/dist/health/index.js +364 -0
  9. package/dist/i18n/index.js +345 -0
  10. package/dist/index.js +9694 -5047
  11. package/dist/jobs/index.js +819 -0
  12. package/dist/lock/index.js +367 -0
  13. package/dist/logger/index.js +281 -0
  14. package/dist/metrics/index.js +289 -0
  15. package/dist/middleware/index.js +77 -0
  16. package/dist/migrations/index.js +571 -0
  17. package/dist/modules/index.js +3411 -0
  18. package/dist/notification/index.js +484 -0
  19. package/dist/observability/index.js +331 -0
  20. package/dist/openapi/index.js +795 -0
  21. package/dist/orm/index.js +1356 -0
  22. package/dist/router/index.js +886 -0
  23. package/dist/rpc/index.js +691 -0
  24. package/dist/schema/index.js +400 -0
  25. package/dist/telemetry/index.js +595 -0
  26. package/dist/template/index.js +640 -0
  27. package/dist/templates/index.js +640 -0
  28. package/dist/testing/index.js +1111 -0
  29. package/dist/types/index.js +60 -0
  30. package/llms.txt +231 -0
  31. package/package.json +125 -27
  32. package/src/cache/index.ts +2 -1
  33. package/src/cli/ARCHITECTURE.md +3 -3
  34. package/src/cli/bin.ts +2 -2
  35. package/src/cli/commands/build.ts +183 -165
  36. package/src/cli/commands/dev.ts +96 -89
  37. package/src/cli/commands/generate.ts +142 -111
  38. package/src/cli/commands/help.ts +20 -16
  39. package/src/cli/commands/index.ts +3 -6
  40. package/src/cli/commands/migration.ts +124 -105
  41. package/src/cli/commands/new.ts +294 -232
  42. package/src/cli/commands/start.ts +81 -79
  43. package/src/cli/core/args.ts +68 -50
  44. package/src/cli/core/console.ts +89 -95
  45. package/src/cli/core/index.ts +4 -4
  46. package/src/cli/core/prompt.ts +65 -62
  47. package/src/cli/core/spinner.ts +23 -20
  48. package/src/cli/index.ts +46 -38
  49. package/src/cli/templates/database/index.ts +37 -18
  50. package/src/cli/templates/database/mysql.ts +3 -3
  51. package/src/cli/templates/database/none.ts +2 -2
  52. package/src/cli/templates/database/postgresql.ts +3 -3
  53. package/src/cli/templates/database/sqlite.ts +3 -3
  54. package/src/cli/templates/deploy.ts +29 -26
  55. package/src/cli/templates/docker.ts +41 -30
  56. package/src/cli/templates/frontend/index.ts +33 -15
  57. package/src/cli/templates/frontend/none.ts +2 -2
  58. package/src/cli/templates/frontend/react.ts +18 -18
  59. package/src/cli/templates/frontend/solid.ts +15 -15
  60. package/src/cli/templates/frontend/svelte.ts +17 -17
  61. package/src/cli/templates/frontend/vue.ts +15 -15
  62. package/src/cli/templates/generators/index.ts +29 -29
  63. package/src/cli/templates/generators/types.ts +21 -21
  64. package/src/cli/templates/index.ts +6 -6
  65. package/src/cli/templates/project/api.ts +37 -36
  66. package/src/cli/templates/project/default.ts +25 -25
  67. package/src/cli/templates/project/fullstack.ts +28 -26
  68. package/src/cli/templates/project/index.ts +55 -16
  69. package/src/cli/templates/project/minimal.ts +17 -12
  70. package/src/cli/templates/project/types.ts +10 -5
  71. package/src/cli/templates/project/website.ts +15 -15
  72. package/src/cli/utils/fs.ts +55 -41
  73. package/src/cli/utils/index.ts +3 -3
  74. package/src/cli/utils/strings.ts +47 -33
  75. package/src/cli/utils/version.ts +14 -8
  76. package/src/config/env-validation.ts +100 -0
  77. package/src/config/env.ts +169 -41
  78. package/src/config/index.ts +28 -20
  79. package/src/config/loader.ts +25 -16
  80. package/src/config/merge.ts +21 -10
  81. package/src/config/types.ts +566 -25
  82. package/src/config/validation.ts +215 -7
  83. package/src/container/forward-ref.ts +22 -22
  84. package/src/container/index.ts +34 -12
  85. package/src/context/index.ts +11 -1
  86. package/src/database/index.ts +7 -190
  87. package/src/database/orm/builder.ts +457 -0
  88. package/src/database/orm/casts/index.ts +130 -0
  89. package/src/database/orm/casts/types.ts +25 -0
  90. package/src/database/orm/compiler.ts +304 -0
  91. package/src/database/orm/hooks/index.ts +114 -0
  92. package/src/database/orm/index.ts +61 -0
  93. package/src/database/orm/model-registry.ts +59 -0
  94. package/src/database/orm/model.ts +821 -0
  95. package/src/database/orm/relationships/base.ts +146 -0
  96. package/src/database/orm/relationships/belongs-to-many.ts +179 -0
  97. package/src/database/orm/relationships/belongs-to.ts +56 -0
  98. package/src/database/orm/relationships/has-many.ts +45 -0
  99. package/src/database/orm/relationships/has-one.ts +41 -0
  100. package/src/database/orm/relationships/index.ts +11 -0
  101. package/src/database/orm/scopes/index.ts +55 -0
  102. package/src/events/__tests__/event-system.test.ts +235 -0
  103. package/src/events/config.ts +238 -0
  104. package/src/events/example-usage.ts +185 -0
  105. package/src/events/index.ts +278 -0
  106. package/src/events/manager.ts +385 -0
  107. package/src/events/registry.ts +182 -0
  108. package/src/events/types.ts +124 -0
  109. package/src/frontend/api-routes.ts +65 -23
  110. package/src/frontend/bundler.ts +76 -34
  111. package/src/frontend/console-client.ts +2 -2
  112. package/src/frontend/console-stream.ts +94 -38
  113. package/src/frontend/dev-server.ts +94 -46
  114. package/src/frontend/file-router.ts +61 -19
  115. package/src/frontend/frameworks/index.ts +37 -10
  116. package/src/frontend/frameworks/react.ts +10 -8
  117. package/src/frontend/frameworks/solid.ts +11 -9
  118. package/src/frontend/frameworks/svelte.ts +15 -9
  119. package/src/frontend/frameworks/vue.ts +13 -11
  120. package/src/frontend/hmr-client.ts +12 -10
  121. package/src/frontend/hmr.ts +146 -103
  122. package/src/frontend/index.ts +14 -5
  123. package/src/frontend/islands.ts +41 -22
  124. package/src/frontend/isr.ts +59 -37
  125. package/src/frontend/layout.ts +36 -21
  126. package/src/frontend/ssr/react.ts +74 -27
  127. package/src/frontend/ssr/solid.ts +54 -20
  128. package/src/frontend/ssr/svelte.ts +48 -14
  129. package/src/frontend/ssr/vue.ts +50 -18
  130. package/src/frontend/ssr.ts +83 -39
  131. package/src/frontend/types.ts +91 -56
  132. package/src/graphql/built-in-engine.ts +598 -0
  133. package/src/graphql/context-builder.ts +110 -0
  134. package/src/graphql/decorators.ts +358 -0
  135. package/src/graphql/execution-pipeline.ts +227 -0
  136. package/src/graphql/graphql-module.ts +563 -0
  137. package/src/graphql/index.ts +101 -0
  138. package/src/graphql/metadata.ts +237 -0
  139. package/src/graphql/schema-builder.ts +319 -0
  140. package/src/graphql/subscription-handler.ts +283 -0
  141. package/src/graphql/types.ts +324 -0
  142. package/src/health/index.ts +21 -9
  143. package/src/i18n/engine.ts +305 -0
  144. package/src/i18n/index.ts +38 -0
  145. package/src/i18n/loader.ts +218 -0
  146. package/src/i18n/middleware.ts +164 -0
  147. package/src/i18n/negotiator.ts +162 -0
  148. package/src/i18n/types.ts +158 -0
  149. package/src/index.ts +182 -27
  150. package/src/jobs/drivers/memory.ts +315 -0
  151. package/src/jobs/drivers/redis.ts +459 -0
  152. package/src/jobs/index.ts +30 -0
  153. package/src/jobs/queue.ts +281 -0
  154. package/src/jobs/types.ts +295 -0
  155. package/src/jobs/worker.ts +380 -0
  156. package/src/logger/index.ts +1 -3
  157. package/src/logger/transports/index.ts +62 -22
  158. package/src/metrics/index.ts +25 -16
  159. package/src/migrations/index.ts +9 -0
  160. package/src/modules/filters.ts +13 -17
  161. package/src/modules/guards.ts +49 -26
  162. package/src/modules/index.ts +457 -299
  163. package/src/modules/interceptors.ts +58 -20
  164. package/src/modules/lazy.ts +11 -19
  165. package/src/modules/lifecycle.ts +15 -7
  166. package/src/modules/metadata.ts +15 -5
  167. package/src/modules/pipes.ts +94 -72
  168. package/src/notification/channels/base.ts +68 -0
  169. package/src/notification/channels/email.ts +105 -0
  170. package/src/notification/channels/push.ts +104 -0
  171. package/src/notification/channels/sms.ts +105 -0
  172. package/src/notification/channels/whatsapp.ts +104 -0
  173. package/src/notification/index.ts +48 -0
  174. package/src/notification/service.ts +354 -0
  175. package/src/notification/types.ts +344 -0
  176. package/src/observability/__tests__/observability.test.ts +483 -0
  177. package/src/observability/breadcrumbs.ts +114 -0
  178. package/src/observability/index.ts +136 -0
  179. package/src/observability/interceptor.ts +85 -0
  180. package/src/observability/service.ts +303 -0
  181. package/src/observability/trace.ts +37 -0
  182. package/src/observability/types.ts +196 -0
  183. package/src/openapi/__tests__/decorators.test.ts +335 -0
  184. package/src/openapi/__tests__/document-builder.test.ts +285 -0
  185. package/src/openapi/__tests__/route-scanner.test.ts +334 -0
  186. package/src/openapi/__tests__/schema-generator.test.ts +275 -0
  187. package/src/openapi/decorators.ts +328 -0
  188. package/src/openapi/document-builder.ts +274 -0
  189. package/src/openapi/index.ts +112 -0
  190. package/src/openapi/metadata.ts +112 -0
  191. package/src/openapi/route-scanner.ts +289 -0
  192. package/src/openapi/schema-generator.ts +256 -0
  193. package/src/openapi/swagger-module.ts +166 -0
  194. package/src/openapi/types.ts +398 -0
  195. package/src/orm/index.ts +10 -0
  196. package/src/rpc/index.ts +3 -1
  197. package/src/schema/index.ts +9 -0
  198. package/src/security/index.ts +15 -6
  199. package/src/ssg/index.ts +9 -8
  200. package/src/telemetry/index.ts +76 -22
  201. package/src/template/index.ts +7 -0
  202. package/src/templates/engine.ts +224 -0
  203. package/src/templates/index.ts +9 -0
  204. package/src/templates/loader.ts +331 -0
  205. package/src/templates/renderers/markdown.ts +212 -0
  206. package/src/templates/renderers/simple.ts +269 -0
  207. package/src/templates/types.ts +154 -0
  208. package/src/testing/index.ts +100 -27
  209. package/src/types/optional-deps.d.ts +347 -187
  210. package/src/validation/index.ts +92 -2
  211. package/src/validation/schemas.ts +536 -0
  212. package/tests/integration/cli.test.ts +19 -19
  213. package/tests/integration/fullstack.test.ts +4 -4
  214. package/tests/unit/cli.test.ts +1 -1
  215. package/tests/unit/database.test.ts +2 -72
  216. package/tests/unit/env-validation.test.ts +166 -0
  217. package/tests/unit/events.test.ts +910 -0
  218. package/tests/unit/graphql.test.ts +991 -0
  219. package/tests/unit/i18n.test.ts +455 -0
  220. package/tests/unit/jobs.test.ts +493 -0
  221. package/tests/unit/notification.test.ts +988 -0
  222. package/tests/unit/observability.test.ts +453 -0
  223. package/tests/unit/orm/builder.test.ts +323 -0
  224. package/tests/unit/orm/casts.test.ts +179 -0
  225. package/tests/unit/orm/compiler.test.ts +220 -0
  226. package/tests/unit/orm/eager-loading.test.ts +285 -0
  227. package/tests/unit/orm/hooks.test.ts +191 -0
  228. package/tests/unit/orm/model.test.ts +373 -0
  229. package/tests/unit/orm/relationships.test.ts +303 -0
  230. package/tests/unit/orm/scopes.test.ts +74 -0
  231. package/tests/unit/templates-simple.test.ts +53 -0
  232. package/tests/unit/templates.test.ts +454 -0
  233. package/tests/unit/validation.test.ts +18 -24
  234. package/tsconfig.json +11 -3
@@ -13,19 +13,26 @@ export type {
13
13
  DatabaseDriver,
14
14
  TemplateFile,
15
15
  SelectOption,
16
- } from './types';
16
+ } from "./types";
17
17
 
18
- import type { ProjectTemplate, ProjectConfig, ProjectTemplateResult } from './types';
19
- import { defaultTemplate } from './default';
20
- import { minimalTemplate } from './minimal';
21
- import { fullstackTemplate } from './fullstack';
22
- import { apiTemplate } from './api';
23
- import { websiteTemplate } from './website';
18
+ import { apiTemplate } from "./api";
19
+ import { defaultTemplate } from "./default";
20
+ import { fullstackTemplate } from "./fullstack";
21
+ import { minimalTemplate } from "./minimal";
22
+ import type {
23
+ ProjectConfig,
24
+ ProjectTemplate,
25
+ ProjectTemplateResult,
26
+ } from "./types";
27
+ import { websiteTemplate } from "./website";
24
28
 
25
29
  /**
26
30
  * Project template registry
27
31
  */
28
- const projectTemplates: Record<ProjectTemplate, (config: ProjectConfig) => ProjectTemplateResult> = {
32
+ const projectTemplates: Record<
33
+ ProjectTemplate,
34
+ (config: ProjectConfig) => ProjectTemplateResult
35
+ > = {
29
36
  default: defaultTemplate,
30
37
  minimal: minimalTemplate,
31
38
  fullstack: fullstackTemplate,
@@ -36,21 +43,53 @@ const projectTemplates: Record<ProjectTemplate, (config: ProjectConfig) => Proje
36
43
  /**
37
44
  * Get project template based on template type
38
45
  */
39
- export function getProjectTemplate(template: ProjectTemplate): (config: ProjectConfig) => ProjectTemplateResult {
46
+ export function getProjectTemplate(
47
+ template: ProjectTemplate,
48
+ ): (config: ProjectConfig) => ProjectTemplateResult {
40
49
  return projectTemplates[template];
41
50
  }
42
51
 
43
52
  /**
44
53
  * Get template selection options for prompts
45
54
  */
46
- export function getTemplateOptions(): { value: ProjectTemplate; name: string; description: string }[] {
55
+ export function getTemplateOptions(): {
56
+ value: ProjectTemplate;
57
+ name: string;
58
+ description: string;
59
+ }[] {
47
60
  return [
48
- { value: 'default', name: 'Default', description: 'Standard project with modules and database' },
49
- { value: 'minimal', name: 'Minimal', description: 'Bare minimum project structure' },
50
- { value: 'fullstack', name: 'Fullstack', description: 'Full-stack project with SSR and frontend' },
51
- { value: 'api', name: 'API', description: 'API-only project without frontend' },
52
- { value: 'website', name: 'Website', description: 'Static website with SSG' },
61
+ {
62
+ value: "default",
63
+ name: "Default",
64
+ description: "Standard project with modules and database",
65
+ },
66
+ {
67
+ value: "minimal",
68
+ name: "Minimal",
69
+ description: "Bare minimum project structure",
70
+ },
71
+ {
72
+ value: "fullstack",
73
+ name: "Fullstack",
74
+ description: "Full-stack project with SSR and frontend",
75
+ },
76
+ {
77
+ value: "api",
78
+ name: "API",
79
+ description: "API-only project without frontend",
80
+ },
81
+ {
82
+ value: "website",
83
+ name: "Website",
84
+ description: "Static website with SSG",
85
+ },
53
86
  ];
54
87
  }
55
88
 
56
- export { defaultTemplate, minimalTemplate, fullstackTemplate, apiTemplate, websiteTemplate };
89
+ export {
90
+ defaultTemplate,
91
+ minimalTemplate,
92
+ fullstackTemplate,
93
+ apiTemplate,
94
+ websiteTemplate,
95
+ };
@@ -2,39 +2,44 @@
2
2
  * Minimal Project Template
3
3
  */
4
4
 
5
- import type { ProjectConfig, ProjectTemplateResult } from './types';
6
- import { getBuenoDependency } from '../../utils/version';
5
+ import { getBuenoDependency } from "../../utils/version";
6
+ import type { ProjectConfig, ProjectTemplateResult } from "./types";
7
7
 
8
8
  export function minimalTemplate(config: ProjectConfig): ProjectTemplateResult {
9
9
  return {
10
10
  files: [
11
11
  {
12
- path: 'server/main.ts',
12
+ path: "server/main.ts",
13
13
  content: `import { createServer } from '@buenojs/bueno';
14
14
 
15
15
  const app = createServer();
16
16
 
17
17
  app.router.get('/', () => {
18
- return { message: 'Hello, Bueno!' };
18
+ return Response.json({ message: 'Hello, Bueno!' });
19
+ });
20
+
21
+ app.router.get('/health', () => {
22
+ return Response.json({ status: 'ok', timestamp: new Date().toISOString() });
19
23
  });
20
24
 
21
25
  await app.listen(3000);
26
+ console.log('🚀 Server running at http://localhost:3000');
22
27
  `,
23
28
  },
24
29
  ],
25
- directories: ['server', 'tests'],
30
+ directories: ["server", "tests"],
26
31
  dependencies: {
27
32
  ...getBuenoDependency(),
28
33
  },
29
34
  devDependencies: {
30
- '@types/bun': 'latest',
31
- typescript: '^5.3.0',
35
+ "@types/bun": "latest",
36
+ typescript: "^5.3.0",
32
37
  },
33
38
  scripts: {
34
- dev: 'bun run --watch server/main.ts',
35
- build: 'bun build ./server/main.ts --outdir ./dist --target bun',
36
- start: 'bun run dist/main.js',
37
- test: 'bun test',
39
+ dev: "bun run --watch server/main.ts",
40
+ build: "bun build ./server/main.ts --outdir ./dist --target bun",
41
+ start: "bun run dist/main.js",
42
+ test: "bun test",
38
43
  },
39
44
  };
40
- }
45
+ }
@@ -7,22 +7,27 @@
7
7
  /**
8
8
  * Project templates
9
9
  */
10
- export type ProjectTemplate = 'default' | 'minimal' | 'fullstack' | 'api' | 'website';
10
+ export type ProjectTemplate =
11
+ | "default"
12
+ | "minimal"
13
+ | "fullstack"
14
+ | "api"
15
+ | "website";
11
16
 
12
17
  /**
13
18
  * Frontend frameworks
14
19
  */
15
- export type FrontendFramework = 'react' | 'vue' | 'svelte' | 'solid' | 'none';
20
+ export type FrontendFramework = "react" | "vue" | "svelte" | "solid" | "none";
16
21
 
17
22
  /**
18
23
  * Database drivers
19
24
  */
20
- export type DatabaseDriver = 'sqlite' | 'postgresql' | 'mysql' | 'none';
25
+ export type DatabaseDriver = "sqlite" | "postgresql" | "mysql" | "none";
21
26
 
22
27
  /**
23
28
  * Deploy platforms
24
29
  */
25
- export type DeployPlatform = 'render' | 'fly' | 'railway';
30
+ export type DeployPlatform = "render" | "fly" | "railway";
26
31
 
27
32
  /**
28
33
  * Project configuration interface
@@ -86,4 +91,4 @@ export interface SelectOption<T> {
86
91
  value: T;
87
92
  name: string;
88
93
  description?: string;
89
- }
94
+ }
@@ -4,21 +4,21 @@
4
4
  * Static website template using SSG (Static Site Generation)
5
5
  */
6
6
 
7
- import type { ProjectConfig, ProjectTemplateResult } from './types';
8
- import { getBuenoDependency } from '../../utils/version';
7
+ import { getBuenoDependency } from "../../utils/version";
8
+ import type { ProjectConfig, ProjectTemplateResult } from "./types";
9
9
 
10
10
  export function websiteTemplate(config: ProjectConfig): ProjectTemplateResult {
11
11
  return {
12
12
  files: [
13
13
  {
14
- path: 'src/build.ts',
14
+ path: "src/build.ts",
15
15
  content: `/**
16
16
  * Build script for ${config.name}
17
17
  *
18
18
  * Uses Bueno's SSG module to generate static HTML from markdown content
19
19
  */
20
20
 
21
- import { SSG, createSSG, type SiteConfig, type LayoutContext } from 'bueno';
21
+ import { SSG, createSSG, type SiteConfig, type LayoutContext } from '@buenojs/bueno';
22
22
 
23
23
  // Site configuration
24
24
  const siteConfig: Partial<SiteConfig> = {
@@ -80,7 +80,7 @@ function renderDefaultLayout(ctx: LayoutContext): string {
80
80
  `,
81
81
  },
82
82
  {
83
- path: 'src/serve.ts',
83
+ path: "src/serve.ts",
84
84
  content: `/**
85
85
  * Development server for serving the built website
86
86
  */
@@ -131,7 +131,7 @@ serve();
131
131
  `,
132
132
  },
133
133
  {
134
- path: 'content/index.md',
134
+ path: "content/index.md",
135
135
  content: `---
136
136
  title: Welcome
137
137
  description: Welcome to my website
@@ -150,7 +150,7 @@ This is a static website built with [Bueno Framework](https://buenojs.dev).
150
150
  `,
151
151
  },
152
152
  {
153
- path: 'public/styles/main.css',
153
+ path: "public/styles/main.css",
154
154
  content: `/* Main styles for the website */
155
155
  * {
156
156
  box-sizing: border-box;
@@ -234,7 +234,7 @@ pre {
234
234
  `,
235
235
  },
236
236
  {
237
- path: '.env.example',
237
+ path: ".env.example",
238
238
  content: `# ${config.name} Environment Variables
239
239
  # Copy this file to .env and customize as needed
240
240
 
@@ -246,18 +246,18 @@ NODE_ENV=development
246
246
  `,
247
247
  },
248
248
  ],
249
- directories: ['src', 'content', 'public/styles', 'layouts'],
249
+ directories: ["src", "content", "public/styles", "layouts"],
250
250
  dependencies: {
251
251
  ...getBuenoDependency(),
252
252
  },
253
253
  devDependencies: {
254
- '@types/bun': 'latest',
255
- typescript: '^5.3.0',
254
+ "@types/bun": "latest",
255
+ typescript: "^5.3.0",
256
256
  },
257
257
  scripts: {
258
- dev: 'bun run --watch src/build.ts --dev',
259
- build: 'bun run src/build.ts',
260
- serve: 'bun run src/serve.ts',
258
+ dev: "bun run --watch src/build.ts --dev",
259
+ build: "bun run src/build.ts",
260
+ serve: "bun run src/serve.ts",
261
261
  },
262
262
  };
263
- }
263
+ }
@@ -4,8 +4,8 @@
4
4
  * Provides file system operations using Bun's native APIs
5
5
  */
6
6
 
7
- import * as fs from 'fs';
8
- import * as path from 'path';
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
9
 
10
10
  /**
11
11
  * Check if a file exists
@@ -73,7 +73,7 @@ export async function readFile(filePath: string): Promise<string> {
73
73
  * Read a file as string (sync)
74
74
  */
75
75
  export function readFileSync(filePath: string): string {
76
- return fs.readFileSync(filePath, 'utf-8');
76
+ return fs.readFileSync(filePath, "utf-8");
77
77
  }
78
78
 
79
79
  /**
@@ -98,7 +98,7 @@ export function writeFileSync(filePath: string, content: string): void {
98
98
  const dir = path.dirname(filePath);
99
99
  createDirectorySync(dir);
100
100
 
101
- fs.writeFileSync(filePath, content, 'utf-8');
101
+ fs.writeFileSync(filePath, content, "utf-8");
102
102
  }
103
103
 
104
104
  /**
@@ -132,10 +132,7 @@ export function deleteDirectorySync(dirPath: string): void {
132
132
  /**
133
133
  * Copy a file
134
134
  */
135
- export async function copyFile(
136
- src: string,
137
- dest: string,
138
- ): Promise<void> {
135
+ export async function copyFile(src: string, dest: string): Promise<void> {
139
136
  // Ensure destination directory exists
140
137
  const dir = path.dirname(dest);
141
138
  await createDirectory(dir);
@@ -211,9 +208,9 @@ export async function findFileUp(
211
208
  options: { stopAt?: string } = {},
212
209
  ): Promise<string | null> {
213
210
  let currentDir = startDir;
214
- const stopAt = options.stopAt ?? '/';
211
+ const stopAt = options.stopAt ?? "/";
215
212
 
216
- while (currentDir !== stopAt && currentDir !== '/') {
213
+ while (currentDir !== stopAt && currentDir !== "/") {
217
214
  const filePath = path.join(currentDir, fileName);
218
215
  if (await fileExists(filePath)) {
219
216
  return filePath;
@@ -231,7 +228,7 @@ export async function getProjectRoot(
231
228
  startDir: string = process.cwd(),
232
229
  ): Promise<string | null> {
233
230
  // Look for package.json as indicator
234
- const packageJsonPath = await findFileUp(startDir, 'package.json');
231
+ const packageJsonPath = await findFileUp(startDir, "package.json");
235
232
  if (packageJsonPath) {
236
233
  return path.dirname(packageJsonPath);
237
234
  }
@@ -248,10 +245,10 @@ export async function isBuenoProject(
248
245
  if (!root) return false;
249
246
 
250
247
  // Check for bueno.config.ts or package.json with bueno dependency
251
- const configPath = path.join(root, 'bueno.config.ts');
248
+ const configPath = path.join(root, "bueno.config.ts");
252
249
  if (await fileExists(configPath)) return true;
253
250
 
254
- const packageJsonPath = path.join(root, 'package.json');
251
+ const packageJsonPath = path.join(root, "package.json");
255
252
  if (await fileExists(packageJsonPath)) {
256
253
  const content = await readFile(packageJsonPath);
257
254
  try {
@@ -281,9 +278,10 @@ export async function writeJson(
281
278
  data: unknown,
282
279
  options: { pretty?: boolean } = {},
283
280
  ): Promise<void> {
284
- const content = options.pretty !== false
285
- ? JSON.stringify(data, null, 2)
286
- : JSON.stringify(data);
281
+ const content =
282
+ options.pretty !== false
283
+ ? JSON.stringify(data, null, 2)
284
+ : JSON.stringify(data);
287
285
  await writeFile(filePath, content);
288
286
  }
289
287
 
@@ -326,7 +324,7 @@ export function getExtName(filePath: string): string {
326
324
  * Normalize path separators
327
325
  */
328
326
  export function normalizePath(filePath: string): string {
329
- return filePath.replace(/\\/g, '/');
327
+ return filePath.replace(/\\/g, "/");
330
328
  }
331
329
 
332
330
  /**
@@ -339,10 +337,7 @@ export interface TemplateData {
339
337
  /**
340
338
  * Process a template string
341
339
  */
342
- export function processTemplate(
343
- template: string,
344
- data: TemplateData,
345
- ): string {
340
+ export function processTemplate(template: string, data: TemplateData): string {
346
341
  let result = template;
347
342
 
348
343
  // Process conditionals: {{#if key}}...{{/if}}
@@ -350,7 +345,7 @@ export function processTemplate(
350
345
  /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g,
351
346
  (_, key: string, content: string) => {
352
347
  const value = data[key];
353
- return value ? content : '';
348
+ return value ? content : "";
354
349
  },
355
350
  );
356
351
 
@@ -359,55 +354,74 @@ export function processTemplate(
359
354
  /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g,
360
355
  (_, key: string, content: string) => {
361
356
  const items = data[key];
362
- if (!Array.isArray(items)) return '';
357
+ if (!Array.isArray(items)) return "";
363
358
 
364
359
  return items
365
360
  .map((item) => {
366
361
  let itemContent = content;
367
- if (typeof item === 'object' && item !== null) {
362
+ if (typeof item === "object" && item !== null) {
368
363
  // Replace nested properties
369
364
  for (const [k, v] of Object.entries(item)) {
370
365
  itemContent = itemContent.replace(
371
- new RegExp(`\\{\\{${k}\\}\\}`, 'g'),
366
+ new RegExp(`\\{\\{${k}\\}\\}`, "g"),
372
367
  String(v),
373
368
  );
374
369
  }
375
370
  }
376
371
  return itemContent;
377
372
  })
378
- .join('');
373
+ .join("");
379
374
  },
380
375
  );
381
376
 
382
377
  // Process simple variables with helpers: {{helperName key}}
383
378
  const helpers: Record<string, (v: string) => string> = {
384
379
  camelCase: (v) =>
385
- v.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : '')).replace(/^(.)/, (c) => c.toLowerCase()),
380
+ v
381
+ .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
382
+ .replace(/^(.)/, (c) => c.toLowerCase()),
386
383
  pascalCase: (v) =>
387
- v.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : '')).replace(/^(.)/, (c) => c.toUpperCase()),
384
+ v
385
+ .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
386
+ .replace(/^(.)/, (c) => c.toUpperCase()),
388
387
  kebabCase: (v) =>
389
- v.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[-_\s]+/g, '-').toLowerCase(),
388
+ v
389
+ .replace(/([a-z])([A-Z])/g, "$1-$2")
390
+ .replace(/[-_\s]+/g, "-")
391
+ .toLowerCase(),
390
392
  snakeCase: (v) =>
391
- v.replace(/([a-z])([A-Z])/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase(),
393
+ v
394
+ .replace(/([a-z])([A-Z])/g, "$1_$2")
395
+ .replace(/[-\s]+/g, "_")
396
+ .toLowerCase(),
392
397
  upperCase: (v) => v.toUpperCase(),
393
398
  lowerCase: (v) => v.toLowerCase(),
394
399
  capitalize: (v) => v.charAt(0).toUpperCase() + v.slice(1),
395
400
  pluralize: (v) => {
396
- if (v.endsWith('y') && !['ay', 'ey', 'iy', 'oy', 'uy'].some((e) => v.endsWith(e))) {
397
- return v.slice(0, -1) + 'ies';
401
+ if (
402
+ v.endsWith("y") &&
403
+ !["ay", "ey", "iy", "oy", "uy"].some((e) => v.endsWith(e))
404
+ ) {
405
+ return v.slice(0, -1) + "ies";
398
406
  }
399
- if (v.endsWith('s') || v.endsWith('x') || v.endsWith('z') || v.endsWith('ch') || v.endsWith('sh')) {
400
- return v + 'es';
407
+ if (
408
+ v.endsWith("s") ||
409
+ v.endsWith("x") ||
410
+ v.endsWith("z") ||
411
+ v.endsWith("ch") ||
412
+ v.endsWith("sh")
413
+ ) {
414
+ return v + "es";
401
415
  }
402
- return v + 's';
416
+ return v + "s";
403
417
  },
404
418
  };
405
419
 
406
420
  for (const [helperName, helperFn] of Object.entries(helpers)) {
407
- const regex = new RegExp(`\\{\\{${helperName}\\s+(\\w+)\\}\\}`, 'g');
421
+ const regex = new RegExp(`\\{\\{${helperName}\\s+(\\w+)\\}\\}`, "g");
408
422
  result = result.replace(regex, (_, key: string) => {
409
423
  const value = data[key];
410
- if (typeof value === 'string') {
424
+ if (typeof value === "string") {
411
425
  return helperFn(value);
412
426
  }
413
427
  return String(value);
@@ -416,13 +430,13 @@ export function processTemplate(
416
430
 
417
431
  // Process simple variables: {{key}}
418
432
  for (const [key, value] of Object.entries(data)) {
419
- const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
433
+ const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
420
434
  result = result.replace(regex, String(value));
421
435
  }
422
436
 
423
437
  // Clean up empty lines left by conditionals
424
- result = result.replace(/^\s*\n/gm, '\n');
425
- result = result.replace(/\n{3,}/g, '\n\n');
438
+ result = result.replace(/^\s*\n/gm, "\n");
439
+ result = result.replace(/\n{3,}/g, "\n\n");
426
440
 
427
441
  return result.trim();
428
- }
442
+ }
@@ -4,6 +4,6 @@
4
4
  * Re-exports all utility functions
5
5
  */
6
6
 
7
- export * from './strings';
8
- export * from './fs';
9
- export * from './version';
7
+ export * from "./strings";
8
+ export * from "./fs";
9
+ export * from "./version";