@ecopages/core 0.2.0-alpha.1

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 (342) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/LICENSE +21 -0
  3. package/README.md +32 -0
  4. package/package.json +279 -0
  5. package/src/adapters/abstract/application-adapter.d.ts +168 -0
  6. package/src/adapters/abstract/application-adapter.js +109 -0
  7. package/src/adapters/abstract/application-adapter.ts +337 -0
  8. package/src/adapters/abstract/router-adapter.d.ts +26 -0
  9. package/src/adapters/abstract/router-adapter.js +5 -0
  10. package/src/adapters/abstract/router-adapter.ts +30 -0
  11. package/src/adapters/abstract/server-adapter.d.ts +69 -0
  12. package/src/adapters/abstract/server-adapter.js +15 -0
  13. package/src/adapters/abstract/server-adapter.ts +79 -0
  14. package/src/adapters/bun/client-bridge.d.ts +34 -0
  15. package/src/adapters/bun/client-bridge.js +48 -0
  16. package/src/adapters/bun/client-bridge.ts +62 -0
  17. package/src/adapters/bun/create-app.d.ts +60 -0
  18. package/src/adapters/bun/create-app.js +117 -0
  19. package/src/adapters/bun/create-app.ts +189 -0
  20. package/src/adapters/bun/define-api-handler.d.ts +61 -0
  21. package/src/adapters/bun/define-api-handler.js +15 -0
  22. package/src/adapters/bun/define-api-handler.ts +114 -0
  23. package/src/adapters/bun/hmr-manager.d.ts +84 -0
  24. package/src/adapters/bun/hmr-manager.js +227 -0
  25. package/src/adapters/bun/hmr-manager.ts +281 -0
  26. package/src/adapters/bun/index.d.ts +3 -0
  27. package/src/adapters/bun/index.js +8 -0
  28. package/src/adapters/bun/index.ts +3 -0
  29. package/src/adapters/bun/server-adapter.d.ts +155 -0
  30. package/src/adapters/bun/server-adapter.js +368 -0
  31. package/src/adapters/bun/server-adapter.ts +492 -0
  32. package/src/adapters/bun/server-lifecycle.d.ts +52 -0
  33. package/src/adapters/bun/server-lifecycle.js +120 -0
  34. package/src/adapters/bun/server-lifecycle.ts +154 -0
  35. package/src/adapters/index.d.ts +6 -0
  36. package/src/adapters/index.js +14 -0
  37. package/src/adapters/index.ts +6 -0
  38. package/src/adapters/node/create-app.d.ts +21 -0
  39. package/src/adapters/node/create-app.js +143 -0
  40. package/src/adapters/node/create-app.ts +179 -0
  41. package/src/adapters/node/index.d.ts +4 -0
  42. package/src/adapters/node/index.js +8 -0
  43. package/src/adapters/node/index.ts +9 -0
  44. package/src/adapters/node/node-client-bridge.d.ts +26 -0
  45. package/src/adapters/node/node-client-bridge.js +66 -0
  46. package/src/adapters/node/node-client-bridge.ts +79 -0
  47. package/src/adapters/node/node-hmr-manager.d.ts +62 -0
  48. package/src/adapters/node/node-hmr-manager.js +221 -0
  49. package/src/adapters/node/node-hmr-manager.ts +271 -0
  50. package/src/adapters/node/server-adapter.d.ts +190 -0
  51. package/src/adapters/node/server-adapter.js +420 -0
  52. package/src/adapters/node/server-adapter.ts +561 -0
  53. package/src/adapters/node/static-content-server.d.ts +24 -0
  54. package/src/adapters/node/static-content-server.js +166 -0
  55. package/src/adapters/node/static-content-server.ts +203 -0
  56. package/src/adapters/shared/api-response.d.ts +52 -0
  57. package/src/adapters/shared/api-response.js +96 -0
  58. package/src/adapters/shared/api-response.ts +104 -0
  59. package/src/adapters/shared/application-adapter.d.ts +18 -0
  60. package/src/adapters/shared/application-adapter.js +90 -0
  61. package/src/adapters/shared/application-adapter.ts +199 -0
  62. package/src/adapters/shared/explicit-static-route-matcher.d.ts +38 -0
  63. package/src/adapters/shared/explicit-static-route-matcher.js +100 -0
  64. package/src/adapters/shared/explicit-static-route-matcher.ts +134 -0
  65. package/src/adapters/shared/file-route-middleware-pipeline.d.ts +65 -0
  66. package/src/adapters/shared/file-route-middleware-pipeline.js +98 -0
  67. package/src/adapters/shared/file-route-middleware-pipeline.ts +123 -0
  68. package/src/adapters/shared/fs-server-response-factory.d.ts +19 -0
  69. package/src/adapters/shared/fs-server-response-factory.js +97 -0
  70. package/src/adapters/shared/fs-server-response-factory.ts +118 -0
  71. package/src/adapters/shared/fs-server-response-matcher.d.ts +71 -0
  72. package/src/adapters/shared/fs-server-response-matcher.js +155 -0
  73. package/src/adapters/shared/fs-server-response-matcher.ts +198 -0
  74. package/src/adapters/shared/render-context.d.ts +14 -0
  75. package/src/adapters/shared/render-context.js +69 -0
  76. package/src/adapters/shared/render-context.ts +105 -0
  77. package/src/adapters/shared/server-adapter.d.ts +87 -0
  78. package/src/adapters/shared/server-adapter.js +353 -0
  79. package/src/adapters/shared/server-adapter.ts +442 -0
  80. package/src/adapters/shared/server-route-handler.d.ts +89 -0
  81. package/src/adapters/shared/server-route-handler.js +120 -0
  82. package/src/adapters/shared/server-route-handler.ts +166 -0
  83. package/src/adapters/shared/server-static-builder.d.ts +38 -0
  84. package/src/adapters/shared/server-static-builder.js +46 -0
  85. package/src/adapters/shared/server-static-builder.ts +82 -0
  86. package/src/build/build-adapter.d.ts +74 -0
  87. package/src/build/build-adapter.js +54 -0
  88. package/src/build/build-adapter.ts +132 -0
  89. package/src/build/build-types.d.ts +57 -0
  90. package/src/build/build-types.js +0 -0
  91. package/src/build/build-types.ts +83 -0
  92. package/src/build/esbuild-build-adapter.d.ts +69 -0
  93. package/src/build/esbuild-build-adapter.js +390 -0
  94. package/src/build/esbuild-build-adapter.ts +510 -0
  95. package/src/config/config-builder.d.ts +227 -0
  96. package/src/config/config-builder.js +392 -0
  97. package/src/config/config-builder.ts +474 -0
  98. package/src/constants.d.ts +32 -0
  99. package/src/constants.js +21 -0
  100. package/src/constants.ts +39 -0
  101. package/src/create-app.d.ts +17 -0
  102. package/src/create-app.js +66 -0
  103. package/src/create-app.ts +87 -0
  104. package/src/declarations.d.ts +26 -0
  105. package/src/define-api-handler.d.ts +25 -0
  106. package/src/define-api-handler.js +15 -0
  107. package/src/define-api-handler.ts +66 -0
  108. package/src/dev/sc-server.d.ts +30 -0
  109. package/src/dev/sc-server.js +111 -0
  110. package/src/dev/sc-server.ts +143 -0
  111. package/src/eco/README.md +636 -0
  112. package/src/eco/component-render-context.d.ts +105 -0
  113. package/src/eco/component-render-context.js +77 -0
  114. package/src/eco/component-render-context.ts +202 -0
  115. package/src/eco/eco.d.ts +9 -0
  116. package/src/eco/eco.js +110 -0
  117. package/src/eco/eco.ts +221 -0
  118. package/src/eco/eco.types.d.ts +170 -0
  119. package/src/eco/eco.types.js +0 -0
  120. package/src/eco/eco.types.ts +202 -0
  121. package/src/eco/eco.utils.d.ts +40 -0
  122. package/src/eco/eco.utils.js +40 -0
  123. package/src/eco/eco.utils.ts +89 -0
  124. package/src/eco/global-injector-map.d.ts +16 -0
  125. package/src/eco/global-injector-map.js +80 -0
  126. package/src/eco/global-injector-map.ts +112 -0
  127. package/src/eco/lazy-injector-map.d.ts +8 -0
  128. package/src/eco/lazy-injector-map.js +70 -0
  129. package/src/eco/lazy-injector-map.ts +120 -0
  130. package/src/eco/module-dependencies.d.ts +18 -0
  131. package/src/eco/module-dependencies.js +49 -0
  132. package/src/eco/module-dependencies.ts +75 -0
  133. package/src/env.d.ts +20 -0
  134. package/src/errors/http-error.d.ts +31 -0
  135. package/src/errors/http-error.js +50 -0
  136. package/src/errors/http-error.ts +72 -0
  137. package/src/errors/index.d.ts +2 -0
  138. package/src/errors/index.js +4 -0
  139. package/src/errors/index.ts +2 -0
  140. package/src/errors/locals-access-error.d.ts +4 -0
  141. package/src/errors/locals-access-error.js +9 -0
  142. package/src/errors/locals-access-error.ts +7 -0
  143. package/src/global/app-logger.d.ts +2 -0
  144. package/src/global/app-logger.js +6 -0
  145. package/src/global/app-logger.ts +4 -0
  146. 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
  147. package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-HMR-Server-Integration-should-load-fixture-app-page-1.png +0 -0
  148. package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-WebSocket-Connection-should-connect-to-correct-HMR-endpoint-1.png +0 -0
  149. package/src/hmr/client/hmr-runtime.d.ts +10 -0
  150. package/src/hmr/client/hmr-runtime.js +86 -0
  151. package/src/hmr/client/hmr-runtime.ts +121 -0
  152. package/src/hmr/hmr-strategy.d.ts +159 -0
  153. package/src/hmr/hmr-strategy.js +29 -0
  154. package/src/hmr/hmr-strategy.ts +172 -0
  155. package/src/hmr/hmr.test.e2e.d.ts +1 -0
  156. package/src/hmr/hmr.test.e2e.js +50 -0
  157. package/src/hmr/hmr.test.e2e.ts +75 -0
  158. package/src/hmr/strategies/default-hmr-strategy.d.ts +43 -0
  159. package/src/hmr/strategies/default-hmr-strategy.js +34 -0
  160. package/src/hmr/strategies/default-hmr-strategy.ts +60 -0
  161. package/src/hmr/strategies/js-hmr-strategy.d.ts +136 -0
  162. package/src/hmr/strategies/js-hmr-strategy.js +179 -0
  163. package/src/hmr/strategies/js-hmr-strategy.ts +308 -0
  164. package/src/index.browser.d.ts +3 -0
  165. package/src/index.browser.js +4 -0
  166. package/src/index.browser.ts +3 -0
  167. package/src/index.d.ts +5 -0
  168. package/src/index.js +10 -0
  169. package/src/index.ts +5 -0
  170. package/src/integrations/ghtml/ghtml-renderer.d.ts +15 -0
  171. package/src/integrations/ghtml/ghtml-renderer.js +60 -0
  172. package/src/integrations/ghtml/ghtml-renderer.ts +93 -0
  173. package/src/integrations/ghtml/ghtml.plugin.d.ts +20 -0
  174. package/src/integrations/ghtml/ghtml.plugin.js +21 -0
  175. package/src/integrations/ghtml/ghtml.plugin.ts +32 -0
  176. package/src/internal-types.d.ts +200 -0
  177. package/src/internal-types.js +0 -0
  178. package/src/internal-types.ts +212 -0
  179. package/src/plugins/alias-resolver-plugin.d.ts +2 -0
  180. package/src/plugins/alias-resolver-plugin.js +39 -0
  181. package/src/plugins/alias-resolver-plugin.ts +45 -0
  182. package/src/plugins/eco-component-meta-plugin.d.ts +95 -0
  183. package/src/plugins/eco-component-meta-plugin.js +157 -0
  184. package/src/plugins/eco-component-meta-plugin.ts +474 -0
  185. package/src/plugins/integration-plugin.d.ts +102 -0
  186. package/src/plugins/integration-plugin.js +100 -0
  187. package/src/plugins/integration-plugin.ts +184 -0
  188. package/src/plugins/processor.d.ts +82 -0
  189. package/src/plugins/processor.js +122 -0
  190. package/src/plugins/processor.ts +220 -0
  191. package/src/public-types.d.ts +1094 -0
  192. package/src/public-types.js +0 -0
  193. package/src/public-types.ts +1255 -0
  194. package/src/route-renderer/GRAPH.md +387 -0
  195. package/src/route-renderer/README.md +135 -0
  196. package/src/route-renderer/component-graph-executor.d.ts +32 -0
  197. package/src/route-renderer/component-graph-executor.js +31 -0
  198. package/src/route-renderer/component-graph-executor.ts +84 -0
  199. package/src/route-renderer/component-graph.d.ts +42 -0
  200. package/src/route-renderer/component-graph.js +72 -0
  201. package/src/route-renderer/component-graph.ts +159 -0
  202. package/src/route-renderer/component-marker.d.ts +52 -0
  203. package/src/route-renderer/component-marker.js +46 -0
  204. package/src/route-renderer/component-marker.ts +117 -0
  205. package/src/route-renderer/dependency-resolver.d.ts +24 -0
  206. package/src/route-renderer/dependency-resolver.js +428 -0
  207. package/src/route-renderer/dependency-resolver.ts +596 -0
  208. package/src/route-renderer/html-post-processing.service.d.ts +40 -0
  209. package/src/route-renderer/html-post-processing.service.js +86 -0
  210. package/src/route-renderer/html-post-processing.service.ts +103 -0
  211. package/src/route-renderer/integration-renderer.d.ts +339 -0
  212. package/src/route-renderer/integration-renderer.js +526 -0
  213. package/src/route-renderer/integration-renderer.ts +696 -0
  214. package/src/route-renderer/marker-graph-resolver.d.ts +76 -0
  215. package/src/route-renderer/marker-graph-resolver.js +93 -0
  216. package/src/route-renderer/marker-graph-resolver.ts +153 -0
  217. package/src/route-renderer/page-module-loader.d.ts +61 -0
  218. package/src/route-renderer/page-module-loader.js +102 -0
  219. package/src/route-renderer/page-module-loader.ts +153 -0
  220. package/src/route-renderer/render-execution.service.d.ts +69 -0
  221. package/src/route-renderer/render-execution.service.js +91 -0
  222. package/src/route-renderer/render-execution.service.ts +158 -0
  223. package/src/route-renderer/render-preparation.service.d.ts +112 -0
  224. package/src/route-renderer/render-preparation.service.js +243 -0
  225. package/src/route-renderer/render-preparation.service.ts +358 -0
  226. package/src/route-renderer/route-renderer.d.ts +26 -0
  227. package/src/route-renderer/route-renderer.js +68 -0
  228. package/src/route-renderer/route-renderer.ts +80 -0
  229. package/src/router/fs-router-scanner.d.ts +41 -0
  230. package/src/router/fs-router-scanner.js +155 -0
  231. package/src/router/fs-router-scanner.ts +217 -0
  232. package/src/router/fs-router.d.ts +26 -0
  233. package/src/router/fs-router.js +100 -0
  234. package/src/router/fs-router.ts +122 -0
  235. package/src/services/asset-processing-service/asset-processing.service.d.ts +41 -0
  236. package/src/services/asset-processing-service/asset-processing.service.js +250 -0
  237. package/src/services/asset-processing-service/asset-processing.service.ts +306 -0
  238. package/src/services/asset-processing-service/asset.factory.d.ts +17 -0
  239. package/src/services/asset-processing-service/asset.factory.js +82 -0
  240. package/src/services/asset-processing-service/asset.factory.ts +105 -0
  241. package/src/services/asset-processing-service/assets.types.d.ts +88 -0
  242. package/src/services/asset-processing-service/assets.types.js +0 -0
  243. package/src/services/asset-processing-service/assets.types.ts +112 -0
  244. package/src/services/asset-processing-service/index.d.ts +3 -0
  245. package/src/services/asset-processing-service/index.js +3 -0
  246. package/src/services/asset-processing-service/index.ts +3 -0
  247. package/src/services/asset-processing-service/processor.interface.d.ts +22 -0
  248. package/src/services/asset-processing-service/processor.interface.js +6 -0
  249. package/src/services/asset-processing-service/processor.interface.ts +27 -0
  250. package/src/services/asset-processing-service/processor.registry.d.ts +8 -0
  251. package/src/services/asset-processing-service/processor.registry.js +15 -0
  252. package/src/services/asset-processing-service/processor.registry.ts +18 -0
  253. package/src/services/asset-processing-service/processors/base/base-processor.d.ts +24 -0
  254. package/src/services/asset-processing-service/processors/base/base-processor.js +59 -0
  255. package/src/services/asset-processing-service/processors/base/base-processor.ts +76 -0
  256. package/src/services/asset-processing-service/processors/base/base-script-processor.d.ts +16 -0
  257. package/src/services/asset-processing-service/processors/base/base-script-processor.js +80 -0
  258. package/src/services/asset-processing-service/processors/base/base-script-processor.ts +105 -0
  259. package/src/services/asset-processing-service/processors/index.d.ts +5 -0
  260. package/src/services/asset-processing-service/processors/index.js +5 -0
  261. package/src/services/asset-processing-service/processors/index.ts +5 -0
  262. package/src/services/asset-processing-service/processors/script/content-script.processor.d.ts +5 -0
  263. package/src/services/asset-processing-service/processors/script/content-script.processor.js +57 -0
  264. package/src/services/asset-processing-service/processors/script/content-script.processor.ts +66 -0
  265. package/src/services/asset-processing-service/processors/script/file-script.processor.d.ts +8 -0
  266. package/src/services/asset-processing-service/processors/script/file-script.processor.js +76 -0
  267. package/src/services/asset-processing-service/processors/script/file-script.processor.ts +88 -0
  268. package/src/services/asset-processing-service/processors/script/node-module-script.processor.d.ts +7 -0
  269. package/src/services/asset-processing-service/processors/script/node-module-script.processor.js +74 -0
  270. package/src/services/asset-processing-service/processors/script/node-module-script.processor.ts +84 -0
  271. package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +5 -0
  272. package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +25 -0
  273. package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.ts +27 -0
  274. package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +9 -0
  275. package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +63 -0
  276. package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.ts +77 -0
  277. package/src/services/cache/cache.types.d.ts +107 -0
  278. package/src/services/cache/cache.types.js +0 -0
  279. package/src/services/cache/cache.types.ts +126 -0
  280. package/src/services/cache/index.d.ts +7 -0
  281. package/src/services/cache/index.js +7 -0
  282. package/src/services/cache/index.ts +18 -0
  283. package/src/services/cache/memory-cache-store.d.ts +42 -0
  284. package/src/services/cache/memory-cache-store.js +98 -0
  285. package/src/services/cache/memory-cache-store.ts +130 -0
  286. package/src/services/cache/page-cache-service.d.ts +70 -0
  287. package/src/services/cache/page-cache-service.js +152 -0
  288. package/src/services/cache/page-cache-service.ts +202 -0
  289. package/src/services/html-transformer.service.d.ts +50 -0
  290. package/src/services/html-transformer.service.js +163 -0
  291. package/src/services/html-transformer.service.ts +217 -0
  292. package/src/services/page-module-import.service.d.ts +37 -0
  293. package/src/services/page-module-import.service.js +88 -0
  294. package/src/services/page-module-import.service.ts +129 -0
  295. package/src/services/page-request-cache-coordinator.service.d.ts +75 -0
  296. package/src/services/page-request-cache-coordinator.service.js +107 -0
  297. package/src/services/page-request-cache-coordinator.service.ts +128 -0
  298. package/src/services/schema-validation-service.d.ts +122 -0
  299. package/src/services/schema-validation-service.js +101 -0
  300. package/src/services/schema-validation-service.ts +204 -0
  301. package/src/services/validation/standard-schema.types.d.ts +65 -0
  302. package/src/services/validation/standard-schema.types.js +0 -0
  303. package/src/services/validation/standard-schema.types.ts +68 -0
  304. package/src/static-site-generator/static-site-generator.d.ts +57 -0
  305. package/src/static-site-generator/static-site-generator.js +272 -0
  306. package/src/static-site-generator/static-site-generator.ts +359 -0
  307. package/src/utils/css.d.ts +1 -0
  308. package/src/utils/css.js +7 -0
  309. package/src/utils/css.ts +5 -0
  310. package/src/utils/deep-merge.d.ts +14 -0
  311. package/src/utils/deep-merge.js +32 -0
  312. package/src/utils/deep-merge.ts +47 -0
  313. package/src/utils/hash.d.ts +1 -0
  314. package/src/utils/hash.js +7 -0
  315. package/src/utils/hash.ts +5 -0
  316. package/src/utils/html.d.ts +1 -0
  317. package/src/utils/html.js +4 -0
  318. package/src/utils/html.ts +1 -0
  319. package/src/utils/invariant.d.ts +5 -0
  320. package/src/utils/invariant.js +11 -0
  321. package/src/utils/invariant.ts +15 -0
  322. package/src/utils/locals-utils.d.ts +15 -0
  323. package/src/utils/locals-utils.js +24 -0
  324. package/src/utils/locals-utils.ts +37 -0
  325. package/src/utils/parse-cli-args.d.ts +24 -0
  326. package/src/utils/parse-cli-args.js +47 -0
  327. package/src/utils/parse-cli-args.ts +83 -0
  328. package/src/utils/path-utils.module.d.ts +5 -0
  329. package/src/utils/path-utils.module.js +14 -0
  330. package/src/utils/path-utils.module.ts +14 -0
  331. package/src/utils/runtime.d.ts +11 -0
  332. package/src/utils/runtime.js +40 -0
  333. package/src/utils/runtime.ts +44 -0
  334. package/src/utils/server-utils.module.d.ts +19 -0
  335. package/src/utils/server-utils.module.js +56 -0
  336. package/src/utils/server-utils.module.ts +67 -0
  337. package/src/watchers/project-watcher.d.ts +120 -0
  338. package/src/watchers/project-watcher.js +238 -0
  339. package/src/watchers/project-watcher.test-helpers.d.ts +4 -0
  340. package/src/watchers/project-watcher.test-helpers.js +51 -0
  341. package/src/watchers/project-watcher.test-helpers.ts +40 -0
  342. package/src/watchers/project-watcher.ts +306 -0
