@ecopages/core 0.2.0-beta.2 → 0.2.0-beta.21

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 (273) hide show
  1. package/README.md +1 -1
  2. package/package.json +163 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +104 -5
  4. package/src/adapters/abstract/application-adapter.js +125 -1
  5. package/src/adapters/abstract/server-adapter.d.ts +6 -2
  6. package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
  7. package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
  8. package/src/adapters/bun/client-bridge.d.ts +1 -0
  9. package/src/adapters/bun/client-bridge.js +3 -0
  10. package/src/adapters/bun/create-app.d.ts +6 -4
  11. package/src/adapters/bun/create-app.js +65 -43
  12. package/src/adapters/bun/server-adapter.d.ts +90 -22
  13. package/src/adapters/bun/server-adapter.js +266 -84
  14. package/src/adapters/bun/static-preview-host.js +25 -20
  15. package/src/adapters/create-app.d.ts +13 -11
  16. package/src/adapters/create-app.js +2 -51
  17. package/src/adapters/index.d.ts +1 -1
  18. package/src/adapters/index.js +1 -2
  19. package/src/adapters/node/create-app.d.ts +5 -1
  20. package/src/adapters/node/create-app.js +51 -12
  21. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  22. package/src/adapters/node/node-client-bridge.js +3 -0
  23. package/src/adapters/node/server-adapter-dependencies.js +4 -0
  24. package/src/adapters/node/server-adapter.d.ts +62 -20
  25. package/src/adapters/node/server-adapter.js +160 -131
  26. package/src/adapters/node/static-content-server.d.ts +1 -0
  27. package/src/adapters/node/static-content-server.js +10 -2
  28. package/src/adapters/node/static-preview-host.js +27 -12
  29. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  30. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  31. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  32. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  33. package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
  34. package/src/adapters/shared/fs-server-response-matcher.js +40 -18
  35. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +21 -30
  36. package/src/adapters/shared/hmr-entrypoint-registrar.js +49 -54
  37. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  38. package/src/adapters/shared/hmr-html-response.js +4 -1
  39. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  40. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  41. package/src/adapters/shared/port-manager.d.ts +116 -0
  42. package/src/adapters/shared/port-manager.js +196 -0
  43. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  44. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  45. package/src/adapters/shared/runtime-server-lifecycle.d.ts +46 -0
  46. package/src/adapters/shared/runtime-server-lifecycle.js +50 -0
  47. package/src/adapters/shared/server-adapter.d.ts +2 -1
  48. package/src/adapters/shared/server-adapter.js +13 -6
  49. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  50. package/src/adapters/shared/server-route-handler.js +5 -2
  51. package/src/adapters/shared/server-static-builder.d.ts +23 -32
  52. package/src/adapters/shared/server-static-builder.js +100 -73
  53. package/src/adapters/shared/shared-hmr-manager.d.ts +45 -11
  54. package/src/adapters/shared/shared-hmr-manager.js +204 -58
  55. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  56. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  57. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  58. package/src/build/README.md +77 -14
  59. package/src/build/app-build-manifest-runtime.d.ts +9 -0
  60. package/src/build/app-build-manifest-runtime.js +87 -0
  61. package/src/build/build-adapter.d.ts +17 -319
  62. package/src/build/build-adapter.js +28 -94
  63. package/src/build/build-contracts.d.ts +97 -0
  64. package/src/build/build-contracts.js +0 -0
  65. package/src/build/build-input-fingerprint.d.ts +28 -0
  66. package/src/build/build-input-fingerprint.js +31 -0
  67. package/src/build/build-profile-options.d.ts +7 -0
  68. package/src/build/build-profile-options.js +37 -0
  69. package/src/build/build-runtime.d.ts +24 -0
  70. package/src/build/build-runtime.js +65 -0
  71. package/src/build/deduping-build-executor.d.ts +28 -0
  72. package/src/build/deduping-build-executor.js +56 -0
  73. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  74. package/src/build/jsx-ownership-plugins.js +41 -0
  75. package/src/build/pages-unified-graph-build.d.ts +31 -0
  76. package/src/build/pages-unified-graph-build.js +206 -0
  77. package/src/build/parallel-build-executor.d.ts +21 -0
  78. package/src/build/parallel-build-executor.js +52 -0
  79. package/src/build/production-build-cache.d.ts +20 -0
  80. package/src/build/production-build-cache.js +63 -0
  81. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  82. package/src/build/rolldown-adapter-helpers.js +71 -6
  83. package/src/build/rolldown-build-adapter.d.ts +2 -6
  84. package/src/build/rolldown-build-adapter.js +3 -3
  85. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  86. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  87. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  88. package/src/build/rolldown-plugin-bridge.js +20 -5
  89. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  90. package/src/build/rolldown-source-transform-pass.js +58 -0
  91. package/src/build/runtime-build-executor.d.ts +8 -15
  92. package/src/build/runtime-build-executor.js +12 -15
  93. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  94. package/src/build/runtime-build-output-normalizer.js +94 -2
  95. package/src/build/server-entry-build-cache.d.ts +44 -0
  96. package/src/build/server-entry-build-cache.js +161 -0
  97. package/src/client/navigation-scripts/classification.d.ts +16 -0
  98. package/src/client/navigation-scripts/classification.js +41 -0
  99. package/src/client/navigation-scripts/index.d.ts +3 -0
  100. package/src/client/navigation-scripts/index.js +23 -0
  101. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
  102. package/src/client/navigation-scripts/registry.d.ts +11 -0
  103. package/src/client/navigation-scripts/registry.js +12 -0
  104. package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
  105. package/src/client/navigation-scripts/rerun-queue.js +73 -0
  106. package/src/client/scroll.d.ts +17 -0
  107. package/src/client/scroll.js +25 -0
  108. package/src/client/scroll.test.browser.js +37 -0
  109. package/src/client/view-transitions.d.ts +16 -0
  110. package/src/client/view-transitions.js +63 -0
  111. package/src/config/config-builder.d.ts +12 -7
  112. package/src/config/config-builder.js +12 -9
  113. package/src/dev/client-bridge-registry.d.ts +5 -0
  114. package/src/dev/client-bridge-registry.js +15 -0
  115. package/src/dev/dev-client-ownership.d.ts +7 -0
  116. package/src/dev/dev-client-ownership.js +6 -0
  117. package/src/dev/hmr-manager-registry.d.ts +8 -0
  118. package/src/dev/hmr-manager-registry.js +15 -0
  119. package/src/dev/host-runtime.d.ts +3 -0
  120. package/src/dev/host-runtime.js +7 -0
  121. package/src/dev/runtime-server-started-message.d.ts +2 -0
  122. package/src/dev/runtime-server-started-message.js +6 -0
  123. package/src/eco/README.md +11 -0
  124. package/src/eco/eco-declared-component.d.ts +14 -0
  125. package/src/eco/eco-declared-component.js +16 -0
  126. package/src/eco/eco.browser.js +5 -6
  127. package/src/eco/eco.js +5 -6
  128. package/src/eco/eco.types.d.ts +2 -2
  129. package/src/eco/page-layout-normalization.d.ts +21 -0
  130. package/src/eco/page-layout-normalization.js +59 -0
  131. package/src/errors/index.d.ts +1 -0
  132. package/src/errors/index.js +7 -1
  133. package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
  134. package/src/errors/undeclared-component-dependency-error.js +17 -0
  135. package/src/hmr/client/hmr-runtime.js +5 -2
  136. package/src/hmr/hmr-entrypoint-output.d.ts +43 -0
  137. package/src/hmr/hmr-entrypoint-output.js +53 -0
  138. package/src/hmr/hmr-strategy.d.ts +15 -0
  139. package/src/hmr/hmr-strategy.js +19 -0
  140. package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -5
  141. package/src/hmr/strategies/js-hmr-strategy.js +67 -19
  142. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  143. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  144. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  145. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  146. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  147. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  148. package/src/plugins/define-integration.d.ts +26 -0
  149. package/src/plugins/define-integration.js +19 -0
  150. package/src/plugins/eco-component-meta-plugin.js +5 -8
  151. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  152. package/src/plugins/integration-plugin.d.ts +19 -9
  153. package/src/plugins/integration-plugin.js +7 -4
  154. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  155. package/src/plugins/jsx-import-source.utils.js +26 -0
  156. package/src/plugins/processor.d.ts +19 -4
  157. package/src/plugins/processor.js +22 -4
  158. package/src/plugins/source-transform.d.ts +31 -0
  159. package/src/plugins/source-transform.js +23 -1
  160. package/src/route-renderer/GRAPH.md +8 -8
  161. package/src/route-renderer/README.md +30 -28
  162. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  163. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  164. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  165. package/src/route-renderer/orchestration/component-graph.js +98 -0
  166. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  167. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  168. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  169. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  170. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
  171. package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
  172. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  173. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  174. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  175. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  176. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  177. package/src/route-renderer/orchestration/integration-renderer.js +113 -195
  178. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  179. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  180. package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
  181. package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
  182. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
  183. package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
  184. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  185. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  186. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  187. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  188. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  189. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  190. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  191. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  192. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
  193. package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
  194. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  195. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  196. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
  197. package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
  198. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  199. package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
  200. package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
  201. package/src/router/client/link-intent.d.ts +11 -21
  202. package/src/router/client/link-intent.js +10 -0
  203. package/src/router/client/link-intent.test.browser.js +12 -1
  204. package/src/router/client/link-navigation-policy.d.ts +45 -0
  205. package/src/router/client/link-navigation-policy.js +101 -0
  206. package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
  207. package/src/router/client/link-navigation-policy.test.browser.js +92 -0
  208. package/src/router/client/navigation-coordinator.d.ts +1 -1
  209. package/src/router/client/navigation-coordinator.js +12 -2
  210. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  211. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  212. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  213. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  214. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.d.ts +0 -1
  215. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +18 -15
  216. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  217. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  218. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  219. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  220. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  221. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  222. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  223. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  224. package/src/services/assets/browser-bundle.service.js +17 -5
  225. package/src/services/html/html-transformer.service.d.ts +1 -1
  226. package/src/services/html/html-transformer.service.js +4 -4
  227. package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
  228. package/src/services/invalidation/development-invalidation.service.js +56 -2
  229. package/src/services/module-loading/README.md +49 -0
  230. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  231. package/src/services/module-loading/page-module-import.service.d.ts +5 -1
  232. package/src/services/module-loading/page-module-import.service.js +96 -45
  233. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  234. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  235. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  236. package/src/services/module-loading/route-module-build-cache.js +38 -0
  237. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  238. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  239. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  240. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  241. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  242. package/src/services/module-loading/route-module-dependency-hasher.js +122 -0
  243. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  244. package/src/static-site-generator/README.md +33 -0
  245. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  246. package/src/static-site-generator/static-build-invalidation.js +59 -0
  247. package/src/static-site-generator/static-export-context.d.ts +22 -0
  248. package/src/static-site-generator/static-export-context.js +0 -0
  249. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  250. package/src/static-site-generator/static-site-generator.js +188 -85
  251. package/src/types/internal-types.d.ts +16 -9
  252. package/src/types/public-types.d.ts +144 -40
  253. package/src/utils/parse-cli-args.d.ts +2 -0
  254. package/src/utils/parse-cli-args.js +6 -0
  255. package/src/utils/server-utils.module.d.ts +3 -2
  256. package/src/utils/server-utils.module.js +5 -6
  257. package/src/utils/static-file-extensions.d.ts +12 -0
  258. package/src/utils/static-file-extensions.js +48 -0
  259. package/src/watchers/project-watcher-ignore.d.ts +11 -0
  260. package/src/watchers/project-watcher-ignore.js +16 -0
  261. package/src/watchers/project-watcher.d.ts +26 -3
  262. package/src/watchers/project-watcher.js +132 -30
  263. package/src/watchers/project-watcher.test-helpers.js +5 -5
  264. package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
  265. package/src/build/rolldown-dev-build-adapter.js +0 -166
  266. package/src/hmr/hmr.postcss.test.e2e.js +0 -31
  267. package/src/hmr/hmr.test.e2e.js +0 -43
  268. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
  269. package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
  270. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
  271. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
  272. /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
  273. /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
