@ecopages/core 0.2.0-alpha.5 → 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 +180 -47
  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 +207 -97
  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 +234 -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 -84
  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 +71 -78
  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 -11
  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 -0
  185. package/src/watchers/project-watcher.test.ts +678 -0
  186. package/src/watchers/project-watcher.ts +49 -50
  187. package/CHANGELOG.md +0 -94
  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 -79
  202. package/src/adapters/bun/hmr-manager.js +0 -222
  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 -62
  218. package/src/adapters/node/node-hmr-manager.js +0 -221
  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 -75
  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 -391
  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 -192
  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 -1094
  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 -132
  409. package/src/watchers/project-watcher.js +0 -281
  410. package/src/watchers/project-watcher.test-helpers.d.ts +0 -4
  411. package/src/watchers/project-watcher.test-helpers.js +0 -51
  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,16 +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';
9
+ import { HmrStrategyType, type HmrStrategy } from '../../hmr/hmr-strategy';
10
10
  import { DefaultHmrStrategy } from '../../hmr/strategies/default-hmr-strategy';
11
11
  import { JsHmrStrategy } from '../../hmr/strategies/js-hmr-strategy';
12
12
  import { appLogger } from '../../global/app-logger';
13
13
  import type { ClientBridge } from './client-bridge';
14
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';
15
26
 
16
27
  type BunSocket = ServerWebSocket<unknown>;
17
28
  type BunSocketHandler = WebSocketHandler<unknown>;
@@ -21,19 +32,36 @@ export interface HmrManagerParams {
21
32
  bridge: ClientBridge;
22
33
  }
