@ecopages/core 0.2.0-beta.25 → 0.2.0-beta.27

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 (144) hide show
  1. package/package.json +58 -50
  2. package/src/adapters/bun/server-adapter.js +2 -2
  3. package/src/adapters/node/server-adapter.js +2 -2
  4. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +10 -0
  5. package/src/adapters/shared/hmr-entrypoint-registrar.js +34 -10
  6. package/src/adapters/shared/runtime-server-lifecycle.js +1 -1
  7. package/src/adapters/shared/server-static-builder.d.ts +2 -1
  8. package/src/adapters/shared/server-static-builder.js +16 -14
  9. package/src/adapters/shared/shared-hmr-manager.d.ts +11 -4
  10. package/src/adapters/shared/shared-hmr-manager.js +22 -3
  11. package/src/build/README.md +92 -46
  12. package/src/build/app-build-manifest-runtime.d.ts +28 -2
  13. package/src/build/app-build-manifest-runtime.js +26 -10
  14. package/src/build/{browser-runtime-plugin.d.ts → browser/browser-runtime-plugin.d.ts} +1 -1
  15. package/src/build/{browser-runtime-plugin.js → browser/browser-runtime-plugin.js} +1 -1
  16. package/src/build/{jsx-ownership-plugins.d.ts → browser/jsx-ownership-plugins.d.ts} +2 -2
  17. package/src/build/{jsx-ownership-plugins.js → browser/jsx-ownership-plugins.js} +2 -2
  18. package/src/build/build-adapter.d.ts +18 -21
  19. package/src/build/build-adapter.js +8 -43
  20. package/src/build/{build-input-fingerprint.d.ts → cache/build-input-fingerprint.d.ts} +3 -3
  21. package/src/build/cache/cache-constants.d.ts +4 -0
  22. package/src/build/cache/cache-constants.js +6 -0
  23. package/src/build/cache/cache-keys.d.ts +22 -0
  24. package/src/build/cache/cache-keys.js +63 -0
  25. package/src/build/{dev-browser-script-cache.d.ts → cache/dev-browser-script-cache.d.ts} +2 -2
  26. package/src/build/{dev-browser-script-cache.js → cache/dev-browser-script-cache.js} +4 -4
  27. package/src/build/{pages-unified-graph-build.d.ts → cache/pages-unified-graph-build.d.ts} +1 -1
  28. package/src/build/{pages-unified-graph-build.js → cache/pages-unified-graph-build.js} +18 -26
  29. package/src/build/{production-build-cache.d.ts → cache/production-build-cache.d.ts} +1 -1
  30. package/src/build/{production-build-cache.js → cache/production-build-cache.js} +2 -2
  31. package/src/build/{server-entry-build-cache.d.ts → cache/server-entry-build-cache.d.ts} +3 -4
  32. package/src/build/{server-entry-build-cache.js → cache/server-entry-build-cache.js} +16 -12
  33. package/src/build/{build-contracts.d.ts → contracts/build-contracts.d.ts} +1 -2
  34. package/src/build/contracts/build-manifest.d.ts +90 -0
  35. package/src/build/{build-manifest.js → contracts/build-manifest.js} +2 -2
  36. package/src/build/{rolldown-adapter-helpers.d.ts → rolldown/rolldown-adapter-helpers.d.ts} +20 -3
  37. package/src/build/{rolldown-adapter-helpers.js → rolldown/rolldown-adapter-helpers.js} +26 -6
  38. package/src/build/{rolldown-build-adapter.d.ts → rolldown/rolldown-build-adapter.d.ts} +1 -1
  39. package/src/build/{rolldown-plugin-bridge.d.ts → rolldown/rolldown-plugin-bridge.d.ts} +2 -2
  40. package/src/build/{rolldown-plugin-bridge.js → rolldown/rolldown-plugin-bridge.js} +1 -1
  41. package/src/build/{rolldown-source-transform-pass.d.ts → rolldown/rolldown-source-transform-pass.d.ts} +1 -1
  42. package/src/build/{rolldown-source-transform-pass.js → rolldown/rolldown-source-transform-pass.js} +4 -1
  43. package/src/build/{runtime-build-output-normalizer.js → rolldown/runtime-build-output-normalizer.js} +2 -2
  44. package/src/build/{build-profile-options.d.ts → runtime/build-profile-options.d.ts} +2 -2
  45. package/src/build/runtime/build-request-identity.d.ts +17 -0
  46. package/src/build/runtime/build-request-identity.js +54 -0
  47. package/src/build/runtime/build-request-policy.d.ts +59 -0
  48. package/src/build/runtime/build-request-policy.js +46 -0
  49. package/src/build/{build-runtime.d.ts → runtime/build-runtime.d.ts} +8 -4
  50. package/src/build/{build-runtime.js → runtime/build-runtime.js} +11 -11
  51. package/src/build/{deduping-build-executor.d.ts → runtime/deduping-build-executor.d.ts} +2 -9
  52. package/src/build/runtime/deduping-build-executor.js +34 -0
  53. package/src/build/{parallel-build-executor.d.ts → runtime/parallel-build-executor.d.ts} +1 -1
  54. package/src/build/{serialized-build-executor.d.ts → runtime/serialized-build-executor.d.ts} +1 -1
  55. package/src/config/config-builder.d.ts +1 -1
  56. package/src/diagnostics/startup-trace.d.ts +49 -2
  57. package/src/diagnostics/startup-trace.js +140 -8
  58. package/src/hmr/client/hmr-runtime.js +1 -1
  59. package/src/hmr/hmr-file-change-prep.d.ts +10 -0
  60. package/src/hmr/hmr-file-change-prep.js +17 -0
  61. package/src/plugins/alias-resolver-plugin.d.ts +1 -1
  62. package/src/plugins/eco-component-meta-plugin.d.ts +1 -1
  63. package/src/plugins/foreign-jsx-override-plugin.d.ts +1 -1
  64. package/src/plugins/integration-plugin.d.ts +16 -5
  65. package/src/plugins/integration-plugin.js +11 -1
  66. package/src/plugins/processor.d.ts +28 -8
  67. package/src/plugins/source-transform.d.ts +1 -1
  68. package/src/route-renderer/GRAPH.md +13 -19
  69. package/src/route-renderer/README.md +1 -1
  70. package/src/route-renderer/orchestration/component-render-context.d.ts +4 -4
  71. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +6 -2
  72. package/src/route-renderer/orchestration/integration-renderer.d.ts +11 -52
  73. package/src/route-renderer/orchestration/integration-renderer.js +38 -91
  74. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +1 -8
  75. package/src/route-renderer/orchestration/integration-route-render-adapter.js +0 -2
  76. package/src/route-renderer/orchestration/page-browser-graph-session.d.ts +106 -0
  77. package/src/route-renderer/orchestration/page-browser-graph-session.js +436 -0
  78. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +9 -3
  79. package/src/route-renderer/orchestration/page-browser-graph.service.js +89 -40
  80. package/src/route-renderer/orchestration/render-output.utils.d.ts +14 -0
  81. package/src/route-renderer/orchestration/render-output.utils.js +30 -0
  82. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +0 -1
  83. package/src/route-renderer/orchestration/route-html-finalization.service.js +4 -9
  84. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +1 -2
  85. package/src/route-renderer/orchestration/route-prepared-options.builder.js +1 -10
  86. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +3 -12
  87. package/src/route-renderer/orchestration/route-render-orchestrator.js +4 -14
  88. package/src/services/assets/asset-processing-service/asset-dependency-keys.d.ts +13 -0
  89. package/src/services/assets/asset-processing-service/asset-dependency-keys.js +3 -0
  90. package/src/services/assets/asset-processing-service/asset-processing.service.d.ts +3 -1
  91. package/src/services/assets/asset-processing-service/asset-processing.service.js +4 -2
  92. package/src/services/assets/asset-processing-service/assets.types.d.ts +1 -1
  93. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -1
  94. package/src/services/assets/asset-processing-service/grouped-content-bundles.js +19 -3
  95. package/src/services/assets/asset-processing-service/inline-content-script-body.d.ts +11 -0
  96. package/src/services/assets/asset-processing-service/inline-content-script-body.js +17 -0
  97. package/src/services/assets/asset-processing-service/materialize-content-script-asset.d.ts +4 -6
  98. package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +2 -1
  99. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.d.ts +0 -2
  100. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +2 -15
  101. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.d.ts +3 -2
  102. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +33 -54
  103. package/src/services/assets/browser-bundle.service.d.ts +27 -31
  104. package/src/services/assets/browser-bundle.service.js +12 -23
  105. package/src/services/module-loading/app-server-module-transpiler.service.d.ts +8 -0
  106. package/src/services/module-loading/app-server-module-transpiler.service.js +1 -7
  107. package/src/services/module-loading/page-module-import.service.d.ts +1 -1
  108. package/src/services/module-loading/page-module-import.service.js +40 -28
  109. package/src/services/module-loading/route-module-build-cache.store.js +1 -1
  110. package/src/services/module-loading/route-module-build-manifest.d.ts +2 -9
  111. package/src/services/module-loading/route-module-build-manifest.js +12 -43
  112. package/src/services/module-loading/server-module-transpiler.service.d.ts +1 -1
  113. package/src/static-site-generator/README.md +1 -1
  114. package/src/static-site-generator/production-page-browser-graph-prebuild.d.ts +28 -0
  115. package/src/static-site-generator/production-page-browser-graph-prebuild.js +22 -0
  116. package/src/static-site-generator/static-build-invalidation.d.ts +2 -2
  117. package/src/static-site-generator/static-build-invalidation.js +3 -3
  118. package/src/static-site-generator/static-site-generator.js +23 -1
  119. package/src/types/internal-types.d.ts +10 -4
  120. package/src/types/public-types.d.ts +58 -9
  121. package/src/watchers/project-watcher.js +12 -2
  122. package/src/build/build-manifest.d.ts +0 -33
  123. package/src/build/deduping-build-executor.js +0 -56
  124. package/src/build/runtime-build-executor.d.ts +0 -14
  125. package/src/build/runtime-build-executor.js +0 -16
  126. /package/src/build/{browser-runtime-manifest.d.ts → browser/browser-runtime-manifest.d.ts} +0 -0
  127. /package/src/build/{browser-runtime-manifest.js → browser/browser-runtime-manifest.js} +0 -0
  128. /package/src/build/{browser-runtime-plugin-helpers.d.ts → browser/browser-runtime-plugin-helpers.d.ts} +0 -0
  129. /package/src/build/{browser-runtime-plugin-helpers.js → browser/browser-runtime-plugin-helpers.js} +0 -0
  130. /package/src/build/{lit-static-render-worker-context.d.ts → browser/lit-static-render-worker-context.d.ts} +0 -0
  131. /package/src/build/{lit-static-render-worker-context.js → browser/lit-static-render-worker-context.js} +0 -0
  132. /package/src/build/{build-input-fingerprint.js → cache/build-input-fingerprint.js} +0 -0
  133. /package/src/build/{build-contracts.js → contracts/build-contracts.js} +0 -0
  134. /package/src/build/{build-types.d.ts → contracts/build-types.d.ts} +0 -0
  135. /package/src/build/{build-types.js → contracts/build-types.js} +0 -0
  136. /package/src/build/{rolldown-build-adapter.js → rolldown/rolldown-build-adapter.js} +0 -0
  137. /package/src/build/{rolldown-build-invocation-metrics.d.ts → rolldown/rolldown-build-invocation-metrics.d.ts} +0 -0
  138. /package/src/build/{rolldown-build-invocation-metrics.js → rolldown/rolldown-build-invocation-metrics.js} +0 -0
  139. /package/src/build/{runtime-build-output-normalizer.d.ts → rolldown/runtime-build-output-normalizer.d.ts} +0 -0
  140. /package/src/build/{server-side-css-shim-plugin.d.ts → rolldown/server-side-css-shim-plugin.d.ts} +0 -0
  141. /package/src/build/{server-side-css-shim-plugin.js → rolldown/server-side-css-shim-plugin.js} +0 -0
  142. /package/src/build/{build-profile-options.js → runtime/build-profile-options.js} +0 -0
  143. /package/src/build/{parallel-build-executor.js → runtime/parallel-build-executor.js} +0 -0
  144. /package/src/build/{serialized-build-executor.js → runtime/serialized-build-executor.js} +0 -0