@@ -0,0 +1,120 @@
1
+ function resolveDocumentShellLayouts(input) {
2
+ if (input.layouts && input.layouts.length > 0) {
3
+ return input.layouts;
4
+ }
5
+ return input.layout ? [input.layout] : [];
6
+ }
7
+ function resolveComponentIntegrationName(component) {
8
+ return component.config?.integration ?? component.config?.__eco?.integration ?? "ecopages";
9
+ }
10
+ async function composeSequentialLayoutChildren(context) {
11
+ if (!context.primaryRender) {
12
+ throw new Error("[ecopages] composeSequentialLayoutChildren requires primaryRender.");
13
+ }
14
+ let children = context.primaryRender.html;
15
+ const layoutRenders = [];
16
+ for (const layout of [...context.layouts].reverse()) {
17
+ const layoutRender = await context.renderComponentWithForeignChildren({
18
+ component: layout.component,
19
+ props: layout.props ?? {},
20
+ children,
21
+ integrationContext: { rendererCache: context.rendererCache }
22
+ });
23
+ layoutRenders.push(layoutRender);
24
+ children = layoutRender.html;
25
+ }
26
+ return {
27
+ children,
28
+ layoutRenders
29
+ };
30
+ }
31
+ async function composeDocumentShell(dependencies, input) {
32
+ const rendererCache = /* @__PURE__ */ new Map();
33
+ const layouts = resolveDocumentShellLayouts(input);
34
+ const composeChildren = input.composeChildren ?? composeSequentialLayoutChildren;
35
+ const usesCustomComposeChildren = input.composeChildren !== void 0;
36
+ let primaryRender;
37
+ let children;
38
+ let layoutRenders;
39
+ if (usesCustomComposeChildren) {
40
+ const composed = await composeChildren({
41
+ primaryComponent: input.primaryComponent,
42
+ primaryProps: input.primaryProps,
43
+ layouts,
44
+ rendererCache,
45
+ renderComponentWithForeignChildren: dependencies.renderComponentWithForeignChildren
46
+ });
47
+ children = composed.children;
48
+ layoutRenders = composed.layoutRenders;
49
+ primaryRender = composed.primaryRender ?? {
50
+ html: typeof composed.children === "string" ? composed.children : "",
51
+ assets: [],
52
+ canAttachAttributes: true,
53
+ rootTag: "main",
54
+ integrationName: resolveComponentIntegrationName(input.primaryComponent)
55
+ };
56
+ } else {
57
+ primaryRender = await dependencies.renderComponentWithForeignChildren({
58
+ component: input.primaryComponent,
59
+ props: input.primaryProps,
60
+ integrationContext: { rendererCache }
61
+ });
62
+ const composed = await composeChildren({
63
+ primaryComponent: input.primaryComponent,
64
+ primaryProps: input.primaryProps,
65
+ primaryRender,
66
+ layouts,
67
+ rendererCache,
68
+ renderComponentWithForeignChildren: dependencies.renderComponentWithForeignChildren
69
+ });
70
+ children = composed.children;
71
+ layoutRenders = composed.layoutRenders;
72
+ }
73
+ const documentRender = await dependencies.renderComponentWithForeignChildren({
74
+ component: input.htmlTemplate,
75
+ props: input.documentProps,
76
+ children,
77
+ integrationContext: { rendererCache }
78
+ });
79
+ dependencies.appendProcessedDependencies(
80
+ primaryRender.assets,
81
+ ...layoutRenders.map((layoutRender) => layoutRender.assets),
82
+ documentRender.assets
83
+ );
84
+ return {
85
+ documentHtml: documentRender.html
86
+ };
87
+ }
88
+ function applyDocumentShellAttributeStamping(html, stamping, options) {
89
+ let nextHtml = html;
90
+ if (options.componentRootAttributes && Object.keys(options.componentRootAttributes).length > 0) {
91
+ nextHtml = stamping.applyAttributesToFirstBodyElement(nextHtml, options.componentRootAttributes);
92
+ }
93
+ if (options.documentAttributes && Object.keys(options.documentAttributes).length > 0) {
94
+ nextHtml = stamping.applyAttributesToHtmlElement(nextHtml, options.documentAttributes);
95
+ }
96
+ return nextHtml;
97
+ }
98
+ async function renderPageDocumentShell(dependencies, input, docType) {
99
+ const { documentHtml: composedDocumentHtml } = await composeDocumentShell(dependencies, {
100
+ primaryComponent: input.page.component,
101
+ primaryProps: input.page.props,
102
+ layout: input.layout,
103
+ layouts: input.layouts,
104
+ composeChildren: input.composeChildren,
105
+ htmlTemplate: input.htmlTemplate,
106
+ documentProps: {
107
+ metadata: input.metadata,
108
+ pageProps: input.pageProps,
109
+ ...input.documentProps ?? {}
110
+ }
111
+ });
112
+ const documentHtml = input.transformDocumentHtml ? input.transformDocumentHtml(composedDocumentHtml) : composedDocumentHtml;
113
+ return `${docType}${documentHtml}`;
114
+ }
115
+ export {
116
+ applyDocumentShellAttributeStamping,
117
+ composeDocumentShell,
118
+ composeSequentialLayoutChildren,
119
+ renderPageDocumentShell
120
+ };
@@ -1,11 +1,36 @@
1
1
  import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
