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

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/hmr-manager.d.ts +2 -1
  13. package/src/adapters/bun/hmr-manager.js +2 -2
  14. package/src/adapters/bun/server-adapter.d.ts +90 -22
  15. package/src/adapters/bun/server-adapter.js +265 -84
  16. package/src/adapters/bun/static-preview-host.js +25 -20
  17. package/src/adapters/create-app.d.ts +13 -11
  18. package/src/adapters/create-app.js +2 -51
  19. package/src/adapters/index.d.ts +1 -1
  20. package/src/adapters/index.js +1 -2
  21. package/src/adapters/node/create-app.d.ts +5 -1
  22. package/src/adapters/node/create-app.js +51 -12
  23. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  24. package/src/adapters/node/node-client-bridge.js +3 -0
  25. package/src/adapters/node/node-hmr-manager.d.ts +2 -1
  26. package/src/adapters/node/node-hmr-manager.js +2 -2
  27. package/src/adapters/node/server-adapter-dependencies.js +4 -0
  28. package/src/adapters/node/server-adapter.d.ts +62 -20
  29. package/src/adapters/node/server-adapter.js +161 -131
  30. package/src/adapters/node/static-content-server.d.ts +1 -0
  31. package/src/adapters/node/static-content-server.js +10 -2
  32. package/src/adapters/node/static-preview-host.js +27 -12
  33. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  34. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  35. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  36. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  37. package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
  38. package/src/adapters/shared/fs-server-response-matcher.js +40 -18
  39. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +0 -2
  40. package/src/adapters/shared/hmr-entrypoint-registrar.js +3 -26
  41. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  42. package/src/adapters/shared/hmr-html-response.js +4 -1
  43. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  44. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  45. package/src/adapters/shared/port-manager.d.ts +116 -0
  46. package/src/adapters/shared/port-manager.js +196 -0
  47. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  48. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  49. package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
  50. package/src/adapters/shared/runtime-server-lifecycle.js +43 -0
  51. package/src/adapters/shared/server-adapter.d.ts +2 -1
  52. package/src/adapters/shared/server-adapter.js +13 -6
  53. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  54. package/src/adapters/shared/server-route-handler.js +5 -2
  55. package/src/adapters/shared/server-static-builder.d.ts +23 -32
  56. package/src/adapters/shared/server-static-builder.js +97 -73
  57. package/src/adapters/shared/shared-hmr-manager.d.ts +28 -0
  58. package/src/adapters/shared/shared-hmr-manager.js +119 -9
  59. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  60. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  61. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  62. package/src/build/README.md +77 -14
  63. package/src/build/app-build-manifest-runtime.d.ts +10 -0
  64. package/src/build/app-build-manifest-runtime.js +90 -0
  65. package/src/build/build-adapter.d.ts +17 -319
  66. package/src/build/build-adapter.js +28 -94
  67. package/src/build/build-contracts.d.ts +97 -0
  68. package/src/build/build-contracts.js +0 -0
  69. package/src/build/build-input-fingerprint.d.ts +28 -0
  70. package/src/build/build-input-fingerprint.js +31 -0
  71. package/src/build/build-profile-options.d.ts +7 -0
  72. package/src/build/build-profile-options.js +37 -0
  73. package/src/build/build-runtime.d.ts +24 -0
  74. package/src/build/build-runtime.js +65 -0
  75. package/src/build/deduping-build-executor.d.ts +28 -0
  76. package/src/build/deduping-build-executor.js +56 -0
  77. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  78. package/src/build/jsx-ownership-plugins.js +41 -0
  79. package/src/build/pages-unified-graph-build.d.ts +31 -0
  80. package/src/build/pages-unified-graph-build.js +206 -0
  81. package/src/build/parallel-build-executor.d.ts +21 -0
  82. package/src/build/parallel-build-executor.js +52 -0
  83. package/src/build/production-build-cache.d.ts +20 -0
  84. package/src/build/production-build-cache.js +63 -0
  85. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  86. package/src/build/rolldown-adapter-helpers.js +71 -6
  87. package/src/build/rolldown-build-adapter.d.ts +2 -6
  88. package/src/build/rolldown-build-adapter.js +3 -3
  89. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  90. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  91. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  92. package/src/build/rolldown-plugin-bridge.js +20 -5
  93. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  94. package/src/build/rolldown-source-transform-pass.js +58 -0
  95. package/src/build/runtime-build-executor.d.ts +8 -15
  96. package/src/build/runtime-build-executor.js +12 -15
  97. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  98. package/src/build/runtime-build-output-normalizer.js +94 -2
  99. package/src/build/server-entry-build-cache.d.ts +44 -0
  100. package/src/build/server-entry-build-cache.js +161 -0
  101. package/src/client/navigation-scripts/classification.d.ts +16 -0
  102. package/src/client/navigation-scripts/classification.js +41 -0
  103. package/src/client/navigation-scripts/index.d.ts +3 -0
  104. package/src/client/navigation-scripts/index.js +23 -0
  105. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
  106. package/src/client/navigation-scripts/registry.d.ts +11 -0
  107. package/src/client/navigation-scripts/registry.js +12 -0
  108. package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
  109. package/src/client/navigation-scripts/rerun-queue.js +73 -0
  110. package/src/client/scroll.d.ts +17 -0
  111. package/src/client/scroll.js +25 -0
  112. package/src/client/scroll.test.browser.js +37 -0
  113. package/src/client/view-transitions.d.ts +16 -0
  114. package/src/client/view-transitions.js +63 -0
  115. package/src/config/config-builder.d.ts +12 -7
  116. package/src/config/config-builder.js +12 -9
  117. package/src/dev/client-bridge-registry.d.ts +5 -0
  118. package/src/dev/client-bridge-registry.js +15 -0
  119. package/src/dev/dev-client-ownership.d.ts +7 -0
  120. package/src/dev/dev-client-ownership.js +6 -0
  121. package/src/dev/hmr-manager-registry.d.ts +8 -0
  122. package/src/dev/hmr-manager-registry.js +15 -0
  123. package/src/dev/host-runtime.d.ts +3 -0
  124. package/src/dev/host-runtime.js +7 -0
  125. package/src/dev/runtime-server-started-message.d.ts +2 -0
  126. package/src/dev/runtime-server-started-message.js +6 -0
  127. package/src/eco/README.md +11 -0
  128. package/src/eco/eco-declared-component.d.ts +14 -0
  129. package/src/eco/eco-declared-component.js +16 -0
  130. package/src/eco/eco.browser.js +5 -6
  131. package/src/eco/eco.js +5 -6
  132. package/src/eco/eco.types.d.ts +2 -2
  133. package/src/eco/page-layout-normalization.d.ts +21 -0
  134. package/src/eco/page-layout-normalization.js +59 -0
  135. package/src/errors/index.d.ts +1 -0
  136. package/src/errors/index.js +7 -1
  137. package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
  138. package/src/errors/undeclared-component-dependency-error.js +17 -0
  139. package/src/hmr/client/hmr-runtime.js +5 -2
  140. package/src/hmr/hmr-entrypoint-output.d.ts +28 -0
  141. package/src/hmr/hmr-entrypoint-output.js +58 -0
  142. package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -0
  143. package/src/hmr/strategies/js-hmr-strategy.js +68 -18
  144. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  145. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  146. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  147. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  148. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  149. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  150. package/src/plugins/define-integration.d.ts +26 -0
  151. package/src/plugins/define-integration.js +19 -0
  152. package/src/plugins/eco-component-meta-plugin.js +5 -8
  153. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  154. package/src/plugins/integration-plugin.d.ts +19 -9
  155. package/src/plugins/integration-plugin.js +7 -4
  156. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  157. package/src/plugins/jsx-import-source.utils.js +26 -0
  158. package/src/plugins/processor.d.ts +19 -4
  159. package/src/plugins/processor.js +22 -4
  160. package/src/plugins/source-transform.d.ts +31 -0
  161. package/src/plugins/source-transform.js +23 -1
  162. package/src/route-renderer/GRAPH.md +8 -8
  163. package/src/route-renderer/README.md +30 -28
  164. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  165. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  166. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  167. package/src/route-renderer/orchestration/component-graph.js +98 -0
  168. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  169. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  170. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  171. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  172. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
  173. package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
  174. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  175. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  176. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  177. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  178. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  179. package/src/route-renderer/orchestration/integration-renderer.js +113 -195
  180. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  181. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  182. package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
  183. package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
  184. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
  185. package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
  186. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  187. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  188. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  189. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  190. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  191. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  192. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  193. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  194. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
  195. package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
  196. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  197. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  198. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
  199. package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
  200. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  201. package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
  202. package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
  203. package/src/router/client/link-intent.d.ts +11 -21
  204. package/src/router/client/link-intent.js +10 -0
  205. package/src/router/client/link-intent.test.browser.js +12 -1
  206. package/src/router/client/link-navigation-policy.d.ts +45 -0
  207. package/src/router/client/link-navigation-policy.js +101 -0
  208. package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
  209. package/src/router/client/link-navigation-policy.test.browser.js +92 -0
  210. package/src/router/client/navigation-coordinator.d.ts +1 -1
  211. package/src/router/client/navigation-coordinator.js +12 -2
  212. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  213. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  214. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  215. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  216. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +36 -14
  217. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  218. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  219. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  220. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  221. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  222. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  223. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  224. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  225. package/src/services/assets/browser-bundle.service.js +17 -5
  226. package/src/services/html/html-transformer.service.d.ts +1 -1
  227. package/src/services/html/html-transformer.service.js +4 -4
  228. package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
  229. package/src/services/invalidation/development-invalidation.service.js +56 -2
  230. package/src/services/module-loading/README.md +49 -0
  231. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  232. package/src/services/module-loading/page-module-import.service.d.ts +5 -1
  233. package/src/services/module-loading/page-module-import.service.js +97 -45
  234. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  235. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  236. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  237. package/src/services/module-loading/route-module-build-cache.js +38 -0
  238. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  239. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  240. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  241. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  242. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  243. package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
  244. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  245. package/src/static-site-generator/README.md +33 -0
  246. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  247. package/src/static-site-generator/static-build-invalidation.js +59 -0
  248. package/src/static-site-generator/static-export-context.d.ts +22 -0
  249. package/src/static-site-generator/static-export-context.js +0 -0
  250. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  251. package/src/static-site-generator/static-site-generator.js +188 -85
  252. package/src/types/internal-types.d.ts +16 -9
  253. package/src/types/public-types.d.ts +138 -26
  254. package/src/utils/parse-cli-args.d.ts +2 -0
  255. package/src/utils/parse-cli-args.js +6 -0
  256. package/src/utils/server-utils.module.d.ts +3 -2
  257. package/src/utils/server-utils.module.js +5 -6
  258. package/src/utils/static-file-extensions.d.ts +12 -0
  259. package/src/utils/static-file-extensions.js +48 -0
  260. package/src/watchers/project-watcher-ignore.d.ts +11 -0
  261. package/src/watchers/project-watcher-ignore.js +16 -0
  262. package/src/watchers/project-watcher.d.ts +25 -2
  263. package/src/watchers/project-watcher.js +130 -23
  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
