vinext 0.0.53 → 0.0.54

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 (188) hide show
  1. package/dist/build/inline-css.d.ts +7 -0
  2. package/dist/build/inline-css.js +50 -0
  3. package/dist/build/inline-css.js.map +1 -0
  4. package/dist/build/prerender.js +2 -1
  5. package/dist/build/prerender.js.map +1 -1
  6. package/dist/check.js +4 -0
  7. package/dist/check.js.map +1 -1
  8. package/dist/client/navigation-runtime.d.ts +2 -1
  9. package/dist/client/navigation-runtime.js.map +1 -1
  10. package/dist/client/window-next.d.ts +7 -0
  11. package/dist/client/window-next.js.map +1 -1
  12. package/dist/config/next-config.d.ts +83 -1
  13. package/dist/config/next-config.js +131 -2
  14. package/dist/config/next-config.js.map +1 -1
  15. package/dist/deploy.js +13 -0
  16. package/dist/deploy.js.map +1 -1
  17. package/dist/entries/app-browser-entry.d.ts +11 -1
  18. package/dist/entries/app-browser-entry.js +16 -6
  19. package/dist/entries/app-browser-entry.js.map +1 -1
  20. package/dist/entries/app-rsc-entry.d.ts +8 -1
  21. package/dist/entries/app-rsc-entry.js +18 -5
  22. package/dist/entries/app-rsc-entry.js.map +1 -1
  23. package/dist/entries/app-rsc-manifest.d.ts +21 -1
  24. package/dist/entries/app-rsc-manifest.js +6 -4
  25. package/dist/entries/app-rsc-manifest.js.map +1 -1
  26. package/dist/entries/pages-client-entry.d.ts +4 -1
  27. package/dist/entries/pages-client-entry.js +18 -2
  28. package/dist/entries/pages-client-entry.js.map +1 -1
  29. package/dist/entries/pages-server-entry.js +82 -4
  30. package/dist/entries/pages-server-entry.js.map +1 -1
  31. package/dist/entries/runtime-entry-module.d.ts +1 -10
  32. package/dist/entries/runtime-entry-module.js +2 -12
  33. package/dist/entries/runtime-entry-module.js.map +1 -1
  34. package/dist/index.js +63 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/plugins/remove-console.d.ts +16 -0
  37. package/dist/plugins/remove-console.js +176 -0
  38. package/dist/plugins/remove-console.js.map +1 -0
  39. package/dist/routing/app-route-graph.d.ts +24 -1
  40. package/dist/routing/app-route-graph.js +52 -4
  41. package/dist/routing/app-route-graph.js.map +1 -1
  42. package/dist/routing/app-router.d.ts +2 -2
  43. package/dist/routing/app-router.js +2 -2
  44. package/dist/routing/app-router.js.map +1 -1
  45. package/dist/routing/file-matcher.d.ts +21 -1
  46. package/dist/routing/file-matcher.js +39 -1
  47. package/dist/routing/file-matcher.js.map +1 -1
  48. package/dist/routing/pages-router.d.ts +1 -1
  49. package/dist/routing/pages-router.js +10 -3
  50. package/dist/routing/pages-router.js.map +1 -1
  51. package/dist/server/api-handler.js +1 -1
  52. package/dist/server/app-browser-entry.js +25 -16
  53. package/dist/server/app-browser-entry.js.map +1 -1
  54. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  55. package/dist/server/app-browser-navigation-controller.js +4 -0
  56. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  57. package/dist/server/app-elements-wire.d.ts +13 -4
  58. package/dist/server/app-elements-wire.js +10 -1
  59. package/dist/server/app-elements-wire.js.map +1 -1
  60. package/dist/server/app-elements.d.ts +2 -2
  61. package/dist/server/app-elements.js +2 -2
  62. package/dist/server/app-elements.js.map +1 -1
  63. package/dist/server/app-fallback-renderer.d.ts +15 -5
  64. package/dist/server/app-fallback-renderer.js +10 -4
  65. package/dist/server/app-fallback-renderer.js.map +1 -1
  66. package/dist/server/app-inline-css-client.d.ts +7 -0
  67. package/dist/server/app-inline-css-client.js +37 -0
  68. package/dist/server/app-inline-css-client.js.map +1 -0
  69. package/dist/server/app-page-boundary.d.ts +21 -1
  70. package/dist/server/app-page-boundary.js +28 -3
  71. package/dist/server/app-page-boundary.js.map +1 -1
  72. package/dist/server/app-page-cache.d.ts +7 -3
  73. package/dist/server/app-page-cache.js +7 -7
  74. package/dist/server/app-page-cache.js.map +1 -1
  75. package/dist/server/app-page-dispatch.d.ts +10 -1
  76. package/dist/server/app-page-dispatch.js +126 -79
  77. package/dist/server/app-page-dispatch.js.map +1 -1
  78. package/dist/server/app-page-element-builder.js +12 -28
  79. package/dist/server/app-page-element-builder.js.map +1 -1
  80. package/dist/server/app-page-render-identity.d.ts +22 -0
  81. package/dist/server/app-page-render-identity.js +42 -0
  82. package/dist/server/app-page-render-identity.js.map +1 -0
  83. package/dist/server/app-page-render.d.ts +8 -1
  84. package/dist/server/app-page-render.js +4 -1
  85. package/dist/server/app-page-render.js.map +1 -1
  86. package/dist/server/app-page-request.d.ts +6 -3
  87. package/dist/server/app-page-request.js +5 -2
  88. package/dist/server/app-page-request.js.map +1 -1
  89. package/dist/server/app-page-response.js +2 -2
  90. package/dist/server/app-page-response.js.map +1 -1
  91. package/dist/server/app-page-route-wiring.d.ts +15 -0
  92. package/dist/server/app-page-route-wiring.js +7 -5
  93. package/dist/server/app-page-route-wiring.js.map +1 -1
  94. package/dist/server/app-page-stream.d.ts +11 -0
  95. package/dist/server/app-page-stream.js +1 -0
  96. package/dist/server/app-page-stream.js.map +1 -1
  97. package/dist/server/app-route-handler-response.js +37 -5
  98. package/dist/server/app-route-handler-response.js.map +1 -1
  99. package/dist/server/app-rsc-handler.d.ts +14 -3
  100. package/dist/server/app-rsc-handler.js +45 -5
  101. package/dist/server/app-rsc-handler.js.map +1 -1
  102. package/dist/server/app-rsc-request-normalization.d.ts +2 -1
  103. package/dist/server/app-rsc-request-normalization.js +3 -2
  104. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  105. package/dist/server/app-server-action-execution.d.ts +21 -3
  106. package/dist/server/app-server-action-execution.js +42 -7
  107. package/dist/server/app-server-action-execution.js.map +1 -1
  108. package/dist/server/app-ssr-entry.d.ts +6 -0
  109. package/dist/server/app-ssr-entry.js +22 -7
  110. package/dist/server/app-ssr-entry.js.map +1 -1
  111. package/dist/server/app-ssr-error-meta.js +3 -3
  112. package/dist/server/app-ssr-error-meta.js.map +1 -1
  113. package/dist/server/app-ssr-stream.d.ts +2 -1
  114. package/dist/server/app-ssr-stream.js +176 -31
  115. package/dist/server/app-ssr-stream.js.map +1 -1
  116. package/dist/server/client-trace-metadata.d.ts +31 -0
  117. package/dist/server/client-trace-metadata.js +83 -0
  118. package/dist/server/client-trace-metadata.js.map +1 -0
  119. package/dist/server/cookie-utils.d.ts +13 -0
  120. package/dist/server/cookie-utils.js +20 -0
  121. package/dist/server/cookie-utils.js.map +1 -0
  122. package/dist/server/dev-server.d.ts +8 -1
  123. package/dist/server/dev-server.js +34 -5
  124. package/dist/server/dev-server.js.map +1 -1
  125. package/dist/server/html.d.ts +2 -1
  126. package/dist/server/html.js +6 -1
  127. package/dist/server/html.js.map +1 -1
  128. package/dist/server/isr-cache.d.ts +7 -5
  129. package/dist/server/isr-cache.js +17 -6
  130. package/dist/server/isr-cache.js.map +1 -1
  131. package/dist/server/middleware-runtime.js +1 -2
  132. package/dist/server/middleware-runtime.js.map +1 -1
  133. package/dist/server/pages-document-initial-props.d.ts +7 -0
  134. package/dist/server/pages-document-initial-props.js +14 -0
  135. package/dist/server/pages-document-initial-props.js.map +1 -0
  136. package/dist/server/pages-page-data.js +3 -0
  137. package/dist/server/pages-page-data.js.map +1 -1
  138. package/dist/server/pages-page-method.d.ts +48 -0
  139. package/dist/server/pages-page-method.js +19 -0
  140. package/dist/server/pages-page-method.js.map +1 -0
  141. package/dist/server/pages-page-response.d.ts +6 -0
  142. package/dist/server/pages-page-response.js +10 -3
  143. package/dist/server/pages-page-response.js.map +1 -1
  144. package/dist/server/pages-serializable-props.d.ts +25 -0
  145. package/dist/server/pages-serializable-props.js +69 -0
  146. package/dist/server/pages-serializable-props.js.map +1 -0
  147. package/dist/server/prod-server.js +3 -0
  148. package/dist/server/prod-server.js.map +1 -1
  149. package/dist/server/server-action-not-found.js +3 -2
  150. package/dist/server/server-action-not-found.js.map +1 -1
  151. package/dist/server/static-file-cache.js +2 -1
  152. package/dist/server/static-file-cache.js.map +1 -1
  153. package/dist/shims/app-router-scroll-state.d.ts +4 -2
  154. package/dist/shims/app-router-scroll-state.js +16 -3
  155. package/dist/shims/app-router-scroll-state.js.map +1 -1
  156. package/dist/shims/app-router-scroll.d.ts +16 -2
  157. package/dist/shims/app-router-scroll.js +18 -3
  158. package/dist/shims/app-router-scroll.js.map +1 -1
  159. package/dist/shims/cache.d.ts +6 -0
  160. package/dist/shims/cache.js +7 -0
  161. package/dist/shims/cache.js.map +1 -1
  162. package/dist/shims/error.js +3 -0
  163. package/dist/shims/error.js.map +1 -1
  164. package/dist/shims/headers.d.ts +7 -0
  165. package/dist/shims/headers.js +9 -1
  166. package/dist/shims/headers.js.map +1 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +37 -0
  168. package/dist/shims/internal/app-route-detection.js +69 -0
  169. package/dist/shims/internal/app-route-detection.js.map +1 -0
  170. package/dist/shims/link.d.ts +18 -2
  171. package/dist/shims/link.js +70 -6
  172. package/dist/shims/link.js.map +1 -1
  173. package/dist/shims/metadata.d.ts +7 -6
  174. package/dist/shims/metadata.js +9 -5
  175. package/dist/shims/metadata.js.map +1 -1
  176. package/dist/shims/navigation.d.ts +1 -2
  177. package/dist/shims/navigation.js +63 -12
  178. package/dist/shims/navigation.js.map +1 -1
  179. package/dist/shims/router.d.ts +5 -0
  180. package/dist/shims/router.js +14 -4
  181. package/dist/shims/router.js.map +1 -1
  182. package/dist/shims/script.d.ts +11 -1
  183. package/dist/shims/script.js +75 -6
  184. package/dist/shims/script.js.map +1 -1
  185. package/dist/utils/path.d.ts +13 -0
  186. package/dist/utils/path.js +16 -0
  187. package/dist/utils/path.js.map +1 -0
  188. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "../client/navigation-runtime.js";