2
2
  import type { BaseIntegrationContext, ComponentRenderInput, ComponentRenderResult, EcoComponent, ForeignSubtreeRenderPayload } from '../../types/public-types.js';
3
3
  import { type ForeignChildRuntime } from './component-render-context.js';
4
- import { QueuedForeignSubtreeResolutionService, type QueuedForeignSubtreeResolution, type QueuedForeignSubtreeResolutionContext } from './queued-foreign-subtree-resolution.service.js';
4
+ export type QueuedForeignChildDecisionInput = {
5
+ currentIntegration: string;
6
+ targetIntegration?: string;
7
+ component: EcoComponent;
8
+ props: Record<string, unknown>;
9
+ };
10
+ export type QueuedForeignSubtreeResolution = {
11
+ token: string;
12
+ component: EcoComponent;
13
+ props: Record<string, unknown>;
14
+ componentInstanceId: string;
15
+ };
16
+ /**
17
+ * Shared mutable state for one renderer-owned queued foreign-subtree runtime.
18
+ */
19
+ export type QueuedForeignSubtreeResolutionContext = {
20
+ rendererCache: Map<string, unknown>;
21
+ componentInstanceScope?: string;
22
+ nextForeignSubtreeId: number;
23
+ queuedResolutions: QueuedForeignSubtreeResolution[];
24
+ };
25
+ type QueuedForeignSubtreeIntegrationContext = BaseIntegrationContext & Record<string, unknown>;
26
+ type QueuedForeignSubtreeChildRenderResult = {
27
+ assets: ProcessedAsset[];
28
+ html?: string;
29
+ children?: unknown;
30
+ };
5
31
  export interface ForeignSubtreeExecutionOwningRenderer {
6
32
  readonly name: string;
7
33
  renderComponentWithForeignChildren(input: ComponentRenderInput): Promise<ComponentRenderResult>;
8
- renderForeignSubtree(input: ComponentRenderInput): Promise<ForeignSubtreeRenderPayload>;
9
34
  }
