@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
@@ -5,7 +5,7 @@
5
5
  * including Vue SFC support, JSX configuration, and Vue-specific defines.
6
6
  */
7
7
 
8
- import type { FrameworkBuildConfig, BuildPlugin } from "../types.js";
8
+ import type { BuildPlugin, FrameworkBuildConfig } from "../types.js";
9
9
 
10
10
  /**
11
11
  * Vue-specific build plugins
@@ -20,26 +20,28 @@ export function getVueBuildConfig(): FrameworkBuildConfig {
20
20
  return {
21
21
  // Vue uses classic JSX runtime
22
22
  jsxRuntime: "classic",
23
-
23
+
24
24
  // Vue-specific file extensions
25
25
  extensions: [".vue", ".jsx", ".tsx", ".js", ".ts"],
26
-
26
+
27
27
  // Vue-specific plugins
28
28
  plugins: vuePlugins,
29
-
29
+
30
30
  // Vue-specific global defines
31
31
  define: {
32
32
  // Vue production mode flag
33
- "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"),
33
+ "process.env.NODE_ENV": JSON.stringify(
34
+ process.env.NODE_ENV || "development",
35
+ ),
34
36
  // Vue 3 specific features
35
- "__VUE_OPTIONS_API__": "true",
36
- "__VUE_PROD_DEVTOOLS__": "false",
37
- "__VUE_PROD_HYDRATION_MISMATCH_DETAILS__": "false",
37
+ __VUE_OPTIONS_API__: "true",
38
+ __VUE_PROD_DEVTOOLS__: "false",
39
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false",
38
40
  },
39
-
41
+
40
42
  // External dependencies
41
43
  external: [],
42
-
44
+
43
45
  // Loader configurations for Vue files
44
46
  loaders: {
45
47
  ".vue": "js", // Vue SFCs are compiled to JS
@@ -89,4 +91,4 @@ export const vueFrameworkMeta: {
89
91
  needsRefreshRuntime: false, // Vue has built-in HMR
90
92
  supportsHMR: true,
91
93
  supportsSSR: true,
92
- };
94
+ };
@@ -637,7 +637,7 @@ export function getHMRClientScript(options?: {
637
637
  if (options?.port) {
638
638
  return HMR_CLIENT_SCRIPT.replace(
639
639
  "return parseInt(window.location.port || '3000', 10) + 1;",
640
- `return ${options.port};`
640
+ `return ${options.port};`,
641
641
  );
642
642
  }
643
643
  return HMR_CLIENT_SCRIPT;
@@ -648,16 +648,18 @@ export function getHMRClientScript(options?: {
648
648
  */
649
649
  export function injectHMRScript(html: string, port?: number): string {
650
650
  const script = getHMRClientScript({ port });
651
-
651
+
652
652
  // Find the </head> or </body> tag to inject before
653
653
  const headMatch = html.match(/<\/head>/i);
654
654
  const bodyMatch = html.match(/<\/body>/i);
655
-
656
- const injectionPoint = headMatch ? headMatch.index! + headMatch[0].length :
657
- bodyMatch ? bodyMatch.index! + bodyMatch[0].length :
658
- html.length;
659
-
660
- const scriptTag = `<script data-hmr-port="${port || ''}">${script}</script>`;
661
-
655
+
656
+ const injectionPoint = headMatch
657
+ ? headMatch.index! + headMatch[0].length
658
+ : bodyMatch
659
+ ? bodyMatch.index! + bodyMatch[0].length
660
+ : html.length;
661
+
662
+ const scriptTag = `<script data-hmr-port="${port || ""}">${script}</script>`;
663
+
662
664
  return html.slice(0, injectionPoint) + scriptTag + html.slice(injectionPoint);
663
- }
665
+ }
@@ -7,29 +7,35 @@
7
7
  * @module frontend/hmr
8
8
  */
9
9
 
