@ecopages/core 0.2.0-beta.31 → 0.2.0-beta.33

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 (343) hide show
  1. package/README.md +1 -0
  2. package/package.json +90 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +0 -8
  4. package/src/adapters/abstract/application-adapter.js +252 -261
  5. package/src/adapters/abstract/router-adapter.js +12 -4
  6. package/src/adapters/abstract/segment-path-matcher.js +132 -134
  7. package/src/adapters/abstract/server-adapter.js +22 -14
  8. package/src/adapters/abstract/ws-pattern-matcher.js +24 -26
  9. package/src/adapters/bun/bun-user-websocket-lifecycle.js +83 -86
  10. package/src/adapters/bun/client-bridge.js +51 -50
  11. package/src/adapters/bun/create-app.js +171 -161
  12. package/src/adapters/bun/hmr-manager.js +62 -57
  13. package/src/adapters/bun/index.js +2 -31
  14. package/src/adapters/bun/runtime-host.js +61 -54
  15. package/src/adapters/bun/server-adapter.d.ts +0 -1
  16. package/src/adapters/bun/server-adapter.js +561 -561
  17. package/src/adapters/bun/static-preview-host.js +43 -46
  18. package/src/adapters/create-app.js +11 -14
  19. package/src/adapters/index.js +2 -30
  20. package/src/adapters/node/create-app.js +123 -129
  21. package/src/adapters/node/http-request-bridge.js +147 -106
  22. package/src/adapters/node/node-client-bridge.js +62 -64
  23. package/src/adapters/node/node-hmr-manager.js +58 -48
  24. package/src/adapters/node/runtime-host.js +96 -89
  25. package/src/adapters/node/server-adapter-dependencies.js +18 -21
  26. package/src/adapters/node/server-adapter.d.ts +0 -1
  27. package/src/adapters/node/server-adapter.js +347 -326
  28. package/src/adapters/node/static-content-server.js +172 -191
  29. package/src/adapters/node/static-preview-host.js +87 -78
  30. package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.d.ts +16 -0
  31. package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.js +32 -0
  32. package/src/adapters/shared/hmr/hmr-html-response.js +46 -32
  33. package/src/adapters/shared/hmr/shared-hmr-manager.d.ts +29 -38
  34. package/src/adapters/shared/hmr/shared-hmr-manager.js +439 -425
  35. package/src/adapters/shared/http/api-request-pipeline.js +122 -119
  36. package/src/adapters/shared/http/api-response.js +77 -77
  37. package/src/adapters/shared/http/create-body-response.js +22 -19
  38. package/src/adapters/shared/http/define-api-handler.js +41 -55
  39. package/src/adapters/shared/http/explicit-static-render-preparation.js +26 -23
  40. package/src/adapters/shared/http/explicit-static-route-matcher.js +55 -62
  41. package/src/adapters/shared/http/file-route-middleware-pipeline.js +81 -75
  42. package/src/adapters/shared/http/fs-server-response-factory.js +101 -96
  43. package/src/adapters/shared/http/fs-server-response-matcher.js +239 -257
  44. package/src/adapters/shared/http/run-middleware-chain.js +15 -15
  45. package/src/adapters/shared/runtime/application-adapter.js +72 -87
  46. package/src/adapters/shared/runtime/copy-runtime-public-dir.js +25 -22
  47. package/src/adapters/shared/runtime/port-manager.js +207 -177
  48. package/src/adapters/shared/runtime/render-context.js +94 -79
  49. package/src/adapters/shared/runtime/runtime-app-bootstrap.js +33 -38
  50. package/src/adapters/shared/runtime/runtime-host.js +1 -0
  51. package/src/adapters/shared/runtime/runtime-server-lifecycle.d.ts +2 -3
  52. package/src/adapters/shared/runtime/runtime-server-lifecycle.js +65 -51
  53. package/src/adapters/shared/runtime/server-adapter.d.ts +1 -0
  54. package/src/adapters/shared/runtime/server-adapter.js +233 -217
  55. package/src/adapters/shared/runtime/server-route-handler.js +74 -70
  56. package/src/adapters/shared/runtime/server-static-builder.js +165 -184
  57. package/src/adapters/shared/runtime/static-preview-host.js +1 -0
  58. package/src/adapters/shared/ws/node-http-websocket-upgrades.js +97 -88
  59. package/src/adapters/shared/ws/websocket-lifecycle.js +57 -50
  60. package/src/build/README.md +52 -1
  61. package/src/build/app-build-manifest-runtime.js +129 -108
  62. package/src/build/browser/browser-runtime-manifest.js +47 -50
  63. package/src/build/browser/browser-runtime-plugin-helpers.js +32 -12
  64. package/src/build/browser/browser-runtime-plugin.js +206 -160
  65. package/src/build/browser/jsx-ownership-plugins.js +51 -37
  66. package/src/build/browser/lit-static-render-worker-context.js +3 -5
  67. package/src/build/build-adapter.js +276 -139
  68. package/src/build/cache/build-input-fingerprint.js +28 -26
  69. package/src/build/cache/cache-constants.js +4 -6
  70. package/src/build/cache/cache-keys.js +64 -56
  71. package/src/build/cache/pages-unified-graph-build.js +179 -178
  72. package/src/build/cache/production-build-cache.js +47 -54
  73. package/src/build/cache/server-entry-build-cache.js +134 -146
  74. package/src/build/contracts/build-contracts.d.ts +4 -4
  75. package/src/build/contracts/build-contracts.js +1 -0
  76. package/src/build/contracts/build-manifest.js +58 -43
  77. package/src/build/contracts/build-types.js +1 -0
  78. package/src/build/rolldown/rolldown-adapter-helpers.d.ts +9 -0
  79. package/src/build/rolldown/rolldown-adapter-helpers.js +405 -298
  80. package/src/build/rolldown/rolldown-build-adapter.js +53 -61
  81. package/src/build/rolldown/rolldown-build-invocation-metrics.js +31 -35
  82. package/src/build/rolldown/rolldown-plugin-bridge.js +264 -194
  83. package/src/build/rolldown/rolldown-source-transform-pass.js +59 -50
  84. package/src/build/rolldown/runtime-build-output-normalizer.js +227 -175
  85. package/src/build/rolldown/server-side-css-shim-plugin.js +54 -31
  86. package/src/build/runtime/build-profile-options.js +36 -36
  87. package/src/build/runtime/build-request-identity.js +70 -45
  88. package/src/build/runtime/build-request-policy.js +80 -42
  89. package/src/build/runtime/build-runtime.js +64 -54
  90. package/src/build/runtime/deduping-build-executor.js +38 -32
  91. package/src/build/runtime/parallel-build-executor.js +58 -50
  92. package/src/build/runtime/serialized-build-executor.js +78 -62
  93. package/src/cache/index.js +6 -6
  94. package/src/cache/module-parse-cache.js +99 -68
  95. package/src/client/navigation-scripts/classification.js +45 -37
  96. package/src/client/navigation-scripts/index.js +3 -23
  97. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +58 -62
  98. package/src/client/navigation-scripts/registry.js +15 -11
  99. package/src/client/navigation-scripts/rerun-queue.js +81 -64
  100. package/src/client/scroll.js +32 -24
  101. package/src/client/scroll.test.browser.js +27 -33
  102. package/src/client/view-transitions.js +68 -55
  103. package/src/config/config-builder.d.ts +8 -0
  104. package/src/config/config-builder.js +637 -642
  105. package/src/config/constants.js +44 -24
  106. package/src/declarations.d.ts +1 -0
  107. package/src/dev/README.md +24 -0
  108. package/src/dev/client-bridge-registry.js +7 -12
  109. package/src/dev/dev-client-ownership.js +7 -5
  110. package/src/dev/hmr-manager-registry.js +7 -12
  111. package/src/dev/host-runtime.js +25 -30
  112. package/src/dev/runtime-server-started-message.js +2 -5
  113. package/src/dev/sc-server.js +106 -103
  114. package/src/dev/transform-server/dev-transform-bundler.d.ts +24 -0
  115. package/src/dev/transform-server/dev-transform-bundler.js +79 -0
  116. package/src/dev/transform-server/dev-transform-externalize-plugin.d.ts +12 -0
  117. package/src/dev/transform-server/dev-transform-externalize-plugin.js +29 -0
  118. package/src/dev/transform-server/dev-transform-import-rewriter.d.ts +16 -0
  119. package/src/dev/transform-server/dev-transform-import-rewriter.js +110 -0
  120. package/src/dev/transform-server/dev-transform-module-kind.d.ts +13 -0
  121. package/src/dev/transform-server/dev-transform-module-kind.js +17 -0
  122. package/src/dev/transform-server/dev-transform-runtime-specifiers.d.ts +5 -0
  123. package/src/dev/transform-server/dev-transform-runtime-specifiers.js +16 -0
  124. package/src/dev/transform-server/dev-transform-server.d.ts +41 -0
  125. package/src/dev/transform-server/dev-transform-server.js +191 -0
  126. package/src/dev/transform-server/dev-transform-url.d.ts +13 -0
  127. package/src/dev/transform-server/dev-transform-url.js +58 -0
  128. package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +27 -0
  129. package/src/dev/transform-server/dev-transform-vendor-registry.js +145 -0
  130. package/src/dev/transform-server/index.d.ts +6 -0
  131. package/src/dev/transform-server/index.js +4 -0
  132. package/src/dev/transform-server/types.d.ts +15 -0
  133. package/src/dev/transform-server/types.js +1 -0
  134. package/src/dev-toolbar/README.md +51 -0
  135. package/src/dev-toolbar/define-dev-tool.d.ts +21 -0
  136. package/src/dev-toolbar/define-dev-tool.js +9 -0
  137. package/src/dev-toolbar/dev-toolbar-config.d.ts +25 -0
  138. package/src/dev-toolbar/dev-toolbar-config.js +19 -0
  139. package/src/dev-toolbar/dev-toolbar-host.d.ts +49 -0
  140. package/src/dev-toolbar/dev-toolbar-host.js +117 -0
  141. package/src/dev-toolbar/dev-toolbar-html-response.d.ts +11 -0
  142. package/src/dev-toolbar/dev-toolbar-html-response.js +14 -0
  143. package/src/dev-toolbar/dev-toolbar-manifest.d.ts +15 -0
  144. package/src/dev-toolbar/dev-toolbar-manifest.js +52 -0
  145. package/src/dev-toolbar/dev-toolbar-package.d.ts +31 -0
  146. package/src/dev-toolbar/dev-toolbar-package.js +72 -0
  147. package/src/dev-toolbar/dev-toolbar-runtime-paths.d.ts +10 -0
  148. package/src/dev-toolbar/dev-toolbar-runtime-paths.js +13 -0
  149. package/src/diagnostics/request-build-dedupe.js +34 -28
  150. package/src/diagnostics/startup-trace.d.ts +10 -1
  151. package/src/diagnostics/startup-trace.js +269 -241
  152. package/src/eco/README.md +6 -0
  153. package/src/eco/eco-declared-component.js +19 -14
  154. package/src/eco/eco.browser.js +56 -69
  155. package/src/eco/eco.js +156 -96
  156. package/src/eco/eco.types.js +5 -0
  157. package/src/eco/eco.utils.js +1 -10
  158. package/src/eco/global-injector-map.js +55 -41
  159. package/src/eco/lazy-injector-map.js +90 -60
  160. package/src/eco/module-dependencies.js +52 -44
  161. package/src/eco/page-layout-normalization.js +59 -49
  162. package/src/errors/http-error.js +50 -49
  163. package/src/errors/index.js +3 -12
  164. package/src/errors/locals-access-error.js +5 -8
  165. package/src/errors/undeclared-component-dependency-error.js +15 -15
  166. package/src/global/app-logger.js +3 -6
  167. package/src/hmr/README.md +10 -0
  168. package/src/hmr/client/hmr-runtime.js +117 -113
  169. package/src/hmr/client/module-update.d.ts +13 -0
  170. package/src/hmr/client/module-update.js +39 -0
  171. package/src/hmr/hmr-asset-paths.d.ts +14 -0
  172. package/src/hmr/hmr-asset-paths.js +22 -0
  173. package/src/hmr/hmr-entrypoint-output.d.ts +11 -39
  174. package/src/hmr/hmr-entrypoint-output.js +28 -54
  175. package/src/hmr/hmr-file-change-prep.js +13 -16
  176. package/src/hmr/hmr-registered-entrypoints-context.d.ts +7 -0
  177. package/src/hmr/hmr-registered-entrypoints-context.js +1 -0
  178. package/src/hmr/hmr-runtime-paths.d.ts +10 -0
  179. package/src/hmr/hmr-runtime-paths.js +13 -0
  180. package/src/hmr/hmr-strategy.d.ts +5 -11
  181. package/src/hmr/hmr-strategy.js +113 -61
  182. package/src/hmr/strategies/default-hmr-strategy.js +55 -33
  183. package/src/hmr/strategies/js-hmr-strategy.d.ts +6 -95
  184. package/src/hmr/strategies/js-hmr-strategy.js +104 -217
  185. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +33 -27
  186. package/src/index.browser.js +1 -4
  187. package/src/index.d.ts +1 -0
  188. package/src/index.js +6 -47
  189. package/src/integrations/ghtml/ghtml-renderer.js +12 -7
  190. package/src/integrations/ghtml/ghtml.constants.js +1 -4
  191. package/src/integrations/ghtml/ghtml.plugin.js +9 -12
  192. package/src/islands/README.md +25 -0
  193. package/src/islands/island-host.d.ts +37 -0
  194. package/src/islands/island-host.js +55 -0
  195. package/src/plugins/alias-resolver-cache.js +114 -97
  196. package/src/plugins/alias-resolver-plugin.js +33 -47
  197. package/src/plugins/define-integration.js +18 -17
  198. package/src/plugins/eco-component-meta-plugin.js +387 -145
  199. package/src/plugins/foreign-jsx-override-plugin.js +49 -35
  200. package/src/plugins/integration-plugin.js +212 -203
  201. package/src/plugins/jsx-import-source.utils.js +16 -21
  202. package/src/plugins/processor.js +159 -149
  203. package/src/plugins/runtime-capability.js +1 -0
  204. package/src/plugins/source-transform.js +109 -79
  205. package/src/plugins/tsconfig-import-resolver.d.ts +17 -0
  206. package/src/plugins/tsconfig-import-resolver.js +218 -142
  207. package/src/route-renderer/README.md +12 -1
  208. package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +143 -136
  209. package/src/route-renderer/orchestration/document-shell/layout-shell-props.service.js +33 -27
  210. package/src/route-renderer/orchestration/foreign-child/component-render-context.js +173 -126
  211. package/src/route-renderer/orchestration/foreign-child/foreign-child-output.utils.js +165 -154
  212. package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +329 -341
  213. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +33 -32
  214. package/src/route-renderer/orchestration/integration-renderer.d.ts +1 -0
  215. package/src/route-renderer/orchestration/integration-renderer.js +731 -750
  216. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +168 -172
  217. package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +119 -131
  218. package/src/route-renderer/orchestration/ownership-graph/component-graph.js +82 -91
  219. package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +82 -76
  220. package/src/route-renderer/orchestration/page-browser-graph/global-injector-assets.service.js +29 -31
  221. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +10 -6
  222. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +31 -28
  223. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +3 -0
  224. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +430 -413
  225. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +191 -212
  226. package/src/route-renderer/orchestration/page-browser-graph/processed-asset-dedupe.js +20 -24
  227. package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +4 -0
  228. package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +24 -24
  229. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +22 -24
  230. package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +41 -34
  231. package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.d.ts +5 -0
  232. package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +42 -22
  233. package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +50 -48
  234. package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.utils.js +29 -32
  235. package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +150 -156
  236. package/src/route-renderer/orchestration/string-markup-renderer.js +48 -51
  237. package/src/route-renderer/page-loading/component-dependency-collection.js +120 -124
  238. package/src/route-renderer/page-loading/declared-asset-collection.js +66 -94
  239. package/src/route-renderer/page-loading/dependency-resolver.js +101 -104
  240. package/src/route-renderer/page-loading/ecopages-virtual-imports.js +68 -54
  241. package/src/route-renderer/page-loading/lazy-entry-collection.js +75 -98
  242. package/src/route-renderer/page-loading/lazy-trigger-planning.js +46 -36
  243. package/src/route-renderer/page-loading/module-declaration-aggregation.js +38 -31
  244. package/src/route-renderer/page-loading/module-declaration-scripts.js +10 -15
  245. package/src/route-renderer/page-loading/page-dependency-bundling.js +156 -113
  246. package/src/route-renderer/page-loading/page-module-loader.js +130 -124
  247. package/src/route-renderer/route-renderer.js +74 -76
  248. package/src/router/README.md +23 -0
  249. package/src/router/client/link-intent.js +55 -39
  250. package/src/router/client/link-intent.test.browser.js +49 -49
  251. package/src/router/client/link-navigation-policy.js +114 -93
  252. package/src/router/client/link-navigation-policy.test.browser.js +70 -80
  253. package/src/router/client/navigation-coordinator.js +235 -211
  254. package/src/router/client/navigation-lifecycle.d.ts +55 -0
  255. package/src/router/client/navigation-lifecycle.js +50 -0
  256. package/src/router/client/navigation-lifecycle.test.browser.d.ts +1 -0
  257. package/src/router/client/navigation-lifecycle.test.browser.js +118 -0
  258. package/src/router/server/route-registry.js +237 -242
  259. package/src/services/assets/asset-processing-service/asset-dependency-keys.js +64 -53
  260. package/src/services/assets/asset-processing-service/asset-processing.service.js +287 -303
  261. package/src/services/assets/asset-processing-service/asset.factory.js +78 -81
  262. package/src/services/assets/asset-processing-service/assets.types.js +1 -0
  263. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +57 -46
  264. package/src/services/assets/asset-processing-service/browser-runtime-entry-resolution.js +106 -69
  265. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +53 -53
  266. package/src/services/assets/asset-processing-service/finalize-processed-asset.js +4 -6
  267. package/src/services/assets/asset-processing-service/grouped-content-bundles.js +79 -80
  268. package/src/services/assets/asset-processing-service/index.js +8 -8
  269. package/src/services/assets/asset-processing-service/inline-content-script-body.js +22 -16
  270. package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +21 -17
  271. package/src/services/assets/asset-processing-service/page-package.js +70 -72
  272. package/src/services/assets/asset-processing-service/processor.interface.js +7 -5
  273. package/src/services/assets/asset-processing-service/processor.registry.js +11 -14
  274. package/src/services/assets/asset-processing-service/processors/base/base-processor.js +59 -62
  275. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +102 -117
  276. package/src/services/assets/asset-processing-service/processors/index.js +5 -5
  277. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +110 -115
  278. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +95 -97
  279. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +136 -135
  280. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +27 -29
  281. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +34 -40
  282. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +36 -35
  283. package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +14 -16
  284. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +27 -39
  285. package/src/services/assets/browser-bundle.service.js +52 -47
  286. package/src/services/cache/cache.types.js +5 -0
  287. package/src/services/cache/index.js +6 -7
  288. package/src/services/cache/memory-cache-store.js +101 -89
  289. package/src/services/cache/page-cache-service.js +156 -145
  290. package/src/services/cache/page-request-cache-coordinator.service.js +102 -102
  291. package/src/services/html/html-rewriter-provider.service.js +61 -61
  292. package/src/services/html/html-transformer.service.js +249 -266
  293. package/src/services/invalidation/development-invalidation.service.js +252 -241
  294. package/src/services/module-loading/app-module-loader.service.js +1 -0
  295. package/src/services/module-loading/app-server-module-transpiler.service.js +107 -101
  296. package/src/services/module-loading/host-module-loader-registry.js +6 -11
  297. package/src/services/module-loading/module-loading-types.js +1 -0
  298. package/src/services/module-loading/page-module-import.service.js +235 -243
  299. package/src/services/module-loading/route-module-build-cache-registry.js +47 -33
  300. package/src/services/module-loading/route-module-build-cache.js +3 -38
  301. package/src/services/module-loading/route-module-build-cache.store.js +195 -200
  302. package/src/services/module-loading/route-module-build-manifest.js +61 -74
  303. package/src/services/module-loading/route-module-dependency-hasher.js +136 -110
  304. package/src/services/module-loading/server-module-transpiler.service.js +68 -62
  305. package/src/services/module-loading/source-module-support.js +15 -7
  306. package/src/services/runtime-state/dev-graph.service.js +165 -157
  307. package/src/services/runtime-state/entrypoint-dependency-graph.service.js +85 -76
  308. package/src/services/runtime-state/server-invalidation-state.service.js +38 -30
  309. package/src/services/validation/schema-validation-service.js +157 -95
  310. package/src/services/validation/standard-schema.types.js +1 -0
  311. package/src/services/validation/validate-standard-schema.js +13 -13
  312. package/src/static-site-generator/production-page-browser-graph-prebuild.js +35 -19
  313. package/src/static-site-generator/sitemap-routes.js +33 -28
  314. package/src/static-site-generator/sitemap.js +38 -29
  315. package/src/static-site-generator/static-build-invalidation.js +34 -56
  316. package/src/static-site-generator/static-export-context.js +1 -0
  317. package/src/static-site-generator/static-site-generator.js +517 -553
  318. package/src/types/internal-types.d.ts +19 -3
  319. package/src/types/internal-types.js +1 -0
  320. package/src/types/public-types.d.ts +16 -46
  321. package/src/types/public-types.js +1 -0
  322. package/src/utils/deep-merge.js +39 -26
  323. package/src/utils/ecopages-route-info.js +27 -23
  324. package/src/utils/hash.js +3 -6
  325. package/src/utils/html-escaping.js +21 -8
  326. package/src/utils/html.js +1 -4
  327. package/src/utils/invariant.js +11 -10
  328. package/src/utils/locals-utils.js +32 -22
  329. package/src/utils/parse-cli-args.js +73 -65
  330. package/src/utils/path-pattern.js +38 -23
  331. package/src/utils/path-utils.module.js +8 -11
  332. package/src/utils/resolve-entry-file.js +70 -38
  333. package/src/utils/resolve-work-dir.js +23 -27
  334. package/src/utils/runtime.js +24 -33
  335. package/src/utils/server-utils.module.js +60 -52
  336. package/src/utils/static-file-extensions.js +52 -46
  337. package/src/watchers/project-watcher-ignore.js +16 -15
  338. package/src/watchers/project-watcher.js +385 -366
  339. package/src/watchers/project-watcher.test-helpers.js +45 -50
  340. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.d.ts +0 -56
  341. package/src/adapters/shared/hmr/hmr-entrypoint-registrar.js +0 -106
  342. package/src/build/cache/dev-browser-script-cache.d.ts +0 -34
  343. package/src/build/cache/dev-browser-script-cache.js +0 -90
