@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
@@ -5,8 +5,8 @@
5
5
  * Falls back to simple input for non-TTY environments
6
6
  */
7
7
 
8
- import * as readline from 'readline';
9
- import { colors } from './console';
8
+ import * as readline from "readline";
9
+ import { colors } from "./console";
10
10
 
11
11
  export interface PromptOptions {
12
12
  default?: string;
@@ -55,11 +55,11 @@ export async function prompt(
55
55
  ): Promise<string> {
56
56
  const defaultValue = options.default;
57
57
  const promptText = defaultValue
58
- ? `${colors.cyan('?')} ${message} ${colors.dim(`(${defaultValue})`)}: `
59
- : `${colors.cyan('?')} ${message}: `;
58
+ ? `${colors.cyan("?")} ${message} ${colors.dim(`(${defaultValue})`)}: `
59
+ : `${colors.cyan("?")} ${message}: `;
60
60
 
61
61
  if (!isInteractive()) {
62
- return defaultValue ?? '';
62
+ return defaultValue ?? "";
63
63
  }
64
64
 
65
65
  return new Promise((resolve) => {
@@ -68,13 +68,14 @@ export async function prompt(
68
68
  rl.question(promptText, (answer) => {
69
69
  rl.close();
70
70
 
71
- const value = answer.trim() || defaultValue || '';
71
+ const value = answer.trim() || defaultValue || "";
72
72
 
73
73
  if (options.validate) {
74
74
  const result = options.validate(value);
75
75
  if (result !== true) {
76
- const errorMsg = typeof result === 'string' ? result : 'Invalid value';
77
- process.stdout.write(`${colors.red('✗')} ${errorMsg}\n`);
76
+ const errorMsg =
77
+ typeof result === "string" ? result : "Invalid value";
78
+ process.stdout.write(`${colors.red("✗")} ${errorMsg}\n`);
78
79
  // Re-prompt on validation failure
79
80
  prompt(message, options).then(resolve);
80
81
  return;
@@ -94,22 +95,24 @@ export async function confirm(
94
95
  options: ConfirmOptions = {},
95
96
  ): Promise<boolean> {
96
97
  const defaultValue = options.default ?? false;
97
- const hint = defaultValue ? 'Y/n' : 'y/N';
98
+ const hint = defaultValue ? "Y/n" : "y/N";
98
99
 
99
100
  if (!isInteractive()) {
100
101
  return defaultValue;
101
102
  }
102
103
 
103
104
  const answer = await prompt(`${message} ${colors.dim(`(${hint})`)}`, {
104
- default: defaultValue ? 'y' : 'n',
105
+ default: defaultValue ? "y" : "n",
105
106
  validate: (value) => {
106
107
  if (!value) return true;
107
- return ['y', 'yes', 'n', 'no'].includes(value.toLowerCase()) ||
108
- 'Please enter y or n';
108
+ return (
109
+ ["y", "yes", "n", "no"].includes(value.toLowerCase()) ||
110
+ "Please enter y or n"
111
+ );
109
112
  },
110
113
  });
111
114
 
112
- return ['y', 'yes'].includes(answer.toLowerCase());
115
+ return ["y", "yes"].includes(answer.toLowerCase());
113
116
  }
114
117
 
115
118
  /**
@@ -121,7 +124,7 @@ export async function select<T extends string>(
121
124
  options: SelectOptions<T> = {},
122
125
  ): Promise<T> {
123
126
  if (!isInteractive()) {
124
- return options.default ?? choices[0]?.value as T;
127
+ return options.default ?? (choices[0]?.value as T);
125
128
  }
126
129
 
127
130
  const pageSize = options.pageSize ?? 10;
@@ -134,7 +137,7 @@ export async function select<T extends string>(
134
137
 
135
138
  return new Promise((resolve) => {
136
139
  // Hide cursor
137
- process.stdout.write('\x1b[?25l');
140
+ process.stdout.write("\x1b[?25l");
138
141
 
139
142
  const render = () => {
140
143
  // Clear previous output
@@ -142,7 +145,7 @@ export async function select<T extends string>(
142
145
  process.stdout.write(`\x1b[${lines + 1}A\x1b[0J`);
143
146
 
144
147
  // Render prompt
145
- process.stdout.write(`${colors.cyan('?')} ${message}\n`);
148
+ process.stdout.write(`${colors.cyan("?")} ${message}\n`);
146
149
 
147
150
  // Render choices
148
151
  const start = Math.max(0, selectedIndex - pageSize + 1);
@@ -153,7 +156,7 @@ export async function select<T extends string>(
153
156
  if (!choice) continue;
154
157
 
155
158
  const isSelected = i === selectedIndex;
156
- const prefix = isSelected ? `${colors.cyan('')} ` : ' ';
159
+ const prefix = isSelected ? `${colors.cyan("")} ` : " ";
157
160
  const name = choice.name ?? choice.value;
158
161
  const text = choice.disabled
159
162
  ? colors.dim(`${name} (disabled)`)
@@ -166,37 +169,37 @@ export async function select<T extends string>(
166
169
  };
167
170
 
168
171
  // Initial render
169
- process.stdout.write(`${colors.cyan('?')} ${message}\n`);
172
+ process.stdout.write(`${colors.cyan("?")} ${message}\n`);
170
173
  render();
171
174
 
172
175
  // Handle key input
173
176
  const stdin = process.stdin;
174
177
  stdin.setRawMode(true);
175
178
  stdin.resume();
176
- stdin.setEncoding('utf8');
179
+ stdin.setEncoding("utf8");
177
180
 
178
181
  const cleanup = () => {
179
182
  stdin.setRawMode(false);
180
183
  stdin.pause();
181
- stdin.removeListener('data', handler);
184
+ stdin.removeListener("data", handler);
182
185
  // Show cursor
183
- process.stdout.write('\x1b[?25h');
186
+ process.stdout.write("\x1b[?25h");
184
187
  };
185
188
 
186
189
  const handler = (key: string) => {
187
- if (key === '\u001b[A' || key === 'k') {
190
+ if (key === "\u001b[A" || key === "k") {
188
191
  // Up
189
192
  do {
190
193
  selectedIndex = (selectedIndex - 1 + choices.length) % choices.length;
191
194
  } while (choices[selectedIndex]?.disabled);
192
195
  render();
193
- } else if (key === '\u001b[B' || key === 'j') {
196
+ } else if (key === "\u001b[B" || key === "j") {
194
197
  // Down
195
198
  do {
196
199
  selectedIndex = (selectedIndex + 1) % choices.length;
197
200
  } while (choices[selectedIndex]?.disabled);
198
201
  render();
199
- } else if (key === '\r' || key === '\n') {
202
+ } else if (key === "\r" || key === "\n") {
200
203
  // Enter
201
204
  cleanup();
202
205
  const selected = choices[selectedIndex];
@@ -205,19 +208,19 @@ export async function select<T extends string>(
205
208
  `\x1b[${Math.min(choices.length, pageSize) + 1}A\x1b[0J`,
206
209
  );
207
210
  process.stdout.write(
208
- `${colors.cyan('?')} ${message} ${colors.cyan(selected.name ?? selected.value)}\n`,
211
+ `${colors.cyan("?")} ${message} ${colors.cyan(selected.name ?? selected.value)}\n`,
209
212
  );
210
213
  resolve(selected.value);
211
214
  }
212
- } else if (key === '\u001b' || key === '\u0003') {
215
+ } else if (key === "\u001b" || key === "\u0003") {
213
216
  // Escape or Ctrl+C
214
217
  cleanup();
215
- process.stdout.write('\n');
218
+ process.stdout.write("\n");
216
219
  process.exit(130);
217
220
  }
218
221
  };
219
222
 
220
- stdin.on('data', handler);
223
+ stdin.on("data", handler);
221
224
  });
222
225
  }
223
226
 
@@ -239,7 +242,7 @@ export async function multiSelect<T extends string>(
239
242
 
240
243
  return new Promise((resolve) => {
241
244
  // Hide cursor
242
- process.stdout.write('\x1b[?25l');
245
+ process.stdout.write("\x1b[?25l");
243
246
 
244
247
  const render = () => {
245
248
  // Clear previous output
@@ -247,7 +250,7 @@ export async function multiSelect<T extends string>(
247
250
  process.stdout.write(`\x1b[${lines + 1}A\x1b[0J`);
248
251
 
249
252
  // Render prompt
250
- process.stdout.write(`${colors.cyan('?')} ${message}\n`);
253
+ process.stdout.write(`${colors.cyan("?")} ${message}\n`);
251
254
 
252
255
  // Render choices
253
256
  const start = Math.max(0, currentIndex - pageSize + 1);
@@ -259,8 +262,8 @@ export async function multiSelect<T extends string>(
259
262
 
260
263
  const isCurrent = i === currentIndex;
261
264
  const isSelected = selected.has(choice.value);
262
- const checkbox = isSelected ? `${colors.green('')}` : '';
263
- const prefix = isCurrent ? `${colors.cyan('')} ` : ' ';
265
+ const checkbox = isSelected ? `${colors.green("")}` : "";
266
+ const prefix = isCurrent ? `${colors.cyan("")} ` : " ";
264
267
  const name = choice.name ?? choice.value;
265
268
  const text = choice.disabled
266
269
  ? colors.dim(`${name} (disabled)`)
@@ -273,37 +276,37 @@ export async function multiSelect<T extends string>(
273
276
  };
274
277
 
275
278
  // Initial render
276
- process.stdout.write(`${colors.cyan('?')} ${message}\n`);
279
+ process.stdout.write(`${colors.cyan("?")} ${message}\n`);
277
280
  render();
278
281
 
279
282
  // Handle key input
280
283
  const stdin = process.stdin;
281
284
  stdin.setRawMode(true);
282
285
  stdin.resume();
283
- stdin.setEncoding('utf8');
286
+ stdin.setEncoding("utf8");
284
287
 
285
288
  const cleanup = () => {
286
289
  stdin.setRawMode(false);
287
290
  stdin.pause();
288
- stdin.removeListener('data', handler);
291
+ stdin.removeListener("data", handler);
289
292
  // Show cursor
290
- process.stdout.write('\x1b[?25h');
293
+ process.stdout.write("\x1b[?25h");
291
294
  };
292
295
 
293
296
  const handler = (key: string) => {
294
- if (key === '\u001b[A' || key === 'k') {
297
+ if (key === "\u001b[A" || key === "k") {
295
298
  // Up
296
299
  do {
297
300
  currentIndex = (currentIndex - 1 + choices.length) % choices.length;
298
301
  } while (choices[currentIndex]?.disabled);
299
302
  render();
300
- } else if (key === '\u001b[B' || key === 'j') {
303
+ } else if (key === "\u001b[B" || key === "j") {
301
304
  // Down
302
305
  do {
303
306
  currentIndex = (currentIndex + 1) % choices.length;
304
307
  } while (choices[currentIndex]?.disabled);
305
308
  render();
306
- } else if (key === ' ' || key === 'x') {
309
+ } else if (key === " " || key === "x") {
307
310
  // Toggle selection
308
311
  const choice = choices[currentIndex];
309
312
  if (choice && !choice.disabled) {
@@ -318,7 +321,7 @@ export async function multiSelect<T extends string>(
318
321
  }
319
322
  render();
320
323
  }
321
- } else if (key === '\r' || key === '\n') {
324
+ } else if (key === "\r" || key === "\n") {
322
325
  // Enter
323
326
  cleanup();
324
327
  const result = Array.from(selected);
@@ -327,20 +330,20 @@ export async function multiSelect<T extends string>(
327
330
  );
328
331
  const names = result
329
332
  .map((v) => choices.find((c) => c.value === v)?.name ?? v)
330
- .join(', ');
333
+ .join(", ");
331
334
  process.stdout.write(
332
- `${colors.cyan('?')} ${message} ${colors.cyan(names || 'none')}\n`,
335
+ `${colors.cyan("?")} ${message} ${colors.cyan(names || "none")}\n`,
333
336
  );
334
337
  resolve(result);
335
- } else if (key === '\u001b' || key === '\u0003') {
338
+ } else if (key === "\u001b" || key === "\u0003") {
336
339
  // Escape or Ctrl+C
337
340
  cleanup();
338
- process.stdout.write('\n');
341
+ process.stdout.write("\n");
339
342
  process.exit(130);
340
343
  }
341
344
  };
342
345
 
343
- stdin.on('data', handler);
346
+ stdin.on("data", handler);
344
347
  });
345
348
  }
346
349
 
@@ -355,8 +358,8 @@ export async function number(
355
358
  ...options,
356
359
  validate: (v) => {
357
360
  if (!v && options.default) return true;
358
- const num = parseFloat(v);
359
- if (isNaN(num)) return 'Please enter a valid number';
361
+ const num = Number.parseFloat(v);
362
+ if (isNaN(num)) return "Please enter a valid number";
360
363
  if (options.min !== undefined && num < options.min) {
361
364
  return `Value must be at least ${options.min}`;
362
365
  }
@@ -370,7 +373,7 @@ export async function number(
370
373
  },
371
374
  });
372
375
 
373
- return parseFloat(value || options.default || '0');
376
+ return Number.parseFloat(value || options.default || "0");
374
377
  }
375
378
 
376
379
  /**
@@ -378,47 +381,47 @@ export async function number(
378
381
  */
379
382
  export async function password(
380
383
  message: string,
381
- options: Omit<PromptOptions, 'default'> = {},
384
+ options: Omit<PromptOptions, "default"> = {},
382
385
  ): Promise<string> {
383
386
  if (!isInteractive()) {
384
- return '';
387
+ return "";
385
388
  }
386
389
 
387
390
  return new Promise((resolve) => {
388
391
  const stdin = process.stdin;
389
392
  const stdout = process.stdout;
390
393
 
391
- stdout.write(`${colors.cyan('?')} ${message}: `);
394
+ stdout.write(`${colors.cyan("?")} ${message}: `);
392
395
 
393
- let value = '';
396
+ let value = "";
394
397
 
395
398
  stdin.setRawMode(true);
396
399
  stdin.resume();
397
- stdin.setEncoding('utf8');
400
+ stdin.setEncoding("utf8");
398
401
 
399
402
  const cleanup = () => {
400
403
  stdin.setRawMode(false);
401
404
  stdin.pause();
402
- stdin.removeListener('data', handler);
405
+ stdin.removeListener("data", handler);
403
406
  };
404
407
 
405
408
  const handler = (key: string) => {
406
- if (key === '\r' || key === '\n') {
409
+ if (key === "\r" || key === "\n") {
407
410
  cleanup();
408
- stdout.write('\n');
411
+ stdout.write("\n");
409
412
  resolve(value);
410
- } else if (key === '\u0003') {
413
+ } else if (key === "\u0003") {
411
414
  cleanup();
412
- stdout.write('\n');
415
+ stdout.write("\n");
413
416
  process.exit(130);
414
- } else if (key === '\u007f' || key === '\b') {
417
+ } else if (key === "\u007f" || key === "\b") {
415
418
  // Backspace
416
419
  value = value.slice(0, -1);
417
- } else if (key[0] !== '\x1b') {
420
+ } else if (key[0] !== "\x1b") {
418
421
  value += key;
419
422
  }
420
423
  };
421
424
 
422
- stdin.on('data', handler);
425
+ stdin.on("data", handler);
423
426
  });
424
- }
427
+ }
@@ -4,14 +4,14 @@
4
4
  * Provides animated spinners and progress indicators
5
5
  */
6
6
 
7
- import { colors, isColorEnabled } from './console';
7
+ import { colors, isColorEnabled } from "./console";
8
8
 
9
- const SPINNER_FRAMES = ['', '', '', '', '', '', '', '', '', ''];
9
+ const SPINNER_FRAMES = ["", "", "", "", "", "", "", "", "", ""];
10
10
  const SPINNER_INTERVAL = 80;
11
11
 
12
12
  export interface SpinnerOptions {
13
13
  text?: string;
14
- color?: 'red' | 'green' | 'yellow' | 'blue' | 'magenta' | 'cyan' | 'white';
14
+ color?: "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white";
15
15
  }
16
16
 
17
17
  export class Spinner {
@@ -23,8 +23,8 @@ export class Spinner {
23
23
  private stream = process.stdout;
24
24
 
25
25
  constructor(options: SpinnerOptions = {}) {
26
- this.text = options.text ?? '';
27
- this.color = options.color ?? 'cyan';
26
+ this.text = options.text ?? "";
27
+ this.color = options.color ?? "cyan";
28
28
  }
29
29
 
30
30
  /**
@@ -38,7 +38,7 @@ export class Spinner {
38
38
  this.frameIndex = 0;
39
39
 
40
40
  // Hide cursor
41
- this.stream.write('\x1b[?25l');
41
+ this.stream.write("\x1b[?25l");
42
42
 
43
43
  this.interval = setInterval(() => {
44
44
  this.render();
@@ -62,28 +62,28 @@ export class Spinner {
62
62
  * Stop the spinner with success
63
63
  */
64
64
  success(text?: string): this {
65
- return this.stop(colors.green(''), text);
65
+ return this.stop(colors.green(""), text);
66
66
  }
67
67
 
68
68
  /**
69
69
  * Stop the spinner with error
70
70
  */
71
71
  error(text?: string): this {
72
- return this.stop(colors.red(''), text);
72
+ return this.stop(colors.red(""), text);
73
73
  }
74
74
 
75
75
  /**
76
76
  * Stop the spinner with warning
77
77
  */
78
78
  warn(text?: string): this {
79
- return this.stop(colors.yellow(''), text);
79
+ return this.stop(colors.yellow(""), text);
80
80
  }
81
81
 
82
82
  /**
83
83
  * Stop the spinner with info
84
84
  */
85
85
  info(text?: string): this {
86
- return this.stop(colors.cyan(''), text);
86
+ return this.stop(colors.cyan(""), text);
87
87
  }
88
88
 
89
89
  /**
@@ -100,7 +100,7 @@ export class Spinner {
100
100
  }
101
101
 
102
102
  // Clear the line
103
- this.stream.write('\r\x1b[K');
103
+ this.stream.write("\r\x1b[K");
104
104
 
105
105
  // Write final message
106
106
  const finalText = text ?? this.text;
@@ -111,7 +111,7 @@ export class Spinner {
111
111
  }
112
112
 
113
113
  // Show cursor
114
- this.stream.write('\x1b[?25h');
114
+ this.stream.write("\x1b[?25h");
115
115
 
116
116
  return this;
117
117
  }
@@ -122,7 +122,7 @@ export class Spinner {
122
122
  clear(): this {
123
123
  if (!this.isSpinning) return this;
124
124
 
125
- this.stream.write('\r\x1b[K');
125
+ this.stream.write("\r\x1b[K");
126
126
  return this;
127
127
  }
128
128
 
@@ -132,7 +132,7 @@ export class Spinner {
132
132
  private render(): void {
133
133
  if (!isColorEnabled()) {
134
134
  // Without colors, just show dots
135
- const dots = '.'.repeat((this.frameIndex % 3) + 1);
135
+ const dots = ".".repeat((this.frameIndex % 3) + 1);
136
136
  this.stream.write(`\r${this.text}${dots} `);
137
137
  this.frameIndex++;
138
138
  return;
@@ -149,7 +149,10 @@ export class Spinner {
149
149
  /**
150
150
  * Create and start a spinner
151
151
  */
152
- export function spinner(text: string, options?: Omit<SpinnerOptions, 'text'>): Spinner {
152
+ export function spinner(
153
+ text: string,
154
+ options?: Omit<SpinnerOptions, "text">,
155
+ ): Spinner {
153
156
  return new Spinner({ text, ...options }).start();
154
157
  }
155
158
 
@@ -176,9 +179,9 @@ export class ProgressBar {
176
179
  constructor(options: ProgressBarOptions) {
177
180
  this.total = options.total;
178
181
  this.width = options.width ?? 40;
179
- this.text = options.text ?? '';
180
- this.completeChar = options.completeChar ?? '';
181
- this.incompleteChar = options.incompleteChar ?? '';
182
+ this.text = options.text ?? "";
183
+ this.completeChar = options.completeChar ?? "";
184
+ this.incompleteChar = options.incompleteChar ?? "";
182
185
  }
183
186
 
184
187
  /**
@@ -212,7 +215,7 @@ export class ProgressBar {
212
215
  complete(): this {
213
216
  this.current = this.total;
214
217
  this.render();
215
- this.stream.write('\n');
218
+ this.stream.write("\n");
216
219
  return this;
217
220
  }
218
221
 
@@ -262,4 +265,4 @@ export async function runTasks(tasks: TaskOptions[]): Promise<void> {
262
265
  throw error;
263
266
  }
264
267
  }
265
- }
268
+ }
package/src/cli/index.ts CHANGED
@@ -4,24 +4,30 @@
4
4
  * Main entry point for the Bueno CLI
5
5
  */
6
6
 
7
- import { readFileSync } from 'fs';
8
- import { join } from 'path';
9
- import { parseArgs, hasFlag, generateGlobalHelpText, generateHelpText, type ParsedArgs } from './core/args';
10
- import { cliConsole, colors, setColorEnabled } from './core/console';
11
- import { registry } from './commands';
7
+ import { readFileSync } from "fs";
8
+ import { join } from "path";
9
+ import { registry } from "./commands";
10
+ import {
11
+ type ParsedArgs,
12
+ generateGlobalHelpText,
13
+ generateHelpText,
14
+ hasFlag,
15
+ parseArgs,
16
+ } from "./core/args";
17
+ import { cliConsole, colors, setColorEnabled } from "./core/console";
12
18
 
13
19
  // Import commands to register them
14
- import './commands/new';
15
- import './commands/generate';
16
- import './commands/migration';
17
- import './commands/dev';
18
- import './commands/build';
19
- import './commands/start';
20
- import './commands/help';
20
+ import "./commands/new";
21
+ import "./commands/generate";
22
+ import "./commands/migration";
23
+ import "./commands/dev";
24
+ import "./commands/build";
25
+ import "./commands/start";
26
+ import "./commands/help";
21
27
 
22
28
  // Read version from package.json dynamically
23
29
  const packageJson = JSON.parse(
24
- readFileSync(join(import.meta.dir, '../../package.json'), 'utf-8')
30
+ readFileSync(join(import.meta.dir, "../../package.json"), "utf-8"),
25
31
  );
26
32
  const VERSION = packageJson.version;
27
33
 
@@ -29,15 +35,15 @@ const VERSION = packageJson.version;
29
35
  * CLI error types
30
36
  */
31
37
  export enum CLIErrorType {
32
- INVALID_ARGS = 'INVALID_ARGS',
33
- FILE_EXISTS = 'FILE_EXISTS',
34
- FILE_NOT_FOUND = 'FILE_NOT_FOUND',
35
- MODULE_NOT_FOUND = 'MODULE_NOT_FOUND',
36
- TEMPLATE_ERROR = 'TEMPLATE_ERROR',
37
- DATABASE_ERROR = 'DATABASE_ERROR',
38
- NETWORK_ERROR = 'NETWORK_ERROR',
39
- PERMISSION_ERROR = 'PERMISSION_ERROR',
40
- NOT_FOUND = 'NOT_FOUND',
38
+ INVALID_ARGS = "INVALID_ARGS",
39
+ FILE_EXISTS = "FILE_EXISTS",
40
+ FILE_NOT_FOUND = "FILE_NOT_FOUND",
41
+ MODULE_NOT_FOUND = "MODULE_NOT_FOUND",
42
+ TEMPLATE_ERROR = "TEMPLATE_ERROR",
43
+ DATABASE_ERROR = "DATABASE_ERROR",
44
+ NETWORK_ERROR = "NETWORK_ERROR",
45
+ PERMISSION_ERROR = "PERMISSION_ERROR",
46
+ NOT_FOUND = "NOT_FOUND",
41
47
  }
42
48
 
43
49
  /**
@@ -50,38 +56,40 @@ export class CLIError extends Error {
50
56
  public readonly exitCode = 1,
51
57
  ) {
52
58
  super(message);
53
- this.name = 'CLIError';
59
+ this.name = "CLIError";
54
60
  }
55
61
  }
56
62
 
57
63
  /**
58
64
  * Run the CLI
59
65
  */
60
- export async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
66
+ export async function run(
67
+ argv: string[] = process.argv.slice(2),
68
+ ): Promise<void> {
61
69
  // Parse arguments
62
70
  const args = parseArgs(argv);
63
71
 
64
72
  // Handle global options
65
- if (hasFlag(args, 'no-color')) {
73
+ if (hasFlag(args, "no-color")) {
66
74
  setColorEnabled(false);
67
75
  }
68
76
 
69
- if (hasFlag(args, 'verbose')) {
70
- process.env.BUENO_VERBOSE = 'true';
77
+ if (hasFlag(args, "verbose")) {
78
+ process.env.BUENO_VERBOSE = "true";
71
79
  }
72
80
 
73
- if (hasFlag(args, 'quiet')) {
74
- process.env.BUENO_QUIET = 'true';
81
+ if (hasFlag(args, "quiet")) {
82
+ process.env.BUENO_QUIET = "true";
75
83
  }
76
84
 
77
85
  // Show version
78
- if (hasFlag(args, 'version') || hasFlag(args, 'v')) {
86
+ if (hasFlag(args, "version") || hasFlag(args, "v")) {
79
87
  cliConsole.log(`bueno v${VERSION}`);
80
88
  process.exit(0);
81
89
  }
82
90
 
83
91
  // Show help if no command or help flag
84
- if (!args.command || hasFlag(args, 'help') || hasFlag(args, 'h')) {
92
+ if (!args.command || hasFlag(args, "help") || hasFlag(args, "h")) {
85
93
  if (args.command && registry.has(args.command)) {
86
94
  // Show command-specific help
87
95
  const cmd = registry.get(args.command);
@@ -105,7 +113,7 @@ export async function run(argv: string[] = process.argv.slice(2)): Promise<void>
105
113
  }
106
114
 
107
115
  if (error instanceof Error) {
108
- if (process.env.BUENO_VERBOSE === 'true') {
116
+ if (process.env.BUENO_VERBOSE === "true") {
109
117
  cliConsole.error(error.stack ?? error.message);
110
118
  } else {
111
119
  cliConsole.error(error.message);
@@ -120,14 +128,14 @@ export async function run(argv: string[] = process.argv.slice(2)): Promise<void>
120
128
  */
121
129
  export async function main(): Promise<void> {
122
130
  // Handle Ctrl+C gracefully
123
- process.on('SIGINT', () => {
131
+ process.on("SIGINT", () => {
124
132
  cliConsole.newline();
125
133
  process.exit(130);
126
134
  });
127
135
 
128
136
  // Handle unhandled rejections
129
- process.on('unhandledRejection', (reason) => {
130
- cliConsole.error('Unhandled rejection:', reason);
137
+ process.on("unhandledRejection", (reason) => {
138
+ cliConsole.error("Unhandled rejection:", reason);
131
139
  process.exit(1);
132
140
  });
133
141
 
@@ -135,6 +143,6 @@ export async function main(): Promise<void> {
135
143
  }
136
144
 
137
145
  // Export for programmatic use
138
- export { registry, defineCommand, command } from './commands';
139
- export * from './core';
140
- export * from './utils';
146
+ export { registry, defineCommand, command } from "./commands";
147
+ export * from "./core";
148
+ export * from "./utils";