@ecopages/core 0.2.0-beta.26 → 0.2.0-beta.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.
Files changed (177) hide show
  1. package/README.md +1 -1
  2. package/package.json +98 -74
  3. package/src/adapters/bun/server-adapter.js +2 -2
  4. package/src/adapters/node/server-adapter.js +2 -2
  5. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +10 -0
  6. package/src/adapters/shared/hmr-entrypoint-registrar.js +34 -10
  7. package/src/adapters/shared/runtime-server-lifecycle.js +1 -1
  8. package/src/adapters/shared/server-static-builder.d.ts +2 -1
  9. package/src/adapters/shared/server-static-builder.js +16 -14
  10. package/src/adapters/shared/shared-hmr-manager.d.ts +11 -4
  11. package/src/adapters/shared/shared-hmr-manager.js +22 -3
  12. package/src/build/README.md +92 -46
  13. package/src/build/app-build-manifest-runtime.d.ts +28 -2
  14. package/src/build/app-build-manifest-runtime.js +26 -10
  15. package/src/build/{browser-runtime-plugin.d.ts → browser/browser-runtime-plugin.d.ts} +1 -1
  16. package/src/build/{browser-runtime-plugin.js → browser/browser-runtime-plugin.js} +1 -1
  17. package/src/build/{jsx-ownership-plugins.d.ts → browser/jsx-ownership-plugins.d.ts} +2 -2
  18. package/src/build/{jsx-ownership-plugins.js → browser/jsx-ownership-plugins.js} +2 -2
  19. package/src/build/build-adapter.d.ts +18 -21
  20. package/src/build/build-adapter.js +8 -43
  21. package/src/build/{build-input-fingerprint.d.ts → cache/build-input-fingerprint.d.ts} +3 -3
  22. package/src/build/cache/cache-constants.d.ts +4 -0
  23. package/src/build/cache/cache-constants.js +6 -0
  24. package/src/build/cache/cache-keys.d.ts +22 -0
  25. package/src/build/cache/cache-keys.js +63 -0
  26. package/src/build/{dev-browser-script-cache.d.ts → cache/dev-browser-script-cache.d.ts} +2 -2
  27. package/src/build/{dev-browser-script-cache.js → cache/dev-browser-script-cache.js} +4 -4
  28. package/src/build/{pages-unified-graph-build.d.ts → cache/pages-unified-graph-build.d.ts} +1 -1
  29. package/src/build/{pages-unified-graph-build.js → cache/pages-unified-graph-build.js} +18 -26
  30. package/src/build/{production-build-cache.d.ts → cache/production-build-cache.d.ts} +1 -1
  31. package/src/build/{production-build-cache.js → cache/production-build-cache.js} +2 -2
  32. package/src/build/{server-entry-build-cache.d.ts → cache/server-entry-build-cache.d.ts} +3 -4
  33. package/src/build/{server-entry-build-cache.js → cache/server-entry-build-cache.js} +16 -12
  34. package/src/build/{build-contracts.d.ts → contracts/build-contracts.d.ts} +1 -2
  35. package/src/build/contracts/build-manifest.d.ts +90 -0
  36. package/src/build/{build-manifest.js → contracts/build-manifest.js} +2 -2
  37. package/src/build/{rolldown-adapter-helpers.d.ts → rolldown/rolldown-adapter-helpers.d.ts} +20 -3
  38. package/src/build/{rolldown-adapter-helpers.js → rolldown/rolldown-adapter-helpers.js} +26 -6
  39. package/src/build/{rolldown-build-adapter.d.ts → rolldown/rolldown-build-adapter.d.ts} +1 -1
  40. package/src/build/{rolldown-plugin-bridge.d.ts → rolldown/rolldown-plugin-bridge.d.ts} +2 -2
  41. package/src/build/{rolldown-plugin-bridge.js → rolldown/rolldown-plugin-bridge.js} +1 -1
  42. package/src/build/{rolldown-source-transform-pass.d.ts → rolldown/rolldown-source-transform-pass.d.ts} +1 -1
  43. package/src/build/{rolldown-source-transform-pass.js → rolldown/rolldown-source-transform-pass.js} +4 -1
  44. package/src/build/{runtime-build-output-normalizer.js → rolldown/runtime-build-output-normalizer.js} +2 -2
  45. package/src/build/{build-profile-options.d.ts → runtime/build-profile-options.d.ts} +2 -2
  46. package/src/build/runtime/build-request-identity.d.ts +17 -0
  47. package/src/build/runtime/build-request-identity.js +54 -0
  48. package/src/build/runtime/build-request-policy.d.ts +59 -0
  49. package/src/build/runtime/build-request-policy.js +46 -0
  50. package/src/build/{build-runtime.d.ts → runtime/build-runtime.d.ts} +8 -4
  51. package/src/build/{build-runtime.js → runtime/build-runtime.js} +11 -11
  52. package/src/build/{deduping-build-executor.d.ts → runtime/deduping-build-executor.d.ts} +2 -9
  53. package/src/build/runtime/deduping-build-executor.js +34 -0
  54. package/src/build/{parallel-build-executor.d.ts → runtime/parallel-build-executor.d.ts} +1 -1
  55. package/src/build/{serialized-build-executor.d.ts → runtime/serialized-build-executor.d.ts} +1 -1
  56. package/src/config/config-builder.d.ts +1 -1
  57. package/src/diagnostics/startup-trace.d.ts +49 -2
  58. package/src/diagnostics/startup-trace.js +140 -8
  59. package/src/eco/eco.js +2 -2
  60. package/src/eco/eco.utils.d.ts +1 -1
  61. package/src/eco/eco.utils.js +1 -1
  62. package/src/hmr/client/hmr-runtime.js +2 -2
  63. package/src/hmr/hmr-file-change-prep.d.ts +10 -0
  64. package/src/hmr/hmr-file-change-prep.js +17 -0
  65. package/src/plugins/alias-resolver-plugin.d.ts +1 -1
  66. package/src/plugins/eco-component-meta-plugin.d.ts +1 -1
  67. package/src/plugins/foreign-jsx-override-plugin.d.ts +1 -1
  68. package/src/plugins/integration-plugin.d.ts +16 -5
  69. package/src/plugins/integration-plugin.js +11 -1
  70. package/src/plugins/processor.d.ts +36 -8
  71. package/src/plugins/source-transform.d.ts +1 -1
  72. package/src/route-renderer/GRAPH.md +22 -28
  73. package/src/route-renderer/README.md +12 -7
  74. package/src/route-renderer/orchestration/{document-shell-render.service.d.ts → document-shell/document-shell-render.service.d.ts} +19 -2
  75. package/src/route-renderer/orchestration/{document-shell-render.service.js → document-shell/document-shell-render.service.js} +28 -0
  76. package/src/route-renderer/orchestration/{layout-shell-props.service.d.ts → document-shell/layout-shell-props.service.d.ts} +1 -1
  77. package/src/route-renderer/orchestration/{component-render-context.d.ts → foreign-child/component-render-context.d.ts} +5 -5
  78. package/src/route-renderer/orchestration/{component-render-context.js → foreign-child/component-render-context.js} +1 -1
  79. package/src/route-renderer/orchestration/{render-output.utils.d.ts → foreign-child/foreign-child-output.utils.d.ts} +15 -7
  80. package/src/route-renderer/orchestration/{render-output.utils.js → foreign-child/foreign-child-output.utils.js} +31 -26
  81. package/src/route-renderer/orchestration/{foreign-subtree-execution.service.d.ts → foreign-child/foreign-subtree-execution.service.d.ts} +5 -5
  82. package/src/route-renderer/orchestration/{foreign-subtree-execution.service.js → foreign-child/foreign-subtree-execution.service.js} +7 -3
  83. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.d.ts +19 -0
  84. package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +35 -0
  85. package/src/route-renderer/orchestration/integration-renderer.d.ts +14 -97
  86. package/src/route-renderer/orchestration/integration-renderer.js +90 -247
  87. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.d.ts +75 -0
  88. package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +177 -0
  89. package/src/route-renderer/orchestration/{component-graph-collectors.d.ts → ownership-graph/component-graph-collectors.d.ts} +4 -4
  90. package/src/route-renderer/orchestration/{component-graph-collectors.js → ownership-graph/component-graph-collectors.js} +1 -1
  91. package/src/route-renderer/orchestration/{component-graph.d.ts → ownership-graph/component-graph.d.ts} +1 -3
  92. package/src/route-renderer/orchestration/{component-graph.js → ownership-graph/component-graph.js} +0 -2
  93. package/src/route-renderer/orchestration/{ownership-validation.service.d.ts → ownership-graph/ownership-validation.service.d.ts} +2 -2
  94. package/src/route-renderer/orchestration/{ownership-validation.service.js → ownership-graph/ownership-validation.service.js} +3 -4
  95. package/src/route-renderer/orchestration/{global-injector-assets.service.d.ts → page-browser-graph/global-injector-assets.service.d.ts} +3 -3
  96. package/src/route-renderer/orchestration/{global-injector-assets.service.js → page-browser-graph/global-injector-assets.service.js} +2 -2
  97. package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.d.ts → page-browser-graph/page-browser-graph-contribution.loader.d.ts} +1 -1
  98. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.d.ts +12 -0
  99. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +29 -0
  100. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +106 -0
  101. package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +436 -0
  102. package/src/route-renderer/orchestration/{page-browser-graph.service.d.ts → page-browser-graph/page-browser-graph.service.d.ts} +12 -6
  103. package/src/route-renderer/orchestration/{page-browser-graph.service.js → page-browser-graph/page-browser-graph.service.js} +91 -42
  104. package/src/route-renderer/orchestration/{processed-asset-dedupe.d.ts → page-browser-graph/processed-asset-dedupe.d.ts} +1 -1
  105. package/src/route-renderer/orchestration/{integration-route-render-adapter.d.ts → route-pipeline/integration-route-render-adapter.d.ts} +3 -10
  106. package/src/route-renderer/orchestration/{integration-route-render-adapter.js → route-pipeline/integration-route-render-adapter.js} +1 -3
  107. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.d.ts +6 -0
  108. package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +27 -0
  109. package/src/route-renderer/orchestration/{route-html-finalization.service.d.ts → route-pipeline/route-html-finalization.service.d.ts} +2 -3
  110. package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +21 -0
  111. package/src/route-renderer/orchestration/{route-prepared-options.builder.d.ts → route-pipeline/route-prepared-options.builder.d.ts} +3 -4
  112. package/src/route-renderer/orchestration/{route-prepared-options.builder.js → route-pipeline/route-prepared-options.builder.js} +3 -12
  113. package/src/route-renderer/orchestration/{route-prepared-options.utils.js → route-pipeline/route-prepared-options.utils.js} +1 -1
  114. package/src/route-renderer/orchestration/{route-render-orchestrator.d.ts → route-pipeline/route-render-orchestrator.d.ts} +8 -17
  115. package/src/route-renderer/orchestration/{route-render-orchestrator.js → route-pipeline/route-render-orchestrator.js} +12 -19
  116. package/src/route-renderer/orchestration/string-markup-renderer.js +1 -1
  117. package/src/services/assets/asset-processing-service/asset-processing.service.js +1 -1
  118. package/src/services/assets/asset-processing-service/assets.types.d.ts +1 -1
  119. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -1
  120. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.d.ts +0 -2
  121. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +2 -15
  122. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +1 -1
  123. package/src/services/assets/browser-bundle.service.d.ts +27 -31
  124. package/src/services/assets/browser-bundle.service.js +12 -23
  125. package/src/services/html/html-transformer.service.js +1 -1
  126. package/src/services/invalidation/development-invalidation.service.d.ts +5 -0
  127. package/src/services/invalidation/development-invalidation.service.js +8 -9
  128. package/src/services/module-loading/app-server-module-transpiler.service.d.ts +8 -0
  129. package/src/services/module-loading/app-server-module-transpiler.service.js +1 -7
  130. package/src/services/module-loading/page-module-import.service.d.ts +1 -1
  131. package/src/services/module-loading/page-module-import.service.js +40 -28
  132. package/src/services/module-loading/route-module-build-cache.store.js +1 -1
  133. package/src/services/module-loading/route-module-build-manifest.d.ts +2 -9
  134. package/src/services/module-loading/route-module-build-manifest.js +12 -43
  135. package/src/services/module-loading/server-module-transpiler.service.d.ts +1 -1
  136. package/src/static-site-generator/README.md +1 -1
  137. package/src/static-site-generator/production-page-browser-graph-prebuild.d.ts +28 -0
  138. package/src/static-site-generator/production-page-browser-graph-prebuild.js +22 -0
  139. package/src/static-site-generator/static-build-invalidation.d.ts +2 -2
  140. package/src/static-site-generator/static-build-invalidation.js +3 -3
  141. package/src/static-site-generator/static-site-generator.js +23 -1
  142. package/src/types/internal-types.d.ts +10 -4
  143. package/src/types/public-types.d.ts +59 -10
  144. package/src/watchers/project-watcher.d.ts +3 -3
  145. package/src/watchers/project-watcher.js +15 -5
  146. package/src/build/build-manifest.d.ts +0 -33
  147. package/src/build/deduping-build-executor.js +0 -56
  148. package/src/build/runtime-build-executor.d.ts +0 -14
  149. package/src/build/runtime-build-executor.js +0 -16
  150. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +0 -1
  151. package/src/route-renderer/orchestration/declared-ownership-graph.js +0 -8
  152. package/src/route-renderer/orchestration/route-html-finalization.service.js +0 -26
  153. package/src/route-renderer/orchestration/template-serialization.d.ts +0 -38
  154. package/src/route-renderer/orchestration/template-serialization.js +0 -45
  155. /package/src/build/{browser-runtime-manifest.d.ts → browser/browser-runtime-manifest.d.ts} +0 -0
  156. /package/src/build/{browser-runtime-manifest.js → browser/browser-runtime-manifest.js} +0 -0
  157. /package/src/build/{browser-runtime-plugin-helpers.d.ts → browser/browser-runtime-plugin-helpers.d.ts} +0 -0
  158. /package/src/build/{browser-runtime-plugin-helpers.js → browser/browser-runtime-plugin-helpers.js} +0 -0
  159. /package/src/build/{lit-static-render-worker-context.d.ts → browser/lit-static-render-worker-context.d.ts} +0 -0
  160. /package/src/build/{lit-static-render-worker-context.js → browser/lit-static-render-worker-context.js} +0 -0
  161. /package/src/build/{build-input-fingerprint.js → cache/build-input-fingerprint.js} +0 -0
  162. /package/src/build/{build-contracts.js → contracts/build-contracts.js} +0 -0
  163. /package/src/build/{build-types.d.ts → contracts/build-types.d.ts} +0 -0
  164. /package/src/build/{build-types.js → contracts/build-types.js} +0 -0
  165. /package/src/build/{rolldown-build-adapter.js → rolldown/rolldown-build-adapter.js} +0 -0
  166. /package/src/build/{rolldown-build-invocation-metrics.d.ts → rolldown/rolldown-build-invocation-metrics.d.ts} +0 -0
  167. /package/src/build/{rolldown-build-invocation-metrics.js → rolldown/rolldown-build-invocation-metrics.js} +0 -0
  168. /package/src/build/{runtime-build-output-normalizer.d.ts → rolldown/runtime-build-output-normalizer.d.ts} +0 -0
  169. /package/src/build/{server-side-css-shim-plugin.d.ts → rolldown/server-side-css-shim-plugin.d.ts} +0 -0
  170. /package/src/build/{server-side-css-shim-plugin.js → rolldown/server-side-css-shim-plugin.js} +0 -0
  171. /package/src/build/{build-profile-options.js → runtime/build-profile-options.js} +0 -0
  172. /package/src/build/{parallel-build-executor.js → runtime/parallel-build-executor.js} +0 -0
  173. /package/src/build/{serialized-build-executor.js → runtime/serialized-build-executor.js} +0 -0
  174. /package/src/route-renderer/orchestration/{layout-shell-props.service.js → document-shell/layout-shell-props.service.js} +0 -0
  175. /package/src/route-renderer/orchestration/{page-browser-graph-contribution.loader.js → page-browser-graph/page-browser-graph-contribution.loader.js} +0 -0
  176. /package/src/route-renderer/orchestration/{processed-asset-dedupe.js → page-browser-graph/processed-asset-dedupe.js} +0 -0
  177. /package/src/route-renderer/orchestration/{route-prepared-options.utils.d.ts → route-pipeline/route-prepared-options.utils.d.ts} +0 -0
@@ -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
- import type { EcoPagesAppConfig } from '../../types/internal-types.js';
2
- import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../types/public-types.js';
3
- import { type AssetProcessingService } from '../../services/assets/asset-processing-service/index.js';
1
+ import type { EcoPagesAppConfig } from '../../../types/internal-types.js';
2
+ import type { PageBrowserGraphContribution, PageBrowserGraphResult } from '../../../types/public-types.js';
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;
@@ -1,8 +1,14 @@
1
1
  import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import {
4
- } from "../../services/assets/asset-processing-service/index.js";
5
- import { appLogger } from "../../global/app-logger.js";
4
+ } from "../../../services/assets/asset-processing-service/index.js";
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
  }
@@ -1,3 +1,3 @@
1
- import type { ProcessedAsset } from '../../services/assets/asset-processing-service/index.js';
1
+ import type { ProcessedAsset } from '../../../services/assets/asset-processing-service/index.js';
2
2
  export declare function buildProcessedAssetDedupeKey(asset: ProcessedAsset): string;
3
3
  export declare function dedupeProcessedAssets(assets: ProcessedAsset[]): ProcessedAsset[];