@ecopages/core 0.2.0-alpha.26 → 0.2.0-alpha.28

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 (106) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +63 -7
  3. package/package.json +8 -94
  4. package/src/adapters/bun/create-app.d.ts +1 -0
  5. package/src/adapters/bun/create-app.js +39 -2
  6. package/src/adapters/bun/hmr-manager.d.ts +1 -13
  7. package/src/adapters/bun/hmr-manager.js +1 -22
  8. package/src/adapters/bun/server-adapter.js +23 -4
  9. package/src/adapters/node/node-hmr-manager.d.ts +2 -14
  10. package/src/adapters/node/node-hmr-manager.js +2 -23
  11. package/src/adapters/shared/explicit-static-render-preparation.d.ts +25 -0
  12. package/src/adapters/shared/explicit-static-render-preparation.js +26 -0
  13. package/src/adapters/shared/explicit-static-route-matcher.d.ts +5 -2
  14. package/src/adapters/shared/explicit-static-route-matcher.js +14 -16
  15. package/src/adapters/shared/file-route-middleware-pipeline.d.ts +7 -10
  16. package/src/adapters/shared/file-route-middleware-pipeline.js +2 -11
  17. package/src/adapters/shared/fs-server-response-factory.d.ts +13 -9
  18. package/src/adapters/shared/fs-server-response-factory.js +10 -26
  19. package/src/adapters/shared/fs-server-response-matcher.d.ts +14 -6
  20. package/src/adapters/shared/fs-server-response-matcher.js +67 -28
  21. package/src/adapters/shared/render-context.d.ts +2 -2
  22. package/src/adapters/shared/server-adapter.d.ts +21 -10
  23. package/src/adapters/shared/server-adapter.js +171 -132
  24. package/src/adapters/shared/server-route-handler.d.ts +2 -2
  25. package/src/adapters/shared/server-route-handler.js +1 -1
  26. package/src/adapters/shared/server-static-builder.d.ts +4 -4
  27. package/src/config/README.md +1 -1
  28. package/src/config/config-builder.d.ts +2 -2
  29. package/src/config/config-builder.js +0 -5
  30. package/src/dev/host-runtime.d.ts +10 -0
  31. package/src/dev/host-runtime.js +24 -0
  32. package/src/eco/eco.js +7 -7
  33. package/src/eco/eco.types.d.ts +3 -3
  34. package/src/errors/index.d.ts +1 -0
  35. package/src/errors/index.js +3 -1
  36. package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -5
  37. package/src/integrations/ghtml/ghtml-renderer.d.ts +0 -4
  38. package/src/integrations/ghtml/ghtml-renderer.js +1 -7
  39. package/src/plugins/eco-component-meta-plugin.js +0 -1
  40. package/src/plugins/integration-plugin.d.ts +14 -18
  41. package/src/plugins/integration-plugin.js +14 -21
  42. package/src/plugins/processor.d.ts +2 -0
  43. package/src/plugins/processor.js +6 -1
  44. package/src/route-renderer/GRAPH.md +81 -289
  45. package/src/route-renderer/README.md +67 -105
  46. package/src/route-renderer/orchestration/component-render-context.d.ts +24 -18
  47. package/src/route-renderer/orchestration/component-render-context.js +14 -14
  48. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +18 -0
  49. package/src/route-renderer/orchestration/declared-ownership-graph.js +34 -0
  50. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +108 -0
  51. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +206 -0
  52. package/src/route-renderer/orchestration/integration-renderer.d.ts +96 -136
  53. package/src/route-renderer/orchestration/integration-renderer.js +280 -303
  54. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +24 -0
  55. package/src/route-renderer/orchestration/ownership-planning.service.js +63 -0
  56. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +29 -0
  57. package/src/route-renderer/orchestration/ownership-validation.service.js +53 -0
  58. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +90 -0
  59. package/src/route-renderer/orchestration/{queued-boundary-runtime.service.js → queued-foreign-subtree-resolution.service.js} +28 -25
  60. package/src/route-renderer/orchestration/render-output.utils.d.ts +3 -3
  61. package/src/route-renderer/orchestration/render-output.utils.js +6 -6
  62. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +120 -0
  63. package/src/route-renderer/orchestration/{render-preparation.service.js → route-render-orchestrator.js} +132 -108
  64. package/src/route-renderer/page-loading/component-dependency-collection.js +8 -1
  65. package/src/route-renderer/page-loading/dependency-resolver.js +5 -7
  66. package/src/route-renderer/page-loading/page-dependency-bundling.d.ts +1 -1
  67. package/src/route-renderer/page-loading/page-dependency-bundling.js +41 -19
  68. package/src/route-renderer/route-renderer.d.ts +28 -26
  69. package/src/route-renderer/route-renderer.js +4 -27
  70. package/src/router/README.md +16 -19
  71. package/src/router/server/route-registry.d.ts +78 -0
  72. package/src/router/server/route-registry.js +262 -0
  73. package/src/services/README.md +1 -2
  74. package/src/services/assets/asset-processing-service/assets.types.d.ts +3 -0
  75. package/src/services/assets/asset-processing-service/index.d.ts +1 -0
  76. package/src/services/assets/asset-processing-service/index.js +1 -0
  77. package/src/services/assets/asset-processing-service/page-package.d.ts +3 -0
  78. package/src/services/assets/asset-processing-service/page-package.js +74 -0
  79. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +4 -4
  80. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +6 -3
  81. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +9 -3
  82. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +4 -2
  83. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -1
  84. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +3 -1
  85. package/src/services/module-loading/node-bootstrap-plugin.js +15 -3
  86. package/src/static-site-generator/static-site-generator.d.ts +20 -21
  87. package/src/static-site-generator/static-site-generator.js +107 -140
  88. package/src/types/internal-types.d.ts +13 -12
  89. package/src/types/public-types.d.ts +46 -36
  90. package/src/watchers/project-watcher.test-helpers.js +5 -5
  91. package/src/route-renderer/orchestration/boundary-planning.service.d.ts +0 -25
  92. package/src/route-renderer/orchestration/boundary-planning.service.js +0 -97
  93. package/src/route-renderer/orchestration/page-packaging.service.d.ts +0 -16
  94. package/src/route-renderer/orchestration/page-packaging.service.js +0 -66
  95. package/src/route-renderer/orchestration/queued-boundary-runtime.service.d.ts +0 -89
  96. package/src/route-renderer/orchestration/render-execution.service.d.ts +0 -43
  97. package/src/route-renderer/orchestration/render-execution.service.js +0 -106
  98. package/src/route-renderer/orchestration/render-preparation.service.d.ts +0 -120
  99. package/src/route-renderer/orchestration/route-shell-composer.service.d.ts +0 -50
  100. package/src/route-renderer/orchestration/route-shell-composer.service.js +0 -81
  101. package/src/router/server/fs-router-scanner.d.ts +0 -41
  102. package/src/router/server/fs-router-scanner.js +0 -161
  103. package/src/router/server/fs-router.d.ts +0 -26
  104. package/src/router/server/fs-router.js +0 -100
  105. package/src/services/runtime-state/runtime-specifier-registry.service.d.ts +0 -69
  106. package/src/services/runtime-state/runtime-specifier-registry.service.js +0 -37
