@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
@@ -0,0 +1,106 @@
1
+ import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
+ import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../types/public-types.js';
3
+ import type { ProcessedAsset } from '../../services/assets/asset-processing-service/assets.types.js';
4
+ export type GraphPolicy = 'development' | 'production';
5
+ export type GraphKey = {
6
+ integrationName: string;
7
+ routeFile: string;
8
+ entryFingerprint: string;
9
+ policy: GraphPolicy;
10
+ };
11
+ export type GraphRecord = {
12
+ key: GraphKey;
13
+ result: PageBrowserGraphResult;
14
+ dependencyPaths: ReadonlySet<string>;
15
+ committedAt: number;
16
+ generation: number;
17
+ };
18
+ export type GraphBuildResult = {
19
+ result: PageBrowserGraphResult;
20
+ dependencyPaths: ReadonlySet<string>;
21
+ };
22
+ type GroupedGraphRecord = {
23
+ assetsByRoute: Map<string, ProcessedAsset[]>;
24
+ dependencyPaths: ReadonlySet<string>;
25
+ generation: number;
26
+ };
27
+ export type GroupedGraphBuildOutcome = GroupedGraphRecord | {
28
+ skipCache: true;
29
+ assetsByRoute: Map<string, ProcessedAsset[]>;
30
+ dependencyPaths: ReadonlySet<string>;
31
+ };
32
+ export type AffectedGraphIdentity = {
33
+ integrationName: string;
34
+ routeFile: string;
35
+ policy: GraphPolicy;
36
+ entryFingerprint: string;
37
+ };
38
+ /**
39
+ * Reverse index from dependency paths to graph cache keys.
40
+ */
41
+ export declare class GraphDependencyIndex {
42
+ private readonly dependencyToGraphKeys;
43
+ bindRecord(record: GraphRecord): void;
44
+ unbindRecord(record: GraphRecord): void;
45
+ bindGroupedRecord(integrationName: string, record: GroupedGraphRecord): void;
46
+ unbindGroupedRecord(integrationName: string, record: GroupedGraphRecord): void;
47
+ getAffectedKeys(filePath: string): Set<string>;
48
+ resetForTests(): void;
49
+ private addDependencyBinding;
50
+ private removeDependencyBinding;
51
+ }
52
+ /**
53
+ * Session-local Page Browser Graph cache with precise dependency invalidation.
54
+ */
55
+ export declare class SessionPageBrowserGraphCache {
56
+ private readonly records;
57
+ private readonly inFlight;
58
+ private readonly groupedRecords;
59
+ private readonly groupedInFlight;
60
+ private readonly dependencyIndex;
61
+ private readonly graphGenerations;
62
+ private readonly groupedGenerations;
63
+ private buildCount;
64
+ getGraphByRoute(integrationName: string, routeFile: string, policy: GraphPolicy): PageBrowserGraphResult | undefined;
65
+ getBuildCount(): number;
66
+ getAffectedGraphIdentities(filePath: string): AffectedGraphIdentity[];
67
+ resolveGraph(key: GraphKey, build: () => Promise<GraphBuildResult | undefined>): Promise<PageBrowserGraphResult | undefined>;
68
+ resolveGroupedGraph(integrationName: string, build: () => Promise<GroupedGraphBuildOutcome | undefined>): Promise<Map<string, ProcessedAsset[]>>;
69
+ peekGroupedGraph(integrationName: string): Map<string, ProcessedAsset[]> | undefined;
70
+ invalidateByFilePath(filePath: string): number;
71
+ invalidateRoute(integrationName: string, routeFile: string): void;
72
+ invalidateByRouteFile(routeFile: string): void;
73
+ resetForTests(): void;
74
+ exportRecords(policy: GraphPolicy): GraphRecord[];
75
+ clearPolicyRecords(policy: GraphPolicy): void;
76
+ private pruneStaleRouteRecords;
77
+ private bumpGraphGeneration;
78
+ private bumpGroupedGeneration;
79
+ private invalidateSerializedGraphKey;
80
+ private invalidateGroupedRecord;
81
+ private runGraphBuild;
82
+ private runGroupedBuild;
83
+ private canCommitGraphBuild;
84
+ private canCommitGroupedBuild;
85
+ }
86
+ /**
87
+ * Returns the session graph cache for one app instance.
88
+ */
89
+ export declare function getAppPageBrowserGraphSession(appConfig: EcoPagesAppConfig): SessionPageBrowserGraphCache;
90
+ /**
91
+ * Invalidates graph records that depend on the changed file path.
92
+ */
93
+ export declare function invalidatePageBrowserGraphSession(appConfig: EcoPagesAppConfig, filePath: string): number;
94
+ /**
95
+ * Returns graph identities affected by a source file change.
96
+ */
97
+ export declare function getAffectedPageBrowserGraphIdentities(appConfig: EcoPagesAppConfig, filePath: string): AffectedGraphIdentity[];
98
+ /**
99
+ * Builds a stable fingerprint from declared browser-entry dependencies and assets.
100
+ */
101
+ export declare function createPageBrowserGraphEntryFingerprint(contribution: PageBrowserGraphContribution): string;
102
+ /**
103
+ * Collects dependency paths used for graph invalidation.
104
+ */
105
+ export declare function collectPageBrowserGraphDependencyPaths(routeFile: string, contribution: PageBrowserGraphContribution, processedAssets: ProcessedAsset[]): ReadonlySet<string>;
106
+ export {};
@@ -0,0 +1,436 @@
1
+ import path from "node:path";
2
+ import { createHash } from "node:crypto";
3
+ import { appLogger } from "../../global/app-logger.js";
4
+ function isSkipCacheGroupedOutcome(outcome) {
5
+ return "skipCache" in outcome;
6
+ }
7
+ function serializeGraphKey(key) {
8
+ return `${key.policy}::${key.integrationName}::${key.routeFile}::${key.entryFingerprint}`;
9
+ }
10
+ function normalizeDependencyPath(filePath) {
11
+ return path.resolve(filePath);
12
+ }
13
+ function parseSerializedGraphKey(serializedKey) {
14
+ if (serializedKey.startsWith("grouped::")) {
15
+ return void 0;
16
+ }
17
+ const [policy, integrationName, routeFile, entryFingerprint] = serializedKey.split("::");
18
+ if (!policy || !integrationName || !routeFile || entryFingerprint === void 0) {
19
+ return void 0;
20
+ }
21
+ if (policy !== "development" && policy !== "production") {
22
+ return void 0;
23
+ }
24
+ return {
25
+ policy,
26
+ integrationName,
27
+ routeFile,
28
+ entryFingerprint
29
+ };
30
+ }
31
+ class GraphDependencyIndex {
32
+ dependencyToGraphKeys = /* @__PURE__ */ new Map();
33
+ bindRecord(record) {
34
+ const serializedKey = serializeGraphKey(record.key);
35
+ for (const dependencyPath of record.dependencyPaths) {
36
+ this.addDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
37
+ }
38
+ }
39
+ unbindRecord(record) {
40
+ const serializedKey = serializeGraphKey(record.key);
41
+ for (const dependencyPath of record.dependencyPaths) {
42
+ this.removeDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
43
+ }
44
+ }
45
+ bindGroupedRecord(integrationName, record) {
46
+ const serializedKey = `grouped::${integrationName}`;
47
+ for (const dependencyPath of record.dependencyPaths) {
48
+ this.addDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
49
+ }
50
+ }
51
+ unbindGroupedRecord(integrationName, record) {
52
+ const serializedKey = `grouped::${integrationName}`;
53
+ for (const dependencyPath of record.dependencyPaths) {
54
+ this.removeDependencyBinding(normalizeDependencyPath(dependencyPath), serializedKey);
55
+ }
56
+ }
57
+ getAffectedKeys(filePath) {
58
+ return new Set(this.dependencyToGraphKeys.get(normalizeDependencyPath(filePath)) ?? []);
59
+ }
60
+ resetForTests() {
61
+ this.dependencyToGraphKeys.clear();
62
+ }
63
+ addDependencyBinding(dependencyPath, serializedKey) {
64
+ const graphKeys = this.dependencyToGraphKeys.get(dependencyPath) ?? /* @__PURE__ */ new Set();
65
+ graphKeys.add(serializedKey);
66
+ this.dependencyToGraphKeys.set(dependencyPath, graphKeys);
67
+ }
68
+ removeDependencyBinding(dependencyPath, serializedKey) {
69
+ const graphKeys = this.dependencyToGraphKeys.get(dependencyPath);
70
+ if (!graphKeys) {
71
+ return;
72
+ }
73
+ graphKeys.delete(serializedKey);
74
+ if (graphKeys.size === 0) {
75
+ this.dependencyToGraphKeys.delete(dependencyPath);
76
+ }
77
+ }
78
+ }
79
+ class SessionPageBrowserGraphCache {
80
+ records = /* @__PURE__ */ new Map();
81
+ inFlight = /* @__PURE__ */ new Map();
82
+ groupedRecords = /* @__PURE__ */ new Map();
83
+ groupedInFlight = /* @__PURE__ */ new Map();
84
+ dependencyIndex = new GraphDependencyIndex();
85
+ graphGenerations = /* @__PURE__ */ new Map();
86
+ groupedGenerations = /* @__PURE__ */ new Map();
87
+ buildCount = 0;
88
+ getGraphByRoute(integrationName, routeFile, policy) {
89
+ const normalizedRoute = normalizeDependencyPath(routeFile);
90
+ for (const record of this.records.values()) {
91
+ if (record.key.integrationName !== integrationName) {
92
+ continue;
93
+ }
94
+ if (record.key.policy !== policy) {
95
+ continue;
96
+ }
97
+ if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
98
+ continue;
99
+ }
100
+ return record.result;
101
+ }
102
+ return void 0;
103
+ }
104
+ getBuildCount() {
105
+ return this.buildCount;
106
+ }
107
+ getAffectedGraphIdentities(filePath) {
108
+ const identities = [];
109
+ for (const serializedKey of this.dependencyIndex.getAffectedKeys(filePath)) {
110
+ const identity = parseSerializedGraphKey(serializedKey);
111
+ if (identity) {
112
+ identities.push(identity);
113
+ }
114
+ }
115
+ return identities;
116
+ }
117
+ resolveGraph(key, build) {
118
+ const serializedKey = serializeGraphKey(key);
119
+ const cached = this.records.get(serializedKey);
120
+ if (cached) {
121
+ return Promise.resolve(cached.result);
122
+ }
123
+ const pending = this.inFlight.get(serializedKey);
124
+ if (pending) {
125
+ return pending;
126
+ }
127
+ const buildGeneration = this.bumpGraphGeneration(serializedKey);
128
+ const buildPromise = this.runGraphBuild(serializedKey, key, buildGeneration, build);
129
+ this.inFlight.set(serializedKey, buildPromise);
130
+ return buildPromise;
131
+ }
132
+ resolveGroupedGraph(integrationName, build) {
133
+ const cached = this.groupedRecords.get(integrationName);
134
+ if (cached) {
135
+ return Promise.resolve(cached.assetsByRoute);
136
+ }
137
+ const pending = this.groupedInFlight.get(integrationName);
138
+ if (pending) {
139
+ return pending;
140
+ }
141
+ const buildGeneration = this.bumpGroupedGeneration(integrationName);
142
+ const buildPromise = this.runGroupedBuild(integrationName, buildGeneration, build);
143
+ this.groupedInFlight.set(integrationName, buildPromise);
144
+ return buildPromise;
145
+ }
146
+ peekGroupedGraph(integrationName) {
147
+ return this.groupedRecords.get(integrationName)?.assetsByRoute;
148
+ }
149
+ invalidateByFilePath(filePath) {
150
+ const normalizedFilePath = normalizeDependencyPath(filePath);
151
+ const affectedKeys = this.dependencyIndex.getAffectedKeys(normalizedFilePath);
152
+ let invalidated = 0;
153
+ for (const serializedKey of affectedKeys) {
154
+ if (serializedKey.startsWith("grouped::")) {
155
+ const integrationName = serializedKey.slice("grouped::".length);
156
+ invalidated += this.invalidateGroupedRecord(integrationName);
157
+ continue;
158
+ }
159
+ invalidated += this.invalidateSerializedGraphKey(serializedKey);
160
+ }
161
+ for (const serializedKey of this.inFlight.keys()) {
162
+ const identity = parseSerializedGraphKey(serializedKey);
163
+ if (!identity) {
164
+ continue;
165
+ }
166
+ if (normalizeDependencyPath(identity.routeFile) !== normalizedFilePath) {
167
+ continue;
168
+ }
169
+ if (affectedKeys.has(serializedKey)) {
170
+ continue;
171
+ }
172
+ this.bumpGraphGeneration(serializedKey);
173
+ this.inFlight.delete(serializedKey);
174
+ invalidated += 1;
175
+ }
176
+ if (invalidated > 0) {
177
+ appLogger.debug(
178
+ `[PageBrowserGraphSession] invalidated ${invalidated} graph record(s) for ${normalizedFilePath}`
179
+ );
180
+ }
181
+ return invalidated;
182
+ }
183
+ invalidateRoute(integrationName, routeFile) {
184
+ const normalizedRoute = normalizeDependencyPath(routeFile);
185
+ for (const [serializedKey, record] of this.records) {
186
+ if (record.key.integrationName !== integrationName) {
187
+ continue;
188
+ }
189
+ if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
190
+ continue;
191
+ }
192
+ this.invalidateSerializedGraphKey(serializedKey);
193
+ }
194
+ }
195
+ invalidateByRouteFile(routeFile) {
196
+ const normalizedRoute = normalizeDependencyPath(routeFile);
197
+ for (const [serializedKey, record] of this.records) {
198
+ if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
199
+ continue;
200
+ }
201
+ this.invalidateSerializedGraphKey(serializedKey);
202
+ }
203
+ }
204
+ resetForTests() {
205
+ this.records.clear();
206
+ this.inFlight.clear();
207
+ this.groupedRecords.clear();
208
+ this.groupedInFlight.clear();
209
+ this.graphGenerations.clear();
210
+ this.groupedGenerations.clear();
211
+ this.dependencyIndex.resetForTests();
212
+ this.buildCount = 0;
213
+ }
214
+ exportRecords(policy) {
215
+ return [...this.records.values()].filter((record) => record.key.policy === policy);
216
+ }
217
+ clearPolicyRecords(policy) {
218
+ for (const [serializedKey, record] of this.records) {
219
+ if (record.key.policy !== policy) {
220
+ continue;
221
+ }
222
+ this.dependencyIndex.unbindRecord(record);
223
+ this.records.delete(serializedKey);
224
+ this.inFlight.delete(serializedKey);
225
+ this.graphGenerations.delete(serializedKey);
226
+ }
227
+ if (policy === "production") {
228
+ for (const integrationName of [...this.groupedRecords.keys()]) {
229
+ const groupedRecord = this.groupedRecords.get(integrationName);
230
+ if (groupedRecord) {
231
+ this.dependencyIndex.unbindGroupedRecord(integrationName, groupedRecord);
232
+ }
233
+ this.groupedRecords.delete(integrationName);
234
+ this.groupedInFlight.delete(integrationName);
235
+ this.groupedGenerations.delete(integrationName);
236
+ }
237
+ }
238
+ }
239
+ pruneStaleRouteRecords(key) {
240
+ const normalizedRoute = normalizeDependencyPath(key.routeFile);
241
+ for (const [serializedKey, record] of this.records) {
242
+ if (record.key.integrationName !== key.integrationName) {
243
+ continue;
244
+ }
245
+ if (normalizeDependencyPath(record.key.routeFile) !== normalizedRoute) {
246
+ continue;
247
+ }
248
+ if (record.key.entryFingerprint === key.entryFingerprint && record.key.policy === key.policy) {
249
+ continue;
250
+ }
251
+ this.invalidateSerializedGraphKey(serializedKey);
252
+ }
253
+ }
254
+ bumpGraphGeneration(serializedKey) {
255
+ const next = (this.graphGenerations.get(serializedKey) ?? 0) + 1;
256
+ this.graphGenerations.set(serializedKey, next);
257
+ return next;
258
+ }
259
+ bumpGroupedGeneration(integrationName) {
260
+ const next = (this.groupedGenerations.get(integrationName) ?? 0) + 1;
261
+ this.groupedGenerations.set(integrationName, next);
262
+ return next;
263
+ }
264
+ invalidateSerializedGraphKey(serializedKey) {
265
+ const record = this.records.get(serializedKey);
266
+ if (record) {
267
+ this.dependencyIndex.unbindRecord(record);
268
+ this.records.delete(serializedKey);
269
+ }
270
+ this.bumpGraphGeneration(serializedKey);
271
+ this.inFlight.delete(serializedKey);
272
+ return record ? 1 : 0;
273
+ }
274
+ invalidateGroupedRecord(integrationName) {
275
+ const groupedRecord = this.groupedRecords.get(integrationName);
276
+ if (groupedRecord) {
277
+ this.dependencyIndex.unbindGroupedRecord(integrationName, groupedRecord);
278
+ this.groupedRecords.delete(integrationName);
279
+ }
280
+ this.bumpGroupedGeneration(integrationName);
281
+ this.groupedInFlight.delete(integrationName);
282
+ return groupedRecord ? 1 : 0;
283
+ }
284
+ async runGraphBuild(serializedKey, key, buildGeneration, build) {
285
+ try {
286
+ this.buildCount += 1;
287
+ const buildResult = await build();
288
+ if (!buildResult) {
289
+ return void 0;
290
+ }
291
+ if (!this.canCommitGraphBuild(serializedKey, buildGeneration)) {
292
+ const existing = this.records.get(serializedKey);
293
+ return existing?.result;
294
+ }
295
+ this.pruneStaleRouteRecords(key);
296
+ const previous = this.records.get(serializedKey);
297
+ if (previous) {
298
+ this.dependencyIndex.unbindRecord(previous);
299
+ }
300
+ const record = {
301
+ key,
302
+ result: buildResult.result,
303
+ dependencyPaths: buildResult.dependencyPaths,
304
+ committedAt: Date.now(),
305
+ generation: buildGeneration
306
+ };
307
+ this.records.set(serializedKey, record);
308
+ this.dependencyIndex.bindRecord(record);
309
+ return record.result;
310
+ } catch (error) {
311
+ return Promise.reject(error);
312
+ } finally {
313
+ this.inFlight.delete(serializedKey);
314
+ }
315
+ }
316
+ async runGroupedBuild(integrationName, buildGeneration, build) {
317
+ try {
318
+ this.buildCount += 1;
319
+ const buildResult = await build();
320
+ if (!buildResult) {
321
+ return /* @__PURE__ */ new Map();
322
+ }
323
+ if (isSkipCacheGroupedOutcome(buildResult)) {
324
+ return buildResult.assetsByRoute;
325
+ }
326
+ if (!this.canCommitGroupedBuild(integrationName, buildGeneration)) {
327
+ const existing = this.groupedRecords.get(integrationName);
328
+ return existing?.assetsByRoute ?? /* @__PURE__ */ new Map();
329
+ }
330
+ const previous = this.groupedRecords.get(integrationName);
331
+ if (previous) {
332
+ this.dependencyIndex.unbindGroupedRecord(integrationName, previous);
333
+ }
334
+ const record = {
335
+ assetsByRoute: buildResult.assetsByRoute,
336
+ dependencyPaths: buildResult.dependencyPaths,
337
+ generation: buildGeneration
338
+ };
339
+ this.groupedRecords.set(integrationName, record);
340
+ this.dependencyIndex.bindGroupedRecord(integrationName, record);
341
+ return record.assetsByRoute;
342
+ } catch (error) {
343
+ return Promise.reject(error);
344
+ } finally {
345
+ this.groupedInFlight.delete(integrationName);
346
+ }
347
+ }
348
+ canCommitGraphBuild(serializedKey, buildGeneration) {
349
+ return this.graphGenerations.get(serializedKey) === buildGeneration;
350
+ }
351
+ canCommitGroupedBuild(integrationName, buildGeneration) {
352
+ return this.groupedGenerations.get(integrationName) === buildGeneration;
353
+ }
354
+ }
355
+ const sessionByAppConfig = /* @__PURE__ */ new WeakMap();
356
+ function getAppPageBrowserGraphSession(appConfig) {
357
+ const existing = appConfig.runtime?.pageBrowserGraphSession;
358
+ if (existing) {
359
+ return existing;
360
+ }
361
+ const cached = sessionByAppConfig.get(appConfig);
362
+ if (cached) {
363
+ return cached;
364
+ }
365
+ const session = new SessionPageBrowserGraphCache();
366
+ sessionByAppConfig.set(appConfig, session);
367
+ appConfig.runtime = {
368
+ ...appConfig.runtime ?? {},
369
+ pageBrowserGraphSession: session
370
+ };
371
+ return session;
372
+ }
373
+ function invalidatePageBrowserGraphSession(appConfig, filePath) {
374
+ return getAppPageBrowserGraphSession(appConfig).invalidateByFilePath(filePath);
375
+ }
376
+ function getAffectedPageBrowserGraphIdentities(appConfig, filePath) {
377
+ return getAppPageBrowserGraphSession(appConfig).getAffectedGraphIdentities(filePath);
378
+ }
379
+ function createPageBrowserGraphEntryFingerprint(contribution) {
380
+ const descriptors = [
381
+ ...(contribution.dependencies ?? []).map((dependency) => describeAssetDependency(dependency)),
382
+ ...(contribution.assets ?? []).map((asset) => describeProcessedAsset(asset))
383
+ ].sort((left, right) => left.localeCompare(right));
384
+ return descriptors.join("|") || "empty";
385
+ }
386
+ function collectPageBrowserGraphDependencyPaths(routeFile, contribution, processedAssets) {
387
+ const dependencyPaths = /* @__PURE__ */ new Set([normalizeDependencyPath(routeFile)]);
388
+ for (const dependency of contribution.dependencies ?? []) {
389
+ if (dependency.source === "file") {
390
+ dependencyPaths.add(normalizeDependencyPath(dependency.filepath));
391
+ }
392
+ }
393
+ for (const asset of processedAssets) {
394
+ if (asset.sourceFilepath) {
395
+ dependencyPaths.add(normalizeDependencyPath(asset.sourceFilepath));
396
+ }
397
+ for (const bundledSourceFilepath of asset.bundledSourceFilepaths ?? []) {
398
+ dependencyPaths.add(normalizeDependencyPath(bundledSourceFilepath));
399
+ }
400
+ }
401
+ return dependencyPaths;
402
+ }
403
+ function describeProcessedAsset(asset) {
404
+ const filepath = asset.filepath ?? "";
405
+ const sourceFilepath = asset.sourceFilepath ?? "";
406
+ const content = typeof asset.content === "string" ? asset.content : "";
407
+ const contentDigest = content ? createHash("sha1").update(content).digest("hex").slice(0, 12) : "";
408
+ return `asset:${asset.kind}:${filepath}:${sourceFilepath}:${contentDigest}`;
409
+ }
410
+ function describeAssetDependency(dependency) {
411
+ switch (dependency.source) {
412
+ case "file":
413
+ return `file:${dependency.filepath}`;
414
+ case "node-module":
415
+ return `module:${dependency.importPath}`;
416
+ case "content": {
417
+ const content = typeof dependency.content === "string" ? dependency.content : "";
418
+ const contentDigest = createHash("sha1").update(content).digest("hex").slice(0, 12);
419
+ const name = "name" in dependency && typeof dependency.name === "string" ? dependency.name : "";
420
+ return `content:${dependency.kind}:${name}:${contentDigest}`;
421
+ }
422
+ default: {
423
+ const exhaustive = dependency;
424
+ return String(exhaustive);
425
+ }
426
+ }
427
+ }
428
+ export {
429
+ GraphDependencyIndex,
430
+ SessionPageBrowserGraphCache,
431
+ collectPageBrowserGraphDependencyPaths,
432
+ createPageBrowserGraphEntryFingerprint,
433
+ getAffectedPageBrowserGraphIdentities,
434
+ getAppPageBrowserGraphSession,
435
+ invalidatePageBrowserGraphSession
436
+ };
@@ -1,10 +1,12 @@
1
1
  import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