10
35
  export interface ForeignSubtreeExecutionDecisionInput {
11
36
  currentIntegration: string;
@@ -54,34 +79,41 @@ export interface ForeignSubtreeQueuedHtmlOptions<TContext extends QueuedForeignS
54
79
  applyAttributesToFirstElement(html: string, attributes: Record<string, string>): string;
55
80
  dedupeProcessedAssets(assets: ProcessedAsset[]): ProcessedAsset[];
56
81
  }
82
+ export interface ResolveQueuedForeignSubtreeTokensOptions<TContext extends QueuedForeignSubtreeResolutionContext> {
83
+ html: string;
84
+ runtimeContext?: TContext;
85
+ queueLabel: string;
86
+ renderQueuedChildren: (children: unknown, runtimeContext: TContext, queuedResolutionsByToken: Map<string, QueuedForeignSubtreeResolution>, resolveToken: (token: string) => Promise<string>) => Promise<QueuedForeignSubtreeChildRenderResult>;
87
+ resolveForeignSubtree: (input: ComponentRenderInput, rendererCache: Map<string, unknown>) => Promise<ForeignSubtreeRenderPayload | undefined>;
88
+ applyAttributesToFirstElement: (html: string, attributes: Record<string, string>) => string;
89
+ dedupeProcessedAssets: (assets: ProcessedAsset[]) => ProcessedAsset[];
90
+ }
91
+ /**
92
+ * Maps one component render result into the foreign-subtree payload shape used by
93
+ * queue resolution and integration tests.
94
+ */
95
+ export declare function toForeignSubtreeRenderPayload(result: ComponentRenderResult): ForeignSubtreeRenderPayload;
57
96
  /**
58
97
  * Executes one component render tree under Foreign Child support.
59
98
  *
60
- * This service owns the execution policy for mixed-integration component trees:
61
- * it decides when a child stays inline, when it must delegate to an owning
62
- * renderer, how one render-pass renderer cache is reused, and how queued
63
- * Foreign Subtree tokens resolve back into final HTML.
99
+ * This service owns mixed-integration execution policy and the queued token
100
+ * mechanics renderers use when foreign children cannot resolve inline.
64
101
  */