@@ -1,9 +1,9 @@
1
- import type { EcoBuildPlugin } from '../build/build-types.js';
1
+ import type { EcoBuildPlugin } from '../build/contracts/build-types.js';
2
2
  import type { EcoPagesAppConfig, IClientBridge } from '../types/internal-types.js';
3
3
  import { GENERATED_BASE_PATHS } from '../config/constants.js';
4
4
  import type { RuntimeCapabilityDeclaration } from './runtime-capability.js';
5
5
  export type { RuntimeCapabilityDeclaration, RuntimeCapabilityTag } from './runtime-capability.js';
6
- export type { EcoBuildLoader, EcoBuildOnLoadArgs, EcoBuildOnLoadResult, EcoBuildOnResolveArgs, EcoBuildOnResolveResult, EcoBuildPlugin, EcoBuildPluginBuilder, } from '../build/build-types.js';
6
+ export type { EcoBuildLoader, EcoBuildOnLoadArgs, EcoBuildOnLoadResult, EcoBuildOnResolveArgs, EcoBuildOnResolveResult, EcoBuildPlugin, EcoBuildPluginBuilder, } from '../build/contracts/build-types.js';
7
7
  export declare const PROCESSOR_ERRORS: {
8
8
  readonly CACHE_DIRECTORY_NOT_SET: "Cache directory not set in context";
9
9
  };