2
  import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../types/public-types.js';
3
3
  import { type AssetProcessingService } from '../../services/assets/asset-processing-service/index.js';
4
+ import { type GraphPolicy } from './page-browser-graph-session.js';
4
5
  export type PageBrowserGraphResolveInput = {
5
6
  routeFile: string;
6
7
  integrationName: string;
7
8
  collectContribution: (routeFile: string) => Promise<PageBrowserGraphContribution | undefined>;
9
+ policy?: GraphPolicy;
8
10
  };
9
11
  /**
10
12
  * Resolves declarative page browser graph assets for one route and integration.
@@ -12,12 +14,16 @@ export type PageBrowserGraphResolveInput = {
12
14
  export declare class PageBrowserGraphService {
13
15
  private readonly appConfig;
14
16
  private readonly assetProcessingService;
15
- private readonly pageBrowserGraphCache;
16
- private readonly groupedPageBrowserGraphCache;
17
17
  constructor(appConfig: EcoPagesAppConfig, assetProcessingService: AssetProcessingService);
18
18
  resolvePageBrowserGraph(input: PageBrowserGraphResolveInput): Promise<PageBrowserGraphResult | undefined>;
19
+ /**
20
+ * Resolves or builds one Page Browser Graph under the given policy.
21
+ */
22
+ getOrBuild(input: PageBrowserGraphResolveInput): Promise<PageBrowserGraphResult | undefined>;
23
+ private resolveGraphPolicy;
19
24
  private isHmrEnabled;
20
- private buildPageBrowserGraph;
25
+ private buildPageBrowserGraphRecord;
26
+ private materializePageBrowserGraph;
21
27
  private resolveGroupedPageBrowserAssets;
22
28
  private buildGroupedPageBrowserAssets;
23
29
  private listIntegrationRouteFiles;