23
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
+ */
24
47
  export class HmrManager implements IHmrManager {
48
+ private static readonly entrypointRegistrationTimeoutMs = 4000;
25
49
  public readonly appConfig: EcoPagesAppConfig;
26
50
  private readonly bridge: ClientBridge;
27
51
  /** Keep track of watchers */
28
52
  private watchers = new Map<string, fs.FSWatcher>();
29
53
  /** entrypoint -> output path */
30
54
  private watchedFiles = new Map<string, string>();
31
- /** bare specifier -> runtime URL (e.g., 'react' -> '/assets/vendors/react.js') */
32
- private specifierMap = new Map<string, string>();
55
+ private entrypointRegistrations = new Map<string, Promise<string>>();
33
56
  private distDir: string;
34
57
  private plugins: EcoBuildPlugin[] = [];
35
58
  private enabled = true;
36
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;
37
65
  private wsHandler!: {
38
66
  open: (ws: BunSocket) => void;
39
67
  close: (ws: BunSocket) => void;
@@ -42,7 +70,24 @@ export class HmrManager implements IHmrManager {
42
70
  constructor({ appConfig, bridge }: HmrManagerParams) {
43
71
  this.appConfig = appConfig;
44
72
  this.bridge = bridge;
45
- 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);
46
91
  this.cleanDistDir();
47
92
  this.initializeStrategies();
48
93
  }
@@ -57,6 +102,29 @@ export class HmrManager implements IHmrManager {
57
102
  fileSystem.ensureDir(this.distDir);
58
103
  }
59
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
+
60
128
  /**
61
129
  * Initializes core HMR strategies.
62
130
  * Strategies are evaluated in priority order (highest first).
@@ -64,10 +132,16 @@ export class HmrManager implements IHmrManager {
64
132
  private initializeStrategies(): void {
65
133
  const jsContext = {
66
134
  getWatchedFiles: () => this.watchedFiles,
67
- getSpecifierMap: () => this.specifierMap,
135
+ getSpecifierMap: () => this.runtimeSpecifierRegistry.getAll(),
68
136
  getDistDir: () => this.distDir,
69
137
  getPlugins: () => this.plugins,
70
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),
71
145
  };
72
146
 
73
147
  this.strategies = [new JsHmrStrategy(jsContext), new DefaultHmrStrategy()];
@@ -95,14 +169,16 @@ export class HmrManager implements IHmrManager {
95
169
  }
96
170
 
97
171
  /**
98
- * Registers a mapping from bare specifiers to vendor URLs.
99
- * Used by integrations to provide their module resolution mappings.
100
- * @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.
101
179
  */
102
180
  public registerSpecifierMap(map: Record<string, string>): void {
103
- for (const [specifier, url] of Object.entries(map)) {
104
- this.specifierMap.set(specifier, url);
105
- }
181
+ this.runtimeSpecifierRegistry.register(map);
106
182
  }
107
183
 
108
184
  public getWebSocketHandler(): BunSocketHandler {
@@ -133,12 +209,12 @@ export class HmrManager implements IHmrManager {
133
209
  public async buildRuntime(): Promise<void> {
134
210
  const runtimeSource = path.resolve(import.meta.dirname, '../../hmr/client/hmr-runtime.ts');
135
211
 
136
- const result = await defaultBuildAdapter.build({
212
+ const result = await this.browserBundleService.bundle({
213
+ profile: 'hmr-runtime',
137
214
  entrypoints: [runtimeSource],
138
215
  outdir: this.distDir,
139
216
  naming: '_hmr_runtime.js',
140
217
  minify: false,
141
- ...defaultBuildAdapter.getTranspileOptions('hmr-runtime'),
142
218
  plugins: this.plugins,
143
219
  });
144
220
 
@@ -158,7 +234,7 @@ export class HmrManager implements IHmrManager {
158
234
  this.bridge.broadcast(event);
159
235
  }
160
236
 
161
- public async handleFileChange(filePath: string): Promise<void> {
237
+ public async handleFileChange(filePath: string, options: HandleFileChangeOptions = {}): Promise<void> {
162
238
  const sorted = [...this.strategies].sort((a, b) => b.priority - a.priority);
163
239
  const strategy = sorted.find((s) => {
164
240
  try {
@@ -177,8 +253,9 @@ export class HmrManager implements IHmrManager {
177
253
  appLogger.debug(`[HmrManager] Selected strategy: ${strategy.constructor.name}`);
178
254
 
179
255
  const action = await strategy.process(filePath);
256
+ const shouldBroadcast = options.broadcast ?? true;
180
257
 
181
- if (action.type === 'broadcast') {
258
+ if (shouldBroadcast && action.type === 'broadcast') {
182
259
  if (action.events) {
183
260
  for (const event of action.events) {
184
261
  this.broadcast(event);
@@ -187,9 +264,6 @@ export class HmrManager implements IHmrManager {
187
264
  }
188
265
  }
189
266
 
190
- /**
191
- * Registers a client entrypoint to be built and watched by Bun.
192
- */
193
267
  public getOutputUrl(entrypointPath: string): string | undefined {
194
268
  return this.watchedFiles.get(entrypointPath);
195
269
  }
@@ -199,7 +273,7 @@ export class HmrManager implements IHmrManager {
199
273
  }
200
274
 
201
275
  public getSpecifierMap(): Map<string, string> {
202
- return this.specifierMap;
276
+ return this.runtimeSpecifierRegistry.getAll();
203
277
  }
204
278
 
205
279
  public getDistDir(): string {
@@ -213,64 +287,123 @@ export class HmrManager implements IHmrManager {
213
287
  public getDefaultContext(): DefaultHmrContext {
214
288
  return {
215
289
  getWatchedFiles: () => this.watchedFiles,
216
- getSpecifierMap: () => this.specifierMap,
290
+ getSpecifierMap: () => this.runtimeSpecifierRegistry.getAll(),
217
291
  getDistDir: () => this.distDir,
218
292
  getPlugins: () => this.plugins,
219
293
  getSrcDir: () => this.appConfig.absolutePaths.srcDir,
220
294
  getLayoutsDir: () => this.appConfig.absolutePaths.layoutsDir,
221
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
+ }),
222
304
  };
223
305
  }
224
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
+ */
225
319
  public async registerEntrypoint(entrypointPath: string): Promise<string> {
226
- if (this.watchedFiles.has(entrypointPath)) {
227
- return this.watchedFiles.get(entrypointPath)!;
228
- }
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
+ }
229
329
 
230
- const srcDir = this.appConfig.absolutePaths.srcDir;
231
- const relativePath = path.relative(srcDir, entrypointPath);
232
- const relativePathJs = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, '.js');
233
- 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
+ }
234
345
 
235
- const urlPath = encodedPathJs.split(path.sep).join('/');
236
- const outputUrl = `/${path.join(RESOLVED_ASSETS_DIR, '_hmr', urlPath)}`;
237
- 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
+ }
238
357
 
239
- 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('/');
240
368
 
241
- await this.handleFileChange(entrypointPath);
369
+ await this.handleFileChange(entrypointPath, { broadcast: false });
242
370
 
243
371
  if (!fileSystem.exists(outputPath)) {
244
- const fallback = await defaultBuildAdapter.build({
372
+ const buildResult = await this.browserBundleService.bundle({
373
+ profile: 'hmr-entrypoint',
245
374
  entrypoints: [entrypointPath],
246
375
  outdir: this.distDir,
247
- naming: encodedPathJs,
376
+ naming,
248
377
  minify: false,
249
- external: Array.from(this.specifierMap.keys()),
250
- ...defaultBuildAdapter.getTranspileOptions('hmr-entrypoint'),
251
378
  plugins: this.plugins,
252
379
  });
253
380
 
254
- if (!fallback.success) {
255
- 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
+ );
256
386
  }
257
387
  }
258
-
259
- return outputUrl;
260
388
  }
261
389
 
262
390
  /**
263
- * Encodes dynamic route segments (brackets) in file paths.
264
- * 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.
265
397
  */
266
- private encodeDynamicSegments(filepath: string): string {
267
- return filepath.replace(/\[([^\]]+)\]/g, '_$1_');
268
- }
269
-
270
398
  public stop() {
399
+ this.entrypointRegistrations.clear();
271
400
  for (const watcher of this.watchers.values()) {
272
401
  watcher.close();
273
402
  }
274
403
  this.watchers.clear();
404
+ this.watchedFiles.clear();
405
+ this.runtimeSpecifierRegistry.clear();
406
+ this.entrypointDependencyGraph.reset();
407
+ this.plugins = [];
275
408
  }
276
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';