10
- import { createLogger, type Logger } from "../logger/index.js";
10
+ import { type Logger, createLogger } from "../logger/index.js";
11
+ import { HMR_CLIENT_SCRIPT } from "./hmr-client.js";
11
12
  import type {
13
+ FileChangeEvent,
14
+ FrontendFramework,
12
15
  HMRClient,
16
+ HMRClientMessage,
13
17
  HMRConfig,
14
- HMRUpdate,
15
- HMRUpdateError,
16
18
  HMRDependencyNode,
17
- HMRClientMessage,
18
19
  HMRServerMessage,
19
- FileChangeEvent,
20
- FrontendFramework,
20
+ HMRUpdate,
21
+ HMRUpdateError,
21
22
  } from "./types.js";
22
- import { HMR_CLIENT_SCRIPT } from "./hmr-client.js";
23
23
 
24
24
  // ============= Constants =============
25
25
 
26
26
  const DEFAULT_DEBOUNCE_MS = 100;
27
- const DEFAULT_IGNORE_PATTERNS = ["node_modules", ".git", "dist", "build", ".bun"];
27
+ const DEFAULT_IGNORE_PATTERNS = [
28
+ "node_modules",
29
+ ".git",
30
+ "dist",
31
+ "build",
32
+ ".bun",
33
+ ];
28
34
 
29
35
  // ============= File Watcher Types =============
30
36
 
31
37
  interface FileWatchEvent {
32
- event: 'create' | 'update' | 'delete';
38
+ event: "create" | "update" | "delete";
33
39
  filePath: string;
34
40
  }
35
41
 