2
- import { createInlineScriptTag, safeJsonStringify } from "./html.js";
2
+ import { createInlineScriptTag, escapeHtmlAttr, htmlTokenListContains, safeJsonStringify } from "./html.js";
3
3
  import { bytesToBase64, concatUint8Arrays } from "./app-rsc-embedded-chunks.js";
4
4
  //#region src/server/app-ssr-stream.ts
5
5
  const NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION)})]`;
@@ -86,6 +86,130 @@ function createRscEmbedTransform(embedStream, scriptNonce) {
86
86
  function fixPreloadAs(html) {
87
87
  return html.replace(/<link(?=[^>]*\srel="preload")[^>]*>/g, (tag) => tag.replace(" as=\"stylesheet\"", " as=\"style\""));
88
88
  }
89
+ const LINK_TAG_RE = /<link\b[^>]*>/gi;
90
+ const HTML_REWRITE_EXCLUDED_REGION_RE = /<!--[\s\S]*?-->|<(script|style|textarea|title)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
91
+ const HTML_REWRITE_EXCLUDED_REGION_START_RE = /<!--|<(script|style|textarea|title)\b[^>]*>/gi;
92
+ function getHtmlAttribute(tag, name) {
93
+ const attrRe = /\s([^\s"'=<>`]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
94
+ let match;
95
+ while ((match = attrRe.exec(tag)) !== null) {
96
+ if (match[1]?.toLowerCase() !== name.toLowerCase()) continue;
97
+ return match[2] ?? match[3] ?? match[4] ?? "";
98
+ }
99
+ return null;
100
+ }
101
+ function htmlAttributeHasToken(tag, name, token) {
102
+ return htmlTokenListContains(getHtmlAttribute(tag, name), token);
103
+ }
104
+ function getInlineCss(manifest, href) {
105
+ if (Object.prototype.hasOwnProperty.call(manifest, href)) return manifest[href] ?? "";
106
+ try {
107
+ const pathname = new URL(href).pathname;
108
+ if (Object.prototype.hasOwnProperty.call(manifest, pathname)) return manifest[pathname] ?? "";
109
+ } catch {}
110
+ return null;
111
+ }
112
+ const TRAILING_LINK_OPEN_RE = /<link/gi;
113
+ function splitTrailingIncompleteLinkTag(html) {
114
+ TRAILING_LINK_OPEN_RE.lastIndex = 0;
115
+ let lastIndex = -1;
116
+ let match;
117
+ while ((match = TRAILING_LINK_OPEN_RE.exec(html)) !== null) lastIndex = match.index;
118
+ if (lastIndex === -1) return {
119
+ complete: html,
120
+ trailing: ""
121
+ };
122
+ if (html.indexOf(">", lastIndex) !== -1) return {
123
+ complete: html,
124
+ trailing: ""
125
+ };
126
+ return {
127
+ complete: html.slice(0, lastIndex),
128
+ trailing: html.slice(lastIndex)
129
+ };
130
+ }
131
+ function findTrailingOpenHtmlRewriteExcludedRegionStart(html) {
132
+ let match;
133
+ HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = 0;
134
+ while ((match = HTML_REWRITE_EXCLUDED_REGION_START_RE.exec(html)) !== null) {
135
+ const start = match.index;
136
+ if (match[0] === "<!--") {
137
+ const close = html.indexOf("-->", HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex);
138
+ if (close === -1) return start;
139
+ HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = close + 3;
140
+ continue;
141
+ }
142
+ const tagName = match[1]?.toLowerCase();
143
+ if (!tagName) continue;
144
+ const close = new RegExp(`</${tagName}\\s*>`, "i").exec(html.slice(HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex));
145
+ if (!close) return start;
146
+ HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex += close.index + close[0].length;
147
+ }
148
+ return null;
149
+ }
150
+ function splitTrailingInlineCssRewriteBoundary(html) {
151
+ const linkSplit = splitTrailingIncompleteLinkTag(html);
152
+ const incompleteLinkStart = linkSplit.trailing ? linkSplit.complete.length : null;
153
+ const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(html);
154
+ const trailingStart = incompleteLinkStart === null ? openRegionStart : openRegionStart === null ? incompleteLinkStart : Math.min(incompleteLinkStart, openRegionStart);
155
+ if (trailingStart === null) return {
156
+ complete: html,
157
+ trailing: ""
158
+ };
159
+ return {
160
+ complete: html.slice(0, trailingStart),
161
+ trailing: html.slice(trailingStart)
162
+ };
163
+ }
164
+ function escapeStyleText(css) {
165
+ return css.replace(/<\/style/gi, "<\\/style");
166
+ }
167
+ const CSS_PREPEND_UNSAFE_PREAMBLE_RE = /^\uFEFF?(?:\s|\/\*[\s\S]*?\*\/)*@(charset|import|layer|namespace)\b/i;
168
+ function canPrependCss(css) {
169
+ return !CSS_PREPEND_UNSAFE_PREAMBLE_RE.test(css);
170
+ }
171
+ function replaceLinkTags(html, replaceLinkTag) {
172
+ LINK_TAG_RE.lastIndex = 0;
173
+ return html.replace(LINK_TAG_RE, replaceLinkTag);
174
+ }
175
+ function replaceLinkTagsOutsideRawText(html, replaceLinkTag) {
176
+ let rewritten = "";
177
+ let cursor = 0;
178
+ let match;
179
+ HTML_REWRITE_EXCLUDED_REGION_RE.lastIndex = 0;
180
+ while ((match = HTML_REWRITE_EXCLUDED_REGION_RE.exec(html)) !== null) {
181
+ rewritten += replaceLinkTags(html.slice(cursor, match.index), replaceLinkTag);
182
+ rewritten += match[0];
183
+ cursor = match.index + match[0].length;
184
+ }
185
+ const tail = html.slice(cursor);
186
+ const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(tail);
187
+ if (openRegionStart === null) return rewritten + replaceLinkTags(tail, replaceLinkTag);
188
+ return rewritten + replaceLinkTags(tail.slice(0, openRegionStart), replaceLinkTag) + tail.slice(openRegionStart);
189
+ }
190
+ function rewriteInlineCssStylesheetLinks(html, inlineCssManifest, prependCss, ssrScriptNonce) {
191
+ if (!inlineCssManifest || Object.keys(inlineCssManifest).length === 0) return {
192
+ html,
193
+ consumedPrependCss: false
194
+ };
195
+ let consumedPrependCss = false;
196
+ return {
197
+ html: replaceLinkTagsOutsideRawText(html, (tag) => {
198
+ if (!htmlAttributeHasToken(tag, "rel", "stylesheet")) return tag;
199
+ const href = getHtmlAttribute(tag, "href");
200
+ const precedence = getHtmlAttribute(tag, "data-precedence") ?? getHtmlAttribute(tag, "precedence");
201
+ if (!href || !precedence) return tag;
202
+ const css = getInlineCss(inlineCssManifest, href);
203
+ if (css === null) return tag;
204
+ const effectiveNonce = getHtmlAttribute(tag, "nonce") ?? ssrScriptNonce;
205
+ const nonceAttr = effectiveNonce ? ` nonce="${escapeHtmlAttr(effectiveNonce)}"` : "";
206
+ const cssPrefix = !consumedPrependCss && prependCss.length > 0 && canPrependCss(css) ? `${prependCss}\n` : "";
207
+ consumedPrependCss ||= cssPrefix.length > 0;
208
+ return `<style data-vinext-inline-css${nonceAttr} data-precedence="${escapeHtmlAttr(precedence)}" data-href="${escapeHtmlAttr(href)}">${escapeStyleText(cssPrefix + css)}</style>`;
209
+ }),
210
+ consumedPrependCss
211
+ };
212
+ }
89
213
  /**
90
214
  * Match the `<head ...>` opening tag in a chunk. Matches both bare `<head>`
91
215
  * and `<head class="foo">` shapes. Used to splice HTML immediately after the
@@ -120,18 +244,25 @@ const HEAD_OPEN_RE = /<head\b[^>]*>/;
120
244
  * to no-op and let the user-rendered Script (in its source-order
121
245
  * position) ship as-is.
122
246
  */
