@ecopages/core 0.2.0-alpha.4 → 0.2.0-alpha.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 (418) hide show
  1. package/README.md +213 -12
  2. package/package.json +100 -188
  3. package/src/adapters/README.md +39 -0
  4. package/src/adapters/bun/hmr-manager.test.ts +267 -0
  5. package/src/adapters/bun/hmr-manager.ts +181 -68
  6. package/src/adapters/bun/index.ts +1 -2
  7. package/src/adapters/bun/server-adapter.ts +41 -34
  8. package/src/adapters/bun/server-lifecycle.ts +40 -70
  9. package/src/adapters/index.ts +1 -1
  10. package/src/adapters/node/bootstrap-dependency-resolver.test.ts +282 -0
  11. package/src/adapters/node/bootstrap-dependency-resolver.ts +301 -0
  12. package/src/adapters/node/index.ts +7 -0
  13. package/src/adapters/node/node-client-bridge.test.ts +198 -0
  14. package/src/adapters/node/node-hmr-manager.test.ts +322 -0
  15. package/src/adapters/node/node-hmr-manager.ts +208 -116
  16. package/src/adapters/node/runtime-adapter.test.ts +868 -0
  17. package/src/adapters/node/runtime-adapter.ts +439 -0
  18. package/src/adapters/node/server-adapter.ts +31 -104
  19. package/src/adapters/node/static-content-server.test.ts +60 -0
  20. package/src/adapters/node/static-content-server.ts +36 -0
  21. package/src/adapters/node/write-runtime-manifest.ts +38 -0
  22. package/src/adapters/shared/api-response.test.ts +97 -0
  23. package/src/{define-api-handler.ts → adapters/shared/define-api-handler.ts} +1 -1
  24. package/src/adapters/shared/explicit-static-route-matcher.test.ts +381 -0
  25. package/src/adapters/shared/explicit-static-route-matcher.ts +7 -1
  26. package/src/adapters/shared/file-route-middleware-pipeline.test.ts +90 -0
  27. package/src/adapters/shared/file-route-middleware-pipeline.ts +6 -2
  28. package/src/adapters/shared/fs-server-response-factory.test.ts +187 -0
  29. package/src/adapters/shared/fs-server-response-matcher.test.ts +286 -0
  30. package/src/adapters/shared/fs-server-response-matcher.ts +17 -10
  31. package/src/adapters/shared/hmr-entrypoint-registrar.ts +149 -0
  32. package/src/adapters/shared/hmr-html-response.ts +52 -0
  33. package/src/adapters/shared/hmr-manager.contract.test.ts +196 -0
  34. package/src/adapters/shared/hmr-manager.dispatch.test.ts +220 -0
  35. package/src/adapters/shared/render-context.test.ts +146 -0
  36. package/src/adapters/shared/render-context.ts +21 -6
  37. package/src/adapters/shared/runtime-bootstrap.ts +79 -0
  38. package/src/adapters/shared/server-adapter.test.ts +77 -0
  39. package/src/adapters/shared/server-adapter.ts +51 -4
  40. package/src/adapters/shared/server-route-handler.test.ts +110 -0
  41. package/src/adapters/shared/server-route-handler.ts +5 -18
  42. package/src/adapters/shared/server-static-builder.test.ts +316 -0
  43. package/src/adapters/shared/server-static-builder.ts +92 -8
  44. package/src/build/README.md +101 -0
  45. package/src/build/build-adapter-serialization.test.ts +268 -0
  46. package/src/build/build-adapter.test.ts +815 -0
  47. package/src/build/build-adapter.ts +235 -6
  48. package/src/build/build-manifest.ts +54 -0
  49. package/src/build/dev-build-coordinator.ts +221 -0
  50. package/src/build/esbuild-build-adapter.ts +132 -83
  51. package/src/build/runtime-build-executor.ts +34 -0
  52. package/src/build/runtime-specifier-alias-plugin.test.ts +43 -0
  53. package/src/build/runtime-specifier-alias-plugin.ts +58 -0
  54. package/src/config/README.md +33 -0
  55. package/src/config/config-builder.test.ts +410 -0
  56. package/src/config/config-builder.ts +281 -49
  57. package/src/constants.ts +15 -0
  58. package/src/declarations.d.ts +18 -13
  59. package/src/eco/README.md +70 -16
  60. package/src/eco/component-render-context.ts +39 -17
  61. package/src/eco/eco.test.ts +678 -0
  62. package/src/eco/eco.ts +29 -8
  63. package/src/eco/eco.types.ts +20 -1
  64. package/src/eco/eco.utils.test.ts +124 -0
  65. package/src/eco/global-injector-map.test.ts +42 -0
  66. package/src/eco/lazy-injector-map.test.ts +66 -0
  67. package/src/eco/module-dependencies.test.ts +30 -0
  68. package/src/errors/http-error.test.ts +134 -0
  69. package/src/global/utils.test.ts +12 -0
  70. package/src/hmr/README.md +26 -0
  71. package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-HMR-Server-Integration-should-have-HMR-script-injected-in-page-1.png +0 -0
  72. package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-HMR-Server-Integration-should-load-fixture-app-page-1.png +0 -0
  73. package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-WebSocket-Connection-should-connect-to-correct-HMR-endpoint-1.png +0 -0
  74. package/src/hmr/client/hmr-runtime.ts +38 -7
  75. package/src/hmr/hmr-strategy.test.ts +124 -0
  76. package/src/hmr/hmr.postcss.test.e2e.ts +41 -0
  77. package/src/hmr/hmr.test.e2e.ts +29 -38
  78. package/src/hmr/strategies/js-hmr-strategy.test.ts +335 -0
  79. package/src/hmr/strategies/js-hmr-strategy.ts +115 -115
  80. package/src/index.ts +1 -1
  81. package/src/integrations/ghtml/ghtml-renderer.test.ts +63 -0
  82. package/src/integrations/ghtml/ghtml-renderer.ts +4 -1
  83. package/src/internal-types.ts +39 -19
  84. package/src/plugins/README.md +34 -0
  85. package/src/plugins/alias-resolver-plugin.test.ts +41 -0
  86. package/src/plugins/alias-resolver-plugin.ts +21 -3
  87. package/src/plugins/eco-component-meta-plugin.test.ts +380 -0
  88. package/src/plugins/eco-component-meta-plugin.ts +10 -3
  89. package/src/plugins/integration-plugin.test.ts +111 -0
  90. package/src/plugins/integration-plugin.ts +45 -3
  91. package/src/plugins/processor.test.ts +148 -0
  92. package/src/plugins/processor.ts +22 -2
  93. package/src/plugins/runtime-capability.ts +14 -0
  94. package/src/public-types.ts +73 -16
  95. package/src/route-renderer/GRAPH.md +16 -20
  96. package/src/route-renderer/README.md +8 -21
  97. package/src/route-renderer/component-graph/component-graph-executor.test.ts +41 -0
  98. package/src/route-renderer/component-graph/component-graph.test.ts +63 -0
  99. package/src/route-renderer/component-graph/component-marker.test.ts +73 -0
  100. package/src/route-renderer/component-graph/component-reference.ts +29 -0
  101. package/src/route-renderer/component-graph/marker-graph-resolver.test.ts +135 -0
  102. package/src/route-renderer/{marker-graph-resolver.ts → component-graph/marker-graph-resolver.ts} +11 -9
  103. package/src/route-renderer/orchestration/integration-renderer.test.ts +936 -0
  104. package/src/route-renderer/{integration-renderer.ts → orchestration/integration-renderer.ts} +113 -19
  105. package/src/route-renderer/orchestration/render-execution.service.test.ts +97 -0
  106. package/src/route-renderer/{render-execution.service.ts → orchestration/render-execution.service.ts} +109 -37
  107. package/src/route-renderer/orchestration/render-preparation.service.test.ts +235 -0
  108. package/src/route-renderer/{render-preparation.service.ts → orchestration/render-preparation.service.ts} +127 -9
  109. package/src/route-renderer/page-loading/dependency-resolver.test.ts +345 -0
  110. package/src/route-renderer/{dependency-resolver.ts → page-loading/dependency-resolver.ts} +28 -12
  111. package/src/route-renderer/page-loading/page-module-loader.test.ts +96 -0
  112. package/src/route-renderer/{page-module-loader.ts → page-loading/page-module-loader.ts} +49 -21
  113. package/src/route-renderer/route-renderer.ts +36 -1
  114. package/src/router/README.md +26 -0
  115. package/src/router/client/link-intent.d.ts +53 -0
  116. package/src/router/client/link-intent.test.browser.ts +51 -0
  117. package/src/router/client/link-intent.ts +92 -0
  118. package/src/router/client/navigation-coordinator.test.ts +237 -0
  119. package/src/router/client/navigation-coordinator.ts +433 -0
  120. package/src/router/server/fs-router-scanner.test.ts +83 -0
  121. package/src/router/{fs-router-scanner.ts → server/fs-router-scanner.ts} +12 -10
  122. package/src/router/server/fs-router.test.ts +214 -0
  123. package/src/router/{fs-router.ts → server/fs-router.ts} +2 -2
  124. package/src/services/README.md +29 -0
  125. package/src/services/assets/asset-processing-service/asset-processing.service.test.ts +385 -0
  126. package/src/services/{asset-processing-service → assets/asset-processing-service}/asset-processing.service.ts +101 -6
  127. package/src/services/assets/asset-processing-service/asset.factory.test.ts +63 -0
  128. package/src/services/{asset-processing-service → assets/asset-processing-service}/asset.factory.ts +2 -2
  129. package/src/services/{asset-processing-service → assets/asset-processing-service}/assets.types.ts +2 -1
  130. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.test.ts +72 -0
  131. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.ts +95 -0
  132. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.test.ts +67 -0
  133. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.ts +78 -0
  134. package/src/services/{asset-processing-service → assets/asset-processing-service}/index.ts +2 -0
  135. package/src/services/{asset-processing-service → assets/asset-processing-service}/processor.interface.ts +1 -1
  136. package/src/services/assets/asset-processing-service/processors/base/base-processor.test.ts +59 -0
  137. package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/base/base-processor.ts +11 -5
  138. package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/base/base-script-processor.ts +17 -27
  139. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.test.ts +286 -0
  140. package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/file-script.processor.ts +3 -3
  141. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.test.ts +227 -0
  142. package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/node-module-script.processor.ts +5 -4
  143. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.test.ts +199 -0
  144. package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/stylesheet/file-stylesheet.processor.ts +4 -1
  145. package/src/services/assets/browser-bundle.service.test.ts +36 -0
  146. package/src/services/assets/browser-bundle.service.ts +53 -0
  147. package/src/services/cache/index.ts +3 -3
  148. package/src/services/cache/memory-cache-store.test.ts +225 -0
  149. package/src/services/cache/memory-cache-store.ts +1 -1
  150. package/src/services/cache/page-cache-service.test.ts +175 -0
  151. package/src/services/cache/page-cache-service.ts +3 -3
  152. package/src/services/cache/page-request-cache-coordinator.service.test.ts +79 -0
  153. package/src/services/{page-request-cache-coordinator.service.ts → cache/page-request-cache-coordinator.service.ts} +9 -6
  154. package/src/services/html/html-rewriter-provider.service.test.ts +183 -0
  155. package/src/services/html/html-rewriter-provider.service.ts +103 -0
  156. package/src/services/html/html-transformer.service.test.ts +378 -0
  157. package/src/services/html/html-transformer.service.ts +279 -0
  158. package/src/services/invalidation/development-invalidation.service.test.ts +77 -0
  159. package/src/services/invalidation/development-invalidation.service.ts +261 -0
  160. package/src/services/module-loading/app-server-module-transpiler.service.ts +52 -0
  161. package/src/services/module-loading/page-module-import.service.test.ts +253 -0
  162. package/src/services/module-loading/page-module-import.service.ts +200 -0
  163. package/src/services/module-loading/server-loader.service.test.ts +161 -0
  164. package/src/services/module-loading/server-loader.service.ts +130 -0
  165. package/src/services/module-loading/server-module-transpiler.service.test.ts +115 -0
  166. package/src/services/module-loading/server-module-transpiler.service.ts +105 -0
  167. package/src/services/runtime-manifest/node-runtime-manifest.service.test.ts +95 -0
  168. package/src/services/runtime-manifest/node-runtime-manifest.service.ts +101 -0
  169. package/src/services/runtime-state/dev-graph.service.ts +217 -0
  170. package/src/services/runtime-state/entrypoint-dependency-graph.service.ts +136 -0
  171. package/src/services/runtime-state/runtime-specifier-registry.service.ts +96 -0
  172. package/src/services/runtime-state/server-invalidation-state.service.ts +68 -0
  173. package/src/services/validation/schema-validation-service.test.ts +223 -0
  174. package/src/services/{schema-validation-service.ts → validation/schema-validation-service.ts} +1 -1
  175. package/src/static-site-generator/README.md +26 -0
  176. package/src/static-site-generator/static-site-generator.test.ts +307 -0
  177. package/src/static-site-generator/static-site-generator.ts +109 -6
  178. package/src/utils/deep-merge.test.ts +114 -0
  179. package/src/utils/invariant.test.ts +22 -0
  180. package/src/utils/path-utils.test.ts +15 -0
  181. package/src/utils/resolve-work-dir.ts +45 -0
  182. package/src/utils/server-utils.test.ts +38 -0
  183. package/src/watchers/project-watcher.integration.test.ts +337 -0
  184. package/src/watchers/project-watcher.test-helpers.ts +1 -1
  185. package/src/watchers/project-watcher.test.ts +678 -0
  186. package/src/watchers/project-watcher.ts +130 -111
  187. package/CHANGELOG.md +0 -91
  188. package/src/adapters/abstract/application-adapter.d.ts +0 -168
  189. package/src/adapters/abstract/application-adapter.js +0 -109
  190. package/src/adapters/abstract/router-adapter.d.ts +0 -26
  191. package/src/adapters/abstract/router-adapter.js +0 -5
  192. package/src/adapters/abstract/server-adapter.d.ts +0 -69
  193. package/src/adapters/abstract/server-adapter.js +0 -15
  194. package/src/adapters/bun/client-bridge.d.ts +0 -34
  195. package/src/adapters/bun/client-bridge.js +0 -48
  196. package/src/adapters/bun/create-app.d.ts +0 -60
  197. package/src/adapters/bun/create-app.js +0 -117
  198. package/src/adapters/bun/define-api-handler.d.ts +0 -61
  199. package/src/adapters/bun/define-api-handler.js +0 -15
  200. package/src/adapters/bun/define-api-handler.ts +0 -114
  201. package/src/adapters/bun/hmr-manager.d.ts +0 -85
  202. package/src/adapters/bun/hmr-manager.js +0 -240
  203. package/src/adapters/bun/index.d.ts +0 -3
  204. package/src/adapters/bun/index.js +0 -8
  205. package/src/adapters/bun/server-adapter.d.ts +0 -155
  206. package/src/adapters/bun/server-adapter.js +0 -368
  207. package/src/adapters/bun/server-lifecycle.d.ts +0 -52
  208. package/src/adapters/bun/server-lifecycle.js +0 -120
  209. package/src/adapters/index.d.ts +0 -6
  210. package/src/adapters/index.js +0 -14
  211. package/src/adapters/node/create-app.d.ts +0 -21
  212. package/src/adapters/node/create-app.js +0 -143
  213. package/src/adapters/node/index.d.ts +0 -4
  214. package/src/adapters/node/index.js +0 -8
  215. package/src/adapters/node/node-client-bridge.d.ts +0 -26
  216. package/src/adapters/node/node-client-bridge.js +0 -66
  217. package/src/adapters/node/node-hmr-manager.d.ts +0 -63
  218. package/src/adapters/node/node-hmr-manager.js +0 -237
  219. package/src/adapters/node/server-adapter.d.ts +0 -190
  220. package/src/adapters/node/server-adapter.js +0 -420
  221. package/src/adapters/node/static-content-server.d.ts +0 -24
  222. package/src/adapters/node/static-content-server.js +0 -166
  223. package/src/adapters/shared/api-response.d.ts +0 -52
  224. package/src/adapters/shared/api-response.js +0 -96
  225. package/src/adapters/shared/application-adapter.d.ts +0 -18
  226. package/src/adapters/shared/application-adapter.js +0 -90
  227. package/src/adapters/shared/explicit-static-route-matcher.d.ts +0 -38
  228. package/src/adapters/shared/explicit-static-route-matcher.js +0 -100
  229. package/src/adapters/shared/file-route-middleware-pipeline.d.ts +0 -65
  230. package/src/adapters/shared/file-route-middleware-pipeline.js +0 -98
  231. package/src/adapters/shared/fs-server-response-factory.d.ts +0 -19
  232. package/src/adapters/shared/fs-server-response-factory.js +0 -97
  233. package/src/adapters/shared/fs-server-response-matcher.d.ts +0 -71
  234. package/src/adapters/shared/fs-server-response-matcher.js +0 -155
  235. package/src/adapters/shared/render-context.d.ts +0 -14
  236. package/src/adapters/shared/render-context.js +0 -69
  237. package/src/adapters/shared/server-adapter.d.ts +0 -87
  238. package/src/adapters/shared/server-adapter.js +0 -353
  239. package/src/adapters/shared/server-route-handler.d.ts +0 -89
  240. package/src/adapters/shared/server-route-handler.js +0 -120
  241. package/src/adapters/shared/server-static-builder.d.ts +0 -38
  242. package/src/adapters/shared/server-static-builder.js +0 -46
  243. package/src/build/build-adapter.d.ts +0 -74
  244. package/src/build/build-adapter.js +0 -54
  245. package/src/build/build-types.d.ts +0 -57
  246. package/src/build/build-types.js +0 -0
  247. package/src/build/esbuild-build-adapter.d.ts +0 -69
  248. package/src/build/esbuild-build-adapter.js +0 -390
  249. package/src/config/config-builder.d.ts +0 -227
  250. package/src/config/config-builder.js +0 -392
  251. package/src/constants.d.ts +0 -32
  252. package/src/constants.js +0 -21
  253. package/src/create-app.d.ts +0 -17
  254. package/src/create-app.js +0 -66
  255. package/src/define-api-handler.d.ts +0 -25
  256. package/src/define-api-handler.js +0 -15
  257. package/src/dev/sc-server.d.ts +0 -30
  258. package/src/dev/sc-server.js +0 -111
  259. package/src/eco/component-render-context.d.ts +0 -105
  260. package/src/eco/component-render-context.js +0 -77
  261. package/src/eco/eco.d.ts +0 -9
  262. package/src/eco/eco.js +0 -110
  263. package/src/eco/eco.types.d.ts +0 -170
  264. package/src/eco/eco.types.js +0 -0
  265. package/src/eco/eco.utils.d.ts +0 -40
  266. package/src/eco/eco.utils.js +0 -40
  267. package/src/eco/global-injector-map.d.ts +0 -16
  268. package/src/eco/global-injector-map.js +0 -80
  269. package/src/eco/lazy-injector-map.d.ts +0 -8
  270. package/src/eco/lazy-injector-map.js +0 -70
  271. package/src/eco/module-dependencies.d.ts +0 -18
  272. package/src/eco/module-dependencies.js +0 -49
  273. package/src/errors/http-error.d.ts +0 -31
  274. package/src/errors/http-error.js +0 -50
  275. package/src/errors/index.d.ts +0 -2
  276. package/src/errors/index.js +0 -4
  277. package/src/errors/locals-access-error.d.ts +0 -4
  278. package/src/errors/locals-access-error.js +0 -9
  279. package/src/global/app-logger.d.ts +0 -2
  280. package/src/global/app-logger.js +0 -6
  281. package/src/hmr/client/hmr-runtime.d.ts +0 -10
  282. package/src/hmr/client/hmr-runtime.js +0 -86
  283. package/src/hmr/hmr-strategy.d.ts +0 -159
  284. package/src/hmr/hmr-strategy.js +0 -29
  285. package/src/hmr/hmr.test.e2e.d.ts +0 -1
  286. package/src/hmr/hmr.test.e2e.js +0 -50
  287. package/src/hmr/strategies/default-hmr-strategy.d.ts +0 -43
  288. package/src/hmr/strategies/default-hmr-strategy.js +0 -34
  289. package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -136
  290. package/src/hmr/strategies/js-hmr-strategy.js +0 -188
  291. package/src/index.browser.d.ts +0 -3
  292. package/src/index.browser.js +0 -4
  293. package/src/index.d.ts +0 -5
  294. package/src/index.js +0 -10
  295. package/src/integrations/ghtml/ghtml-renderer.d.ts +0 -15
  296. package/src/integrations/ghtml/ghtml-renderer.js +0 -60
  297. package/src/integrations/ghtml/ghtml.plugin.d.ts +0 -20
  298. package/src/integrations/ghtml/ghtml.plugin.js +0 -21
  299. package/src/internal-types.d.ts +0 -200
  300. package/src/internal-types.js +0 -0
  301. package/src/plugins/alias-resolver-plugin.d.ts +0 -2
  302. package/src/plugins/alias-resolver-plugin.js +0 -39
  303. package/src/plugins/eco-component-meta-plugin.d.ts +0 -95
  304. package/src/plugins/eco-component-meta-plugin.js +0 -157
  305. package/src/plugins/integration-plugin.d.ts +0 -102
  306. package/src/plugins/integration-plugin.js +0 -100
  307. package/src/plugins/processor.d.ts +0 -82
  308. package/src/plugins/processor.js +0 -122
  309. package/src/public-types.d.ts +0 -1098
  310. package/src/public-types.js +0 -0
  311. package/src/route-renderer/component-graph-executor.d.ts +0 -32
  312. package/src/route-renderer/component-graph-executor.js +0 -31
  313. package/src/route-renderer/component-graph.d.ts +0 -42
  314. package/src/route-renderer/component-graph.js +0 -72
  315. package/src/route-renderer/component-marker.d.ts +0 -52
  316. package/src/route-renderer/component-marker.js +0 -46
  317. package/src/route-renderer/dependency-resolver.d.ts +0 -24
  318. package/src/route-renderer/dependency-resolver.js +0 -428
  319. package/src/route-renderer/html-post-processing.service.d.ts +0 -40
  320. package/src/route-renderer/html-post-processing.service.js +0 -86
  321. package/src/route-renderer/html-post-processing.service.ts +0 -103
  322. package/src/route-renderer/integration-renderer.d.ts +0 -339
  323. package/src/route-renderer/integration-renderer.js +0 -526
  324. package/src/route-renderer/marker-graph-resolver.d.ts +0 -76
  325. package/src/route-renderer/marker-graph-resolver.js +0 -93
  326. package/src/route-renderer/page-module-loader.d.ts +0 -61
  327. package/src/route-renderer/page-module-loader.js +0 -102
  328. package/src/route-renderer/render-execution.service.d.ts +0 -69
  329. package/src/route-renderer/render-execution.service.js +0 -91
  330. package/src/route-renderer/render-preparation.service.d.ts +0 -112
  331. package/src/route-renderer/render-preparation.service.js +0 -243
  332. package/src/route-renderer/route-renderer.d.ts +0 -26
  333. package/src/route-renderer/route-renderer.js +0 -68
  334. package/src/router/fs-router-scanner.d.ts +0 -41
  335. package/src/router/fs-router-scanner.js +0 -155
  336. package/src/router/fs-router.d.ts +0 -26
  337. package/src/router/fs-router.js +0 -100
  338. package/src/services/asset-processing-service/asset-processing.service.d.ts +0 -41
  339. package/src/services/asset-processing-service/asset-processing.service.js +0 -250
  340. package/src/services/asset-processing-service/asset.factory.d.ts +0 -17
  341. package/src/services/asset-processing-service/asset.factory.js +0 -82
  342. package/src/services/asset-processing-service/assets.types.d.ts +0 -88
  343. package/src/services/asset-processing-service/assets.types.js +0 -0
  344. package/src/services/asset-processing-service/index.d.ts +0 -3
  345. package/src/services/asset-processing-service/index.js +0 -3
  346. package/src/services/asset-processing-service/processor.interface.d.ts +0 -22
  347. package/src/services/asset-processing-service/processor.interface.js +0 -6
  348. package/src/services/asset-processing-service/processor.registry.d.ts +0 -8
  349. package/src/services/asset-processing-service/processor.registry.js +0 -15
  350. package/src/services/asset-processing-service/processors/base/base-processor.d.ts +0 -24
  351. package/src/services/asset-processing-service/processors/base/base-processor.js +0 -59
  352. package/src/services/asset-processing-service/processors/base/base-script-processor.d.ts +0 -16
  353. package/src/services/asset-processing-service/processors/base/base-script-processor.js +0 -80
  354. package/src/services/asset-processing-service/processors/index.d.ts +0 -5
  355. package/src/services/asset-processing-service/processors/index.js +0 -5
  356. package/src/services/asset-processing-service/processors/script/content-script.processor.d.ts +0 -5
  357. package/src/services/asset-processing-service/processors/script/content-script.processor.js +0 -57
  358. package/src/services/asset-processing-service/processors/script/file-script.processor.d.ts +0 -8
  359. package/src/services/asset-processing-service/processors/script/file-script.processor.js +0 -76
  360. package/src/services/asset-processing-service/processors/script/node-module-script.processor.d.ts +0 -7
  361. package/src/services/asset-processing-service/processors/script/node-module-script.processor.js +0 -74
  362. package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -5
  363. package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +0 -25
  364. package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -9
  365. package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +0 -63
  366. package/src/services/cache/cache.types.d.ts +0 -107
  367. package/src/services/cache/cache.types.js +0 -0
  368. package/src/services/cache/index.d.ts +0 -7
  369. package/src/services/cache/index.js +0 -7
  370. package/src/services/cache/memory-cache-store.d.ts +0 -42
  371. package/src/services/cache/memory-cache-store.js +0 -98
  372. package/src/services/cache/page-cache-service.d.ts +0 -70
  373. package/src/services/cache/page-cache-service.js +0 -152
  374. package/src/services/html-transformer.service.d.ts +0 -50
  375. package/src/services/html-transformer.service.js +0 -163
  376. package/src/services/html-transformer.service.ts +0 -217
  377. package/src/services/page-module-import.service.d.ts +0 -37
  378. package/src/services/page-module-import.service.js +0 -88
  379. package/src/services/page-module-import.service.ts +0 -129
  380. package/src/services/page-request-cache-coordinator.service.d.ts +0 -75
  381. package/src/services/page-request-cache-coordinator.service.js +0 -107
  382. package/src/services/schema-validation-service.d.ts +0 -122
  383. package/src/services/schema-validation-service.js +0 -101
  384. package/src/services/validation/standard-schema.types.d.ts +0 -65
  385. package/src/services/validation/standard-schema.types.js +0 -0
  386. package/src/static-site-generator/static-site-generator.d.ts +0 -57
  387. package/src/static-site-generator/static-site-generator.js +0 -272
  388. package/src/utils/css.d.ts +0 -1
  389. package/src/utils/css.js +0 -7
  390. package/src/utils/deep-merge.d.ts +0 -14
  391. package/src/utils/deep-merge.js +0 -32
  392. package/src/utils/hash.d.ts +0 -1
  393. package/src/utils/hash.js +0 -7
  394. package/src/utils/html.d.ts +0 -1
  395. package/src/utils/html.js +0 -4
  396. package/src/utils/invariant.d.ts +0 -5
  397. package/src/utils/invariant.js +0 -11
  398. package/src/utils/locals-utils.d.ts +0 -15
  399. package/src/utils/locals-utils.js +0 -24
  400. package/src/utils/parse-cli-args.d.ts +0 -24
  401. package/src/utils/parse-cli-args.js +0 -47
  402. package/src/utils/path-utils.module.d.ts +0 -5
  403. package/src/utils/path-utils.module.js +0 -14
  404. package/src/utils/runtime.d.ts +0 -11
  405. package/src/utils/runtime.js +0 -40
  406. package/src/utils/server-utils.module.d.ts +0 -19
  407. package/src/utils/server-utils.module.js +0 -56
  408. package/src/watchers/project-watcher.d.ts +0 -125
  409. package/src/watchers/project-watcher.js +0 -265
  410. package/src/watchers/project-watcher.test-helpers.d.ts +0 -4
  411. package/src/watchers/project-watcher.test-helpers.js +0 -52
  412. /package/src/route-renderer/{component-graph-executor.ts → component-graph/component-graph-executor.ts} +0 -0
  413. /package/src/route-renderer/{component-graph.ts → component-graph/component-graph.ts} +0 -0
  414. /package/src/route-renderer/{component-marker.ts → component-graph/component-marker.ts} +0 -0
  415. /package/src/services/{asset-processing-service → assets/asset-processing-service}/processor.registry.ts +0 -0
  416. /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/index.ts +0 -0
  417. /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/content-script.processor.ts +0 -0
  418. /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/stylesheet/content-stylesheet.processor.ts +0 -0
