@bleedingdev/modern-js-app-tools-extensions 3.8.3-ultramodern.2

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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/cloudflare/artifacts.js +127 -0
  4. package/dist/cjs/cloudflare/constants.js +141 -0
  5. package/dist/cjs/cloudflare/delivery-unit.js +161 -0
  6. package/dist/cjs/cloudflare/i18n-worker.js +135 -0
  7. package/dist/cjs/cloudflare/index.js +156 -0
  8. package/dist/cjs/cloudflare/security-policies.js +205 -0
  9. package/dist/cjs/cloudflare/types.js +18 -0
  10. package/dist/cjs/cloudflare/utils.js +66 -0
  11. package/dist/cjs/cloudflare/worker-manifest.js +239 -0
  12. package/dist/cjs/cloudflare/wrangler-config.js +205 -0
  13. package/dist/cjs/cloudflare-builder.js +394 -0
  14. package/dist/cjs/cloudflare-output-contract.js +108 -0
  15. package/dist/cjs/cloudflare-output-plan.js +93 -0
  16. package/dist/cjs/cloudflare-output-verifier/identity.js +92 -0
  17. package/dist/cjs/cloudflare-output-verifier/index.js +188 -0
  18. package/dist/cjs/cloudflare-output-verifier/issues.js +54 -0
  19. package/dist/cjs/cloudflare-output-verifier/manifest.js +89 -0
  20. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +151 -0
  21. package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +378 -0
  22. package/dist/cjs/config.js +18 -0
  23. package/dist/cjs/css-extract-runtime-plugin.js +74 -0
  24. package/dist/cjs/index.js +125 -0
  25. package/dist/cjs/read-template.js +56 -0
  26. package/dist/cjs/release-envelope/canonical.js +68 -0
  27. package/dist/cjs/release-envelope/framework-output.js +649 -0
  28. package/dist/cjs/release-envelope/index.js +399 -0
  29. package/dist/cjs/release-envelope/plugin.js +102 -0
  30. package/dist/cjs/release-envelope/types.js +51 -0
  31. package/dist/cjs/release-identity.js +110 -0
  32. package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
  33. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
  34. package/dist/cjs/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
  35. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
  36. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
  37. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
  38. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +11 -0
  39. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +281 -0
  40. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  41. package/dist/cjs/templates/cloudflare-worker-path.mjs +98 -0
  42. package/dist/esm/cloudflare/artifacts.mjs +64 -0
  43. package/dist/esm/cloudflare/constants.mjs +34 -0
  44. package/dist/esm/cloudflare/delivery-unit.mjs +110 -0
  45. package/dist/esm/cloudflare/i18n-worker.mjs +97 -0
  46. package/dist/esm/cloudflare/index.mjs +108 -0
  47. package/dist/esm/cloudflare/security-policies.mjs +167 -0
  48. package/dist/esm/cloudflare/types.mjs +0 -0
  49. package/dist/esm/cloudflare/utils.mjs +12 -0
  50. package/dist/esm/cloudflare/worker-manifest.mjs +179 -0
  51. package/dist/esm/cloudflare/wrangler-config.mjs +151 -0
  52. package/dist/esm/cloudflare-builder.mjs +327 -0
  53. package/dist/esm/cloudflare-output-contract.mjs +31 -0
  54. package/dist/esm/cloudflare-output-plan.mjs +45 -0
  55. package/dist/esm/cloudflare-output-verifier/identity.mjs +54 -0
  56. package/dist/esm/cloudflare-output-verifier/index.mjs +133 -0
  57. package/dist/esm/cloudflare-output-verifier/issues.mjs +10 -0
  58. package/dist/esm/cloudflare-output-verifier/manifest.mjs +51 -0
  59. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +99 -0
  60. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +313 -0
  61. package/dist/esm/config.mjs +0 -0
  62. package/dist/esm/css-extract-runtime-plugin.mjs +32 -0
  63. package/dist/esm/index.mjs +10 -0
  64. package/dist/esm/read-template.mjs +5 -0
  65. package/dist/esm/release-envelope/canonical.mjs +21 -0
  66. package/dist/esm/release-envelope/framework-output.mjs +573 -0
  67. package/dist/esm/release-envelope/index.mjs +335 -0
  68. package/dist/esm/release-envelope/plugin.mjs +54 -0
  69. package/dist/esm/release-envelope/types.mjs +7 -0
  70. package/dist/esm/release-identity.mjs +66 -0
  71. package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
  72. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
  73. package/dist/esm/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
  74. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
  75. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
  76. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
  77. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +11 -0
  78. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +281 -0
  79. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  80. package/dist/esm/templates/cloudflare-worker-path.mjs +98 -0
  81. package/dist/esm-node/cloudflare/artifacts.mjs +65 -0
  82. package/dist/esm-node/cloudflare/constants.mjs +35 -0
  83. package/dist/esm-node/cloudflare/delivery-unit.mjs +111 -0
  84. package/dist/esm-node/cloudflare/i18n-worker.mjs +98 -0
  85. package/dist/esm-node/cloudflare/index.mjs +109 -0
  86. package/dist/esm-node/cloudflare/security-policies.mjs +168 -0
  87. package/dist/esm-node/cloudflare/types.mjs +1 -0
  88. package/dist/esm-node/cloudflare/utils.mjs +13 -0
  89. package/dist/esm-node/cloudflare/worker-manifest.mjs +180 -0
  90. package/dist/esm-node/cloudflare/wrangler-config.mjs +152 -0
  91. package/dist/esm-node/cloudflare-builder.mjs +328 -0
  92. package/dist/esm-node/cloudflare-output-contract.mjs +32 -0
  93. package/dist/esm-node/cloudflare-output-plan.mjs +46 -0
  94. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +55 -0
  95. package/dist/esm-node/cloudflare-output-verifier/index.mjs +134 -0
  96. package/dist/esm-node/cloudflare-output-verifier/issues.mjs +11 -0
  97. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +52 -0
  98. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +100 -0
  99. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +314 -0
  100. package/dist/esm-node/config.mjs +1 -0
  101. package/dist/esm-node/css-extract-runtime-plugin.mjs +33 -0
  102. package/dist/esm-node/index.mjs +11 -0
  103. package/dist/esm-node/read-template.mjs +9 -0
  104. package/dist/esm-node/release-envelope/canonical.mjs +22 -0
  105. package/dist/esm-node/release-envelope/framework-output.mjs +574 -0
  106. package/dist/esm-node/release-envelope/index.mjs +336 -0
  107. package/dist/esm-node/release-envelope/plugin.mjs +55 -0
  108. package/dist/esm-node/release-envelope/types.mjs +8 -0
  109. package/dist/esm-node/release-identity.mjs +67 -0
  110. package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
  111. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
  112. package/dist/esm-node/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
  113. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
  114. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
  115. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
  116. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +11 -0
  117. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +281 -0
  118. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  119. package/dist/esm-node/templates/cloudflare-worker-path.mjs +98 -0
  120. package/dist/types/cloudflare/artifacts.d.ts +15 -0
  121. package/dist/types/cloudflare/constants.d.ts +24 -0
  122. package/dist/types/cloudflare/delivery-unit.d.ts +23 -0
  123. package/dist/types/cloudflare/i18n-worker.d.ts +22 -0
  124. package/dist/types/cloudflare/index.d.ts +2 -0
  125. package/dist/types/cloudflare/security-policies.d.ts +52 -0
  126. package/dist/types/cloudflare/types.d.ts +28 -0
  127. package/dist/types/cloudflare/utils.d.ts +6 -0
  128. package/dist/types/cloudflare/worker-manifest.d.ts +149 -0
  129. package/dist/types/cloudflare/wrangler-config.d.ts +51 -0
  130. package/dist/types/cloudflare-builder.d.ts +59 -0
  131. package/dist/types/cloudflare-output-contract.d.ts +14 -0
  132. package/dist/types/cloudflare-output-plan.d.ts +34 -0
  133. package/dist/types/cloudflare-output-verifier/identity.d.ts +8 -0
  134. package/dist/types/cloudflare-output-verifier/index.d.ts +18 -0
  135. package/dist/types/cloudflare-output-verifier/issues.d.ts +14 -0
  136. package/dist/types/cloudflare-output-verifier/manifest.d.ts +2 -0
  137. package/dist/types/cloudflare-output-verifier/mutation-policy.d.ts +14 -0
  138. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +20 -0
  139. package/dist/types/config.d.ts +175 -0
  140. package/dist/types/css-extract-runtime-plugin.d.ts +6 -0
  141. package/dist/types/index.d.ts +10 -0
  142. package/dist/types/read-template.d.ts +2 -0
  143. package/dist/types/release-envelope/canonical.d.ts +5 -0
  144. package/dist/types/release-envelope/framework-output.d.ts +32 -0
  145. package/dist/types/release-envelope/index.d.ts +6 -0
  146. package/dist/types/release-envelope/plugin.d.ts +30 -0
  147. package/dist/types/release-envelope/types.d.ts +62 -0
  148. package/dist/types/release-identity.d.ts +16 -0
  149. package/dist/types/templates/cloudflare-entry.001-bootstrap-security.d.mts +1 -0
  150. package/dist/types/templates/cloudflare-entry.002-assets-routes.d.mts +1 -0
  151. package/dist/types/templates/cloudflare-entry.003-i18n-locales.d.mts +1 -0
  152. package/dist/types/templates/cloudflare-entry.004-rendering-css.d.mts +1 -0
  153. package/dist/types/templates/cloudflare-entry.005-worker-dispatch.d.mts +1 -0
  154. package/dist/types/templates/cloudflare-entry.006-fetch-handler.d.mts +4 -0
  155. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +5 -0
  156. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +48 -0
  157. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +13 -0
  158. package/dist/types/templates/cloudflare-worker-path.d.mts +21 -0
  159. package/package.json +131 -0
  160. package/src/cloudflare/artifacts.ts +165 -0
  161. package/src/cloudflare/constants.ts +50 -0
  162. package/src/cloudflare/delivery-unit.ts +195 -0
  163. package/src/cloudflare/i18n-worker.ts +197 -0
  164. package/src/cloudflare/index.ts +259 -0
  165. package/src/cloudflare/security-policies.ts +194 -0
  166. package/src/cloudflare/types.ts +33 -0
  167. package/src/cloudflare/utils.ts +40 -0
  168. package/src/cloudflare/worker-manifest.ts +313 -0
  169. package/src/cloudflare/wrangler-config.ts +338 -0
  170. package/src/cloudflare-builder.ts +653 -0
  171. package/src/cloudflare-output-contract.ts +30 -0
  172. package/src/cloudflare-output-plan.ts +108 -0
  173. package/src/cloudflare-output-verifier/identity.ts +87 -0
  174. package/src/cloudflare-output-verifier/index.ts +277 -0
  175. package/src/cloudflare-output-verifier/issues.ts +54 -0
  176. package/src/cloudflare-output-verifier/manifest.ts +76 -0
  177. package/src/cloudflare-output-verifier/mutation-policy.ts +159 -0
  178. package/src/cloudflare-output-verifier/worker-bundles.ts +664 -0
  179. package/src/config.ts +196 -0
  180. package/src/css-extract-runtime-plugin.ts +70 -0
  181. package/src/index.ts +10 -0
  182. package/src/read-template.ts +8 -0
  183. package/src/release-envelope/canonical.ts +58 -0
  184. package/src/release-envelope/framework-output.ts +1121 -0
  185. package/src/release-envelope/index.ts +656 -0
  186. package/src/release-envelope/plugin.ts +125 -0
  187. package/src/release-envelope/types.ts +83 -0
  188. package/src/release-identity.ts +134 -0
  189. package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +251 -0
  190. package/src/templates/cloudflare-entry.002-assets-routes.mjs +191 -0
  191. package/src/templates/cloudflare-entry.003-i18n-locales.mjs +514 -0
  192. package/src/templates/cloudflare-entry.004-rendering-css.mjs +1194 -0
  193. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +421 -0
  194. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +93 -0
  195. package/src/templates/cloudflare-worker-fs-promises.mjs +11 -0
  196. package/src/templates/cloudflare-worker-loadable-server.mjs +281 -0
  197. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +14 -0
  198. package/src/templates/cloudflare-worker-path.mjs +98 -0