123
- function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadOpenHTML = "") {
247
+ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadOpenHTML = "", inlineCssManifest, inlineCssPrependCss = "", inlineCssPrependFallbackHTML = "", inlineCssScriptNonce) {
124
248
  const decoder = new TextDecoder();
125
249
  const encoder = new TextEncoder();
126
250
  const insertsPerFlush = typeof injectHTML === "function";
127
251
  let injected = false;
128
252
  let preHeadInjected = false;
129
253
  let buffered = [];
254
+ let pendingHtml = "";
130
255
  let timeoutId = null;
256
+ const hasInlineCssManifest = inlineCssManifest !== void 0 && Object.keys(inlineCssManifest).length > 0;
131
257
  const readInsertion = () => typeof injectHTML === "function" ? injectHTML() : injectHTML;
132
258
  const readPreHeadInsertion = () => typeof injectAfterHeadOpenHTML === "function" ? injectAfterHeadOpenHTML() : injectAfterHeadOpenHTML;
259
+ const readInlineCssPrependFallback = () => {
260
+ if (!inlineCssPrependCss || !inlineCssPrependFallbackHTML) return "";
261
+ inlineCssPrependCss = "";
262
+ return inlineCssPrependFallbackHTML;
263
+ };
133
264
  const emitInsertion = (controller) => {
134
- const insertion = readInsertion();
265
+ const insertion = readInlineCssPrependFallback() + readInsertion();
135
266
  if (insertion) controller.enqueue(encoder.encode(insertion));
136
267
  };
137
268
  /**
@@ -140,11 +271,11 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
140
271
  * rewritten chunk and a flag indicating whether the splice happened, so the
141
272
  * caller can mark `preHeadInjected` and stop scanning further chunks.
142
273
  *
143
- * NOTE: This is called only when `<head ...>` lies fully inside `chunk`
144
- * we deliberately avoid stitching across chunk boundaries because doing so
145
- * would force the transform to hold output until it had seen `<head ...>`,
146
- * which both delays TTFB and complicates the existing `</head>` injection
147
- * path. In practice React Fizz emits the opening shell as a single chunk.
274
+ * NOTE: This is called only when `<head ...>` lies fully inside the current
275
+ * tick-buffered batch. We deliberately avoid retaining arbitrary output until
276
+ * a future chunk completes `<head ...>`, which would delay TTFB and complicate
277
+ * the existing `</head>` injection path. In practice React Fizz emits the
278
+ * opening shell as a single batch.
148
279
  */
149
280
  const spliceAfterHeadOpen = (chunk) => {
150
281
  if (preHeadInjected) return {
@@ -167,35 +298,47 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
167
298
  spliced: true
168
299
  };
169
300
  };
170
- const flushBuffered = (controller) => {
171
- if (buffered.length === 0) return;
301
+ const flushBuffered = (controller, final = false) => {
302
+ if (buffered.length === 0 && !pendingHtml) return;
303
+ const rawHtml = pendingHtml + buffered.join("");
304
+ buffered = [];
305
+ pendingHtml = "";
306
+ const split = final || !hasInlineCssManifest ? {
307
+ complete: rawHtml,
308
+ trailing: ""
309
+ } : splitTrailingInlineCssRewriteBoundary(rawHtml);
310
+ if (split.trailing) pendingHtml = split.trailing;
311
+ if (!split.complete) return;
172
312
  if (injected && insertsPerFlush) emitInsertion(controller);
173
- for (const chunk of buffered) {
174
- let working = chunk;
175
- if (!preHeadInjected) {
176
- const result = spliceAfterHeadOpen(working);
177
- if (result.spliced) {
178
- working = result.chunk;
179
- preHeadInjected = true;
180
- }
313
+ const preparedHtml = fixPreloadAs(split.complete);
314
+ const inlineCssResult = hasInlineCssManifest ? rewriteInlineCssStylesheetLinks(preparedHtml, inlineCssManifest, inlineCssPrependCss, inlineCssScriptNonce) : {
315
+ html: preparedHtml,
316
+ consumedPrependCss: false
317
+ };
318
+ if (inlineCssResult.consumedPrependCss) inlineCssPrependCss = "";
319
+ let working = inlineCssResult.html;
320
+ if (!preHeadInjected) {
321
+ const result = spliceAfterHeadOpen(working);
322
+ if (result.spliced) {
323
+ working = result.chunk;
324
+ preHeadInjected = true;
181
325
  }
182
- if (!injected) {
183
- const headEnd = working.indexOf("</head>");
184
- if (headEnd !== -1) {
185
- const before = working.slice(0, headEnd);
186
- const after = working.slice(headEnd);
187
- controller.enqueue(encoder.encode(before + readInsertion() + after));
188
- injected = true;
189
- continue;
190
- }
326
+ }
327
+ if (!injected) {
328
+ const headEnd = working.indexOf("</head>");
329
+ if (headEnd !== -1) {
330
+ const before = working.slice(0, headEnd);
331
+ const after = working.slice(headEnd);
332
+ controller.enqueue(encoder.encode(before + readInlineCssPrependFallback() + readInsertion() + after));
333
+ injected = true;
334
+ return;
191
335
  }
192
- controller.enqueue(encoder.encode(working));
193
336
  }
194
- buffered = [];
337
+ controller.enqueue(encoder.encode(working));
195
338
  };
196
339
  return new TransformStream({
197
340
  transform(chunk, controller) {
198
- buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));
341
+ buffered.push(decoder.decode(chunk, { stream: true }));
199
342
  if (timeoutId !== null) return;
200
343
  timeoutId = setTimeout(() => {
201
344
  try {
@@ -211,7 +354,9 @@ function createTickBufferedTransform(rscEmbed, injectHTML = "", injectAfterHeadO
211
354
  clearTimeout(timeoutId);
212
355
  timeoutId = null;
213
356
  }
214
- flushBuffered(controller);
357
+ const remainder = decoder.decode();
358
+ if (remainder) buffered.push(remainder);
359
+ flushBuffered(controller, true);
215
360
  if (!injected) {
216
361
  emitInsertion(controller);
217
362
  injected = true;
@@ -1 +1 @@
1
- {"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import { createInlineScriptTag, safeJsonStringify } from \"./html.js\";\nimport {\n bytesToBase64,\n concatUint8Arrays,\n RSC_EMBEDDED_BINARY_CHUNK,\n type RscEmbeddedChunk,\n} from \"./app-rsc-embedded-chunks.js\";\nimport { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from \"../client/navigation-runtime.js\";\n\ntype RscEmbedTransform = {\n flush(): string;\n finalize(): Promise<string>;\n /** Resolves when all raw bytes from the embed stream have been read. */\n getRawBuffer(): Promise<ArrayBuffer>;\n};\n\ntype HtmlInsertion = string | (() => string);\n\nconst NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(\n NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION,\n)})]`;\n\nexport function navigationRuntimeRscBootstrapExpression(): string {\n return `((${NAVIGATION_RUNTIME_REFERENCE}??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]})`;\n}\n\nexport function createNavigationRuntimeRscMetadataScript(\n params: Record<string, string | string[]>,\n nav: { pathname: string; searchParams: [string, string][] },\n): string {\n return (\n \"Object.assign(\" +\n navigationRuntimeRscBootstrapExpression() +\n \",{params:\" +\n safeJsonStringify(params) +\n \",nav:\" +\n safeJsonStringify(nav) +\n \"})\"\n );\n}\n\nfunction createNavigationRuntimeRscChunkScript(chunk: RscEmbeddedChunk): string {\n return navigationRuntimeRscBootstrapExpression() + \".rsc.push(\" + safeJsonStringify(chunk) + \")\";\n}\n\nfunction createNavigationRuntimeRscDoneScript(): string {\n return navigationRuntimeRscBootstrapExpression() + \".done=true\";\n}\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n scriptNonce?: string,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n let pendingChunks: RscEmbeddedChunk[] = [];\n const rawChunks: Uint8Array[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n // Accumulate raw bytes BEFORE fixFlightHints so the cache stores\n // unmodified RSC data. The embed script path below applies fixes.\n rawChunks.push(result.value);\n try {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const text = decoder.decode(result.value);\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n } catch {\n pendingChunks.push([RSC_EMBEDDED_BINARY_CHUNK, bytesToBase64(result.value)]);\n }\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n throw error;\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts += createInlineScriptTag(createNavigationRuntimeRscChunkScript(chunk), scriptNonce);\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += createInlineScriptTag(createNavigationRuntimeRscDoneScript(), scriptNonce);\n return scripts;\n },\n\n async getRawBuffer(): Promise<ArrayBuffer> {\n await pumpPromise;\n const buffer = concatUint8Arrays(rawChunks);\n rawChunks.length = 0;\n return buffer.buffer;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) =>\n tag.replace(' as=\"stylesheet\"', ' as=\"style\"'),\n );\n}\n\n/**\n * Match the `<head ...>` opening tag in a chunk. Matches both bare `<head>`\n * and `<head class=\"foo\">` shapes. Used to splice HTML immediately after the\n * opening tag so injected content runs before any React-emitted resource\n * hints (stylesheets, modulepreloads) that React Float hoists into `<head>`.\n */\nconst HEAD_OPEN_RE = /<head\\b[^>]*>/;\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n *\n * Two insertion points are supported in tandem:\n *\n * - `injectHTML` is emitted immediately before `</head>`. This is where the\n * bulk of vinext's head additions live (RSC navigation runtime metadata,\n * bootstrap modulepreload, server-inserted HTML, font preloads, etc.).\n * - `injectAfterHeadOpenHTML` is emitted immediately after the `<head ...>`\n * opening tag so the content runs before any React-emitted resource\n * hints. This is where inline `<Script strategy=\"beforeInteractive\">`\n * captures land so the no-flash dark-mode pattern works.\n *\n * Fallback behaviour differs by insertion point:\n *\n * - `injectHTML` is emitted at end-of-stream by the `flush` handler when no\n * chunk ever contained `</head>` — callers still see the payload on\n * highly fragmented streams (just at the end of the body rather than in\n * the head).\n * - `injectAfterHeadOpenHTML` is silently dropped when `<head ...>` is not\n * found in a discoverable chunk. Emitting it at end-of-stream would put\n * it after the document body, defeating the point — the splice has to\n * happen before resource hints to be useful, so the safer behaviour is\n * to no-op and let the user-rendered Script (in its source-order\n * position) ship as-is.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML: HtmlInsertion = \"\",\n injectAfterHeadOpenHTML: HtmlInsertion = \"\",\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n const insertsPerFlush = typeof injectHTML === \"function\";\n let injected = false;\n let preHeadInjected = false;\n let buffered: string[] = [];\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n const readInsertion = (): string =>\n typeof injectHTML === \"function\" ? injectHTML() : injectHTML;\n const readPreHeadInsertion = (): string =>\n typeof injectAfterHeadOpenHTML === \"function\"\n ? injectAfterHeadOpenHTML()\n : injectAfterHeadOpenHTML;\n const emitInsertion = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n const insertion = readInsertion();\n if (insertion) {\n controller.enqueue(encoder.encode(insertion));\n }\n };\n\n /**\n * Splice the pre-head insertion (typically captured beforeInteractive inline\n * scripts) immediately after the `<head ...>` opening tag. Returns the\n * rewritten chunk and a flag indicating whether the splice happened, so the\n * caller can mark `preHeadInjected` and stop scanning further chunks.\n *\n * NOTE: This is called only when `<head ...>` lies fully inside `chunk` —\n * we deliberately avoid stitching across chunk boundaries because doing so\n * would force the transform to hold output until it had seen `<head ...>`,\n * which both delays TTFB and complicates the existing `</head>` injection\n * path. In practice React Fizz emits the opening shell as a single chunk.\n */\n const spliceAfterHeadOpen = (chunk: string): { chunk: string; spliced: boolean } => {\n if (preHeadInjected) return { chunk, spliced: false };\n const insertion = readPreHeadInsertion();\n if (!insertion) return { chunk, spliced: false };\n const match = HEAD_OPEN_RE.exec(chunk);\n if (!match) return { chunk, spliced: false };\n const insertAt = match.index + match[0].length;\n return {\n chunk: chunk.slice(0, insertAt) + insertion + chunk.slice(insertAt),\n spliced: true,\n };\n };\n\n const flushBuffered = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n if (buffered.length === 0) return;\n\n if (injected && insertsPerFlush) {\n // Emit newly collected server-inserted HTML before the next Fizz HTML\n // batch so CSS-in-JS styles precede the elements they style.\n emitInsertion(controller);\n }\n\n for (const chunk of buffered) {\n let working = chunk;\n if (!preHeadInjected) {\n const result = spliceAfterHeadOpen(working);\n if (result.spliced) {\n working = result.chunk;\n preHeadInjected = true;\n }\n }\n if (!injected) {\n const headEnd = working.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = working.slice(0, headEnd);\n const after = working.slice(headEnd);\n controller.enqueue(encoder.encode(before + readInsertion() + after));\n injected = true;\n continue;\n }\n }\n controller.enqueue(encoder.encode(working));\n }\n buffered = [];\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(fixPreloadAs(decoder.decode(chunk, { stream: true })));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n try {\n flushBuffered(controller);\n\n const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\n }\n } catch {\n // Stream was cancelled between when the timeout was registered and\n // when it fired (e.g. client disconnected, health-check cancelled\n // the response body). Ignore — the stream is already closed.\n }\n\n timeoutId = null;\n }, 0);\n },\n\n async flush(controller) {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n flushBuffered(controller);\n\n if (!injected) {\n emitInsertion(controller);\n injected = true;\n } else if (insertsPerFlush) {\n emitInsertion(controller);\n }\n\n const finalScripts = await rscEmbed.finalize();\n if (finalScripts) {\n controller.enqueue(encoder.encode(finalScripts));\n }\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-ssr-stream.js","names":[],"sources":["../../src/server/app-ssr-stream.ts"],"sourcesContent":["import {\n createInlineScriptTag,\n escapeHtmlAttr,\n htmlTokenListContains,\n safeJsonStringify,\n} from \"./html.js\";\nimport {\n bytesToBase64,\n concatUint8Arrays,\n RSC_EMBEDDED_BINARY_CHUNK,\n type RscEmbeddedChunk,\n} from \"./app-rsc-embedded-chunks.js\";\nimport { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from \"../client/navigation-runtime.js\";\n\ntype RscEmbedTransform = {\n flush(): string;\n finalize(): Promise<string>;\n /** Resolves when all raw bytes from the embed stream have been read. */\n getRawBuffer(): Promise<ArrayBuffer>;\n};\n\ntype HtmlInsertion = string | (() => string);\ntype InlineCssManifest = Record<string, string>;\ntype InlineCssRewriteResult = {\n html: string;\n consumedPrependCss: boolean;\n};\n\nconst NAVIGATION_RUNTIME_REFERENCE = `self[Symbol.for(${safeJsonStringify(\n NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION,\n)})]`;\n\nexport function navigationRuntimeRscBootstrapExpression(): string {\n return `((${NAVIGATION_RUNTIME_REFERENCE}??={bootstrap:{routeManifest:null},functions:{}}).bootstrap.rsc??={rsc:[]})`;\n}\n\nexport function createNavigationRuntimeRscMetadataScript(\n params: Record<string, string | string[]>,\n nav: { pathname: string; searchParams: [string, string][] },\n): string {\n return (\n \"Object.assign(\" +\n navigationRuntimeRscBootstrapExpression() +\n \",{params:\" +\n safeJsonStringify(params) +\n \",nav:\" +\n safeJsonStringify(nav) +\n \"})\"\n );\n}\n\nfunction createNavigationRuntimeRscChunkScript(chunk: RscEmbeddedChunk): string {\n return navigationRuntimeRscBootstrapExpression() + \".rsc.push(\" + safeJsonStringify(chunk) + \")\";\n}\n\nfunction createNavigationRuntimeRscDoneScript(): string {\n return navigationRuntimeRscBootstrapExpression() + \".done=true\";\n}\n\n/**\n * Fix invalid preload \"as\" values in RSC Flight hint lines before they reach\n * the client. React Flight emits HL hints with as=\"stylesheet\" for CSS, but\n * the HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixFlightHints(text: string): string {\n return text.replace(/(\\d*:HL\\[.*?),\"stylesheet\"(\\]|,)/g, '$1,\"style\"$2');\n}\n\n/**\n * Create a helper that progressively embeds RSC chunks as inline <script> tags.\n * The browser entry turns the embedded chunks back into Uint8Array data.\n */\nexport function createRscEmbedTransform(\n embedStream: ReadableStream<Uint8Array>,\n scriptNonce?: string,\n): RscEmbedTransform {\n const reader = embedStream.getReader();\n let pendingChunks: RscEmbeddedChunk[] = [];\n const rawChunks: Uint8Array[] = [];\n let reading = false;\n\n async function pumpReader(): Promise<void> {\n if (reading) return;\n reading = true;\n try {\n while (true) {\n const result = await reader.read();\n if (result.done) break;\n // Accumulate raw bytes BEFORE fixFlightHints so the cache stores\n // unmodified RSC data. The embed script path below applies fixes.\n rawChunks.push(result.value);\n try {\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n const text = decoder.decode(result.value);\n // The RSC entry already fixes HL hints at the source. Keep this second\n // pass as defense in depth for any embed stream that bypasses that\n // wrapper; the rewrite is idempotent, so double-application is safe.\n pendingChunks.push(fixFlightHints(text));\n } catch {\n pendingChunks.push([RSC_EMBEDDED_BINARY_CHUNK, bytesToBase64(result.value)]);\n }\n }\n } catch (error) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[vinext] RSC embed stream read error:\", error);\n }\n throw error;\n } finally {\n reading = false;\n }\n }\n\n const pumpPromise = pumpReader();\n\n return {\n flush(): string {\n if (pendingChunks.length === 0) return \"\";\n\n const chunks = pendingChunks;\n pendingChunks = [];\n\n let scripts = \"\";\n for (const chunk of chunks) {\n scripts += createInlineScriptTag(createNavigationRuntimeRscChunkScript(chunk), scriptNonce);\n }\n return scripts;\n },\n\n async finalize(): Promise<string> {\n await pumpPromise;\n let scripts = this.flush();\n scripts += createInlineScriptTag(createNavigationRuntimeRscDoneScript(), scriptNonce);\n return scripts;\n },\n\n async getRawBuffer(): Promise<ArrayBuffer> {\n await pumpPromise;\n const buffer = concatUint8Arrays(rawChunks);\n rawChunks.length = 0;\n return buffer.buffer;\n },\n };\n}\n\n/**\n * Fix invalid preload \"as\" values in server-rendered HTML.\n * React Fizz emits <link rel=\"preload\" as=\"stylesheet\"> for CSS, but the\n * HTML spec requires as=\"style\" for <link rel=\"preload\">.\n */\nexport function fixPreloadAs(html: string): string {\n return html.replace(/<link(?=[^>]*\\srel=\"preload\")[^>]*>/g, (tag) =>\n tag.replace(' as=\"stylesheet\"', ' as=\"style\"'),\n );\n}\n\n// These `g`-flag regexes carry mutable `lastIndex` state. Every consumer below\n// resets `lastIndex` before use, which is safe only because they run to\n// completion synchronously within a single call. They must not be shared across\n// concurrent/interleaved call paths.\nconst LINK_TAG_RE = /<link\\b[^>]*>/gi;\nconst HTML_REWRITE_EXCLUDED_REGION_RE =\n /<!--[\\s\\S]*?-->|<(script|style|textarea|title)\\b[^>]*>[\\s\\S]*?<\\/\\1\\s*>/gi;\nconst HTML_REWRITE_EXCLUDED_REGION_START_RE = /<!--|<(script|style|textarea|title)\\b[^>]*>/gi;\n\nfunction getHtmlAttribute(tag: string, name: string): string | null {\n const attrRe = /\\s([^\\s\"'=<>`]+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g;\n let match: RegExpExecArray | null;\n\n while ((match = attrRe.exec(tag)) !== null) {\n if (match[1]?.toLowerCase() !== name.toLowerCase()) continue;\n return match[2] ?? match[3] ?? match[4] ?? \"\";\n }\n\n return null;\n}\n\nfunction htmlAttributeHasToken(tag: string, name: string, token: string): boolean {\n return htmlTokenListContains(getHtmlAttribute(tag, name), token);\n}\n\nfunction getInlineCss(manifest: InlineCssManifest, href: string): string | null {\n if (Object.prototype.hasOwnProperty.call(manifest, href)) {\n return manifest[href] ?? \"\";\n }\n\n try {\n const pathname = new URL(href).pathname;\n if (Object.prototype.hasOwnProperty.call(manifest, pathname)) {\n return manifest[pathname] ?? \"\";\n }\n } catch {\n // Relative asset URLs are looked up by their emitted href.\n }\n\n return null;\n}\n\n// Module-level regex; consumers reset `lastIndex` before each scan. Same\n// shared-state constraint as the other `g`-flag regexes above.\nconst TRAILING_LINK_OPEN_RE = /<link/gi;\n\nfunction splitTrailingIncompleteLinkTag(html: string): { complete: string; trailing: string } {\n // Scan forward to find the last `<link` opening without allocating a\n // lowercased copy of `html` — this runs on every flush of the streaming\n // hot path, and `html` can be tens of KB.\n TRAILING_LINK_OPEN_RE.lastIndex = 0;\n let lastIndex = -1;\n let match: RegExpExecArray | null;\n while ((match = TRAILING_LINK_OPEN_RE.exec(html)) !== null) {\n lastIndex = match.index;\n }\n if (lastIndex === -1) return { complete: html, trailing: \"\" };\n const close = html.indexOf(\">\", lastIndex);\n if (close !== -1) return { complete: html, trailing: \"\" };\n return {\n complete: html.slice(0, lastIndex),\n trailing: html.slice(lastIndex),\n };\n}\n\nfunction findTrailingOpenHtmlRewriteExcludedRegionStart(html: string): number | null {\n let match: RegExpExecArray | null;\n\n HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = 0;\n while ((match = HTML_REWRITE_EXCLUDED_REGION_START_RE.exec(html)) !== null) {\n const start = match.index;\n if (match[0] === \"<!--\") {\n const close = html.indexOf(\"-->\", HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex);\n if (close === -1) return start;\n HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex = close + 3;\n continue;\n }\n\n const tagName = match[1]?.toLowerCase();\n if (!tagName) continue;\n\n const closeTagRe = new RegExp(`</${tagName}\\\\s*>`, \"i\");\n const close = closeTagRe.exec(html.slice(HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex));\n if (!close) return start;\n HTML_REWRITE_EXCLUDED_REGION_START_RE.lastIndex += close.index + close[0].length;\n }\n\n return null;\n}\n\nfunction splitTrailingInlineCssRewriteBoundary(html: string): {\n complete: string;\n trailing: string;\n} {\n const linkSplit = splitTrailingIncompleteLinkTag(html);\n const incompleteLinkStart = linkSplit.trailing ? linkSplit.complete.length : null;\n const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(html);\n const trailingStart =\n incompleteLinkStart === null\n ? openRegionStart\n : openRegionStart === null\n ? incompleteLinkStart\n : Math.min(incompleteLinkStart, openRegionStart);\n\n if (trailingStart === null) return { complete: html, trailing: \"\" };\n\n return {\n complete: html.slice(0, trailingStart),\n trailing: html.slice(trailingStart),\n };\n}\n\nfunction escapeStyleText(css: string): string {\n return css.replace(/<\\/style/gi, \"<\\\\/style\");\n}\n\nconst CSS_PREPEND_UNSAFE_PREAMBLE_RE =\n /^\\uFEFF?(?:\\s|\\/\\*[\\s\\S]*?\\*\\/)*@(charset|import|layer|namespace)\\b/i;\n\nfunction canPrependCss(css: string): boolean {\n return !CSS_PREPEND_UNSAFE_PREAMBLE_RE.test(css);\n}\n\nfunction replaceLinkTags(html: string, replaceLinkTag: (tag: string) => string): string {\n LINK_TAG_RE.lastIndex = 0;\n return html.replace(LINK_TAG_RE, replaceLinkTag);\n}\n\nfunction replaceLinkTagsOutsideRawText(\n html: string,\n replaceLinkTag: (tag: string) => string,\n): string {\n let rewritten = \"\";\n let cursor = 0;\n let match: RegExpExecArray | null;\n\n HTML_REWRITE_EXCLUDED_REGION_RE.lastIndex = 0;\n while ((match = HTML_REWRITE_EXCLUDED_REGION_RE.exec(html)) !== null) {\n rewritten += replaceLinkTags(html.slice(cursor, match.index), replaceLinkTag);\n rewritten += match[0];\n cursor = match.index + match[0].length;\n }\n\n const tail = html.slice(cursor);\n const openRegionStart = findTrailingOpenHtmlRewriteExcludedRegionStart(tail);\n if (openRegionStart === null) {\n return rewritten + replaceLinkTags(tail, replaceLinkTag);\n }\n\n return (\n rewritten +\n replaceLinkTags(tail.slice(0, openRegionStart), replaceLinkTag) +\n tail.slice(openRegionStart)\n );\n}\n\nfunction rewriteInlineCssStylesheetLinks(\n html: string,\n inlineCssManifest: InlineCssManifest | undefined,\n prependCss: string,\n ssrScriptNonce: string | undefined,\n): InlineCssRewriteResult {\n if (!inlineCssManifest || Object.keys(inlineCssManifest).length === 0) {\n return { html, consumedPrependCss: false };\n }\n let consumedPrependCss = false;\n\n const rewritten = replaceLinkTagsOutsideRawText(html, (tag) => {\n if (!htmlAttributeHasToken(tag, \"rel\", \"stylesheet\")) return tag;\n\n const href = getHtmlAttribute(tag, \"href\");\n const precedence =\n getHtmlAttribute(tag, \"data-precedence\") ?? getHtmlAttribute(tag, \"precedence\");\n if (!href || !precedence) return tag;\n\n const css = getInlineCss(inlineCssManifest, href);\n if (css === null) return tag;\n\n // Prefer the link's own nonce if Fizz emitted one; otherwise fall back to\n // the SSR-time script/style nonce so sites with CSP `style-src 'nonce-…'`\n // policies don't block the inlined `<style>` block. The `<link>` tag this\n // replaces wasn't subject to inline-style CSP, but the new `<style>` is.\n const linkNonce = getHtmlAttribute(tag, \"nonce\");\n const effectiveNonce = linkNonce ?? ssrScriptNonce;\n const nonceAttr = effectiveNonce ? ` nonce=\"${escapeHtmlAttr(effectiveNonce)}\"` : \"\";\n const shouldPrependCss = !consumedPrependCss && prependCss.length > 0 && canPrependCss(css);\n const cssPrefix = shouldPrependCss ? `${prependCss}\\n` : \"\";\n consumedPrependCss ||= cssPrefix.length > 0;\n\n return (\n `<style data-vinext-inline-css${nonceAttr}` +\n ` data-precedence=\"${escapeHtmlAttr(precedence)}\"` +\n ` data-href=\"${escapeHtmlAttr(href)}\">` +\n `${escapeStyleText(cssPrefix + css)}</style>`\n );\n });\n\n return { html: rewritten, consumedPrependCss };\n}\n\n/**\n * Match the `<head ...>` opening tag in a chunk. Matches both bare `<head>`\n * and `<head class=\"foo\">` shapes. Used to splice HTML immediately after the\n * opening tag so injected content runs before any React-emitted resource\n * hints (stylesheets, modulepreloads) that React Float hoists into `<head>`.\n */\nconst HEAD_OPEN_RE = /<head\\b[^>]*>/;\n\n/**\n * Create the tick-buffered HTML transform that injects RSC scripts between\n * React Fizz flush cycles without corrupting split HTML chunks.\n *\n * Two insertion points are supported in tandem:\n *\n * - `injectHTML` is emitted immediately before `</head>`. This is where the\n * bulk of vinext's head additions live (RSC navigation runtime metadata,\n * bootstrap modulepreload, server-inserted HTML, font preloads, etc.).\n * - `injectAfterHeadOpenHTML` is emitted immediately after the `<head ...>`\n * opening tag so the content runs before any React-emitted resource\n * hints. This is where inline `<Script strategy=\"beforeInteractive\">`\n * captures land so the no-flash dark-mode pattern works.\n *\n * Fallback behaviour differs by insertion point:\n *\n * - `injectHTML` is emitted at end-of-stream by the `flush` handler when no\n * chunk ever contained `</head>` — callers still see the payload on\n * highly fragmented streams (just at the end of the body rather than in\n * the head).\n * - `injectAfterHeadOpenHTML` is silently dropped when `<head ...>` is not\n * found in a discoverable chunk. Emitting it at end-of-stream would put\n * it after the document body, defeating the point — the splice has to\n * happen before resource hints to be useful, so the safer behaviour is\n * to no-op and let the user-rendered Script (in its source-order\n * position) ship as-is.\n */\nexport function createTickBufferedTransform(\n rscEmbed: RscEmbedTransform,\n injectHTML: HtmlInsertion = \"\",\n injectAfterHeadOpenHTML: HtmlInsertion = \"\",\n inlineCssManifest?: InlineCssManifest,\n inlineCssPrependCss = \"\",\n inlineCssPrependFallbackHTML = \"\",\n inlineCssScriptNonce?: string,\n): TransformStream<Uint8Array, Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n const insertsPerFlush = typeof injectHTML === \"function\";\n let injected = false;\n let preHeadInjected = false;\n let buffered: string[] = [];\n let pendingHtml = \"\";\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n // Computed once at transform creation: every flush is a hot path, so we\n // avoid re-running Object.keys() on the manifest per chunk. Gates both the\n // split-link boundary buffering and the inline-css link rewrite below.\n const hasInlineCssManifest =\n inlineCssManifest !== undefined && Object.keys(inlineCssManifest).length > 0;\n const readInsertion = (): string =>\n typeof injectHTML === \"function\" ? injectHTML() : injectHTML;\n const readPreHeadInsertion = (): string =>\n typeof injectAfterHeadOpenHTML === \"function\"\n ? injectAfterHeadOpenHTML()\n : injectAfterHeadOpenHTML;\n const readInlineCssPrependFallback = (): string => {\n if (!inlineCssPrependCss || !inlineCssPrependFallbackHTML) return \"\";\n inlineCssPrependCss = \"\";\n return inlineCssPrependFallbackHTML;\n };\n const emitInsertion = (controller: TransformStreamDefaultController<Uint8Array>): void => {\n const insertion = readInlineCssPrependFallback() + readInsertion();\n if (insertion) {\n controller.enqueue(encoder.encode(insertion));\n }\n };\n\n /**\n * Splice the pre-head insertion (typically captured beforeInteractive inline\n * scripts) immediately after the `<head ...>` opening tag. Returns the\n * rewritten chunk and a flag indicating whether the splice happened, so the\n * caller can mark `preHeadInjected` and stop scanning further chunks.\n *\n * NOTE: This is called only when `<head ...>` lies fully inside the current\n * tick-buffered batch. We deliberately avoid retaining arbitrary output until\n * a future chunk completes `<head ...>`, which would delay TTFB and complicate\n * the existing `</head>` injection path. In practice React Fizz emits the\n * opening shell as a single batch.\n */\n const spliceAfterHeadOpen = (chunk: string): { chunk: string; spliced: boolean } => {\n if (preHeadInjected) return { chunk, spliced: false };\n const insertion = readPreHeadInsertion();\n if (!insertion) return { chunk, spliced: false };\n const match = HEAD_OPEN_RE.exec(chunk);\n if (!match) return { chunk, spliced: false };\n const insertAt = match.index + match[0].length;\n return {\n chunk: chunk.slice(0, insertAt) + insertion + chunk.slice(insertAt),\n spliced: true,\n };\n };\n\n const flushBuffered = (\n controller: TransformStreamDefaultController<Uint8Array>,\n final = false,\n ): void => {\n if (buffered.length === 0 && !pendingHtml) return;\n const rawHtml = pendingHtml + buffered.join(\"\");\n buffered = [];\n pendingHtml = \"\";\n\n const split =\n final || !hasInlineCssManifest\n ? { complete: rawHtml, trailing: \"\" }\n : splitTrailingInlineCssRewriteBoundary(rawHtml);\n if (split.trailing) {\n pendingHtml = split.trailing;\n }\n if (!split.complete) return;\n\n if (injected && insertsPerFlush) {\n // Emit newly collected server-inserted HTML before the next Fizz HTML\n // batch so CSS-in-JS styles precede the elements they style.\n emitInsertion(controller);\n }\n\n const preparedHtml = fixPreloadAs(split.complete);\n const inlineCssResult = hasInlineCssManifest\n ? rewriteInlineCssStylesheetLinks(\n preparedHtml,\n inlineCssManifest,\n inlineCssPrependCss,\n inlineCssScriptNonce,\n )\n : { html: preparedHtml, consumedPrependCss: false };\n if (inlineCssResult.consumedPrependCss) {\n inlineCssPrependCss = \"\";\n }\n\n let working = inlineCssResult.html;\n if (!preHeadInjected) {\n const result = spliceAfterHeadOpen(working);\n if (result.spliced) {\n working = result.chunk;\n preHeadInjected = true;\n }\n }\n if (!injected) {\n const headEnd = working.indexOf(\"</head>\");\n if (headEnd !== -1) {\n const before = working.slice(0, headEnd);\n const after = working.slice(headEnd);\n controller.enqueue(\n encoder.encode(before + readInlineCssPrependFallback() + readInsertion() + after),\n );\n injected = true;\n return;\n }\n }\n controller.enqueue(encoder.encode(working));\n };\n\n return new TransformStream<Uint8Array, Uint8Array>({\n transform(chunk, controller) {\n buffered.push(decoder.decode(chunk, { stream: true }));\n\n if (timeoutId !== null) return;\n\n timeoutId = setTimeout(() => {\n try {\n flushBuffered(controller);\n\n const rscScripts = rscEmbed.flush();\n if (rscScripts) {\n controller.enqueue(encoder.encode(rscScripts));\n }\n } catch {\n // Stream was cancelled between when the timeout was registered and\n // when it fired (e.g. client disconnected, health-check cancelled\n // the response body). Ignore — the stream is already closed.\n }\n\n timeoutId = null;\n }, 0);\n },\n\n async flush(controller) {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n const remainder = decoder.decode();\n if (remainder) {\n buffered.push(remainder);\n }\n\n flushBuffered(controller, true);\n\n if (!injected) {\n emitInsertion(controller);\n injected = true;\n } else if (insertsPerFlush) {\n emitInsertion(controller);\n }\n\n const finalScripts = await rscEmbed.finalize();\n if (finalScripts) {\n controller.enqueue(encoder.encode(finalScripts));\n }\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ //#region src/server/client-trace-metadata.d.ts
2
+ type ClientTraceDataEntry = {
3
+ key: string;
4
+ value: string;
5
+ };
6
+ /**
7
+ * Filter an entry list against the configured `clientTraceMetadata` allow-list.
8
+ * Returns `undefined` when the allow-list is unset so callers can skip
9
+ * rendering altogether.
10
+ */
11
+ declare function filterClientTraceMetadata(entries: readonly ClientTraceDataEntry[], allowList: readonly string[] | undefined): ClientTraceDataEntry[] | undefined;
12
+ /**
13
+ * Render the filtered entries as a sequence of self-closing `<meta>` tags.
14
+ * Names and values are HTML-attribute escaped. Returns an empty string when
15
+ * `entries` is empty or undefined so callers can append unconditionally.
16
+ */
17
+ declare function renderClientTraceMetadataTags(entries: readonly ClientTraceDataEntry[] | undefined): string;
18
+ /**
19
+ * Convenience helper: read OTel propagation data, filter against the
20
+ * configured allow-list, and render the resulting `<meta>` tags. Returns an
21
+ * empty string when the allow-list is unset, OTel is not installed, or no
22
+ * matching keys were emitted by the propagator.
23
+ *
24
+ * Safe to call unconditionally on every SSR render — when nothing is
25
+ * configured/active this is a few `try/catch`-bounded operations and returns
26
+ * `""`.
27
+ */
28
+ declare function getClientTraceMetadataHTML(allowList: readonly string[] | undefined): string;
29
+ //#endregion
30
+ export { ClientTraceDataEntry, filterClientTraceMetadata, getClientTraceMetadataHTML, renderClientTraceMetadataTags };
31
+ //# sourceMappingURL=client-trace-metadata.d.ts.map
@@ -0,0 +1,83 @@
1
+ import { escapeHtmlAttr } from "./html.js";
2
+ //#region src/server/client-trace-metadata.ts
3
+ /**
4
+ * Client trace metadata renderer.
5
+ *
6
+ * When `experimental.clientTraceMetadata` is configured in `next.config`,
7
+ * vinext emits `<meta name="..." content="...">` tags in the SSR HTML head
8
+ * for each configured key. The values are sourced from the active
9
+ * OpenTelemetry context via the registered propagator.
10
+ *
11
+ * This mirrors Next.js' implementation:
12
+ * - packages/next/src/server/lib/trace/utils.ts (getTracedMetadata)
13
+ * - packages/next/src/server/app-render/make-get-server-inserted-html.tsx (traceMetaTags)
14
+ *
15
+ * OpenTelemetry is an optional peer — we resolve `@opentelemetry/api` at
16
+ * runtime and silently no-op when it is not installed. This matches user
17
+ * expectations: apps that don't configure OTel get no meta tags, and apps
18
+ * that do get the filtered subset they asked for in `clientTraceMetadata`.
19
+ */
20
+ const carrierSetter = { set(carrier, key, value) {
21
+ if (typeof key !== "string" || typeof value !== "string") return;
22
+ carrier.push({
23
+ key,
24
+ value
25
+ });
26
+ } };
27
+ function getOpenTelemetryTraceData() {
28
+ let api;
29
+ try {
30
+ const req = globalThis.require;
31
+ if (typeof req === "function") api = req("@opentelemetry/api");
32
+ } catch {
33
+ return [];
34
+ }
35
+ if (!api) return [];
36
+ try {
37
+ const activeContext = api.context.active();
38
+ const entries = [];
39
+ api.propagation.inject(activeContext, entries, carrierSetter);
40
+ return entries;
41
+ } catch {
42
+ return [];
43
+ }
44
+ }
45
+ /**
46
+ * Filter an entry list against the configured `clientTraceMetadata` allow-list.
47
+ * Returns `undefined` when the allow-list is unset so callers can skip
48
+ * rendering altogether.
49
+ */
50
+ function filterClientTraceMetadata(entries, allowList) {
51
+ if (!allowList || allowList.length === 0) return void 0;
52
+ const allowSet = new Set(allowList);
53
+ return entries.filter(({ key }) => allowSet.has(key));
54
+ }
55
+ /**
56
+ * Render the filtered entries as a sequence of self-closing `<meta>` tags.
57
+ * Names and values are HTML-attribute escaped. Returns an empty string when
58
+ * `entries` is empty or undefined so callers can append unconditionally.
59
+ */
60
+ function renderClientTraceMetadataTags(entries) {
61
+ if (!entries || entries.length === 0) return "";
62
+ let html = "";
63
+ for (const { key, value } of entries) html += `<meta name="${escapeHtmlAttr(key)}" content="${escapeHtmlAttr(value)}"/>`;
64
+ return html;
65
+ }
66
+ /**
67
+ * Convenience helper: read OTel propagation data, filter against the
68
+ * configured allow-list, and render the resulting `<meta>` tags. Returns an
69
+ * empty string when the allow-list is unset, OTel is not installed, or no
70
+ * matching keys were emitted by the propagator.
71
+ *
72
+ * Safe to call unconditionally on every SSR render — when nothing is
73
+ * configured/active this is a few `try/catch`-bounded operations and returns
74
+ * `""`.
75
+ */
76
+ function getClientTraceMetadataHTML(allowList) {
77
+ if (!allowList || allowList.length === 0) return "";
78
+ return renderClientTraceMetadataTags(filterClientTraceMetadata(getOpenTelemetryTraceData(), allowList));
79
+ }
80
+ //#endregion
81
+ export { filterClientTraceMetadata, getClientTraceMetadataHTML, renderClientTraceMetadataTags };
82
+
83
+ //# sourceMappingURL=client-trace-metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-trace-metadata.js","names":[],"sources":["../../src/server/client-trace-metadata.ts"],"sourcesContent":["/**\n * Client trace metadata renderer.\n *\n * When `experimental.clientTraceMetadata` is configured in `next.config`,\n * vinext emits `<meta name=\"...\" content=\"...\">` tags in the SSR HTML head\n * for each configured key. The values are sourced from the active\n * OpenTelemetry context via the registered propagator.\n *\n * This mirrors Next.js' implementation:\n * - packages/next/src/server/lib/trace/utils.ts (getTracedMetadata)\n * - packages/next/src/server/app-render/make-get-server-inserted-html.tsx (traceMetaTags)\n *\n * OpenTelemetry is an optional peer — we resolve `@opentelemetry/api` at\n * runtime and silently no-op when it is not installed. This matches user\n * expectations: apps that don't configure OTel get no meta tags, and apps\n * that do get the filtered subset they asked for in `clientTraceMetadata`.\n */\nimport { escapeHtmlAttr } from \"./html.js\";\n\nexport type ClientTraceDataEntry = {\n key: string;\n value: string;\n};\n\ntype TextMapSetter = {\n set(carrier: ClientTraceDataEntry[], key: string, value: string): void;\n};\n\nconst carrierSetter: TextMapSetter = {\n set(carrier, key, value) {\n if (typeof key !== \"string\" || typeof value !== \"string\") return;\n carrier.push({ key, value });\n },\n};\n\n/**\n * Pull entries off the active OpenTelemetry context via the registered\n * propagator. Returns an empty array when `@opentelemetry/api` is not\n * installed or when no propagator has been registered.\n *\n * The implementation mirrors Next.js's `NextTracerImpl.getTracePropagationData`:\n * we call `propagation.inject(activeContext, entries, setter)` and let the\n * setter push entries into our carrier array.\n */\ntype OpenTelemetryApi = {\n context: { active(): unknown };\n propagation: {\n inject(context: unknown, carrier: ClientTraceDataEntry[], setter: TextMapSetter): void;\n };\n};\n\nfunction getOpenTelemetryTraceData(): ClientTraceDataEntry[] {\n let api: OpenTelemetryApi | undefined;\n try {\n // Use require() at runtime so `@opentelemetry/api` is an optional peer.\n // Bundlers (Vite/esbuild) leave the `require` reference alone, so apps\n // that don't install the package never hit this branch.\n const req = (globalThis as { require?: (id: string) => unknown }).require;\n if (typeof req === \"function\") {\n api = req(\"@opentelemetry/api\") as OpenTelemetryApi;\n }\n } catch {\n return [];\n }\n\n if (!api) return [];\n\n try {\n const activeContext = api.context.active();\n const entries: ClientTraceDataEntry[] = [];\n api.propagation.inject(activeContext, entries, carrierSetter);\n return entries;\n } catch {\n return [];\n }\n}\n\n/**\n * Filter an entry list against the configured `clientTraceMetadata` allow-list.\n * Returns `undefined` when the allow-list is unset so callers can skip\n * rendering altogether.\n */\nexport function filterClientTraceMetadata(\n entries: readonly ClientTraceDataEntry[],\n allowList: readonly string[] | undefined,\n): ClientTraceDataEntry[] | undefined {\n if (!allowList || allowList.length === 0) return undefined;\n const allowSet = new Set(allowList);\n return entries.filter(({ key }) => allowSet.has(key));\n}\n\n/**\n * Render the filtered entries as a sequence of self-closing `<meta>` tags.\n * Names and values are HTML-attribute escaped. Returns an empty string when\n * `entries` is empty or undefined so callers can append unconditionally.\n */\nexport function renderClientTraceMetadataTags(\n entries: readonly ClientTraceDataEntry[] | undefined,\n): string {\n if (!entries || entries.length === 0) return \"\";\n let html = \"\";\n for (const { key, value } of entries) {\n html += `<meta name=\"${escapeHtmlAttr(key)}\" content=\"${escapeHtmlAttr(value)}\"/>`;\n }\n return html;\n}\n\n/**\n * Convenience helper: read OTel propagation data, filter against the\n * configured allow-list, and render the resulting `<meta>` tags. Returns an\n * empty string when the allow-list is unset, OTel is not installed, or no\n * matching keys were emitted by the propagator.\n *\n * Safe to call unconditionally on every SSR render — when nothing is\n * configured/active this is a few `try/catch`-bounded operations and returns\n * `\"\"`.\n */\nexport function getClientTraceMetadataHTML(allowList: readonly string[] | undefined): string {\n if (!allowList || allowList.length === 0) return \"\";\n const entries = getOpenTelemetryTraceData();\n const filtered = filterClientTraceMetadata(entries, allowList);\n return renderClientTraceMetadataTags(filtered);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4BA,MAAM,gBAA+B,EACnC,IAAI,SAAS,KAAK,OAAO;CACvB,IAAI,OAAO,QAAQ,YAAY,OAAO,UAAU,UAAU;CAC1D,QAAQ,KAAK;EAAE;EAAK;EAAO,CAAC;GAE/B;AAkBD,SAAS,4BAAoD;CAC3D,IAAI;CACJ,IAAI;EAIF,MAAM,MAAO,WAAqD;EAClE,IAAI,OAAO,QAAQ,YACjB,MAAM,IAAI,qBAAqB;SAE3B;EACN,OAAO,EAAE;;CAGX,IAAI,CAAC,KAAK,OAAO,EAAE;CAEnB,IAAI;EACF,MAAM,gBAAgB,IAAI,QAAQ,QAAQ;EAC1C,MAAM,UAAkC,EAAE;EAC1C,IAAI,YAAY,OAAO,eAAe,SAAS,cAAc;EAC7D,OAAO;SACD;EACN,OAAO,EAAE;;;;;;;;AASb,SAAgB,0BACd,SACA,WACoC;CACpC,IAAI,CAAC,aAAa,UAAU,WAAW,GAAG,OAAO,KAAA;CACjD,MAAM,WAAW,IAAI,IAAI,UAAU;CACnC,OAAO,QAAQ,QAAQ,EAAE,UAAU,SAAS,IAAI,IAAI,CAAC;;;;;;;AAQvD,SAAgB,8BACd,SACQ;CACR,IAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,OAAO;CAC7C,IAAI,OAAO;CACX,KAAK,MAAM,EAAE,KAAK,WAAW,SAC3B,QAAQ,eAAe,eAAe,IAAI,CAAC,aAAa,eAAe,MAAM,CAAC;CAEhF,OAAO;;;;;;;;;;;;AAaT,SAAgB,2BAA2B,WAAkD;CAC3F,IAAI,CAAC,aAAa,UAAU,WAAW,GAAG,OAAO;CAGjD,OAAO,8BADU,0BADD,2BACkC,EAAE,UACP,CAAC"}
@@ -0,0 +1,13 @@
1
+ //#region src/server/cookie-utils.d.ts
2
+ /**
3
+ * Parse the cookie name out of a serialised Set-Cookie line.
4
+ *
5
+ * Bounded by the first `;` so the attribute portion (e.g. `Path=/`) is never
6
+ * mistaken for part of the name when the value happens to contain another
7
+ * `=`. Returns null when the line is not parseable (defensive — callers keep
8
+ * unparseable entries verbatim so they don't drop user-supplied cookies).
9
+ */
10
+ declare function getSetCookieName(cookie: string): string | null;
11
+ //#endregion
12
+ export { getSetCookieName };
13
+ //# sourceMappingURL=cookie-utils.d.ts.map
@@ -0,0 +1,20 @@
1
+ //#region src/server/cookie-utils.ts
2
+ /**
3
+ * Parse the cookie name out of a serialised Set-Cookie line.
4
+ *
5
+ * Bounded by the first `;` so the attribute portion (e.g. `Path=/`) is never
6
+ * mistaken for part of the name when the value happens to contain another
7
+ * `=`. Returns null when the line is not parseable (defensive — callers keep
8
+ * unparseable entries verbatim so they don't drop user-supplied cookies).
9
+ */
10
+ function getSetCookieName(cookie) {
11
+ const equalsIndex = cookie.indexOf("=");
12
+ if (equalsIndex <= 0) return null;
13
+ const semicolonIndex = cookie.indexOf(";");
14
+ const end = semicolonIndex === -1 ? equalsIndex : Math.min(equalsIndex, semicolonIndex);
15
+ return cookie.slice(0, end);
16
+ }
17
+ //#endregion
18
+ export { getSetCookieName };
19
+
20
+ //# sourceMappingURL=cookie-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-utils.js","names":[],"sources":["../../src/server/cookie-utils.ts"],"sourcesContent":["/**\n * Parse the cookie name out of a serialised Set-Cookie line.\n *\n * Bounded by the first `;` so the attribute portion (e.g. `Path=/`) is never\n * mistaken for part of the name when the value happens to contain another\n * `=`. Returns null when the line is not parseable (defensive — callers keep\n * unparseable entries verbatim so they don't drop user-supplied cookies).\n */\nexport function getSetCookieName(cookie: string): string | null {\n const equalsIndex = cookie.indexOf(\"=\");\n if (equalsIndex <= 0) {\n return null;\n }\n const semicolonIndex = cookie.indexOf(\";\");\n const end = semicolonIndex === -1 ? equalsIndex : Math.min(equalsIndex, semicolonIndex);\n return cookie.slice(0, end);\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,iBAAiB,QAA+B;CAC9D,MAAM,cAAc,OAAO,QAAQ,IAAI;CACvC,IAAI,eAAe,GACjB,OAAO;CAET,MAAM,iBAAiB,OAAO,QAAQ,IAAI;CAC1C,MAAM,MAAM,mBAAmB,KAAK,cAAc,KAAK,IAAI,aAAa,eAAe;CACvF,OAAO,OAAO,MAAM,GAAG,IAAI"}
@@ -36,7 +36,14 @@ declare function parseCookieLocale(req: IncomingMessage, i18nConfig: NextI18nCon
36
36
  * 4. Render the component to HTML
37
37
  * 5. Wrap in _document shell and send response
38
38
  */
39
- declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean, hasMiddleware?: boolean): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
39
+ declare function createSSRHandler(server: ViteDevServer, runner: ModuleImporter, routes: Route[], pagesDir: string, i18nConfig?: NextI18nConfig | null, fileMatcher?: ValidFileMatcher, basePath?: string, trailingSlash?: boolean, hasMiddleware?: boolean,
40
+ /**
41
+ * Allow-list of OpenTelemetry propagation keys to emit as `<meta>` tags
42
+ * in the SSR head. Sourced from `experimental.clientTraceMetadata` in
43
+ * `next.config`. When undefined or empty, no meta tags are emitted.
44
+ */
45
+
46
+ clientTraceMetadata?: readonly string[]): (req: IncomingMessage, res: ServerResponse, url: string, /** Status code override — propagated from middleware rewrite status. */
40
47
 
41
48
  statusCode?: number,
42
49
  /**