@@ -7,18 +7,23 @@ import { HttpError } from "../../errors/http-error.js";
7
7
  import { DependencyResolverService } from "../page-loading/dependency-resolver.js";
8
8
  import { PageModuleLoaderService } from "../page-loading/page-module-loader.js";
9
9
  import { OwnershipValidationService } from "./ownership-validation.service.js";
10
+ import { hasForeignChildDescendantsInGraph } from "./component-graph-collectors.js";
10
11
  import {
11
12
  RouteRenderOrchestrator
12
13
  } from "./route-render-orchestrator.js";
13
- import { normalizeUnresolvedMarkerArtifactHtml } from "./render-output.utils.js";
14
+ import { createIntegrationRouteRenderAdapter } from "./integration-route-render-adapter.js";
15
+ import { loadPageBrowserGraphContribution } from "./page-browser-graph-contribution.loader.js";
16
+ import { normalizeUnresolvedMarkerArtifactHtml, isMarkupNodeLike } from "./render-output.utils.js";
14
17
  import {
15
18
  ForeignSubtreeExecutionService
16
19
  } from "./foreign-subtree-execution.service.js";
17
- import {} from "./queued-foreign-subtree-resolution.service.js";
18
20
  import { buildProcessedAssetDedupeKey } from "./processed-asset-dedupe.js";