@@ -0,0 +1,206 @@
1
+ import {
2
+ getComponentRenderContext,
3
+ runWithComponentRenderContext
4
+ } from "./component-render-context.js";
5
+ import {
6
+ QueuedForeignSubtreeResolutionService
7
+ } from "./queued-foreign-subtree-resolution.service.js";
8
+ class ForeignSubtreeExecutionService {
9
+ queuedForeignSubtreeResolutionService;
10
+ constructor(queuedForeignSubtreeResolutionService = new QueuedForeignSubtreeResolutionService()) {
11
+ this.queuedForeignSubtreeResolutionService = queuedForeignSubtreeResolutionService;
12
+ }
13
+ /**
14
+ * Returns whether the current render pass must hand the child off to a foreign owner.
15
+ */
16
+ shouldDelegateForeignChild(input) {
17
+ return !!input.targetIntegration && input.targetIntegration !== input.currentIntegration;
18
+ }
19
+ /**
20
+ * Creates the base runtime used when a renderer has not supplied its own queueing runtime.
21
+ *
22
+ * The runtime allows same-integration children to continue inline and fails fast
23
+ * when execution crosses into a foreign owner without a renderer-owned handoff.
24
+ */
25
+ createFailFastRuntime(rendererName) {
26
+ const interceptForeignChild = (input) => {
27
+ if (!this.shouldDelegateForeignChild(input)) {
28
+ return { kind: "inline" };
29
+ }
30
+ throw new Error(
31
+ `[ecopages] ${rendererName} renderer crossed into ${input.targetIntegration} without a renderer-owned foreign-child runtime. Override createForeignChildRuntime() to resolve foreign children inside the owning renderer.`
32
+ );
33
+ };
34
+ return {
35
+ interceptForeignChild,
36
+ interceptForeignChildSync: interceptForeignChild
37
+ };
38
+ }
39
+ getQueuedRuntimeContext(input, runtimeContextKey) {
40
+ return this.queuedForeignSubtreeResolutionService.getRuntimeContext(input, runtimeContextKey);
41
+ }
42
+ createQueuedRuntime(options) {
43
+ return this.queuedForeignSubtreeResolutionService.createRuntime({
44
+ renderInput: options.renderInput,
45
+ rendererCache: options.rendererCache,
46
+ runtimeContextKey: options.runtimeContextKey,
47
+ tokenPrefix: options.tokenPrefix,
48
+ shouldQueueForeignChild: (input) => this.shouldDelegateForeignChild(input),
49
+ createRuntimeContext: options.createRuntimeContext
50
+ });
51
+ }
52
+ /**
53
+ * Resolves a string-first renderer HTML fragment that may contain queued Foreign Subtree tokens.
54
+ */
55
+ async resolveStringQueuedHtml(options) {
56
+ const runtimeContext = this.getQueuedRuntimeContext(options.renderInput, options.runtimeContextKey);
57
+ return this.resolveQueuedHtml({
58
+ currentIntegrationName: options.currentIntegrationName,
59
+ html: options.html,
60
+ runtimeContext,
61
+ queueLabel: options.queueLabel,
62
+ renderQueuedChildren: async (children, _runtimeContext, queuedResolutionsByToken, resolveToken) => {
63
+ if (children === void 0) {
64
+ return { assets: [], html: void 0 };
65
+ }
66
+ const html = await this.resolveQueuedTokens(
67
+ typeof children === "string" ? children : String(children ?? ""),
68
+ queuedResolutionsByToken,
69
+ resolveToken
70
+ );
71
+ return { assets: [], html };
72
+ },
73
+ getOwningRenderer: options.getOwningRenderer,
74
+ applyAttributesToFirstElement: options.applyAttributesToFirstElement,
75
+ dedupeProcessedAssets: options.dedupeProcessedAssets
76
+ });
77
+ }
78
+ async resolveQueuedHtml(options) {
79
+ return this.queuedForeignSubtreeResolutionService.resolveQueuedHtml({
80
+ html: options.html,
81
+ runtimeContext: options.runtimeContext,
82
+ queueLabel: options.queueLabel,
83
+ renderQueuedChildren: options.renderQueuedChildren,
84
+ resolveForeignSubtree: (input, rendererCache) => this.resolveForeignSubtreeInOwningRenderer({
85
+ currentIntegrationName: options.currentIntegrationName,
86
+ input,
87
+ rendererCache,
88
+ getOwningRenderer: options.getOwningRenderer
89
+ }),
90
+ applyAttributesToFirstElement: options.applyAttributesToFirstElement,
91
+ dedupeProcessedAssets: options.dedupeProcessedAssets
92
+ });
93
+ }
94
+ /**
95
+ * Executes one component render with Foreign Child support under the current integration.
96
+ */
97
+ async executeComponentRender(options) {
98
+ const rendererCache = this.getRendererCache(options.input.integrationContext) ?? /* @__PURE__ */ new Map();
99
+ const delegatedForeignChildRender = await this.resolveForeignChildInOwningRenderer({
100
+ currentIntegrationName: options.currentIntegrationName,
101
+ input: options.input,
102
+ rendererCache,
103
+ getOwningRenderer: options.getOwningRenderer
104
+ });
105
+ if (delegatedForeignChildRender) {
106
+ return delegatedForeignChildRender;
107
+ }
108
+ const hasForeignChildren = options.hasForeignChildDescendants(options.input.component);
109
+ const activeRenderContext = getComponentRenderContext();
110
+ if (!hasForeignChildren) {
111
+ if (!activeRenderContext || activeRenderContext.currentIntegration === options.currentIntegrationName) {
112
+ return options.normalizeComponentRenderOutput(await options.renderComponent(options.input));
113
+ }
114
+ const sameIntegrationExecution = await runWithComponentRenderContext(
115
+ {
116
+ currentIntegration: options.currentIntegrationName
117
+ },
118
+ async () => options.renderComponent(options.input)
119
+ );
120
+ return options.normalizeComponentRenderOutput(sameIntegrationExecution.value);
121
+ }
122
+ const execution = await runWithComponentRenderContext(
123
+ {
124
+ currentIntegration: options.currentIntegrationName,
125
+ foreignChildRuntime: options.createForeignChildRuntime({
126
+ renderInput: options.input,
127
+ rendererCache
128
+ })
129
+ },
130
+ async () => options.renderComponent(options.input)
131
+ );
132
+ return options.normalizeComponentRenderOutput(execution.value);
133
+ }
134
+ getRendererCache(integrationContext) {
135
+ if (integrationContext?.rendererCache instanceof Map) {
136
+ return integrationContext.rendererCache;
137
+ }
138
+ return void 0;
139
+ }
140
+ withRendererCache(input, rendererCache) {
141
+ const integrationContext = input.integrationContext;
142
+ const sharedRendererCache = rendererCache;
143
+ return {
144
+ ...input,
145
+ integrationContext: integrationContext ? { ...integrationContext, rendererCache: sharedRendererCache } : { rendererCache: sharedRendererCache }
146
+ };
147
+ }
148
+ /**
149
+ * Returns the delegatable owning renderer integration for one component.
150
+ *
151
+ * The pseudo `html` integration marks document-shell ownership only and does
152
+ * not participate in component-level foreign subtree execution.
153
+ */
154
+ getForeignOwnerIntegrationName(component, currentIntegrationName) {
155
+ const integrationName = component.config?.integration ?? component.config?.__eco?.integration;
156
+ if (!integrationName || integrationName === "html" || integrationName === currentIntegrationName) {
157
+ return void 0;
158
+ }
159
+ return integrationName;
160
+ }
161
+ async resolveForeignChildInOwningRenderer(options) {
162
+ return await this.runInForeignOwningRenderer({
163
+ currentIntegrationName: options.currentIntegrationName,
164
+ input: options.input,
165
+ rendererCache: options.rendererCache,
166
+ getOwningRenderer: options.getOwningRenderer,
167
+ run: (owningRenderer, delegatedInput) => owningRenderer.renderComponentWithForeignChildren(delegatedInput)
168
+ });
169
+ }
170
+ async resolveForeignSubtreeInOwningRenderer(options) {
171
+ return await this.runInForeignOwningRenderer({
172
+ currentIntegrationName: options.currentIntegrationName,
173
+ input: options.input,
174
+ rendererCache: options.rendererCache,
175
+ getOwningRenderer: options.getOwningRenderer,
176
+ run: (owningRenderer, delegatedInput) => owningRenderer.renderForeignSubtree(delegatedInput)
177
+ });
178
+ }
179
+ async runInForeignOwningRenderer(options) {
180
+ const foreignOwnerIntegrationName = this.getForeignOwnerIntegrationName(
181
+ options.input.component,
182
+ options.currentIntegrationName
183
+ );
184
+ if (!foreignOwnerIntegrationName) {
185
+ return void 0;
186
+ }
187
+ const owningRenderer = options.getOwningRenderer(foreignOwnerIntegrationName, options.rendererCache);
188
+ if (owningRenderer.name === options.currentIntegrationName) {
189
+ return void 0;
190
+ }
191
+ return await options.run(owningRenderer, this.withRendererCache(options.input, options.rendererCache));
192
+ }
193
+ async resolveQueuedTokens(html, queuedResolutionsByToken, resolveToken) {
194
+ let resolvedHtml = html;
195
+ for (const token of queuedResolutionsByToken.keys()) {
196
+ if (!resolvedHtml.includes(token)) {
197
+ continue;
198
+ }
199
+ resolvedHtml = resolvedHtml.split(token).join(await resolveToken(token));
200
+ }
201
+ return resolvedHtml;
202
+ }
203
+ }
204
+ export {
205
+ ForeignSubtreeExecutionService
206
+ };
@@ -4,22 +4,16 @@
4
4
  * @module
