@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
|
@@ -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,
|
|
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 {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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
|
|
60
|
-
protected
|
|
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
|
|
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
|
|
75
|
+
* @param integrationContext - Optional render context carried with one render input.
|
|
85
76
|
* @returns The current execution cache when present.
|
|
86
77
|
*/
|
|
87
|
-
private
|
|
88
|
-
private
|
|
78
|
+
private getOwningRendererCache;
|
|
79
|
+
private getForeignOwnerIntegrationName;
|
|
89
80
|
/**
|
|
90
|
-
* Attaches an execution-scoped
|
|
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
|
|
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
|
|
88
|
+
* @param input - Original render input.
|
|
98
89
|
* @param rendererCache - Execution-scoped renderer cache to propagate.
|
|
99
|
-
* @returns
|
|
90
|
+
* @returns Render input augmented with the shared renderer cache.
|
|
100
91
|
*/
|
|
101
|
-
private
|
|
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
|
|
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 `
|
|
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
|
|
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
|
-
*
|
|
201
|
-
* delegated
|
|
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
|
|
215
|
+
* Renders one string-first component with serialized children and collects its assets.
|
|
224
216
|
*
|
|
225
|
-
* String-oriented integrations frequently share the same
|
|
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
|
-
*
|
|
221
|
+
* string-render boilerplate.
|
|
230
222
|
*
|
|
231
|
-
* @param 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
|
|
236
|
-
protected
|
|
237
|
-
protected
|
|
238
|
-
protected
|
|
239
|
-
|
|
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
|
-
}):
|
|
247
|
-
protected
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
466
|
-
* any nested foreign
|
|
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
|
|
470
|
-
* renderer's nested-
|
|
434
|
+
* render them with no active foreign-child runtime, which bypasses the owning
|
|
435
|
+
* renderer's nested foreign-child handoff.
|
|
471
436
|
*/
|
|
472
|
-
|
|
437
|
+
renderComponentWithForeignChildren(input: ComponentRenderInput): Promise<ComponentRenderResult>;
|
|
473
438
|
/**
|
|
474
|
-
* Compatibility
|
|
439
|
+
* Compatibility foreign-subtree contract that exposes a narrower payload shape for
|
|
475
440
|
* future route-composition work while preserving the current
|
|
476
|
-
* `
|
|
441
|
+
* `renderComponentWithForeignChildren()` runtime semantics.
|
|
477
442
|
*/
|
|
478
|
-
|
|
479
|
-
private
|
|
480
|
-
protected
|
|
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
|
|
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
|
|
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
|
|
506
|
-
* already-resolved deferred
|
|
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
|
-
*
|
|
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
|
|
493
|
+
* @returns The structured Page Browser Graph or undefined.
|
|
529
494
|
*/
|
|
530
|
-
protected
|
|
495
|
+
protected buildPageBrowserGraph(_file: string): Promise<{
|
|
496
|
+
assets: ProcessedAsset[];
|
|
497
|
+
} | undefined>;
|
|
531
498
|
/**
|
|
532
|
-
* Creates the per-render
|
|
499
|
+
* Creates the per-render foreign-child runtime adopted by the shared component
|
|
533
500
|
* render context.
|
|
534
501
|
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
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
|
|
541
|
-
|
|
507
|
+
protected createForeignChildRuntime(options: {
|
|
508
|
+
renderInput: ComponentRenderInput;
|
|
542
509
|
rendererCache: Map<string, IntegrationRenderer<any>>;
|
|
543
|
-
}):
|
|
510
|
+
}): ForeignChildRuntime;
|
|
544
511
|
/**
|
|
545
|
-
*
|
|
546
|
-
*
|
|
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
|
|
515
|
+
protected createFailFastForeignChildRuntime(): ForeignChildRuntime;
|
|
555
516
|
}
|
|
556
|
-
export {};
|