@@ -1,752 +1,733 @@
1
- import {
2
- createPagePackage
3
- } from "../../services/assets/asset-processing-service/index.js";
4
- import { HtmlTransformerService } from "../../services/html/html-transformer.service.js";
5
- import { invariant } from "../../utils/invariant.js";
6
- import { HttpError } from "../../errors/http-error.js";
7
- import { DependencyResolverService } from "../page-loading/dependency-resolver.js";
8
- import { PageModuleLoaderService } from "../page-loading/page-module-loader.js";
9
- import { OwnershipValidationService } from "./ownership-graph/ownership-validation.service.js";
10
- import { hasForeignChildDescendantsInGraph } from "./ownership-graph/component-graph-collectors.js";
11
- import {
12
- RouteRenderOrchestrator
13
- } from "./route-pipeline/route-render-orchestrator.js";
14
- import { createIntegrationRouteRenderAdapter } from "./route-pipeline/integration-route-render-adapter.js";
15
- import { loadPageBrowserGraphContribution } from "./page-browser-graph/page-browser-graph-contribution.loader.js";
16
- import { normalizeUnresolvedMarkerArtifactHtml } from "./route-pipeline/marker-artifact.utils.js";
17
- import { isMarkupNodeLike } from "./foreign-child/foreign-child-output.utils.js";
18
- import {
19
- getForeignSubtreeResolutionContextKey,
20
- getForeignSubtreeTokenPrefix,
21
- resolveOwningIntegrationRenderer
22
- } from "./foreign-child/owning-renderer-resolution.js";
23
- import { ensureIntegrationRuntimeReady } from "../../build/app-build-manifest-runtime.js";
24
- import { ForeignSubtreeExecutionService } from "./foreign-child/foreign-subtree-execution.service.js";
25
- import {
26
- composeDocumentShell,
27
- finalizeDocumentShellHtml,
28
- renderPageDocumentShell
29
- } from "./document-shell/document-shell-render.service.js";
30
- import {
31
- resolveInnermostPageLayout,
32
- resolvePageLayoutComponents
33
- } from "./document-shell/layout-shell-props.service.js";
34
- class IntegrationRenderer {
35
- appConfig;
36
- assetProcessingService;
37
- htmlTransformer;
38
- hmrManager;
39
- resolvedIntegrationDependencies = [];
40
- rendererModules;
41
- runtimeOrigin;
42
- dependencyResolverService;
43
- pageModuleLoaderService;
44
- routeRenderOrchestrator;
45
- foreignSubtreeExecutionService = new ForeignSubtreeExecutionService();
46
- /**
47
- * Serializes route and view renders that mutate `htmlTransformer` state.
48
- *
49
- * Integration renderers are cached per integration, so concurrent static builds
50
- * and overlapping SSR requests must not share one transformer page package.
51
- */
52
- renderExclusiveChain = Promise.resolve();
53
- DOC_TYPE = "<!DOCTYPE html>";
54
- runRenderExclusive(operation) {
55
- const run = this.renderExclusiveChain.then(async () => {
56
- await this.ensureIntegrationRuntimeActivated();
57
- return operation();
58
- });
59
- this.renderExclusiveChain = run.then(
60
- () => void 0,
61
- () => void 0
62
- );
63
- return run;
64
- }
65
- /**
66
- * Activates the owning integration runtime on first render or graph use.
67
- */
68
- async ensureIntegrationRuntimeActivated() {
69
- await ensureIntegrationRuntimeReady({
70
- appConfig: this.appConfig,
71
- integrationName: this.name,
72
- runtimeOrigin: this.runtimeOrigin
73
- });
74
- }
75
- /**
76
- * Prebuilds the production Page Browser Graph for one route file.
77
- */
78
- async prebuildProductionPageBrowserGraph(routeFile) {
79
- await this.ensureIntegrationRuntimeActivated();
80
- await this.resolvePageBrowserGraphForFile(routeFile);
81
- }
82
- /**
83
- * Loads one route module through the owning renderer's import path.
84
- *
85
- * Request-time infrastructure may need page metadata such as cache strategy or
86
- * middleware before full rendering starts. Exposing this narrow entrypoint lets
87
- * those callers reuse integration-specific import setup instead of bypassing it
88
- * with raw transpiler access.
89
- */
90
- async loadPageModule(file, options) {
91
- return this.importPageFile(file, options);
92
- }
93
- getRendererModuleValue(key) {
94
- if (!this.rendererModules || typeof this.rendererModules !== "object") {
95
- return void 0;
96
- }
97
- return this.rendererModules[key];
98
- }
99
- getRendererModuleString(key) {
100
- const value = this.getRendererModuleValue(key);
101
- return typeof value === "string" && value.length > 0 ? value : void 0;
102
- }
103
- getRendererBootstrapDependencies(partial = false) {
104
- if (partial) {
105
- return [];
106
- }
107
- const islandClientModuleId = this.getRendererModuleString("islandClientModuleId");
108
- if (!islandClientModuleId) {
109
- return [];
110
- }
111
- return [
112
- {
113
- attributes: {
114
- crossorigin: "anonymous",
115
- "data-ecopages-runtime": "islands",
116
- type: "module"
117
- },
118
- content: `import ${JSON.stringify(islandClientModuleId)};`,
119
- inline: true,
120
- kind: "script",
121
- packageRole: "keep-separate",
122
- position: "body"
123
- }
124
- ];
125
- }
126
- setHmrManager(hmrManager) {
127
- this.hmrManager = hmrManager;
128
- if (this.assetProcessingService) {
129
- this.assetProcessingService.setHmrManager(hmrManager);
130
- }
131
- }
132
- /**
133
- * Build response headers with optional custom headers.
134
- * @param contentType - The Content-Type header value
135
- * @param customHeaders - Optional custom headers to merge
136
- * @returns Headers object
137
- */
138
- buildHeaders(contentType, customHeaders) {
139
- const headers = new Headers({ "Content-Type": contentType });
140
- if (customHeaders) {
141
- const incoming = new Headers(customHeaders);
142
- incoming.forEach((value, key) => headers.set(key, value));
143
- }
144
- return headers;
145
- }
146
- /**
147
- * Create an HTML Response.
148
- * @param body - Response body (string or ReadableStream)
149
- * @param ctx - Render context with status and headers
150
- * @returns Response object
151
- */
152
- createHtmlResponse(body, ctx) {
153
- return new Response(body, {
154
- status: ctx.status ?? 200,
155
- headers: this.buildHeaders("text/html; charset=utf-8", ctx.headers)
156
- });
157
- }
158
- /**
159
- * Create an HttpError for render failures.
160
- * @param message - Error message
161
- * @param cause - Original error if available
162
- * @returns HttpError with 500 status
163
- */
164
- createRenderError(message, cause) {
165
- const errorMessage = cause instanceof Error ? `${message}: ${cause.message}` : message;
166
- return HttpError.InternalServerError(errorMessage);
167
- }
168
- /**
169
- * Prepares dependencies for renderToResponse by resolving component dependencies
170
- * and configuring the HTML transformer.
171
- * @param view - The view component being rendered
172
- * @param layout - Optional layout component
173
- * @returns Resolved processed assets
174
- */
175
- async prepareViewDependencies(view, layout) {
176
- const HtmlTemplate = await this.getHtmlTemplate();
177
- const componentsToResolve = layout ? [HtmlTemplate, layout, view] : [HtmlTemplate, view];
178
- const resolvedDependencies = this.htmlTransformer.dedupeProcessedAssets(
179
- await this.resolveDependencies(componentsToResolve)
180
- );
181
- this.htmlTransformer.setPagePackage(createPagePackage(resolvedDependencies));
182
- return resolvedDependencies;
183
- }
184
- async resolvePageBrowserGraphForFile(filePath) {
185
- return await this.routeRenderOrchestrator.resolveDeclaredPageBrowserGraph({
186
- routeFile: filePath,
187
- integrationName: this.name,
188
- collectContribution: (routeFile) => loadPageBrowserGraphContribution(
189
- routeFile,
190
- (targetFile) => this.importPageFile(targetFile),
191
- (context) => this.collectPageBrowserGraphContribution(context)
192
- )
193
- });
194
- }
195
- /**
196
- * Merges component-scoped assets into the active HTML transformer state.
197
- *
198
- * Explicit page, layout, and document shell composition can produce assets at
199
- * each foreign subtree. This helper deduplicates those groups and folds them back into
200
- * the transformer so downstream HTML finalization sees one canonical asset set.
201
- *
202
- * @param assetGroups - Optional groups of processed assets to merge.
203
- * @returns The deduplicated asset subset contributed by this merge operation.
204
- */
205
- appendProcessedDependencies(...assetGroups) {
206
- const nextDependencies = this.htmlTransformer.dedupeProcessedAssets(
207
- assetGroups.flatMap((assets) => assets ?? [])
208
- );
209
- if (nextDependencies.length === 0) {
210
- return nextDependencies;
211
- }
212
- const mergedDependencies = this.htmlTransformer.dedupeProcessedAssets([
213
- ...this.htmlTransformer.getProcessedDependencies(),
214
- ...nextDependencies
215
- ]);
216
- const currentPageBrowserGraph = this.htmlTransformer.getPagePackage()?.pageBrowserGraph;
217
- this.htmlTransformer.setPagePackage(
218
- createPagePackage(mergedDependencies, {
219
- pageBrowserGraph: currentPageBrowserGraph
220
- })
221
- );
222
- return nextDependencies;
223
- }
224
- /**
225
- * Resolves metadata for explicit view rendering.
226
- *
227
- * When a view declares a `metadata()` function, that contract owns the final
228
- * metadata for the explicit render. Otherwise the app-level default metadata is
229
- * reused so explicit routes and page-module routes share the same fallback.
230
- *
231
- * @param view - View component being rendered.
232
- * @param props - Props passed to the view.
233
- * @returns Resolved metadata for the final document shell.
234
- */
235
- async resolveViewMetadata(view, props) {
236
- return view.metadata ? await view.metadata({
237
- params: {},
238
- query: {},
239
- props,
240
- appConfig: this.appConfig
241
- }) : this.appConfig.defaultMetadata;
242
- }
243
- /**
244
- * Renders one explicit view response in partial mode.
245
- *
246
- * Same-integration views can optionally stream or render inline via the caller's
247
- * `renderInline()` hook. Once a view may cross integration boundaries, this
248
- * helper routes the render through `renderComponentWithForeignChildren()` instead so mixed
249
- * shells can reuse the execution-scoped renderer cache and resolve nested
250
- * foreign ownership before the partial response is returned.
251
- *
252
- * @param input - View render options for the partial response.
253
- * @returns HTML response for the partial render.
254
- */
255
- async renderPartialViewResponse(input) {
256
- if (input.renderInline && !this.hasForeignChildDescendants(input.view)) {
257
- return this.createHtmlResponse(await input.renderInline(), input.ctx);
258
- }
259
- const rendererCache = /* @__PURE__ */ new Map();
260
- const viewRender = await this.renderComponentWithForeignChildren({
261
- component: input.view,
262
- props: input.props ?? {},
263
- integrationContext: { rendererCache }
264
- });
265
- const html = input.transformHtml ? input.transformHtml(viewRender.html) : viewRender.html;
266
- return this.createHtmlResponse(html, input.ctx);
267
- }
268
- /**
269
- * Renders an explicit view through optional layout and document shells.
270
- *
271
- * This helper is the shared explicit-route path for string-oriented and mixed
272
- * integrations. It prepares view dependencies, resolves metadata, and composes
273
- * view, layout, and html template boundaries with one execution-scoped renderer
274
- * cache so repeated foreign shell delegation can reuse initialized renderers
275
- * during the same render flow.
276
- *
277
- * @param input - View, props, and optional layout metadata for the render.
278
- * @returns HTML response for the explicit view render.
279
- */
280
- async renderViewWithDocumentShell(input) {
281
- await this.ensureIntegrationRuntimeActivated();
282
- const normalizedProps = input.props ?? {};
283
- if (input.ctx.partial) {
284
- return this.renderPartialViewResponse({
285
- ...input,
286
- transformHtml: input.transformDocumentHtml
287
- });
288
- }
289
- await this.prepareViewDependencies(input.view, input.layout);
290
- const HtmlTemplate = await this.getHtmlTemplate();
291
- const metadata = await this.resolveViewMetadata(input.view, input.props);
292
- const { documentHtml } = await composeDocumentShell(
293
- {
294
- renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
295
- appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups)
296
- },
297
- {
298
- primaryComponent: input.view,
299
- primaryProps: normalizedProps,
300
- layout: input.layout ? {
301
- component: input.layout,
302
- props: {}
303
- } : void 0,
304
- htmlTemplate: HtmlTemplate,
305
- documentProps: {
306
- metadata,
307
- pageProps: normalizedProps
1
+ /**
2
+ * This module contains the abstract class for the Integration Renderer
3
+ * Every integration renderer should extend this class
4
+ * @module
5
+ */
6
+ import { createPagePackage, } from '../../services/assets/asset-processing-service/index.js';
7
+ import { HtmlTransformerService } from '../../services/html/html-transformer.service.js';
8
+ import { invariant } from '../../utils/invariant.js';
9
+ import { HttpError } from '../../errors/http-error.js';
10
+ import { DependencyResolverService } from '../page-loading/dependency-resolver.js';
11
+ import { PageModuleLoaderService } from '../page-loading/page-module-loader.js';
12
+ import { OwnershipValidationService } from './ownership-graph/ownership-validation.service.js';
13
+ import { hasForeignChildDescendantsInGraph } from './ownership-graph/component-graph-collectors.js';
14
+ import { RouteRenderOrchestrator, } from './route-pipeline/route-render-orchestrator.js';
15
+ import { createIntegrationRouteRenderAdapter } from './route-pipeline/integration-route-render-adapter.js';
16
+ import { loadPageBrowserGraphContribution } from './page-browser-graph/page-browser-graph-contribution.loader.js';
17
+ import { normalizeUnresolvedMarkerArtifactHtml } from './route-pipeline/marker-artifact.utils.js';
18
+ import { isMarkupNodeLike } from './foreign-child/foreign-child-output.utils.js';
19
+ import { getForeignSubtreeResolutionContextKey, getForeignSubtreeTokenPrefix, resolveOwningIntegrationRenderer, } from './foreign-child/owning-renderer-resolution.js';
20
+ import { ensureIntegrationRuntimeReady } from '../../build/app-build-manifest-runtime.js';
21
+ import { ForeignSubtreeExecutionService } from './foreign-child/foreign-subtree-execution.service.js';
22
+ import { composeDocumentShell, finalizeDocumentShellHtml, renderPageDocumentShell, } from './document-shell/document-shell-render.service.js';
23
+ import { resolveInnermostPageLayout, resolvePageLayoutComponents, } from './document-shell/layout-shell-props.service.js';
24
+ import { finalizeIslandComponentRender } from '../../islands/island-host.js';
25
+ /**
26
+ * The IntegrationRenderer class is an abstract class that provides a base for rendering integration-specific components in the EcoPages framework.
27
+ * It handles the import of page files, collection of dependencies, and preparation of render options.
28
+ * The class is designed to be extended by specific integration renderers.
29
+ */
30
+ export class IntegrationRenderer {
31
+ appConfig;
32
+ assetProcessingService;
33
+ htmlTransformer;
34
+ hmrManager;
35
+ resolvedIntegrationDependencies = [];
36
+ rendererModules;
37
+ runtimeOrigin;
38
+ dependencyResolverService;
39
+ pageModuleLoaderService;
40
+ routeRenderOrchestrator;
41
+ foreignSubtreeExecutionService = new ForeignSubtreeExecutionService();
42
+ /**
43
+ * Serializes route and view renders that mutate `htmlTransformer` state.
44
+ *
45
+ * Integration renderers are cached per integration, so concurrent static builds
46
+ * and overlapping SSR requests must not share one transformer page package.
47
+ */
48
+ renderExclusiveChain = Promise.resolve();
49
+ DOC_TYPE = '<!DOCTYPE html>';
50
+ runRenderExclusive(operation) {
51
+ const run = this.renderExclusiveChain.then(async () => {
52
+ await this.ensureIntegrationRuntimeActivated();
53
+ return operation();
54
+ });
55
+ this.renderExclusiveChain = run.then(() => undefined, () => undefined);
56
+ return run;
57
+ }
58
+ /**
59
+ * Activates the owning integration runtime on first render or graph use.
60
+ */
61
+ async ensureIntegrationRuntimeActivated() {
62
+ await ensureIntegrationRuntimeReady({
63
+ appConfig: this.appConfig,
64
+ integrationName: this.name,
65
+ runtimeOrigin: this.runtimeOrigin,
66
+ });
67
+ }
68
+ /**
69
+ * Prebuilds the production Page Browser Graph for one route file.
70
+ */
71
+ async prebuildProductionPageBrowserGraph(routeFile) {
72
+ await this.ensureIntegrationRuntimeActivated();
73
+ await this.resolvePageBrowserGraphForFile(routeFile);
74
+ }
75
+ /**
76
+ * Loads one route module through the owning renderer's import path.
77
+ *
78
+ * Request-time infrastructure may need page metadata such as cache strategy or
79
+ * middleware before full rendering starts. Exposing this narrow entrypoint lets
80
+ * those callers reuse integration-specific import setup instead of bypassing it
81
+ * with raw transpiler access.
82
+ */
83
+ async loadPageModule(file, options) {
84
+ return this.importPageFile(file, options);
85
+ }
86
+ getRendererModuleValue(key) {
87
+ if (!this.rendererModules || typeof this.rendererModules !== 'object') {
88
+ return undefined;
89
+ }
90
+ return this.rendererModules[key];
91
+ }
92
+ getRendererModuleString(key) {
93
+ const value = this.getRendererModuleValue(key);
94
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
95
+ }
96
+ getRendererBootstrapDependencies(partial = false) {
97
+ if (partial) {
98
+ return [];
99
+ }
100
+ const islandClientModuleId = this.getRendererModuleString('islandClientModuleId');
101
+ if (!islandClientModuleId) {
102
+ return [];
103
+ }
104
+ return [
105
+ {
106
+ attributes: {
107
+ crossorigin: 'anonymous',
108
+ 'data-ecopages-runtime': 'islands',
109
+ type: 'module',
110
+ },
111
+ content: `import ${JSON.stringify(islandClientModuleId)};`,
112
+ inline: true,
113
+ kind: 'script',
114
+ packageRole: 'keep-separate',
115
+ position: 'body',
116
+ },
117
+ ];
118
+ }
119
+ setHmrManager(hmrManager) {
120
+ this.hmrManager = hmrManager;
121
+ if (this.assetProcessingService) {
122
+ this.assetProcessingService.setHmrManager(hmrManager);
123
+ }
124
+ }
125
+ /**
126
+ * Build response headers with optional custom headers.
127
+ * @param contentType - The Content-Type header value
128
+ * @param customHeaders - Optional custom headers to merge
129
+ * @returns Headers object
130
+ */
131
+ buildHeaders(contentType, customHeaders) {
132
+ const headers = new Headers({ 'Content-Type': contentType });
133
+ if (customHeaders) {
134
+ const incoming = new Headers(customHeaders);
135
+ incoming.forEach((value, key) => headers.set(key, value));
136
+ }
137
+ return headers;
138
+ }
139
+ /**
140
+ * Create an HTML Response.
141
+ * @param body - Response body (string or ReadableStream)
142
+ * @param ctx - Render context with status and headers
143
+ * @returns Response object
144
+ */
145
+ createHtmlResponse(body, ctx) {
146
+ return new Response(body, {
147
+ status: ctx.status ?? 200,
148
+ headers: this.buildHeaders('text/html; charset=utf-8', ctx.headers),
149
+ });
150
+ }
151
+ /**
152
+ * Create an HttpError for render failures.
153
+ * @param message - Error message
154
+ * @param cause - Original error if available
155
+ * @returns HttpError with 500 status
156
+ */
157
+ createRenderError(message, cause) {
158
+ const errorMessage = cause instanceof Error ? `${message}: ${cause.message}` : message;
159
+ return HttpError.InternalServerError(errorMessage);
160
+ }
161
+ /**
162
+ * Prepares dependencies for renderToResponse by resolving component dependencies
163
+ * and configuring the HTML transformer.
164
+ * @param view - The view component being rendered
165
+ * @param layout - Optional layout component
166
+ * @returns Resolved processed assets
167
+ */
168
+ async prepareViewDependencies(view, layout) {
169
+ const HtmlTemplate = await this.getHtmlTemplate();
170
+ const componentsToResolve = layout ? [HtmlTemplate, layout, view] : [HtmlTemplate, view];
171
+ const resolvedDependencies = this.htmlTransformer.dedupeProcessedAssets(await this.resolveDependencies(componentsToResolve));
172
+ this.htmlTransformer.setPagePackage(createPagePackage(resolvedDependencies));
173
+ return resolvedDependencies;
174
+ }
175
+ async resolvePageBrowserGraphForFile(filePath) {
176
+ return await this.routeRenderOrchestrator.resolveDeclaredPageBrowserGraph({
177
+ routeFile: filePath,
178
+ integrationName: this.name,
179
+ collectContribution: (routeFile) => loadPageBrowserGraphContribution(routeFile, (targetFile) => this.importPageFile(targetFile), (context) => this.collectPageBrowserGraphContribution(context)),
180
+ });
181
+ }
182
+ /**
183
+ * Merges component-scoped assets into the active HTML transformer state.
184
+ *
185
+ * Explicit page, layout, and document shell composition can produce assets at
186
+ * each foreign subtree. This helper deduplicates those groups and folds them back into
187
+ * the transformer so downstream HTML finalization sees one canonical asset set.
188
+ *
189
+ * @param assetGroups - Optional groups of processed assets to merge.
190
+ * @returns The deduplicated asset subset contributed by this merge operation.
191
+ */
192
+ appendProcessedDependencies(...assetGroups) {
193
+ const nextDependencies = this.htmlTransformer.dedupeProcessedAssets(assetGroups.flatMap((assets) => assets ?? []));
194
+ if (nextDependencies.length === 0) {
195
+ return nextDependencies;
196
+ }
197
+ const mergedDependencies = this.htmlTransformer.dedupeProcessedAssets([
198
+ ...this.htmlTransformer.getProcessedDependencies(),
199
+ ...nextDependencies,
200
+ ]);
201
+ const currentPageBrowserGraph = this.htmlTransformer.getPagePackage()?.pageBrowserGraph;
202
+ this.htmlTransformer.setPagePackage(createPagePackage(mergedDependencies, {
203
+ pageBrowserGraph: currentPageBrowserGraph,
204
+ }));
205
+ return nextDependencies;
206
+ }
207
+ /**
208
+ * Resolves metadata for explicit view rendering.
209
+ *
210
+ * When a view declares a `metadata()` function, that contract owns the final
211
+ * metadata for the explicit render. Otherwise the app-level default metadata is
212
+ * reused so explicit routes and page-module routes share the same fallback.
213
+ *
214
+ * @param view - View component being rendered.
215
+ * @param props - Props passed to the view.
216
+ * @returns Resolved metadata for the final document shell.
217
+ */
218
+ async resolveViewMetadata(view, props) {
219
+ return view.metadata
220
+ ? await view.metadata({
221
+ params: {},
222
+ query: {},
223
+ props,
224
+ appConfig: this.appConfig,
225
+ })
226
+ : this.appConfig.defaultMetadata;
227
+ }
228
+ /**
229
+ * Renders one explicit view response in partial mode.
230
+ *
231
+ * Same-integration views can optionally stream or render inline via the caller's
232
+ * `renderInline()` hook. Once a view may cross integration boundaries, this
233
+ * helper routes the render through `renderComponentWithForeignChildren()` instead so mixed
234
+ * shells can reuse the execution-scoped renderer cache and resolve nested
235
+ * foreign ownership before the partial response is returned.
236
+ *
237
+ * @param input - View render options for the partial response.
238
+ * @returns HTML response for the partial render.
239
+ */
240
+ async renderPartialViewResponse(input) {
241
+ if (input.renderInline && !this.hasForeignChildDescendants(input.view)) {
242
+ return this.createHtmlResponse(await input.renderInline(), input.ctx);
243
+ }
244
+ const rendererCache = new Map();
245
+ const viewRender = await this.renderComponentWithForeignChildren({
246
+ component: input.view,
247
+ props: (input.props ?? {}),
248
+ integrationContext: { rendererCache },
249
+ });
250
+ const html = input.transformHtml ? input.transformHtml(viewRender.html) : viewRender.html;
251
+ return this.createHtmlResponse(html, input.ctx);
252
+ }
253
+ /**
254
+ * Renders an explicit view through optional layout and document shells.
255
+ *
256
+ * This helper is the shared explicit-route path for string-oriented and mixed
257
+ * integrations. It prepares view dependencies, resolves metadata, and composes
258
+ * view, layout, and html template boundaries with one execution-scoped renderer
259
+ * cache so repeated foreign shell delegation can reuse initialized renderers
260
+ * during the same render flow.
261
+ *
262
+ * @param input - View, props, and optional layout metadata for the render.
263
+ * @returns HTML response for the explicit view render.
264
+ */
265
+ async renderViewWithDocumentShell(input) {
266
+ await this.ensureIntegrationRuntimeActivated();
267
+ const normalizedProps = (input.props ?? {});
268
+ if (input.ctx.partial) {
269
+ return this.renderPartialViewResponse({
270
+ ...input,
271
+ transformHtml: input.transformDocumentHtml,
272
+ });
273
+ }
274
+ await this.prepareViewDependencies(input.view, input.layout);
275
+ const HtmlTemplate = await this.getHtmlTemplate();
276
+ const metadata = await this.resolveViewMetadata(input.view, input.props);
277
+ const { documentHtml } = await composeDocumentShell({
278
+ renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
279
+ appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups),
280
+ }, {
281
+ primaryComponent: input.view,
282
+ primaryProps: normalizedProps,
283
+ layout: input.layout
284
+ ? {
285
+ component: input.layout,
286
+ props: {},
287
+ }
288
+ : undefined,
289
+ htmlTemplate: HtmlTemplate,
290
+ documentProps: {
291
+ metadata,
292
+ pageProps: normalizedProps,
293
+ },
294
+ });
295
+ const transformedDocumentHtml = input.transformDocumentHtml
296
+ ? input.transformDocumentHtml(documentHtml)
297
+ : documentHtml;
298
+ this.appendProcessedDependencies(this.getRendererBootstrapDependencies(false));
299
+ const html = await finalizeDocumentShellHtml(this.htmlTransformer, {
300
+ html: `${this.DOC_TYPE}${transformedDocumentHtml}`,
301
+ partial: false,
302
+ htmlContributions: this.getHtmlDocumentContributions({ partial: false }),
303
+ });
304
+ return this.createHtmlResponse(html, input.ctx);
305
+ }
306
+ /**
307
+ * Renders a route page through optional layout and document shells.
308
+ *
309
+ * Route rendering and explicit view rendering now share the same renderer-owned
310
+ * shell composition model. This helper composes page, layout, and html template
311
+ * renders while threading one execution-scoped renderer cache through every
312
+ * delegated foreign subtree so foreign shell ownership remains stable and renderer
313
+ * initialization is reused inside the current request.
314
+ *
315
+ * @param input - Page, layout, document, and metadata inputs for the route render.
316
+ * @returns Final serialized document HTML including the doctype prefix.
317
+ */
318
+ async renderPageWithDocumentShell(input) {
319
+ return renderPageDocumentShell({
320
+ renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
321
+ appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups),
322
+ }, input, this.DOC_TYPE);
323
+ }
324
+ async renderStringComponentWithSerializedChildren(input, component) {
325
+ const serializedChildren = input.children === undefined
326
+ ? undefined
327
+ : typeof input.children === 'string'
328
+ ? input.children
329
+ : isMarkupNodeLike(input.children) && typeof input.children.outerHTML === 'string'
330
+ ? input.children.outerHTML
331
+ : undefined;
332
+ if (input.children !== undefined && serializedChildren === undefined) {
333
+ const componentFile = input.component.config?.__eco?.file ?? 'unknown component';
334
+ const childTag = Object.prototype.toString.call(input.children);
335
+ throw new TypeError(`[ecopages] ${this.name} renderer expected serialized children for ${componentFile}, received ${childTag}.`);
336
+ }
337
+ const props = serializedChildren === undefined ? input.props : { ...input.props, children: serializedChildren };
338
+ const content = await component(props);
339
+ if (typeof content !== 'string') {
340
+ const componentFile = input.component.config?.__eco?.file ?? 'unknown component';
341
+ const contentTag = Object.prototype.toString.call(content);
342
+ throw new TypeError(`[ecopages] ${this.name} renderer expected a string render result for ${componentFile}, received ${contentTag}.`);
343
+ }
344
+ const html = content;
345
+ const assets = input.component.config?.dependencies &&
346
+ typeof this.assetProcessingService?.processDependencies === 'function'
347
+ ? await this.processComponentDependencies([input.component])
348
+ : undefined;
349
+ return {
350
+ html,
351
+ canAttachAttributes: true,
352
+ rootTag: this.getRootTagName(html),
353
+ integrationName: this.name,
354
+ assets,
355
+ };
356
+ }
357
+ /**
358
+ * Renders a string-first component, then resolves any queued foreign
359
+ * boundaries before returning final component HTML.
360
+ */
361
+ async renderStringComponentWithQueuedForeignSubtrees(input, component) {
362
+ const componentRender = await this.renderStringComponentWithSerializedChildren(input, component);
363
+ const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveStringQueuedHtml({
364
+ currentIntegrationName: this.name,
365
+ renderInput: input,
366
+ html: componentRender.html,
367
+ runtimeContextKey: getForeignSubtreeResolutionContextKey(this.name),
368
+ queueLabel: 'String',
369
+ getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
370
+ appConfig: this.appConfig,
371
+ runtimeOrigin: this.runtimeOrigin,
372
+ currentIntegrationName: this.name,
373
+ currentRenderer: this,
374
+ integrationName,
375
+ cache: rendererCache,
376
+ }),
377
+ applyAttributesToFirstElement: (html, attributes) => this.htmlTransformer.applyAttributesToFirstElement(html, attributes),
378
+ dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
379
+ });
380
+ const mergedAssets = this.htmlTransformer.dedupeProcessedAssets([
381
+ ...(componentRender.assets ?? []),
382
+ ...queuedForeignSubtreeResolution.assets,
383
+ ]);
384
+ return this.finalizeIslandComponentRender(input, {
385
+ ...componentRender,
386
+ html: queuedForeignSubtreeResolution.html,
387
+ rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
388
+ assets: mergedAssets.length > 0 ? mergedAssets : undefined,
389
+ });
390
+ }
391
+ constructor({ appConfig, assetProcessingService, resolvedIntegrationDependencies, rendererModules, runtimeOrigin, }) {
392
+ this.appConfig = appConfig;
393
+ this.assetProcessingService = assetProcessingService;
394
+ this.htmlTransformer = new HtmlTransformerService();
395
+ this.resolvedIntegrationDependencies = resolvedIntegrationDependencies || [];
396
+ this.rendererModules = rendererModules ?? appConfig.runtime?.rendererModuleContext;
397
+ this.runtimeOrigin = runtimeOrigin;
398
+ this.dependencyResolverService = new DependencyResolverService(appConfig, assetProcessingService);
399
+ this.pageModuleLoaderService = new PageModuleLoaderService(appConfig, runtimeOrigin);
400
+ this.routeRenderOrchestrator = new RouteRenderOrchestrator(appConfig, assetProcessingService, {
401
+ ownershipValidationService: new OwnershipValidationService(appConfig),
402
+ });
403
+ }
404
+ /**
405
+ * Returns the HTML template component.
406
+ * It imports the HTML template from the specified path in the app configuration.
407
+ *
408
+ * @returns The HTML template component.
409
+ */
410
+ async getHtmlTemplate() {
411
+ const htmlTemplatePath = this.getRendererModuleString('htmlTemplateModulePath') ?? this.appConfig.absolutePaths.htmlTemplatePath;
412
+ try {
413
+ const { default: HtmlTemplate } = await this.importPageFile(htmlTemplatePath);
414
+ return HtmlTemplate;
415
+ }
416
+ catch (error) {
417
+ invariant(false, `Error importing HtmlTemplate: ${error}`);
308
418
  }
309
- }
310
- );
311
- const transformedDocumentHtml = input.transformDocumentHtml ? input.transformDocumentHtml(documentHtml) : documentHtml;
312
- this.appendProcessedDependencies(this.getRendererBootstrapDependencies(false));
313
- const html = await finalizeDocumentShellHtml(this.htmlTransformer, {
314
- html: `${this.DOC_TYPE}${transformedDocumentHtml}`,
315
- partial: false,
316
- htmlContributions: this.getHtmlDocumentContributions({ partial: false })
317
- });
318
- return this.createHtmlResponse(html, input.ctx);
319
- }
320
- /**
321
- * Renders a route page through optional layout and document shells.
322
- *
323
- * Route rendering and explicit view rendering now share the same renderer-owned
324
- * shell composition model. This helper composes page, layout, and html template
325
- * renders while threading one execution-scoped renderer cache through every
326
- * delegated foreign subtree so foreign shell ownership remains stable and renderer
327
- * initialization is reused inside the current request.
328
- *
329
- * @param input - Page, layout, document, and metadata inputs for the route render.
330
- * @returns Final serialized document HTML including the doctype prefix.
331
- */
332
- async renderPageWithDocumentShell(input) {
333
- return renderPageDocumentShell(
334
- {
335
- renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
336
- appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups)
337
- },
338
- input,
339
- this.DOC_TYPE
340
- );
341
- }
342
- async renderStringComponentWithSerializedChildren(input, component) {
343
- const serializedChildren = input.children === void 0 ? void 0 : typeof input.children === "string" ? input.children : isMarkupNodeLike(input.children) && typeof input.children.outerHTML === "string" ? input.children.outerHTML : void 0;
344
- if (input.children !== void 0 && serializedChildren === void 0) {
345
- const componentFile = input.component.config?.__eco?.file ?? "unknown component";
346
- const childTag = Object.prototype.toString.call(input.children);
347
- throw new TypeError(
348
- `[ecopages] ${this.name} renderer expected serialized children for ${componentFile}, received ${childTag}.`
349
- );
350
- }
351
- const props = serializedChildren === void 0 ? input.props : { ...input.props, children: serializedChildren };
352
- const content = await component(props);
353
- if (typeof content !== "string") {
354
- const componentFile = input.component.config?.__eco?.file ?? "unknown component";
355
- const contentTag = Object.prototype.toString.call(content);
356
- throw new TypeError(
357
- `[ecopages] ${this.name} renderer expected a string render result for ${componentFile}, received ${contentTag}.`
358
- );
359
- }
360
- const html = content;
361
- const assets = input.component.config?.dependencies && typeof this.assetProcessingService?.processDependencies === "function" ? await this.processComponentDependencies([input.component]) : void 0;
362
- return {
363
- html,
364
- canAttachAttributes: true,
365
- rootTag: this.getRootTagName(html),
366
- integrationName: this.name,
367
- assets
368
- };
369
- }
370
- /**
371
- * Renders a string-first component, then resolves any queued foreign
372
- * boundaries before returning final component HTML.
373
- */
374
- async renderStringComponentWithQueuedForeignSubtrees(input, component) {
375
- const componentRender = await this.renderStringComponentWithSerializedChildren(input, component);
376
- const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveStringQueuedHtml({
377
- currentIntegrationName: this.name,
378
- renderInput: input,
379
- html: componentRender.html,
380
- runtimeContextKey: getForeignSubtreeResolutionContextKey(this.name),
381
- queueLabel: "String",
382
- getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
383
- appConfig: this.appConfig,
384
- runtimeOrigin: this.runtimeOrigin,
385
- currentIntegrationName: this.name,
386
- currentRenderer: this,
387
- integrationName,
388
- cache: rendererCache
389
- }),
390
- applyAttributesToFirstElement: (html, attributes) => this.htmlTransformer.applyAttributesToFirstElement(html, attributes),
391
- dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets)
392
- });
393
- const mergedAssets = this.htmlTransformer.dedupeProcessedAssets([
394
- ...componentRender.assets ?? [],
395
- ...queuedForeignSubtreeResolution.assets
396
- ]);
397
- return {
398
- ...componentRender,
399
- html: queuedForeignSubtreeResolution.html,
400
- rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
401
- assets: mergedAssets.length > 0 ? mergedAssets : void 0
402
- };
403
- }
404
- constructor({
405
- appConfig,
406
- assetProcessingService,
407
- resolvedIntegrationDependencies,
408
- rendererModules,
409
- runtimeOrigin
410
- }) {
411
- this.appConfig = appConfig;
412
- this.assetProcessingService = assetProcessingService;
413
- this.htmlTransformer = new HtmlTransformerService();
414
- this.resolvedIntegrationDependencies = resolvedIntegrationDependencies || [];
415
- this.rendererModules = rendererModules ?? appConfig.runtime?.rendererModuleContext;
416
- this.runtimeOrigin = runtimeOrigin;
417
- this.dependencyResolverService = new DependencyResolverService(appConfig, assetProcessingService);
418
- this.pageModuleLoaderService = new PageModuleLoaderService(appConfig, runtimeOrigin);
419
- this.routeRenderOrchestrator = new RouteRenderOrchestrator(appConfig, assetProcessingService, {
420
- ownershipValidationService: new OwnershipValidationService(appConfig)
421
- });
422
- }
423
- /**
424
- * Returns the HTML template component.
425
- * It imports the HTML template from the specified path in the app configuration.
426
- *
427
- * @returns The HTML template component.
428
- */
429
- async getHtmlTemplate() {
430
- const htmlTemplatePath = this.getRendererModuleString("htmlTemplateModulePath") ?? this.appConfig.absolutePaths.htmlTemplatePath;
431
- try {
432
- const { default: HtmlTemplate } = await this.importPageFile(htmlTemplatePath);
433
- return HtmlTemplate;
434
- } catch (error) {
435
- invariant(false, `Error importing HtmlTemplate: ${error}`);
436
- }
437
- }
438
- usesIntegrationPageImporter(_file) {
439
- return false;
440
- }
441
- async importIntegrationPageFile(_file, _options) {
442
- invariant(false, "Integration page importer must be implemented when enabled");
443
- }
444
- normalizeImportedPageFile(_file, pageModule) {
445
- return pageModule;
446
- }
447
- /**
448
- * Imports the page file from the specified path.
449
- * It uses dynamic import to load the file and returns the imported module.
450
- *
451
- * @param file - The file path to import.
452
- * @returns The imported module.
453
- */
454
- async importPageFile(file, options) {
455
- const bypassCache = options?.bypassCache ?? false;
456
- const pageModule = this.usesIntegrationPageImporter(file) ? await this.importIntegrationPageFile(file, {
457
- bypassCache,
458
- cacheScope: options?.cacheScope
459
- }) : await this.pageModuleLoaderService.importPageFile(file, {
460
- bypassCache,
461
- cacheScope: options?.cacheScope
462
- });
463
- return this.normalizeImportedPageFile(file, pageModule);
464
- }
465
- /**
466
- * Resolves the dependency path based on the component directory.
467
- * It combines the component directory with the provided path URL.
468
- *
469
- * @param componentDir - The component directory path.
470
- * @param pathUrl - The path URL to resolve.
471
- * @returns The resolved dependency path.
472
- */
473
- resolveDependencyPath(componentDir, pathUrl) {
474
- return this.dependencyResolverService.resolveDependencyPath(componentDir, pathUrl);
475
- }
476
- /**
477
- * Collects the dependencies for the provided components.
478
- * Combines component-specific dependencies with global integration dependencies.
479
- *
480
- * @param components - The components to collect dependencies from.
481
- */
482
- async resolveDependencies(components) {
483
- const componentDeps = await this.processComponentDependencies(components);
484
- return this.resolvedIntegrationDependencies.concat(componentDeps);
485
- }
486
- /**
487
- * Processes component-specific dependencies WITHOUT prepending global integration dependencies.
488
- * Use this method when you need only the component's own assets.
489
- *
490
- * @param components - The components to collect dependencies from.
491
- */
492
- async processComponentDependencies(components) {
493
- return this.dependencyResolverService.processComponentDependencies(components, this.name);
494
- }
495
- /**
496
- * Builds the internal route-render adapter consumed by `RouteRenderOrchestrator`.
497
- *
498
- * The route orchestrator needs a narrow orchestration contract, but those hooks should
499
- * not become public API on the renderer base class. Keeping the adapter object
500
- * local to the execution path lets the orchestrator depend on one explicit seam while
501
- * subclasses continue to override protected renderer behavior directly.
502
- */
503
- createRouteRenderOrchestratorAdapter() {
504
- return createIntegrationRouteRenderAdapter({
505
- name: this.name,
506
- resolveRouteRenderInputs: (routeOptions) => this.resolveRouteRenderInputs(routeOptions),
507
- resolveRouteDependencies: (input) => this.resolveRouteDependencies(input),
508
- importPageFile: (file) => this.importPageFile(file),
509
- collectPageBrowserGraphContribution: (context) => this.collectPageBrowserGraphContribution(context),
510
- renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
511
- getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
512
- getHtmlDocumentContributions: (options) => this.getHtmlDocumentContributions(options),
513
- applyAttributesToHtmlElement: (html, attributes) => this.applyAttributesToHtmlElement(html, attributes),
514
- transformRouteResponse: async (response, htmlContributions, pagePackage) => {
515
- const resolvedPagePackage = this.htmlTransformer.getPagePackage() ?? pagePackage;
516
- const transformedResponse = await this.htmlTransformer.transform(
517
- response,
518
- htmlContributions,
519
- resolvedPagePackage
520
- );
521
- return transformedResponse.body ?? await transformedResponse.text();
522
- }
523
- });
524
- }
525
- async resolveRouteRenderInputs(routeOptions) {
526
- const pageModule = await this.pageModuleLoaderService.resolvePageModule({
527
- file: routeOptions.file,
528
- importPageFileFn: (targetFile) => this.importPageFile(targetFile)
529
- });
530
- const { Page, integrationSpecificProps } = pageModule;
531
- const HtmlTemplate = await this.getHtmlTemplate();
532
- const Layouts = resolvePageLayoutComponents(Page.config?.layouts);
533
- const Layout = resolveInnermostPageLayout(Layouts);
534
- const { props, metadata } = await this.pageModuleLoaderService.resolvePageData({
535
- pageModule,
536
- routeOptions
537
- });
538
- return {
539
- Page,
540
- HtmlTemplate,
541
- Layouts,
542
- Layout,
543
- layoutEntries: Page.config?.layoutEntries,
544
- props,
545
- metadata,
546
- integrationSpecificProps
547
- };
548
- }
549
- async resolveRouteDependencies(input) {
550
- return {
551
- resolvedDependencies: await this.resolveDependencies(input.components)
552
- };
553
- }
554
- async renderRouteBody(renderOptions) {
555
- return this.render(renderOptions);
556
- }
557
- /**
558
- * Prepares the render options for the integration renderer.
559
- * It imports the page file, collects dependencies, and prepares the render options.
560
- *
561
- * @param options - The route renderer options.
562
- * @returns The prepared render options.
563
- */
564
- async prepareRenderOptions(options, adapter = this.createRouteRenderOrchestratorAdapter()) {
565
- const renderOptions = await this.routeRenderOrchestrator.prepareRenderOptions(options, adapter);
566
- invariant(renderOptions.pagePackage !== void 0, "Expected render preparation to produce a page package");
567
- this.htmlTransformer.setPagePackage(renderOptions.pagePackage);
568
- return renderOptions;
569
- }
570
- /**
571
- * Executes the integration renderer with the provided options.
572
- *
573
- * Execution flow:
574
- * 1. Build normalized render options (`prepareRenderOptions`).
575
- * 2. Render the route body once.
576
- * 3. Reject unresolved route-level eco-marker artifacts.
577
- * 4. Optionally apply document attributes for integration-owned document boundaries.
578
- * 5. Run HTML transformer with final dependency set.
579
- *
580
- * Stream-safety note: the first render result is normalized to a string once,
581
- * then the pipeline continues with that immutable HTML value to avoid disturbed
582
- * response-body errors.
583
- *
584
- * @param options Route renderer options.
585
- * @returns Rendered route body plus effective cache strategy.
586
- */
587
- async execute(options) {
588
- return this.runRenderExclusive(async () => {
589
- this.htmlTransformer.setProcessedDependencies([]);
590
- const adapter = this.createRouteRenderOrchestratorAdapter();
591
- const renderOptions = await this.prepareRenderOptions(options, adapter);
592
- return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
593
- });
594
- }
595
- /**
596
- * Returns document-level attributes to stamp onto the rendered `<html>` tag.
597
- *
598
- * Integrations can override this to expose explicit document ownership or
599
- * other runtime coordination markers without relying on script sniffing.
600
- */
601
- getDocumentAttributes(_renderOptions) {
602
- return void 0;
603
- }
604
- applyAttributesToFirstBodyElement(html, attributes) {
605
- return this.htmlTransformer.applyAttributesToFirstBodyElement(html, attributes);
606
- }
607
- applyAttributesToHtmlElement(html, attributes) {
608
- return this.htmlTransformer.applyAttributesToHtmlElement(html, attributes);
609
- }
610
- /**
611
- * Returns declarative HTML fragments that core should inject into the final document.
612
- *
613
- * @remarks
614
- * Integrations may contribute document markup here, but core retains ownership
615
- * of the final HTML rewrite pipeline and placement semantics. This is the
616
- * supported document-markup extension point for integrations instead of custom
617
- * response finalization logic.
618
- */
619
- getHtmlDocumentContributions(_options) {
620
- return void 0;
621
- }
622
- /**
623
- * Renders one component under this integration's foreign-child runtime and resolves
624
- * any nested foreign children captured during that render.
625
- *
626
- * Without this wrapper, a component tree with foreign-owned descendants would
627
- * render them with no active foreign-child runtime, which bypasses the owning
628
- * renderer's nested foreign-child handoff.
629
- */
630
- async renderComponentWithForeignChildren(input) {
631
- return await this.foreignSubtreeExecutionService.executeComponentRender({
632
- currentIntegrationName: this.name,
633
- input,
634
- renderComponent: (renderInput) => this.renderComponent(renderInput),
635
- normalizeComponentRenderOutput: (result) => this.normalizeComponentRenderOutput(result),
636
- hasForeignChildDescendants: (component) => this.hasForeignChildDescendants(component),
637
- createForeignChildRuntime: ({ renderInput, rendererCache }) => this.createForeignChildRuntime({
638
- renderInput,
639
- rendererCache
640
- }),
641
- getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
642
- appConfig: this.appConfig,
643
- runtimeOrigin: this.runtimeOrigin,
644
- currentIntegrationName: this.name,
645
- currentRenderer: this,
646
- integrationName,
647
- cache: rendererCache
648
- })
649
- });
650
- }
651
- normalizeComponentRenderOutput(result) {
652
- const normalizedHtml = this.normalizeUnresolvedMarkerArtifactHtml(result.html);
653
- return normalizedHtml === result.html ? result : {
654
- ...result,
655
- html: normalizedHtml
656
- };
657
- }
658
- normalizeUnresolvedMarkerArtifactHtml(html) {
659
- return normalizeUnresolvedMarkerArtifactHtml(html);
660
- }
661
- /**
662
- * Returns whether the component dependency tree crosses into another
663
- * integration.
664
- *
665
- * This keeps foreign-child runtime setup narrow: same-integration trees can render
666
- * directly without paying the queue orchestration cost.
667
- */
668
- hasForeignChildDescendants(component) {
669
- return hasForeignChildDescendantsInGraph(component, this.name);
670
- }
671
- /**
672
- * Render a single component and return structured output for orchestration paths.
673
- *
674
- * Default behavior delegates to `renderToResponse` in partial mode and wraps
675
- * the resulting HTML into the `ComponentRenderResult` contract.
676
- *
677
- * In foreign-subtree resolution, this method is the integration-owned step that turns an
678
- * already-resolved deferred foreign subtree into concrete HTML, assets, and optional
679
- * root attributes.
680
- *
681
- * Integrations can override this for richer behavior (asset emission,
682
- * root attributes, integration-specific hydration metadata).
683
- *
684
- * @param input Component render request.
685
- * @returns Structured render result used by component/page orchestration.
686
- */
687
- async renderComponent(input) {
688
- const response = await this.renderToResponse(
689
- input.component,
690
- input.props,
691
- { partial: true }
692
- );
693
- const html = await response.text();
694
- return {
695
- html,
696
- canAttachAttributes: true,
697
- rootTag: this.getRootTagName(html),
698
- integrationName: this.name
699
- };
700
- }
701
- /**
702
- * Extracts the first root element tag name from HTML output.
703
- *
704
- * @param html HTML fragment.
705
- * @returns Root tag name when present; otherwise `undefined`.
706
- */
707
- getRootTagName(html) {
708
- const rootTag = html.match(/^(?:\s|<!--[\s\S]*?-->)*<([a-zA-Z][a-zA-Z0-9:-]*)\b/);
709
- return rootTag?.[1];
710
- }
711
- /**
712
- * Collects declarative Page Browser Graph contributions for one Page.
713
- *
714
- * @remarks
715
- * Integrations may describe page-scoped browser requirements here, while core
716
- * retains ownership of dependency processing and final graph assembly. This is
717
- * the supported page-browser extension point for integrations.
718
- *
719
- * @param context - The route file path and already imported page module.
720
- * @returns Declarative dependencies or pre-resolved assets for the Page.
721
- */
722
- async collectPageBrowserGraphContribution(_context) {
723
- return void 0;
724
- }
725
- /**
726
- * Creates the per-render foreign-child runtime adopted by the shared component
727
- * render context.
728
- *
729
- * The default runtime queues delegated foreign subtrees inside the owning
730
- * renderer so string and markup renderers do not need to re-declare the same
731
- * handoff boilerplate. Override only when a renderer needs custom runtime
732
- * context or a different foreign-child execution strategy.
733
- */
734
- createForeignChildRuntime(options) {
735
- return this.foreignSubtreeExecutionService.createQueuedRuntime({
736
- renderInput: options.renderInput,
737
- rendererCache: options.rendererCache,
738
- runtimeContextKey: getForeignSubtreeResolutionContextKey(this.name),
739
- tokenPrefix: getForeignSubtreeTokenPrefix(this.name)
740
- });
741
- }
742
- /**
743
- * Creates an explicit fail-fast runtime for tests or renderers that do not
744
- * support cross-integration foreign-child execution.
745
- */
746
- createFailFastForeignChildRuntime() {
747
- return this.foreignSubtreeExecutionService.createFailFastRuntime(this.name);
748
- }
419
+ }
420
+ usesIntegrationPageImporter(_file) {
421
+ return false;
422
+ }
423
+ async importIntegrationPageFile(_file, _options) {
424
+ invariant(false, 'Integration page importer must be implemented when enabled');
425
+ }
426
+ normalizeImportedPageFile(_file, pageModule) {
427
+ return pageModule;
428
+ }
429
+ /**
430
+ * Imports the page file from the specified path.
431
+ * It uses dynamic import to load the file and returns the imported module.
432
+ *
433
+ * @param file - The file path to import.
434
+ * @returns The imported module.
435
+ */
436
+ async importPageFile(file, options) {
437
+ const bypassCache = options?.bypassCache ?? false;
438
+ const pageModule = this.usesIntegrationPageImporter(file)
439
+ ? await this.importIntegrationPageFile(file, {
440
+ bypassCache,
441
+ cacheScope: options?.cacheScope,
442
+ })
443
+ : await this.pageModuleLoaderService.importPageFile(file, {
444
+ bypassCache,
445
+ cacheScope: options?.cacheScope,
446
+ });
447
+ return this.normalizeImportedPageFile(file, pageModule);
448
+ }
449
+ /**
450
+ * Resolves the dependency path based on the component directory.
451
+ * It combines the component directory with the provided path URL.
452
+ *
453
+ * @param componentDir - The component directory path.
454
+ * @param pathUrl - The path URL to resolve.
455
+ * @returns The resolved dependency path.
456
+ */
457
+ resolveDependencyPath(componentDir, pathUrl) {
458
+ return this.dependencyResolverService.resolveDependencyPath(componentDir, pathUrl);
459
+ }
460
+ /**
461
+ * Collects the dependencies for the provided components.
462
+ * Combines component-specific dependencies with global integration dependencies.
463
+ *
464
+ * @param components - The components to collect dependencies from.
465
+ */
466
+ async resolveDependencies(components) {
467
+ const componentDeps = await this.processComponentDependencies(components);
468
+ return this.resolvedIntegrationDependencies.concat(componentDeps);
469
+ }
470
+ /**
471
+ * Processes component-specific dependencies WITHOUT prepending global integration dependencies.
472
+ * Use this method when you need only the component's own assets.
473
+ *
474
+ * @param components - The components to collect dependencies from.
475
+ */
476
+ async processComponentDependencies(components) {
477
+ return this.dependencyResolverService.processComponentDependencies(components, this.name);
478
+ }
479
+ /**
480
+ * Builds the internal route-render adapter consumed by `RouteRenderOrchestrator`.
481
+ *
482
+ * The route orchestrator needs a narrow orchestration contract, but those hooks should
483
+ * not become public API on the renderer base class. Keeping the adapter object
484
+ * local to the execution path lets the orchestrator depend on one explicit seam while
485
+ * subclasses continue to override protected renderer behavior directly.
486
+ */
487
+ createRouteRenderOrchestratorAdapter() {
488
+ return createIntegrationRouteRenderAdapter({
489
+ name: this.name,
490
+ appConfig: this.appConfig,
491
+ watch: this.hmrManager?.isEnabled() === true,
492
+ hostOwnsDevClient: this.appConfig.runtime?.devClientOwner === 'host',
493
+ resolveRouteRenderInputs: (routeOptions) => this.resolveRouteRenderInputs(routeOptions),
494
+ resolveRouteDependencies: (input) => this.resolveRouteDependencies(input),
495
+ importPageFile: (file) => this.importPageFile(file),
496
+ collectPageBrowserGraphContribution: (context) => this.collectPageBrowserGraphContribution(context),
497
+ renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
498
+ getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
499
+ getHtmlDocumentContributions: (options) => this.getHtmlDocumentContributions(options),
500
+ applyAttributesToHtmlElement: (html, attributes) => this.applyAttributesToHtmlElement(html, attributes),
501
+ transformRouteResponse: async (response, htmlContributions, pagePackage) => {
502
+ const resolvedPagePackage = this.htmlTransformer.getPagePackage() ?? pagePackage;
503
+ const transformedResponse = await this.htmlTransformer.transform(response, htmlContributions, resolvedPagePackage);
504
+ return (transformedResponse.body ?? (await transformedResponse.text()));
505
+ },
506
+ });
507
+ }
508
+ async resolveRouteRenderInputs(routeOptions) {
509
+ const pageModule = await this.pageModuleLoaderService.resolvePageModule({
510
+ file: routeOptions.file,
511
+ importPageFileFn: (targetFile) => this.importPageFile(targetFile),
512
+ });
513
+ const { Page, integrationSpecificProps } = pageModule;
514
+ const HtmlTemplate = await this.getHtmlTemplate();
515
+ const Layouts = resolvePageLayoutComponents(Page.config?.layouts);
516
+ const Layout = resolveInnermostPageLayout(Layouts);
517
+ const { props, metadata } = await this.pageModuleLoaderService.resolvePageData({
518
+ pageModule,
519
+ routeOptions,
520
+ });
521
+ return {
522
+ Page,
523
+ HtmlTemplate: HtmlTemplate,
524
+ Layouts,
525
+ Layout,
526
+ layoutEntries: Page.config?.layoutEntries,
527
+ props,
528
+ metadata,
529
+ integrationSpecificProps,
530
+ };
531
+ }
532
+ async resolveRouteDependencies(input) {
533
+ return {
534
+ resolvedDependencies: await this.resolveDependencies(input.components),
535
+ };
536
+ }
537
+ async renderRouteBody(renderOptions) {
538
+ return this.render(renderOptions);
539
+ }
540
+ /**
541
+ * Prepares the render options for the integration renderer.
542
+ * It imports the page file, collects dependencies, and prepares the render options.
543
+ *
544
+ * @param options - The route renderer options.
545
+ * @returns The prepared render options.
546
+ */
547
+ async prepareRenderOptions(options, adapter = this.createRouteRenderOrchestratorAdapter()) {
548
+ const renderOptions = await this.routeRenderOrchestrator.prepareRenderOptions(options, adapter);
549
+ invariant(renderOptions.pagePackage !== undefined, 'Expected render preparation to produce a page package');
550
+ this.htmlTransformer.setPagePackage(renderOptions.pagePackage);
551
+ return renderOptions;
552
+ }
553
+ /**
554
+ * Executes the integration renderer with the provided options.
555
+ *
556
+ * Execution flow:
557
+ * 1. Build normalized render options (`prepareRenderOptions`).
558
+ * 2. Render the route body once.
559
+ * 3. Reject unresolved route-level eco-marker artifacts.
560
+ * 4. Optionally apply document attributes for integration-owned document boundaries.
561
+ * 5. Run HTML transformer with final dependency set.
562
+ *
563
+ * Stream-safety note: the first render result is normalized to a string once,
564
+ * then the pipeline continues with that immutable HTML value to avoid disturbed
565
+ * response-body errors.
566
+ *
567
+ * @param options Route renderer options.
568
+ * @returns Rendered route body plus effective cache strategy.
569
+ */
570
+ async execute(options) {
571
+ return this.runRenderExclusive(async () => {
572
+ this.htmlTransformer.setProcessedDependencies([]);
573
+ const adapter = this.createRouteRenderOrchestratorAdapter();
574
+ const renderOptions = await this.prepareRenderOptions(options, adapter);
575
+ return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
576
+ });
577
+ }
578
+ /**
579
+ * Returns document-level attributes to stamp onto the rendered `<html>` tag.
580
+ *
581
+ * Integrations can override this to expose explicit document ownership or
582
+ * other runtime coordination markers without relying on script sniffing.
583
+ */
584
+ getDocumentAttributes(_renderOptions) {
585
+ return undefined;
586
+ }
587
+ applyAttributesToFirstBodyElement(html, attributes) {
588
+ return this.htmlTransformer.applyAttributesToFirstBodyElement(html, attributes);
589
+ }
590
+ applyAttributesToHtmlElement(html, attributes) {
591
+ return this.htmlTransformer.applyAttributesToHtmlElement(html, attributes);
592
+ }
593
+ /**
594
+ * Returns declarative HTML fragments that core should inject into the final document.
595
+ *
596
+ * @remarks
597
+ * Integrations may contribute document markup here, but core retains ownership
598
+ * of the final HTML rewrite pipeline and placement semantics. This is the
599
+ * supported document-markup extension point for integrations instead of custom
600
+ * response finalization logic.
601
+ */
602
+ getHtmlDocumentContributions(_options) {
603
+ return undefined;
604
+ }
605
+ /**
606
+ * Renders one component under this integration's foreign-child runtime and resolves
607
+ * any nested foreign children captured during that render.
608
+ *
609
+ * Without this wrapper, a component tree with foreign-owned descendants would
610
+ * render them with no active foreign-child runtime, which bypasses the owning
611
+ * renderer's nested foreign-child handoff.
612
+ */
613
+ async renderComponentWithForeignChildren(input) {
614
+ return await this.foreignSubtreeExecutionService.executeComponentRender({
615
+ currentIntegrationName: this.name,
616
+ input,
617
+ renderComponent: (renderInput) => this.renderComponent(renderInput),
618
+ normalizeComponentRenderOutput: (result) => this.normalizeComponentRenderOutput(result),
619
+ hasForeignChildDescendants: (component) => this.hasForeignChildDescendants(component),
620
+ createForeignChildRuntime: ({ renderInput, rendererCache }) => this.createForeignChildRuntime({
621
+ renderInput,
622
+ rendererCache: rendererCache,
623
+ }),
624
+ getOwningRenderer: (integrationName, rendererCache) => resolveOwningIntegrationRenderer({
625
+ appConfig: this.appConfig,
626
+ runtimeOrigin: this.runtimeOrigin,
627
+ currentIntegrationName: this.name,
628
+ currentRenderer: this,
629
+ integrationName,
630
+ cache: rendererCache,
631
+ }),
632
+ });
633
+ }
634
+ finalizeIslandComponentRender(input, result) {
635
+ return finalizeIslandComponentRender(input, result);
636
+ }
637
+ normalizeComponentRenderOutput(result) {
638
+ const normalizedHtml = this.normalizeUnresolvedMarkerArtifactHtml(result.html);
639
+ return normalizedHtml === result.html
640
+ ? result
641
+ : {
642
+ ...result,
643
+ html: normalizedHtml,
644
+ };
645
+ }
646
+ normalizeUnresolvedMarkerArtifactHtml(html) {
647
+ return normalizeUnresolvedMarkerArtifactHtml(html);
648
+ }
649
+ /**
650
+ * Returns whether the component dependency tree crosses into another
651
+ * integration.
652
+ *
653
+ * This keeps foreign-child runtime setup narrow: same-integration trees can render
654
+ * directly without paying the queue orchestration cost.
655
+ */
656
+ hasForeignChildDescendants(component) {
657
+ return hasForeignChildDescendantsInGraph(component, this.name);
658
+ }
659
+ /**
660
+ * Render a single component and return structured output for orchestration paths.
661
+ *
662
+ * Default behavior delegates to `renderToResponse` in partial mode and wraps
663
+ * the resulting HTML into the `ComponentRenderResult` contract.
664
+ *
665
+ * In foreign-subtree resolution, this method is the integration-owned step that turns an
666
+ * already-resolved deferred foreign subtree into concrete HTML, assets, and optional
667
+ * root attributes.
668
+ *
669
+ * Integrations can override this for richer behavior (asset emission,
670
+ * root attributes, integration-specific hydration metadata).
671
+ *
672
+ * @param input Component render request.
673
+ * @returns Structured render result used by component/page orchestration.
674
+ */
675
+ async renderComponent(input) {
676
+ const response = await this.renderToResponse(input.component, input.props, { partial: true });
677
+ const html = await response.text();
678
+ return {
679
+ html,
680
+ canAttachAttributes: true,
681
+ rootTag: this.getRootTagName(html),
682
+ integrationName: this.name,
683
+ };
684
+ }
685
+ /**
686
+ * Extracts the first root element tag name from HTML output.
687
+ *
688
+ * @param html HTML fragment.
689
+ * @returns Root tag name when present; otherwise `undefined`.
690
+ */
691
+ getRootTagName(html) {
692
+ const rootTag = html.match(/^(?:\s|<!--[\s\S]*?-->)*<([a-zA-Z][a-zA-Z0-9:-]*)\b/);
693
+ return rootTag?.[1];
694
+ }
695
+ /**
696
+ * Collects declarative Page Browser Graph contributions for one Page.
697
+ *
698
+ * @remarks
699
+ * Integrations may describe page-scoped browser requirements here, while core
700
+ * retains ownership of dependency processing and final graph assembly. This is
701
+ * the supported page-browser extension point for integrations.
702
+ *
703
+ * @param context - The route file path and already imported page module.
704
+ * @returns Declarative dependencies or pre-resolved assets for the Page.
705
+ */
706
+ async collectPageBrowserGraphContribution(_context) {
707
+ return undefined;
708
+ }
709
+ /**
710
+ * Creates the per-render foreign-child runtime adopted by the shared component
711
+ * render context.
712
+ *
713
+ * The default runtime queues delegated foreign subtrees inside the owning
714
+ * renderer so string and markup renderers do not need to re-declare the same
715
+ * handoff boilerplate. Override only when a renderer needs custom runtime
716
+ * context or a different foreign-child execution strategy.
717
+ */
718
+ createForeignChildRuntime(options) {
719
+ return this.foreignSubtreeExecutionService.createQueuedRuntime({
720
+ renderInput: options.renderInput,
721
+ rendererCache: options.rendererCache,
722
+ runtimeContextKey: getForeignSubtreeResolutionContextKey(this.name),
723
+ tokenPrefix: getForeignSubtreeTokenPrefix(this.name),
724
+ });
725
+ }
726
+ /**
727
+ * Creates an explicit fail-fast runtime for tests or renderers that do not
728
+ * support cross-integration foreign-child execution.
729
+ */
730
+ createFailFastForeignChildRuntime() {
731
+ return this.foreignSubtreeExecutionService.createFailFastRuntime(this.name);
732
+ }
749
733
  }
750
- export {
751
- IntegrationRenderer
752
- };