@@ -56,9 +62,13 @@ export class HMRManager {
56
62
  private debounceTimer: ReturnType<typeof setTimeout> | null = null;
57
63
  private framework: FrontendFramework;
58
64
  private devServerPort: number;
59
- private watcher: ReturnType<typeof import('fs').watch> | null = null;
65
+ private watcher: ReturnType<typeof import("fs").watch> | null = null;
60
66
 
61
- constructor(framework: FrontendFramework, devServerPort: number, config?: Partial<HMRConfig>) {
67
+ constructor(
68
+ framework: FrontendFramework,
69
+ devServerPort: number,
70
+ config?: Partial<HMRConfig>,
71
+ ) {
62
72
  this.framework = framework;
63
73
  this.devServerPort = devServerPort;
64
74
  this.config = this.normalizeConfig(config);
@@ -114,7 +124,7 @@ export class HMRManager {
114
124
  */
115
125
  handleUpgrade(request: Request): WebSocket | null {
116
126
  const url = new URL(request.url);
117
-
127
+
118
128
  if (url.pathname !== "/_hmr") {
119
129
  return null;
120
130
  }
@@ -136,17 +146,22 @@ export class HMRManager {
136
146
  },
137
147
  });
138
148
 
139
- this.logger.info(`HMR WebSocket server started on port ${this.config.port}`);
140
-
149
+ this.logger.info(
150
+ `HMR WebSocket server started on port ${this.config.port}`,
151
+ );
152
+
141
153
  return null; // The server handles the WebSocket directly
142
154
  }
143
155
 
144
156
  /**
145
157
  * Handle fetch requests for WebSocket server
146
158
  */
147
- private handleWebSocketFetch(request: Request, server: any): Response | undefined {
159
+ private handleWebSocketFetch(
160
+ request: Request,
161
+ server: any,
162
+ ): Response | undefined {
148
163
  const url = new URL(request.url);
149
-
164
+
150
165
  if (url.pathname === "/_hmr") {
151
166
  const success = server.upgrade(request);
152
167
  if (success) {
@@ -154,7 +169,7 @@ export class HMRManager {
154
169
  }
155
170
  return new Response("WebSocket upgrade failed", { status: 400 });
156
171
  }
157
-
172
+
158
173
  return new Response("Not found", { status: 404 });
159
174
  }
160
175
 
@@ -168,12 +183,12 @@ export class HMRManager {
168
183
  ws: ws,
169
184
  subscribedFiles: new Set(),
170
185
  };
171
-
186
+
172
187
  this.clients.set(clientId, client);
173
188
  ws.data = { clientId };
174
-
189
+
175
190
  this.logger.debug(`HMR client connected: ${clientId}`);
176
-
191
+
177
192
  // Send connected message
178
193
  this.sendToClient(ws, {
179
194
  type: "connected",
@@ -199,31 +214,33 @@ export class HMRManager {
199
214
  try {
200
215
  const data: HMRClientMessage = JSON.parse(message.toString());
201
216
  const clientId = ws.data?.clientId;
202
-
217
+
203
218
  if (!clientId) {
204
219
  return;
205
220
  }
206
-
221
+
207
222
  const client = this.clients.get(clientId);
208
223
  if (!client) {
209
224
  return;
210
225
  }
211
-
226
+
212
227
  switch (data.type) {
213
228
  case "subscribe":
214
229
  client.subscribedFiles.add(data.fileId);
215
230
  this.logger.debug(`Client ${clientId} subscribed to: ${data.fileId}`);
216
231
  break;
217
-
232
+
218
233
  case "unsubscribe":
219
234
  client.subscribedFiles.delete(data.fileId);
220
- this.logger.debug(`Client ${clientId} unsubscribed from: ${data.fileId}`);
235
+ this.logger.debug(
236
+ `Client ${clientId} unsubscribed from: ${data.fileId}`,
237
+ );
221
238
  break;
222
-
239
+
223
240
  case "ping":
224
241
  this.sendToClient(ws, { type: "pong" });
225
242
  break;
226
-
243
+
227
244
  case "module-accepted":
228
245
  this.handleModuleAccepted(data.moduleId, data.dependencies);
229
246
  break;
@@ -247,21 +264,23 @@ export class HMRManager {
247
264
  */
248
265
  broadcastUpdate(update: HMRUpdate): void {
249
266
  const message = JSON.stringify(update);
250
-
267
+
251
268
  for (const client of this.clients.values()) {
252
269
  // Check if client is subscribed to any of the changed files
253
- const isSubscribed = update.changes.some(
254
- (file) => client.subscribedFiles.has(file)
270
+ const isSubscribed = update.changes.some((file) =>
271
+ client.subscribedFiles.has(file),
255
272
  );
256
-
273
+
257
274
  if (isSubscribed || update.type === "reload" || update.type === "error") {
258
275
  if (client.ws.readyState === WebSocket.OPEN) {
259
276
  client.ws.send(message);
260
277
  }
261
278
  }
262
279
  }
263
-
264
- this.logger.debug(`Broadcasted ${update.type} update for: ${update.fileId}`);
280
+
281
+ this.logger.debug(
282
+ `Broadcasted ${update.type} update for: ${update.fileId}`,
283
+ );
265
284
  }
266
285
 
267
286
  /**
@@ -271,32 +290,36 @@ export class HMRManager {
271
290
  if (!this.config.enabled) {
272
291
  return;
273
292
  }
274
-
293
+
275
294
  this.logger.info(`Starting file watcher for: ${rootDir}`);
276
-
295
+
277
296
  // Use Node's fs.watch for file watching
278
- const fs = require('fs');
279
- const path = require('path');
280
-
297
+ const fs = require("fs");
298
+ const path = require("path");
299
+
281
300
  try {
282
- this.watcher = fs.watch(rootDir, { recursive: true }, (eventType: string, filename: string | null) => {
283
- if (!filename) return;
284
-
285
- const filePath = path.join(rootDir, filename);
286
-
287
- if (eventType === 'rename') {
288
- // Check if file exists to determine if it's create or delete
289
- try {
290
- fs.accessSync(filePath, fs.constants.F_OK);
291
- this.handleFileChange(filePath, "create");
292
- } catch {
293
- this.handleFileChange(filePath, "delete");
301
+ this.watcher = fs.watch(
302
+ rootDir,
303
+ { recursive: true },
304
+ (eventType: string, filename: string | null) => {
305
+ if (!filename) return;
306
+
307
+ const filePath = path.join(rootDir, filename);
308
+
309
+ if (eventType === "rename") {
310
+ // Check if file exists to determine if it's create or delete
311
+ try {
312
+ fs.accessSync(filePath, fs.constants.F_OK);
313
+ this.handleFileChange(filePath, "create");
314
+ } catch {
315
+ this.handleFileChange(filePath, "delete");
316
+ }
317
+ } else if (eventType === "change") {
318
+ this.handleFileChange(filePath, "update");
294
319
  }
295
- } else if (eventType === 'change') {
296
- this.handleFileChange(filePath, "update");
297
- }
298
- });
299
-
320
+ },
321
+ );
322
+
300
323
  this.logger.info("File watcher started");
301
324
  } catch (error) {
302
325
  this.logger.error("Failed to start file watcher", error);
@@ -317,21 +340,24 @@ export class HMRManager {
317
340
  /**
318
341
  * Handle a file change event
319
342
  */
320
- private handleFileChange(filePath: string, event: "create" | "update" | "delete"): void {
343
+ private handleFileChange(
344
+ filePath: string,
345
+ event: "create" | "update" | "delete",
346
+ ): void {
321
347
  // Check if file should be ignored
322
348
  if (this.shouldIgnoreFile(filePath)) {
323
349
  return;
324
350
  }
325
-
351
+
326
352
  this.logger.debug(`File ${event}: ${filePath}`);
327
-
353
+
328
354
  // Add to pending updates
329
355
  this.pendingUpdates.set(filePath, {
330
356
  path: filePath,
331
357
  event,
332
358
  timestamp: Date.now(),
333
359
  });
334
-
360
+
335
361
  // Debounce updates
336
362
  this.scheduleDebouncedUpdate();
337
363
  }
@@ -346,11 +372,22 @@ export class HMRManager {
346
372
  return true;
347
373
  }
348
374
  }
349
-
375
+
350
376
  // Only watch relevant file types
351
- const relevantExtensions = [".js", ".jsx", ".ts", ".tsx", ".css", ".scss", ".sass", ".less", ".vue", ".svelte"];
377
+ const relevantExtensions = [
378
+ ".js",
379
+ ".jsx",
380
+ ".ts",
381
+ ".tsx",
382
+ ".css",
383
+ ".scss",
384
+ ".sass",
385
+ ".less",
386
+ ".vue",
387
+ ".svelte",
388
+ ];
352
389
  const ext = filePath.substring(filePath.lastIndexOf("."));
353
-
390
+
354
391
  return !relevantExtensions.includes(ext);
355
392
  }
356
393
 
@@ -361,7 +398,7 @@ export class HMRManager {
361
398
  if (this.debounceTimer) {
362
399
  clearTimeout(this.debounceTimer);
363
400
  }
364
-
401
+
365
402
  this.debounceTimer = setTimeout(() => {
366
403
  this.processPendingUpdates();
367
404
  }, this.config.debounceMs);
@@ -374,16 +411,16 @@ export class HMRManager {
374
411
  if (this.pendingUpdates.size === 0) {
375
412
  return;
376
413
  }
377
-
414
+
378
415
  const updates = Array.from(this.pendingUpdates.values());
379
416
  this.pendingUpdates.clear();
380
-
417
+
381
418
  // Group updates by type
382
419
  const changedFiles = updates.map((u) => u.path);
383
-
420
+
384
421
  // Determine update type based on file changes
385
422
  const updateType = this.determineUpdateType(changedFiles);
386
-
423
+
387
424
  // Create and broadcast update
388
425
  const update: HMRUpdate = {
389
426
  type: updateType,
@@ -391,7 +428,7 @@ export class HMRManager {
391
428
  timestamp: Date.now(),
392
429
  changes: changedFiles,
393
430
  };
394
-
431
+
395
432
  this.broadcastUpdate(update);
396
433
  }
397
434
 
@@ -402,17 +439,21 @@ export class HMRManager {
402
439
  // Check if any changed file requires a full reload
403
440
  for (const file of changedFiles) {
404
441
  const ext = file.substring(file.lastIndexOf("."));
405
-
442
+
406
443
  // Configuration files always require full reload
407
- if (file.includes("config") || file.endsWith(".config.js") || file.endsWith(".config.ts")) {
444
+ if (
445
+ file.includes("config") ||
446
+ file.endsWith(".config.js") ||
447
+ file.endsWith(".config.ts")
448
+ ) {
408
449
  return "reload";
409
450
  }
410
-
451
+
411
452
  // HTML files require full reload
412
453
  if (ext === ".html") {
413
454
  return "reload";
414
455
  }
415
-
456
+
416
457
  // Check dependency graph for breaking changes
417
458
  const node = this.dependencyGraph.get(file);
418
459
  if (node && node.importedBy.size > 0) {
@@ -421,7 +462,7 @@ export class HMRManager {
421
462
  // In a full implementation, we'd analyze the actual changes
422
463
  }
423
464
  }
424
-
465
+
425
466
  return "update";
426
467
  }
427
468
 
@@ -434,7 +475,7 @@ export class HMRManager {
434
475
  if (node) {
435
476
  // Update imports
436
477
  node.imports = new Set(dependencies);
437
-
478
+
438
479
  // Update reverse dependencies
439
480
  for (const dep of dependencies) {
440
481
  const depNode = this.dependencyGraph.get(dep);
@@ -443,7 +484,7 @@ export class HMRManager {
443
484
  }
444
485
  }
445
486
  }
446
-
487
+
447
488
  this.logger.debug(`Module accepted: ${moduleId}`);
448
489
  }
449
490
 
@@ -457,7 +498,7 @@ export class HMRManager {
457
498
  importedBy: new Set(),
458
499
  lastModified: Date.now(),
459
500
  };
460
-
501
+
461
502
  // Update reverse dependencies
462
503
  for (const imp of imports) {
463
504
  const importedNode = this.dependencyGraph.get(imp);
@@ -465,7 +506,7 @@ export class HMRManager {
465
506
  importedNode.importedBy.add(filePath);
466
507
  }
467
508
  }
468
-
509
+
469
510
  this.dependencyGraph.set(filePath, node);
470
511
  }
471
512
 
@@ -477,7 +518,7 @@ export class HMRManager {
477
518
  if (!node) {
478
519
  return [];
479
520
  }
480
-
521
+
481
522
  return Array.from(node.importedBy);
482
523
  }
483
524
 
@@ -489,7 +530,7 @@ export class HMRManager {
489
530
  if (!node) {
490
531
  return [];
491
532
  }
492
-
533
+
493
534
  return Array.from(node.imports);
494
535
  }
495
536
 
@@ -497,13 +538,14 @@ export class HMRManager {
497
538
  * Broadcast an error to all clients
498
539
  */
499
540
  broadcastError(error: Error | HMRUpdateError): void {
500
- const updateError: HMRUpdateError = error instanceof Error
501
- ? {
502
- message: error.message,
503
- stack: error.stack,
504
- }
505
- : error;
506
-
541
+ const updateError: HMRUpdateError =
542
+ error instanceof Error
543
+ ? {
544
+ message: error.message,
545
+ stack: error.stack,
546
+ }
547
+ : error;
548
+
507
549
  const update: HMRUpdate = {
508
550
  type: "error",
509
551
  fileId: updateError.file || "unknown",
@@ -511,7 +553,7 @@ export class HMRManager {
511
553
  changes: [],
512
554
  error: updateError,
513
555
  };
514
-
556
+
515
557
  this.broadcastUpdate(update);
516
558
  }
517
559
 
@@ -553,7 +595,7 @@ export class HMRManager {
553
595
  client.ws.close();
554
596
  }
555
597
  }
556
-
598
+
557
599
  this.clients.clear();
558
600
  this.logger.info("All HMR clients disconnected");
559
601
  }
@@ -566,12 +608,12 @@ export class HMRManager {
566
608
  this.disconnectAll();
567
609
  this.dependencyGraph.clear();
568
610
  this.pendingUpdates.clear();
569
-
611
+
570
612
  if (this.debounceTimer) {
571
613
  clearTimeout(this.debounceTimer);
572
614
  this.debounceTimer = null;
573
615
  }
574
-
616
+
575
617
  this.logger.info("HMR manager stopped");
576
618
  }
577
619
 
@@ -658,7 +700,7 @@ export class HMRManager {
658
700
  export function createHMRManager(
659
701
  framework: FrontendFramework,
660
702
  devServerPort: number,
661
- config?: Partial<HMRConfig>
703
+ config?: Partial<HMRConfig>,
662
704
  ): HMRManager {
663
705
  return new HMRManager(framework, devServerPort, config);
664
706
  }
@@ -676,7 +718,7 @@ export function isHMRBoundary(filePath: string, content: string): boolean {
676
718
  /import\.meta\.hot\.accept/,
677
719
  /if\s*\(\s*import\.meta\.hot\s*\)/,
678
720
  ];
679
-
721
+
680
722
  return patterns.some((pattern) => pattern.test(content));
681
723
  }
682
724
 
@@ -685,29 +727,30 @@ export function isHMRBoundary(filePath: string, content: string): boolean {
685
727
  */
686
728
  export function parseImports(content: string, filePath: string): string[] {
687
729
  const imports: string[] = [];
688
-
730
+
689
731
  // Match ES6 imports
690
- const es6Pattern = /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
732
+ const es6Pattern =
733
+ /import\s+(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
691
734
  let match;
692
-
735
+
693
736
  while ((match = es6Pattern.exec(content)) !== null) {
694
737
  imports.push(resolveImport(match[1], filePath));
695
738
  }
696
-
739
+
697
740
  // Match dynamic imports
698
741
  const dynamicPattern = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
699
-
742
+
700
743
  while ((match = dynamicPattern.exec(content)) !== null) {
701
744
  imports.push(resolveImport(match[1], filePath));
702
745
  }
703
-
746
+
704
747
  // Match CommonJS requires
705
748
  const requirePattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
706
-
749
+
707
750
  while ((match = requirePattern.exec(content)) !== null) {
708
751
  imports.push(resolveImport(match[1], filePath));
709
752
  }
710
-
753
+
711
754
  return imports;
712
755
  }
713
756
 
@@ -719,10 +762,10 @@ function resolveImport(importPath: string, fromFile: string): string {
719
762
  if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
720
763
  return importPath;
721
764
  }
722
-
765
+
723
766
  // Resolve relative paths
724
767
  const dir = fromFile.substring(0, fromFile.lastIndexOf("/"));
725
768
  const resolved = new URL(importPath, `file://${dir}/`).pathname;
726
-
769
+
727
770
  return resolved;
728
- }
771
+ }
@@ -79,19 +79,28 @@ export { DevServer, createDevServer } from "./dev-server.js";
79
79
 
80
80
  // ============= HMR =============
81
81
 
82
- export { HMRManager, createHMRManager, isHMRBoundary, parseImports } from "./hmr.js";
83
- export { HMR_CLIENT_SCRIPT, getHMRClientScript, injectHMRScript } from "./hmr-client.js";
82
+ export {
83
+ HMRManager,
84
+ createHMRManager,
85
+ isHMRBoundary,
86
+ parseImports,
87
+ } from "./hmr.js";
88
+ export {
89
+ HMR_CLIENT_SCRIPT,
90
+ getHMRClientScript,
91
+ injectHMRScript,
92
+ } from "./hmr-client.js";
84
93
 
85
94
  // ============= Console Stream =============
86
95
 
87
96
  export {
88
97
  ConsoleStreamManager,
89
98
  createConsoleStreamManager,
90
- injectConsoleScript
99
+ injectConsoleScript,
91
100
  } from "./console-stream.js";
92
101
  export {
93
102
  CONSOLE_CLIENT_SCRIPT,
94
- getConsoleClientScript
103
+ getConsoleClientScript,
95
104
  } from "./console-client.js";
96
105
 
97
106
  // ============= Bundler =============
@@ -339,4 +348,4 @@ export {
339
348
  isAPIRouteFile,
340
349
  isMiddlewareFile,
341
350
  getModuleMethods,
342
- } from "./api-routes.js";
351
+ } from "./api-routes.js";