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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/package.json +58 -50
  2. package/src/adapters/bun/server-adapter.js +2 -2
  3. package/src/adapters/node/server-adapter.js +2 -2
  4. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +10 -0
  5. package/src/adapters/shared/hmr-entrypoint-registrar.js +34 -10
  6. package/src/adapters/shared/runtime-server-lifecycle.js +1 -1
  7. package/src/adapters/shared/server-static-builder.d.ts +2 -1
  8. package/src/adapters/shared/server-static-builder.js +16 -14
  9. package/src/adapters/shared/shared-hmr-manager.d.ts +11 -4
  10. package/src/adapters/shared/shared-hmr-manager.js +22 -3
  11. package/src/build/README.md +92 -46
  12. package/src/build/app-build-manifest-runtime.d.ts +28 -2
  13. package/src/build/app-build-manifest-runtime.js +26 -10
  14. package/src/build/{browser-runtime-plugin.d.ts → browser/browser-runtime-plugin.d.ts} +1 -1
  15. package/src/build/{browser-runtime-plugin.js → browser/browser-runtime-plugin.js} +1 -1
  16. package/src/build/{jsx-ownership-plugins.d.ts → browser/jsx-ownership-plugins.d.ts} +2 -2
  17. package/src/build/{jsx-ownership-plugins.js → browser/jsx-ownership-plugins.js} +2 -2
  18. package/src/build/build-adapter.d.ts +18 -21
  19. package/src/build/build-adapter.js +8 -43
  20. package/src/build/{build-input-fingerprint.d.ts → cache/build-input-fingerprint.d.ts} +3 -3
  21. package/src/build/cache/cache-constants.d.ts +4 -0
  22. package/src/build/cache/cache-constants.js +6 -0
  23. package/src/build/cache/cache-keys.d.ts +22 -0
  24. package/src/build/cache/cache-keys.js +63 -0
  25. package/src/build/{dev-browser-script-cache.d.ts → cache/dev-browser-script-cache.d.ts} +2 -2
  26. package/src/build/{dev-browser-script-cache.js → cache/dev-browser-script-cache.js} +4 -4
  27. package/src/build/{pages-unified-graph-build.d.ts → cache/pages-unified-graph-build.d.ts} +1 -1
  28. package/src/build/{pages-unified-graph-build.js → cache/pages-unified-graph-build.js} +18 -26
  29. package/src/build/{production-build-cache.d.ts → cache/production-build-cache.d.ts} +1 -1
  30. package/src/build/{production-build-cache.js → cache/production-build-cache.js} +2 -2
  31. package/src/build/{server-entry-build-cache.d.ts → cache/server-entry-build-cache.d.ts} +3 -4
  32. package/src/build/{server-entry-build-cache.js → cache/server-entry-build-cache.js} +16 -12
  33. package/src/build/{build-contracts.d.ts → contracts/build-contracts.d.ts} +1 -2
  34. package/src/build/contracts/build-manifest.d.ts +90 -0
  35. package/src/build/{build-manifest.js → contracts/build-manifest.js} +2 -2
  36. package/src/build/{rolldown-adapter-helpers.d.ts → rolldown/rolldown-adapter-helpers.d.ts} +20 -3
  37. package/src/build/{rolldown-adapter-helpers.js → rolldown/rolldown-adapter-helpers.js} +26 -6
  38. package/src/build/{rolldown-build-adapter.d.ts → rolldown/rolldown-build-adapter.d.ts} +1 -1
  39. package/src/build/{rolldown-plugin-bridge.d.ts → rolldown/rolldown-plugin-bridge.d.ts} +2 -2
  40. package/src/build/{rolldown-plugin-bridge.js → rolldown/rolldown-plugin-bridge.js} +1 -1
  41. package/src/build/{rolldown-source-transform-pass.d.ts → rolldown/rolldown-source-transform-pass.d.ts} +1 -1
  42. package/src/build/{rolldown-source-transform-pass.js → rolldown/rolldown-source-transform-pass.js} +4 -1
  43. package/src/build/{runtime-build-output-normalizer.js → rolldown/runtime-build-output-normalizer.js} +2 -2
  44. package/src/build/{build-profile-options.d.ts → runtime/build-profile-options.d.ts} +2 -2
  45. package/src/build/runtime/build-request-identity.d.ts +17 -0
  46. package/src/build/runtime/build-request-identity.js +54 -0
  47. package/src/build/runtime/build-request-policy.d.ts +59 -0
  48. package/src/build/runtime/build-request-policy.js +46 -0
  49. package/src/build/{build-runtime.d.ts → runtime/build-runtime.d.ts} +8 -4
  50. package/src/build/{build-runtime.js → runtime/build-runtime.js} +11 -11
  51. package/src/build/{deduping-build-executor.d.ts → runtime/deduping-build-executor.d.ts} +2 -9
  52. package/src/build/runtime/deduping-build-executor.js +34 -0
  53. package/src/build/{parallel-build-executor.d.ts → runtime/parallel-build-executor.d.ts} +1 -1
  54. package/src/build/{serialized-build-executor.d.ts → runtime/serialized-build-executor.d.ts} +1 -1
  55. package/src/config/config-builder.d.ts +1 -1
  56. package/src/diagnostics/startup-trace.d.ts +49 -2
  57. package/src/diagnostics/startup-trace.js +140 -8
  58. package/src/hmr/client/hmr-runtime.js +1 -1
  59. package/src/hmr/hmr-file-change-prep.d.ts +10 -0
  60. package/src/hmr/hmr-file-change-prep.js +17 -0
  61. package/src/plugins/alias-resolver-plugin.d.ts +1 -1
  62. package/src/plugins/eco-component-meta-plugin.d.ts +1 -1
  63. package/src/plugins/foreign-jsx-override-plugin.d.ts +1 -1
  64. package/src/plugins/integration-plugin.d.ts +16 -5
  65. package/src/plugins/integration-plugin.js +11 -1
  66. package/src/plugins/processor.d.ts +28 -8
  67. package/src/plugins/source-transform.d.ts +1 -1
  68. package/src/route-renderer/GRAPH.md +13 -19
  69. package/src/route-renderer/README.md +1 -1
  70. package/src/route-renderer/orchestration/component-render-context.d.ts +4 -4
  71. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +6 -2
  72. package/src/route-renderer/orchestration/integration-renderer.d.ts +11 -52
  73. package/src/route-renderer/orchestration/integration-renderer.js +38 -91
  74. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +1 -8
  75. package/src/route-renderer/orchestration/integration-route-render-adapter.js +0 -2
  76. package/src/route-renderer/orchestration/page-browser-graph-session.d.ts +106 -0
  77. package/src/route-renderer/orchestration/page-browser-graph-session.js +436 -0
  78. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +9 -3
  79. package/src/route-renderer/orchestration/page-browser-graph.service.js +89 -40
  80. package/src/route-renderer/orchestration/render-output.utils.d.ts +14 -0
  81. package/src/route-renderer/orchestration/render-output.utils.js +30 -0
  82. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +0 -1
  83. package/src/route-renderer/orchestration/route-html-finalization.service.js +4 -9
  84. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +1 -2
  85. package/src/route-renderer/orchestration/route-prepared-options.builder.js +1 -10
  86. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +3 -12
  87. package/src/route-renderer/orchestration/route-render-orchestrator.js +4 -14
  88. package/src/services/assets/asset-processing-service/asset-processing.service.js +1 -1
  89. package/src/services/assets/asset-processing-service/assets.types.d.ts +1 -1
  90. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -1
  91. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.d.ts +0 -2
  92. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +2 -15
  93. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +1 -1
  94. package/src/services/assets/browser-bundle.service.d.ts +27 -31
  95. package/src/services/assets/browser-bundle.service.js +12 -23
  96. package/src/services/module-loading/app-server-module-transpiler.service.d.ts +8 -0
  97. package/src/services/module-loading/app-server-module-transpiler.service.js +1 -7
  98. package/src/services/module-loading/page-module-import.service.d.ts +1 -1
  99. package/src/services/module-loading/page-module-import.service.js +40 -28
  100. package/src/services/module-loading/route-module-build-cache.store.js +1 -1
  101. package/src/services/module-loading/route-module-build-manifest.d.ts +2 -9
  102. package/src/services/module-loading/route-module-build-manifest.js +12 -43
  103. package/src/services/module-loading/server-module-transpiler.service.d.ts +1 -1
  104. package/src/static-site-generator/README.md +1 -1
  105. package/src/static-site-generator/production-page-browser-graph-prebuild.d.ts +28 -0
  106. package/src/static-site-generator/production-page-browser-graph-prebuild.js +22 -0
  107. package/src/static-site-generator/static-build-invalidation.d.ts +2 -2
  108. package/src/static-site-generator/static-build-invalidation.js +3 -3
  109. package/src/static-site-generator/static-site-generator.js +23 -1
  110. package/src/types/internal-types.d.ts +10 -4
  111. package/src/types/public-types.d.ts +58 -9
  112. package/src/watchers/project-watcher.js +12 -2
  113. package/src/build/build-manifest.d.ts +0 -33
  114. package/src/build/deduping-build-executor.js +0 -56
  115. package/src/build/runtime-build-executor.d.ts +0 -14
  116. package/src/build/runtime-build-executor.js +0 -16
  117. /package/src/build/{browser-runtime-manifest.d.ts → browser/browser-runtime-manifest.d.ts} +0 -0
  118. /package/src/build/{browser-runtime-manifest.js → browser/browser-runtime-manifest.js} +0 -0
  119. /package/src/build/{browser-runtime-plugin-helpers.d.ts → browser/browser-runtime-plugin-helpers.d.ts} +0 -0
  120. /package/src/build/{browser-runtime-plugin-helpers.js → browser/browser-runtime-plugin-helpers.js} +0 -0
  121. /package/src/build/{lit-static-render-worker-context.d.ts → browser/lit-static-render-worker-context.d.ts} +0 -0
  122. /package/src/build/{lit-static-render-worker-context.js → browser/lit-static-render-worker-context.js} +0 -0
  123. /package/src/build/{build-input-fingerprint.js → cache/build-input-fingerprint.js} +0 -0
  124. /package/src/build/{build-contracts.js → contracts/build-contracts.js} +0 -0
  125. /package/src/build/{build-types.d.ts → contracts/build-types.d.ts} +0 -0
  126. /package/src/build/{build-types.js → contracts/build-types.js} +0 -0
  127. /package/src/build/{rolldown-build-adapter.js → rolldown/rolldown-build-adapter.js} +0 -0
  128. /package/src/build/{rolldown-build-invocation-metrics.d.ts → rolldown/rolldown-build-invocation-metrics.d.ts} +0 -0
  129. /package/src/build/{rolldown-build-invocation-metrics.js → rolldown/rolldown-build-invocation-metrics.js} +0 -0
  130. /package/src/build/{runtime-build-output-normalizer.d.ts → rolldown/runtime-build-output-normalizer.d.ts} +0 -0
  131. /package/src/build/{server-side-css-shim-plugin.d.ts → rolldown/server-side-css-shim-plugin.d.ts} +0 -0
  132. /package/src/build/{server-side-css-shim-plugin.js → rolldown/server-side-css-shim-plugin.js} +0 -0
  133. /package/src/build/{build-profile-options.js → runtime/build-profile-options.js} +0 -0
  134. /package/src/build/{parallel-build-executor.js → runtime/parallel-build-executor.js} +0 -0
  135. /package/src/build/{serialized-build-executor.js → runtime/serialized-build-executor.js} +0 -0
