@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
|
@@ -2,7 +2,7 @@ import type { Readable } from 'node:stream';
|
|
|
2
2
|
import type { ApiResponseBuilder } from '../adapters/shared/api-response.js';
|
|
3
3
|
import type { BuildExecutor } from '../build/build-adapter.js';
|
|
4
4
|
import type { EcoBuildPlugin } from '../build/build-types.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ForeignChildRuntime } from '../route-renderer/orchestration/component-render-context.js';
|
|
6
6
|
import type { EcoPageComponent } from '../eco/eco.types.js';
|
|
7
7
|
import type { EcoPagesAppConfig } from './internal-types.js';
|
|
8
8
|
import type { HmrStrategy } from '../hmr/hmr-strategy.js';
|
|
@@ -14,7 +14,7 @@ export type { EcoPagesAppConfig } from './internal-types.js';
|
|
|
14
14
|
export type { EcoPageComponent } from '../eco/eco.types.js';
|
|
15
15
|
export type { ProcessedAsset } from '../services/assets/asset-processing-service/assets.types.js';
|
|
16
16
|
import type { StandardSchema, StandardSchemaResult, StandardSchemaSuccessResult, StandardSchemaFailureResult, StandardSchemaIssue, InferOutput } from '../services/validation/standard-schema.types.js';
|
|
17
|
-
export type { StandardSchema, StandardSchemaResult, StandardSchemaSuccessResult, StandardSchemaFailureResult, StandardSchemaIssue, InferOutput,
|
|
17
|
+
export type { StandardSchema, StandardSchemaResult, StandardSchemaSuccessResult, StandardSchemaFailureResult, StandardSchemaIssue, InferOutput, ForeignChildRuntime, };
|
|
18
18
|
export type InteractionEventsString = ScriptsInjectorInteractionEventsString;
|
|
19
19
|
export type DependencyLazyTrigger = {
|
|
20
20
|
'on:idle': true;
|
|
@@ -94,10 +94,6 @@ export interface DefaultHmrContext {
|
|
|
94
94
|
* Map of registered entrypoints to their output URLs.
|
|
95
95
|
*/
|
|
96
96
|
getWatchedFiles(): Map<string, string>;
|
|
97
|
-
/**
|
|
98
|
-
* Map of bare specifiers to runtime URLs for browser import resolution.
|
|
99
|
-
*/
|
|
100
|
-
getSpecifierMap(): Map<string, string>;
|
|
101
97
|
/**
|
|
102
98
|
* Directory where HMR bundles are written.
|
|
103
99
|
*/
|
|
@@ -188,15 +184,6 @@ export interface IHmrManager {
|
|
|
188
184
|
* script bundling path.
|
|
189
185
|
*/
|
|
190
186
|
registerScriptEntrypoint(entrypointPath: string): Promise<string>;
|
|
191
|
-
/**
|
|
192
|
-
* Registers mappings from bare specifiers to runtime URLs.
|
|
193
|
-
*
|
|
194
|
-
* @remarks
|
|
195
|
-
* This is the shared registration seam for integration-owned runtime alias
|
|
196
|
-
* maps. The registry may later back a broader import-map-style facility, but
|
|
197
|
-
* the mappings themselves remain integration-owned.
|
|
198
|
-
*/
|
|
199
|
-
registerSpecifierMap(map: Record<string, string>): void;
|
|
200
187
|
/**
|
|
201
188
|
* Registers a custom HMR strategy.
|
|
202
189
|
*/
|
|
@@ -225,10 +212,6 @@ export interface IHmrManager {
|
|
|
225
212
|
* Gets the map of watched files.
|
|
226
213
|
*/
|
|
227
214
|
getWatchedFiles(): Map<string, string>;
|
|
228
|
-
/**
|
|
229
|
-
* Gets the registered bare-specifier map.
|
|
230
|
-
*/
|
|
231
|
-
getSpecifierMap(): Map<string, string>;
|
|
232
215
|
/**
|
|
233
216
|
* Gets the HMR dist directory.
|
|
234
217
|
*/
|
|
@@ -679,7 +662,7 @@ export type IntegrationRendererRenderOptions<C = EcoPagesElement> = RouteRendere
|
|
|
679
662
|
pageProps?: Record<string, unknown>;
|
|
680
663
|
cacheStrategy?: CacheStrategy;
|
|
681
664
|
pageLocals?: RequestLocals;
|
|
682
|
-
|
|
665
|
+
ownershipPlan?: OwnershipPlan;
|
|
683
666
|
};
|
|
684
667
|
/**
|
|
685
668
|
* Structured page asset package produced after dependency resolution.
|
|
@@ -718,51 +701,65 @@ export interface PagePackageResult {
|
|
|
718
701
|
*/
|
|
719
702
|
dynamicChunks: ProcessedAsset[];
|
|
720
703
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
704
|
+
/**
|
|
705
|
+
* Page-scoped browser output planned before final HTML packaging.
|
|
706
|
+
*
|
|
707
|
+
* The initial seam keeps the graph payload intentionally small: integrations
|
|
708
|
+
* return the processed assets that belong to the Page browser graph, while the
|
|
709
|
+
* surrounding route pipeline remains free to evolve toward richer entry/lazy/
|
|
710
|
+
* shared chunk structure later.
|
|
711
|
+
*/
|
|
712
|
+
export interface PageBrowserGraphResult {
|
|
713
|
+
/**
|
|
714
|
+
* Processed assets owned by the current Page browser graph.
|
|
715
|
+
*/
|
|
716
|
+
assets: ProcessedAsset[];
|
|
717
|
+
}
|
|
718
|
+
export type OwnershipValidationErrorCode = 'UNKNOWN_INTEGRATION_OWNER' | 'MISSING_COMPONENT_METADATA';
|
|
719
|
+
export interface OwnershipValidationError {
|
|
720
|
+
code: OwnershipValidationErrorCode;
|
|
724
721
|
message: string;
|
|
725
722
|
componentId?: string;
|
|
726
723
|
componentFile?: string;
|
|
727
724
|
integrationName?: string;
|
|
728
725
|
}
|
|
729
|
-
export type
|
|
730
|
-
export interface
|
|
726
|
+
export type OwnershipPlanNodeSource = 'route' | 'page' | 'layout' | 'html-template' | 'dependency';
|
|
727
|
+
export interface IntegrationOwnership {
|
|
731
728
|
integrationName: string;
|
|
732
729
|
componentId: string;
|
|
733
730
|
componentFile?: string;
|
|
734
731
|
isPageEntry: boolean;
|
|
735
732
|
isForeignToParent: boolean;
|
|
736
733
|
}
|
|
737
|
-
export interface
|
|
734
|
+
export interface OwnershipPlanNode {
|
|
738
735
|
id: string;
|
|
739
|
-
source:
|
|
740
|
-
ownership:
|
|
741
|
-
children:
|
|
736
|
+
source: OwnershipPlanNodeSource;
|
|
737
|
+
ownership: IntegrationOwnership;
|
|
738
|
+
children: OwnershipPlanNode[];
|
|
742
739
|
declaredDependenciesValid: boolean;
|
|
743
740
|
}
|
|
744
|
-
export interface
|
|
745
|
-
root:
|
|
741
|
+
export interface OwnershipPlan {
|
|
742
|
+
root: OwnershipPlanNode;
|
|
746
743
|
rendererNames: string[];
|
|
747
744
|
foreignEdgeCount: number;
|
|
748
745
|
hasValidationErrors: boolean;
|
|
749
|
-
validationErrors:
|
|
746
|
+
validationErrors: OwnershipValidationError[];
|
|
750
747
|
}
|
|
751
|
-
export type
|
|
748
|
+
export type ForeignSubtreeAttachmentPolicy = {
|
|
752
749
|
kind: 'none';
|
|
753
750
|
} | {
|
|
754
751
|
kind: 'first-element';
|
|
755
752
|
};
|
|
756
|
-
export interface
|
|
753
|
+
export interface ForeignSubtreeRenderPayload {
|
|
757
754
|
html: string;
|
|
758
755
|
assets: ProcessedAsset[];
|
|
759
756
|
rootTag?: string;
|
|
760
757
|
rootAttributes?: Record<string, string>;
|
|
761
|
-
attachmentPolicy:
|
|
758
|
+
attachmentPolicy: ForeignSubtreeAttachmentPolicy;
|
|
762
759
|
integrationName: string;
|
|
763
760
|
}
|
|
764
761
|
/**
|
|
765
|
-
* Shared execution-scoped context threaded through
|
|
762
|
+
* Shared execution-scoped context threaded through foreign-child renders.
|
|
766
763
|
*
|
|
767
764
|
* Integrations can extend this with renderer-local runtime keys, but the cache
|
|
768
765
|
* and optional component instance identity are shared across all renderers.
|
|
@@ -1016,6 +1013,15 @@ export interface ApiHandlerContext<TRequest extends Request = Request, TServer =
|
|
|
1016
1013
|
*/
|
|
1017
1014
|
headers?: unknown;
|
|
1018
1015
|
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Context available to file-route page middleware.
|
|
1018
|
+
*
|
|
1019
|
+
* Page middleware can mutate locals, short-circuit the request, and use the
|
|
1020
|
+
* response helpers, but final document rendering stays owned by the page route
|
|
1021
|
+
* execution path.
|
|
1022
|
+
*/
|
|
1023
|
+
export interface FileRouteMiddlewareContext<TRequest extends Request = Request, TServer = any> extends Omit<ApiHandlerContext<TRequest, TServer>, 'render' | 'renderPartial'> {
|
|
1024
|
+
}
|
|
1019
1025
|
/**
|
|
1020
1026
|
* Next function for middleware chain.
|
|
1021
1027
|
* Call to continue to the next middleware or final handler.
|
|
@@ -1051,6 +1057,10 @@ export type MiddlewareNext = () => Promise<Response>;
|
|
|
1051
1057
|
* ```
|
|
1052
1058
|
*/
|
|
1053
1059
|
export type Middleware<TRequest extends Request = Request, TServer = any, TContext extends ApiHandlerContext<TRequest, TServer> = ApiHandlerContext<TRequest, TServer>> = (context: TContext, next: MiddlewareNext) => Promise<Response> | Response;
|
|
1060
|
+
/**
|
|
1061
|
+
* Middleware contract for file-based page routes.
|
|
1062
|
+
*/
|
|
1063
|
+
export type FileRouteMiddleware<TRequest extends Request = Request, TServer = any, TContext extends FileRouteMiddlewareContext<TRequest, TServer> = FileRouteMiddlewareContext<TRequest, TServer>> = (context: TContext, next: MiddlewareNext) => Promise<Response> | Response;
|
|
1054
1064
|
/**
|
|
1055
1065
|
* Helper type for defining middleware with extended context.
|
|
1056
1066
|
* Automatically infers TRequest and TServer from the provided context type.
|
|
@@ -10,23 +10,23 @@ const createMockHmrManager = () => ({
|
|
|
10
10
|
}),
|
|
11
11
|
registerEntrypoint: vi.fn(async () => ""),
|
|
12
12
|
registerScriptEntrypoint: vi.fn(async () => ""),
|
|
13
|
-
registerSpecifierMap: vi.fn(() => {
|
|
14
|
-
}),
|
|
15
13
|
registerStrategy: vi.fn(() => {
|
|
16
14
|
}),
|
|
17
15
|
isEnabled: vi.fn(() => true),
|
|
18
16
|
getOutputUrl: vi.fn(() => void 0),
|
|
19
17
|
getWatchedFiles: vi.fn(() => /* @__PURE__ */ new Map()),
|
|
20
|
-
getSpecifierMap: vi.fn(() => /* @__PURE__ */ new Map()),
|
|
21
18
|
getDistDir: vi.fn(() => ""),
|
|
22
19
|
getPlugins: vi.fn(() => []),
|
|
23
20
|
getDefaultContext: vi.fn(() => ({
|
|
24
21
|
getWatchedFiles: () => /* @__PURE__ */ new Map(),
|
|
25
|
-
getSpecifierMap: () => /* @__PURE__ */ new Map(),
|
|
26
22
|
getDistDir: () => "",
|
|
27
23
|
getPlugins: () => [],
|
|
28
24
|
getSrcDir: () => "",
|
|
29
|
-
getLayoutsDir: () => ""
|
|
25
|
+
getLayoutsDir: () => "",
|
|
26
|
+
getPagesDir: () => "",
|
|
27
|
+
getBuildExecutor: () => ({ build: vi.fn(async () => ({ success: true, logs: [], outputs: [] })) }),
|
|
28
|
+
getBrowserBundleService: () => ({ bundle: vi.fn(async () => ({ success: true, logs: [], outputs: [] })) }),
|
|
29
|
+
importServerModule: vi.fn(async () => ({}))
|
|
30
30
|
}))
|
|
31
31
|
});
|
|
32
32
|
const createMockBridge = () => ({
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { EcoPagesAppConfig } from '../../types/internal-types.js';
|
|
2
|
-
import type { BoundaryPlan, EcoComponent } from '../../types/public-types.js';
|
|
3
|
-
type BoundaryPlanBuildInput = {
|
|
4
|
-
routeFile: string;
|
|
5
|
-
currentIntegrationName: string;
|
|
6
|
-
HtmlTemplate: EcoComponent;
|
|
7
|
-
Layout?: EcoComponent;
|
|
8
|
-
Page: EcoComponent;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Builds a declared ownership plan from the component dependency graph.
|
|
12
|
-
*
|
|
13
|
-
* The plan is intentionally conservative: it reflects declared component
|
|
14
|
-
* dependencies available during render preparation and records diagnostics for
|
|
15
|
-
* foreign ownership edges that cannot be validated against registered
|
|
16
|
-
* integrations or stable component metadata.
|
|
17
|
-
*/
|
|
18
|
-
export declare class BoundaryPlanningService {
|
|
19
|
-
private readonly appConfig;
|
|
20
|
-
private nextSyntheticId;
|
|
21
|
-
constructor(appConfig: EcoPagesAppConfig);
|
|
22
|
-
buildPlan(input: BoundaryPlanBuildInput): BoundaryPlan;
|
|
23
|
-
private isRegisteredIntegration;
|
|
24
|
-
}
|
|
25
|
-
export {};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
class BoundaryPlanningService {
|
|
2
|
-
appConfig;
|
|
3
|
-
nextSyntheticId = 0;
|
|
4
|
-
constructor(appConfig) {
|
|
5
|
-
this.appConfig = appConfig;
|
|
6
|
-
}
|
|
7
|
-
buildPlan(input) {
|
|
8
|
-
this.nextSyntheticId = 0;
|
|
9
|
-
const validationErrors = [];
|
|
10
|
-
const rendererNames = /* @__PURE__ */ new Set([input.currentIntegrationName]);
|
|
11
|
-
let foreignEdgeCount = 0;
|
|
12
|
-
const buildNode = (component, source, parentIntegrationName, lineage) => {
|
|
13
|
-
const integrationName = component.config?.integration ?? component.config?.__eco?.integration ?? parentIntegrationName;
|
|
14
|
-
const componentMeta = component.config?.__eco;
|
|
15
|
-
const isForeignToParent = integrationName !== parentIntegrationName;
|
|
16
|
-
const componentId = componentMeta?.id ?? componentMeta?.file ?? `${source}:${this.nextSyntheticId += 1}`;
|
|
17
|
-
rendererNames.add(integrationName);
|
|
18
|
-
if (isForeignToParent) {
|
|
19
|
-
foreignEdgeCount += 1;
|
|
20
|
-
if (!componentMeta) {
|
|
21
|
-
validationErrors.push({
|
|
22
|
-
code: "MISSING_COMPONENT_METADATA",
|
|
23
|
-
message: `[ecopages] Foreign boundary "${componentId}" must provide stable __eco metadata so ownership diagnostics stay actionable. Declared dependencies must include all possible foreign children.`,
|
|
24
|
-
componentId,
|
|
25
|
-
integrationName
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
if (!this.isRegisteredIntegration(integrationName, input.currentIntegrationName)) {
|
|
29
|
-
validationErrors.push({
|
|
30
|
-
code: "UNKNOWN_INTEGRATION_OWNER",
|
|
31
|
-
message: `[ecopages] Foreign boundary "${componentId}" references unknown integration owner "${integrationName}". Declared dependencies must include all possible foreign children and those integrations must be registered.`,
|
|
32
|
-
componentId,
|
|
33
|
-
componentFile: componentMeta?.file,
|
|
34
|
-
integrationName
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const nextLineage = new Set(lineage);
|
|
39
|
-
nextLineage.add(component);
|
|
40
|
-
const children = (component.config?.dependencies?.components ?? []).flatMap((child) => {
|
|
41
|
-
if (!child || nextLineage.has(child)) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
return [buildNode(child, "dependency", integrationName, nextLineage)];
|
|
45
|
-
});
|
|
46
|
-
return {
|
|
47
|
-
id: componentId,
|
|
48
|
-
source,
|
|
49
|
-
ownership: {
|
|
50
|
-
integrationName,
|
|
51
|
-
componentId,
|
|
52
|
-
componentFile: componentMeta?.file,
|
|
53
|
-
isPageEntry: source === "page",
|
|
54
|
-
isForeignToParent
|
|
55
|
-
},
|
|
56
|
-
children,
|
|
57
|
-
declaredDependenciesValid: true
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
const roots = [
|
|
61
|
-
{ component: input.HtmlTemplate, source: "html-template" },
|
|
62
|
-
...input.Layout ? [{ component: input.Layout, source: "layout" }] : [],
|
|
63
|
-
{ component: input.Page, source: "page" }
|
|
64
|
-
];
|
|
65
|
-
const root = {
|
|
66
|
-
id: `route:${input.routeFile}`,
|
|
67
|
-
source: "route",
|
|
68
|
-
ownership: {
|
|
69
|
-
integrationName: input.currentIntegrationName,
|
|
70
|
-
componentId: `route:${input.routeFile}`,
|
|
71
|
-
componentFile: input.routeFile,
|
|
72
|
-
isPageEntry: false,
|
|
73
|
-
isForeignToParent: false
|
|
74
|
-
},
|
|
75
|
-
children: roots.map(
|
|
76
|
-
({ component, source }) => buildNode(component, source, input.currentIntegrationName, /* @__PURE__ */ new Set())
|
|
77
|
-
),
|
|
78
|
-
declaredDependenciesValid: validationErrors.length === 0
|
|
79
|
-
};
|
|
80
|
-
return {
|
|
81
|
-
root,
|
|
82
|
-
rendererNames: Array.from(rendererNames),
|
|
83
|
-
foreignEdgeCount,
|
|
84
|
-
hasValidationErrors: validationErrors.length > 0,
|
|
85
|
-
validationErrors
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
isRegisteredIntegration(integrationName, currentIntegrationName) {
|
|
89
|
-
if (integrationName === currentIntegrationName) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
return this.appConfig.integrations.some((integration) => integration.name === integrationName);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
export {
|
|
96
|
-
BoundaryPlanningService
|
|
97
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
|
|
2
|
-
import type { PagePackageResult } from '../../types/public-types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Creates the structured page package consumed by final HTML injection.
|
|
5
|
-
*
|
|
6
|
-
* This first pass is intentionally behavior-preserving. It establishes the
|
|
7
|
-
* packaging seam while forwarding the current flat processed asset list.
|
|
8
|
-
*/
|
|
9
|
-
export declare class PagePackagingService {
|
|
10
|
-
/**
|
|
11
|
-
* Partitions processed assets into the page-level groups used during final
|
|
12
|
-
* HTML injection and post-processing.
|
|
13
|
-
*/
|
|
14
|
-
createPagePackage(assets: ProcessedAsset[]): PagePackageResult;
|
|
15
|
-
private shouldIncludeInHtml;
|
|
16
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
class PagePackagingService {
|
|
2
|
-
/**
|
|
3
|
-
* Partitions processed assets into the page-level groups used during final
|
|
4
|
-
* HTML injection and post-processing.
|
|
5
|
-
*/
|
|
6
|
-
createPagePackage(assets) {
|
|
7
|
-
const inlineAssets = [];
|
|
8
|
-
const separateAssets = [];
|
|
9
|
-
const dynamicChunks = [];
|
|
10
|
-
let pageScript;
|
|
11
|
-
let pageStylesheet;
|
|
12
|
-
for (const asset of assets) {
|
|
13
|
-
if (asset.inline) {
|
|
14
|
-
inlineAssets.push(asset);
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
if (asset.packageRole === "dynamic-chunk") {
|
|
18
|
-
dynamicChunks.push(asset);
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
if (!pageScript && asset.packageRole === "page-script") {
|
|
22
|
-
pageScript = asset;
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
if (!pageStylesheet && asset.packageRole === "page-style") {
|
|
26
|
-
pageStylesheet = asset;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
if (asset.packageRole === "keep-separate" || asset.packageRole === "runtime") {
|
|
30
|
-
separateAssets.push(asset);
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (!pageScript && asset.kind === "script" && !asset.excludeFromHtml) {
|
|
34
|
-
pageScript = asset;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (!pageStylesheet && asset.kind === "stylesheet") {
|
|
38
|
-
pageStylesheet = asset;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
separateAssets.push(asset);
|
|
42
|
-
}
|
|
43
|
-
const htmlAssets = assets.filter((asset) => this.shouldIncludeInHtml(asset));
|
|
44
|
-
return {
|
|
45
|
-
assets,
|
|
46
|
-
htmlAssets,
|
|
47
|
-
pageScript,
|
|
48
|
-
pageStylesheet,
|
|
49
|
-
inlineAssets,
|
|
50
|
-
separateAssets,
|
|
51
|
-
dynamicChunks
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
shouldIncludeInHtml(asset) {
|
|
55
|
-
if (asset.excludeFromHtml) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
if (asset.packageRole === "runtime") {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
export {
|
|
65
|
-
PagePackagingService
|
|
66
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
|
|
2
|
-
import type { BaseIntegrationContext, BoundaryRenderPayload, ComponentRenderInput, EcoComponent } from '../../types/public-types.js';
|
|
3
|
-
import type { ComponentBoundaryRuntime } from './component-render-context.js';
|
|
4
|
-
export type QueuedBoundaryDecisionInput = {
|
|
5
|
-
currentIntegration: string;
|
|
6
|
-
targetIntegration?: string;
|
|
7
|
-
component: EcoComponent;
|
|
8
|
-
props: Record<string, unknown>;
|
|
9
|
-
};
|
|
10
|
-
export type QueuedBoundaryResolution = {
|
|
11
|
-
token: string;
|
|
12
|
-
component: EcoComponent;
|
|
13
|
-
props: Record<string, unknown>;
|
|
14
|
-
componentInstanceId: string;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Shared mutable state for one renderer-owned queued boundary runtime.
|
|
18
|
-
*
|
|
19
|
-
* Renderers that cannot resolve foreign boundaries inline can enqueue transport
|
|
20
|
-
* tokens during their initial render, then resolve those tokens against the
|
|
21
|
-
* owning renderer before returning final HTML.
|
|
22
|
-
*/
|
|
23
|
-
export type QueuedBoundaryRuntimeContext = {
|
|
24
|
-
rendererCache: Map<string, unknown>;
|
|
25
|
-
componentInstanceScope?: string;
|
|
26
|
-
nextBoundaryId: number;
|
|
27
|
-
queuedResolutions: QueuedBoundaryResolution[];
|
|
28
|
-
};
|
|
29
|
-
type QueuedBoundaryIntegrationContext = BaseIntegrationContext & Record<string, unknown>;
|
|
30
|
-
type QueuedBoundaryChildRenderResult = {
|
|
31
|
-
assets: ProcessedAsset[];
|
|
32
|
-
html?: string;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Shared queue orchestration for renderer-owned boundary runtimes that emit
|
|
36
|
-
* temporary transport tokens during one render pass.
|
|
37
|
-
*
|
|
38
|
-
* The service keeps three responsibilities in one place:
|
|
39
|
-
* - storing per-render queue state on the active integration context
|
|
40
|
-
* - creating a `ComponentBoundaryRuntime` that enqueues foreign boundaries
|
|
41
|
-
* - resolving queued tokens back through the owning renderer before final HTML
|
|
42
|
-
* leaves the current renderer
|
|
43
|
-
*
|
|
44
|
-
* Renderers still own framework-specific child rendering. This service only
|
|
45
|
-
* handles queue bookkeeping, recursion, cycle detection, and asset merging.
|
|
46
|
-
*/
|
|
47
|
-
export declare class QueuedBoundaryRuntimeService {
|
|
48
|
-
/**
|
|
49
|
-
* Reads the queued boundary runtime state previously attached to one render.
|
|
50
|
-
*/
|
|
51
|
-
getRuntimeContext<TContext extends QueuedBoundaryRuntimeContext>(input: ComponentRenderInput, runtimeContextKey: string): TContext | undefined;
|
|
52
|
-
/**
|
|
53
|
-
* Creates the runtime hook used by `runWithComponentRenderContext()` for one
|
|
54
|
-
* renderer-owned queue.
|
|
55
|
-
*
|
|
56
|
-
* When the renderer decides a boundary must be handed off, the runtime returns
|
|
57
|
-
* a resolved transport token instead of rendering the foreign component inline.
|
|
58
|
-
*/
|
|
59
|
-
createRuntime<TContext extends QueuedBoundaryRuntimeContext>(options: {
|
|
60
|
-
boundaryInput: ComponentRenderInput;
|
|
61
|
-
rendererCache: Map<string, unknown>;
|
|
62
|
-
runtimeContextKey: string;
|
|
63
|
-
tokenPrefix: string;
|
|
64
|
-
shouldQueueBoundary: (input: QueuedBoundaryDecisionInput) => boolean;
|
|
65
|
-
createRuntimeContext?: (integrationContext: QueuedBoundaryIntegrationContext, rendererCache: Map<string, unknown>) => TContext;
|
|
66
|
-
}): ComponentBoundaryRuntime;
|
|
67
|
-
/**
|
|
68
|
-
* Resolves every queued transport token in one renderer-owned HTML fragment.
|
|
69
|
-
*
|
|
70
|
-
* The caller supplies framework-specific child rendering, while this service
|
|
71
|
-
* handles recursive token replacement, cycle detection, root-attribute
|
|
72
|
-
* application, and merged asset collection.
|
|
73
|
-
*/
|
|
74
|
-
resolveQueuedHtml<TContext extends QueuedBoundaryRuntimeContext>(options: {
|
|
75
|
-
html: string;
|
|
76
|
-
runtimeContext?: TContext;
|
|
77
|
-
queueLabel: string;
|
|
78
|
-
renderQueuedChildren: (children: unknown, runtimeContext: TContext, queuedResolutionsByToken: Map<string, QueuedBoundaryResolution>, resolveToken: (token: string) => Promise<string>) => Promise<QueuedBoundaryChildRenderResult>;
|
|
79
|
-
resolveBoundary: (input: ComponentRenderInput, rendererCache: Map<string, unknown>) => Promise<BoundaryRenderPayload | undefined>;
|
|
80
|
-
applyAttributesToFirstElement: (html: string, attributes: Record<string, string>) => string;
|
|
81
|
-
dedupeProcessedAssets: (assets: ProcessedAsset[]) => ProcessedAsset[];
|
|
82
|
-
}): Promise<{
|
|
83
|
-
assets: ProcessedAsset[];
|
|
84
|
-
html: string;
|
|
85
|
-
}>;
|
|
86
|
-
private createBoundaryToken;
|
|
87
|
-
private ensureRuntimeContext;
|
|
88
|
-
}
|
|
89
|
-
export {};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { IntegrationRendererRenderOptions, RouteRendererBody, RouteRendererOptions, RouteRenderResult } from '../../types/public-types.js';
|
|
2
|
-
export interface CapturedHtmlRenderResult {
|
|
3
|
-
body: RouteRendererBody;
|
|
4
|
-
html: string;
|
|
5
|
-
}
|
|
6
|
-
export interface FinalizeHtmlRenderOptions {
|
|
7
|
-
html: string;
|
|
8
|
-
componentRootAttributes?: Record<string, string>;
|
|
9
|
-
documentAttributes?: Record<string, string>;
|
|
10
|
-
}
|
|
11
|
-
export interface RenderExecutionCallbacks<C> {
|
|
12
|
-
prepareRenderOptions(options: RouteRendererOptions): Promise<IntegrationRendererRenderOptions<C>>;
|
|
13
|
-
render(renderOptions: IntegrationRendererRenderOptions<C>): Promise<RouteRendererBody>;
|
|
14
|
-
getDocumentAttributes(renderOptions: IntegrationRendererRenderOptions<C>): Record<string, string> | undefined;
|
|
15
|
-
applyAttributesToHtmlElement(html: string, attributes: Record<string, string>): string;
|
|
16
|
-
applyAttributesToFirstBodyElement(html: string, attributes: Record<string, string>): string;
|
|
17
|
-
transformResponse(response: Response): Promise<RouteRendererBody>;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Executes the main post-preparation rendering flow for integration renderers.
|
|
21
|
-
*
|
|
22
|
-
* This service owns the orchestration that happens after normalized render
|
|
23
|
-
* options have been prepared: one render pass, unresolved boundary-marker
|
|
24
|
-
* enforcement, root-attribute application, and final HTML transformation into
|
|
25
|
-
* a response body stream.
|
|
26
|
-
*/
|
|
27
|
-
export declare class RenderExecutionService {
|
|
28
|
-
captureHtmlRender(render: () => Promise<RouteRendererBody>): Promise<CapturedHtmlRenderResult>;
|
|
29
|
-
/**
|
|
30
|
-
* Executes one integration render pass and returns the final route render
|
|
31
|
-
* result.
|
|
32
|
-
*
|
|
33
|
-
* @typeParam C Integration render output element type.
|
|
34
|
-
* @param options Route-level render options.
|
|
35
|
-
* @param currentIntegrationName Active integration name for this render pass.
|
|
36
|
-
* @param callbacks Renderer-specific hooks required during execution.
|
|
37
|
-
* @returns Final route render output with body and cache strategy.
|
|
38
|
-
*/
|
|
39
|
-
execute<C = unknown>(options: RouteRendererOptions, callbacks: RenderExecutionCallbacks<C>): Promise<RouteRenderResult>;
|
|
40
|
-
private captureRenderedBody;
|
|
41
|
-
finalizeHtmlRender(options: FinalizeHtmlRenderOptions, callbacks: Pick<RenderExecutionCallbacks<unknown>, 'applyAttributesToHtmlElement' | 'applyAttributesToFirstBodyElement'>): Promise<string>;
|
|
42
|
-
private applyFinalHtmlAttributes;
|
|
43
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { inspectBoundaryArtifactHtml } from "./render-output.utils.js";
|
|
2
|
-
class RenderExecutionService {
|
|
3
|
-
async captureHtmlRender(render) {
|
|
4
|
-
const renderedBody = await render();
|
|
5
|
-
const capturedRender = await this.captureRenderedBody(renderedBody);
|
|
6
|
-
return {
|
|
7
|
-
body: capturedRender.body,
|
|
8
|
-
html: capturedRender.html
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Executes one integration render pass and returns the final route render
|
|
13
|
-
* result.
|
|
14
|
-
*
|
|
15
|
-
* @typeParam C Integration render output element type.
|
|
16
|
-
* @param options Route-level render options.
|
|
17
|
-
* @param currentIntegrationName Active integration name for this render pass.
|
|
18
|
-
* @param callbacks Renderer-specific hooks required during execution.
|
|
19
|
-
* @returns Final route render output with body and cache strategy.
|
|
20
|
-
*/
|
|
21
|
-
async execute(options, callbacks) {
|
|
22
|
-
const renderOptions = await callbacks.prepareRenderOptions(options);
|
|
23
|
-
const shouldApplyComponentRootAttributes = renderOptions.componentRender?.canAttachAttributes && renderOptions.componentRender.rootAttributes && Object.keys(renderOptions.componentRender.rootAttributes).length > 0;
|
|
24
|
-
const renderExecution = await this.captureHtmlRender(async () => callbacks.render(renderOptions));
|
|
25
|
-
const boundaryArtifacts = inspectBoundaryArtifactHtml(renderExecution.html);
|
|
26
|
-
const documentAttributes = callbacks.getDocumentAttributes(renderOptions);
|
|
27
|
-
const hasBoundaryMarkerHtml = boundaryArtifacts.hasUnresolvedBoundaryArtifacts;
|
|
28
|
-
if (hasBoundaryMarkerHtml) {
|
|
29
|
-
throw new Error(
|
|
30
|
-
"[ecopages] Route render returned unresolved boundary artifact HTML. Full-route unresolved-boundary fallback has been removed; resolve mixed boundaries inside renderComponentBoundary()."
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
const canReuseCapturedBody = !hasBoundaryMarkerHtml && !shouldApplyComponentRootAttributes && !(documentAttributes && Object.keys(documentAttributes).length > 0);
|
|
34
|
-
if (canReuseCapturedBody) {
|
|
35
|
-
const body2 = await callbacks.transformResponse(
|
|
36
|
-
new Response(renderExecution.body, {
|
|
37
|
-
headers: {
|
|
38
|
-
"Content-Type": "text/html"
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
);
|
|
42
|
-
return {
|
|
43
|
-
body: body2,
|
|
44
|
-
cacheStrategy: renderOptions.cacheStrategy
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
const finalization = await this.finalizeHtmlRender(
|
|
48
|
-
{
|
|
49
|
-
html: boundaryArtifacts.normalizedHtml,
|
|
50
|
-
componentRootAttributes: shouldApplyComponentRootAttributes ? renderOptions.componentRender?.rootAttributes : void 0,
|
|
51
|
-
documentAttributes
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
applyAttributesToHtmlElement: callbacks.applyAttributesToHtmlElement,
|
|
55
|
-
applyAttributesToFirstBodyElement: callbacks.applyAttributesToFirstBodyElement
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
const body = await callbacks.transformResponse(
|
|
59
|
-
new Response(finalization, {
|
|
60
|
-
headers: {
|
|
61
|
-
"Content-Type": "text/html"
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
);
|
|
65
|
-
return {
|
|
66
|
-
body,
|
|
67
|
-
cacheStrategy: renderOptions.cacheStrategy
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
async captureRenderedBody(body) {
|
|
71
|
-
const response = new Response(body);
|
|
72
|
-
if (typeof body === "string") {
|
|
73
|
-
return {
|
|
74
|
-
body,
|
|
75
|
-
html: await response.text()
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
if (!response.body) {
|
|
79
|
-
return {
|
|
80
|
-
body,
|
|
81
|
-
html: await response.text()
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
const [capturedBody, replayBody] = response.body.tee();
|
|
85
|
-
return {
|
|
86
|
-
body: replayBody,
|
|
87
|
-
html: await new Response(capturedBody).text()
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
async finalizeHtmlRender(options, callbacks) {
|
|
91
|
-
return this.applyFinalHtmlAttributes(options.html, options, callbacks);
|
|
92
|
-
}
|
|
93
|
-
applyFinalHtmlAttributes(html, options, callbacks) {
|
|
94
|
-
let renderedHtml = html;
|
|
95
|
-
if (options.componentRootAttributes && Object.keys(options.componentRootAttributes).length > 0) {
|
|
96
|
-
renderedHtml = callbacks.applyAttributesToFirstBodyElement(renderedHtml, options.componentRootAttributes);
|
|
97
|
-
}
|
|
98
|
-
if (options.documentAttributes && Object.keys(options.documentAttributes).length > 0) {
|
|
99
|
-
renderedHtml = callbacks.applyAttributesToHtmlElement(renderedHtml, options.documentAttributes);
|
|
100
|
-
}
|
|
101
|
-
return renderedHtml;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
export {
|
|
105
|
-
RenderExecutionService
|
|
106
|
-
};
|