19
- function isMarkupNodeLike(value) {
20
- return typeof value === "object" && value !== null && "nodeType" in value && typeof value.nodeType === "number" && "outerHTML" in value && typeof value.outerHTML === "string";
21
- }
21
+ import {
22
+ applyDocumentShellAttributeStamping,
23
+ composeDocumentShell,
24
+ renderPageDocumentShell
25
+ } from "./document-shell-render.service.js";
26
+ import { resolveInnermostPageLayout, resolvePageLayoutComponents } from "./layout-shell-props.service.js";
22
27
  class IntegrationRenderer {
23
28
  appConfig;
24
29
  assetProcessingService;
@@ -31,7 +36,22 @@ class IntegrationRenderer {
31
36
  pageModuleLoaderService;
32
37
  routeRenderOrchestrator;
33
38
  foreignSubtreeExecutionService = new ForeignSubtreeExecutionService();
39
+ /**
40
+ * Serializes route and view renders that mutate `htmlTransformer` state.
41
+ *
42
+ * Integration renderers are cached per integration, so concurrent static builds
43
+ * and overlapping SSR requests must not share one transformer page package.
44
+ */
45
+ renderExclusiveChain = Promise.resolve();
34
46
  DOC_TYPE = "<!DOCTYPE html>";
47
+ runRenderExclusive(operation) {
48
+ const run = this.renderExclusiveChain.then(operation, operation);
49
+ this.renderExclusiveChain = run.then(
50
+ () => void 0,
51
+ () => void 0
52
+ );
53
+ return run;
54
+ }
35
55
  /**
36
56
  * Loads one route module through the owning renderer's import path.
37
57
  *
@@ -43,52 +63,6 @@ class IntegrationRenderer {
43
63
  async loadPageModule(file, options) {
44
64
  return this.importPageFile(file, options);
45
65
  }
46
- /**
47
- * Reads the execution-scoped owning-renderer cache from one render input.
48
- *
49
- * Shared page/layout/document shell helpers pass one cache through
50
- * `integrationContext` so repeated delegation to the same foreign integration
51
- * can reuse a single initialized renderer instance during one render flow.
52
- * The cache is deliberately scoped to the current render execution rather than
53
- * stored on the renderer, which avoids leaking mutable integration state across
54
- * requests while still preventing redundant renderer initialization.
55
- *
56
- * @param integrationContext - Optional render context carried with one render input.
57
- * @returns The current execution cache when present.
58
- */
59
- getOwningRendererCache(integrationContext) {
60
- if (integrationContext?.rendererCache instanceof Map) {
61
- return integrationContext.rendererCache;
62
- }
63
- return void 0;
64
- }
65
- getForeignOwnerIntegrationName(component) {
66
- const integrationName = component.config?.integration ?? component.config?.__eco?.integration;
67
- if (!integrationName || integrationName === this.name) {
68
- return void 0;
69
- }
70
- return this.appConfig.integrations.some((integration) => integration.name === integrationName) ? integrationName : void 0;
71
- }
72
- /**
73
- * Attaches an execution-scoped owning-renderer cache to one render input.
74
- *
75
- * Foreign-owned page, layout, or document shells may delegate several times in
76
- * the same render flow. Threading the cache through `integrationContext`
77
- * preserves renderer reuse without changing the public render input contract.
78
- * Existing integration-specific context is preserved and augmented.
79
- *
80
- * @param input - Original render input.
81
- * @param rendererCache - Execution-scoped renderer cache to propagate.
82
- * @returns Render input augmented with the shared renderer cache.
83
- */
84
- withOwningRendererCache(input, rendererCache) {
85
- const integrationContext = input.integrationContext;
86
- const sharedRendererCache = rendererCache;
87
- return {
88
- ...input,
89
- integrationContext: integrationContext ? { ...integrationContext, rendererCache: sharedRendererCache } : { rendererCache: sharedRendererCache }
90
- };
91
- }
92
66
  getRendererModuleValue(key) {
93
67
  if (!this.rendererModules || typeof this.rendererModules !== "object") {
94
68
  return void 0;
@@ -184,10 +158,11 @@ class IntegrationRenderer {
184
158
  return await this.routeRenderOrchestrator.resolveDeclaredPageBrowserGraph({
185
159
  routeFile: filePath,
186
160
  integrationName: this.name,
187
- collectContribution: async (routeFile) => {
188
- const pageModule = await this.importPageFile(routeFile);
189
- return await this.collectPageBrowserGraphContribution({ file: routeFile, pageModule });
190
- }
161
+ collectContribution: (routeFile) => loadPageBrowserGraphContribution(
162
+ routeFile,
163
+ (targetFile) => this.importPageFile(targetFile),
164
+ (context) => this.collectPageBrowserGraphContribution(context)
165
+ )
191
166
  });
192
167
  }
193
168
  mergePageBrowserGraphIntoPagePackage(pageBrowserGraph) {
@@ -313,19 +288,25 @@ class IntegrationRenderer {
313
288
  await this.prepareViewDependencies(input.view, input.layout);
314
289
  const HtmlTemplate = await this.getHtmlTemplate();
315
290
  const metadata = await this.resolveViewMetadata(input.view, input.props);
316
- const { documentHtml } = await this.composeDocumentShell({
317
- primaryComponent: input.view,
318
- primaryProps: normalizedProps,
319
- layout: input.layout ? {
320
- component: input.layout,
321
- props: {}
322
- } : void 0,
323
- htmlTemplate: HtmlTemplate,
324
- documentProps: {
325
- metadata,
326
- pageProps: normalizedProps
291
+ const { documentHtml } = await composeDocumentShell(
292
+ {
293
+ renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
294
+ appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups)
295
+ },
296
+ {
297
+ primaryComponent: input.view,
298
+ primaryProps: normalizedProps,
299
+ layout: input.layout ? {
300
+ component: input.layout,
301
+ props: {}
302
+ } : void 0,
303
+ htmlTemplate: HtmlTemplate,
304
+ documentProps: {
305
+ metadata,
306
+ pageProps: normalizedProps
307
+ }
327
308
  }
328
- });
309
+ );
329
310
  const html = await this.finalizeResolvedHtml({
330
311
  html: `${this.DOC_TYPE}${documentHtml}`,
331
312
  partial: false
@@ -345,59 +326,17 @@ class IntegrationRenderer {
345
326
  * @returns Final serialized document HTML including the doctype prefix.
346
327
  */
347
328
  async renderPageWithDocumentShell(input) {
348
- const { documentHtml: composedDocumentHtml } = await this.composeDocumentShell({
349
- primaryComponent: input.page.component,
350
- primaryProps: input.page.props,
351
- layout: input.layout,
352
- htmlTemplate: input.htmlTemplate,
353
- documentProps: {
354
- metadata: input.metadata,
355
- pageProps: input.pageProps,
356
- ...input.documentProps ?? {}
357
- }
358
- });
359
- const documentHtml = input.transformDocumentHtml ? input.transformDocumentHtml(composedDocumentHtml) : composedDocumentHtml;
360
- return `${this.DOC_TYPE}${documentHtml}`;
361
- }
362
- async composeDocumentShell(input) {
363
- const rendererCache = /* @__PURE__ */ new Map();
364
- const primaryRender = await this.renderComponentWithForeignChildren({
365
- component: input.primaryComponent,
366
- props: input.primaryProps,
367
- integrationContext: { rendererCache }
368
- });
369
- const layoutRender = input.layout ? await this.renderComponentWithForeignChildren({
370
- component: input.layout.component,
371
- props: input.layout.props ?? {},
372
- children: primaryRender.html,
373
- integrationContext: { rendererCache }
374
- }) : void 0;
375
- const documentRender = await this.renderComponentWithForeignChildren({
376
- component: input.htmlTemplate,
377
- props: input.documentProps,
378
- children: layoutRender?.html ?? primaryRender.html,
379
- integrationContext: { rendererCache }
380
- });
381
- this.appendProcessedDependencies(primaryRender.assets, layoutRender?.assets, documentRender.assets);
382
- return {
383
- documentHtml: documentRender.html
384
- };
329
+ return renderPageDocumentShell(
330
+ {
331
+ renderComponentWithForeignChildren: (renderInput) => this.renderComponentWithForeignChildren(renderInput),
332
+ appendProcessedDependencies: (...assetGroups) => this.appendProcessedDependencies(...assetGroups)
333
+ },
334
+ input,
335
+ this.DOC_TYPE
336
+ );
385
337
  }
386
- /**
387
- * Renders one string-first component with serialized children and collects its assets.
388
- *
389
- * String-oriented integrations frequently share the same component contract:
390
- * pass serialized children through props, coerce the render result to HTML, and
391
- * attach any component-scoped dependencies. This helper centralizes that flow
392
- * so integrations can opt into shared orchestration without repeating the same
393
- * string-render boilerplate.
394
- *
395
- * @param input - Component render input.
396
- * @param component - String-oriented component implementation to execute.
397
- * @returns Structured component render result for orchestration paths.
398
- */
399
338
  async renderStringComponentWithSerializedChildren(input, component) {
400
- const serializedChildren = input.children === void 0 ? void 0 : typeof input.children === "string" ? input.children : isMarkupNodeLike(input.children) ? input.children.outerHTML : void 0;
339
+ const serializedChildren = input.children === void 0 ? void 0 : typeof input.children === "string" ? input.children : isMarkupNodeLike(input.children) && typeof input.children.outerHTML === "string" ? input.children.outerHTML : void 0;
401
340
  if (input.children !== void 0 && serializedChildren === void 0) {
402
341
  const componentFile = input.component.config?.__eco?.file ?? "unknown component";
403
342
  const childTag = Object.prototype.toString.call(input.children);
@@ -438,6 +377,18 @@ class IntegrationRenderer {
438
377
  this.getForeignSubtreeResolutionContextKey()
439
378
  );
440
379
  }
380
+ async resolveQueuedForeignSubtreeHtml(html, runtimeContext, renderQueuedChildren, queueLabel = this.name) {
381
+ return this.foreignSubtreeExecutionService.resolveQueuedHtml({
382
+ currentIntegrationName: this.name,
383
+ html,
384
+ runtimeContext,
385
+ queueLabel,
386
+ getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
387
+ applyAttributesToFirstElement: (resolvedHtml, attributes) => this.htmlTransformer.applyAttributesToFirstElement(resolvedHtml, attributes),
388
+ dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets),
389
+ renderQueuedChildren
390
+ });
391
+ }
441
392
  /**
442
393
  * Renders a string-first component, then resolves any queued foreign
443
394
  * boundaries before returning final component HTML.
@@ -533,7 +484,7 @@ class IntegrationRenderer {
533
484
  * @returns The imported module.
534
485
  */
535
486
  async importPageFile(file, options) {
536
- const bypassCache = options?.bypassCache ?? (typeof Bun !== "undefined" && process.env.NODE_ENV === "development");
487
+ const bypassCache = options?.bypassCache ?? false;
537
488
  const pageModule = this.usesIntegrationPageImporter(file) ? await this.importIntegrationPageFile(file, {
538
489
  bypassCache,
539
490
  cacheScope: options?.cacheScope
@@ -622,19 +573,20 @@ class IntegrationRenderer {
622
573
  * subclasses continue to override protected renderer behavior directly.
623
574
  */
624
575
  createRouteRenderOrchestratorAdapter() {
625
- return {
576
+ return createIntegrationRouteRenderAdapter({
626
577
  name: this.name,
627
578
  resolveRouteRenderInputs: (routeOptions) => this.resolveRouteRenderInputs(routeOptions),
628
579
  resolveRouteDependencies: (input) => this.resolveRouteDependencies(input),
629
- collectPageBrowserGraphContribution: async (routeFile) => {
630
- const pageModule = await this.importPageFile(routeFile);
631
- return await this.collectPageBrowserGraphContribution({ file: routeFile, pageModule });
632
- },
580
+ importPageFile: (file) => this.importPageFile(file),
581
+ collectPageBrowserGraphContribution: (context) => this.collectPageBrowserGraphContribution(context),
633
582
  resolveRoutePageComponentRender: (input) => this.resolveRoutePageComponentRender(input),
634
583
  renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
635
- getRouteHtmlFinalization: (renderOptions) => this.getRouteHtmlFinalization(renderOptions),
636
- transformRouteResponse: (response, htmlContributions) => this.transformRouteResponse(response, htmlContributions)
637
- };
584
+ getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
585
+ getHtmlDocumentContributions: (options) => this.getHtmlDocumentContributions(options),
586
+ applyAttributesToFirstBodyElement: (html, attributes) => this.applyAttributesToFirstBodyElement(html, attributes),
587
+ applyAttributesToHtmlElement: (html, attributes) => this.applyAttributesToHtmlElement(html, attributes),
588
+ transformRouteResponse: (response, htmlContributions, pagePackage) => this.transformRouteResponse(response, htmlContributions, pagePackage)
589
+ });
638
590
  }
639
591
  async resolveRouteRenderInputs(routeOptions) {
640
592
  const pageModule = await this.pageModuleLoaderService.resolvePageModule({
@@ -643,7 +595,8 @@ class IntegrationRenderer {
643
595
  });
644
596
  const { Page, integrationSpecificProps } = pageModule;
645
597
  const HtmlTemplate = await this.getHtmlTemplate();
646
- const Layout = Page.config?.layout;
598
+ const Layouts = resolvePageLayoutComponents(Page.config?.layouts);
599
+ const Layout = resolveInnermostPageLayout(Layouts);
647
600
  const { props, metadata } = await this.pageModuleLoaderService.resolvePageData({
648
601
  pageModule,
649
602
  routeOptions
@@ -651,7 +604,9 @@ class IntegrationRenderer {
651
604
  return {
652
605
  Page,
653
606
  HtmlTemplate,
607
+ Layouts,
654
608
  Layout,
609
+ layoutEntries: Page.config?.layoutEntries,
655
610
  props,
656
611
  metadata,
657
612
  integrationSpecificProps
@@ -681,33 +636,13 @@ class IntegrationRenderer {
681
636
  async renderRouteBody(renderOptions) {
682
637
  return this.render(renderOptions);
683
638
  }
684
- getRouteHtmlFinalization(renderOptions) {
685
- const componentRootAttributes = renderOptions.componentRender?.canAttachAttributes && renderOptions.componentRender.rootAttributes && Object.keys(renderOptions.componentRender.rootAttributes).length > 0 ? renderOptions.componentRender.rootAttributes : void 0;
686
- const documentAttributes = this.getDocumentAttributes(renderOptions);
687
- const htmlContributions = this.getHtmlDocumentContributions({ renderOptions, partial: false });
688
- const hasStructuralFinalization = componentRootAttributes && Object.keys(componentRootAttributes).length > 0 || documentAttributes && Object.keys(documentAttributes).length > 0;
689
- if (!hasStructuralFinalization && (!htmlContributions || htmlContributions.length === 0)) {
690
- return {};
691
- }
692
- return {
639
+ async transformRouteResponse(response, htmlContributions, pagePackage) {
640
+ const resolvedPagePackage = this.htmlTransformer.getPagePackage() ?? pagePackage;
641
+ const transformedResponse = await this.htmlTransformer.transform(
642
+ response,
693
643
  htmlContributions,
694
- finalizeHtml: (html) => {
695
- let renderedHtml = html;
696
- if (componentRootAttributes) {
697
- renderedHtml = this.htmlTransformer.applyAttributesToFirstBodyElement(
698
- renderedHtml,
699
- componentRootAttributes
700
- );
701
- }
702
- if (documentAttributes) {
703
- renderedHtml = this.htmlTransformer.applyAttributesToHtmlElement(renderedHtml, documentAttributes);
704
- }
705
- return renderedHtml;
706
- }
707
- };
708
- }
709
- async transformRouteResponse(response, htmlContributions) {
710
- const transformedResponse = await this.htmlTransformer.transform(response, htmlContributions);
644
+ resolvedPagePackage
645
+ );
711
646
  return transformedResponse.body ?? await transformedResponse.text();
712
647
  }
713
648
  /**
@@ -752,9 +687,12 @@ class IntegrationRenderer {
752
687
  * @returns Rendered route body plus effective cache strategy.
753
688
  */
754
689
  async execute(options) {
755
- const adapter = this.createRouteRenderOrchestratorAdapter();
756
- const renderOptions = await this.prepareRenderOptions(options, adapter);
757
- return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
690
+ return this.runRenderExclusive(async () => {
691
+ this.htmlTransformer.setProcessedDependencies([]);
692
+ const adapter = this.createRouteRenderOrchestratorAdapter();
693
+ const renderOptions = await this.prepareRenderOptions(options, adapter);
694
+ return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
695
+ });
758
696
  }
759
697
  /**
760
698
  * Finalizes already-resolved HTML for explicit renderer-owned paths.
@@ -766,13 +704,17 @@ class IntegrationRenderer {
766
704
  async finalizeResolvedHtml(options) {
767
705
  const rendererBootstrapDependencies = this.getRendererBootstrapDependencies(options.partial);
768
706
  this.appendProcessedDependencies(rendererBootstrapDependencies);
769
- let html = options.html;
770
- if (options.componentRootAttributes && Object.keys(options.componentRootAttributes).length > 0) {
771
- html = this.htmlTransformer.applyAttributesToFirstBodyElement(html, options.componentRootAttributes);
772
- }
773
- if (options.documentAttributes && Object.keys(options.documentAttributes).length > 0) {
774
- html = this.htmlTransformer.applyAttributesToHtmlElement(html, options.documentAttributes);
775
- }
707
+ let html = applyDocumentShellAttributeStamping(
708
+ options.html,
709
+ {
710
+ applyAttributesToFirstBodyElement: (nextHtml, attributes) => this.htmlTransformer.applyAttributesToFirstBodyElement(nextHtml, attributes),
711
+ applyAttributesToHtmlElement: (nextHtml, attributes) => this.htmlTransformer.applyAttributesToHtmlElement(nextHtml, attributes)
712
+ },
713
+ {
714
+ componentRootAttributes: options.componentRootAttributes,
715
+ documentAttributes: options.documentAttributes
716
+ }
717
+ );
776
718
  const shouldTransform = options.transformHtml ?? !options.partial;
777
719
  if (!shouldTransform) {
778
720
  return html;
@@ -797,6 +739,12 @@ class IntegrationRenderer {
797
739
  getDocumentAttributes(_renderOptions) {
798
740
  return void 0;
799
741
  }
742
+ applyAttributesToFirstBodyElement(html, attributes) {
743
+ return this.htmlTransformer.applyAttributesToFirstBodyElement(html, attributes);
744
+ }
745
+ applyAttributesToHtmlElement(html, attributes) {
746
+ return this.htmlTransformer.applyAttributesToHtmlElement(html, attributes);
747
+ }
800
748
  /**
801
749
  * Returns declarative HTML fragments that core should inject into the final document.
802
750
  *
@@ -859,22 +807,6 @@ class IntegrationRenderer {
859
807
  getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache)
860
808
  });
861
809
  }
862
- /**
863
- * Compatibility foreign-subtree contract that exposes a narrower payload shape for
864
- * future route-composition work while preserving the current
865
- * `renderComponentWithForeignChildren()` runtime semantics.
866
- */
867
- async renderForeignSubtree(input) {
868
- const result = await this.renderComponentWithForeignChildren(input);
869
- return {
870
- html: result.html,
871
- assets: result.assets ?? [],
872
- rootTag: result.rootTag,
873
- rootAttributes: result.rootAttributes,
874
- attachmentPolicy: result.canAttachAttributes ? { kind: "first-element" } : { kind: "none" },
875
- integrationName: result.integrationName
876
- };
877
- }
878
810
  normalizeComponentRenderOutput(result) {
879
811
  const normalizedHtml = this.normalizeUnresolvedMarkerArtifactHtml(result.html);
880
812
  return normalizedHtml === result.html ? result : {
@@ -893,21 +825,7 @@ class IntegrationRenderer {
893
825
  * directly without paying the queue orchestration cost.
894
826
  */
895
827
  hasForeignChildDescendants(component) {
896
- const stack = [component];
897
- const seen = /* @__PURE__ */ new Set();
898
- while (stack.length > 0) {
899
- const current = stack.pop();
900
- if (!current || seen.has(current)) {
901
- continue;
902
- }
903
- seen.add(current);
904
- const integrationName = current.config?.integration ?? current.config?.__eco?.integration;
905
- if (integrationName && integrationName !== this.name) {
906
- return true;
907
- }
908
- stack.push(...current.config?.dependencies?.components ?? []);
909
- }
910
- return false;
828
+ return hasForeignChildDescendantsInGraph(component, this.name);
911
829
  }
912
830
  /**
913
831
  * Render a single component and return structured output for orchestration paths.
@@ -0,0 +1,38 @@
1
+ import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
2
+ import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
3
+ import type { ComponentRenderResult, EcoComponent, EcoPageFile, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PagePackageResult, RouteRendererBody, RouteRendererOptions } from '../../types/public-types.js';
4
+ import type { RouteHtmlFinalization, RouteRenderOrchestratorAdapter, RouteRenderOrchestratorResolvedInputs } from './route-render-orchestrator.js';
5
+ /**
6
+ * Host surface required to build the orchestrator adapter for one integration renderer.
7
+ */
8
+ export type IntegrationRouteRenderAdapterHost<C> = {
9
+ readonly name: string;
10
+ resolveRouteRenderInputs(routeOptions: RouteRendererOptions): Promise<RouteRenderOrchestratorResolvedInputs>;
11
+ resolveRouteDependencies(input: {
12
+ components: (EcoComponent | Partial<EcoComponent>)[];
13
+ }): Promise<{
14
+ resolvedDependencies: ProcessedAsset[];
15
+ }>;
16
+ importPageFile(file: string): Promise<EcoPageFile>;
17
+ collectPageBrowserGraphContribution(context: PageBrowserGraphContributionContext): Promise<PageBrowserGraphContribution | undefined>;
18
+ resolveRoutePageComponentRender(input: {
19
+ Page: EcoComponent;
20
+ Layout?: EcoComponent;
21
+ props: Record<string, unknown>;
22
+ routeOptions: RouteRendererOptions;
23
+ }): Promise<ComponentRenderResult | undefined>;
24
+ renderRouteBody(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
25
+ getDocumentAttributes(renderOptions: IntegrationRendererRenderOptions<C>): Record<string, string> | undefined;
26
+ getHtmlDocumentContributions(options: {
27
+ renderOptions: IntegrationRendererRenderOptions<C>;
28
+ partial: boolean;
29
+ }): HtmlDocumentContribution[] | undefined;
30
+ applyAttributesToFirstBodyElement(html: string, attributes: Record<string, string>): string;
31
+ applyAttributesToHtmlElement(html: string, attributes: Record<string, string>): string;
32
+ transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[], pagePackage?: PagePackageResult): Promise<RouteRendererBody>;
33
+ };
34
+ /**
35
+ * Builds the internal route-render adapter consumed by `RouteRenderOrchestrator`.
36
+ */
37
+ export declare function createIntegrationRouteRenderAdapter<C>(host: IntegrationRouteRenderAdapterHost<C>): RouteRenderOrchestratorAdapter<C>;
38
+ export type { RouteHtmlFinalization };
@@ -0,0 +1,27 @@
1
+ import { loadPageBrowserGraphContribution } from "./page-browser-graph-contribution.loader.js";
2
+ import { buildRouteHtmlFinalization } from "./route-html-finalization.service.js";
3
+ function createIntegrationRouteRenderAdapter(host) {
4
+ return {
5
+ name: host.name,
6
+ resolveRouteRenderInputs: (routeOptions) => host.resolveRouteRenderInputs(routeOptions),
7
+ resolveRouteDependencies: (input) => host.resolveRouteDependencies(input),
8
+ collectPageBrowserGraphContribution: (routeFile) => loadPageBrowserGraphContribution(
9
+ routeFile,
10
+ (file) => host.importPageFile(file),
11
+ (context) => host.collectPageBrowserGraphContribution(context)
12
+ ),
13
+ resolveRoutePageComponentRender: (input) => host.resolveRoutePageComponentRender(input),
14
+ renderRouteBody: (renderOptions) => host.renderRouteBody(renderOptions),
15
+ getRouteHtmlFinalization: (renderOptions) => buildRouteHtmlFinalization({
16
+ renderOptions,
17
+ getDocumentAttributes: (options) => host.getDocumentAttributes(options),
18
+ getHtmlDocumentContributions: (options) => host.getHtmlDocumentContributions(options),
19
+ applyAttributesToFirstBodyElement: (html, attributes) => host.applyAttributesToFirstBodyElement(html, attributes),
20
+ applyAttributesToHtmlElement: (html, attributes) => host.applyAttributesToHtmlElement(html, attributes)
21
+ }),
22
+ transformRouteResponse: (response, htmlContributions, pagePackage) => host.transformRouteResponse(response, htmlContributions, pagePackage)
23
+ };
24
+ }
25
+ export {
26
+ createIntegrationRouteRenderAdapter
27
+ };
@@ -0,0 +1,20 @@
1
+ import type { EcoPageLayoutEntry, LayoutPropsContext } from '../../types/public-types.js';
2
+ export type LayoutShellPropsContext = LayoutPropsContext & {
3
+ pageProps?: Record<string, unknown>;
4
+ };
5
+ /**
6
+ * Resolves the default shell props passed to a layout tier during route render.
7
+ */
8
+ export declare function resolveLayoutShellProps(context: LayoutShellPropsContext): Record<string, unknown>;
9
+ /**
10
+ * Resolves props for one layout entry, merging shell props with an optional factory.
11
+ */
12
+ export declare function resolveLayoutEntryProps(entry: EcoPageLayoutEntry, context: LayoutShellPropsContext): Record<string, unknown>;
13
+ /**
14
+ * Reads the normalized layout stack from a page component config.
15
+ */
16
+ export declare function resolvePageLayoutComponents(layouts?: EcoPageLayoutEntry['component'][]): EcoPageLayoutEntry['component'][];
17
+ /**
18
+ * Returns the innermost layout component from a normalized page layout stack.
19
+ */
20
+ export declare function resolveInnermostPageLayout(layouts?: EcoPageLayoutEntry['component'][]): EcoPageLayoutEntry['component'] | undefined;
@@ -0,0 +1,31 @@
1
+ function resolveLayoutShellProps(context) {
2
+ return context.locals ? { locals: context.locals } : {};
3
+ }
4
+ function resolveLayoutEntryProps(entry, context) {
5
+ const shellProps = resolveLayoutShellProps(context);
6
+ if (!entry.props) {
7
+ return shellProps;
8
+ }
9
+ const layoutPropsContext = {
10
+ params: context.params,
11
+ query: context.query,
12
+ locals: context.locals
13
+ };
14
+ return {
15
+ ...shellProps,
16
+ ...entry.props(layoutPropsContext)
17
+ };
18
+ }
19
+ function resolvePageLayoutComponents(layouts) {
20
+ return layouts && layouts.length > 0 ? layouts : [];
21
+ }
22
+ function resolveInnermostPageLayout(layouts) {
23
+ const stack = resolvePageLayoutComponents(layouts);
24
+ return stack[stack.length - 1];
25
+ }
26
+ export {
27
+ resolveInnermostPageLayout,
28
+ resolveLayoutEntryProps,
29
+ resolveLayoutShellProps,
30
+ resolvePageLayoutComponents
31
+ };
@@ -24,6 +24,11 @@ export declare class OwnershipValidationService {
24
24
  * Validates foreign ownership edges reachable from the supplied route roots.
25
25
  */
26
26
  validate(input: OwnershipValidationInput): OwnershipValidationError[];
27
+ private assertDeclaredComponentDependencies;
27
28
  private isRegisteredIntegration;
28
29
  }
30
+ /**
31
+ * Throws when declared ownership validation collected one or more errors.
32
+ */
33
+ export declare function throwIfOwnershipInvalid(validationErrors: OwnershipValidationError[]): void;
29
34
  export {};
@@ -1,4 +1,6 @@
1
+ import { assertEcoDeclaredComponent } from "../../eco/eco-declared-component.js";
1
2
  import { mapDeclaredOwnershipGraph } from "./declared-ownership-graph.js";
3
+ import { walkComponentGraph } from "./component-graph.js";
2
4
  class OwnershipValidationService {
3
5
  appConfig;
4
6
  /**
@@ -11,6 +13,7 @@ class OwnershipValidationService {
11
13
  * Validates foreign ownership edges reachable from the supplied route roots.
12
14
  */
13
15
  validate(input) {
16
+ this.assertDeclaredComponentDependencies(input);
14
17
  return mapDeclaredOwnershipGraph({
15
18
  roots: input.roots,
16
19
  currentIntegrationName: input.currentIntegrationName,
@@ -20,6 +23,9 @@ class OwnershipValidationService {
20
23
  if (!isForeignToParent) {
21
24
  return errors;
22
25
  }
26
+ if (integrationName === "html") {
27
+ return errors;
28
+ }
23
29
  if (!componentMeta) {
24
30
  errors.push({
25
31
  code: "MISSING_COMPONENT_METADATA",
@@ -41,6 +47,21 @@ class OwnershipValidationService {
41
47
  }
42
48
  }).flat();
43
49
  }
50
+ assertDeclaredComponentDependencies(input) {
51
+ walkComponentGraph({
52
+ roots: input.roots,
53
+ currentIntegrationName: input.currentIntegrationName,
54
+ onComponent: ({ component }) => {
55
+ const parentFile = component.config?.__eco?.file;
56
+ for (const child of component.config?.dependencies?.components ?? []) {
57
+ if (!child) {
58
+ continue;
59
+ }
60
+ assertEcoDeclaredComponent(child, { parentComponentFile: parentFile });
61
+ }
62
+ }
63
+ });
64
+ }
44
65
  isRegisteredIntegration(integrationName, currentIntegrationName) {
45
66
  if (integrationName === currentIntegrationName) {
46
67
  return true;
@@ -48,6 +69,13 @@ class OwnershipValidationService {
48
69
  return this.appConfig.integrations.some((integration) => integration.name === integrationName);
49
70
  }
50
71
  }
72
+ function throwIfOwnershipInvalid(validationErrors) {
73
+ if (validationErrors.length === 0) {
74
+ return;
75
+ }
76
+ throw new Error(validationErrors.map((error) => error.message).join("\n"));
77
+ }
51
78
  export {
52
- OwnershipValidationService
79
+ OwnershipValidationService,
80
+ throwIfOwnershipInvalid
53
81
  };
@@ -0,0 +1,9 @@
1
+ import type { EcoPageFile, PageBrowserGraphContribution, PageBrowserGraphContributionContext } from '../../types/public-types.js';
2
+ /**
3
+ * Loads a page module once and collects declarative browser-graph requirements.
4
+ *
5
+ * @remarks
6
+ * Shared by route orchestration prep and the integration route-render adapter so
7
+ * page imports are not duplicated when both paths need the same contribution.
8
+ */
9
+ export declare function loadPageBrowserGraphContribution(routeFile: string, importPageFile: (file: string) => Promise<EcoPageFile>, collectContribution: (context: PageBrowserGraphContributionContext) => Promise<PageBrowserGraphContribution | undefined>): Promise<PageBrowserGraphContribution | undefined>;
@@ -0,0 +1,7 @@
1
+ async function loadPageBrowserGraphContribution(routeFile, importPageFile, collectContribution) {
2
+ const pageModule = await importPageFile(routeFile);
3
+ return collectContribution({ file: routeFile, pageModule });
4
+ }
5
+ export {
6
+ loadPageBrowserGraphContribution
7
+ };