@@ -0,0 +1,1194 @@
1
+ function createNoopMonitors() {
2
+ const noop = () => {};
3
+
4
+ return {
5
+ debug: noop,
6
+ error: noop,
7
+ info: noop,
8
+ warn: noop,
9
+ };
10
+ }
11
+
12
+ const DISTRIBUTED_SSR_FRAGMENTS_LOCALS_KEY = '__modernDistributedSsrFragments';
13
+ const DISTRIBUTED_SSR_FRAGMENT_REQUEST_LOCALS_KEY =
14
+ '__modernDistributedSsrFragmentRequest';
15
+ const DISTRIBUTED_SSR_CSS_HEADER = 'x-modern-distributed-ssr-css';
16
+ const DISTRIBUTED_SSR_PROVENANCE_HEADER = 'x-modern-distributed-ssr-provenance';
17
+ const CLOUDFLARE_STYLESHEET_LINKS_SENTINEL =
18
+ '<meta data-modern-cloudflare-stylesheet-links>';
19
+
20
+ function distributedSsrFragmentKey(remote, expose) {
21
+ return `${remote}::${expose}`;
22
+ }
23
+
24
+ function escapeFragmentRegExp(value) {
25
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
26
+ }
27
+
28
+ function openingTagHasAttribute(openingTag, name, value) {
29
+ const pattern = new RegExp(
30
+ `\\s${escapeFragmentRegExp(name)}=(?:"${escapeFragmentRegExp(value)}"|'${escapeFragmentRegExp(value)}')`,
31
+ 'u',
32
+ );
33
+
34
+ return pattern.test(openingTag);
35
+ }
36
+
37
+ function extractRenderedBoundaryElement(html, boundaryId, expose) {
38
+ const boundaryPattern = new RegExp(
39
+ `\\sdata-modern-boundary-id=(?:"${escapeFragmentRegExp(boundaryId)}"|'${escapeFragmentRegExp(boundaryId)}')`,
40
+ 'u',
41
+ );
42
+ const boundaryMatch = boundaryPattern.exec(html);
43
+ if (!boundaryMatch) {
44
+ return undefined;
45
+ }
46
+
47
+ const openingStart = html.lastIndexOf('<', boundaryMatch.index);
48
+ const openingEnd = html.indexOf('>', boundaryMatch.index);
49
+ if (openingStart < 0 || openingEnd < 0) {
50
+ return undefined;
51
+ }
52
+
53
+ const openingTag = html.slice(openingStart, openingEnd + 1);
54
+ const tagName = /^<([a-z][\w:-]*)\b/iu.exec(openingTag)?.[1];
55
+ if (
56
+ !tagName ||
57
+ !openingTagHasAttribute(openingTag, 'data-modern-mf-expose', expose)
58
+ ) {
59
+ return undefined;
60
+ }
61
+
62
+ const tagPattern = new RegExp(
63
+ `<\\/?${escapeFragmentRegExp(tagName)}\\b[^>]*>`,
64
+ 'giu',
65
+ );
66
+ let depth = 0;
67
+
68
+ for (const tagMatch of html.slice(openingStart).matchAll(tagPattern)) {
69
+ const tag = tagMatch[0];
70
+ const absoluteStart = openingStart + (tagMatch.index ?? 0);
71
+ if (tag.startsWith('</')) {
72
+ depth -= 1;
73
+ if (depth === 0) {
74
+ return html.slice(openingStart, absoluteStart + tag.length);
75
+ }
76
+ } else if (!tag.endsWith('/>')) {
77
+ depth += 1;
78
+ }
79
+ }
80
+
81
+ return undefined;
82
+ }
83
+
84
+ function findFragmentMarker(html, boundaryId, expose, marker, fromIndex = 0) {
85
+ const tagPattern = /<template\b[^>]*>/giu;
86
+ tagPattern.lastIndex = fromIndex;
87
+
88
+ for (const match of html.matchAll(tagPattern)) {
89
+ const tag = match[0];
90
+ if (
91
+ openingTagHasAttribute(tag, 'data-modern-boundary-id', boundaryId) &&
92
+ openingTagHasAttribute(tag, 'data-modern-mf-expose', expose) &&
93
+ openingTagHasAttribute(tag, 'data-modern-distributed-ssr-marker', marker)
94
+ ) {
95
+ return {
96
+ end: (match.index ?? 0) + tag.length,
97
+ start: match.index ?? 0,
98
+ };
99
+ }
100
+ }
101
+
102
+ return undefined;
103
+ }
104
+
105
+ function extractRenderedFragmentHtml(html, boundaryId, expose) {
106
+ const start = findFragmentMarker(html, boundaryId, expose, 'start');
107
+ if (start) {
108
+ const startClose = html.indexOf('</template>', start.end);
109
+ const contentStart =
110
+ startClose === -1 ? start.end : startClose + '</template>'.length;
111
+ const end = findFragmentMarker(
112
+ html,
113
+ boundaryId,
114
+ expose,
115
+ 'end',
116
+ contentStart,
117
+ );
118
+
119
+ if (end && end.start >= contentStart) {
120
+ return html.slice(contentStart, end.start);
121
+ }
122
+ }
123
+
124
+ return extractRenderedBoundaryElement(html, boundaryId, expose);
125
+ }
126
+
127
+ function readOpeningTagAttribute(openingTag, name) {
128
+ const pattern = new RegExp(
129
+ `\\s${escapeFragmentRegExp(name)}=(?:"([^"]*)"|'([^']*)')`,
130
+ 'iu',
131
+ );
132
+ const match = pattern.exec(openingTag);
133
+
134
+ return match?.[1] ?? match?.[2];
135
+ }
136
+
137
+ function collectStylesheetHrefs(html) {
138
+ const hrefs = [];
139
+
140
+ for (const match of html.matchAll(/<link\b[^>]*>/giu)) {
141
+ const link = match[0];
142
+ const rel = readOpeningTagAttribute(link, 'rel');
143
+ const href = readOpeningTagAttribute(link, 'href');
144
+
145
+ if (href && rel?.split(/\s+/u).includes('stylesheet')) {
146
+ hrefs.push(href);
147
+ }
148
+ }
149
+
150
+ return hrefs;
151
+ }
152
+
153
+ function normalizeStylesheetHref(href, requestUrl) {
154
+ try {
155
+ return new URL(href, requestUrl).toString();
156
+ } catch {
157
+ return href;
158
+ }
159
+ }
160
+
161
+ function dedupeStylesheetLinks(html, seenHrefs = new Set(), requestUrl) {
162
+ return html.replace(/<link\b[^>]*>/giu, link => {
163
+ const rel = readOpeningTagAttribute(link, 'rel');
164
+ const href = readOpeningTagAttribute(link, 'href');
165
+
166
+ if (!href || !rel?.split(/\s+/u).includes('stylesheet')) {
167
+ return link;
168
+ }
169
+
170
+ const normalizedHref = normalizeStylesheetHref(href, requestUrl);
171
+ if (seenHrefs.has(normalizedHref)) {
172
+ return '';
173
+ }
174
+
175
+ seenHrefs.add(normalizedHref);
176
+ return link;
177
+ });
178
+ }
179
+
180
+ function stripStylesheetLinks(html) {
181
+ return html.replace(/<link\b[^>]*>/giu, link => {
182
+ const rel = readOpeningTagAttribute(link, 'rel');
183
+ return rel?.split(/\s+/u).includes('stylesheet') ? '' : link;
184
+ });
185
+ }
186
+
187
+ function createStylesheetLinksHtml(stylesheetEntries) {
188
+ return stylesheetEntries
189
+ .map(({ href, reactResource }) =>
190
+ reactResource
191
+ ? `<link href="${escapeAttribute(href)}" rel="stylesheet" type="text/css" data-precedence="default">`
192
+ : `<link rel="stylesheet" href="${escapeAttribute(href)}">`,
193
+ )
194
+ .join('');
195
+ }
196
+
197
+ function createStylesheetLinkStream(body, stylesheetEntries, requestUrl) {
198
+ const decoder = new TextDecoder();
199
+ const encoder = new TextEncoder();
200
+ let injected = false;
201
+ let pending = '';
202
+
203
+ const flushReadyHtml = (controller, final) => {
204
+ const sentinelIndex = pending.indexOf(CLOUDFLARE_STYLESHEET_LINKS_SENTINEL);
205
+ const closingHeadIndex =
206
+ sentinelIndex >= 0 ? pending.indexOf('</head>', sentinelIndex) : -1;
207
+ if (!injected && sentinelIndex >= 0 && (closingHeadIndex >= 0 || final)) {
208
+ const headEnd = closingHeadIndex >= 0 ? closingHeadIndex : pending.length;
209
+ const existingHrefs = new Set(
210
+ collectStylesheetHrefs(pending.slice(0, headEnd)).map(href =>
211
+ normalizeStylesheetHref(href, requestUrl),
212
+ ),
213
+ );
214
+ const links = createStylesheetLinksHtml(
215
+ stylesheetEntries.filter(
216
+ ({ preloadHref }) => !existingHrefs.has(preloadHref),
217
+ ),
218
+ );
219
+ const output = pending.replace(
220
+ CLOUDFLARE_STYLESHEET_LINKS_SENTINEL,
221
+ links,
222
+ );
223
+ injected = true;
224
+ pending = '';
225
+ controller.enqueue(encoder.encode(output));
226
+ return;
227
+ }
228
+
229
+ if (!final && !injected) {
230
+ return;
231
+ }
232
+ if (pending.length > 0) {
233
+ controller.enqueue(encoder.encode(pending));
234
+ pending = '';
235
+ }
236
+ };
237
+
238
+ return body.pipeThrough(
239
+ new TransformStream({
240
+ flush(controller) {
241
+ pending += decoder.decode();
242
+ flushReadyHtml(controller, true);
243
+ },
244
+ transform(chunk, controller) {
245
+ pending +=
246
+ typeof chunk === 'string'
247
+ ? chunk
248
+ : decoder.decode(chunk, { stream: true });
249
+ flushReadyHtml(controller, false);
250
+ },
251
+ }),
252
+ );
253
+ }
254
+
255
+ function readFragmentStylesheetAssets(response) {
256
+ const value = response.headers.get(DISTRIBUTED_SSR_CSS_HEADER);
257
+
258
+ if (value === null) {
259
+ return undefined;
260
+ }
261
+
262
+ try {
263
+ const assets = JSON.parse(value);
264
+
265
+ return Array.isArray(assets) &&
266
+ assets.every(asset => typeof asset === 'string' && asset.endsWith('.css'))
267
+ ? assets
268
+ : undefined;
269
+ } catch {
270
+ return undefined;
271
+ }
272
+ }
273
+
274
+ async function resolveFragmentStylesheetHrefs(
275
+ stylesheetAssets,
276
+ fragment,
277
+ fragmentUrl,
278
+ request,
279
+ env,
280
+ ) {
281
+ const hostManifest = await readAssetJson('mf-manifest.json', request, env);
282
+ const remote = (
283
+ Array.isArray(hostManifest?.remotes) ? hostManifest.remotes : []
284
+ ).find(
285
+ candidate =>
286
+ candidate?.alias === fragment.remote ||
287
+ candidate?.federationContainerName === fragment.boundaryId,
288
+ );
289
+ const manifestUrl = remote
290
+ ? getRemoteManifestUrl(remote, request)
291
+ : undefined;
292
+ const publicBase = manifestUrl
293
+ ? new URL('.', manifestUrl).toString()
294
+ : undefined;
295
+ const hrefs = stylesheetAssets.map(href => {
296
+ const resolved = new URL(href, fragmentUrl);
297
+
298
+ if (
299
+ publicBase &&
300
+ (!isAbsoluteUrl(href) || resolved.origin === fragmentUrl.origin)
301
+ ) {
302
+ return new URL(href.replace(/^\/+/u, ''), publicBase).toString();
303
+ }
304
+
305
+ return resolved.toString();
306
+ });
307
+
308
+ return [...new Set(hrefs)];
309
+ }
310
+
311
+ function createHydratableFragmentHtml(fragmentHtml) {
312
+ return `<!--$-->${fragmentHtml}<!--/$-->`;
313
+ }
314
+
315
+ async function sha256Hex(value) {
316
+ const bytes = new Uint8Array(
317
+ await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value)),
318
+ );
319
+
320
+ return [...bytes].map(byte => byte.toString(16).padStart(2, '0')).join('');
321
+ }
322
+
323
+ function serializeDistributedSsrProps(props) {
324
+ const seen = new WeakSet();
325
+ const serialized = JSON.stringify(props, (_key, value) => {
326
+ if (
327
+ typeof value === 'bigint' ||
328
+ typeof value === 'function' ||
329
+ typeof value === 'symbol'
330
+ ) {
331
+ throw new TypeError('Distributed SSR props must be JSON serializable.');
332
+ }
333
+ if (typeof value === 'number' && !Number.isFinite(value)) {
334
+ throw new TypeError('Distributed SSR props must contain finite numbers.');
335
+ }
336
+ if (value && typeof value === 'object') {
337
+ if (seen.has(value)) {
338
+ throw new TypeError('Distributed SSR props must not contain cycles.');
339
+ }
340
+ seen.add(value);
341
+ }
342
+ return value;
343
+ });
344
+
345
+ if (typeof serialized !== 'string') {
346
+ throw new TypeError('Distributed SSR props must serialize to JSON.');
347
+ }
348
+
349
+ return serialized;
350
+ }
351
+
352
+ function readFragmentProvenance(response) {
353
+ const value = response.headers.get(DISTRIBUTED_SSR_PROVENANCE_HEADER);
354
+ if (value === null) {
355
+ return undefined;
356
+ }
357
+
358
+ try {
359
+ const provenance = JSON.parse(decodeURIComponent(value));
360
+ return provenance && typeof provenance === 'object'
361
+ ? provenance
362
+ : undefined;
363
+ } catch {
364
+ return undefined;
365
+ }
366
+ }
367
+
368
+ function fragmentLocale(request) {
369
+ const segment = new URL(request.url).pathname.split('/').filter(Boolean)[0];
370
+
371
+ return segment && /^[a-z\d-]+$/iu.test(segment) ? segment : 'en';
372
+ }
373
+
374
+ function createFragmentFailure(fragment, reason) {
375
+ return {
376
+ boundaryId: fragment.boundaryId,
377
+ expose: fragment.expose,
378
+ reason,
379
+ remote: fragment.remote,
380
+ status: 'degraded',
381
+ };
382
+ }
383
+
384
+ function emitFragmentFailure(binding, fragment, reason) {
385
+ try {
386
+ console.error(
387
+ JSON.stringify({
388
+ appName: 'modern-js-cloudflare-worker',
389
+ eventName: 'modernjs:microvertical-server-fallback',
390
+ metadata: {
391
+ classification: 'remote-unavailable',
392
+ expose: fragment.expose,
393
+ platform: 'cloudflare-service-binding',
394
+ reason,
395
+ remote: fragment.remote,
396
+ serviceBinding: binding.binding,
397
+ status: 'degraded',
398
+ },
399
+ phase: 'discovery',
400
+ reason: 'remote-unavailable',
401
+ schemaVersion: 1,
402
+ }),
403
+ );
404
+ } catch {}
405
+ }
406
+
407
+ async function fetchDistributedSsrFragment(
408
+ binding,
409
+ fragment,
410
+ propsJson,
411
+ request,
412
+ env,
413
+ ) {
414
+ const service = env?.[binding.binding];
415
+ if (!service || typeof service.fetch !== 'function') {
416
+ emitFragmentFailure(binding, fragment, 'binding-unavailable');
417
+ return createFragmentFailure(fragment, 'binding-unavailable');
418
+ }
419
+
420
+ const fragmentPath = fragment.path.replaceAll(
421
+ '{locale}',
422
+ encodeURIComponent(fragmentLocale(request)),
423
+ );
424
+ const fragmentUrl = new URL(fragmentPath, request.url);
425
+ const headers = new Headers(request.headers);
426
+ headers.delete('host');
427
+ headers.set('x-modern-js-fragment-request', '1');
428
+ headers.set('x-modern-distributed-ssr-boundary-id', fragment.boundaryId);
429
+ headers.set('x-modern-distributed-ssr-expose', fragment.expose);
430
+ headers.set('x-modern-distributed-ssr-props', encodeURIComponent(propsJson));
431
+ headers.set('x-modern-distributed-ssr-remote', fragment.remote);
432
+ headers.set('x-modern-distributed-ssr-source-url', request.url);
433
+
434
+ try {
435
+ const response = await service.fetch(
436
+ new Request(fragmentUrl, {
437
+ headers,
438
+ method: 'GET',
439
+ }),
440
+ );
441
+ if (!response.ok) {
442
+ const reason = `fragment-http-${response.status}`;
443
+ emitFragmentFailure(binding, fragment, reason);
444
+ return createFragmentFailure(fragment, reason);
445
+ }
446
+
447
+ const html = await response.text();
448
+ const stylesheetAssets = readFragmentStylesheetAssets(response);
449
+ const renderedBoundaryHtml = extractRenderedFragmentHtml(
450
+ html,
451
+ fragment.boundaryId,
452
+ fragment.expose,
453
+ );
454
+ const provenance = readFragmentProvenance(response);
455
+ if (
456
+ !renderedBoundaryHtml ||
457
+ stylesheetAssets === undefined ||
458
+ provenance?.boundaryId !== fragment.boundaryId ||
459
+ provenance?.expose !== fragment.expose ||
460
+ provenance?.remote !== fragment.remote ||
461
+ typeof provenance?.buildMarker !== 'string' ||
462
+ typeof provenance?.digest !== 'string' ||
463
+ typeof provenance?.sourceRevision !== 'string' ||
464
+ typeof provenance?.unitId !== 'string' ||
465
+ provenance.digest !== (await sha256Hex(renderedBoundaryHtml))
466
+ ) {
467
+ emitFragmentFailure(binding, fragment, 'fragment-contract-mismatch');
468
+ return createFragmentFailure(fragment, 'fragment-contract-mismatch');
469
+ }
470
+ const stylesheetHrefs = await resolveFragmentStylesheetHrefs(
471
+ stylesheetAssets,
472
+ fragment,
473
+ fragmentUrl,
474
+ request,
475
+ env,
476
+ );
477
+ const fragmentHtml = createHydratableFragmentHtml(
478
+ stripStylesheetLinks(renderedBoundaryHtml),
479
+ );
480
+
481
+ return {
482
+ boundaryId: fragment.boundaryId,
483
+ buildMarker: provenance.buildMarker,
484
+ digest: provenance.digest,
485
+ expose: fragment.expose,
486
+ html: fragmentHtml,
487
+ provenance,
488
+ remote: fragment.remote,
489
+ status: 'ready',
490
+ stylesheetHrefs,
491
+ };
492
+ } catch {
493
+ emitFragmentFailure(binding, fragment, 'fragment-request-failed');
494
+ return createFragmentFailure(fragment, 'fragment-request-failed');
495
+ }
496
+ }
497
+
498
+ function createDistributedSsrFragmentContext(request, env) {
499
+ const bindings = Array.isArray(MODERN_WORKER_MANIFEST.serviceBindings)
500
+ ? MODERN_WORKER_MANIFEST.serviceBindings
501
+ : [];
502
+ const configuredFragments = bindings.flatMap(binding =>
503
+ Array.isArray(binding.fragments)
504
+ ? binding.fragments.map(fragment => ({ binding, fragment }))
505
+ : [],
506
+ );
507
+ if (configuredFragments.length === 0) {
508
+ return undefined;
509
+ }
510
+ const cache = new Map();
511
+ const pendingResolutions = new Set();
512
+ const stylesheetHrefsByFragment = configuredFragments.map(() => new Set());
513
+
514
+ return {
515
+ required: true,
516
+ async getStylesheetHrefs() {
517
+ while (pendingResolutions.size > 0) {
518
+ await Promise.all([...pendingResolutions]);
519
+ }
520
+
521
+ const hrefs = new Set();
522
+ for (const fragmentHrefs of stylesheetHrefsByFragment) {
523
+ for (const href of fragmentHrefs) {
524
+ hrefs.add(href);
525
+ }
526
+ }
527
+
528
+ return [...hrefs];
529
+ },
530
+ resolve(remote, expose, props) {
531
+ const configuredIndex = configuredFragments.findIndex(
532
+ candidate =>
533
+ candidate.fragment.remote === remote &&
534
+ candidate.fragment.expose === expose,
535
+ );
536
+ if (configuredIndex < 0) {
537
+ return {
538
+ boundaryId: remote,
539
+ expose,
540
+ reason: 'fragment-not-configured',
541
+ remote,
542
+ status: 'degraded',
543
+ };
544
+ }
545
+ const configured = configuredFragments[configuredIndex];
546
+
547
+ let propsJson;
548
+ try {
549
+ propsJson = serializeDistributedSsrProps(props);
550
+ } catch {
551
+ return createFragmentFailure(
552
+ configured.fragment,
553
+ 'fragment-props-not-serializable',
554
+ );
555
+ }
556
+ const key = `${distributedSsrFragmentKey(remote, expose)}::${propsJson}`;
557
+ if (!cache.has(key)) {
558
+ const resolution = fetchDistributedSsrFragment(
559
+ configured.binding,
560
+ configured.fragment,
561
+ propsJson,
562
+ request,
563
+ env,
564
+ ).then(result => {
565
+ if (result.status === 'ready') {
566
+ for (const href of result.stylesheetHrefs) {
567
+ stylesheetHrefsByFragment[configuredIndex].add(href);
568
+ }
569
+ }
570
+ const { stylesheetHrefs: _stylesheetHrefs, ...publicResult } = result;
571
+ cache.set(key, publicResult);
572
+ return publicResult;
573
+ });
574
+
575
+ pendingResolutions.add(resolution);
576
+ resolution.then(
577
+ () => pendingResolutions.delete(resolution),
578
+ () => pendingResolutions.delete(resolution),
579
+ );
580
+ cache.set(key, resolution);
581
+ }
582
+
583
+ return cache.get(key);
584
+ },
585
+ };
586
+ }
587
+
588
+ function readDistributedSsrFragmentRequest(request) {
589
+ if (request.headers.get('x-modern-js-fragment-request') !== '1') {
590
+ return undefined;
591
+ }
592
+
593
+ try {
594
+ const boundaryId = request.headers.get(
595
+ 'x-modern-distributed-ssr-boundary-id',
596
+ );
597
+ const expose = request.headers.get('x-modern-distributed-ssr-expose');
598
+ const propsValue = request.headers.get('x-modern-distributed-ssr-props');
599
+ const remote = request.headers.get('x-modern-distributed-ssr-remote');
600
+ const sourceUrl = request.headers.get(
601
+ 'x-modern-distributed-ssr-source-url',
602
+ );
603
+ const props = JSON.parse(decodeURIComponent(propsValue ?? ''));
604
+ if (
605
+ !boundaryId ||
606
+ !expose ||
607
+ !remote ||
608
+ !sourceUrl ||
609
+ !props ||
610
+ typeof props !== 'object' ||
611
+ Array.isArray(props)
612
+ ) {
613
+ return undefined;
614
+ }
615
+
616
+ return { boundaryId, expose, props, remote, sourceUrl };
617
+ } catch {
618
+ return undefined;
619
+ }
620
+ }
621
+
622
+ function withCloudflareStylesheetLinksSentinel(htmlTemplate) {
623
+ if (htmlTemplate.includes(CLOUDFLARE_STYLESHEET_LINKS_SENTINEL)) {
624
+ return htmlTemplate;
625
+ }
626
+
627
+ const closingHeads = [...htmlTemplate.matchAll(/<\/head\s*>/giu)];
628
+ const closingHead = closingHeads.at(-1);
629
+ if (closingHead?.index === undefined) {
630
+ return htmlTemplate;
631
+ }
632
+
633
+ return `${htmlTemplate.slice(
634
+ 0,
635
+ closingHead.index,
636
+ )}${CLOUDFLARE_STYLESHEET_LINKS_SENTINEL}${htmlTemplate.slice(
637
+ closingHead.index,
638
+ )}`;
639
+ }
640
+
641
+ function createRequestHandlerOptions({
642
+ route,
643
+ htmlTemplate,
644
+ routeManifest,
645
+ loadableStats,
646
+ distributedSsrFragments,
647
+ distributedSsrFragmentRequest,
648
+ }) {
649
+ const monitors = createNoopMonitors();
650
+
651
+ return {
652
+ resource: {
653
+ route,
654
+ routeManifest,
655
+ loadableStats,
656
+ htmlTemplate: withCloudflareStylesheetLinksSentinel(htmlTemplate),
657
+ entryName: route.entryName,
658
+ },
659
+ params: {},
660
+ loaderContext: {},
661
+ config: {},
662
+ locals: {
663
+ ...(distributedSsrFragments === undefined
664
+ ? {}
665
+ : {
666
+ [DISTRIBUTED_SSR_FRAGMENTS_LOCALS_KEY]: distributedSsrFragments,
667
+ }),
668
+ ...(distributedSsrFragmentRequest === undefined
669
+ ? {}
670
+ : {
671
+ [DISTRIBUTED_SSR_FRAGMENT_REQUEST_LOCALS_KEY]:
672
+ distributedSsrFragmentRequest,
673
+ }),
674
+ },
675
+ staticGenerate: false,
676
+ monitors,
677
+ onError(error) {
678
+ monitors.error(error);
679
+ },
680
+ onTiming() {},
681
+ reporter: {
682
+ reportTiming: () => {},
683
+ },
684
+ };
685
+ }
686
+
687
+ function collectRouteCssAssets(route, routeManifest) {
688
+ const routeAssets = routeManifest?.routeAssets || {};
689
+ const candidateKeys = [route.entryName, `async-${route.entryName}`].filter(
690
+ Boolean,
691
+ );
692
+ const assets = new Set();
693
+
694
+ for (const key of candidateKeys) {
695
+ const routeAsset = routeAssets[key];
696
+ const cssAssets = [
697
+ ...(Array.isArray(routeAsset?.referenceCssAssets)
698
+ ? routeAsset.referenceCssAssets
699
+ : []),
700
+ ...(Array.isArray(routeAsset?.assets) ? routeAsset.assets : []),
701
+ ];
702
+
703
+ for (const asset of cssAssets) {
704
+ if (typeof asset === 'string' && asset.endsWith('.css')) {
705
+ assets.add(asset);
706
+ }
707
+ }
708
+ }
709
+
710
+ return [...assets];
711
+ }
712
+
713
+ function collectRenderedFederatedExposes(html) {
714
+ const renderedExposes = [];
715
+ const tagPattern =
716
+ /<[^>]*data-modern-(?:boundary-id|mf-expose)=["'][^"']+["'][^>]*>/g;
717
+ const attributePattern =
718
+ /\s(data-modern-(?:boundary-id|mf-expose))=["']([^"']+)["']/g;
719
+
720
+ for (const [tag] of html.matchAll(tagPattern)) {
721
+ const attributes = {};
722
+
723
+ for (const [, name, value] of tag.matchAll(attributePattern)) {
724
+ attributes[name] = value;
725
+ }
726
+
727
+ const boundaryId = attributes['data-modern-boundary-id'];
728
+ const expose = attributes['data-modern-mf-expose'];
729
+
730
+ if (boundaryId && expose) {
731
+ renderedExposes.push({ boundaryId, expose });
732
+ }
733
+ }
734
+
735
+ return renderedExposes;
736
+ }
737
+
738
+ function getRemoteManifestUrl(remote, request) {
739
+ const entry = remote?.entry;
740
+
741
+ if (typeof entry !== 'string' || entry.length === 0) {
742
+ return undefined;
743
+ }
744
+
745
+ return new URL(entry, request.url).toString();
746
+ }
747
+
748
+ function ensureTrailingSlash(value) {
749
+ return value.endsWith('/') ? value : `${value}/`;
750
+ }
751
+
752
+ function isAutomaticPublicPath(publicPath) {
753
+ return publicPath === 'auto' || publicPath === 'auto/';
754
+ }
755
+
756
+ function getRemoteAssetBase(remoteManifest, manifestUrl) {
757
+ const publicPath =
758
+ typeof remoteManifest?.metaData?.publicPath === 'string'
759
+ ? remoteManifest.metaData.publicPath
760
+ : undefined;
761
+ const fallbackBase = new URL('.', manifestUrl).toString();
762
+
763
+ if (!publicPath || isAutomaticPublicPath(publicPath)) {
764
+ return fallbackBase;
765
+ }
766
+
767
+ try {
768
+ return new URL(ensureTrailingSlash(publicPath), manifestUrl).toString();
769
+ } catch {
770
+ return fallbackBase;
771
+ }
772
+ }
773
+
774
+ async function fetchRemoteJson(jsonUrl) {
775
+ if (!remoteJsonPromises.has(jsonUrl)) {
776
+ remoteJsonPromises.set(
777
+ jsonUrl,
778
+ fetch(jsonUrl)
779
+ .then(response => {
780
+ if (!response.ok) {
781
+ remoteJsonPromises.delete(jsonUrl);
782
+
783
+ return {};
784
+ }
785
+
786
+ return response.json().catch(() => {
787
+ remoteJsonPromises.delete(jsonUrl);
788
+
789
+ return {};
790
+ });
791
+ })
792
+ .catch(() => {
793
+ remoteJsonPromises.delete(jsonUrl);
794
+
795
+ return {};
796
+ }),
797
+ );
798
+ }
799
+
800
+ return remoteJsonPromises.get(jsonUrl);
801
+ }
802
+
803
+ function findRemoteExpose(remoteManifest, exposePath) {
804
+ const exposes = Array.isArray(remoteManifest?.exposes)
805
+ ? remoteManifest.exposes
806
+ : [];
807
+ const normalizedExpose = exposePath.replace(/^\.\//u, '');
808
+
809
+ return exposes.find(expose => {
810
+ if (!expose || typeof expose !== 'object') {
811
+ return false;
812
+ }
813
+
814
+ return (
815
+ expose.path === exposePath ||
816
+ expose.path === `./${normalizedExpose}` ||
817
+ expose.name === normalizedExpose
818
+ );
819
+ });
820
+ }
821
+
822
+ function collectCssAssetEntries(assets) {
823
+ const cssAssets = assets?.css;
824
+
825
+ return [
826
+ ...(Array.isArray(cssAssets?.sync) ? cssAssets.sync : []),
827
+ ...(Array.isArray(cssAssets?.async) ? cssAssets.async : []),
828
+ ].filter(asset => typeof asset === 'string' && asset.endsWith('.css'));
829
+ }
830
+
831
+ function collectLocalFragmentCssAssets(html, request) {
832
+ if (request.headers.get('x-modern-js-fragment-request') !== '1') {
833
+ return undefined;
834
+ }
835
+
836
+ const renderedExposes = collectRenderedFederatedExposes(html);
837
+ const localManifest = MODERN_WORKER_MANIFEST.moduleFederation;
838
+ if (!localManifest) {
839
+ return undefined;
840
+ }
841
+ const containerName = localManifest?.name;
842
+ const assets = new Set();
843
+
844
+ for (const { boundaryId, expose } of renderedExposes) {
845
+ if (containerName && boundaryId !== containerName) {
846
+ continue;
847
+ }
848
+
849
+ const localExpose = findRemoteExpose(localManifest, expose);
850
+
851
+ for (const asset of Array.isArray(localExpose?.css)
852
+ ? localExpose.css
853
+ : []) {
854
+ assets.add(asset);
855
+ }
856
+ }
857
+
858
+ return [...assets];
859
+ }
860
+
861
+ async function createLocalFragmentProvenance(html, request) {
862
+ const fragmentRequest = readDistributedSsrFragmentRequest(request);
863
+ if (!fragmentRequest) {
864
+ return undefined;
865
+ }
866
+ const localManifest = MODERN_WORKER_MANIFEST.moduleFederation;
867
+ const deliveryUnit = MODERN_WORKER_MANIFEST.deliveryUnit;
868
+ if (
869
+ !localManifest ||
870
+ localManifest.name !== fragmentRequest.boundaryId ||
871
+ !findRemoteExpose(localManifest, fragmentRequest.expose) ||
872
+ typeof deliveryUnit?.buildMarker !== 'string' ||
873
+ typeof deliveryUnit?.sourceRevision !== 'string' ||
874
+ typeof deliveryUnit?.unitId !== 'string'
875
+ ) {
876
+ return undefined;
877
+ }
878
+ const fragmentHtml = extractRenderedFragmentHtml(
879
+ html,
880
+ fragmentRequest.boundaryId,
881
+ fragmentRequest.expose,
882
+ );
883
+ if (!fragmentHtml) {
884
+ return undefined;
885
+ }
886
+
887
+ return {
888
+ boundaryId: fragmentRequest.boundaryId,
889
+ buildMarker: deliveryUnit.buildMarker,
890
+ digest: await sha256Hex(fragmentHtml),
891
+ expose: fragmentRequest.expose,
892
+ remote: fragmentRequest.remote,
893
+ sourceRevision: deliveryUnit.sourceRevision,
894
+ unitId: deliveryUnit.unitId,
895
+ };
896
+ }
897
+
898
+ function collectRouteManifestCssAssets(routeManifest) {
899
+ const routeAssets = routeManifest?.routeAssets || {};
900
+ const assets = new Set();
901
+
902
+ for (const routeAsset of Object.values(routeAssets)) {
903
+ const cssAssets = [
904
+ ...(Array.isArray(routeAsset?.referenceCssAssets)
905
+ ? routeAsset.referenceCssAssets
906
+ : []),
907
+ ...(Array.isArray(routeAsset?.assets) ? routeAsset.assets : []),
908
+ ];
909
+
910
+ for (const asset of cssAssets) {
911
+ if (typeof asset === 'string' && asset.endsWith('.css')) {
912
+ assets.add(asset);
913
+ }
914
+ }
915
+ }
916
+
917
+ return [...assets];
918
+ }
919
+
920
+ async function collectRenderedRemoteCssHrefs(html, request, env) {
921
+ const bodyStart = html.indexOf('</head>');
922
+ const composedFragmentHrefs = collectStylesheetHrefs(
923
+ bodyStart >= 0 ? html.slice(bodyStart + '</head>'.length) : html,
924
+ );
925
+
926
+ if (composedFragmentHrefs.length > 0) {
927
+ return [...new Set(composedFragmentHrefs)];
928
+ }
929
+
930
+ const renderedExposes = collectRenderedFederatedExposes(html);
931
+
932
+ if (renderedExposes.length === 0) {
933
+ return [];
934
+ }
935
+
936
+ const hostManifest = await readAssetJson('mf-manifest.json', request, env);
937
+ const remotes = Array.isArray(hostManifest?.remotes)
938
+ ? hostManifest.remotes
939
+ : [];
940
+ const remoteByBoundary = new Map();
941
+ const hrefs = new Set();
942
+
943
+ for (const remote of remotes) {
944
+ if (typeof remote?.alias === 'string') {
945
+ remoteByBoundary.set(remote.alias, remote);
946
+ }
947
+
948
+ if (typeof remote?.federationContainerName === 'string') {
949
+ remoteByBoundary.set(remote.federationContainerName, remote);
950
+ }
951
+ }
952
+
953
+ await Promise.all(
954
+ renderedExposes.map(async ({ boundaryId, expose }) => {
955
+ const remote = remoteByBoundary.get(boundaryId);
956
+ const manifestUrl = remote
957
+ ? getRemoteManifestUrl(remote, request)
958
+ : undefined;
959
+
960
+ if (!manifestUrl) {
961
+ return;
962
+ }
963
+
964
+ const remoteManifest = await fetchRemoteJson(manifestUrl);
965
+ const remoteExpose = findRemoteExpose(remoteManifest, expose);
966
+ const publicPath = getRemoteAssetBase(remoteManifest, manifestUrl);
967
+ const remoteRouteManifest = await fetchRemoteJson(
968
+ new URL('routes-manifest.json', publicPath).toString(),
969
+ );
970
+
971
+ for (const asset of collectCssAssetEntries(remoteExpose?.assets)) {
972
+ hrefs.add(new URL(asset, publicPath).toString());
973
+ }
974
+
975
+ for (const asset of collectRouteManifestCssAssets(remoteRouteManifest)) {
976
+ hrefs.add(new URL(asset, publicPath).toString());
977
+ }
978
+ }),
979
+ );
980
+
981
+ return [...hrefs];
982
+ }
983
+
984
+ function escapeAttribute(value) {
985
+ return String(value)
986
+ .replace(/&/g, '&amp;')
987
+ .replace(/"/g, '&quot;')
988
+ .replace(/</g, '&lt;')
989
+ .replace(/>/g, '&gt;');
990
+ }
991
+
992
+ function isAbsoluteUrl(value) {
993
+ return /^[a-z][a-z\d+.-]*:/iu.test(value);
994
+ }
995
+
996
+ function toRouteCssHtmlHref(asset) {
997
+ if (isAbsoluteUrl(asset) || asset.startsWith('/')) {
998
+ return asset;
999
+ }
1000
+
1001
+ return `/${asset.replace(/^\/+/u, '')}`;
1002
+ }
1003
+
1004
+ async function withRouteCssLinks(
1005
+ response,
1006
+ route,
1007
+ routeManifest,
1008
+ request,
1009
+ env,
1010
+ distributedSsrFragmentCssHrefs,
1011
+ ) {
1012
+ const contentType = response.headers.get('content-type') || '';
1013
+
1014
+ if (!contentType.includes('text/html')) {
1015
+ return response;
1016
+ }
1017
+
1018
+ const resolvedDistributedSsrFragmentCssHrefs = await Promise.resolve(
1019
+ distributedSsrFragmentCssHrefs ?? [],
1020
+ );
1021
+ if (
1022
+ response.body !== null &&
1023
+ resolvedDistributedSsrFragmentCssHrefs.length > 0 &&
1024
+ readDistributedSsrFragmentRequest(request) === undefined
1025
+ ) {
1026
+ const headers = new Headers(response.headers);
1027
+ const cssEntries = [
1028
+ ...collectRouteCssAssets(route, routeManifest).map(asset => {
1029
+ const href = toRouteCssHtmlHref(asset);
1030
+
1031
+ return {
1032
+ href,
1033
+ preloadHref: new URL(href, request.url).toString(),
1034
+ reactResource: false,
1035
+ };
1036
+ }),
1037
+ ...resolvedDistributedSsrFragmentCssHrefs.map(href => ({
1038
+ href,
1039
+ preloadHref: new URL(href, request.url).toString(),
1040
+ reactResource: true,
1041
+ })),
1042
+ ];
1043
+ const seenCssHrefs = new Set();
1044
+ const uniqueCssEntries = cssEntries.filter(entry => {
1045
+ if (seenCssHrefs.has(entry.preloadHref)) {
1046
+ return false;
1047
+ }
1048
+
1049
+ seenCssHrefs.add(entry.preloadHref);
1050
+ return true;
1051
+ });
1052
+ const requestOrigin = new URL(request.url).origin;
1053
+ for (const { preloadHref } of uniqueCssEntries) {
1054
+ const preloadUrl = new URL(preloadHref);
1055
+ const preloadReference =
1056
+ preloadUrl.origin === requestOrigin
1057
+ ? `${preloadUrl.pathname}${preloadUrl.search}`
1058
+ : preloadUrl.toString();
1059
+
1060
+ headers.append('link', `<${preloadReference}>; rel=preload; as=style`);
1061
+ }
1062
+ headers.delete('content-length');
1063
+
1064
+ return new Response(
1065
+ createStylesheetLinkStream(response.body, uniqueCssEntries, request.url),
1066
+ {
1067
+ headers,
1068
+ status: response.status,
1069
+ statusText: response.statusText,
1070
+ },
1071
+ );
1072
+ }
1073
+
1074
+ const html = dedupeStylesheetLinks(
1075
+ await response.text(),
1076
+ undefined,
1077
+ request.url,
1078
+ );
1079
+ const headers = new Headers(response.headers);
1080
+ const localFragmentCssAssets = collectLocalFragmentCssAssets(html, request);
1081
+ const localFragmentProvenance = await createLocalFragmentProvenance(
1082
+ html,
1083
+ request,
1084
+ );
1085
+
1086
+ if (localFragmentCssAssets !== undefined) {
1087
+ headers.set(
1088
+ DISTRIBUTED_SSR_CSS_HEADER,
1089
+ JSON.stringify(localFragmentCssAssets),
1090
+ );
1091
+ }
1092
+ if (localFragmentProvenance !== undefined) {
1093
+ headers.set(
1094
+ DISTRIBUTED_SSR_PROVENANCE_HEADER,
1095
+ encodeURIComponent(JSON.stringify(localFragmentProvenance)),
1096
+ );
1097
+ }
1098
+
1099
+ const cssEntries = [
1100
+ ...collectRouteCssAssets(route, routeManifest).map(asset => {
1101
+ const href = toRouteCssHtmlHref(asset);
1102
+
1103
+ return {
1104
+ href,
1105
+ preloadHref: new URL(href, request.url).toString(),
1106
+ reactResource: false,
1107
+ };
1108
+ }),
1109
+ ...(resolvedDistributedSsrFragmentCssHrefs.length > 0
1110
+ ? resolvedDistributedSsrFragmentCssHrefs
1111
+ : await collectRenderedRemoteCssHrefs(html, request, env)
1112
+ ).map(href => ({
1113
+ href,
1114
+ preloadHref: new URL(href, request.url).toString(),
1115
+ reactResource: true,
1116
+ })),
1117
+ ];
1118
+
1119
+ const requestOrigin = new URL(request.url).origin;
1120
+ if (cssEntries.length === 0) {
1121
+ return new Response(html, {
1122
+ headers,
1123
+ status: response.status,
1124
+ statusText: response.statusText,
1125
+ });
1126
+ }
1127
+
1128
+ const seenCssHrefs = new Set();
1129
+ const uniqueCssEntries = cssEntries.filter(entry => {
1130
+ if (seenCssHrefs.has(entry.preloadHref)) {
1131
+ return false;
1132
+ }
1133
+
1134
+ seenCssHrefs.add(entry.preloadHref);
1135
+ return true;
1136
+ });
1137
+ for (const { href, preloadHref } of uniqueCssEntries) {
1138
+ const preloadUrl = new URL(preloadHref);
1139
+ const preloadReference =
1140
+ preloadUrl.origin === requestOrigin
1141
+ ? `${preloadUrl.pathname}${preloadUrl.search}`
1142
+ : preloadUrl.toString();
1143
+
1144
+ headers.append('link', `<${preloadReference}>; rel=preload; as=style`);
1145
+ }
1146
+
1147
+ const links = uniqueCssEntries
1148
+ .filter(
1149
+ ({ href, preloadHref }) =>
1150
+ !html.includes(href) && !html.includes(preloadHref),
1151
+ )
1152
+ .map(({ href, reactResource }) =>
1153
+ reactResource
1154
+ ? `<link href="${escapeAttribute(href)}" rel="stylesheet" type="text/css" data-precedence="default">`
1155
+ : `<link rel="stylesheet" href="${escapeAttribute(href)}">`,
1156
+ );
1157
+
1158
+ if (links.length === 0 || !html.includes('</head>')) {
1159
+ return new Response(html, {
1160
+ headers,
1161
+ status: response.status,
1162
+ statusText: response.statusText,
1163
+ });
1164
+ }
1165
+
1166
+ return new Response(html.replace('</head>', `${links.join('')}</head>`), {
1167
+ headers,
1168
+ status: response.status,
1169
+ statusText: response.statusText,
1170
+ });
1171
+ }
1172
+
1173
+ async function getRequestHandlerOptions(route, request, env) {
1174
+ const [htmlTemplate, routeManifest, loadableStats] = await Promise.all([
1175
+ readAssetText(route.entryPath, request, env),
1176
+ readAssetJson(MODERN_WORKER_MANIFEST.resources.routeManifest, request, env),
1177
+ readAssetJson(MODERN_WORKER_MANIFEST.resources.loadableStats, request, env),
1178
+ ]);
1179
+ const distributedSsrFragments = createDistributedSsrFragmentContext(
1180
+ request,
1181
+ env,
1182
+ );
1183
+ const distributedSsrFragmentRequest =
1184
+ readDistributedSsrFragmentRequest(request);
1185
+
1186
+ return createRequestHandlerOptions({
1187
+ route,
1188
+ htmlTemplate: htmlTemplate || '',
1189
+ routeManifest,
1190
+ loadableStats,
1191
+ distributedSsrFragments,
1192
+ distributedSsrFragmentRequest,
1193
+ });
1194
+ }