5
5
  */
6
6
  import type { EcoPagesAppConfig, IHmrManager } from '../../types/internal-types.js';
7
- import type { ComponentRenderInput, ComponentRenderResult, BoundaryRenderPayload, EcoComponent, EcoComponentDependencies, EcoPageComponent, EcoPageFile, EcoPagesElement, GetMetadata, GetMetadataContext, GetStaticProps, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
7
+ import type { ComponentRenderInput, ComponentRenderResult, ForeignSubtreeRenderPayload, EcoComponent, EcoComponentDependencies, EcoPageFile, EcoPagesElement, BaseIntegrationContext, HtmlTemplateProps, IntegrationRendererRenderOptions, PageMetadataProps, 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 { HttpError } from '../../errors/http-error.js';
11
11
  import { DependencyResolverService } from '../page-loading/dependency-resolver.js';
12
12
  import { PageModuleLoaderService } from '../page-loading/page-module-loader.js';
13
- import { PagePackagingService } from './page-packaging.service.js';
14
- import { RenderExecutionService } from './render-execution.service.js';
15
- import { RenderPreparationService } from './render-preparation.service.js';
16
- import { RouteShellComposer } from './route-shell-composer.service.js';
17
- import type { ComponentBoundaryRuntime } from './component-render-context.js';
18
- import { QueuedBoundaryRuntimeService, type QueuedBoundaryResolution, type QueuedBoundaryRuntimeContext } from './queued-boundary-runtime.service.js';
19
- type BoundaryRenderDecisionInput = {
20
- currentIntegration: string;
21
- targetIntegration?: string;
22
- };
13
+ import { type RouteHtmlFinalization, RouteRenderOrchestrator, type RouteRenderOrchestratorAdapter, type RouteRenderOrchestratorResolvedInputs } from './route-render-orchestrator.js';
14
+ import type { ForeignChildRuntime } from './component-render-context.js';
15
+ import { ForeignSubtreeExecutionService, type ForeignSubtreeExecutionOwningRenderer } from './foreign-subtree-execution.service.js';
16
+ import { type QueuedForeignSubtreeResolutionContext } from './queued-foreign-subtree-resolution.service.js';
23
17
  /**
24
18
  * Controls how one route module is loaded outside the normal render path.
25
19
  *
@@ -56,11 +50,8 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
56
50
  protected runtimeOrigin: string;
57
51
  protected dependencyResolverService: DependencyResolverService;
58
52
  protected pageModuleLoaderService: PageModuleLoaderService;
59
- protected renderPreparationService: RenderPreparationService;
60
- protected renderExecutionService: RenderExecutionService;
61
- protected pagePackagingService: PagePackagingService;
62
- protected readonly routeShellComposer: RouteShellComposer;
63
- protected readonly queuedBoundaryRuntimeService: QueuedBoundaryRuntimeService;
53
+ protected routeRenderOrchestrator: RouteRenderOrchestrator;
54
+ protected readonly foreignSubtreeExecutionService: ForeignSubtreeExecutionService;
64
55
  protected DOC_TYPE: string;
65
56
  /**
66
57
  * Loads one route module through the owning renderer's import path.
@@ -72,7 +63,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
72
63
  */
73
64
  loadPageModule(file: string, options?: RouteModuleLoadOptions): Promise<EcoPageFile>;
74
65
  /**
75
- * Reads the execution-scoped foreign renderer cache from one boundary input.
66
+ * Reads the execution-scoped owning-renderer cache from one render input.
76
67
  *
77
68
  * Shared page/layout/document shell helpers pass one cache through
78
69
  * `integrationContext` so repeated delegation to the same foreign integration
@@ -81,24 +72,24 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
81
72
  * stored on the renderer, which avoids leaking mutable integration state across
82
73
  * requests while still preventing redundant renderer initialization.
83
74
  *
84
- * @param integrationContext - Optional boundary context carried with one render input.
75
+ * @param integrationContext - Optional render context carried with one render input.
85
76
  * @returns The current execution cache when present.
86
77
  */
87
- private getBoundaryRendererCache;
88
- private getRegisteredBoundaryOwner;
78
+ private getOwningRendererCache;
79
+ private getForeignOwnerIntegrationName;
89
80
  /**
90
- * Attaches an execution-scoped foreign renderer cache to one boundary input.
81
+ * Attaches an execution-scoped owning-renderer cache to one render input.
91
82
  *
92
83
  * Foreign-owned page, layout, or document shells may delegate several times in
93
84
  * the same render flow. Threading the cache through `integrationContext`
94
- * preserves renderer reuse without changing the public boundary input contract.
85
+ * preserves renderer reuse without changing the public render input contract.
95
86
  * Existing integration-specific context is preserved and augmented.
96
87
  *
97
- * @param input - Original boundary render input.
88
+ * @param input - Original render input.
98
89
  * @param rendererCache - Execution-scoped renderer cache to propagate.
99
- * @returns Boundary input augmented with the shared renderer cache.
90
+ * @returns Render input augmented with the shared renderer cache.
100
91
  */
101
- private withBoundaryRendererCache;
92
+ private withOwningRendererCache;
102
93
  protected getRendererModuleValue(key: string): unknown;
103
94
  protected getRendererModuleString(key: string): string | undefined;
104
95
  protected getRendererBootstrapDependencies(partial?: boolean): ProcessedAsset[];
@@ -136,7 +127,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
136
127
  * Merges component-scoped assets into the active HTML transformer state.
137
128
  *
138
129
  * Explicit page, layout, and document shell composition can produce assets at
139
- * each boundary. This helper deduplicates those groups and folds them back into
130
+ * each foreign subtree. This helper deduplicates those groups and folds them back into
140
131
  * the transformer so downstream HTML finalization sees one canonical asset set.
141
132
  *
142
133
  * @param assetGroups - Optional groups of processed assets to merge.
@@ -160,7 +151,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
160
151
  *
161
152
  * Same-integration views can optionally stream or render inline via the caller's
162
153
  * `renderInline()` hook. Once a view may cross integration boundaries, this
163
- * helper routes the render through `renderComponentBoundary()` instead so mixed
154
+ * helper routes the render through `renderComponentWithForeignChildren()` instead so mixed
164
155
  * shells can reuse the execution-scoped renderer cache and resolve nested
165
156
  * foreign ownership before the partial response is returned.
166
157
  *
@@ -195,10 +186,10 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
195
186
  /**
196
187
  * Renders a route page through optional layout and document shells.
197
188
  *
198
- * Route rendering and explicit view rendering now share the same boundary-owned
189
+ * Route rendering and explicit view rendering now share the same renderer-owned
199
190
  * shell composition model. This helper composes page, layout, and html template
200
- * boundaries while threading one execution-scoped renderer cache through every
201
- * delegated boundary so foreign shell ownership remains stable and renderer
191
+ * renders while threading one execution-scoped renderer cache through every
192
+ * delegated foreign subtree so foreign shell ownership remains stable and renderer
202
193
  * initialization is reused inside the current request.
203
194
  *
204
195
  * @param input - Page, layout, document, and metadata inputs for the route render.
@@ -219,48 +210,36 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
219
210
  documentProps?: Record<string, unknown>;
220
211
  transformDocumentHtml?: (html: string) => string;
221
212
  }): Promise<string>;
213
+ private composeDocumentShell;
222
214
  /**
223
- * Renders one string-first component boundary and collects its assets.
215
+ * Renders one string-first component with serialized children and collects its assets.
224
216
  *
225
- * String-oriented integrations frequently share the same boundary contract:
217
+ * String-oriented integrations frequently share the same component contract:
226
218
  * pass serialized children through props, coerce the render result to HTML, and
227
219
  * attach any component-scoped dependencies. This helper centralizes that flow
228
220
  * so integrations can opt into shared orchestration without repeating the same
229
- * boundary boilerplate.
221
+ * string-render boilerplate.
230
222
  *
231
- * @param input - Boundary render input.
223
+ * @param input - Component render input.
232
224
  * @param component - String-oriented component implementation to execute.
233
225
  * @returns Structured component render result for orchestration paths.
234
226
  */
235
- protected renderStringComponentBoundary(input: ComponentRenderInput, component: (props: Record<string, unknown>) => Promise<EcoPagesElement> | EcoPagesElement): Promise<ComponentRenderResult>;
236
- protected getBoundaryTokenPrefix(): string;
237
- protected getBoundaryRuntimeContextKey(): string;
238
- protected getQueuedBoundaryRuntime<TContext extends QueuedBoundaryRuntimeContext>(input: ComponentRenderInput, runtimeContextKey?: string): TContext | undefined;
239
- protected resolveQueuedBoundaryTokens(html: string, queuedResolutionsByToken: Map<string, QueuedBoundaryResolution>, resolveToken: (token: string) => Promise<string>): Promise<string>;
240
- protected createQueuedBoundaryRuntime<TContext extends QueuedBoundaryRuntimeContext>(options: {
241
- boundaryInput: ComponentRenderInput;
227
+ protected renderStringComponentWithSerializedChildren(input: ComponentRenderInput, component: (props: Record<string, unknown>) => Promise<EcoPagesElement> | EcoPagesElement): Promise<ComponentRenderResult>;
228
+ protected getForeignSubtreeTokenPrefix(): string;
229
+ protected getForeignSubtreeResolutionContextKey(): string;
230
+ protected createQueuedForeignSubtreeExecutionRuntime<TContext extends QueuedForeignSubtreeResolutionContext>(options: {
231
+ renderInput: ComponentRenderInput;
242
232
  rendererCache: Map<string, IntegrationRenderer<any>>;
243
233
  runtimeContextKey?: string;
244
234
  tokenPrefix?: string;
245
235
  createRuntimeContext?: (integrationContext: BaseIntegrationContext & Record<string, unknown>, rendererCache: Map<string, unknown>) => TContext;
246
- }): ComponentBoundaryRuntime;
247
- protected resolveRendererOwnedQueuedBoundaryHtml<TContext extends QueuedBoundaryRuntimeContext>(options: {
248
- html: string;
249
- runtimeContext?: TContext;
250
- queueLabel: string;
251
- renderQueuedChildren: (children: unknown, runtimeContext: TContext, queuedResolutionsByToken: Map<string, QueuedBoundaryResolution>, resolveToken: (token: string) => Promise<string>) => Promise<{
252
- assets: ProcessedAsset[];
253
- html?: string;
254
- }>;
255
- }): Promise<{
256
- assets: ProcessedAsset[];
257
- html: string;
258
- }>;
236
+ }): ForeignChildRuntime;
237
+ protected getQueuedForeignSubtreeResolutionContext<TContext extends QueuedForeignSubtreeResolutionContext>(input: ComponentRenderInput): TContext | undefined;
259
238
  /**
260
239
  * Renders a string-first component, then resolves any queued foreign
261
240
  * boundaries before returning final component HTML.
262
241
  */
263
- protected renderStringComponentBoundaryWithQueuedForeignBoundaries(input: ComponentRenderInput, component: (props: Record<string, unknown>) => Promise<EcoPagesElement> | EcoPagesElement): Promise<ComponentRenderResult>;
242
+ protected renderStringComponentWithQueuedForeignSubtrees(input: ComponentRenderInput, component: (props: Record<string, unknown>) => Promise<EcoPagesElement> | EcoPagesElement): Promise<ComponentRenderResult>;
264
243
  constructor({ appConfig, assetProcessingService, resolvedIntegrationDependencies, rendererModules, runtimeOrigin, }: {
265
244
  appConfig: EcoPagesAppConfig;
266
245
  assetProcessingService: AssetProcessingService;
@@ -285,27 +264,6 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
285
264
  * @returns The HTML template component.
286
265
  */
287
266
  protected getHtmlTemplate(): Promise<EcoComponent<HtmlTemplateProps>>;
288
- /**
289
- * Returns the static props for the page.
290
- * It calls the provided getStaticProps function with the given options.
291
- *
292
- * @param getStaticProps - The function to get static props.
293
- * @param options - The options to pass to the getStaticProps function.
294
- * @returns The static props and metadata.
295
- */
296
- protected getStaticProps(getStaticProps?: GetStaticProps<Record<string, unknown>>, options?: Pick<RouteRendererOptions, 'params'>): Promise<{
297
- props: Record<string, unknown>;
298
- metadata?: PageMetadataProps;
299
- }>;
300
- /**
301
- * Returns the metadata properties for the page.
302
- * It calls the provided getMetadata function with the given context.
303
- *
304
- * @param getMetadata - The function to get metadata.
305
- * @param context - The context to pass to the getMetadata function.
306
- * @returns The metadata properties.
307
- */
308
- protected getMetadataProps(getMetadata: GetMetadata | undefined, { props, params, query }: GetMetadataContext): Promise<PageMetadataProps>;
309
267
  protected usesIntegrationPageImporter(_file: string): boolean;
310
268
  protected importIntegrationPageFile(_file: string, _options?: RouteModuleLoadOptions): Promise<EcoPageFile>;
311
269
  protected normalizeImportedPageFile<TPageModule extends EcoPageFile>(_file: string, pageModule: TPageModule): TPageModule;
@@ -361,6 +319,34 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
361
319
  * @param components - The components to collect dependencies from.
362
320
  */
363
321
  protected processComponentDependencies(components: (EcoComponent | Partial<EcoComponent>)[]): Promise<ProcessedAsset[]>;
322
+ /**
323
+ * Builds the internal route-render adapter consumed by `RouteRenderOrchestrator`.
324
+ *
325
+ * The route orchestrator needs a narrow orchestration contract, but those hooks should
326
+ * not become public API on the renderer base class. Keeping the adapter object
327
+ * local to the execution path lets the orchestrator depend on one explicit seam while
328
+ * subclasses continue to override protected renderer behavior directly.
329
+ */
330
+ protected createRouteRenderOrchestratorAdapter(): RouteRenderOrchestratorAdapter<C>;
331
+ protected resolveRouteRenderInputs(routeOptions: RouteRendererOptions): Promise<RouteRenderOrchestratorResolvedInputs>;
332
+ protected resolveRouteAssets(input: {
333
+ routeOptions: RouteRendererOptions;
334
+ components: (EcoComponent | Partial<EcoComponent>)[];
335
+ }): Promise<{
336
+ resolvedDependencies: ProcessedAsset[];
337
+ pageBrowserGraph?: {
338
+ assets: ProcessedAsset[];
339
+ };
340
+ }>;
341
+ protected resolveRoutePageComponentRender(input: {
342
+ Page: EcoComponent;
343
+ Layout?: EcoComponent;
344
+ props: Record<string, unknown>;
345
+ routeOptions: RouteRendererOptions;
346
+ }): Promise<ComponentRenderResult | undefined>;
347
+ protected renderRouteBody(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
348
+ protected getRouteHtmlFinalization(renderOptions: IntegrationRendererRenderOptions<C>): RouteHtmlFinalization;
349
+ protected transformRouteResponse(response: Response): Promise<RouteRendererBody>;
364
350
  /**
365
351
  * Prepares the render options for the integration renderer.
366
352
  * It imports the page file, collects dependencies, and prepares the render options.
@@ -368,7 +354,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
368
354
  * @param options - The route renderer options.
369
355
  * @returns The prepared render options.
370
356
  */
371
- protected prepareRenderOptions(options: RouteRendererOptions): Promise<IntegrationRendererRenderOptions>;
357
+ protected prepareRenderOptions(options: RouteRendererOptions, adapter?: RouteRenderOrchestratorAdapter<C>): Promise<IntegrationRendererRenderOptions<C>>;
372
358
  /**
373
359
  * Controls whether the page root should be rendered through `renderComponent()`
374
360
  * during route option preparation in component-capable modes.
@@ -382,32 +368,13 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
382
368
  Layout?: EcoComponent;
383
369
  options: RouteRendererOptions;
384
370
  }): boolean;
385
- /**
386
- * Resolves the page module and normalizes exports.
387
- */
388
- protected resolvePageModule(file: string): Promise<{
389
- Page: EcoPageFile['default'] | EcoPageComponent<any>;
390
- getStaticProps?: GetStaticProps<Record<string, unknown>>;
391
- getMetadata?: GetMetadata;
392
- integrationSpecificProps: Record<string, unknown>;
393
- }>;
394
- /**
395
- * Resolves static props and metadata for the page.
396
- */
397
- protected resolvePageData(pageModule: {
398
- getStaticProps?: GetStaticProps<Record<string, unknown>>;
399
- getMetadata?: GetMetadata;
400
- }, options: RouteRendererOptions): Promise<{
401
- props: Record<string, unknown>;
402
- metadata: PageMetadataProps;
403
- }>;
404
371
  /**
405
372
  * Executes the integration renderer with the provided options.
406
373
  *
407
374
  * Execution flow:
408
375
  * 1. Build normalized render options (`prepareRenderOptions`).
409
376
  * 2. Render the route body once.
410
- * 3. Reject unresolved route-level boundary artifacts.
377
+ * 3. Reject unresolved route-level eco-marker artifacts.
411
378
  * 4. Optionally apply root attributes for page/component root boundaries.
412
379
  * 5. Run HTML transformer with final dependency set.
413
380
  *
@@ -423,7 +390,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
423
390
  * Finalizes already-resolved HTML for explicit renderer-owned paths.
424
391
  *
425
392
  * This keeps document and root-attribute stamping plus HTML transformation
426
- * available after a renderer has completed nested boundary resolution without
393
+ * available after a renderer has completed nested foreign-subtree resolution without
427
394
  * routing back through shared route execution.
428
395
  */
429
396
  protected finalizeResolvedHtml(options: {
@@ -450,7 +417,7 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
450
417
  * @returns Renderer for the requested integration.
451
418
  * @throws Error when no integration plugin matches `integrationName`.
452
419
  */
453
- private getIntegrationRendererForName;
420
+ protected getIntegrationRendererForName(integrationName: string, cache: Map<string, ForeignSubtreeExecutionOwningRenderer>): ForeignSubtreeExecutionOwningRenderer;
454
421
  /**
455
422
  * Abstract method to render the integration-specific component.
456
423
  * This method should be implemented by the specific integration renderer.
@@ -459,33 +426,31 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
459
426
  * @returns The rendered body.
460
427
  */
461
428
  abstract render(options: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
462
- protected resolveBoundaryInOwningRenderer(input: ComponentRenderInput, rendererCache: Map<string, IntegrationRenderer<any>>): Promise<ComponentRenderResult | undefined>;
463
- protected resolveBoundaryPayloadInOwningRenderer(input: ComponentRenderInput, rendererCache: Map<string, IntegrationRenderer<any>>): Promise<BoundaryRenderPayload | undefined>;
464
429
  /**
465
- * Renders one component under this integration's boundary runtime and resolves
466
- * any nested foreign boundaries captured during that render.
430
+ * Renders one component under this integration's foreign-child runtime and resolves
431
+ * any nested foreign children captured during that render.
467
432
  *
468
433
  * Without this wrapper, a component tree with foreign-owned descendants would
469
- * render them with no active boundary runtime, which bypasses the owning
470
- * renderer's nested-boundary handoff.
434
+ * render them with no active foreign-child runtime, which bypasses the owning
435
+ * renderer's nested foreign-child handoff.
471
436
  */
472
- renderComponentBoundary(input: ComponentRenderInput): Promise<ComponentRenderResult>;
437
+ renderComponentWithForeignChildren(input: ComponentRenderInput): Promise<ComponentRenderResult>;
473
438
  /**
474
- * Compatibility boundary contract that exposes a narrower payload shape for
439
+ * Compatibility foreign-subtree contract that exposes a narrower payload shape for
475
440
  * future route-composition work while preserving the current
476
- * `renderComponentBoundary()` runtime semantics.
441
+ * `renderComponentWithForeignChildren()` runtime semantics.
477
442
  */
478
- renderBoundary(input: ComponentRenderInput): Promise<BoundaryRenderPayload>;
479
- private normalizeComponentBoundaryRender;
480
- protected normalizeBoundaryArtifactHtml(html: string): string;
443
+ renderForeignSubtree(input: ComponentRenderInput): Promise<ForeignSubtreeRenderPayload>;
444
+ private normalizeComponentRenderOutput;
445
+ protected normalizeUnresolvedMarkerArtifactHtml(html: string): string;
481
446
  /**
482
447
  * Returns whether the component dependency tree crosses into another
483
448
  * integration.
484
449
  *
485
- * This keeps boundary-runtime setup narrow: same-integration trees can render
450
+ * This keeps foreign-child runtime setup narrow: same-integration trees can render
486
451
  * directly without paying the queue orchestration cost.
487
452
  */
488
- protected hasForeignBoundaryDescendants(component: EcoComponent): boolean;
453
+ protected hasForeignChildDescendants(component: EcoComponent): boolean;
489
454
  /**
490
455
  * Render a view directly to a Response object.
491
456
  * Used for explicit routing where views are rendered from route handlers.
@@ -502,8 +467,8 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
502
467
  * Default behavior delegates to `renderToResponse` in partial mode and wraps
503
468
  * the resulting HTML into the `ComponentRenderResult` contract.
504
469
  *
505
- * In boundary resolution, this method is the integration-owned step that turns an
506
- * already-resolved deferred boundary into concrete HTML, assets, and optional
470
+ * In foreign-subtree resolution, this method is the integration-owned step that turns an
471
+ * already-resolved deferred foreign subtree into concrete HTML, assets, and optional
507
472
  * root attributes.
508
473
  *
509
474
  * Integrations can override this for richer behavior (asset emission,
@@ -521,36 +486,31 @@ export declare abstract class IntegrationRenderer<C = EcoPagesElement> {
521
486
  */
522
487
  protected getRootTagName(html: string): string | undefined;
523
488
  /**
524
- * Method to build route render assets.
489
+ * Builds the Page Browser Graph owned by this integration for one Page.
525
490
  * This method can be optionally overridden by the specific integration renderer.
526
491
  *
527
492
  * @param file - The file path to build assets for.
528
- * @returns The processed assets or undefined.
493
+ * @returns The structured Page Browser Graph or undefined.
529
494
  */
530
- protected buildRouteRenderAssets(_file: string): Promise<ProcessedAsset[]> | undefined;
495
+ protected buildPageBrowserGraph(_file: string): Promise<{
496
+ assets: ProcessedAsset[];
497
+ } | undefined>;
531
498
  /**
532
- * Creates the per-render boundary runtime adopted by the shared component
499
+ * Creates the per-render foreign-child runtime adopted by the shared component
533
500
  * render context.
534
501
  *
535
- * Real mixed-integration renderers should override this and keep foreign
536
- * boundary resolution inside their own renderer-owned queue. The base runtime
537
- * fails fast when a renderer crosses into a foreign owner without providing its
538
- * own handoff mechanism.
502
+ * The default runtime queues delegated foreign subtrees inside the owning
503
+ * renderer so string and markup renderers do not need to re-declare the same
504
+ * handoff boilerplate. Override only when a renderer needs custom runtime
505
+ * context or a different foreign-child execution strategy.
539
506
  */
540
- protected createComponentBoundaryRuntime(_options: {
541
- boundaryInput: ComponentRenderInput;
507
+ protected createForeignChildRuntime(options: {
508
+ renderInput: ComponentRenderInput;
542
509
  rendererCache: Map<string, IntegrationRenderer<any>>;
543
- }): ComponentBoundaryRuntime;
510
+ }): ForeignChildRuntime;
544
511
  /**
545
- * Resolves whether a boundary should leave the current render pass and be
546
- * resolved by its owning renderer.
547
- *
548
- * Boundaries owned by the current integration always render inline. Foreign-
549
- * owned boundaries must be handed off by a renderer-owned runtime.
550
- *
551
- * @param input Boundary metadata for the active render pass.
552
- * @returns `true` when the boundary should leave the current pass; otherwise `false`.
512
+ * Creates an explicit fail-fast runtime for tests or renderers that do not
513
+ * support cross-integration foreign-child execution.
553
514
  */
554
- protected shouldResolveBoundaryInOwningRenderer(input: BoundaryRenderDecisionInput): boolean;
515
+ protected createFailFastForeignChildRuntime(): ForeignChildRuntime;
555
516
  }
556
- export {};