@solidjs/web 2.0.0-rc.4 → 2.0.0-rc.6

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 (61) hide show
  1. package/dist/dev.cjs +163 -36
  2. package/dist/dev.js +161 -37
  3. package/dist/server.cjs +178 -37
  4. package/dist/server.js +177 -38
  5. package/dist/web.cjs +143 -36
  6. package/dist/web.js +141 -37
  7. package/frames/dist/client.cjs +41 -8
  8. package/frames/dist/client.dev.cjs +41 -8
  9. package/frames/dist/client.dev.js +41 -8
  10. package/frames/dist/client.js +41 -8
  11. package/frames/dist/server.cjs +497 -44
  12. package/frames/dist/server.js +497 -44
  13. package/package.json +2 -2
  14. package/serialization/dist/decode.cjs +4 -2
  15. package/serialization/dist/decode.js +4 -2
  16. package/serialization/dist/serialization.cjs +12 -8
  17. package/serialization/dist/serialization.js +12 -8
  18. package/serialization/types/index.d.ts +7 -0
  19. package/serialization/types/serializer-decode.d.ts +14 -1
  20. package/serialization/types/serializer.d.ts +7 -0
  21. package/serialization/types-cjs/index.d.cts +7 -0
  22. package/serialization/types-cjs/serializer-decode.d.cts +14 -1
  23. package/serialization/types-cjs/serializer.d.cts +7 -0
  24. package/server-functions/dist/client.cjs +164 -35
  25. package/server-functions/dist/client.js +161 -36
  26. package/server-functions/dist/server.cjs +899 -136
  27. package/server-functions/dist/server.dev.cjs +919 -136
  28. package/server-functions/dist/server.dev.js +915 -137
  29. package/server-functions/dist/server.js +895 -137
  30. package/types/client.d.ts +2 -1
  31. package/types/constants.d.ts +3 -1
  32. package/types/cookies.d.ts +6 -14
  33. package/types/frames/frame-client.d.ts +4 -0
  34. package/types/frames/serializer-decode.d.ts +14 -1
  35. package/types/frames/serializer.d.ts +7 -0
  36. package/types/jsx.d.ts +11 -16
  37. package/types/response.d.ts +11 -0
  38. package/types/serializer-decode.d.ts +14 -1
  39. package/types/serializer.d.ts +7 -0
  40. package/types/server-functions/client.d.ts +22 -2
  41. package/types/server-functions/flash.d.ts +9 -0
  42. package/types/server-functions/server.d.ts +131 -11
  43. package/types/server-functions/shared.d.ts +77 -14
  44. package/types/server-mock.d.ts +17 -2
  45. package/types/server.d.ts +16 -2
  46. package/types-cjs/client.d.cts +2 -1
  47. package/types-cjs/constants.d.cts +3 -1
  48. package/types-cjs/cookies.d.cts +6 -14
  49. package/types-cjs/frames/frame-client.d.cts +4 -0
  50. package/types-cjs/frames/serializer-decode.d.cts +14 -1
  51. package/types-cjs/frames/serializer.d.cts +7 -0
  52. package/types-cjs/jsx.d.cts +11 -16
  53. package/types-cjs/response.d.cts +11 -0
  54. package/types-cjs/serializer-decode.d.cts +14 -1
  55. package/types-cjs/serializer.d.cts +7 -0
  56. package/types-cjs/server-functions/client.d.cts +22 -2
  57. package/types-cjs/server-functions/flash.d.cts +9 -0
  58. package/types-cjs/server-functions/server.d.cts +131 -11
  59. package/types-cjs/server-functions/shared.d.cts +77 -14
  60. package/types-cjs/server-mock.d.cts +17 -2
  61. package/types-cjs/server.d.cts +16 -2