65
102
  export declare class ForeignSubtreeExecutionService {
66
- private readonly queuedForeignSubtreeResolutionService;
67
- constructor(queuedForeignSubtreeResolutionService?: QueuedForeignSubtreeResolutionService);
68
103
  private requiresForeignChildRuntime;
69
- /**
70
- * Returns whether the current render pass must hand the child off to a foreign owner.
71
- */
72
104
  shouldDelegateForeignChild(input: ForeignSubtreeExecutionDecisionInput): boolean;
73
- /**
74
- * Creates the base runtime used when a renderer has not supplied its own queueing runtime.
75
- *
76
- * The runtime allows same-integration children to continue inline and fails fast
77
- * when execution crosses into a foreign owner without a renderer-owned handoff.
78
- */
79
105
  createFailFastRuntime(rendererName: string): ForeignChildRuntime;
80
106
  getQueuedRuntimeContext<TContext extends QueuedForeignSubtreeResolutionContext>(input: ComponentRenderInput, runtimeContextKey: string): TContext | undefined;
81
107
  createQueuedRuntime<TContext extends QueuedForeignSubtreeResolutionContext>(options: ForeignSubtreeQueuedRuntimeOptions<TContext>): ForeignChildRuntime;
82
- /**
83
- * Resolves a string-first renderer HTML fragment that may contain queued Foreign Subtree tokens.
84
- */
108
+ createQueueRuntime<TContext extends QueuedForeignSubtreeResolutionContext>(options: {
109
+ renderInput: ComponentRenderInput;
110
+ rendererCache: Map<string, unknown>;
111
+ runtimeContextKey: string;
112
+ tokenPrefix: string;
113
+ shouldQueueForeignChild: (input: QueuedForeignChildDecisionInput) => boolean;
114
+ createRuntimeContext?: (integrationContext: QueuedForeignSubtreeIntegrationContext, rendererCache: Map<string, unknown>) => TContext;
115
+ }): ForeignChildRuntime;
116
+ getRuntimeContext<TContext extends QueuedForeignSubtreeResolutionContext>(input: ComponentRenderInput, runtimeContextKey: string): TContext | undefined;
85
117
  resolveStringQueuedHtml<TContext extends QueuedForeignSubtreeResolutionContext>(options: ForeignSubtreeStringQueuedHtmlOptions): Promise<{
86
118
  assets: ProcessedAsset[];
87
119
  html: string;
@@ -90,21 +122,19 @@ export declare class ForeignSubtreeExecutionService {
90
122
  assets: ProcessedAsset[];
91
123
  html: string;
92
124
  }>;
93
- /**
94
- * Executes one component render with Foreign Child support under the current integration.
95
- */
125
+ resolveQueuedForeignSubtreeTokens<TContext extends QueuedForeignSubtreeResolutionContext>(options: ResolveQueuedForeignSubtreeTokensOptions<TContext>): Promise<{
126
+ assets: ProcessedAsset[];
127
+ html: string;
128
+ }>;
96
129
  executeComponentRender(options: ForeignSubtreeExecutionRenderOptions): Promise<ComponentRenderResult>;
97
130
  private getRendererCache;
98
131
  private withRendererCache;
99
- /**
100
- * Returns the delegatable owning renderer integration for one component.
101
- *
102
- * The pseudo `html` integration marks document-shell ownership only and does
103
- * not participate in component-level foreign subtree execution.
104
- */
105
132
  private getForeignOwnerIntegrationName;
106
133
  private resolveForeignChildInOwningRenderer;
107
134
  private resolveForeignSubtreeInOwningRenderer;
108
135
  private runInForeignOwningRenderer;
109
136
  resolveQueuedTokens(html: string, queuedResolutionsByToken: Map<string, QueuedForeignSubtreeResolution>, resolveToken: (token: string) => Promise<string>): Promise<string>;
137
+ private createForeignSubtreeToken;
138
+ private ensureRuntimeContext;
110
139
  }
140
+ export {};
@@ -2,17 +2,18 @@ import {
2
2
  getComponentRenderContext,
3
3
  runWithComponentRenderContext
4
4
  } from "./component-render-context.js";
5
- import {
6
- QueuedForeignSubtreeResolutionService
7
- } from "./queued-foreign-subtree-resolution.service.js";
8
- function isMarkupNodeLike(value) {
9
- return typeof value === "object" && value !== null && "nodeType" in value && typeof value.nodeType === "number" && (!("outerHTML" in value) || typeof value.outerHTML === "string");
5
+ import { isMarkupNodeLike } from "./render-output.utils.js";
6
+ function toForeignSubtreeRenderPayload(result) {
7
+ return {
8
+ html: result.html,
9
+ assets: result.assets ?? [],
10
+ rootTag: result.rootTag,
11
+ rootAttributes: result.rootAttributes,
12
+ attachmentPolicy: result.canAttachAttributes ? { kind: "first-element" } : { kind: "none" },
13
+ integrationName: result.integrationName
14
+ };
10
15
  }
11
16
  class ForeignSubtreeExecutionService {
12
- queuedForeignSubtreeResolutionService;
13
- constructor(queuedForeignSubtreeResolutionService = new QueuedForeignSubtreeResolutionService()) {
14
- this.queuedForeignSubtreeResolutionService = queuedForeignSubtreeResolutionService;
15
- }
16
17
  requiresForeignChildRuntime(input) {
17
18
  const children = input.children ?? input.props.children;
18
19
  if (children === void 0 || children === null) {
@@ -26,18 +27,9 @@ class ForeignSubtreeExecutionService {
26
27
  }
27
28
  return !isMarkupNodeLike(children);
28
29
  }
29
- /**
30
- * Returns whether the current render pass must hand the child off to a foreign owner.
31
- */
32
30
  shouldDelegateForeignChild(input) {
33
31
  return !!input.targetIntegration && input.targetIntegration !== input.currentIntegration;
34
32
  }
35
- /**
36
- * Creates the base runtime used when a renderer has not supplied its own queueing runtime.
37
- *
38
- * The runtime allows same-integration children to continue inline and fails fast
39
- * when execution crosses into a foreign owner without a renderer-owned handoff.
40
- */
41
33
  createFailFastRuntime(rendererName) {
42
34
  const interceptForeignChild = (input) => {
43
35
  if (!this.shouldDelegateForeignChild(input)) {
@@ -56,10 +48,10 @@ class ForeignSubtreeExecutionService {
56
48
  };
57
49
  }
58
50
  getQueuedRuntimeContext(input, runtimeContextKey) {
59
- return this.queuedForeignSubtreeResolutionService.getRuntimeContext(input, runtimeContextKey);
51
+ return this.getRuntimeContext(input, runtimeContextKey);
60
52
  }
61
53
  createQueuedRuntime(options) {
62
- return this.queuedForeignSubtreeResolutionService.createRuntime({
54
+ return this.createQueueRuntime({
63
55
  renderInput: options.renderInput,
64
56
  rendererCache: options.rendererCache,
65
57
  runtimeContextKey: options.runtimeContextKey,
@@ -68,9 +60,42 @@ class ForeignSubtreeExecutionService {
68
60
  createRuntimeContext: options.createRuntimeContext
69
61
  });
70
62
  }
71
- /**
72
- * Resolves a string-first renderer HTML fragment that may contain queued Foreign Subtree tokens.
73
- */
63
+ createQueueRuntime(options) {
64
+ const runtimeContext = this.ensureRuntimeContext(options);
65
+ const interceptForeignChild = (input) => {
66
+ if (!options.shouldQueueForeignChild(input)) {
67
+ return {
68
+ kind: "inline",
69
+ props: { ...input.props }
70
+ };
71
+ }
72
+ runtimeContext.nextForeignSubtreeId += 1;
73
+ const foreignSubtreeId = runtimeContext.nextForeignSubtreeId;
74
+ const token = this.createForeignSubtreeToken(options.tokenPrefix, runtimeContext, foreignSubtreeId);
75
+ runtimeContext.queuedResolutions.push({
76
+ token,
77
+ component: input.component,
78
+ props: { ...input.props },
79
+ componentInstanceId: runtimeContext.componentInstanceScope ? `${runtimeContext.componentInstanceScope}_n_${foreignSubtreeId}` : `n_${foreignSubtreeId}`
80
+ });
81
+ return {
82
+ kind: "resolved",
83
+ value: token
84
+ };
85
+ };
86
+ return {
87
+ interceptForeignChild,
88
+ interceptForeignChildSync: interceptForeignChild
89
+ };
90
+ }
91
+ getRuntimeContext(input, runtimeContextKey) {
92
+ const integrationContext = input.integrationContext;
93
+ const runtimeContext = integrationContext?.[runtimeContextKey];
94
+ if (typeof runtimeContext !== "object" || runtimeContext === null) {
95
+ return void 0;
96
+ }
97
+ return runtimeContext;
98
+ }
74
99
  async resolveStringQueuedHtml(options) {
75
100
  const runtimeContext = this.getQueuedRuntimeContext(options.renderInput, options.runtimeContextKey);
76
101
  return this.resolveQueuedHtml({
@@ -98,7 +123,7 @@ class ForeignSubtreeExecutionService {
98
123
  });
99
124
  }
100
125
  async resolveQueuedHtml(options) {
101
- return this.queuedForeignSubtreeResolutionService.resolveQueuedHtml({
126
+ return this.resolveQueuedForeignSubtreeTokens({
102
127
  html: options.html,
103
128
  runtimeContext: options.runtimeContext,
104
129
  queueLabel: options.queueLabel,
@@ -113,9 +138,93 @@ class ForeignSubtreeExecutionService {
113
138
  dedupeProcessedAssets: options.dedupeProcessedAssets
114
139
  });
115
140
  }
116
- /**
117
- * Executes one component render with Foreign Child support under the current integration.
118
- */
141
+ async resolveQueuedForeignSubtreeTokens(options) {
142
+ if (!options.runtimeContext || options.runtimeContext.queuedResolutions.length === 0) {
143
+ return { assets: [], html: options.html };
144
+ }
145
+ const runtimeContext = options.runtimeContext;
146
+ const queuedResolutionsByToken = /* @__PURE__ */ new Map();
147
+ const resolvedHtmlByToken = /* @__PURE__ */ new Map();
148
+ const resolvingTokens = /* @__PURE__ */ new Set();
149
+ const collectedAssets = [];
150
+ const syncQueuedResolutions = () => {
151
+ for (const resolution of runtimeContext.queuedResolutions) {
152
+ if (!queuedResolutionsByToken.has(resolution.token)) {
153
+ queuedResolutionsByToken.set(resolution.token, resolution);
154
+ }
155
+ }
156
+ };
157
+ const resolveToken = async (token) => {
158
+ syncQueuedResolutions();
159
+ const cachedHtml = resolvedHtmlByToken.get(token);
160
+ if (cachedHtml) {
161
+ return cachedHtml;
162
+ }
163
+ const resolution = queuedResolutionsByToken.get(token);
164
+ if (!resolution) {
165
+ return token;
166
+ }
167
+ if (resolvingTokens.has(token)) {
168
+ throw new Error(
169
+ `[ecopages] ${options.queueLabel} foreign-subtree queue contains a cycle or unresolved dependency links.`
170
+ );
171
+ }
172
+ resolvingTokens.add(token);
173
+ try {
174
+ const renderedChildren = await options.renderQueuedChildren(
175
+ resolution.props.children,
176
+ runtimeContext,
177
+ queuedResolutionsByToken,
178
+ resolveToken
179
+ );
180
+ syncQueuedResolutions();
181
+ if (renderedChildren.assets.length > 0) {
182
+ collectedAssets.push(...renderedChildren.assets);
183
+ }
184
+ const foreignSubtreeRender = await options.resolveForeignSubtree(
185
+ {
186
+ component: resolution.component,
187
+ props: { ...resolution.props },
188
+ children: renderedChildren.html ?? renderedChildren.children,
189
+ integrationContext: {
190
+ rendererCache: runtimeContext.rendererCache,
191
+ componentInstanceId: resolution.componentInstanceId
192
+ }
193
+ },
194
+ runtimeContext.rendererCache
195
+ );
196
+ if (!foreignSubtreeRender) {
197
+ throw new Error(
198
+ `[ecopages] ${options.queueLabel} queued foreign subtree could not resolve its owning renderer.`
199
+ );
200
+ }
201
+ if ((foreignSubtreeRender.assets?.length ?? 0) > 0) {
202
+ collectedAssets.push(...foreignSubtreeRender.assets ?? []);
203
+ }
204
+ const resolvedHtml2 = foreignSubtreeRender.attachmentPolicy.kind === "first-element" && foreignSubtreeRender.rootAttributes ? options.applyAttributesToFirstElement(
205
+ foreignSubtreeRender.html,
206
+ foreignSubtreeRender.rootAttributes
207
+ ) : foreignSubtreeRender.html;
208
+ resolvedHtmlByToken.set(token, resolvedHtml2);
209
+ return resolvedHtml2;
210
+ } finally {
211
+ resolvingTokens.delete(token);
212
+ }
213
+ };
214
+ let resolvedHtml = options.html;
215
+ for (let index = 0; index < runtimeContext.queuedResolutions.length; index += 1) {
216
+ syncQueuedResolutions();
217
+ const resolution = runtimeContext.queuedResolutions[index];
218
+ if (!resolvedHtml.includes(resolution.token)) {
219
+ continue;
220
+ }
221
+ resolvedHtml = resolvedHtml.split(resolution.token).join(await resolveToken(resolution.token));
222
+ }
223
+ return {
224
+ assets: options.dedupeProcessedAssets(collectedAssets),
225
+ html: resolvedHtml
226
+ };
227
+ }
119
228
  async executeComponentRender(options) {
120
229
  const rendererCache = this.getRendererCache(options.input.integrationContext) ?? /* @__PURE__ */ new Map();
121
230
  const delegatedForeignChildRender = await this.resolveForeignChildInOwningRenderer({
@@ -167,12 +276,6 @@ class ForeignSubtreeExecutionService {
167
276
  integrationContext: integrationContext ? { ...integrationContext, rendererCache: sharedRendererCache } : { rendererCache: sharedRendererCache }
168
277
  };
169
278
  }
170
- /**
171
- * Returns the delegatable owning renderer integration for one component.
172
- *
173
- * The pseudo `html` integration marks document-shell ownership only and does
174
- * not participate in component-level foreign subtree execution.
175
- */
176
279
  getForeignOwnerIntegrationName(component, currentIntegrationName) {
177
280
  const integrationName = component.config?.integration ?? component.config?.__eco?.integration;
178
281
  if (!integrationName || integrationName === "html" || integrationName === currentIntegrationName) {
@@ -195,7 +298,7 @@ class ForeignSubtreeExecutionService {
195
298
  input: options.input,
196
299
  rendererCache: options.rendererCache,
197
300
  getOwningRenderer: options.getOwningRenderer,
198
- run: (owningRenderer, delegatedInput) => owningRenderer.renderForeignSubtree(delegatedInput)
301
+ run: async (owningRenderer, delegatedInput) => toForeignSubtreeRenderPayload(await owningRenderer.renderComponentWithForeignChildren(delegatedInput))
199
302
  });
200
303
  }
201
304
  async runInForeignOwningRenderer(options) {
@@ -227,7 +330,33 @@ class ForeignSubtreeExecutionService {
227
330
  }
228
331
  return resolvedHtml;
229
332
  }
333
+ createForeignSubtreeToken(tokenPrefix, runtimeContext, foreignSubtreeId) {
334
+ return `${tokenPrefix}${runtimeContext.componentInstanceScope ?? "root"}__${foreignSubtreeId}__`;
335
+ }
336
+ ensureRuntimeContext(options) {
337
+ let integrationContext;
338
+ if (typeof options.renderInput.integrationContext === "object" && options.renderInput.integrationContext !== null) {
339
+ integrationContext = options.renderInput.integrationContext;
340
+ } else {
341
+ integrationContext = {};
342
+ }
343
+ const existingRuntimeContext = integrationContext[options.runtimeContextKey];
344
+ if (typeof existingRuntimeContext !== "object" || existingRuntimeContext === null) {
345
+ integrationContext[options.runtimeContextKey] = options.createRuntimeContext?.(integrationContext, options.rendererCache) ?? {
346
+ rendererCache: options.rendererCache,
347
+ componentInstanceScope: integrationContext.componentInstanceId,
348
+ nextForeignSubtreeId: 0,
349
+ queuedResolutions: []
350
+ };
351
+ } else {
352
+ existingRuntimeContext.rendererCache = options.rendererCache;
353
+ }
354
+ integrationContext.rendererCache = options.rendererCache;
355
+ options.renderInput.integrationContext = integrationContext;
356
+ return integrationContext[options.runtimeContextKey];
357
+ }
230
358
  }
231
359
  export {
232
- ForeignSubtreeExecutionService
360
+ ForeignSubtreeExecutionService,
361
+ toForeignSubtreeRenderPayload
233
362
  };
@@ -0,0 +1,7 @@
1
+ import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
+ import type { ResolvedLazyTrigger } from '../../types/public-types.js';
3
+ import { type AssetProcessingService, type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
4
+ /**
5
+ * Builds processed global-injector map and bootstrap assets for resolved lazy triggers.
6
+ */
7
+ export declare function buildGlobalInjectorAssets(appConfig: EcoPagesAppConfig, assetProcessingService: AssetProcessingService, triggers: ResolvedLazyTrigger[], currentIntegrationName: string): Promise<ProcessedAsset[]>;
@@ -0,0 +1,32 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import {
4
+ AssetFactory
5
+ } from "../../services/assets/asset-processing-service/index.js";
6
+ import { buildGlobalInjectorBootstrapContent, buildGlobalInjectorMapScript } from "../../eco/global-injector-map.js";
7
+ async function buildGlobalInjectorAssets(appConfig, assetProcessingService, triggers, currentIntegrationName) {
8
+ const appProjectDir = appConfig.rootDir ?? appConfig.absolutePaths?.projectDir ?? process.cwd();
9
+ const appPackageRequire = createRequire(path.join(appProjectDir, "package.json"));
10
+ const corePackageEntryPath = appPackageRequire.resolve("@ecopages/core");
11
+ const globalInjectorImportPath = createRequire(corePackageEntryPath).resolve("@ecopages/scripts-injector/global");
12
+ const mapScript = AssetFactory.createInlineContentScript({
13
+ position: "head",
14
+ name: "ecopages-global-injector-map",
15
+ content: buildGlobalInjectorMapScript(triggers),
16
+ attributes: { type: "ecopages/global-injector-map" },
17
+ packageRole: "keep-separate",
18
+ bundle: false
19
+ });
20
+ const bootstrapInlineScript = AssetFactory.createInlineContentScript({
21
+ position: "head",
22
+ name: "ecopages-global-injector-bootstrap",
23
+ content: buildGlobalInjectorBootstrapContent(globalInjectorImportPath),
24
+ attributes: { type: "module" },
25
+ packageRole: "keep-separate",
26
+ bundle: true
27
+ });
28
+ return assetProcessingService.processDependencies([mapScript, bootstrapInlineScript], currentIntegrationName);
29
+ }
30
+ export {
31
+ buildGlobalInjectorAssets
32
+ };