@@ -0,0 +1,387 @@
1
+ # Rendering Logic Graph
2
+
3
+ This document maps the end-to-end rendering logic in core, including request-time rendering, explicit route rendering, static generation, and marker graph orchestration.
4
+
5
+ ## Design Principles
6
+
7
+ These diagrams are based on a few architectural assumptions that seem important to preserve:
8
+
9
+ - **Rendering entry points are separate, but should converge on shared renderer contracts.**
10
+ Request-time page rendering, explicit view rendering, and static generation all eventually depend on the same integration renderer behavior.
11
+ - **Integration choice should remain a boundary concern.**
12
+ Adapters and route matchers decide which renderer to use; once selected, the integration renderer owns the page render pipeline.
13
+ - **Data resolution should happen before HTML transformation.**
14
+ Static props, metadata, dependency processing, and route assets are all upstream of final HTML injection.
15
+ - **Marker graph orchestration should be a post-render reconciliation step.**
16
+ The initial integration render may emit deferred component markers; those are resolved after the first HTML pass, not during route matching.
17
+ - **Marker emission is integration-defined boundary behavior.**
18
+ Markers are emitted when the active render boundary policy decides a component boundary must be deferred. If no `eco-marker` tokens are emitted, the marker graph stage is skipped entirely.
19
+ - **The marker pipeline remains generic after emission.**
20
+ Once markers exist, core resolves them generically through marker graph extraction, integration renderer dispatch, asset collection, and HTML replacement. The current built-in React integration is one concrete consumer of that mechanism.
21
+ - **Caching policy is authoritative at the page layer.**
22
+ Middleware, locals, and response reuse all depend on the effective page cache strategy.
23
+ - **Asset emission should converge into one injection stage.**
24
+ Route-level assets, integration assets, page-root component assets, and marker-generated assets all end up in the HTML transformer.
25
+
26
+ ## Entry Points
27
+
28
+ There are three main rendering entry points:
29
+
30
+ 1. **Runtime request rendering**
31
+ - handled through the server adapters and file-system route matching
32
+ 2. **Explicit rendering APIs**
33
+ - handled through `renderToResponse()` and route-handler render context helpers
34
+ 3. **Static site generation**
35
+ - handled through explicit route generation and route renderer reuse at build time
36
+
37
+ ## 1) Runtime Request Flow (Bun and Node)
38
+
39
+ ```mermaid
40
+ flowchart TD
41
+ A[HTTP request] --> B[SharedServerAdapter handleSharedRequest]
42
+ B --> C{API route?}
43
+ C -- Yes --> D[executeApiHandler]
44
+ C -- No --> E[ServerRouteHandler handleResponse]
45
+
46
+ E --> F{Explicit static route match?}
47
+ F -- Yes --> G[ExplicitStaticRouteMatcher handleMatch]
48
+ F -- No --> H{FS route match without file extension?}
49
+ H -- Yes --> I[FileSystemResponseMatcher handleMatch]
50
+ H -- No --> J[FileSystemResponseMatcher handleNoMatch]
51
+
52
+ I --> K[RouteRendererFactory createRenderer]
53
+ K --> L[RouteRenderer createRoute]
54
+ L --> M[IntegrationRenderer execute]
55
+ M --> N[response body and cache strategy]
56
+
57
+ J --> O{Static asset request?}
58
+ O -- Yes --> P[FileSystemServerResponseFactory createFileResponse]
59
+ O -- No --> Q[FileSystemServerResponseFactory createCustomNotFoundResponse]
60
+ Q --> R[routeRendererFactory createRenderer for 404 template]
61
+ R --> S[RouteRenderer createRoute]
62
+ ```
63
+
64
+ ## 2) FS Route Rendering + Middleware + Cache
65
+
66
+ This is the most operationally important runtime path because it is where middleware, locals, and cache behavior are coordinated.
67
+
68
+ ```mermaid
69
+ flowchart TD
70
+ A[FileSystemResponseMatcher handleMatch] --> B[PageModuleImportService importModule]
71
+ B --> C[read Page cache and Page middleware]
72
+ C --> D[FileRouteMiddlewarePipeline assertValidConfiguration]
73
+ D --> E{Middleware exists?}
74
+
75
+ E -- Yes --> F{cache dynamic?}
76
+ F -- No --> G[throw LocalsAccessError]
77
+ F -- Yes --> H[FileRouteMiddlewarePipeline createContext]
78
+ H --> I[FileRouteMiddlewarePipeline run]
79
+
80
+ E -- No --> J[renderResponse]
81
+ I --> J
82
+
83
+ J --> K[PageRequestCacheCoordinator render]
84
+ K --> L{cache disabled OR dynamic page?}
85
+ L -- Yes --> M[renderFn directly]
86
+ L -- No --> N[PageCacheService getOrCreate]
87
+
88
+ M --> O[routeRenderer createRoute]
89
+ N --> O
90
+ O --> P[PageRequestCacheCoordinator bodyToString]
91
+ P --> Q[html and strategy]
92
+ Q --> R[createCachedResponse with cache headers]
93
+ ```
94
+
95
+ ## 3) RouteRendererFactory Selection Logic
96
+
97
+ This is a small but important boundary. It centralizes integration selection and renderer reuse, which helps keep adapter code thin.
98
+
99
+ ```mermaid
100
+ flowchart TD
101
+ A[createRenderer filePath] --> B[getRouteRendererEngine filePath]
102
+ B --> C[getIntegrationPlugin by template extension]
103
+ C --> D{renderer cached by integration name?}
104
+ D -- Yes --> E[Reuse renderer]
105
+ D -- No --> F[integrationPlugin.initializeRenderer]
106
+ F --> G[Cache renderer]
107
+ E --> H[RouteRenderer]
108
+ G --> H
109
+
110
+ I[getRendererByIntegration integrationName] --> J{plugin exists?}
111
+ J -- No --> K[return null]
112
+ J -- Yes --> L{renderer cached?}
113
+ L -- Yes --> M[Reuse renderer]
114
+ L -- No --> N[initializeRenderer + cache]
115
+ ```
116
+
117
+ ## 4) IntegrationRenderer Pipeline
118
+
119
+ The original single graph was accurate but a bit dense. Splitting it into preparation and execution phases makes the orchestration easier to reason about.
120
+
121
+ ### 4.1 Render preparation via `RenderPreparationService`
122
+
123
+ ```mermaid
124
+ flowchart TD
125
+ A[IntegrationRenderer prepareRenderOptions] --> B[RenderPreparationService prepare]
126
+ B --> C[resolvePageModule]
127
+ C --> D[resolvePageData staticProps to metadata]
128
+ D --> E[resolveDependencies and used integration deps and route assets]
129
+ E --> F{shouldRenderPageComponent?}
130
+ F -- Yes --> G[renderPageRoot via renderComponent]
131
+ F -- No --> H[skip page root component render]
132
+ G --> I[merge component assets]
133
+ H --> I
134
+ I --> J[collect lazy triggers]
135
+ J --> K{triggers found?}
136
+ K -- Yes --> L[buildGlobalInjectorAssets]
137
+ K -- No --> M[continue]
138
+ L --> M
139
+ M --> N[HtmlPostProcessingService dedupeProcessedAssets]
140
+ N --> O[htmlTransformer setProcessedDependencies]
141
+ O --> P[return normalized render options]
142
+ ```
143
+
144
+ ### 4.2 Main execute flow via `RenderExecutionService`
145
+
146
+ Important nuance: this phase does not always run marker graph resolution. It only does that when the first render pass actually produced `eco-marker` placeholders. In practice today, that usually means a non-React renderer crossed into React component rendering and deferred those nodes for the second pass.
147
+
148
+ ```mermaid
149
+ flowchart TD
150
+ A[IntegrationRenderer execute] --> B[RenderExecutionService execute]
151
+ B --> C[prepareRenderOptions]
152
+ C --> D[runWithComponentRenderContext then render]
153
+ D --> E[normalize response body to renderedHtml]
154
+ E --> F[merge captured and explicit componentGraphContext]
155
+ F --> G{contains eco marker token?}
156
+ G -- Yes --> H[resolveMarkerGraphHtml]
157
+ G -- No --> I[skip graph resolution]
158
+ H --> J[HtmlPostProcessingService dedupeProcessedAssets]
159
+ J --> K[merge marker assets into transformer deps]
160
+ I --> L{root attributes attachable?}
161
+ K --> L
162
+ L -- Yes --> M[HtmlPostProcessingService applyAttributesToFirstBodyElement]
163
+ L -- No --> N[leave html unchanged]
164
+ M --> O[htmlTransformer transform]
165
+ N --> O
166
+ O --> P[return body stream and cache strategy]
167
+ ```
168
+
169
+ ### 4.3 Render preparation responsibilities
170
+
171
+ ```mermaid
172
+ flowchart LR
173
+ A[Page module loader] --> B[static props]
174
+ B --> C[metadata]
175
+ C --> D[page props and locals]
176
+
177
+ E[dependency resolver] --> F[component assets]
178
+ F --> G[integration assets]
179
+ G --> H[route assets]
180
+ H --> I[global injector assets]
181
+
182
+ D --> J[prepared render options]
183
+ I --> J
184
+ ```
185
+
186
+ ### 4.4 Service boundary map
187
+
188
+ ```mermaid
189
+ flowchart LR
190
+ A[IntegrationRenderer] --> B[RenderPreparationService]
191
+ A --> C[RenderExecutionService]
192
+ A --> D[MarkerGraphResolver]
193
+ A --> E[HtmlPostProcessingService]
194
+ A --> F[PageModuleLoaderService]
195
+ A --> G[DependencyResolverService]
196
+ A --> H[HtmlTransformerService]
197
+
198
+ B --> F
199
+ B --> G
200
+ B --> E
201
+ C --> D
202
+ C --> E
203
+ C --> H
204
+ ```
205
+
206
+ ## 5) Marker Emission + Graph Resolution
207
+
208
+ This part is architecturally interesting because it introduces a second render stage. The first pass captures boundaries; the second pass resolves them in dependency order.
209
+
210
+ If this feels complex, the simplest mental model is:
211
+
212
+ - first pass: render everything that can be rendered safely right now
213
+ - when a boundary cannot be rendered safely in the current integration pass, emit a placeholder marker instead
214
+ - second pass: revisit those placeholders and render them using the correct integration renderer
215
+ - final pass: merge any emitted assets and perform the normal HTML transformation
216
+
217
+ In the current implementation, this marker path exists to defer React subtrees that cannot be rendered inline during the active non-React integration pass.
218
+
219
+ Important clarification: not every integration automatically goes through this stage. The marker pipeline is conditional.
220
+
221
+ - If the first render pass returns plain HTML with no `eco-marker` tokens, rendering continues directly to post-processing and HTML transformation.
222
+ - If the first render pass emits `eco-marker` tokens, the marker graph is built and resolved before the final HTML rewrite.
223
+ - In the current implementation, marker emission is triggered when the active render pass boundary policy decides that entering React should be deferred. The policy is injected through component render context, and the React integration currently opts into that deferred behavior.
224
+
225
+ ### Why this exists
226
+
227
+ The marker pipeline exists because some component boundaries cannot always be rendered eagerly inside the current integration pass.
228
+
229
+ Typical reasons include:
230
+
231
+ - the child component belongs to a different integration/runtime
232
+ - the child integration needs its own renderer entry point
233
+ - the parent render needs to preserve ordering and slots before the child subtree is resolved
234
+ - the child render may emit its own assets or root attributes that must be merged back into the final document
235
+
236
+ So the first pass captures a stable placeholder plus serialized render context, and the second pass resolves those placeholders using the correct integration renderer.
237
+
238
+ Responsibility split:
239
+
240
+ - core resolves the deferred marker mechanically: graph shape, refs, slot relationships, and target renderer lookup
241
+ - the selected integration renderer resolves the actual component render once it receives `component`, `props`, and optional `children`
242
+
243
+ Another way to say it:
244
+
245
+ - a marker is a promise that says "this subtree will be rendered later by another renderer"
246
+ - the marker stores just enough information to make that later render deterministic
247
+ - the graph exists so nested deferred boundaries resolve from leaves to parents, preserving child insertion order and slot structure
248
+
249
+ ### 5.1 Marker emission in `eco.component` factory
250
+
251
+ The key rule here today is: markers are not a general-purpose placeholder for every component. During an active component render pass, `eco.component` asks the current render boundary context whether the next boundary should be deferred. When the answer is defer, it captures props/refs/slot links and returns an `eco-marker` token instead of rendering the component immediately.
252
+
253
+ For the current built-in integrations, this is how non-React renders defer React subtrees until the marker resolution pass.
254
+
255
+ ```mermaid
256
+ flowchart TD
257
+ A[eco component render] --> B[getComponentRenderContext]
258
+ B --> C[boundaryContext decideBoundaryRender]
259
+ C --> D{decision is defer?}
260
+ D -- No --> E[render component content immediately]
261
+ D -- Yes --> F[create nodeId + propsRef]
262
+ F --> G[store props in propsByRef]
263
+ G --> H{children include eco-marker tokens?}
264
+ H -- Yes --> I[create slotRef and slotChildrenByRef links]
265
+ H -- No --> J[no slot links]
266
+ I --> K[createComponentMarker]
267
+ J --> K
268
+ K --> L[return eco marker token]
269
+ ```
270
+
271
+ ### 5.2 Marker graph execution
272
+
273
+ Once markers exist in the HTML, the second pass is integration-agnostic at execution time. Each marker carries its target integration name, so the resolver can ask the right renderer to render that specific node. Even though marker emission is currently React-focused, the resolution phase itself is generic and works off the marker payload plus renderer lookup.
274
+
275
+ This means the marker itself is not interpreted by the integration renderer. Core interprets the marker and reconstructs render input; the integration renderer only performs the final component render.
276
+
277
+ ```mermaid
278
+ flowchart TD
279
+ A[resolveMarkerGraphHtml] --> B[buildComponentRefRegistry]
280
+ B --> C[extractComponentGraph from html and slot registry]
281
+ C --> D[resolveComponentGraph in reverse levels]
282
+ D --> E[for each marker node]
283
+ E --> F[resolve component by componentRef]
284
+ F --> G[resolve props by propsRef]
285
+ G --> H[stitch child html from slotRef node ids]
286
+ H --> I[getIntegrationRendererForName]
287
+ I --> J[renderer.renderComponent]
288
+ J --> K[collect component assets]
289
+ K --> L[apply root attributes to first element]
290
+ L --> M[replace marker token in HTML]
291
+ M --> N[resolved html and assets]
292
+ ```
293
+
294
+ ## 6) Explicit Rendering Paths (outside FS page matching)
295
+
296
+ These paths are simpler than request-time file-system rendering because they bypass most router and cache orchestration.
297
+
298
+ ```mermaid
299
+ flowchart TD
300
+ A[ExplicitStaticRouteMatcher handleMatch] --> B[route loader returns view]
301
+ B --> C[read view integration metadata]
302
+ C --> D[routeRendererFactory getRendererByIntegration]
303
+ D --> E[optional view.staticProps]
304
+ E --> F[renderer renderToResponse]
305
+
306
+ G[createRenderContext render or renderPartial] --> H[get renderer from integrations list]
307
+ H --> I[merge props + locals]
308
+ I --> J[renderer renderToResponse]
309
+
310
+ K[StaticSiteGenerator explicit routes] --> L[getRendererByIntegration]
311
+ L --> M[optional staticPaths and staticProps]
312
+ M --> N[renderer renderToResponse]
313
+ N --> O[write html to dist]
314
+ ```
315
+
316
+ ## 7) Current Concrete Integration in core
317
+
318
+ Today the concrete in-core renderer is `GhtmlRenderer`. That makes it a useful reference implementation for the abstract integration renderer contract.
319
+
320
+ ```mermaid
321
+ flowchart TD
322
+ A[GhtmlRenderer.render] --> B[Page params/query/props/locals]
323
+ B --> C{Layout exists?}
324
+ C -- Yes --> D[Layout wraps page content]
325
+ C -- No --> E[use page content]
326
+ D --> F[HtmlTemplate metadata + children + pageProps]
327
+ E --> F
328
+ F --> G[prepend DOCTYPE]
329
+
330
+ H[GhtmlRenderer renderToResponse] --> I{partial?}
331
+ I -- Yes --> J[return component html only]
332
+ I -- No --> K[resolve Layout and HtmlTemplate and metadata]
333
+ K --> L[prepend DOCTYPE]
334
+ J --> M[createHtmlResponse]
335
+ L --> M
336
+ ```
337
+
338
+ ## 8) Reading Order
339
+
340
+ For someone new to the rendering system, this is probably the most useful order to read the code:
341
+
342
+ 1. `server-route-handler.ts`
343
+ 2. `fs-server-response-matcher.ts`
344
+ 3. `file-route-middleware-pipeline.ts`
345
+ 4. `page-request-cache-coordinator.service.ts`
346
+ 5. `route-renderer.ts`
347
+ 6. `integration-renderer.ts`
348
+ 7. `render-preparation.service.ts`
349
+ 8. `render-execution.service.ts`
350
+ 9. `marker-graph-resolver.ts`
351
+ 10. `html-post-processing.service.ts`
352
+ 11. `page-module-loader.ts`
353
+ 12. `dependency-resolver.ts`
354
+ 13. `component-marker.ts`
355
+ 14. `component-graph.ts`
356
+ 15. `component-graph-executor.ts`
357
+ 16. `eco.ts`
358
+ 17. `component-render-context.ts`
359
+ 18. `html-transformer.service.ts`
360
+
361
+ ## 9) Key Files
362
+
363
+ - `packages/core/src/adapters/shared/server-adapter.ts`
364
+ - `packages/core/src/adapters/shared/server-route-handler.ts`
365
+ - `packages/core/src/adapters/shared/fs-server-response-matcher.ts`
366
+ - `packages/core/src/adapters/shared/file-route-middleware-pipeline.ts`
367
+ - `packages/core/src/adapters/shared/fs-server-response-factory.ts`
368
+ - `packages/core/src/adapters/shared/explicit-static-route-matcher.ts`
369
+ - `packages/core/src/adapters/shared/render-context.ts`
370
+ - `packages/core/src/route-renderer/route-renderer.ts`
371
+ - `packages/core/src/route-renderer/integration-renderer.ts`
372
+ - `packages/core/src/route-renderer/render-preparation.service.ts`
373
+ - `packages/core/src/route-renderer/render-execution.service.ts`
374
+ - `packages/core/src/route-renderer/html-post-processing.service.ts`
375
+ - `packages/core/src/route-renderer/marker-graph-resolver.ts`
376
+ - `packages/core/src/route-renderer/component-marker.ts`
377
+ - `packages/core/src/route-renderer/component-graph.ts`
378
+ - `packages/core/src/route-renderer/component-graph-executor.ts`
379
+ - `packages/core/src/route-renderer/page-module-loader.ts`
380
+ - `packages/core/src/route-renderer/dependency-resolver.ts`
381
+ - `packages/core/src/services/page-module-import.service.ts`
382
+ - `packages/core/src/services/page-request-cache-coordinator.service.ts`
383
+ - `packages/core/src/eco/component-render-context.ts`
384
+ - `packages/core/src/eco/eco.ts`
385
+ - `packages/core/src/services/html-transformer.service.ts`
386
+ - `packages/core/src/services/cache/page-cache-service.ts`
387
+ - `packages/core/src/integrations/ghtml/ghtml-renderer.ts`
@@ -0,0 +1,135 @@
1
+ # Route Renderer Architecture
2
+
3
+ This folder contains the core rendering orchestration for Ecopages.
4
+
5
+ ## Purpose
6
+
7
+ The route renderer layer is responsible for:
8
+
9
+ - Selecting the correct integration renderer for a route.
10
+ - Loading page modules and resolving static data/metadata.
11
+ - Resolving and processing component dependencies.
12
+ - Applying full orchestration.
13
+ - Emitting final HTML body output plus metadata/cache strategy.
14
+
15
+ ## Main Components
16
+
17
+ ### `route-renderer.ts`
18
+
19
+ - `RouteRendererFactory` chooses integration renderers based on route file extension.
20
+ - `RouteRenderer` delegates route execution to the selected integration renderer.
21
+
22
+ ### `integration-renderer.ts`
23
+
24
+ Abstract base class that coordinates end-to-end route rendering:
25
+
26
+ 1. Resolve page module and page data.
27
+ 2. Resolve and process component dependencies.
28
+ 3. Add route-level assets and orchestration assets.
29
+ 4. Render HTML via integration-specific `render()`.
30
+ 5. Inject processed assets into final HTML through `HtmlTransformerService`.
31
+
32
+ It also provides:
33
+
34
+ - `renderToResponse()` contract for explicit-route rendering.
35
+ - `renderComponent()` contract for component-level orchestration and artifact reporting.
36
+ - marker graph resolution for nested cross-integration component boundaries.
37
+
38
+ ### `component-marker.ts`
39
+
40
+ Defines marker token contract for component-level orchestration:
41
+
42
+ - `createComponentMarker()` for canonical `<eco-marker ...></eco-marker>` generation.
43
+ - `parseComponentMarkers()` for marker extraction from rendered HTML.
44
+
45
+ ### `component-graph.ts`
46
+
47
+ Builds a deterministic DAG from marker nodes:
48
+
49
+ - node collection by marker id.
50
+ - parent/child edges from slot reference registry.
51
+ - topological levels for bottom-up execution.
52
+
53
+ ### `component-graph-executor.ts`
54
+
55
+ Resolves graph levels in reverse order (leaf to root) and replaces markers with rendered HTML via integration `renderComponent()`.
56
+
57
+ ### `page-module-loader.ts`
58
+
59
+ Service for loading page modules and deriving page data:
60
+
61
+ - `importPageFile()` runtime-aware loading.
62
+ - `resolvePageModule()` normalizes exports and statics.
63
+ - `resolvePageData()` resolves static props then metadata.
64
+
65
+ ### `dependency-resolver.ts`
66
+
67
+ Builds processed assets from component dependency declarations:
68
+
69
+ - Scripts/styles/components/modules.
70
+ - Lazy dependency grouping and trigger derivation.
71
+ - Default global injector behavior with optional legacy scripts-injector compatibility.
72
+
73
+ ## Rendering Behavior
74
+
75
+ Default behavior:
76
+
77
+ - marker-graph component orchestration + component render artifacts.
78
+ - global lazy trigger map + global injector bootstrap.
79
+
80
+ Global injector lifecycle notes:
81
+
82
+ - The bootstrap remains active across client-side navigations.
83
+ - On `eco:after-swap`, it prunes stale `ecopages/global-injector-map` scripts and calls `refresh()` so newly swapped `data-eco-trigger` elements can bind their lazy rules.
84
+ - It must not call injector `cleanup()` on every swap, because that permanently disables future refresh work for the current runtime instance.
85
+
86
+ ## Current Component Artifact Contract
87
+
88
+ Integration `renderComponent()` returns `ComponentRenderResult` with:
89
+
90
+ - `html`
91
+ - `canAttachAttributes`
92
+ - `rootTag`
93
+ - `integrationName`
94
+ - optional `rootAttributes`
95
+ - optional `assets`
96
+
97
+ Current base orchestration behavior:
98
+
99
+ - Calls `renderComponent()` for the page root component.
100
+ - Merges returned `assets` into processed dependencies.
101
+ - Applies returned `rootAttributes` to the first element under `<body>`.
102
+
103
+ When rendered output contains `eco-marker` nodes:
104
+
105
+ - builds marker graph using `componentGraphContext` (`propsByRef`, `slotChildrenByRef`) from integration-specific page module exports.
106
+ - resolves markers bottom-up through integration-specific `renderComponent()` calls.
107
+ - fails fast when marker component refs or props refs are missing.
108
+ - merges marker-rendered assets back into the dependency pipeline with deduplication.
109
+
110
+ This enables island-style hydration assets (for example React/Lit/Kita integration outputs) to be emitted through the normal dependency injection pipeline.
111
+
112
+ ## React Island Boundary Notes
113
+
114
+ - React component-level islands are emitted without synthetic wrapper elements.
115
+ - The React integration attaches `data-eco-component-id` on the component's own SSR root element when a single root is available.
116
+ - Island client bootstrap mounts with `createRoot()` into that root boundary.
117
+ - The emitted hydration bootstrap also listens for `eco:after-swap` so islands hydrate correctly when their SSR markup appears after client-side navigation.
118
+ - React hydration bootstraps are emitted with `data-eco-rerun` and stable `data-eco-script-id` metadata so head-script reconciliation can safely re-execute them when needed.
119
+ - This keeps authored DOM shape stable for global layout/style selectors while preserving per-island runtime isolation.
120
+
121
+ ## Output Pipeline (High-Level)
122
+
123
+ 1. Factory selects integration renderer.
124
+ 2. Integration renderer prepares render options.
125
+ 3. Dependencies are resolved and processed.
126
+ 4. Orchestration assets/artifacts are merged.
127
+ 5. Integration renderer generates HTML body.
128
+ 6. HTML transformer injects head/body dependencies.
129
+ 7. Route result returns body + metadata + cache strategy.
130
+
131
+ ## Notes for Future Work
132
+
133
+ - Expand integration-side marker emission so more nested trees are resolved through graph mode by default.
134
+ - Add broader fixtures/e2e for deep multi-level slot graphs.
135
+ - Add optional batching by integration per graph level to reduce repeated renderer invocations.
@@ -0,0 +1,32 @@
1
+ import type { ComponentMarker } from './component-marker.js';
2
+ import type { ComponentGraph } from './component-graph.js';
3
+ /**
4
+ * Render result returned by a graph node resolver.
5
+ *
6
+ * `html` is inserted in place of the corresponding marker token.
7
+ */
8
+ export type GraphNodeRenderResult = {
9
+ html: string;
10
+ };
11
+ /**
12
+ * Callback used to render one marker node during graph execution.
13
+ *
14
+ * Resolver implementations may call integration-specific `renderComponent`
15
+ * and can use closure state to wire child output into parent render calls.
16
+ */
17
+ export type GraphNodeResolver = (marker: ComponentMarker) => Promise<GraphNodeRenderResult>;
18
+ /**
19
+ * Resolves all markers in bottom-up order based on computed graph levels.
20
+ *
21
+ * Child nodes are resolved first so parent slot content can consume already
22
+ * resolved child HTML in subsequent resolver invocations.
23
+ *
24
+ * If a node id exists in `graph.levels` but no marker is present in `inputHtml`,
25
+ * that node is skipped.
26
+ *
27
+ * @param inputHtml HTML containing marker tokens.
28
+ * @param graph Precomputed marker graph with topological levels.
29
+ * @param resolver Async callback that renders each marker node.
30
+ * @returns HTML with resolved markers replaced.
31
+ */
32
+ export declare function resolveComponentGraph(inputHtml: string, graph: ComponentGraph, resolver: GraphNodeResolver): Promise<string>;
@@ -0,0 +1,31 @@
1
+ import { parseComponentMarkers } from "./component-marker.js";
2
+ function escapeRegex(value) {
3
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
+ }
5
+ function replaceMarkerByNodeId(html, nodeId, replacement) {
6
+ const pattern = `<eco-marker[^>]*data-eco-node-id="${escapeRegex(nodeId)}"[^>]*><\\/eco-marker>`;
7
+ const markerRegex = new RegExp(pattern);
8
+ return html.replace(markerRegex, replacement);
9
+ }
10
+ async function resolveComponentGraph(inputHtml, graph, resolver) {
11
+ let html = inputHtml;
12
+ const markersById = /* @__PURE__ */ new Map();
13
+ for (const marker of parseComponentMarkers(inputHtml)) {
14
+ markersById.set(marker.nodeId, marker);
15
+ }
16
+ const levels = [...graph.levels].reverse();
17
+ for (const level of levels) {
18
+ for (const nodeId of level) {
19
+ const marker = markersById.get(nodeId);
20
+ if (!marker) {
21
+ continue;
22
+ }
23
+ const result = await resolver(marker);
24
+ html = replaceMarkerByNodeId(html, nodeId, result.html);
25
+ }
26
+ }
27
+ return html;
28
+ }
29
+ export {
30
+ resolveComponentGraph
31
+ };
@@ -0,0 +1,84 @@
1
+ import type { ComponentMarker, MarkerNodeId } from './component-marker.ts';
2
+ import { parseComponentMarkers } from './component-marker.ts';
3
+ import type { ComponentGraph } from './component-graph.ts';
4
+
5
+ /**
6
+ * Render result returned by a graph node resolver.
7
+ *
8
+ * `html` is inserted in place of the corresponding marker token.
9
+ */
10
+ export type GraphNodeRenderResult = {
11
+ html: string;
12
+ };
13
+
14
+ /**
15
+ * Callback used to render one marker node during graph execution.
16
+ *
17
+ * Resolver implementations may call integration-specific `renderComponent`
18
+ * and can use closure state to wire child output into parent render calls.
19
+ */
20
+ export type GraphNodeResolver = (marker: ComponentMarker) => Promise<GraphNodeRenderResult>;
21
+
22
+ /**
23
+ * Escapes dynamic content before embedding it in a regular expression.
24
+ *
25
+ * @param value Raw regex input.
26
+ * @returns Escaped regex-safe value.
27
+ */
28
+ function escapeRegex(value: string): string {
29
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
30
+ }
31
+
32
+ /**
33
+ * Replaces exactly one marker token matching the provided node id.
34
+ *
35
+ * @param html Current HTML buffer.
36
+ * @param nodeId Marker node id to replace.
37
+ * @param replacement Rendered HTML replacement.
38
+ * @returns Updated HTML buffer.
39
+ */
40
+ function replaceMarkerByNodeId(html: string, nodeId: MarkerNodeId, replacement: string): string {
41
+ const pattern = `<eco-marker[^>]*data-eco-node-id="${escapeRegex(nodeId)}"[^>]*><\\/eco-marker>`;
42
+ const markerRegex = new RegExp(pattern);
43
+ return html.replace(markerRegex, replacement);
44
+ }
45
+
46
+ /**
47
+ * Resolves all markers in bottom-up order based on computed graph levels.
48
+ *
49
+ * Child nodes are resolved first so parent slot content can consume already
50
+ * resolved child HTML in subsequent resolver invocations.
51
+ *
52
+ * If a node id exists in `graph.levels` but no marker is present in `inputHtml`,
53
+ * that node is skipped.
54
+ *
55
+ * @param inputHtml HTML containing marker tokens.
56
+ * @param graph Precomputed marker graph with topological levels.
57
+ * @param resolver Async callback that renders each marker node.
58
+ * @returns HTML with resolved markers replaced.
59
+ */
60
+ export async function resolveComponentGraph(
61
+ inputHtml: string,
62
+ graph: ComponentGraph,
63
+ resolver: GraphNodeResolver,
64
+ ): Promise<string> {
65
+ let html = inputHtml;
66
+ const markersById = new Map<MarkerNodeId, ComponentMarker>();
67
+ for (const marker of parseComponentMarkers(inputHtml)) {
68
+ markersById.set(marker.nodeId, marker);
69
+ }
70
+
71
+ const levels = [...graph.levels].reverse();
72
+ for (const level of levels) {
73
+ for (const nodeId of level) {
74
+ const marker = markersById.get(nodeId);
75
+ if (!marker) {
76
+ continue;
77
+ }
78
+ const result = await resolver(marker);
79
+ html = replaceMarkerByNodeId(html, nodeId, result.html);
80
+ }
81
+ }
82
+
83
+ return html;
84
+ }