@@ -0,0 +1,267 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { afterEach, test, vi } from 'vitest';
6
+ import { ConfigBuilder } from '../../config/config-builder.ts';
7
+ import { resolveInternalExecutionDir, resolveInternalWorkDir } from '../../utils/resolve-work-dir.ts';
8
+ import { HmrManager } from './hmr-manager.ts';
9
+
10
+ const tempRoots: string[] = [];
11
+
12
+ function createTempRoot(prefix: string): string {
13
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), `${prefix}-`));
14
+ tempRoots.push(root);
15
+ return root;
16
+ }
17
+
18
+ afterEach(() => {
19
+ for (const root of tempRoots.splice(0)) {
20
+ fs.rmSync(root, { recursive: true, force: true });
21
+ }
22
+ vi.restoreAllMocks();
23
+ });
24
+
25
+ test('HmrManager shares one in-flight entrypoint registration across concurrent callers', async () => {
26
+ const rootDir = createTempRoot('ecopages-bun-hmr-register');
27
+ const srcDir = path.join(rootDir, 'src');
28
+ const pagesDir = path.join(srcDir, 'pages');
29
+ fs.mkdirSync(pagesDir, { recursive: true });
30
+
31
+ const entrypointPath = path.join(pagesDir, 'react-lab.tsx');
32
+ fs.writeFileSync(entrypointPath, 'export default function Page() { return null; }', 'utf8');
33
+
34
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
35
+ const manager = new HmrManager({
36
+ appConfig: config,
37
+ bridge: {
38
+ subscriberCount: 0,
39
+ broadcast: () => {},
40
+ subscribe: () => {},
41
+ unsubscribe: () => {},
42
+ } as any,
43
+ });
44
+
45
+ const relativePathJs = path
46
+ .relative(config.absolutePaths.srcDir, entrypointPath)
47
+ .replace(/\.(tsx?|jsx?|mdx?)$/, '.js');
48
+ const encodedPathJs = relativePathJs.replace(/\[([^\]]+)\]/g, '_$1_');
49
+ const outputPath = path.join(resolveInternalWorkDir(config), 'assets', '_hmr', encodedPathJs);
50
+
51
+ const handleFileChange = vi.spyOn(manager, 'handleFileChange').mockImplementation(async () => {
52
+ await new Promise((resolve) => setTimeout(resolve, 25));
53
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
54
+ fs.writeFileSync(outputPath, 'export default 1;', 'utf8');
55
+ });
56
+
57
+ const [firstUrl, secondUrl] = await Promise.all([
58
+ manager.registerEntrypoint(entrypointPath),
59
+ manager.registerEntrypoint(entrypointPath),
60
+ ]);
61
+
62
+ assert.equal(firstUrl, '/assets/_hmr/pages/react-lab.js');
63
+ assert.equal(secondUrl, '/assets/_hmr/pages/react-lab.js');
64
+ assert.equal(handleFileChange.mock.calls.length, 1);
65
+ assert.equal(fs.existsSync(outputPath), true);
66
+
67
+ manager.stop();
68
+ });
69
+
70
+ test('HmrManager clears timed-out entrypoint registrations so later requests can retry', async () => {
71
+ const rootDir = createTempRoot('ecopages-bun-hmr-timeout-register');
72
+ const srcDir = path.join(rootDir, 'src');
73
+ const pagesDir = path.join(srcDir, 'pages');
74
+ fs.mkdirSync(pagesDir, { recursive: true });
75
+
76
+ const entrypointPath = path.join(pagesDir, 'stuck-page.tsx');
77
+ fs.writeFileSync(entrypointPath, 'export default function Page() { return null; }', 'utf8');
78
+
79
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
80
+ const manager = new HmrManager({
81
+ appConfig: config,
82
+ bridge: {
83
+ subscriberCount: 0,
84
+ broadcast: () => {},
85
+ subscribe: () => {},
86
+ unsubscribe: () => {},
87
+ } as any,
88
+ });
89
+
90
+ const previousNodeEnv = process.env.NODE_ENV;
91
+ process.env.NODE_ENV = 'development';
92
+
93
+ const handleFileChange = vi.spyOn(manager, 'handleFileChange').mockImplementation(async () => {
94
+ await new Promise(() => undefined);
95
+ });
96
+
97
+ try {
98
+ await assert.rejects(() => manager.registerEntrypoint(entrypointPath), /Timed out registering entrypoint/);
99
+
100
+ const registrations = (manager as unknown as { entrypointRegistrations: Map<string, Promise<string>> })
101
+ .entrypointRegistrations;
102
+ const watchedFiles = manager.getWatchedFiles();
103
+ assert.equal(registrations.size, 0);
104
+ assert.equal(watchedFiles.has(entrypointPath), false);
105
+
106
+ handleFileChange.mockImplementationOnce(async () => {
107
+ const relativePathJs = path
108
+ .relative(config.absolutePaths.srcDir, entrypointPath)
109
+ .replace(/\.(tsx?|jsx?|mdx?)$/, '.js');
110
+ const encodedPathJs = relativePathJs.replace(/\[([^\]]+)\]/g, '_$1_');
111
+ const outputPath = path.join(resolveInternalWorkDir(config), 'assets', '_hmr', encodedPathJs);
112
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
113
+ fs.writeFileSync(outputPath, 'export default 2;', 'utf8');
114
+ });
115
+
116
+ const retriedUrl = await manager.registerEntrypoint(entrypointPath);
117
+ assert.equal(retriedUrl, '/assets/_hmr/pages/stuck-page.js');
118
+ assert.equal(watchedFiles.get(entrypointPath), retriedUrl);
119
+ } finally {
120
+ process.env.NODE_ENV = previousNodeEnv;
121
+ manager.stop();
122
+ }
123
+ });
124
+
125
+ test('HmrManager fails strict entrypoint registration when the owning integration emits no output', async () => {
126
+ const rootDir = createTempRoot('ecopages-bun-hmr-strict-fail');
127
+ const srcDir = path.join(rootDir, 'src');
128
+ const pagesDir = path.join(srcDir, 'pages');
129
+ fs.mkdirSync(pagesDir, { recursive: true });
130
+
131
+ const entrypointPath = path.join(pagesDir, 'react-content.mdx');
132
+ fs.writeFileSync(entrypointPath, '# Hello', 'utf8');
133
+
134
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
135
+ const manager = new HmrManager({
136
+ appConfig: config,
137
+ bridge: {
138
+ subscriberCount: 0,
139
+ broadcast: () => {},
140
+ subscribe: () => {},
141
+ unsubscribe: () => {},
142
+ } as any,
143
+ });
144
+
145
+ vi.spyOn(manager, 'handleFileChange').mockImplementation(async () => {});
146
+
147
+ await assert.rejects(() => manager.registerEntrypoint(entrypointPath), /Integration failed to emit entrypoint/);
148
+ assert.equal(manager.getWatchedFiles().has(path.resolve(entrypointPath)), false);
149
+
150
+ manager.stop();
151
+ });
152
+
153
+ test('HmrManager uses the generic build path for script entrypoints when no strategy emits output', async () => {
154
+ const rootDir = createTempRoot('ecopages-bun-hmr-script-fallback');
155
+ const srcDir = path.join(rootDir, 'src');
156
+ fs.mkdirSync(srcDir, { recursive: true });
157
+
158
+ const entrypointPath = path.join(srcDir, 'script.ts');
159
+ fs.writeFileSync(entrypointPath, 'console.log("hello");', 'utf8');
160
+
161
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
162
+ const manager = new HmrManager({
163
+ appConfig: config,
164
+ bridge: {
165
+ subscriberCount: 0,
166
+ broadcast: () => {},
167
+ subscribe: () => {},
168
+ unsubscribe: () => {},
169
+ } as any,
170
+ });
171
+
172
+ const outputPath = path.join(resolveInternalWorkDir(config), 'assets', '_hmr', 'script.js');
173
+ const buildCalls: string[] = [];
174
+ config.runtime!.buildExecutor = {
175
+ build: vi.fn(async (options) => {
176
+ buildCalls.push(options.entrypoints[0] as string);
177
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
178
+ fs.writeFileSync(outputPath, 'fresh-output', 'utf8');
179
+
180
+ return {
181
+ success: true,
182
+ logs: [],
183
+ outputs: [{ path: outputPath }],
184
+ };
185
+ }),
186
+ };
187
+
188
+ vi.spyOn(manager, 'handleFileChange').mockImplementation(async () => {});
189
+
190
+ const outputUrl = await manager.registerScriptEntrypoint(entrypointPath);
191
+
192
+ assert.equal(outputUrl, '/assets/_hmr/script.js');
193
+ assert.deepEqual(buildCalls, [entrypointPath]);
194
+ assert.equal(fs.readFileSync(outputPath, 'utf8'), 'fresh-output');
195
+
196
+ manager.stop();
197
+ });
198
+
199
+ test('HmrManager stop clears retained registration state', async () => {
200
+ const rootDir = createTempRoot('ecopages-bun-hmr-stop-cleanup');
201
+ const srcDir = path.join(rootDir, 'src');
202
+ const pagesDir = path.join(srcDir, 'pages');
203
+ fs.mkdirSync(pagesDir, { recursive: true });
204
+
205
+ const entrypointPath = path.join(pagesDir, 'react-content.tsx');
206
+ fs.writeFileSync(entrypointPath, 'export default function Page() { return null; }', 'utf8');
207
+
208
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
209
+ const manager = new HmrManager({
210
+ appConfig: config,
211
+ bridge: {
212
+ subscriberCount: 0,
213
+ broadcast: () => {},
214
+ subscribe: () => {},
215
+ unsubscribe: () => {},
216
+ } as any,
217
+ });
218
+
219
+ const relativePathJs = path
220
+ .relative(config.absolutePaths.srcDir, entrypointPath)
221
+ .replace(/\.(tsx?|jsx?|mdx?)$/, '.js');
222
+ const encodedPathJs = relativePathJs.replace(/\[([^\]]+)\]/g, '_$1_');
223
+ const outputPath = path.join(resolveInternalWorkDir(config), 'assets', '_hmr', encodedPathJs);
224
+
225
+ vi.spyOn(manager, 'handleFileChange').mockImplementation(async () => {
226
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
227
+ fs.writeFileSync(outputPath, 'export default 1;', 'utf8');
228
+ });
229
+
230
+ manager.registerSpecifierMap({ react: '/assets/vendors/react.js' });
231
+ await manager.registerEntrypoint(entrypointPath);
232
+
233
+ manager.stop();
234
+
235
+ assert.equal(manager.getWatchedFiles().size, 0);
236
+ assert.equal(manager.getSpecifierMap().size, 0);
237
+ });
238
+
239
+ test('HmrManager keeps internal browser and server-module outputs out of distDir', async () => {
240
+ const rootDir = createTempRoot('ecopages-bun-hmr-internal-paths');
241
+ const config = await new ConfigBuilder().setRootDir(rootDir).build();
242
+ const manager = new HmrManager({
243
+ appConfig: config,
244
+ bridge: {
245
+ subscriberCount: 0,
246
+ broadcast: () => {},
247
+ subscribe: () => {},
248
+ unsubscribe: () => {},
249
+ } as any,
250
+ });
251
+
252
+ assert.equal(manager.getDistDir(), path.join(resolveInternalWorkDir(config), 'assets', '_hmr'));
253
+
254
+ const importModule = vi.fn(async (_options: { outdir: string }) => ({}));
255
+ (manager as unknown as { serverModuleTranspiler: { importModule: typeof importModule } }).serverModuleTranspiler = {
256
+ importModule,
257
+ };
258
+
259
+ await manager.getDefaultContext().importServerModule(path.join(config.absolutePaths.srcDir, 'pages', 'index.tsx'));
260
+
261
+ assert.equal(
262
+ importModule.mock.calls[0]?.[0]?.outdir,
263
+ path.join(resolveInternalExecutionDir(config), '.server-modules'),
264
+ );
265
+
266
+ manager.stop();
267
+ });
@@ -2,17 +2,27 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import type { ServerWebSocket, WebSocketHandler } from 'bun';
4
4
  import { RESOLVED_ASSETS_DIR } from '../../constants';