@@ -3,6 +3,12 @@ import { fileSystem } from "@ecopages/file-system";
3
3
  import {
4
4
  } from "../../services/assets/asset-processing-service/index.js";
5
5
  import { appLogger } from "../../global/app-logger.js";
6
+ import { startupTrace } from "../../diagnostics/startup-trace.js";
7
+ import {
8
+ collectPageBrowserGraphDependencyPaths,
9
+ createPageBrowserGraphEntryFingerprint,
10
+ getAppPageBrowserGraphSession
11
+ } from "./page-browser-graph-session.js";
6
12
  function isGroupedContentScriptAsset(asset) {
7
13
  return asset.kind === "script" && asset.source === "content" && Boolean(asset.groupedBundle?.id);
8
14
  }
@@ -12,76 +18,106 @@ function getGroupedBundleAssetKey(groupedBundle) {
12
18
  class PageBrowserGraphService {
13
19
  appConfig;
14
20
  assetProcessingService;
15
- pageBrowserGraphCache = /* @__PURE__ */ new Map();
16
- groupedPageBrowserGraphCache = /* @__PURE__ */ new Map();
17
21
  constructor(appConfig, assetProcessingService) {
18
22
  this.appConfig = appConfig;
19
23
  this.assetProcessingService = assetProcessingService;
20
24
  }
21
25
  async resolvePageBrowserGraph(input) {
22
- if (this.isHmrEnabled()) {
23
- return await this.buildPageBrowserGraph(input);
26
+ return await this.getOrBuild(input);
27
+ }
28
+ /**
29
+ * Resolves or builds one Page Browser Graph under the given policy.
30
+ */
31
+ async getOrBuild(input) {
32
+ const policy = input.policy ?? this.resolveGraphPolicy();
33
+ const session = getAppPageBrowserGraphSession(this.appConfig);
34
+ if (!this.isHmrEnabled()) {
35
+ const cachedByRoute = session.getGraphByRoute(input.integrationName, input.routeFile, policy);
36
+ if (cachedByRoute) {
37
+ return cachedByRoute;
38
+ }
24
39
  }
25
- const cacheKey = `${input.integrationName}:${input.routeFile}`;
26
- const cachedGraph = this.pageBrowserGraphCache.get(cacheKey);
27
- if (cachedGraph) {
28
- return await cachedGraph;
40
+ const contribution = await input.collectContribution(input.routeFile);
41
+ if (!contribution) {
42
+ return void 0;
29
43
  }
30
- const pendingGraph = this.buildPageBrowserGraph(input).catch((error) => {
31
- this.pageBrowserGraphCache.delete(cacheKey);
32
- throw error;
33
- });
34
- this.pageBrowserGraphCache.set(cacheKey, pendingGraph);
35
- return await pendingGraph;
44
+ const entryFingerprint = createPageBrowserGraphEntryFingerprint(contribution);
45
+ return await session.resolveGraph(
46
+ {
47
+ integrationName: input.integrationName,
48
+ routeFile: input.routeFile,
49
+ entryFingerprint,
50
+ policy
51
+ },
52
+ async () => this.buildPageBrowserGraphRecord(input, contribution)
53
+ );
54
+ }
55
+ resolveGraphPolicy() {
56
+ return this.isHmrEnabled() ? "development" : "production";
36
57
  }
37
58
  isHmrEnabled() {
38
59
  return typeof this.assetProcessingService.getHmrManager === "function" && this.assetProcessingService.getHmrManager()?.isEnabled() === true;
39
60
  }
40
- async buildPageBrowserGraph(input) {
41
- const contribution = await input.collectContribution(input.routeFile);
42
- if (!contribution) {
61
+ async buildPageBrowserGraphRecord(input, contribution) {
62
+ const result = await this.materializePageBrowserGraph(input, contribution);
63
+ if (!result) {
43
64
  return void 0;
44
65
  }
66
+ startupTrace.markFirstPageBrowserGraphReady(getAppPageBrowserGraphSession(this.appConfig).getBuildCount());
67
+ return result;
68
+ }
69
+ async materializePageBrowserGraph(input, contribution) {
45
70
  const groupedDependencies = (contribution.dependencies ?? []).filter((dep) => isGroupedContentScriptAsset(dep));
46
71
  const ungroupedDependencies = (contribution.dependencies ?? []).filter(
47
72
  (dep) => !isGroupedContentScriptAsset(dep)
48
73
  );
49
- const groupedAssets = groupedDependencies.length ? (await this.resolveGroupedPageBrowserAssets(input, contribution)).get(input.routeFile) ?? [] : [];
74
+ const groupedAssets = groupedDependencies.length ? (await this.resolveGroupedPageBrowserAssets(input, contribution)).assetsByRoute.get(input.routeFile) ?? [] : [];
50
75
  const processedDependencies = ungroupedDependencies.length ? await this.assetProcessingService.processDependencies(
51
76
  ungroupedDependencies,
52
77
  `${input.integrationName}:${input.routeFile}`
53
78
  ) : [];
54
79
  const resolvedAssets = [...processedDependencies, ...groupedAssets, ...contribution.assets ?? []];
55
- return this.partitionPageBrowserGraphAssets(resolvedAssets);
80
+ const result = this.partitionPageBrowserGraphAssets(resolvedAssets);
81
+ const dependencyPaths = collectPageBrowserGraphDependencyPaths(input.routeFile, contribution, resolvedAssets);
82
+ return {
83
+ result,
84
+ dependencyPaths
85
+ };
56
86
  }
57
87
  async resolveGroupedPageBrowserAssets(input, currentContribution) {
58
88
  if (this.isHmrEnabled()) {
59
- const result = await this.buildGroupedPageBrowserAssets(input, currentContribution);
60
- return result.assetsByRoute;
89
+ return await this.buildGroupedPageBrowserAssets(input, currentContribution);
61
90
  }
62
- const cacheKey = input.integrationName;
63
- let pending = this.groupedPageBrowserGraphCache.get(cacheKey);
64
- if (!pending) {
65
- pending = this.buildGroupedPageBrowserAssets(input, currentContribution).then((result) => {
66
- if (result.hasCollectionFailures) {
67
- this.groupedPageBrowserGraphCache.delete(cacheKey);
68
- }
69
- return result.assetsByRoute;
70
- }).catch((error) => {
71
- this.groupedPageBrowserGraphCache.delete(cacheKey);
72
- throw error;
73
- });
74
- this.groupedPageBrowserGraphCache.set(cacheKey, pending);
75
- }
76
- return await pending;
91
+ const session = getAppPageBrowserGraphSession(this.appConfig);
92
+ const assetsByRoute = await session.resolveGroupedGraph(input.integrationName, async () => {
93
+ const built = await this.buildGroupedPageBrowserAssets(input, currentContribution);
94
+ if (built.hasCollectionFailures) {
95
+ return {
96
+ skipCache: true,
97
+ assetsByRoute: built.assetsByRoute,
98
+ dependencyPaths: built.dependencyPaths
99
+ };
100
+ }
101
+ return {
102
+ assetsByRoute: built.assetsByRoute,
103
+ dependencyPaths: built.dependencyPaths,
104
+ generation: 0
105
+ };
106
+ });
107
+ return {
108
+ assetsByRoute,
109
+ dependencyPaths: /* @__PURE__ */ new Set(),
110
+ hasCollectionFailures: false
111
+ };
77
112
  }
78
113
  async buildGroupedPageBrowserAssets(input, currentContribution) {
79
- const routeFiles = await this.listIntegrationRouteFiles(input.integrationName);
114
+ const routeFiles = this.isHmrEnabled() ? [input.routeFile] : await this.listIntegrationRouteFiles(input.integrationName);
80
115
  const currentRouteGroupedDependencies = (currentContribution.dependencies ?? []).filter(
81
116
  (dep) => isGroupedContentScriptAsset(dep)
82
117
  );
83
118
  const groupedDependencies = [...currentRouteGroupedDependencies];
84
119
  const groupedAssetKeysByRoute = /* @__PURE__ */ new Map();
120
+ const dependencyPaths = /* @__PURE__ */ new Set([path.resolve(input.routeFile)]);
85
121
  let hasCollectionFailures = false;
86
122
  if (currentRouteGroupedDependencies.length > 0) {
87
123
  groupedAssetKeysByRoute.set(
@@ -103,9 +139,6 @@ class PageBrowserGraphService {
103
139
  );
104
140
  continue;
105
141
  }
106
- if (this.isHmrEnabled()) {
107
- continue;
108
- }
109
142
  if (!contribution?.dependencies?.length) {
110
143
  continue;
111
144
  }
@@ -120,13 +153,20 @@ class PageBrowserGraphService {
120
153
  routeFile,
121
154
  new Set(routeGroupedDependencies.map((dep) => getGroupedBundleAssetKey(dep.groupedBundle)))
122
155
  );
156
+ dependencyPaths.add(path.resolve(routeFile));
123
157
  }
124
158
  if (groupedDependencies.length === 0) {
125
159
  return {
126
160
  assetsByRoute: /* @__PURE__ */ new Map(),
161
+ dependencyPaths,
127
162
  hasCollectionFailures
128
163
  };
129
164
  }
165
+ for (const dependency of groupedDependencies) {
166
+ if (dependency.source === "file") {
167
+ dependencyPaths.add(path.resolve(dependency.filepath));
168
+ }
169
+ }
130
170
  const processedGroupedDependencies = await this.assetProcessingService.processDependencies(
131
171
  groupedDependencies,
132
172
  `${input.integrationName}:grouped-page-browser-graph`
@@ -146,8 +186,17 @@ class PageBrowserGraphService {
146
186
  }
147
187
  groupedAssetsByRoute.set(routeFile, matchedAssets);
148
188
  }
189
+ for (const asset of processedGroupedDependencies) {
190
+ if (asset.sourceFilepath) {
191
+ dependencyPaths.add(path.resolve(asset.sourceFilepath));
192
+ }
193
+ for (const bundledSourceFilepath of asset.bundledSourceFilepaths ?? []) {
194
+ dependencyPaths.add(path.resolve(bundledSourceFilepath));
195
+ }
196
+ }
149
197
  return {
150
198
  assetsByRoute: groupedAssetsByRoute,
199
+ dependencyPaths,
151
200
  hasCollectionFailures
152
201
  };
153
202
  }
@@ -23,6 +23,20 @@ type MarkupNodeLikeShape = {
23
23
  export declare function isThenable<T>(value: unknown): value is PromiseLike<T>;
24
24
  /** Returns whether a value looks like a DOM-like markup node from SSR integrations. */
25
25
  export declare function isMarkupNodeLike(value: unknown): value is MarkupNodeLikeShape;
26
+ /**
27
+ * Returns whether a value is a plain opaque object that string serializers would
28
+ * coerce via `String(value)` rather than render as markup.
29
+ *
30
+ * @remarks
31
+ * Template results (`strings`/`values`), markup nodes, arrays, and framework
32
+ * element markers (`$$typeof`) are not opaque. Cross-integration children that
33
+ * are only plain objects must use EcoEmbed or already-serialized HTML.
34
+ */
35
+ export declare function isOpaqueForeignChildValue(children: unknown): boolean;
36
+ /**
37
+ * Throws when foreign-path children would be coerced from an opaque object.
38
+ */
39
+ export declare function assertForeignChildrenNotOpaque(children: unknown, context: string): void;
26
40
  /**
27
41
  * Injects `data-eco-trigger` into the first real HTML element opening tag of
28
42
  * a component's rendered output string.
@@ -85,6 +85,34 @@ function isMarkupNodeLikeShape(value) {
85
85
  function isMarkupNodeLike(value) {
86
86
  return isMarkupNodeLikeShape(value);
87
87
  }
88
+ function isOpaqueForeignChildValue(children) {
89
+ if (children === null || typeof children !== "object") {
90
+ return false;
91
+ }
92
+ if (Array.isArray(children)) {
93
+ return false;
94
+ }
95
+ if ("$$typeof" in children) {
96
+ return false;
97
+ }
98
+ if ("strings" in children && Array.isArray(children.strings)) {
99
+ return false;
100
+ }
101
+ if (isMarkupNodeLikeShape(children)) {
102
+ return false;
103
+ }
104
+ const prototype = Object.getPrototypeOf(children);
105
+ return prototype === Object.prototype || prototype === null;
106
+ }
107
+ function assertForeignChildrenNotOpaque(children, context) {
108
+ if (!isOpaqueForeignChildValue(children)) {
109
+ return;
110
+ }
111
+ const childTag = Object.prototype.toString.call(children);
112
+ throw new TypeError(
113
+ `[ecopages] ${context} refused to coerce opaque foreign children (${childTag}). Use EcoEmbed for cross-integration children or pass already-serialized HTML.`
114
+ );
115
+ }
88
116
  function injectTriggerAttributeIntoString(content, triggerId) {
89
117
  const str = content;
90
118
  let i = 0;
@@ -166,9 +194,11 @@ function inspectUnresolvedMarkerArtifactHtml(html) {
166
194
  }
167
195
  export {
168
196
  addTriggerAttribute,
197
+ assertForeignChildrenNotOpaque,
169
198
  decodeHtmlEntities,
170
199
  inspectUnresolvedMarkerArtifactHtml,
171
200
  isMarkupNodeLike,
201
+ isOpaqueForeignChildValue,
172
202
  isThenable,
173
203
  normalizeUnresolvedMarkerArtifactHtml,
174
204
  wrapWithScriptsInjector
@@ -8,7 +8,6 @@ export type RouteHtmlFinalizationContext<C> = {
8
8
  renderOptions: IntegrationRendererRenderOptions<C>;
9
9
  partial: boolean;
10
10
  }) => HtmlDocumentContribution[] | undefined;
11
- applyAttributesToFirstBodyElement: (html: string, attributes: Record<string, string>) => string;
12
11
  applyAttributesToHtmlElement: (html: string, attributes: Record<string, string>) => string;
13
12
  };
14
13
  /**
@@ -1,23 +1,18 @@
1
1
  function buildRouteHtmlFinalization(context) {
2
2
  const { renderOptions } = context;
3
- const componentRootAttributes = renderOptions.componentRender?.canAttachAttributes && renderOptions.componentRender.rootAttributes && Object.keys(renderOptions.componentRender.rootAttributes).length > 0 ? renderOptions.componentRender.rootAttributes : void 0;
4
3
  const documentAttributes = context.getDocumentAttributes(renderOptions);
5
4
  const htmlContributions = context.getHtmlDocumentContributions({ renderOptions, partial: false });
6
- const hasStructuralFinalization = componentRootAttributes && Object.keys(componentRootAttributes).length > 0 || documentAttributes && Object.keys(documentAttributes).length > 0;
5
+ const hasStructuralFinalization = documentAttributes && Object.keys(documentAttributes).length > 0;
7
6
  if (!hasStructuralFinalization && (!htmlContributions || htmlContributions.length === 0)) {
8
7
  return {};
9
8
  }
10
9
  return {
11
10
  htmlContributions,
12
11
  finalizeHtml: (html) => {
13
- let renderedHtml = html;
14
- if (componentRootAttributes) {
15
- renderedHtml = context.applyAttributesToFirstBodyElement(renderedHtml, componentRootAttributes);
12
+ if (!documentAttributes || Object.keys(documentAttributes).length === 0) {
13
+ return html;
16
14
  }
17
- if (documentAttributes) {
18
- renderedHtml = context.applyAttributesToHtmlElement(renderedHtml, documentAttributes);
19
- }
20
- return renderedHtml;
15
+ return context.applyAttributesToHtmlElement(html, documentAttributes);
21
16
  }
22
17
  };
23
18
  }
@@ -1,5 +1,5 @@
1
1
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
- import type { ComponentRenderResult, EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphResult, PageMetadataProps, EcoPageLayoutEntry, RouteRendererOptions } from '../../types/public-types.js';
2
+ import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphResult, PageMetadataProps, EcoPageLayoutEntry, RouteRendererOptions } from '../../types/public-types.js';
3
3
  import { type ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
4
4
  type PreparedRenderInputs = {
5
5
  Page: EcoPageFile['default'] | EcoPageComponent<any>;
@@ -20,7 +20,6 @@ export declare function buildPreparedRenderOptions<C = unknown>(input: {
20
20
  resolvedDependencies: ProcessedAsset[];
21
21
  allDependencies: ProcessedAsset[];
22
22
  pageBrowserGraph?: PageBrowserGraphResult;
23
- componentRender?: ComponentRenderResult;
24
23
  appConfig: EcoPagesAppConfig;
25
24
  }): IntegrationRendererRenderOptions<C>;
26
25
  export {};
@@ -2,15 +2,7 @@ import { createPagePackage } from "../../services/assets/asset-processing-servic
2
2
  import { dedupeProcessedAssets } from "./processed-asset-dedupe.js";
3
3
  import { createPageLocalsProxy } from "./route-prepared-options.utils.js";
4
4
  function buildPreparedRenderOptions(input) {
5
- const {
6
- routeOptions,
7
- resolvedInputs,
8
- resolvedDependencies,
9
- allDependencies,
10
- pageBrowserGraph,
11
- componentRender,
12
- appConfig
13
- } = input;
5
+ const { routeOptions, resolvedInputs, resolvedDependencies, allDependencies, pageBrowserGraph, appConfig } = input;
14
6
  const { Page, HtmlTemplate, Layouts, Layout, layoutEntries, props, metadata, integrationSpecificProps } = resolvedInputs;
15
7
  const dedupedDependencies = dedupeProcessedAssets(allDependencies);
16
8
  const pagePackage = createPagePackage(dedupedDependencies, { pageBrowserGraph });
@@ -29,7 +21,6 @@ function buildPreparedRenderOptions(input) {
29
21
  ...routeOptions,
30
22
  resolvedDependencies,
31
23
  pagePackage,
32
- componentRender,
33
24
  HtmlTemplate,
34
25
  Layouts,
35
26
  Layout,
@@ -1,5 +1,5 @@
1
1
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
- import type { ComponentRenderResult, EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult, EcoPageLayoutEntry } from '../../types/public-types.js';
2
+ import type { EcoComponent, EcoPageComponent, EcoPageFile, HtmlTemplateProps, IntegrationRendererRenderOptions, PageBrowserGraphContribution, PageBrowserGraphResult, PageMetadataProps, RouteRendererBody, RouteRendererOptions, RouteRenderResult, EcoPageLayoutEntry } from '../../types/public-types.js';
3
3
  import type { AssetProcessingService, ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
4
4
  import type { HtmlDocumentContribution } from '../../services/html/html-transformer.service.js';
5
5
  import { OwnershipValidationService } from './ownership-validation.service.js';
@@ -46,15 +46,6 @@ export interface RouteRenderOrchestratorAdapter<C> {
46
46
  * Collects declarative Page Browser Graph requirements for one route.
47
47
  */
48
48
  collectPageBrowserGraphContribution(routeFile: string): Promise<PageBrowserGraphContribution | undefined>;
49
- /**
50
- * Resolves the optional page-root render through the foreign-child-aware component contract.
51
- */
52
- resolveRoutePageComponentRender(input: {
53
- Page: EcoComponent;
54
- Layout?: EcoComponent;
55
- props: Record<string, unknown>;
56
- routeOptions: RouteRendererOptions;
57
- }): Promise<ComponentRenderResult | undefined>;
58
49
  /**
59
50
  * Executes the Integration-specific route render.
60
51
  */
@@ -100,8 +91,8 @@ export declare class RouteRenderOrchestrator {
100
91
  * Builds normalized route render options before the integration render runs.
101
92
  *
102
93
  * This preparation step validates route-root ownership, resolves page data,
103
- * collects processed assets, captures optional page-root render metadata, and
104
- * produces the page package consumed by downstream HTML transformation.
94
+ * collects processed assets, and produces the page package consumed by downstream
95
+ * HTML transformation.
105
96
  */
106
97
  prepareRenderOptions<C = unknown>(routeOptions: RouteRendererOptions, adapter: RouteRenderOrchestratorAdapter<C>): Promise<IntegrationRendererRenderOptions<C>>;
107
98
  resolveDeclaredPageBrowserGraph(input: {
@@ -23,12 +23,12 @@ class RouteRenderOrchestrator {
23
23
  * Builds normalized route render options before the integration render runs.
24
24
  *
25
25
  * This preparation step validates route-root ownership, resolves page data,
26
- * collects processed assets, captures optional page-root render metadata, and
27
- * produces the page package consumed by downstream HTML transformation.
26
+ * collects processed assets, and produces the page package consumed by downstream
27
+ * HTML transformation.
28
28
  */
29
29
  async prepareRenderOptions(routeOptions, adapter) {
30
30
  const resolvedInputs = await adapter.resolveRouteRenderInputs(routeOptions);
31
- const { Page, HtmlTemplate, Layouts, Layout } = resolvedInputs;
31
+ const { Page, HtmlTemplate, Layouts } = resolvedInputs;
32
32
  const validationErrors = this.ownershipValidationService.validate({
33
33
  currentIntegrationName: adapter.name,
34
34
  roots: [
@@ -39,7 +39,7 @@ class RouteRenderOrchestrator {
39
39
  });
40
40
  throwIfOwnershipInvalid(validationErrors);
41
41
  const componentsToResolve = [HtmlTemplate, ...Layouts, Page];
42
- const [{ resolvedDependencies }, pageBrowserGraph, componentRender] = await Promise.all([
42
+ const [{ resolvedDependencies }, pageBrowserGraph] = await Promise.all([
43
43
  adapter.resolveRouteDependencies({
44
44
  components: componentsToResolve
45
45
  }),
@@ -47,21 +47,12 @@ class RouteRenderOrchestrator {
47
47
  routeFile: routeOptions.file,
48
48
  integrationName: adapter.name,
49
49
  collectContribution: async (routeFile) => await adapter.collectPageBrowserGraphContribution(routeFile)
50
- }),
51
- adapter.resolveRoutePageComponentRender({
52
- Page,
53
- Layout,
54
- props: resolvedInputs.props,
55
- routeOptions
56
50
  })
57
51
  ]);
58
52
  const allDependencies = [
59
53
  ...resolvedDependencies,
60
54
  ...collectUsedIntegrationDependenciesFromGraph(this.appConfig, componentsToResolve, adapter.name)
61
55
  ];
62
- if (componentRender?.assets?.length) {
63
- allDependencies.push(...componentRender.assets);
64
- }
65
56
  const triggers = collectResolvedLazyTriggersFromGraph(componentsToResolve, adapter.name);
66
57
  const [globalAssets, eagerSsrLazyAssets] = await Promise.all([
67
58
  triggers.length > 0 ? buildGlobalInjectorAssets(this.appConfig, this.assetProcessingService, triggers, adapter.name) : Promise.resolve([]),
@@ -74,7 +65,6 @@ class RouteRenderOrchestrator {
74
65
  resolvedDependencies,
75
66
  allDependencies,
76
67
  pageBrowserGraph,
77
- componentRender,
78
68
  appConfig: this.appConfig
79
69
  });
80
70
  }
@@ -16,7 +16,7 @@ import { materializeContentScriptAsset } from "./materialize-content-script-asse
16
16
  import {
17
17
  getDevBrowserScriptCacheEntry,
18
18
  setDevBrowserScriptCacheEntry
19
- } from "../../../build/dev-browser-script-cache.js";
19
+ } from "../../../build/cache/dev-browser-script-cache.js";
20
20
  import {
21
21
  ContentScriptProcessor,
22
22
  ContentStylesheetProcessor,
@@ -1,4 +1,4 @@
1
- import type { EcoBuildPlugin } from '../../../build/build-types.js';
1
+ import type { EcoBuildPlugin } from '../../../build/contracts/build-types.js';
2
2
  export type AssetSource = 'content' | 'file' | 'node-module';
3
3
  export type AssetKind = 'script' | 'stylesheet';
4
4
  export type AssetPosition = 'head' | 'body';
@@ -1,4 +1,4 @@
1
- import type { EcoBuildPlugin } from '../../../build/build-types.js';
1
+ import type { EcoBuildPlugin } from '../../../build/contracts/build-types.js';
2
2
  import type { AssetDefinition } from './assets.types.js';
3
3
  import { type BrowserRuntimeEntryModuleConfig } from './browser-runtime-entry.factory.js';
4
4
  export declare const BROWSER_RUNTIME_SCRIPT_ATTRIBUTES: {
@@ -1,5 +1,4 @@
1
1
  import type { EcoPagesAppConfig } from '../../../../../types/internal-types.js';
2
- import type { EcoBuildPlugin } from '../../../../../build/build-types.js';
3
2
  import type { ScriptAsset } from '../../assets.types.js';
4
3
  import { BaseProcessor } from './base-processor.js';
5
4
  import { type BrowserBundleGroupedEntry } from '../../../browser-bundle.service.js';
@@ -10,7 +9,6 @@ export declare abstract class BaseScriptProcessor<T extends ScriptAsset> extends
10
9
  });
11
10
  protected shouldBundle(dep: T): boolean;
12
11
  protected getBundlerOptions(dep: T): Record<string, any>;
13
- protected collectBuildPlugins(excludePluginNames?: string[]): EcoBuildPlugin[];
14
12
  protected bundleScript({ entrypoint, outdir, excludeAppBuildPlugins, plugins: additionalPlugins, ...rest }: {
15
13
  entrypoint: string;
16
14
  outdir: string;
@@ -1,5 +1,4 @@
1
1
  import { appLogger } from "../../../../../global/app-logger.js";
2
- import { getAppBrowserBuildPlugins } from "../../../../../build/build-adapter.js";
3
2
  import { fileSystem } from "@ecopages/file-system";
4
3
  import path from "node:path";
5
4
  import { BaseProcessor } from "./base-processor.js";
@@ -22,14 +21,6 @@ class BaseScriptProcessor extends BaseProcessor {
22
21
  }
23
22
  return dep.bundleOptions || {};
24
23
  }
25
- collectBuildPlugins(excludePluginNames) {
26
- const buildPlugins = getAppBrowserBuildPlugins(this.appConfig);
27
- if (!excludePluginNames || excludePluginNames.length === 0) {
28
- return buildPlugins;
29
- }
30
- const excluded = new Set(excludePluginNames);
31
- return buildPlugins.filter((plugin) => !excluded.has(plugin.name));
32
- }
33
24
  async bundleScript({
34
25
  entrypoint,
35
26
  outdir,
@@ -37,8 +28,6 @@ class BaseScriptProcessor extends BaseProcessor {
37
28
  plugins: additionalPlugins,
38
29
  ...rest
39
30
  }) {
40
- const buildPlugins = this.collectBuildPlugins(excludeAppBuildPlugins);
41
- const allPlugins = additionalPlugins ? [...additionalPlugins, ...buildPlugins] : buildPlugins;
42
31
  const buildResult = await this.browserBundleService.bundle({
43
32
  profile: "browser-script",
44
33
  entrypoints: [entrypoint],
@@ -47,7 +36,7 @@ class BaseScriptProcessor extends BaseProcessor {
47
36
  excludeAppBuildPlugins,
48
37
  splitting: true,
49
38
  naming: "[name]-[hash].[ext]",
50
- plugins: allPlugins,
39
+ plugins: additionalPlugins,
51
40
  ...rest
52
41
  });
53
42
  if (!buildResult.success) {
@@ -86,8 +75,6 @@ class BaseScriptProcessor extends BaseProcessor {
86
75
  plugins: additionalPlugins,
87
76
  ...rest
88
77
  }) {
89
- const buildPlugins = this.collectBuildPlugins(excludeAppBuildPlugins);
90
- const allPlugins = additionalPlugins ? [...additionalPlugins, ...buildPlugins] : buildPlugins;
91
78
  const buildResult = await this.browserBundleService.bundleGroupedEntries(entries, {
92
79
  profile: "browser-script",
93
80
  outdir,
@@ -96,7 +83,7 @@ class BaseScriptProcessor extends BaseProcessor {
96
83
  ...rest,
97
84
  splitting: true,
98
85
  naming: "[name]-[hash].[ext]",
99
- plugins: allPlugins
86
+ plugins: additionalPlugins
100
87
  });
101
88
  if (!buildResult.success) {
102
89
  for (const log of buildResult.logs) {
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import { resolveInlineContentScriptBody } from "../../inline-content-script-body.js";
4
- import { shouldUseDevBrowserScriptCache } from "../../../../../build/dev-browser-script-cache.js";
4
+ import { shouldUseDevBrowserScriptCache } from "../../../../../build/cache/dev-browser-script-cache.js";
5
5
  import { BaseScriptProcessor } from "../base/base-script-processor.js";
6
6
  class ContentScriptProcessor extends BaseScriptProcessor {
7
7
  getContentScriptEntryDir() {
@@ -1,6 +1,24 @@
1
- import type { BuildResult, BuildTranspileProfile } from '../../build/build-adapter.js';
2
- import type { EcoBuildPlugin } from '../../build/build-types.js';
1
+ import type { BuildResult } from '../../build/build-adapter.js';
2
+ import type { BuildTranspileProfile } from '../../build/build-adapter.js';
3
+ import type { EcoBuildPlugin } from '../../build/contracts/build-types.js';
3
4
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
5
+ /**
6
+ * Browser-oriented build request accepted by {@link BrowserBundleService}.
7
+ *
8
+ * @remarks
9
+ * `profile` is a {@link BuildTranspileProfile} (transpile settings only).
10
+ * `executor` selects the {@link BuildRuntime} concurrency slot:
11
+ * - `'hmr'` + (`hmr-entrypoint` | `hmr-runtime`) → `'browser-hmr'`
12
+ * - everything else (including `browser-script`, and `'build'`) → `'route-module'`
13
+ *
14
+ * Defaults `executor` to `'hmr'`. Request options are always assembled with
15
+ * browser-hmr *defaults* via {@link createBrowserBuildRequest}; only the
16
+ * concurrency/dedupe wrapper differs.
17
+ *
18
+ * Uses request-scope {@link requestBuildDedupe} plus in-flight
19
+ * {@link DedupingBuildExecutor} coalescing; both key on
20
+ * {@link createBuildRequestIdentity}.
21
+ */
4
22
  export type BrowserBundleOptions = {
5
23
  entrypoints: string[] | Record<string, string>;
6
24
  outdir?: string;
@@ -17,28 +35,10 @@ export type BrowserBundleOptions = {
17
35
  external?: string[];
18
36
  plugins?: EcoBuildPlugin[];
19
37
  executor?: 'build' | 'hmr';
20
- [key: string]: unknown;
21
- profile: BuildTranspileProfile;
22
- excludeAppBuildPlugins?: string[];
23
- };
24
- type BrowserBundleGroupedOptions = {
25
- outdir?: string;
26
- outbase?: string;
27
- naming?: string;
28
- conditions?: string[];
29
- define?: Record<string, string>;
30
- minify?: boolean;
31
- treeshaking?: boolean;
32
- splitting?: boolean;
33
- root?: string;
34
- bundle?: boolean;
35
- externalPackages?: boolean;
36
- external?: string[];
37
- plugins?: EcoBuildPlugin[];
38
- [key: string]: unknown;
39
38
  profile: BuildTranspileProfile;
40
39
  excludeAppBuildPlugins?: string[];
41
40
  };
41
+ type BrowserBundleGroupedOptions = Omit<BrowserBundleOptions, 'entrypoints'>;
42
42
  export interface BrowserBundleExecutor {
43
43
  bundle(options: BrowserBundleOptions): Promise<BuildResult>;
44
44
  }
@@ -50,24 +50,20 @@ export type BrowserBundleGroupedEntry = {
50
50
  * App-owned boundary for browser-oriented bundle work.
51
51
  *
52
52
  * @remarks
53
- * This service owns the shared browser transpile defaults and ensures browser
54
- * builds always run through the app-owned executor rather than direct backend
55
- * calls scattered across HMR and asset processing paths.
53
+ * Owns shared browser transpile defaults and ensures browser builds run through
54
+ * the app-owned executor rather than direct backend calls. Assembles a complete
55
+ * request via {@link createBrowserBuildRequest} before scheduling.
56
56
  */
57
57
  export declare class BrowserBundleService implements BrowserBundleExecutor {
58
58
  private readonly appConfig;
59
- /**
60
- * Creates the browser bundle boundary for one finalized app instance.
61
- */
62
59
  constructor(appConfig: EcoPagesAppConfig);
63
60
  /**
64
61
  * Runs one browser-targeted build through the app-owned executor.
65
62
  *
66
63
  * @remarks
67
- * Browser defaults and app-owned browser build plugins are applied here so HMR
68
- * and runtime asset generation do not have to recreate that policy at each call
69
- * site. Also forwards {@link getAppSourceTransforms | app source transforms} so
70
- * metadata injection runs after boundary/runtime `onLoad` rewrites.
64
+ * Browser defaults, app-owned browser plugins, and
65
+ * {@link getAppSourceTransforms | app source transforms} are applied here
66
+ * so HMR and asset generation do not recreate that policy at each call site.
71
67
  */
72
68
  bundle(options: BrowserBundleOptions): Promise<BuildResult>;
73
69
  bundleGroupedEntries(entries: BrowserBundleGroupedEntry[], options: BrowserBundleGroupedOptions): Promise<BuildResult>;