@@ -57,10 +57,17 @@ export interface ProcessorContext {
57
57
  cache?: string;
58
58
  }
59
59
  /**
60
- * Interface for processor build plugins
61
- * This is used to pass plugins to the build process directly from the processor
62
- * For instance it can become very handy when dealing with virtual modules that needs to be recognized by the bundler
63
- * i.e. @ecopages/image-processor
60
+ * Base class for content and asset processors that contribute build plugins.
61
+ *
62
+ * @remarks
63
+ * Processors declare plugins through two getters that map to
64
+ * {@link AppBuildManifest} buckets (names differ from integrations):
65
+ *
66
+ * - `plugins` → `runtimePlugins` (server **and** browser builds)
67
+ * - `buildPlugins` → `browserBundlePlugins` (browser bundles only)
68
+ *
69
+ * Virtual modules that must resolve during route-module transpile belong in
70
+ * `plugins`. Browser-only bundler hooks belong in `buildPlugins`.
64
71
  */
65
72
  export declare abstract class Processor<TOptions = Record<string, unknown>> {
66
73
  readonly name: string;
@@ -69,9 +76,22 @@ export declare abstract class Processor<TOptions = Record<string, unknown>> {
69
76
  protected watchConfig?: ProcessorWatchConfig;
70
77
  protected capabilities: ProcessorAssetCapability[];
71
78
  readonly runtimeCapability?: RuntimeCapabilityDeclaration;
72
- /** Plugins that are only used during the build process */
79
+ /**
80
+ * Browser-bundle-only plugins.
81
+ *
82
+ * @remarks
83
+ * Maps to {@link AppBuildManifest.browserBundlePlugins}. Integrations name the
84
+ * same bucket `browserBuildPlugins`.
85
+ */
73
86
  abstract buildPlugins?: EcoBuildPlugin[];
74
- /** Plugins that are used during runtime for file processing */
87
+ /**
88
+ * Shared build plugins for server-oriented and browser-oriented work.
89
+ *
90
+ * @remarks
91
+ * Maps to {@link AppBuildManifest.runtimePlugins}. Despite the name, these are
92
+ * bundler plugins—not dev-server file processors. Runtime-only setup stays in
93
+ * {@link setup}.
94
+ */
75
95
  abstract plugins?: EcoBuildPlugin[];
76
96
  constructor(config: ProcessorConfig<TOptions>);
77
97
  setContext(appConfig: EcoPagesAppConfig): void;
@@ -1,4 +1,4 @@
1
- import type { EcoBuildPlugin } from '../build/build-types.js';
1
+ import type { EcoBuildPlugin } from '../build/contracts/build-types.js';
2
2
  import type { EcoPagesAppConfig } from '../types/internal-types.js';
3
3
  export interface EcoSourceTransformResult {
4
4
  code: string;
@@ -42,23 +42,17 @@ flowchart TD
42
42
  C --> D[ownershipValidationService.validate]
43
43
  D --> E[resolvePageData]
44
44
  E --> F[ownershipValidationService.validate + throwIfOwnershipInvalid]
45
- F --> G[resolveDependencies]
46
- G --> H[buildPageBrowserGraph]
47
- H --> I{shouldRenderPageComponent?}
48
- I -- Yes --> J[renderPageComponent]
49
- I -- No --> K[skip page-root render]
50
- J --> L[merge component assets]
51
- K --> L
52
- L --> M[collect injector and eager SSR lazy assets]
53
- M --> N[build pagePackage and prepared render options]
54
- N --> O[callbacks.render]
55
- O --> P[capture rendered body as html]
56
- P --> Q[inspect unresolved marker artifacts]
57
- Q --> R{unresolved eco-marker remains?}
58
- R -- Yes --> S[throw unresolved artifact error]
59
- R -- No --> T[stamp root or document attributes when needed]
60
- T --> U[htmlTransformer transform]
61
- U --> V[final body and cache strategy]
45
+ F --> G[resolveDependencies + buildPageBrowserGraph]
46
+ G --> H[collect injector and eager SSR lazy assets]
47
+ H --> I[build pagePackage and prepared render options]
48
+ I --> J[IntegrationRenderer.render via document shell]
49
+ J --> K[capture rendered body as html]
50
+ K --> L[inspect unresolved marker artifacts]
51
+ L --> M{unresolved eco-marker remains?}
52
+ M -- Yes --> N[throw unresolved artifact error]
53
+ M -- No --> O[stamp document attributes when needed]
54
+ O --> P[htmlTransformer transform]
55
+ P --> Q[final body and cache strategy]
62
56
  ```
63
57
 
64
58
  ## 3) Mixed-Integration Render Model
@@ -124,7 +118,7 @@ The most useful reading order is:
124
118
  2. `orchestration/route-render-orchestrator.ts`
125
119
  3. `orchestration/integration-renderer.ts`
126
120
  4. `orchestration/ownership-validation.service.ts`
127
- 5. `orchestration/ownership-planning.service.ts`
121
+ 5. `orchestration/component-graph.ts`
128
122
  6. `orchestration/component-render-context.ts`
129
123
  7. `orchestration/foreign-subtree-execution.service.ts`
130
124
  8. `page-loading/page-module-loader.ts`
@@ -137,7 +131,7 @@ The most useful reading order is:
137
131
  - `packages/core/src/route-renderer/orchestration/route-render-orchestrator.ts`
138
132
  - `packages/core/src/route-renderer/orchestration/integration-renderer.ts`
139
133
  - `packages/core/src/route-renderer/orchestration/ownership-validation.service.ts`
140
- - `packages/core/src/route-renderer/orchestration/ownership-planning.service.ts`
134
+ - `packages/core/src/route-renderer/orchestration/component-graph.ts`
141
135
  - `packages/core/src/route-renderer/orchestration/component-render-context.ts`
142
136
  - `packages/core/src/route-renderer/orchestration/foreign-subtree-execution.service.ts`
143
137
  - `packages/core/src/route-renderer/page-loading/page-module-loader.ts`
@@ -72,7 +72,7 @@ The route-render contract is:
72
72
  3. `RouteRenderOrchestrator.prepareRenderOptions()` loads the page module, validates ownership (fail-fast), resolves page data, resolves dependencies, and builds the page browser graph.
73
73
  4. The integration renderer performs page, layout, and document-shell rendering. When it encounters a foreign child, it delegates that child back to the owning renderer.
74
74
  5. If a renderer needs queued handoff, it emits internal foreign-subtree tokens and resolves them before returning final HTML.
75
- 6. `RouteRenderOrchestrator.executePrepared()` captures the final body, rejects unresolved `<eco-marker>` artifacts, stamps root or document attributes when needed, and runs the HTML transformer.
75
+ 6. `RouteRenderOrchestrator.executePrepared()` captures the final body, rejects unresolved `<eco-marker>` artifacts, stamps document attributes when needed, and runs the HTML transformer.
76
76
 
77
77
  Important:
78
78
 
@@ -2,16 +2,16 @@ import type { EcoComponent } from '../../types/public-types.js';
2
2
  /**
3
3
  * Result returned by a renderer-owned foreign-child runtime.
4
4
  *
5
- * `inline` keeps rendering inside the current integration. `resolved` returns a
6
- * renderer-owned value immediately, which can be final HTML or a renderer-local
7
- * transport token for later queue resolution.
5
+ * `inline` keeps rendering inside the current integration. `resolved.value` is
6
+ * the queue transport token string allocated by `createQueuedRuntime`; callers
7
+ * must not treat arbitrary objects as resolved output.
8
8
  */
9
9
  export type ForeignChildInterceptionResult = {
10
10
  kind: 'inline';
11
11
  props?: Record<string, unknown>;
12
12
  } | {
13
13
  kind: 'resolved';
14
- value: unknown;
14
+ value: string;
15
15
  } | undefined;
16
16
  /**
17
17
  * Foreign-child metadata passed into the active renderer-owned runtime.
@@ -2,7 +2,7 @@ import {
2
2
  getComponentRenderContext,
3
3
  runWithComponentRenderContext
4
4
  } from "./component-render-context.js";
5
- import { isMarkupNodeLike } from "./render-output.utils.js";
5
+ import { assertForeignChildrenNotOpaque, isMarkupNodeLike } from "./render-output.utils.js";
6
6
  function toForeignSubtreeRenderPayload(result) {
7
7
  return {
8
8
  html: result.html,
@@ -69,6 +69,9 @@ class ForeignSubtreeExecutionService {
69
69
  props: { ...input.props }
70
70
  };
71
71
  }
72
+ if ("children" in input.props) {
73
+ assertForeignChildrenNotOpaque(input.props.children, "foreign-subtree queue");
74
+ }
72
75
  runtimeContext.nextForeignSubtreeId += 1;
73
76
  const foreignSubtreeId = runtimeContext.nextForeignSubtreeId;
74
77
  const token = this.createForeignSubtreeToken(options.tokenPrefix, runtimeContext, foreignSubtreeId);
@@ -108,10 +111,11 @@ class ForeignSubtreeExecutionService {
108
111
  return { assets: [], html: void 0 };
109
112
  }
110
113
  if (typeof children !== "string" && !isMarkupNodeLike(children)) {
114
+ assertForeignChildrenNotOpaque(children, options.queueLabel);
111
115
  return { assets: [], children };
112
116
  }
113
117
  const html = await this.resolveQueuedTokens(
114
- typeof children === "string" ? children : children.outerHTML ?? String(children ?? ""),
118
+ typeof children === "string" ? children : children.outerHTML ?? "",
115
119
  queuedResolutionsByToken,
116
120
  resolveToken
117
121
  );
@@ -4,7 +4,7 @@
4
4
  * @module
5
5
  */
6
6
  import type { EcoPagesAppConfig, IHmrManager } from '../../types/internal-types.js';
7
- import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoComponentDependencies, EcoPageFile, EcoPagesElement, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, PagePackageResult, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
7
+ import type { ComponentRenderInput, ComponentRenderResult, EcoComponent, EcoPageFile, EcoPagesElement, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PageBrowserGraphResult, PageMetadataProps, PagePackageResult, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
8
8
  import { type AssetProcessingService, type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
9
9
  import { HtmlTransformerService } from '../../services/html/html-transformer.service.js';
10
10
  import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
@@ -67,6 +67,14 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
67
67
  private renderExclusiveChain;
68
68
  protected DOC_TYPE: string;
69
69
  private runRenderExclusive;
70
+ /**
71
+ * Activates the owning integration runtime on first render or graph use.
72
+ */
73
+ protected ensureIntegrationRuntimeActivated(): Promise<void>;
74
+ /**
75
+ * Prebuilds the production Page Browser Graph for one route file.
76
+ */
77
+ prebuildProductionPageBrowserGraph(routeFile: string): Promise<void>;
70
78
  /**
71
79
  * Loads one route module through the owning renderer's import path.
72
80
  *
@@ -170,6 +178,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
170
178
  props: P;
171
179
  ctx: RenderToResponseContext;
172
180
  layout?: EcoComponent;
181
+ transformDocumentHtml?: (html: string) => string;
173
182
  }): Promise<Response>;
174
183
  /**
175
184
  * Renders a route page through optional layout and document shells.
@@ -225,16 +234,6 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
225
234
  rendererModules?: unknown;
226
235
  runtimeOrigin: string;
227
236
  });
228
- /**
229
- * Returns the HTML path from the provided file path.
230
- * It extracts the path relative to the pages directory and removes the 'index' part if present.
231
- *
232
- * @param file - The file path to extract the HTML path from.
233
- * @returns The extracted HTML path.
234
- */
235
- protected getHtmlPath({ file }: {
236
- file: string;
237
- }): string;
238
237
  /**
239
238
  * Returns the HTML template component.
240
239
  * It imports the HTML template from the specified path in the app configuration.
@@ -262,27 +261,6 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
262
261
  * @returns The resolved dependency path.
263
262
  */
264
263
  protected resolveDependencyPath(componentDir: string, pathUrl: string): string;
265
- /**
266
- * Extracts the dependencies from the provided component configuration.
267
- * It resolves the paths for scripts and stylesheets based on the component directory.
268
- *
269
- * @param componentDir - The component directory path.
270
- * @param scripts - The scripts to extract.
271
- * @param stylesheets - The stylesheets to extract.
272
- * @returns The extracted dependencies.
273
- */
274
- protected extractDependencies({ componentDir, scripts, stylesheets, }: {
275
- componentDir: string;
276
- } & EcoComponentDependencies): EcoComponentDependencies;
277
- /**
278
- * Resolves lazy script paths to public asset URLs.
279
- * Converts source paths to their final bundled output paths.
280
- *
281
- * @param componentDir - The component directory path.
282
- * @param scripts - The lazy script paths to resolve.
283
- * @returns Comma-separated string of resolved public script paths.
284
- */
285
- protected resolveLazyScripts(componentDir: string, scripts: string[]): string;
286
264
  /**
287
265
  * Collects the dependencies for the provided components.
288
266
  * Combines component-specific dependencies with global integration dependencies.
@@ -312,12 +290,6 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
312
290
  }): Promise<{
313
291
  resolvedDependencies: ProcessedAsset[];
314
292
  }>;
315
- protected resolveRoutePageComponentRender(input: {
316
- Page: EcoComponent;
317
- Layout?: EcoComponent;
318
- props: Record<string, unknown>;
319
- routeOptions: RouteRendererOptions;
320
- }): Promise<ComponentRenderResult | undefined>;
321
293
  protected renderRouteBody(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
322
294
  protected transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[], pagePackage?: PagePackageResult): Promise<RouteRendererBody>;
323
295
  /**
@@ -328,19 +300,6 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
328
300
  * @returns The prepared render options.
329
301
  */
330
302
  protected prepareRenderOptions(options: RouteRendererOptions, adapter?: RouteRenderOrchestratorAdapter<C>): Promise<IntegrationRendererRenderOptions<C>>;
331
- /**
332
- * Controls whether the page root should be rendered through `renderComponent()`
333
- * during route option preparation in component-capable modes.
334
- *
335
- * Integrations that already own page-level hydration (for example router-driven
336
- * React rendering) can override this and return `false` to avoid duplicate root
337
- * mount assets and competing hydration entrypoints.
338
- */
339
- protected shouldRenderPageComponent(_input: {
340
- Page: EcoComponent;
341
- Layout?: EcoComponent;
342
- options: RouteRendererOptions;
343
- }): boolean;
344
303
  /**
345
304
  * Executes the integration renderer with the provided options.
346
305
  *
@@ -348,7 +307,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
348
307
  * 1. Build normalized render options (`prepareRenderOptions`).
349
308
  * 2. Render the route body once.
350
309
  * 3. Reject unresolved route-level eco-marker artifacts.
351
- * 4. Optionally apply root attributes for page/component root boundaries.
310
+ * 4. Optionally apply document attributes for integration-owned document boundaries.
352
311
  * 5. Run HTML transformer with final dependency set.
353
312
  *
354
313
  * Stream-safety note: the first render result is normalized to a string once,
@@ -14,6 +14,7 @@ import {
14
14
  import { createIntegrationRouteRenderAdapter } from "./integration-route-render-adapter.js";
15
15
  import { loadPageBrowserGraphContribution } from "./page-browser-graph-contribution.loader.js";
16
16
  import { normalizeUnresolvedMarkerArtifactHtml, isMarkupNodeLike } from "./render-output.utils.js";
17
+ import { ensureIntegrationRuntimeReady } from "../../build/app-build-manifest-runtime.js";
17
18
  import {
18
19
  ForeignSubtreeExecutionService
19
20
  } from "./foreign-subtree-execution.service.js";
@@ -45,13 +46,33 @@ class IntegrationRenderer {
45
46
  renderExclusiveChain = Promise.resolve();
46
47
  DOC_TYPE = "<!DOCTYPE html>";
47
48
  runRenderExclusive(operation) {
48
- const run = this.renderExclusiveChain.then(operation, operation);
49
+ const run = this.renderExclusiveChain.then(async () => {
50
+ await this.ensureIntegrationRuntimeActivated();
51
+ return operation();
52
+ });
49
53
  this.renderExclusiveChain = run.then(
50
54
  () => void 0,
51
55
  () => void 0
52
56
  );
53
57
  return run;
54
58
  }
59
+ /**
60
+ * Activates the owning integration runtime on first render or graph use.
61
+ */
62
+ async ensureIntegrationRuntimeActivated() {
63
+ await ensureIntegrationRuntimeReady({
64
+ appConfig: this.appConfig,
65
+ integrationName: this.name,
66
+ runtimeOrigin: this.runtimeOrigin
67
+ });
68
+ }
69
+ /**
70
+ * Prebuilds the production Page Browser Graph for one route file.
71
+ */
72
+ async prebuildProductionPageBrowserGraph(routeFile) {
73
+ await this.ensureIntegrationRuntimeActivated();
74
+ await this.resolvePageBrowserGraphForFile(routeFile);
75
+ }
55
76
  /**
56
77
  * Loads one route module through the owning renderer's import path.
57
78
  *
@@ -281,9 +302,13 @@ class IntegrationRenderer {
281
302
  * @returns HTML response for the explicit view render.
282
303
  */
283
304
  async renderViewWithDocumentShell(input) {
305
+ await this.ensureIntegrationRuntimeActivated();
284
306
  const normalizedProps = input.props ?? {};
285
307
  if (input.ctx.partial) {
286
- return this.renderPartialViewResponse(input);
308
+ return this.renderPartialViewResponse({
309
+ ...input,
310
+ transformHtml: input.transformDocumentHtml
311
+ });
287
312
  }
288
313
  await this.prepareViewDependencies(input.view, input.layout);
289
314
  const HtmlTemplate = await this.getHtmlTemplate();
@@ -307,8 +332,9 @@ class IntegrationRenderer {
307
332
  }
308
333
  }
309
334
  );
335
+ const transformedDocumentHtml = input.transformDocumentHtml ? input.transformDocumentHtml(documentHtml) : documentHtml;
310
336
  const html = await this.finalizeResolvedHtml({
311
- html: `${this.DOC_TYPE}${documentHtml}`,
337
+ html: `${this.DOC_TYPE}${transformedDocumentHtml}`,
312
338
  partial: false
313
339
  });
314
340
  return this.createHtmlResponse(html, input.ctx);
@@ -346,7 +372,14 @@ class IntegrationRenderer {
346
372
  }
347
373
  const props = serializedChildren === void 0 ? input.props : { ...input.props, children: serializedChildren };
348
374
  const content = await component(props);
349
- const html = String(content);
375
+ if (typeof content !== "string") {
376
+ const componentFile = input.component.config?.__eco?.file ?? "unknown component";
377
+ const contentTag = Object.prototype.toString.call(content);
378
+ throw new TypeError(
379
+ `[ecopages] ${this.name} renderer expected a string render result for ${componentFile}, received ${contentTag}.`
380
+ );
381
+ }
382
+ const html = content;
350
383
  const assets = input.component.config?.dependencies && typeof this.assetProcessingService?.processDependencies === "function" ? await this.processComponentDependencies([input.component]) : void 0;
351
384
  return {
352
385
  html,
@@ -435,23 +468,6 @@ class IntegrationRenderer {
435
468
  ownershipValidationService: new OwnershipValidationService(appConfig)
436
469
  });
437
470
  }
438
- /**
439
- * Returns the HTML path from the provided file path.
440
- * It extracts the path relative to the pages directory and removes the 'index' part if present.
441
- *
442
- * @param file - The file path to extract the HTML path from.
443
- * @returns The extracted HTML path.
444
- */
445
- getHtmlPath({ file }) {
446
- const pagesDir = this.appConfig.absolutePaths.pagesDir;
447
- const pagesIndex = file.indexOf(pagesDir);
448
- if (pagesIndex === -1) return file;
449
- const startIndex = file.indexOf(pagesDir) + pagesDir.length;
450
- const endIndex = file.lastIndexOf("/");
451
- const path = file.substring(startIndex, endIndex);
452
- if (path === "/index") return "";
453
- return path;
454
- }
455
471
  /**
456
472
  * Returns the HTML template component.
457
473
  * It imports the HTML template from the specified path in the app configuration.
@@ -505,46 +521,6 @@ class IntegrationRenderer {
505
521
  resolveDependencyPath(componentDir, pathUrl) {
506
522
  return this.dependencyResolverService.resolveDependencyPath(componentDir, pathUrl);
507
523
  }
508
- /**
509
- * Extracts the dependencies from the provided component configuration.
510
- * It resolves the paths for scripts and stylesheets based on the component directory.
511
- *
512
- * @param componentDir - The component directory path.
513
- * @param scripts - The scripts to extract.
514
- * @param stylesheets - The stylesheets to extract.
515
- * @returns The extracted dependencies.
516
- */
517
- extractDependencies({
518
- componentDir,
519
- scripts,
520
- stylesheets
521
- }) {
522
- const scriptsPaths = [
523
- ...new Set(
524
- (scripts ?? []).filter((script) => typeof script === "string" ? true : !script.lazy).map((script) => typeof script === "string" ? script : script.src).filter((script) => Boolean(script)).map((script) => this.resolveDependencyPath(componentDir, script))
525
- )
526
- ];
527
- const stylesheetsPaths = [
528
- ...new Set(
529
- (stylesheets ?? []).map((style) => typeof style === "string" ? style : style.src).filter((style) => Boolean(style)).map((style) => this.resolveDependencyPath(componentDir, style))
530
- )
531
- ];
532
- return {
533
- scripts: scriptsPaths,
534
- stylesheets: stylesheetsPaths
535
- };
536
- }
537
- /**
538
- * Resolves lazy script paths to public asset URLs.
539
- * Converts source paths to their final bundled output paths.
540
- *
541
- * @param componentDir - The component directory path.
542
- * @param scripts - The lazy script paths to resolve.
543
- * @returns Comma-separated string of resolved public script paths.
544
- */
545
- resolveLazyScripts(componentDir, scripts) {
546
- return this.dependencyResolverService.resolveLazyScripts(componentDir, scripts);
547
- }
548
524
  /**
549
525
  * Collects the dependencies for the provided components.
550
526
  * Combines component-specific dependencies with global integration dependencies.
@@ -579,11 +555,9 @@ class IntegrationRenderer {
579
555
  resolveRouteDependencies: (input) => this.resolveRouteDependencies(input),
580
556
  importPageFile: (file) => this.importPageFile(file),
581
557
  collectPageBrowserGraphContribution: (context) => this.collectPageBrowserGraphContribution(context),
582
- resolveRoutePageComponentRender: (input) => this.resolveRoutePageComponentRender(input),
583
558
  renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
584
559
  getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
585
560
  getHtmlDocumentContributions: (options) => this.getHtmlDocumentContributions(options),
586
- applyAttributesToFirstBodyElement: (html, attributes) => this.applyAttributesToFirstBodyElement(html, attributes),
587
561
  applyAttributesToHtmlElement: (html, attributes) => this.applyAttributesToHtmlElement(html, attributes),
588
562
  transformRouteResponse: (response, htmlContributions, pagePackage) => this.transformRouteResponse(response, htmlContributions, pagePackage)
589
563
  });
@@ -617,22 +591,6 @@ class IntegrationRenderer {
617
591
  resolvedDependencies: await this.resolveDependencies(input.components)
618
592
  };
619
593
  }
620
- async resolveRoutePageComponentRender(input) {
621
- if (!this.shouldRenderPageComponent({ Page: input.Page, Layout: input.Layout, options: input.routeOptions })) {
622
- return void 0;
623
- }
624
- return this.renderComponentWithForeignChildren({
625
- component: input.Page,
626
- props: {
627
- ...input.props,
628
- params: input.routeOptions.params || {},
629
- query: input.routeOptions.query || {}
630
- },
631
- integrationContext: {
632
- componentInstanceId: "eco-page-root"
633
- }
634
- });
635
- }
636
594
  async renderRouteBody(renderOptions) {
637
595
  return this.render(renderOptions);
638
596
  }
@@ -658,17 +616,6 @@ class IntegrationRenderer {
658
616
  this.htmlTransformer.setPagePackage(renderOptions.pagePackage);
659
617
  return renderOptions;
660
618
  }
661
- /**
662
- * Controls whether the page root should be rendered through `renderComponent()`
663
- * during route option preparation in component-capable modes.
664
- *
665
- * Integrations that already own page-level hydration (for example router-driven
666
- * React rendering) can override this and return `false` to avoid duplicate root
667
- * mount assets and competing hydration entrypoints.
668
- */
669
- shouldRenderPageComponent(_input) {
670
- return true;
671
- }
672
619
  /**
673
620
  * Executes the integration renderer with the provided options.
674
621
  *
@@ -676,7 +623,7 @@ class IntegrationRenderer {
676
623
  * 1. Build normalized render options (`prepareRenderOptions`).
677
624
  * 2. Render the route body once.
678
625
  * 3. Reject unresolved route-level eco-marker artifacts.
679
- * 4. Optionally apply root attributes for page/component root boundaries.
626
+ * 4. Optionally apply document attributes for integration-owned document boundaries.
680
627
  * 5. Run HTML transformer with final dependency set.
681
628
  *
682
629
  * Stream-safety note: the first render result is normalized to a string once,
@@ -1,6 +1,6 @@
1
1
  import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
2
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';
3
+ import type { EcoComponent, EcoPageFile, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphContributionContext, PagePackageResult, RouteRendererBody, RouteRendererOptions } from '../../types/public-types.js';
4
4
  import type { RouteHtmlFinalization, RouteRenderOrchestratorAdapter, RouteRenderOrchestratorResolvedInputs } from './route-render-orchestrator.js';
5
5
  /**
6
6
  * Host surface required to build the orchestrator adapter for one integration renderer.
@@ -15,19 +15,12 @@ export type IntegrationRouteRenderAdapterHost<C> = {
15
15
  }>;
16
16
  importPageFile(file: string): Promise<EcoPageFile>;
17
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
18
  renderRouteBody(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
25
19
  getDocumentAttributes(renderOptions: IntegrationRendererRenderOptions<C>): Record<string, string> | undefined;
26
20
  getHtmlDocumentContributions(options: {
27
21
  renderOptions: IntegrationRendererRenderOptions<C>;
28
22
  partial: boolean;
29
23
  }): HtmlDocumentContribution[] | undefined;
30
- applyAttributesToFirstBodyElement(html: string, attributes: Record<string, string>): string;
31
24
  applyAttributesToHtmlElement(html: string, attributes: Record<string, string>): string;
32
25
  transformRouteResponse(response: Response, htmlContributions?: HtmlDocumentContribution[], pagePackage?: PagePackageResult): Promise<RouteRendererBody>;
33
26
  };
@@ -10,13 +10,11 @@ function createIntegrationRouteRenderAdapter(host) {
10
10
  (file) => host.importPageFile(file),
11
11
  (context) => host.collectPageBrowserGraphContribution(context)
12
12
  ),
13
- resolveRoutePageComponentRender: (input) => host.resolveRoutePageComponentRender(input),
14
13
  renderRouteBody: (renderOptions) => host.renderRouteBody(renderOptions),
15
14
  getRouteHtmlFinalization: (renderOptions) => buildRouteHtmlFinalization({
16
15
  renderOptions,
17
16
  getDocumentAttributes: (options) => host.getDocumentAttributes(options),
18
17
  getHtmlDocumentContributions: (options) => host.getHtmlDocumentContributions(options),
19
- applyAttributesToFirstBodyElement: (html, attributes) => host.applyAttributesToFirstBodyElement(html, attributes),
20
18
  applyAttributesToHtmlElement: (html, attributes) => host.applyAttributesToHtmlElement(html, attributes)
21
19
  }),
22
20
  transformRouteResponse: (response, htmlContributions, pagePackage) => host.transformRouteResponse(response, htmlContributions, pagePackage)