5
- import { defaultBuildAdapter } from '../../build/build-adapter.ts';
5
+ import { getAppBuildExecutor } from '../../build/build-adapter.ts';
6
6
  import type { DefaultHmrContext, EcoPagesAppConfig, IHmrManager } from '../../internal-types';
7
7
  import type { EcoBuildPlugin } from '../../build/build-types.ts';
8
8
  import { fileSystem } from '@ecopages/file-system';
9
- import type { HmrStrategy } from '../../hmr/hmr-strategy';
10
- import { HmrStrategyType } from '../../hmr/hmr-strategy';
9
+ import { HmrStrategyType, type HmrStrategy } from '../../hmr/hmr-strategy';
11
10
  import { DefaultHmrStrategy } from '../../hmr/strategies/default-hmr-strategy';
12
11
  import { JsHmrStrategy } from '../../hmr/strategies/js-hmr-strategy';
13
12
  import { appLogger } from '../../global/app-logger';
14
13
  import type { ClientBridge } from './client-bridge';
15
14
  import type { ClientBridgeEvent } from '../../public-types';
15
+ import { HmrEntrypointRegistrar } from '../shared/hmr-entrypoint-registrar.ts';
16
+ import { BrowserBundleService } from '../../services/assets/browser-bundle.service.ts';
17
+ import { getAppServerModuleTranspiler } from '../../services/module-loading/app-server-module-transpiler.service';
18
+ import {
19
+ getAppEntrypointDependencyGraph,
20
+ NoopEntrypointDependencyGraph,
21
+ setAppEntrypointDependencyGraph,
22
+ } from '../../services/runtime-state/entrypoint-dependency-graph.service.ts';
23
+ import { getAppRuntimeSpecifierRegistry } from '../../services/runtime-state/runtime-specifier-registry.service.ts';
24
+ import type { ServerModuleTranspiler } from '../../services/module-loading/server-module-transpiler.service';
25
+ import { resolveInternalExecutionDir, resolveInternalWorkDir } from '../../utils/resolve-work-dir.ts';
16
26
 