package/dist/server.cjs CHANGED
@@ -49,6 +49,15 @@ const Namespaces = {
49
49
  const VoidElements = /*#__PURE__*/new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
50
50
  const RawTextElements = /*#__PURE__*/new Set(["style", "script", "noscript", "template", "textarea", "title"]);
51
51
  const DOMElements = /*#__PURE__*/new Set(/*#__PURE__*/"a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,isindex,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,math,menu,menuitem,meta,meter,multicol,nav,nextid,nobr,noembed,noframes,noindex,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,portal,pre,progress,q,rb,rp,rt,rtc,ruby,s,samp,script,search,section,select,shadow,slot,small,source,spacer,span,strike,strong,style,sub,summary,sup,svg,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,webview,xmp".split(","));
52
+ const COMPOSED_BODY_FRAMING = /*#__PURE__*/new Set(["content-length", "content-encoding", "transfer-encoding"]);
53
+ function isHttpNavigationTarget(target) {
54
+ try {
55
+ const protocol = new URL(target, "http://base.invalid").protocol;
56
+ return protocol === "http:" || protocol === "https:";
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
52
61
 
53
62
  const transparentOptions = {
54
63
  transparent: true,
@@ -151,13 +160,13 @@ function resolveSerializerPlugins(customPlugins) {
151
160
  seroval.Feature.RegExp;
152
161
 
153
162
  const DEFAULT_DISABLED_FEATURES = seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
154
- const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
163
+ const serializeOnlyDisabledFeatures = serializeErrorStacks => (serializeErrorStacks === undefined ? process.env.NODE_ENV === "development" : serializeErrorStacks) ? 0 : seroval.Feature.ErrorPrototypeStack;
155
164
  const HYDRATION_GLOBAL = "_$HY.r";
156
165
  function createSerializer(options) {
157
166
  return new seroval.Serializer({
158
167
  ...options,
159
168
  plugins: resolveSerializerPlugins(options.plugins),
160
- disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
169
+ disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures(options.serializeErrorStacks)
161
170
  });
162
171
  }
163
172
  function createHydrationSerializer({
@@ -214,6 +223,7 @@ function isSafeError(value) {
214
223
  return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
215
224
  }
216
225
  const REVALIDATE_HEADER = "X-Revalidate";
226
+ const RESPONSE_HEADER_VALUE_LIMIT = 4096;
217
227
  function initWithRevalidate(init = {}) {
218
228
  const resolved = typeof init === "number" ? {
219
229
  status: init
@@ -234,7 +244,13 @@ function initWithRevalidate(init = {}) {
234
244
  } else {
235
245
  headers = new Headers(responseInit.headers);
236
246
  }
237
- revalidate !== undefined && headers.set(REVALIDATE_HEADER, revalidate.toString());
247
+ if (revalidate !== undefined) {
248
+ const keys = revalidate.toString();
249
+ if (keys.length > RESPONSE_HEADER_VALUE_LIMIT) {
250
+ throw new TypeError(`revalidate names ${keys.length} characters of cache keys; past ` + `${RESPONSE_HEADER_VALUE_LIMIT} the ${REVALIDATE_HEADER} header would overflow ` + `receivers (an 8 KiB proxy buffer holds the whole header block) and the response ` + `dies at the socket after the mutation committed. Split the invalidation across ` + `calls or use coarser keys — a dropped key would be a silently stale cache, so ` + `the runtime refuses rather than trims.`);
251
+ }
252
+ headers.set(REVALIDATE_HEADER, keys);
253
+ }
238
254
  return {
239
255
  responseInit,
240
256
  headers
@@ -252,7 +268,12 @@ function redirect(url, init = 302) {
252
268
  responseInit.status = 302;
253
269
  }
254
270
  const target = typeof url === "string" ? url : url[HREF];
255
- headers.set("Location", typeof target === "string" ? target : String(url));
271
+ const location = typeof target === "string" ? target : String(url);
272
+ const encoded = location.replace(/[^\x00-\x7f]/gu, encodeURIComponent);
273
+ if (encoded.length > RESPONSE_HEADER_VALUE_LIMIT) {
274
+ throw new TypeError(`redirect() target is ${encoded.length} characters; past ` + `${RESPONSE_HEADER_VALUE_LIMIT} the Location header (and the scripted transport's ` + `redirect header) would overflow receivers and the response dies at the socket. ` + `A target this size is usually unbounded input echoed into the url — carry the ` + `state server-side instead. Refused rather than trimmed: a cut target is a ` + `different address.`);
275
+ }
276
+ headers.set("Location", encoded);
256
277
  return new Response(null, {
257
278
  ...responseInit,
258
279
  headers
@@ -268,11 +289,19 @@ function reload(init = {}) {
268
289
  headers
269
290
  });
270
291
  }
292
+ const NULL_BODY_STATUSES = new Set([204, 205, 304]);
271
293
  function respond(value, init = {}) {
272
294
  const {
273
295
  responseInit,
274
296
  headers
275
297
  } = initWithRevalidate(init);
298
+ for (const header of COMPOSED_BODY_FRAMING) headers.delete(header);
299
+ if (NULL_BODY_STATUSES.has(responseInit.status)) {
300
+ return new ResponseEnvelope(new Response(null, {
301
+ ...responseInit,
302
+ headers
303
+ }), value);
304
+ }
276
305
  headers.set("Content-Type", "application/json");
277
306
  return new ResponseEnvelope(new Response(JSON.stringify(value), {
278
307
  ...responseInit,
@@ -323,6 +352,7 @@ function serializeCookie(name, value, options = {}) {
323
352
  if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
324
353
  if (options.httpOnly) cookie += "; HttpOnly";
325
354
  if (options.secure) cookie += "; Secure";
355
+ if (options.partitioned) cookie += "; Partitioned";
326
356
  if (options.sameSite) {
327
357
  const sameSite = options.sameSite.toLowerCase();
328
358
  cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
@@ -340,6 +370,7 @@ function clearFlashCookie() {
340
370
 
341
371
  const ERROR_HEADER = "X-Server-Function-Error";
342
372
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
373
+ const REDIRECT_HEADER = "X-Server-Function-Redirect";
343
374
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
344
375
 
345
376
  const HEAD_ELIGIBLE_TAGS = new Set(["title", "meta", "link", "style", "script", "base"]);
@@ -378,7 +409,8 @@ function resourceIdentity(tag, props) {
378
409
  let id = "res:" + tag + ":" + (props.rel || "") + ":" + (props.href || props.src || "");
379
410
  for (let i = 0; i < RESOURCE_QUALIFIERS.length; i++) {
380
411
  const q = RESOURCE_QUALIFIERS[i];
381
- if (props[q] != null) id += ":" + q + "=" + props[q];
412
+ const value = props[q];
413
+ if (value != null) id += ":" + q + "=" + (value === true ? "" : value);
382
414
  }
383
415
  return id;
384
416
  }
@@ -440,6 +472,7 @@ function resolveAssets(moduleUrl, manifest) {
440
472
  if (!entry) return null;
441
473
  const css = [];
442
474
  const js = [];
475
+ let preloads;
443
476
  const visited = new Set();
444
477
  const walk = key => {
445
478
  if (visited.has(key)) return;
@@ -448,13 +481,26 @@ function resolveAssets(moduleUrl, manifest) {
448
481
  if (!e) return;
449
482
  js.push(joinAssetPath(base, e.file));
450
483
  if (e.css) for (let i = 0; i < e.css.length; i++) css.push(joinAssetPath(base, e.css[i]));
484
+ if (e.preloads) {
485
+ for (let i = 0; i < e.preloads.length; i++) {
486
+ const link = e.preloads[i];
487
+ if (!link || typeof link.href !== "string" || !link.href) continue;
488
+ if (!preloads) preloads = [];
489
+ preloads.push({
490
+ ...link,
491
+ href: joinAssetPath(base, link.href)
492
+ });
493
+ }
494
+ }
451
495
  if (e.imports) for (let i = 0; i < e.imports.length; i++) walk(e.imports[i]);
452
496
  };
453
497
  walk(moduleUrl);
454
- return {
498
+ const assets = {
455
499
  js,
456
500
  css
457
501
  };
502
+ if (preloads) assets.preloads = preloads;
503
+ return assets;
458
504
  }
459
505
  function registerEntryAssets(manifest) {
460
506
  if (!manifest || typeof manifest === "function" || typeof manifest.resolve === "function") return;
@@ -464,6 +510,7 @@ function registerEntryAssets(manifest) {
464
510
  if (manifest[key].isEntry) {
465
511
  const assets = resolveAssets(key, manifest);
466
512
  if (assets) {
513
+ if (assets.preloads) for (let i = 0; i < assets.preloads.length; i++) ctx.registerAsset("preload", assets.preloads[i]);
467
514
  for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
468
515
  for (let i = 1; i < assets.js.length; i++) ctx.registerAsset("module", assets.js[i]);
469
516
  }
@@ -482,6 +529,7 @@ function createAssetTracking() {
482
529
  boundaryStyles,
483
530
  emittedAssets,
484
531
  inlineStyles,
532
+ preloadLinks: null,
485
533
  registerInlineStyle(desc) {
486
534
  let entry = inlineStyles.get(desc.id);
487
535
  if (!entry) {
@@ -573,6 +621,56 @@ function isCssUrl(url) {
573
621
  const q = url.search(/[?#]/);
574
622
  return (q === -1 ? url : url.slice(0, q)).endsWith(".css");
575
623
  }
624
+ const PRELOAD_LINK_ATTRIBUTES = ["type", "crossorigin", "integrity", "referrerpolicy", "fetchpriority", "media"];
625
+ function registerPreloadLink(tracking, headRegistry, link, nonce) {
626
+ if (!link || typeof link !== "object" || typeof link.href !== "string" || !link.href) {
627
+ return null;
628
+ }
629
+ if (typeof link.as !== "string") {
630
+ return null;
631
+ }
632
+ const as = asciiLowerCase(link.as);
633
+ let destination = null;
634
+ switch (as) {
635
+ case "script":
636
+ case "style":
637
+ destination = as;
638
+ break;
639
+ case "fetch":
640
+ case "font":
641
+ case "image":
642
+ case "track":
643
+ break;
644
+ default:
645
+ return null;
646
+ }
647
+ const props = {
648
+ rel: "preload",
649
+ href: link.href,
650
+ as
651
+ };
652
+ for (let i = 0; i < PRELOAD_LINK_ATTRIBUTES.length; i++) {
653
+ const name = PRELOAD_LINK_ATTRIBUTES[i];
654
+ const value = link[name];
655
+ if (value == null || value === false) continue;
656
+ props[name] = value === true ? "" : String(value);
657
+ }
658
+ const identity = resourceIdentity("link", props);
659
+ if (headRegistry.resources.has(identity)) return null;
660
+ const attrs = headAttrRecord(props);
661
+ const nonceValue = destination && nonce && nonce[destination];
662
+ if (typeof nonceValue === "string" && nonceValue) attrs.nonce = nonceValue;
663
+ const entry = {
664
+ href: props.href,
665
+ attrs,
666
+ attrHtml: renderHeadAttrHtml(props) + nonceAttr(nonce, destination)
667
+ };
668
+ headRegistry.resources.add(identity);
669
+ let links = tracking.preloadLinks;
670
+ if (!links) tracking.preloadLinks = links = [];
671
+ links.push(entry);
672
+ return entry;
673
+ }
576
674
  function createHeadRegistry() {
577
675
  return {
578
676
  pending: [],
@@ -1049,6 +1147,10 @@ function renderToString(code, options = {}) {
1049
1147
  serializer.write(id, p);
1050
1148
  },
1051
1149
  registerAsset(type, value) {
1150
+ if (type === "preload") {
1151
+ registerPreloadLink(tracking, headRegistry, value, nonce);
1152
+ return;
1153
+ }
1052
1154
  if (type === "inline-style") {
1053
1155
  tracking.registerInlineStyle(value);
1054
1156
  return;
@@ -1076,7 +1178,7 @@ function renderToString(code, options = {}) {
1076
1178
  solidJs.sharedConfig.context.noHydrate = true;
1077
1179
  serializer.close();
1078
1180
  const head = renderShellHead(headRegistry, nonce, null, noScripts);
1079
- return assembleDocument(resolveSSRSelectValues(html), tracking.emittedAssets, tracking.inlineStyles, scripts.length ? scripts : "", nonce, head, onHead);
1181
+ return assembleDocument(resolveSSRSelectValues(html), tracking.emittedAssets, tracking.preloadLinks, tracking.inlineStyles, scripts.length ? scripts : "", nonce, head, onHead);
1080
1182
  }
1081
1183
  function renderToStream(code, options = {}) {
1082
1184
  let {
@@ -1230,6 +1332,8 @@ function renderToStream(code, options = {}) {
1230
1332
  asset(type, value) {
1231
1333
  if (type === "module") {
1232
1334
  buffer.write(`<link rel="modulepreload" href="${value}"${nonceAttr(nonce, "script")}>`);
1335
+ } else if (type === "preload") {
1336
+ buffer.write(`<link${value.attrHtml}>`);
1233
1337
  } else if (type === "inline-style") {
1234
1338
  buffer.write(renderInlineStyle(value, nonce));
1235
1339
  } else if (type === "head-tag") {
@@ -1237,7 +1341,7 @@ function renderToStream(code, options = {}) {
1237
1341
  }
1238
1342
  },
1239
1343
  shell(shellHtml, meta) {
1240
- buffer.write(assembleDocument(shellHtml, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce, meta.head, onHead));
1344
+ buffer.write(assembleDocument(shellHtml, meta.preloads, meta.preloadLinks, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce, meta.head, onHead));
1241
1345
  },
1242
1346
  ...options.sink
1243
1347
  };
@@ -1270,6 +1374,29 @@ function renderToStream(code, options = {}) {
1270
1374
  }
1271
1375
  };
1272
1376
  const registry = new Map();
1377
+ const pendingSerialized = new Map();
1378
+ const trackSerialized = (id, p) => {
1379
+ let settle;
1380
+ const raced = Promise.race([p, new Promise(r => settle = r)]);
1381
+ pendingSerialized.set(id, settle);
1382
+ const drop = () => pendingSerialized.delete(id);
1383
+ p.then(drop, drop);
1384
+ return raced;
1385
+ };
1386
+ const abandonSubtree = key => {
1387
+ for (const [k, entry] of registry) {
1388
+ if (k.length > key.length && k.startsWith(key)) {
1389
+ registry.delete(k);
1390
+ entry.resolve();
1391
+ }
1392
+ }
1393
+ for (const [id, settle] of pendingSerialized) {
1394
+ if (id.startsWith(key)) {
1395
+ pendingSerialized.delete(id);
1396
+ settle();
1397
+ }
1398
+ }
1399
+ };
1273
1400
  const writeTasks = () => {
1274
1401
  if (tasks.length && !completed && firstFlushed) {
1275
1402
  buffer.write(`<script${nonceAttr(nonce, "script")}>${tasks}</script>`);
@@ -1340,6 +1467,11 @@ function renderToStream(code, options = {}) {
1340
1467
  }, nonce, tags);
1341
1468
  },
1342
1469
  registerAsset(type, value) {
1470
+ if (type === "preload") {
1471
+ const entry = registerPreloadLink(tracking, headRegistry, value, nonce);
1472
+ if (entry && firstFlushed) sink.asset("preload", entry);
1473
+ return;
1474
+ }
1343
1475
  if (type === "inline-style") {
1344
1476
  const entry = tracking.registerInlineStyle(value);
1345
1477
  if (firstFlushed && !tracking.currentBoundaryId && !entry.emitted) {
@@ -1384,13 +1516,14 @@ function renderToStream(code, options = {}) {
1384
1516
  },
1385
1517
  serialize(id, p, deferStream) {
1386
1518
  if (solidJs.sharedConfig.context.noHydrate) return;
1387
- if (!firstFlushed && p && typeof p === "object" && "then" in p) {
1388
- if (deferStream) {
1519
+ if (p && typeof p === "object" && typeof p.then === "function") {
1520
+ if (!firstFlushed && deferStream) {
1389
1521
  blockingPromises.add(p);
1390
1522
  p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
1391
1523
  return;
1392
1524
  }
1393
- if (canBatchStubs && !shellCompleted) {
1525
+ p = trackSerialized(id, p);
1526
+ if (!firstFlushed && canBatchStubs && !shellCompleted) {
1394
1527
  (stubBatch ||= new Map()).set(id, p);
1395
1528
  return;
1396
1529
  }
@@ -1435,6 +1568,7 @@ function renderToStream(code, options = {}) {
1435
1568
  if (registry.has(key)) {
1436
1569
  const item = registry.get(key);
1437
1570
  registry.delete(key);
1571
+ if (error) abandonSubtree(key);
1438
1572
  if (item.children) {
1439
1573
  for (const k in item.children) {
1440
1574
  value = replacePlaceholder(value, k, item.children[k]);
@@ -1559,6 +1693,7 @@ function renderToStream(code, options = {}) {
1559
1693
  const head = renderShellHead(headRegistry, nonce, k => registry.has(k), noScripts);
1560
1694
  sink.shell(resolveSSRSelectValues(html), {
1561
1695
  preloads: tracking.emittedAssets,
1696
+ preloadLinks: tracking.preloadLinks,
1562
1697
  inlineStyles: tracking.inlineStyles,
1563
1698
  tasks,
1564
1699
  head
@@ -2376,6 +2511,7 @@ function ssr(t) {
2376
2511
  return result;
2377
2512
  }
2378
2513
  function ssrClassName(value) {
2514
+ if (typeof value === "number") return "" + value;
2379
2515
  if (!value) return "";
2380
2516
  if (typeof value === "string") return escape(value, true);
2381
2517
  value = classListToObject(value);
@@ -2705,12 +2841,12 @@ function allSettled(promises) {
2705
2841
  return;
2706
2842
  });
2707
2843
  }
2708
- function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
2844
+ function assembleDocument(html, emittedAssets, preloadLinks, inlineStyles, scripts, nonce, headTags, onHead) {
2709
2845
  const scriptTag = scripts ? `<script${nonceAttr(nonce, "script")}>${scripts}</script>` : "";
2710
2846
  const title = headTags ? headTags.title : null;
2711
2847
  let headTagsHtml = headTags ? headTags.html : "";
2712
2848
  const headPrelude = headTags ? headTags.prelude : "";
2713
- if (!onHead && title == null && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2849
+ if (!onHead && title == null && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !preloadLinks && !(inlineStyles && inlineStyles.size)) {
2714
2850
  if (!scriptTag) return html;
2715
2851
  const xs = html.indexOf("<!--xs-->");
2716
2852
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
@@ -2729,7 +2865,7 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2729
2865
  if (title != null) {
2730
2866
  titleHtml = headTags.noScripts ? `<title data-dh="title">${escape(title)}</title>` : `<script${nonceAttr(nonce, "script")}>(function(x,t){(t=document.querySelector("title"))?(t.hasAttribute("data-dh")||t.setAttribute("data-dhf",t.textContent),t.textContent=x):(document.title=x,t=document.querySelector("title"));t&&t.setAttribute("data-dh","title")})(${JSON.stringify(title).replace(/</g, "\\u003C")})</script>`;
2731
2867
  }
2732
- onHead(headPrelude + headTagsHtml + titleHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2868
+ onHead(headPrelude + headTagsHtml + titleHtml + renderHeadAssets(emittedAssets, preloadLinks, inlineStyles, nonce));
2733
2869
  }
2734
2870
  if (!scriptTag) return html;
2735
2871
  const xs = html.indexOf("<!--xs-->");
@@ -2749,13 +2885,13 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2749
2885
  headTagsHtml = `<title data-dh="title">${winner}</title>` + headTagsHtml;
2750
2886
  }
2751
2887
  }
2752
- const head = headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce);
2888
+ const head = headTagsHtml + renderHeadAssets(emittedAssets, preloadLinks, inlineStyles, nonce);
2753
2889
  if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
2754
2890
  const xsIdx = html.indexOf("<!--xs-->");
2755
2891
  if (xsIdx === -1) return html.slice(0, headIdx) + head + html.slice(headIdx) + scriptTag;
2756
2892
  return xsIdx < headIdx ? html.slice(0, xsIdx) + scriptTag + html.slice(xsIdx, headIdx) + head + html.slice(headIdx) : html.slice(0, headIdx) + head + html.slice(headIdx, xsIdx) + scriptTag + html.slice(xsIdx);
2757
2893
  }
2758
- function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2894
+ function renderHeadAssets(emittedAssets, preloadLinks, inlineStyles, nonce) {
2759
2895
  let head = "";
2760
2896
  const styleAttr = nonceAttr(nonce, "style");
2761
2897
  const scriptAttr = nonceAttr(nonce, "script");
@@ -2764,6 +2900,9 @@ function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2764
2900
  head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}"${styleAttr}>` : `<link rel="modulepreload" href="${url}"${scriptAttr}>`;
2765
2901
  }
2766
2902
  }
2903
+ if (preloadLinks) {
2904
+ for (const entry of preloadLinks) head += `<link${entry.attrHtml}>`;
2905
+ }
2767
2906
  if (inlineStyles && inlineStyles.size) {
2768
2907
  for (const entry of inlineStyles.values()) {
2769
2908
  if (entry.emitted) continue;
@@ -2858,7 +2997,8 @@ function classListToObject(classList) {
2858
2997
  function flattenClassList(list, result) {
2859
2998
  for (let i = 0, len = list.length; i < len; i++) {
2860
2999
  const item = list[i];
2861
- if (Array.isArray(item)) flattenClassList(item, result);else if (typeof item === "object" && item != null) Object.assign(result, item);else if (item || item === 0) result[item] = true;
3000
+ if (Array.isArray(item)) flattenClassList(item, result);else if (typeof item === "object" && item != null) Object.assign(result, item);
3001
+ else if (typeof item !== "boolean" && (item || item === 0)) result[item] = true;
2862
3002
  }
2863
3003
  }
2864
3004
  function tryResolveString(node) {
@@ -3028,7 +3168,8 @@ function copyInitHeaders(init) {
3028
3168
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
3029
3169
  return headers;
3030
3170
  }
3031
- const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
3171
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
3172
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
3032
3173
  function fillsStubGap(key, headers, response) {
3033
3174
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
3034
3175
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -3044,27 +3185,20 @@ function commitEventResponse(response, event = getRequestEvent()) {
3044
3185
  if (fillsStubGap(key, response.headers, response)) hasGaps = true;
3045
3186
  });
3046
3187
  if (!cookies.length && !hasGaps) return response;
3047
- try {
3048
- for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
3049
- stub.headers.forEach((value, key) => {
3050
- if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
3051
- });
3052
- return response;
3053
- } catch {
3054
- const headers = copyInitHeaders(response.headers);
3055
- for (const cookie of cookies) headers.append("Set-Cookie", cookie);
3056
- stub.headers.forEach((value, key) => {
3057
- if (fillsStubGap(key, headers, response)) headers.set(key, value);
3058
- });
3059
- return new Response(response.body, {
3060
- status: response.status,
3061
- statusText: response.statusText,
3062
- headers
3063
- });
3064
- }
3188
+ const headers = copyInitHeaders(response.headers);
3189
+ for (const cookie of cookies) headers.append("Set-Cookie", cookie);
3190
+ stub.headers.forEach((value, key) => {
3191
+ if (fillsStubGap(key, headers, response)) headers.set(key, value);
3192
+ });
3193
+ return new Response(response.body, {
3194
+ status: response.status,
3195
+ statusText: response.statusText,
3196
+ headers
3197
+ });
3065
3198
  }
3066
3199
  function deriveHead(stub, responseInit = {}) {
3067
3200
  const headers = mergeStubHeaders(copyInitHeaders(responseInit.headers), stub);
3201
+ for (const header of COMPOSED_BODY_FRAMING) headers.delete(header);
3068
3202
  const status = stub && stub.status || responseInit.status || 200;
3069
3203
  const statusText = stub && stub.statusText || responseInit.statusText || undefined;
3070
3204
  return {
@@ -3148,7 +3282,7 @@ function createSSRResponse(result, event, options = {}) {
3148
3282
  end() {
3149
3283
  if (closed || !controller) return;
3150
3284
  const location = stub && stub.headers.get("Location");
3151
- if (location) {
3285
+ if (location && isHttpNavigationTarget(location)) {
3152
3286
  const attr = nonceAttr(nonce, "script");
3153
3287
  enqueue(`<script${attr}>window.location=${JSON.stringify(location).replace(/</g, "\\u003c")}</script>`);
3154
3288
  }
@@ -3227,6 +3361,11 @@ function registerClientOnlyPreload(moduleUrl) {
3227
3361
  const css = assets.css[i];
3228
3362
  if (typeof css === "string") registerAsset("style", css);else registerAsset("inline-style", css);
3229
3363
  }
3364
+ if (assets.preloads) {
3365
+ for (let i = 0; i < assets.preloads.length; i++) {
3366
+ registerAsset("preload", assets.preloads[i]);
3367
+ }
3368
+ }
3230
3369
  for (let i = 0; i < assets.js.length; i++) registerAsset("module", assets.js[i]);
3231
3370
  };
3232
3371
  const assets = resolve(moduleUrl);
@@ -3405,8 +3544,10 @@ exports.Dynamic = Dynamic;
3405
3544
  exports.HREF = HREF;
3406
3545
  exports.HydrationScript = HydrationScript;
3407
3546
  exports.MathMLElements = MathMLElements;
3547
+ exports.NULL_BODY_STATUSES = NULL_BODY_STATUSES;
3408
3548
  exports.Namespaces = Namespaces;
3409
3549
  exports.Portal = Portal;
3550
+ exports.RESPONSE_HEADER_VALUE_LIMIT = RESPONSE_HEADER_VALUE_LIMIT;
3410
3551
  exports.REVALIDATE_HEADER = REVALIDATE_HEADER;
3411
3552
  exports.RawTextElements = RawTextElements;
3412
3553
  exports.RequestContext = RequestContext;