@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.
- package/CHANGELOG.md +25 -0
- package/README.md +63 -7
- package/package.json +8 -94
- package/src/adapters/bun/create-app.d.ts +1 -0
- package/src/adapters/bun/create-app.js +39 -2
- package/src/adapters/bun/hmr-manager.d.ts +1 -13
- package/src/adapters/bun/hmr-manager.js +1 -22
- package/src/adapters/bun/server-adapter.js +23 -4
- package/src/adapters/node/node-hmr-manager.d.ts +2 -14
- package/src/adapters/node/node-hmr-manager.js +2 -23
- package/src/adapters/shared/explicit-static-render-preparation.d.ts +25 -0
- package/src/adapters/shared/explicit-static-render-preparation.js +26 -0
- package/src/adapters/shared/explicit-static-route-matcher.d.ts +5 -2
- package/src/adapters/shared/explicit-static-route-matcher.js +14 -16
- package/src/adapters/shared/file-route-middleware-pipeline.d.ts +7 -10
- package/src/adapters/shared/file-route-middleware-pipeline.js +2 -11
- package/src/adapters/shared/fs-server-response-factory.d.ts +13 -9
- package/src/adapters/shared/fs-server-response-factory.js +10 -26
- package/src/adapters/shared/fs-server-response-matcher.d.ts +14 -6
- package/src/adapters/shared/fs-server-response-matcher.js +67 -28
- package/src/adapters/shared/render-context.d.ts +2 -2
- package/src/adapters/shared/server-adapter.d.ts +21 -10
- package/src/adapters/shared/server-adapter.js +171 -132
- package/src/adapters/shared/server-route-handler.d.ts +2 -2
- package/src/adapters/shared/server-route-handler.js +1 -1
- package/src/adapters/shared/server-static-builder.d.ts +4 -4
- package/src/config/README.md +1 -1
- package/src/config/config-builder.d.ts +2 -2
- package/src/config/config-builder.js +0 -5
- package/src/dev/host-runtime.d.ts +10 -0
- package/src/dev/host-runtime.js +24 -0
- package/src/eco/eco.js +7 -7
- package/src/eco/eco.types.d.ts +3 -3
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +3 -1
- package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -5
- package/src/integrations/ghtml/ghtml-renderer.d.ts +0 -4
- package/src/integrations/ghtml/ghtml-renderer.js +1 -7
- package/src/plugins/eco-component-meta-plugin.js +0 -1
- package/src/plugins/integration-plugin.d.ts +14 -18
- package/src/plugins/integration-plugin.js +14 -21
- package/src/plugins/processor.d.ts +2 -0
- package/src/plugins/processor.js +6 -1
- package/src/route-renderer/GRAPH.md +81 -289
- package/src/route-renderer/README.md +67 -105
- package/src/route-renderer/orchestration/component-render-context.d.ts +24 -18
- package/src/route-renderer/orchestration/component-render-context.js +14 -14
- package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +18 -0
- package/src/route-renderer/orchestration/declared-ownership-graph.js +34 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +108 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +206 -0
- package/src/route-renderer/orchestration/integration-renderer.d.ts +96 -136
- package/src/route-renderer/orchestration/integration-renderer.js +280 -303
- package/src/route-renderer/orchestration/ownership-planning.service.d.ts +24 -0
- package/src/route-renderer/orchestration/ownership-planning.service.js +63 -0
- package/src/route-renderer/orchestration/ownership-validation.service.d.ts +29 -0
- package/src/route-renderer/orchestration/ownership-validation.service.js +53 -0
- package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +90 -0
- package/src/route-renderer/orchestration/{queued-boundary-runtime.service.js → queued-foreign-subtree-resolution.service.js} +28 -25
- package/src/route-renderer/orchestration/render-output.utils.d.ts +3 -3
- package/src/route-renderer/orchestration/render-output.utils.js +6 -6
- package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +120 -0
- package/src/route-renderer/orchestration/{render-preparation.service.js → route-render-orchestrator.js} +132 -108
- package/src/route-renderer/page-loading/component-dependency-collection.js +8 -1
- package/src/route-renderer/page-loading/dependency-resolver.js +5 -7
- package/src/route-renderer/page-loading/page-dependency-bundling.d.ts +1 -1
- package/src/route-renderer/page-loading/page-dependency-bundling.js +41 -19
- package/src/route-renderer/route-renderer.d.ts +28 -26
- package/src/route-renderer/route-renderer.js +4 -27
- package/src/router/README.md +16 -19
- package/src/router/server/route-registry.d.ts +78 -0
- package/src/router/server/route-registry.js +262 -0
- package/src/services/README.md +1 -2
- package/src/services/assets/asset-processing-service/assets.types.d.ts +3 -0
- package/src/services/assets/asset-processing-service/index.d.ts +1 -0
- package/src/services/assets/asset-processing-service/index.js +1 -0
- package/src/services/assets/asset-processing-service/page-package.d.ts +3 -0
- package/src/services/assets/asset-processing-service/page-package.js +74 -0
- package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +4 -4
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +6 -3
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +9 -3
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +4 -2
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -1
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +3 -1
- package/src/services/module-loading/node-bootstrap-plugin.js +15 -3
- package/src/static-site-generator/static-site-generator.d.ts +20 -21
- package/src/static-site-generator/static-site-generator.js +107 -140
- package/src/types/internal-types.d.ts +13 -12
- package/src/types/public-types.d.ts +46 -36
- package/src/watchers/project-watcher.test-helpers.js +5 -5
- package/src/route-renderer/orchestration/boundary-planning.service.d.ts +0 -25
- package/src/route-renderer/orchestration/boundary-planning.service.js +0 -97
- package/src/route-renderer/orchestration/page-packaging.service.d.ts +0 -16
- package/src/route-renderer/orchestration/page-packaging.service.js +0 -66
- package/src/route-renderer/orchestration/queued-boundary-runtime.service.d.ts +0 -89
- package/src/route-renderer/orchestration/render-execution.service.d.ts +0 -43
- package/src/route-renderer/orchestration/render-execution.service.js +0 -106
- package/src/route-renderer/orchestration/render-preparation.service.d.ts +0 -120
- package/src/route-renderer/orchestration/route-shell-composer.service.d.ts +0 -50
- package/src/route-renderer/orchestration/route-shell-composer.service.js +0 -81
- package/src/router/server/fs-router-scanner.d.ts +0 -41
- package/src/router/server/fs-router-scanner.js +0 -161
- package/src/router/server/fs-router.d.ts +0 -26
- package/src/router/server/fs-router.js +0 -100
- package/src/services/runtime-state/runtime-specifier-registry.service.d.ts +0 -69
- package/src/services/runtime-state/runtime-specifier-registry.service.js +0 -37
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createPagePackage
|
|
2
3
|
} from "../../services/assets/asset-processing-service/index.js";
|
|
3
4
|
import { HtmlTransformerService } from "../../services/html/html-transformer.service.js";
|
|
4
5
|
import { invariant } from "../../utils/invariant.js";
|
|
5
6
|
import { HttpError } from "../../errors/http-error.js";
|
|
6
7
|
import { DependencyResolverService } from "../page-loading/dependency-resolver.js";
|
|
7
8
|
import { PageModuleLoaderService } from "../page-loading/page-module-loader.js";
|
|
8
|
-
import {
|
|
9
|
-
import { RenderExecutionService } from "./render-execution.service.js";
|
|
10
|
-
import { RenderPreparationService } from "./render-preparation.service.js";
|
|
11
|
-
import { RouteShellComposer } from "./route-shell-composer.service.js";
|
|
12
|
-
import { normalizeBoundaryArtifactHtml } from "./render-output.utils.js";
|
|
13
|
-
import { getComponentRenderContext, runWithComponentRenderContext } from "./component-render-context.js";
|
|
9
|
+
import { OwnershipValidationService } from "./ownership-validation.service.js";
|
|
14
10
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./
|
|
11
|
+
RouteRenderOrchestrator
|
|
12
|
+
} from "./route-render-orchestrator.js";
|
|
13
|
+
import { normalizeUnresolvedMarkerArtifactHtml } from "./render-output.utils.js";
|
|
14
|
+
import {
|
|
15
|
+
ForeignSubtreeExecutionService
|
|
16
|
+
} from "./foreign-subtree-execution.service.js";
|
|
17
|
+
import {} from "./queued-foreign-subtree-resolution.service.js";
|
|
17
18
|
class IntegrationRenderer {
|
|
18
19
|
appConfig;
|
|
19
20
|
assetProcessingService;
|
|
@@ -24,11 +25,8 @@ class IntegrationRenderer {
|
|
|
24
25
|
runtimeOrigin;
|
|
25
26
|
dependencyResolverService;
|
|
26
27
|
pageModuleLoaderService;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
pagePackagingService;
|
|
30
|
-
routeShellComposer = new RouteShellComposer();
|
|
31
|
-
queuedBoundaryRuntimeService = new QueuedBoundaryRuntimeService();
|
|
28
|
+
routeRenderOrchestrator;
|
|
29
|
+
foreignSubtreeExecutionService = new ForeignSubtreeExecutionService();
|
|
32
30
|
DOC_TYPE = "<!DOCTYPE html>";
|
|
33
31
|
/**
|
|
34
32
|
* Loads one route module through the owning renderer's import path.
|
|
@@ -42,7 +40,7 @@ class IntegrationRenderer {
|
|
|
42
40
|
return this.importPageFile(file, options);
|
|
43
41
|
}
|
|
44
42
|
/**
|
|
45
|
-
* Reads the execution-scoped
|
|
43
|
+
* Reads the execution-scoped owning-renderer cache from one render input.
|
|
46
44
|
*
|
|
47
45
|
* Shared page/layout/document shell helpers pass one cache through
|
|
48
46
|
* `integrationContext` so repeated delegation to the same foreign integration
|
|
@@ -51,16 +49,16 @@ class IntegrationRenderer {
|
|
|
51
49
|
* stored on the renderer, which avoids leaking mutable integration state across
|
|
52
50
|
* requests while still preventing redundant renderer initialization.
|
|
53
51
|
*
|
|
54
|
-
* @param integrationContext - Optional
|
|
52
|
+
* @param integrationContext - Optional render context carried with one render input.
|
|
55
53
|
* @returns The current execution cache when present.
|
|
56
54
|
*/
|
|
57
|
-
|
|
55
|
+
getOwningRendererCache(integrationContext) {
|
|
58
56
|
if (integrationContext?.rendererCache instanceof Map) {
|
|
59
57
|
return integrationContext.rendererCache;
|
|
60
58
|
}
|
|
61
59
|
return void 0;
|
|
62
60
|
}
|
|
63
|
-
|
|
61
|
+
getForeignOwnerIntegrationName(component) {
|
|
64
62
|
const integrationName = component.config?.integration ?? component.config?.__eco?.integration;
|
|
65
63
|
if (!integrationName || integrationName === this.name) {
|
|
66
64
|
return void 0;
|
|
@@ -68,18 +66,18 @@ class IntegrationRenderer {
|
|
|
68
66
|
return this.appConfig.integrations.some((integration) => integration.name === integrationName) ? integrationName : void 0;
|
|
69
67
|
}
|
|
70
68
|
/**
|
|
71
|
-
* Attaches an execution-scoped
|
|
69
|
+
* Attaches an execution-scoped owning-renderer cache to one render input.
|
|
72
70
|
*
|
|
73
71
|
* Foreign-owned page, layout, or document shells may delegate several times in
|
|
74
72
|
* the same render flow. Threading the cache through `integrationContext`
|
|
75
|
-
* preserves renderer reuse without changing the public
|
|
73
|
+
* preserves renderer reuse without changing the public render input contract.
|
|
76
74
|
* Existing integration-specific context is preserved and augmented.
|
|
77
75
|
*
|
|
78
|
-
* @param input - Original
|
|
76
|
+
* @param input - Original render input.
|
|
79
77
|
* @param rendererCache - Execution-scoped renderer cache to propagate.
|
|
80
|
-
* @returns
|
|
78
|
+
* @returns Render input augmented with the shared renderer cache.
|
|
81
79
|
*/
|
|
82
|
-
|
|
80
|
+
withOwningRendererCache(input, rendererCache) {
|
|
83
81
|
const integrationContext = input.integrationContext;
|
|
84
82
|
const sharedRendererCache = rendererCache;
|
|
85
83
|
return {
|
|
@@ -175,14 +173,14 @@ class IntegrationRenderer {
|
|
|
175
173
|
const resolvedDependencies = this.htmlTransformer.dedupeProcessedAssets(
|
|
176
174
|
await this.resolveDependencies(componentsToResolve)
|
|
177
175
|
);
|
|
178
|
-
this.htmlTransformer.setPagePackage(
|
|
176
|
+
this.htmlTransformer.setPagePackage(createPagePackage(resolvedDependencies));
|
|
179
177
|
return resolvedDependencies;
|
|
180
178
|
}
|
|
181
179
|
/**
|
|
182
180
|
* Merges component-scoped assets into the active HTML transformer state.
|
|
183
181
|
*
|
|
184
182
|
* Explicit page, layout, and document shell composition can produce assets at
|
|
185
|
-
* each
|
|
183
|
+
* each foreign subtree. This helper deduplicates those groups and folds them back into
|
|
186
184
|
* the transformer so downstream HTML finalization sees one canonical asset set.
|
|
187
185
|
*
|
|
188
186
|
* @param assetGroups - Optional groups of processed assets to merge.
|
|
@@ -199,7 +197,7 @@ class IntegrationRenderer {
|
|
|
199
197
|
...this.htmlTransformer.getProcessedDependencies(),
|
|
200
198
|
...nextDependencies
|
|
201
199
|
]);
|
|
202
|
-
this.htmlTransformer.setPagePackage(
|
|
200
|
+
this.htmlTransformer.setPagePackage(createPagePackage(mergedDependencies));
|
|
203
201
|
return nextDependencies;
|
|
204
202
|
}
|
|
205
203
|
/**
|
|
@@ -226,7 +224,7 @@ class IntegrationRenderer {
|
|
|
226
224
|
*
|
|
227
225
|
* Same-integration views can optionally stream or render inline via the caller's
|
|
228
226
|
* `renderInline()` hook. Once a view may cross integration boundaries, this
|
|
229
|
-
* helper routes the render through `
|
|
227
|
+
* helper routes the render through `renderComponentWithForeignChildren()` instead so mixed
|
|
230
228
|
* shells can reuse the execution-scoped renderer cache and resolve nested
|
|
231
229
|
* foreign ownership before the partial response is returned.
|
|
232
230
|
*
|
|
@@ -234,17 +232,17 @@ class IntegrationRenderer {
|
|
|
234
232
|
* @returns HTML response for the partial render.
|
|
235
233
|
*/
|
|
236
234
|
async renderPartialViewResponse(input) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
finalizeResolvedHtml: (options) => this.finalizeResolvedHtml(options),
|
|
246
|
-
docType: this.DOC_TYPE
|
|
235
|
+
if (input.renderInline && !this.hasForeignChildDescendants(input.view)) {
|
|
236
|
+
return this.createHtmlResponse(await input.renderInline(), input.ctx);
|
|
237
|
+
}
|
|
238
|
+
const rendererCache = /* @__PURE__ */ new Map();
|
|
239
|
+
const viewRender = await this.renderComponentWithForeignChildren({
|
|
240
|
+
component: input.view,
|
|
241
|
+
props: input.props ?? {},
|
|
242
|
+
integrationContext: { rendererCache }
|
|
247
243
|
});
|
|
244
|
+
const html = input.transformHtml ? input.transformHtml(viewRender.html) : viewRender.html;
|
|
245
|
+
return this.createHtmlResponse(html, input.ctx);
|
|
248
246
|
}
|
|
249
247
|
/**
|
|
250
248
|
* Renders an explicit view through optional layout and document shells.
|
|
@@ -259,57 +257,97 @@ class IntegrationRenderer {
|
|
|
259
257
|
* @returns HTML response for the explicit view render.
|
|
260
258
|
*/
|
|
261
259
|
async renderViewWithDocumentShell(input) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
260
|
+
const normalizedProps = input.props ?? {};
|
|
261
|
+
if (input.ctx.partial) {
|
|
262
|
+
return this.renderPartialViewResponse(input);
|
|
263
|
+
}
|
|
264
|
+
await this.prepareViewDependencies(input.view, input.layout);
|
|
265
|
+
const HtmlTemplate = await this.getHtmlTemplate();
|
|
266
|
+
const metadata = await this.resolveViewMetadata(input.view, input.props);
|
|
267
|
+
const { documentHtml } = await this.composeDocumentShell({
|
|
268
|
+
primaryComponent: input.view,
|
|
269
|
+
primaryProps: normalizedProps,
|
|
270
|
+
layout: input.layout ? {
|
|
271
|
+
component: input.layout,
|
|
272
|
+
props: {}
|
|
273
|
+
} : void 0,
|
|
274
|
+
htmlTemplate: HtmlTemplate,
|
|
275
|
+
documentProps: {
|
|
276
|
+
metadata,
|
|
277
|
+
pageProps: normalizedProps
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const html = await this.finalizeResolvedHtml({
|
|
281
|
+
html: `${this.DOC_TYPE}${documentHtml}`,
|
|
282
|
+
partial: false
|
|
272
283
|
});
|
|
284
|
+
return this.createHtmlResponse(html, input.ctx);
|
|
273
285
|
}
|
|
274
286
|
/**
|
|
275
287
|
* Renders a route page through optional layout and document shells.
|
|
276
288
|
*
|
|
277
|
-
* Route rendering and explicit view rendering now share the same
|
|
289
|
+
* Route rendering and explicit view rendering now share the same renderer-owned
|
|
278
290
|
* shell composition model. This helper composes page, layout, and html template
|
|
279
|
-
*
|
|
280
|
-
* delegated
|
|
291
|
+
* renders while threading one execution-scoped renderer cache through every
|
|
292
|
+
* delegated foreign subtree so foreign shell ownership remains stable and renderer
|
|
281
293
|
* initialization is reused inside the current request.
|
|
282
294
|
*
|
|
283
295
|
* @param input - Page, layout, document, and metadata inputs for the route render.
|
|
284
296
|
* @returns Final serialized document HTML including the doctype prefix.
|
|
285
297
|
*/
|
|
286
298
|
async renderPageWithDocumentShell(input) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
299
|
+
const { documentHtml: composedDocumentHtml } = await this.composeDocumentShell({
|
|
300
|
+
primaryComponent: input.page.component,
|
|
301
|
+
primaryProps: input.page.props,
|
|
302
|
+
layout: input.layout,
|
|
303
|
+
htmlTemplate: input.htmlTemplate,
|
|
304
|
+
documentProps: {
|
|
305
|
+
metadata: input.metadata,
|
|
306
|
+
pageProps: input.pageProps,
|
|
307
|
+
...input.documentProps ?? {}
|
|
308
|
+
}
|
|
297
309
|
});
|
|
310
|
+
const documentHtml = input.transformDocumentHtml ? input.transformDocumentHtml(composedDocumentHtml) : composedDocumentHtml;
|
|
311
|
+
return `${this.DOC_TYPE}${documentHtml}`;
|
|
312
|
+
}
|
|
313
|
+
async composeDocumentShell(input) {
|
|
314
|
+
const rendererCache = /* @__PURE__ */ new Map();
|
|
315
|
+
const primaryRender = await this.renderComponentWithForeignChildren({
|
|
316
|
+
component: input.primaryComponent,
|
|
317
|
+
props: input.primaryProps,
|
|
318
|
+
integrationContext: { rendererCache }
|
|
319
|
+
});
|
|
320
|
+
const layoutRender = input.layout ? await this.renderComponentWithForeignChildren({
|
|
321
|
+
component: input.layout.component,
|
|
322
|
+
props: input.layout.props ?? {},
|
|
323
|
+
children: primaryRender.html,
|
|
324
|
+
integrationContext: { rendererCache }
|
|
325
|
+
}) : void 0;
|
|
326
|
+
const documentRender = await this.renderComponentWithForeignChildren({
|
|
327
|
+
component: input.htmlTemplate,
|
|
328
|
+
props: input.documentProps,
|
|
329
|
+
children: layoutRender?.html ?? primaryRender.html,
|
|
330
|
+
integrationContext: { rendererCache }
|
|
331
|
+
});
|
|
332
|
+
this.appendProcessedDependencies(primaryRender.assets, layoutRender?.assets, documentRender.assets);
|
|
333
|
+
return {
|
|
334
|
+
documentHtml: documentRender.html
|
|
335
|
+
};
|
|
298
336
|
}
|
|
299
337
|
/**
|
|
300
|
-
* Renders one string-first component
|
|
338
|
+
* Renders one string-first component with serialized children and collects its assets.
|
|
301
339
|
*
|
|
302
|
-
* String-oriented integrations frequently share the same
|
|
340
|
+
* String-oriented integrations frequently share the same component contract:
|
|
303
341
|
* pass serialized children through props, coerce the render result to HTML, and
|
|
304
342
|
* attach any component-scoped dependencies. This helper centralizes that flow
|
|
305
343
|
* so integrations can opt into shared orchestration without repeating the same
|
|
306
|
-
*
|
|
344
|
+
* string-render boilerplate.
|
|
307
345
|
*
|
|
308
|
-
* @param input -
|
|
346
|
+
* @param input - Component render input.
|
|
309
347
|
* @param component - String-oriented component implementation to execute.
|
|
310
348
|
* @returns Structured component render result for orchestration paths.
|
|
311
349
|
*/
|
|
312
|
-
async
|
|
350
|
+
async renderStringComponentWithSerializedChildren(input, component) {
|
|
313
351
|
const props = input.children === void 0 ? input.props : { ...input.props, children: input.children };
|
|
314
352
|
const content = await component(props);
|
|
315
353
|
const html = String(content);
|
|
@@ -322,79 +360,51 @@ class IntegrationRenderer {
|
|
|
322
360
|
assets
|
|
323
361
|
};
|
|
324
362
|
}
|
|
325
|
-
|
|
326
|
-
return `__${this.name}
|
|
363
|
+
getForeignSubtreeTokenPrefix() {
|
|
364
|
+
return `__${this.name}_foreign_subtree__`;
|
|
327
365
|
}
|
|
328
|
-
|
|
329
|
-
return `__${this.name}
|
|
366
|
+
getForeignSubtreeResolutionContextKey() {
|
|
367
|
+
return `__${this.name}_foreign_subtree_runtime__`;
|
|
330
368
|
}
|
|
331
|
-
|
|
332
|
-
return this.
|
|
333
|
-
|
|
334
|
-
async resolveQueuedBoundaryTokens(html, queuedResolutionsByToken, resolveToken) {
|
|
335
|
-
let resolvedHtml = html;
|
|
336
|
-
for (const token of queuedResolutionsByToken.keys()) {
|
|
337
|
-
if (!resolvedHtml.includes(token)) {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
resolvedHtml = resolvedHtml.split(token).join(await resolveToken(token));
|
|
341
|
-
}
|
|
342
|
-
return resolvedHtml;
|
|
343
|
-
}
|
|
344
|
-
createQueuedBoundaryRuntime(options) {
|
|
345
|
-
return this.queuedBoundaryRuntimeService.createRuntime({
|
|
346
|
-
boundaryInput: options.boundaryInput,
|
|
369
|
+
createQueuedForeignSubtreeExecutionRuntime(options) {
|
|
370
|
+
return this.foreignSubtreeExecutionService.createQueuedRuntime({
|
|
371
|
+
renderInput: options.renderInput,
|
|
347
372
|
rendererCache: options.rendererCache,
|
|
348
|
-
runtimeContextKey: options.runtimeContextKey ?? this.
|
|
349
|
-
tokenPrefix: options.tokenPrefix ?? this.
|
|
350
|
-
shouldQueueBoundary: (input) => this.shouldResolveBoundaryInOwningRenderer(input),
|
|
373
|
+
runtimeContextKey: options.runtimeContextKey ?? this.getForeignSubtreeResolutionContextKey(),
|
|
374
|
+
tokenPrefix: options.tokenPrefix ?? this.getForeignSubtreeTokenPrefix(),
|
|
351
375
|
createRuntimeContext: options.createRuntimeContext
|
|
352
376
|
});
|
|
353
377
|
}
|
|
354
|
-
|
|
355
|
-
return this.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
renderQueuedChildren: options.renderQueuedChildren,
|
|
360
|
-
resolveBoundary: (input, rendererCache) => this.resolveBoundaryPayloadInOwningRenderer(
|
|
361
|
-
input,
|
|
362
|
-
rendererCache
|
|
363
|
-
),
|
|
364
|
-
applyAttributesToFirstElement: (html, attributes) => this.htmlTransformer.applyAttributesToFirstElement(html, attributes),
|
|
365
|
-
dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets)
|
|
366
|
-
});
|
|
378
|
+
getQueuedForeignSubtreeResolutionContext(input) {
|
|
379
|
+
return this.foreignSubtreeExecutionService.getQueuedRuntimeContext(
|
|
380
|
+
input,
|
|
381
|
+
this.getForeignSubtreeResolutionContextKey()
|
|
382
|
+
);
|
|
367
383
|
}
|
|
368
384
|
/**
|
|
369
385
|
* Renders a string-first component, then resolves any queued foreign
|
|
370
386
|
* boundaries before returning final component HTML.
|
|
371
387
|
*/
|
|
372
|
-
async
|
|
373
|
-
const componentRender = await this.
|
|
374
|
-
const
|
|
388
|
+
async renderStringComponentWithQueuedForeignSubtrees(input, component) {
|
|
389
|
+
const componentRender = await this.renderStringComponentWithSerializedChildren(input, component);
|
|
390
|
+
const queuedForeignSubtreeResolution = await this.foreignSubtreeExecutionService.resolveStringQueuedHtml({
|
|
391
|
+
currentIntegrationName: this.name,
|
|
392
|
+
renderInput: input,
|
|
375
393
|
html: componentRender.html,
|
|
376
|
-
|
|
394
|
+
runtimeContextKey: this.getForeignSubtreeResolutionContextKey(),
|
|
377
395
|
queueLabel: "String",
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
const html = await this.resolveQueuedBoundaryTokens(
|
|
383
|
-
typeof children === "string" ? children : String(children ?? ""),
|
|
384
|
-
queuedResolutionsByToken,
|
|
385
|
-
resolveToken
|
|
386
|
-
);
|
|
387
|
-
return { assets: [], html };
|
|
388
|
-
}
|
|
396
|
+
getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache),
|
|
397
|
+
applyAttributesToFirstElement: (html, attributes) => this.htmlTransformer.applyAttributesToFirstElement(html, attributes),
|
|
398
|
+
dedupeProcessedAssets: (assets) => this.htmlTransformer.dedupeProcessedAssets(assets)
|
|
389
399
|
});
|
|
390
400
|
const mergedAssets = this.htmlTransformer.dedupeProcessedAssets([
|
|
391
401
|
...componentRender.assets ?? [],
|
|
392
|
-
...
|
|
402
|
+
...queuedForeignSubtreeResolution.assets
|
|
393
403
|
]);
|
|
394
404
|
return {
|
|
395
405
|
...componentRender,
|
|
396
|
-
html:
|
|
397
|
-
rootTag: this.getRootTagName(
|
|
406
|
+
html: queuedForeignSubtreeResolution.html,
|
|
407
|
+
rootTag: this.getRootTagName(queuedForeignSubtreeResolution.html),
|
|
398
408
|
assets: mergedAssets.length > 0 ? mergedAssets : void 0
|
|
399
409
|
};
|
|
400
410
|
}
|
|
@@ -408,16 +418,14 @@ class IntegrationRenderer {
|
|
|
408
418
|
this.appConfig = appConfig;
|
|
409
419
|
this.assetProcessingService = assetProcessingService;
|
|
410
420
|
this.htmlTransformer = new HtmlTransformerService();
|
|
411
|
-
this.pagePackagingService = new PagePackagingService();
|
|
412
421
|
this.resolvedIntegrationDependencies = resolvedIntegrationDependencies || [];
|
|
413
422
|
this.rendererModules = rendererModules ?? appConfig.runtime?.rendererModuleContext;
|
|
414
423
|
this.runtimeOrigin = runtimeOrigin;
|
|
415
424
|
this.dependencyResolverService = new DependencyResolverService(appConfig, assetProcessingService);
|
|
416
425
|
this.pageModuleLoaderService = new PageModuleLoaderService(appConfig, runtimeOrigin);
|
|
417
|
-
this.
|
|
418
|
-
|
|
426
|
+
this.routeRenderOrchestrator = new RouteRenderOrchestrator(appConfig, assetProcessingService, {
|
|
427
|
+
ownershipValidationService: new OwnershipValidationService(appConfig)
|
|
419
428
|
});
|
|
420
|
-
this.renderExecutionService = new RenderExecutionService();
|
|
421
429
|
}
|
|
422
430
|
/**
|
|
423
431
|
* Returns the HTML path from the provided file path.
|
|
@@ -451,34 +459,6 @@ class IntegrationRenderer {
|
|
|
451
459
|
invariant(false, `Error importing HtmlTemplate: ${error}`);
|
|
452
460
|
}
|
|
453
461
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Returns the static props for the page.
|
|
456
|
-
* It calls the provided getStaticProps function with the given options.
|
|
457
|
-
*
|
|
458
|
-
* @param getStaticProps - The function to get static props.
|
|
459
|
-
* @param options - The options to pass to the getStaticProps function.
|
|
460
|
-
* @returns The static props and metadata.
|
|
461
|
-
*/
|
|
462
|
-
async getStaticProps(getStaticProps, options) {
|
|
463
|
-
return this.pageModuleLoaderService.getStaticPropsForPage({
|
|
464
|
-
getStaticProps,
|
|
465
|
-
params: options?.params
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Returns the metadata properties for the page.
|
|
470
|
-
* It calls the provided getMetadata function with the given context.
|
|
471
|
-
*
|
|
472
|
-
* @param getMetadata - The function to get metadata.
|
|
473
|
-
* @param context - The context to pass to the getMetadata function.
|
|
474
|
-
* @returns The metadata properties.
|
|
475
|
-
*/
|
|
476
|
-
async getMetadataProps(getMetadata, { props, params, query }) {
|
|
477
|
-
return this.pageModuleLoaderService.getMetadataPropsForPage({
|
|
478
|
-
getMetadata,
|
|
479
|
-
context: { props, params, query }
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
462
|
usesIntegrationPageImporter(_file) {
|
|
483
463
|
return false;
|
|
484
464
|
}
|
|
@@ -576,6 +556,98 @@ class IntegrationRenderer {
|
|
|
576
556
|
async processComponentDependencies(components) {
|
|
577
557
|
return this.dependencyResolverService.processComponentDependencies(components, this.name);
|
|
578
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Builds the internal route-render adapter consumed by `RouteRenderOrchestrator`.
|
|
561
|
+
*
|
|
562
|
+
* The route orchestrator needs a narrow orchestration contract, but those hooks should
|
|
563
|
+
* not become public API on the renderer base class. Keeping the adapter object
|
|
564
|
+
* local to the execution path lets the orchestrator depend on one explicit seam while
|
|
565
|
+
* subclasses continue to override protected renderer behavior directly.
|
|
566
|
+
*/
|
|
567
|
+
createRouteRenderOrchestratorAdapter() {
|
|
568
|
+
return {
|
|
569
|
+
name: this.name,
|
|
570
|
+
resolveRouteRenderInputs: (routeOptions) => this.resolveRouteRenderInputs(routeOptions),
|
|
571
|
+
resolveRouteAssets: (input) => this.resolveRouteAssets(input),
|
|
572
|
+
resolveRoutePageComponentRender: (input) => this.resolveRoutePageComponentRender(input),
|
|
573
|
+
renderRouteBody: (renderOptions) => this.renderRouteBody(renderOptions),
|
|
574
|
+
getRouteHtmlFinalization: (renderOptions) => this.getRouteHtmlFinalization(renderOptions),
|
|
575
|
+
transformRouteResponse: (response) => this.transformRouteResponse(response)
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
async resolveRouteRenderInputs(routeOptions) {
|
|
579
|
+
const pageModule = await this.pageModuleLoaderService.resolvePageModule({
|
|
580
|
+
file: routeOptions.file,
|
|
581
|
+
importPageFileFn: (targetFile) => this.importPageFile(targetFile)
|
|
582
|
+
});
|
|
583
|
+
const { Page, integrationSpecificProps } = pageModule;
|
|
584
|
+
const HtmlTemplate = await this.getHtmlTemplate();
|
|
585
|
+
const Layout = Page.config?.layout;
|
|
586
|
+
const { props, metadata } = await this.pageModuleLoaderService.resolvePageData({
|
|
587
|
+
pageModule,
|
|
588
|
+
routeOptions
|
|
589
|
+
});
|
|
590
|
+
return {
|
|
591
|
+
Page,
|
|
592
|
+
HtmlTemplate,
|
|
593
|
+
Layout,
|
|
594
|
+
props,
|
|
595
|
+
metadata,
|
|
596
|
+
integrationSpecificProps
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
async resolveRouteAssets(input) {
|
|
600
|
+
return {
|
|
601
|
+
resolvedDependencies: await this.resolveDependencies(input.components),
|
|
602
|
+
pageBrowserGraph: await this.buildPageBrowserGraph(input.routeOptions.file)
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
async resolveRoutePageComponentRender(input) {
|
|
606
|
+
if (!this.shouldRenderPageComponent({ Page: input.Page, Layout: input.Layout, options: input.routeOptions })) {
|
|
607
|
+
return void 0;
|
|
608
|
+
}
|
|
609
|
+
return this.renderComponentWithForeignChildren({
|
|
610
|
+
component: input.Page,
|
|
611
|
+
props: {
|
|
612
|
+
...input.props,
|
|
613
|
+
params: input.routeOptions.params || {},
|
|
614
|
+
query: input.routeOptions.query || {}
|
|
615
|
+
},
|
|
616
|
+
integrationContext: {
|
|
617
|
+
componentInstanceId: "eco-page-root"
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
async renderRouteBody(renderOptions) {
|
|
622
|
+
return this.render(renderOptions);
|
|
623
|
+
}
|
|
624
|
+
getRouteHtmlFinalization(renderOptions) {
|
|
625
|
+
const componentRootAttributes = renderOptions.componentRender?.canAttachAttributes && renderOptions.componentRender.rootAttributes && Object.keys(renderOptions.componentRender.rootAttributes).length > 0 ? renderOptions.componentRender.rootAttributes : void 0;
|
|
626
|
+
const documentAttributes = this.getDocumentAttributes(renderOptions);
|
|
627
|
+
const hasStructuralFinalization = componentRootAttributes && Object.keys(componentRootAttributes).length > 0 || documentAttributes && Object.keys(documentAttributes).length > 0;
|
|
628
|
+
if (!hasStructuralFinalization) {
|
|
629
|
+
return {};
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
finalizeHtml: (html) => {
|
|
633
|
+
let renderedHtml = html;
|
|
634
|
+
if (componentRootAttributes) {
|
|
635
|
+
renderedHtml = this.htmlTransformer.applyAttributesToFirstBodyElement(
|
|
636
|
+
renderedHtml,
|
|
637
|
+
componentRootAttributes
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (documentAttributes) {
|
|
641
|
+
renderedHtml = this.htmlTransformer.applyAttributesToHtmlElement(renderedHtml, documentAttributes);
|
|
642
|
+
}
|
|
643
|
+
return renderedHtml;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
async transformRouteResponse(response) {
|
|
648
|
+
const transformedResponse = await this.htmlTransformer.transform(response);
|
|
649
|
+
return transformedResponse.body ?? await transformedResponse.text();
|
|
650
|
+
}
|
|
579
651
|
/**
|
|
580
652
|
* Prepares the render options for the integration renderer.
|
|
581
653
|
* It imports the page file, collects dependencies, and prepares the render options.
|
|
@@ -583,25 +655,11 @@ class IntegrationRenderer {
|
|
|
583
655
|
* @param options - The route renderer options.
|
|
584
656
|
* @returns The prepared render options.
|
|
585
657
|
*/
|
|
586
|
-
async prepareRenderOptions(options) {
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
resolveDependencies: (components) => this.resolveDependencies(components),
|
|
592
|
-
buildRouteRenderAssets: (file) => this.buildRouteRenderAssets(file),
|
|
593
|
-
shouldRenderPageComponent: (input) => this.shouldRenderPageComponent(input),
|
|
594
|
-
renderPageComponent: ({ component, props }) => this.renderComponentBoundary({
|
|
595
|
-
component,
|
|
596
|
-
props,
|
|
597
|
-
integrationContext: {
|
|
598
|
-
componentInstanceId: "eco-page-root"
|
|
599
|
-
}
|
|
600
|
-
})
|
|
601
|
-
});
|
|
602
|
-
invariant(preparedOptions.pagePackage !== void 0, "Expected render preparation to produce a page package");
|
|
603
|
-
this.htmlTransformer.setPagePackage(preparedOptions.pagePackage);
|
|
604
|
-
return preparedOptions;
|
|
658
|
+
async prepareRenderOptions(options, adapter = this.createRouteRenderOrchestratorAdapter()) {
|
|
659
|
+
const renderOptions = await this.routeRenderOrchestrator.prepareRenderOptions(options, adapter);
|
|
660
|
+
invariant(renderOptions.pagePackage !== void 0, "Expected render preparation to produce a page package");
|
|
661
|
+
this.htmlTransformer.setPagePackage(renderOptions.pagePackage);
|
|
662
|
+
return renderOptions;
|
|
605
663
|
}
|
|
606
664
|
/**
|
|
607
665
|
* Controls whether the page root should be rendered through `renderComponent()`
|
|
@@ -614,31 +672,13 @@ class IntegrationRenderer {
|
|
|
614
672
|
shouldRenderPageComponent(_input) {
|
|
615
673
|
return true;
|
|
616
674
|
}
|
|
617
|
-
/**
|
|
618
|
-
* Resolves the page module and normalizes exports.
|
|
619
|
-
*/
|
|
620
|
-
async resolvePageModule(file) {
|
|
621
|
-
return this.pageModuleLoaderService.resolvePageModule({
|
|
622
|
-
file,
|
|
623
|
-
importPageFileFn: (targetFile) => this.importPageFile(targetFile)
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Resolves static props and metadata for the page.
|
|
628
|
-
*/
|
|
629
|
-
async resolvePageData(pageModule, options) {
|
|
630
|
-
return this.pageModuleLoaderService.resolvePageData({
|
|
631
|
-
pageModule,
|
|
632
|
-
routeOptions: options
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
675
|
/**
|
|
636
676
|
* Executes the integration renderer with the provided options.
|
|
637
677
|
*
|
|
638
678
|
* Execution flow:
|
|
639
679
|
* 1. Build normalized render options (`prepareRenderOptions`).
|
|
640
680
|
* 2. Render the route body once.
|
|
641
|
-
* 3. Reject unresolved route-level
|
|
681
|
+
* 3. Reject unresolved route-level eco-marker artifacts.
|
|
642
682
|
* 4. Optionally apply root attributes for page/component root boundaries.
|
|
643
683
|
* 5. Run HTML transformer with final dependency set.
|
|
644
684
|
*
|
|
@@ -650,23 +690,15 @@ class IntegrationRenderer {
|
|
|
650
690
|
* @returns Rendered route body plus effective cache strategy.
|
|
651
691
|
*/
|
|
652
692
|
async execute(options) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
getDocumentAttributes: (renderOptions) => this.getDocumentAttributes(renderOptions),
|
|
657
|
-
applyAttributesToHtmlElement: (html, attributes) => this.htmlTransformer.applyAttributesToHtmlElement(html, attributes),
|
|
658
|
-
applyAttributesToFirstBodyElement: (html, attributes) => this.htmlTransformer.applyAttributesToFirstBodyElement(html, attributes),
|
|
659
|
-
transformResponse: async (response) => {
|
|
660
|
-
const transformedResponse = await this.htmlTransformer.transform(response);
|
|
661
|
-
return transformedResponse.body ?? await transformedResponse.text();
|
|
662
|
-
}
|
|
663
|
-
});
|
|
693
|
+
const adapter = this.createRouteRenderOrchestratorAdapter();
|
|
694
|
+
const renderOptions = await this.prepareRenderOptions(options, adapter);
|
|
695
|
+
return this.routeRenderOrchestrator.executePrepared(renderOptions, adapter);
|
|
664
696
|
}
|
|
665
697
|
/**
|
|
666
698
|
* Finalizes already-resolved HTML for explicit renderer-owned paths.
|
|
667
699
|
*
|
|
668
700
|
* This keeps document and root-attribute stamping plus HTML transformation
|
|
669
|
-
* available after a renderer has completed nested
|
|
701
|
+
* available after a renderer has completed nested foreign-subtree resolution without
|
|
670
702
|
* routing back through shared route execution.
|
|
671
703
|
*/
|
|
672
704
|
async finalizeResolvedHtml(options) {
|
|
@@ -720,82 +752,42 @@ class IntegrationRenderer {
|
|
|
720
752
|
const integrationPlugin = this.appConfig.integrations.find(
|
|
721
753
|
(integration) => integration.name === integrationName
|
|
722
754
|
);
|
|
723
|
-
invariant(!!integrationPlugin, `[ecopages] Integration not found for
|
|
755
|
+
invariant(!!integrationPlugin, `[ecopages] Integration not found for foreign owner: ${integrationName}`);
|
|
724
756
|
const renderer = integrationPlugin.initializeRenderer({
|
|
725
757
|
rendererModules: this.appConfig.runtime?.rendererModuleContext
|
|
726
758
|
});
|
|
727
759
|
cache.set(integrationName, renderer);
|
|
728
760
|
return renderer;
|
|
729
761
|
}
|
|
730
|
-
async resolveBoundaryInOwningRenderer(input, rendererCache) {
|
|
731
|
-
const boundaryOwner = this.getRegisteredBoundaryOwner(input.component);
|
|
732
|
-
if (!boundaryOwner) {
|
|
733
|
-
return void 0;
|
|
734
|
-
}
|
|
735
|
-
const owningRenderer = this.getIntegrationRendererForName(boundaryOwner, rendererCache);
|
|
736
|
-
if (owningRenderer === this || owningRenderer.name === this.name) {
|
|
737
|
-
return void 0;
|
|
738
|
-
}
|
|
739
|
-
return await owningRenderer.renderComponentBoundary(this.withBoundaryRendererCache(input, rendererCache));
|
|
740
|
-
}
|
|
741
|
-
async resolveBoundaryPayloadInOwningRenderer(input, rendererCache) {
|
|
742
|
-
const boundaryOwner = this.getRegisteredBoundaryOwner(input.component);
|
|
743
|
-
if (!boundaryOwner) {
|
|
744
|
-
return void 0;
|
|
745
|
-
}
|
|
746
|
-
const owningRenderer = this.getIntegrationRendererForName(boundaryOwner, rendererCache);
|
|
747
|
-
if (owningRenderer === this || owningRenderer.name === this.name) {
|
|
748
|
-
return void 0;
|
|
749
|
-
}
|
|
750
|
-
return await owningRenderer.renderBoundary(this.withBoundaryRendererCache(input, rendererCache));
|
|
751
|
-
}
|
|
752
762
|
/**
|
|
753
|
-
* Renders one component under this integration's
|
|
754
|
-
* any nested foreign
|
|
763
|
+
* Renders one component under this integration's foreign-child runtime and resolves
|
|
764
|
+
* any nested foreign children captured during that render.
|
|
755
765
|
*
|
|
756
766
|
* Without this wrapper, a component tree with foreign-owned descendants would
|
|
757
|
-
* render them with no active
|
|
758
|
-
* renderer's nested-
|
|
759
|
-
*/
|
|
760
|
-
async
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
{
|
|
774
|
-
currentIntegration: this.name
|
|
775
|
-
},
|
|
776
|
-
async () => this.renderComponent(input)
|
|
777
|
-
);
|
|
778
|
-
return this.normalizeComponentBoundaryRender(sameIntegrationExecution.value);
|
|
779
|
-
}
|
|
780
|
-
const execution = await runWithComponentRenderContext(
|
|
781
|
-
{
|
|
782
|
-
currentIntegration: this.name,
|
|
783
|
-
boundaryRuntime: this.createComponentBoundaryRuntime({
|
|
784
|
-
boundaryInput: input,
|
|
785
|
-
rendererCache
|
|
786
|
-
})
|
|
787
|
-
},
|
|
788
|
-
async () => this.renderComponent(input)
|
|
789
|
-
);
|
|
790
|
-
return this.normalizeComponentBoundaryRender(execution.value);
|
|
767
|
+
* render them with no active foreign-child runtime, which bypasses the owning
|
|
768
|
+
* renderer's nested foreign-child handoff.
|
|
769
|
+
*/
|
|
770
|
+
async renderComponentWithForeignChildren(input) {
|
|
771
|
+
return await this.foreignSubtreeExecutionService.executeComponentRender({
|
|
772
|
+
currentIntegrationName: this.name,
|
|
773
|
+
input,
|
|
774
|
+
renderComponent: (renderInput) => this.renderComponent(renderInput),
|
|
775
|
+
normalizeComponentRenderOutput: (result) => this.normalizeComponentRenderOutput(result),
|
|
776
|
+
hasForeignChildDescendants: (component) => this.hasForeignChildDescendants(component),
|
|
777
|
+
createForeignChildRuntime: ({ renderInput, rendererCache }) => this.createForeignChildRuntime({
|
|
778
|
+
renderInput,
|
|
779
|
+
rendererCache
|
|
780
|
+
}),
|
|
781
|
+
getOwningRenderer: (integrationName, rendererCache) => this.getIntegrationRendererForName(integrationName, rendererCache)
|
|
782
|
+
});
|
|
791
783
|
}
|
|
792
784
|
/**
|
|
793
|
-
* Compatibility
|
|
785
|
+
* Compatibility foreign-subtree contract that exposes a narrower payload shape for
|
|
794
786
|
* future route-composition work while preserving the current
|
|
795
|
-
* `
|
|
787
|
+
* `renderComponentWithForeignChildren()` runtime semantics.
|
|
796
788
|
*/
|
|
797
|
-
async
|
|
798
|
-
const result = await this.
|
|
789
|
+
async renderForeignSubtree(input) {
|
|
790
|
+
const result = await this.renderComponentWithForeignChildren(input);
|
|
799
791
|
return {
|
|
800
792
|
html: result.html,
|
|
801
793
|
assets: result.assets ?? [],
|
|
@@ -805,24 +797,24 @@ class IntegrationRenderer {
|
|
|
805
797
|
integrationName: result.integrationName
|
|
806
798
|
};
|
|
807
799
|
}
|
|
808
|
-
|
|
809
|
-
const normalizedHtml = this.
|
|
800
|
+
normalizeComponentRenderOutput(result) {
|
|
801
|
+
const normalizedHtml = this.normalizeUnresolvedMarkerArtifactHtml(result.html);
|
|
810
802
|
return normalizedHtml === result.html ? result : {
|
|
811
803
|
...result,
|
|
812
804
|
html: normalizedHtml
|
|
813
805
|
};
|
|
814
806
|
}
|
|
815
|
-
|
|
816
|
-
return
|
|
807
|
+
normalizeUnresolvedMarkerArtifactHtml(html) {
|
|
808
|
+
return normalizeUnresolvedMarkerArtifactHtml(html);
|
|
817
809
|
}
|
|
818
810
|
/**
|
|
819
811
|
* Returns whether the component dependency tree crosses into another
|
|
820
812
|
* integration.
|
|
821
813
|
*
|
|
822
|
-
* This keeps
|
|
814
|
+
* This keeps foreign-child runtime setup narrow: same-integration trees can render
|
|
823
815
|
* directly without paying the queue orchestration cost.
|
|
824
816
|
*/
|
|
825
|
-
|
|
817
|
+
hasForeignChildDescendants(component) {
|
|
826
818
|
const stack = [component];
|
|
827
819
|
const seen = /* @__PURE__ */ new Set();
|
|
828
820
|
while (stack.length > 0) {
|
|
@@ -845,8 +837,8 @@ class IntegrationRenderer {
|
|
|
845
837
|
* Default behavior delegates to `renderToResponse` in partial mode and wraps
|
|
846
838
|
* the resulting HTML into the `ComponentRenderResult` contract.
|
|
847
839
|
*
|
|
848
|
-
* In
|
|
849
|
-
* already-resolved deferred
|
|
840
|
+
* In foreign-subtree resolution, this method is the integration-owned step that turns an
|
|
841
|
+
* already-resolved deferred foreign subtree into concrete HTML, assets, and optional
|
|
850
842
|
* root attributes.
|
|
851
843
|
*
|
|
852
844
|
* Integrations can override this for richer behavior (asset emission,
|
|
@@ -880,51 +872,36 @@ class IntegrationRenderer {
|
|
|
880
872
|
return rootTag?.[1];
|
|
881
873
|
}
|
|
882
874
|
/**
|
|
883
|
-
*
|
|
875
|
+
* Builds the Page Browser Graph owned by this integration for one Page.
|
|
884
876
|
* This method can be optionally overridden by the specific integration renderer.
|
|
885
877
|
*
|
|
886
878
|
* @param file - The file path to build assets for.
|
|
887
|
-
* @returns The
|
|
879
|
+
* @returns The structured Page Browser Graph or undefined.
|
|
888
880
|
*/
|
|
889
|
-
|
|
881
|
+
async buildPageBrowserGraph(_file) {
|
|
890
882
|
return void 0;
|
|
891
883
|
}
|
|
892
884
|
/**
|
|
893
|
-
* Creates the per-render
|
|
885
|
+
* Creates the per-render foreign-child runtime adopted by the shared component
|
|
894
886
|
* render context.
|
|
895
887
|
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
888
|
+
* The default runtime queues delegated foreign subtrees inside the owning
|
|
889
|
+
* renderer so string and markup renderers do not need to re-declare the same
|
|
890
|
+
* handoff boilerplate. Override only when a renderer needs custom runtime
|
|
891
|
+
* context or a different foreign-child execution strategy.
|
|
900
892
|
*/
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
throw new Error(
|
|
907
|
-
`[ecopages] ${this.name} renderer crossed into ${input.targetIntegration} without a renderer-owned boundary runtime. Override createComponentBoundaryRuntime() to resolve foreign boundaries inside the owning renderer.`
|
|
908
|
-
);
|
|
909
|
-
};
|
|
910
|
-
const runtime = {
|
|
911
|
-
interceptBoundary: decideBoundaryInterception,
|
|
912
|
-
interceptBoundarySync: decideBoundaryInterception
|
|
913
|
-
};
|
|
914
|
-
return runtime;
|
|
893
|
+
createForeignChildRuntime(options) {
|
|
894
|
+
return this.createQueuedForeignSubtreeExecutionRuntime({
|
|
895
|
+
renderInput: options.renderInput,
|
|
896
|
+
rendererCache: options.rendererCache
|
|
897
|
+
});
|
|
915
898
|
}
|
|
916
899
|
/**
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
* Boundaries owned by the current integration always render inline. Foreign-
|
|
921
|
-
* owned boundaries must be handed off by a renderer-owned runtime.
|
|
922
|
-
*
|
|
923
|
-
* @param input Boundary metadata for the active render pass.
|
|
924
|
-
* @returns `true` when the boundary should leave the current pass; otherwise `false`.
|
|
900
|
+
* Creates an explicit fail-fast runtime for tests or renderers that do not
|
|
901
|
+
* support cross-integration foreign-child execution.
|
|
925
902
|
*/
|
|
926
|
-
|
|
927
|
-
return
|
|
903
|
+
createFailFastForeignChildRuntime() {
|
|
904
|
+
return this.foreignSubtreeExecutionService.createFailFastRuntime(this.name);
|
|
928
905
|
}
|
|
929
906
|
}
|
|
930
907
|
export {
|