17
27
  type BunSocket = ServerWebSocket<unknown>;
18
28
  type BunSocketHandler = WebSocketHandler<unknown>;
@@ -22,19 +32,36 @@ export interface HmrManagerParams {
22
32
  bridge: ClientBridge;
23
33
  }
24
34
 
35
+ type HandleFileChangeOptions = {
36
+ broadcast?: boolean;
37
+ };
38
+
39
+ /**
40
+ * Bun development HMR manager.
41
+ *
42
+ * @remarks
43
+ * Bun shares the same public contract as the Node manager: page entrypoints are
44
+ * strict integration-owned registrations, while generic script assets use their
45
+ * own explicit registration path.
46
+ */
25
47
  export class HmrManager implements IHmrManager {
48
+ private static readonly entrypointRegistrationTimeoutMs = 4000;
26
49
  public readonly appConfig: EcoPagesAppConfig;
27
50
  private readonly bridge: ClientBridge;
28
51
  /** Keep track of watchers */
29
52
  private watchers = new Map<string, fs.FSWatcher>();
30
53
  /** entrypoint -> output path */
31
54
  private watchedFiles = new Map<string, string>();
32
- /** bare specifier -> runtime URL (e.g., 'react' -> '/assets/vendors/react.js') */
33
- private specifierMap = new Map<string, string>();
55
+ private entrypointRegistrations = new Map<string, Promise<string>>();
34
56
  private distDir: string;
35
57
  private plugins: EcoBuildPlugin[] = [];
36
58
  private enabled = true;
37
59
  private strategies: HmrStrategy[] = [];
60
+ private readonly entrypointRegistrar: HmrEntrypointRegistrar;
61
+ private readonly browserBundleService: BrowserBundleService;
62
+ private readonly entrypointDependencyGraph: ReturnType<typeof getAppEntrypointDependencyGraph>;
63
+ private readonly runtimeSpecifierRegistry: ReturnType<typeof getAppRuntimeSpecifierRegistry>;
64
+ private readonly serverModuleTranspiler: ServerModuleTranspiler;
38
65
  private wsHandler!: {
39
66
  open: (ws: BunSocket) => void;
40
67
  close: (ws: BunSocket) => void;
@@ -43,7 +70,24 @@ export class HmrManager implements IHmrManager {
43
70
  constructor({ appConfig, bridge }: HmrManagerParams) {
44
71
  this.appConfig = appConfig;
45
72
  this.bridge = bridge;
46
- this.distDir = path.join(this.appConfig.absolutePaths.distDir, RESOLVED_ASSETS_DIR, '_hmr');
73
+ this.distDir = path.join(resolveInternalWorkDir(this.appConfig), RESOLVED_ASSETS_DIR, '_hmr');
74
+ this.entrypointRegistrar = new HmrEntrypointRegistrar({
75
+ srcDir: this.appConfig.absolutePaths.srcDir,
76
+ distDir: this.distDir,
77
+ entrypointRegistrations: this.entrypointRegistrations,
78
+ watchedFiles: this.watchedFiles,
79
+ clearFailedRegistration: (entrypointPath) => this.clearFailedEntrypointRegistration(entrypointPath),
80
+ registrationTimeoutMs: HmrManager.entrypointRegistrationTimeoutMs,
81
+ });
82
+ this.browserBundleService = new BrowserBundleService(appConfig);
83
+ const existingEntrypointDependencyGraph = getAppEntrypointDependencyGraph(appConfig);
84
+ this.entrypointDependencyGraph =
85
+ existingEntrypointDependencyGraph instanceof NoopEntrypointDependencyGraph
86
+ ? existingEntrypointDependencyGraph
87
+ : new NoopEntrypointDependencyGraph();
88
+ setAppEntrypointDependencyGraph(this.appConfig, this.entrypointDependencyGraph);
89
+ this.runtimeSpecifierRegistry = getAppRuntimeSpecifierRegistry(this.appConfig);
90
+ this.serverModuleTranspiler = getAppServerModuleTranspiler(this.appConfig);
47
91
  this.cleanDistDir();
48
92
  this.initializeStrategies();
49
93
  }
@@ -58,6 +102,29 @@ export class HmrManager implements IHmrManager {
58
102
  fileSystem.ensureDir(this.distDir);
59
103
  }
60
104
 
105
+ /**
106
+ * Returns whether the generic JS strategy may rebuild an entrypoint.
107
+ *
108
+ * @remarks
109
+ * Integration-owned page entrypoints are excluded so a shared dependency
110
+ * invalidation cannot replace framework-owned browser output with a generic JS
111
+ * rebuild.
112
+ */
113
+ private shouldJsStrategyProcessEntrypoint(entrypointPath: string): boolean {
114
+ return !this.strategies.some((strategy) => {
115
+ if (strategy.type !== HmrStrategyType.INTEGRATION || strategy.priority <= HmrStrategyType.SCRIPT) {
116
+ return false;
117
+ }
118
+
119
+ try {
120
+ return strategy.matches(entrypointPath);
121
+ } catch (error) {
122
+ appLogger.error(error);
123
+ return false;
124
+ }
125
+ });
126
+ }
127
+
61
128
  /**
62
129
  * Initializes core HMR strategies.
63
130
  * Strategies are evaluated in priority order (highest first).
@@ -65,10 +132,16 @@ export class HmrManager implements IHmrManager {
65
132
  private initializeStrategies(): void {
66
133
  const jsContext = {
67
134
  getWatchedFiles: () => this.watchedFiles,
68
- getSpecifierMap: () => this.specifierMap,
135
+ getSpecifierMap: () => this.runtimeSpecifierRegistry.getAll(),
69
136
  getDistDir: () => this.distDir,
70
137
  getPlugins: () => this.plugins,
71
138
  getSrcDir: () => this.appConfig.absolutePaths.srcDir,
139
+ getPagesDir: () => this.appConfig.absolutePaths.pagesDir,
140
+ getLayoutsDir: () => this.appConfig.absolutePaths.layoutsDir,
141
+ getTemplateExtensions: () => this.appConfig.templatesExt,
142
+ getBrowserBundleService: () => this.browserBundleService,
143
+ getEntrypointDependencyGraph: () => this.entrypointDependencyGraph,
144
+ shouldProcessEntrypoint: (entrypointPath: string) => this.shouldJsStrategyProcessEntrypoint(entrypointPath),
72
145
  };
73
146
 
74
147
  this.strategies = [new JsHmrStrategy(jsContext), new DefaultHmrStrategy()];
@@ -96,14 +169,16 @@ export class HmrManager implements IHmrManager {
96
169
  }
97
170
 
98
171
  /**
99
- * Registers a mapping from bare specifiers to vendor URLs.
100
- * Used by integrations to provide their module resolution mappings.
101
- * @param map - Object mapping bare specifiers to vendor URLs
172
+ * Registers runtime bare-specifier mappings exposed by integrations.
173
+ *
174
+ * @remarks
175
+ * These mappings are consumed by framework-owned HMR strategies that preserve
176
+ * shared runtime imports in browser bundles. The registry stays generic so
177
+ * these mappings can later support broader import-map-style runtime features
178
+ * without moving integration semantics into core.
102
179
  */
103
180
  public registerSpecifierMap(map: Record<string, string>): void {
104
- for (const [specifier, url] of Object.entries(map)) {
105
- this.specifierMap.set(specifier, url);
106
- }
181
+ this.runtimeSpecifierRegistry.register(map);
107
182
  }
108
183
 
109
184
  public getWebSocketHandler(): BunSocketHandler {
@@ -134,12 +209,12 @@ export class HmrManager implements IHmrManager {
134
209
  public async buildRuntime(): Promise<void> {
135
210
  const runtimeSource = path.resolve(import.meta.dirname, '../../hmr/client/hmr-runtime.ts');
136
211
 
137
- const result = await defaultBuildAdapter.build({
212
+ const result = await this.browserBundleService.bundle({
213
+ profile: 'hmr-runtime',
138
214
  entrypoints: [runtimeSource],
139
215
  outdir: this.distDir,
140
216
  naming: '_hmr_runtime.js',
141
217
  minify: false,
142
- ...defaultBuildAdapter.getTranspileOptions('hmr-runtime'),
143
218
  plugins: this.plugins,
144
219
  });
145
220
 
@@ -159,32 +234,13 @@ export class HmrManager implements IHmrManager {
159
234
  this.bridge.broadcast(event);
160
235
  }
161
236
 
162
- public canHandleFileChange(filePath: string): boolean {
163
- const sorted = [...this.strategies].sort((a, b) => b.priority - a.priority);
164
- const strategy = sorted.find((candidate) => {
165
- try {
166
- return candidate.matches(filePath);
167
- } catch (err) {
168
- appLogger.error(`[HmrManager] Error checking match for ${candidate.constructor.name}:`, err as Error);
169
- return false;
170
- }
171
- });
172
-
173
- return strategy !== undefined && strategy.type !== HmrStrategyType.FALLBACK;
174
- }
175
-
176
- /**
177
- * Handles file changes using registered HMR strategies.
178
- * Strategies are evaluated in priority order until one matches.
179
- * @param filePath - Absolute path to the changed file
180
- */
181
- public async handleFileChange(filePath: string): Promise<void> {
237
+ public async handleFileChange(filePath: string, options: HandleFileChangeOptions = {}): Promise<void> {
182
238
  const sorted = [...this.strategies].sort((a, b) => b.priority - a.priority);
183
239
  const strategy = sorted.find((s) => {
184
240
  try {
185
241
  return s.matches(filePath);
186
242
  } catch (err) {
187
- appLogger.error(`[HmrManager] Error checking match for ${s.constructor.name}:`, err as Error);
243
+ appLogger.error(err);
188
244
  return false;
189
245
  }
190
246
  });
@@ -197,8 +253,9 @@ export class HmrManager implements IHmrManager {
197
253
  appLogger.debug(`[HmrManager] Selected strategy: ${strategy.constructor.name}`);
198
254
 
199
255
  const action = await strategy.process(filePath);
256
+ const shouldBroadcast = options.broadcast ?? true;
200
257
 
201
- if (action.type === 'broadcast') {
258
+ if (shouldBroadcast && action.type === 'broadcast') {
202
259
  if (action.events) {
203
260
  for (const event of action.events) {
204
261
  this.broadcast(event);
@@ -207,9 +264,6 @@ export class HmrManager implements IHmrManager {
207
264
  }
208
265
  }
209
266
 
210
- /**
211
- * Registers a client entrypoint to be built and watched by Bun.
212
- */
213
267
  public getOutputUrl(entrypointPath: string): string | undefined {
214
268
  return this.watchedFiles.get(entrypointPath);
215
269
  }
@@ -219,7 +273,7 @@ export class HmrManager implements IHmrManager {
219
273
  }
220
274
 
221
275
  public getSpecifierMap(): Map<string, string> {
222
- return this.specifierMap;
276
+ return this.runtimeSpecifierRegistry.getAll();
223
277
  }
224
278
 
225
279
  public getDistDir(): string {
@@ -233,64 +287,123 @@ export class HmrManager implements IHmrManager {
233
287
  public getDefaultContext(): DefaultHmrContext {
234
288
  return {
235
289
  getWatchedFiles: () => this.watchedFiles,
236
- getSpecifierMap: () => this.specifierMap,
290
+ getSpecifierMap: () => this.runtimeSpecifierRegistry.getAll(),
237
291
  getDistDir: () => this.distDir,
238
292
  getPlugins: () => this.plugins,
239
293
  getSrcDir: () => this.appConfig.absolutePaths.srcDir,
240
294
  getLayoutsDir: () => this.appConfig.absolutePaths.layoutsDir,
241
295
  getPagesDir: () => this.appConfig.absolutePaths.pagesDir,
296
+ getBuildExecutor: () => getAppBuildExecutor(this.appConfig),
297
+ getBrowserBundleService: () => this.browserBundleService,
298
+ importServerModule: async <T>(filePath: string) =>
299
+ await this.serverModuleTranspiler.importModule<T>({
300
+ filePath,
301
+ outdir: path.join(resolveInternalExecutionDir(this.appConfig), '.server-modules'),
302
+ externalPackages: true,
303
+ }),
242
304
  };
243
305
  }
244
306
 
307
+ private clearFailedEntrypointRegistration(entrypointPath: string): void {
308
+ this.watchedFiles.delete(entrypointPath);
309
+ }
310
+
311
+ /**
312
+ * Registers one integration-owned page entrypoint.
313
+ *
314
+ * @remarks
315
+ * Concurrent callers share one in-flight registration. The registration is
316
+ * cleared from the dedupe map when it settles so later callers cannot inherit a
317
+ * stale promise.
318
+ */
245
319
  public async registerEntrypoint(entrypointPath: string): Promise<string> {
246
- if (this.watchedFiles.has(entrypointPath)) {
247
- return this.watchedFiles.get(entrypointPath)!;
248
- }
320
+ return await this.entrypointRegistrar.registerEntrypoint(entrypointPath, {
321
+ emit: async (normalizedEntrypoint, outputPath) =>
322
+ await this.emitStrictEntrypoint(normalizedEntrypoint, outputPath),
323
+ getMissingOutputError: (normalizedEntrypoint, outputPath) =>
324
+ new Error(
325
+ `[HMR] Integration failed to emit entrypoint ${normalizedEntrypoint} to ${outputPath}. Page entrypoints must be produced by their owning integration.`,
326
+ ),
327
+ });
328
+ }
249
329
 
250
- const srcDir = this.appConfig.absolutePaths.srcDir;
251
- const relativePath = path.relative(srcDir, entrypointPath);
252
- const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, '.js');
253
- const encodedPathJs = this.encodeDynamicSegments(relativePathJs);
330
+ /**
331
+ * Registers one generic script entrypoint.
332
+ *
333
+ * @remarks
334
+ * This explicit path keeps the page-entrypoint contract strict while still
335
+ * allowing generic script assets to use the fallback build path.
336
+ */
337
+ public async registerScriptEntrypoint(entrypointPath: string): Promise<string> {
338
+ return await this.entrypointRegistrar.registerEntrypoint(entrypointPath, {
339
+ emit: async (normalizedEntrypoint, outputPath) =>
340
+ await this.emitScriptEntrypoint(normalizedEntrypoint, outputPath),
341
+ getMissingOutputError: (normalizedEntrypoint) =>
342
+ new Error(`[HMR] Failed to register script entrypoint: ${normalizedEntrypoint}`),
343
+ });
344
+ }
254
345
 
255
- const urlPath = encodedPathJs.split(path.sep).join('/');
256
- const outputUrl = `/${path.join(RESOLVED_ASSETS_DIR, '_hmr', urlPath)}`;
257
- const outputPath = path.join(this.distDir, urlPath);
346
+ /**
347
+ * Performs strict integration-owned registration for one normalized path.
348
+ *
349
+ * @remarks
350
+ * The manager reserves the output URL, removes any stale emitted file, runs
351
+ * strategy processing without broadcasting, and then verifies that the owning
352
+ * integration emitted the expected file.
353
+ */
354
+ private async emitStrictEntrypoint(entrypointPath: string, _outputPath: string): Promise<void> {
355
+ await this.handleFileChange(entrypointPath, { broadcast: false });
356
+ }
258
357
 
259
- this.watchedFiles.set(entrypointPath, outputUrl);
358
+ /**
359
+ * Performs registration for a generic script asset.
360
+ *
361
+ * @remarks
362
+ * Strategies get the first chance to emit output. If no output exists after
363
+ * that pass, Bun falls back to the generic browser build for this explicit
364
+ * script-only path.
365
+ */
366
+ private async emitScriptEntrypoint(entrypointPath: string, outputPath: string): Promise<void> {
367
+ const naming = path.relative(this.distDir, outputPath).split(path.sep).join('/');
260
368
 
261
- await this.handleFileChange(entrypointPath);
369
+ await this.handleFileChange(entrypointPath, { broadcast: false });
262
370
 
263
371
  if (!fileSystem.exists(outputPath)) {
264
- const fallback = await defaultBuildAdapter.build({
372
+ const buildResult = await this.browserBundleService.bundle({
373
+ profile: 'hmr-entrypoint',
265
374
  entrypoints: [entrypointPath],
266
375
  outdir: this.distDir,
267
- naming: encodedPathJs,
376
+ naming,
268
377
  minify: false,
269
- external: Array.from(this.specifierMap.keys()),
270
- ...defaultBuildAdapter.getTranspileOptions('hmr-entrypoint'),
271
378
  plugins: this.plugins,
272
379
  });
273
380
 
274
- if (!fallback.success) {
275
- appLogger.error(`[HMR] Fallback build failed for ${entrypointPath}:`, fallback.logs);
381
+ if (!buildResult.success) {
382
+ appLogger.error(
383
+ `[HMR] Generic script entrypoint build failed for ${entrypointPath}:`,
384
+ buildResult.logs,
385
+ );
276
386
  }
277
387
  }
278
-
279
- return outputUrl;
280
388
  }
281
389
 
282
390
  /**
283
- * Encodes dynamic route segments (brackets) in file paths.
284
- * Converts `[slug]` to `_slug_` to avoid filesystem/URL issues.
391
+ * Stops active watchers and releases retained registration state.
392
+ *
393
+ * @remarks
394
+ * Emitted `_hmr` files remain on disk because parallel app processes may share
395
+ * the same dist directory. The in-memory indexes are cleared so stale
396
+ * entrypoints and specifier maps cannot leak through a reused manager object.
285
397
  */
286
- private encodeDynamicSegments(filepath: string): string {
287
- return filepath.replace(/\[([^\]]+)\]/g, '_$1_');
288
- }
289
-
290
398
  public stop() {
399
+ this.entrypointRegistrations.clear();
291
400
  for (const watcher of this.watchers.values()) {
292
401
  watcher.close();
293
402
  }
294
403
  this.watchers.clear();
404
+ this.watchedFiles.clear();
405
+ this.runtimeSpecifierRegistry.clear();
406
+ this.entrypointDependencyGraph.reset();
407
+ this.plugins = [];
295
408
  }
296
409
  }
@@ -1,3 +1,2 @@
1
1
  export { EcopagesApp, createApp } from './create-app.ts';
2
- export { defineApiHandler, defineGroupHandler } from './define-api-handler.ts';
3
- export type { GroupHandler } from './define-api-handler.ts';
2
+ export { defineApiHandler, defineGroupHandler, type GroupHandler } from '../shared/define-api-handler.ts';