@solidjs/web 2.0.0-rc.0 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/dev.cjs +40 -11
  3. package/dist/dev.js +39 -12
  4. package/dist/server.cjs +419 -69
  5. package/dist/server.js +411 -73
  6. package/dist/web.cjs +37 -11
  7. package/dist/web.js +36 -12
  8. package/frames/dist/client.cjs +94 -8
  9. package/frames/dist/client.dev.cjs +97 -8
  10. package/frames/dist/client.dev.js +98 -9
  11. package/frames/dist/client.js +95 -9
  12. package/frames/dist/server.cjs +266 -56
  13. package/frames/dist/server.js +267 -57
  14. package/package.json +4 -3
  15. package/serialization/dist/decode.cjs +32 -3
  16. package/serialization/dist/decode.js +33 -4
  17. package/serialization/dist/serialization.cjs +32 -3
  18. package/serialization/dist/serialization.js +33 -4
  19. package/server-functions/dist/client.cjs +196 -8
  20. package/server-functions/dist/client.js +195 -9
  21. package/server-functions/dist/server.cjs +201 -36
  22. package/server-functions/dist/server.dev.cjs +201 -36
  23. package/server-functions/dist/server.dev.js +199 -37
  24. package/server-functions/dist/server.js +199 -37
  25. package/types/core.d.ts +3 -0
  26. package/types/frames/frame-client.d.ts +18 -0
  27. package/types/index.d.ts +16 -2
  28. package/types/jsx.d.ts +9 -0
  29. package/types/server-functions/client.d.ts +61 -0
  30. package/types/server-functions/server.d.ts +94 -1
  31. package/types/server-mock.d.ts +11 -2
  32. package/types/server.d.ts +23 -2
  33. package/types-cjs/core.d.cts +3 -0
  34. package/types-cjs/frames/frame-client.d.cts +18 -0
  35. package/types-cjs/index.d.cts +16 -2
  36. package/types-cjs/jsx.d.cts +9 -0
  37. package/types-cjs/server-functions/client.d.cts +61 -0
  38. package/types-cjs/server-functions/server.d.cts +94 -1
  39. package/types-cjs/server-mock.d.cts +11 -2
  40. package/types-cjs/server.d.cts +23 -2
@@ -1,5 +1,5 @@
1
1
  import { createMemo, runWithOwner, createOwner, sharedConfig, createRoot, ssrHandleError, getOwner, inServerComponentScope, creationStamp, NoHydration, runInServerComponentScope, Hydration, getProjectionTrace } from 'solid-js';
2
- import { Feature, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
2
+ import { Feature, createStream, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
3
3
  import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
4
4
 
5
5
  const STATE = Symbol.for("dom-expressions.container-trace-state");
@@ -10,6 +10,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
10
10
  function setContainerTraceResolver(fn) {
11
11
  state.resolveTrace = fn;
12
12
  }
13
+ function setContainerTraceStreamMint(fn) {
14
+ state.streamOf = fn;
15
+ }
13
16
  function isContainerTraced(value) {
14
17
  const resolve = state.resolveTrace;
15
18
  return !!(resolve && value && typeof value === "object" && resolve(value));
@@ -58,9 +61,10 @@ function materialize(marker) {
58
61
  }
59
62
  function parseTrace(value, ctx) {
60
63
  const trace = value[TRACE];
64
+ const sub = trace.subscribe();
61
65
  return {
62
66
  a: trace.array ? 1 : 0,
63
- i: ctx.parse(trace.subscribe())
67
+ i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
64
68
  };
65
69
  }
66
70
  const ContainerTracePlugin = {
@@ -74,9 +78,10 @@ const ContainerTracePlugin = {
74
78
  },
75
79
  async async(value, ctx) {
76
80
  const trace = value[TRACE];
81
+ const sub = trace.subscribe();
77
82
  return {
78
83
  a: trace.array ? 1 : 0,
79
- i: await ctx.parse(trace.subscribe())
84
+ i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
80
85
  };
81
86
  },
82
87
  stream: parseTrace
@@ -101,6 +106,18 @@ const ssrAsyncValue = value => createMemo(() => value, {
101
106
  serialize: false
102
107
  });
103
108
 
109
+ setContainerTraceStreamMint(iterable => {
110
+ const stream = createStream();
111
+ (async () => {
112
+ try {
113
+ for await (const value of iterable) stream.next(value);
114
+ stream.return(undefined);
115
+ } catch (error) {
116
+ stream.throw(error);
117
+ }
118
+ })();
119
+ return stream;
120
+ });
104
121
  const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
105
122
  CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
106
123
  FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
@@ -480,6 +497,7 @@ function registerEntryAssets(manifest) {
480
497
  const assets = resolveAssets(key, manifest);
481
498
  if (assets) {
482
499
  for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
500
+ for (let i = 1; i < assets.js.length; i++) ctx.registerAsset("module", assets.js[i]);
483
501
  }
484
502
  return;
485
503
  }
@@ -739,7 +757,7 @@ function emitHeadResource(registry, context, tracking, emitResource, nonce, desc
739
757
  if (!styles) tracking.boundaryStyles.set(tracking.currentBoundaryId, styles = new Set());
740
758
  styles.add(entry);
741
759
  }
742
- const markup = `<link${attrHtml}>`;
760
+ const markup = `<link${attrHtml}${nonceAttr(nonce, "style")}>`;
743
761
  if (emitResource) emitResource(markup, entry);else {
744
762
  registry.eagerHtml += markup;
745
763
  entry.emitted = true;
@@ -837,7 +855,7 @@ function headGroupSignature(winner) {
837
855
  }
838
856
  return sig;
839
857
  }
840
- function renderShellHead(registry, nonce, isPendingFragment) {
858
+ function renderShellHead(registry, nonce, isPendingFragment, noScripts) {
841
859
  commitHeadBoundary(registry, "", isPendingFragment);
842
860
  registry.shellFlushed = true;
843
861
  const winners = resolveHead(registry.committed);
@@ -847,8 +865,14 @@ function renderShellHead(registry, nonce, isPendingFragment) {
847
865
  let metas = "";
848
866
  let others = "";
849
867
  let scripts = "";
868
+ let title = null;
850
869
  for (const [identity, winner] of winners) {
851
870
  registry.flushed.set(identity, headGroupSignature(winner));
871
+ if (identity === "title") {
872
+ const children = winner.tags[0].props.children;
873
+ title = children == null ? "" : String(children);
874
+ continue;
875
+ }
852
876
  for (let i = 0; i < winner.tags.length; i++) {
853
877
  const t = winner.tags[i];
854
878
  const markup = renderHeadTagMarkup(t.tag, t.props, identity, nonce);
@@ -857,10 +881,12 @@ function renderShellHead(registry, nonce, isPendingFragment) {
857
881
  }
858
882
  return {
859
883
  prelude,
860
- html: registry.eagerHtml + links + metas + others + scripts
884
+ html: registry.eagerHtml + links + metas + others + scripts,
885
+ title,
886
+ noScripts
861
887
  };
862
888
  }
863
- function flushHeadFragment(registry, boundary) {
889
+ function flushHeadFragment(registry, boundary, nonce) {
864
890
  const groups = commitHeadBoundary(registry, boundary);
865
891
  if (!groups.length) return null;
866
892
  const winners = resolveHead(registry.committed);
@@ -891,12 +917,75 @@ function flushHeadFragment(registry, boundary) {
891
917
  if (v == null || v === false) continue;
892
918
  attrs[name] = v === true ? "" : String(v);
893
919
  }
920
+ if (nonce && !hasNonceProp(t.props)) {
921
+ const destination = nonceDestination(t.tag, t.props);
922
+ const value = destination && nonce[destination];
923
+ if (value) attrs.nonce = String(value);
924
+ }
894
925
  const children = t.props.children;
895
926
  ops.push(["a", identity, t.tag, attrs, children == null ? null : String(children)]);
896
927
  }
897
928
  }
898
929
  return ops.length ? ops : null;
899
930
  }
931
+ function normalizeNonce(nonce) {
932
+ if (nonce == null) return undefined;
933
+ if (typeof nonce === "string") {
934
+ const attr = nonce ? ` nonce="${escape(nonce, true)}"` : "";
935
+ return {
936
+ script: nonce,
937
+ style: nonce,
938
+ scriptAttr: attr,
939
+ styleAttr: attr
940
+ };
941
+ }
942
+ const script = nonce.script;
943
+ const style = nonce.style;
944
+ if (!script && !style) return undefined;
945
+ return {
946
+ script,
947
+ style,
948
+ scriptAttr: typeof script === "string" && script ? ` nonce="${escape(script, true)}"` : "",
949
+ styleAttr: typeof style === "string" && style ? ` nonce="${escape(style, true)}"` : ""
950
+ };
951
+ }
952
+ function asciiLowerCase(value) {
953
+ return value.replace(/[A-Z]/g, c => String.fromCharCode(c.charCodeAt(0) + 32));
954
+ }
955
+ function hasNonceProp(props) {
956
+ for (const name in props) if (name.length === 5 && asciiLowerCase(name) === "nonce" && props[name] != null) return true;
957
+ return false;
958
+ }
959
+ function nonceDestination(tag, props) {
960
+ if (tag === "script") return "script";
961
+ if (tag === "style") return "style";
962
+ if (tag !== "link") return null;
963
+ const rel = props.rel;
964
+ if (typeof rel !== "string") return null;
965
+ const rels = asciiLowerCase(rel).split(/[\t\n\f\r ]+/);
966
+ if (rels.includes("stylesheet")) return "style";
967
+ const isModulePreload = rels.includes("modulepreload");
968
+ if (!isModulePreload && !rels.includes("preload")) return null;
969
+ const as = typeof props.as === "string" ? asciiLowerCase(props.as) : "";
970
+ if (as === "style") return "style";
971
+ if (as === "script") return "script";
972
+ if (!isModulePreload) return null;
973
+ switch (as) {
974
+ case "fetch":
975
+ case "font":
976
+ case "image":
977
+ case "json":
978
+ case "text":
979
+ case "track":
980
+ return null;
981
+ default:
982
+ return "script";
983
+ }
984
+ }
985
+ function nonceAttr(nonce, destination) {
986
+ if (!nonce || !destination) return "";
987
+ return destination === "script" ? nonce.scriptAttr : nonce.styleAttr;
988
+ }
900
989
  function renderHeadAttrHtml(props) {
901
990
  let attrs = "";
902
991
  for (const name in props) {
@@ -925,17 +1014,16 @@ function headAttrRecord(props, skipRelHref) {
925
1014
  function renderHeadTagMarkup(tag, props, identity, nonce) {
926
1015
  let attrs = renderHeadAttrHtml(props);
927
1016
  if (identity != null) attrs += ` data-dh="${escape(identity, true)}"`;
928
- if (nonce && (tag === "script" || tag === "style")) attrs += ` nonce="${nonce}"`;
1017
+ if (nonce && !hasNonceProp(props)) attrs += nonceAttr(nonce, nonceDestination(tag, props));
929
1018
  if (tag === "meta" || tag === "link" || tag === "base") return `<${tag}${attrs}>`;
930
1019
  let body = props.children == null ? "" : String(props.children);
931
1020
  if (tag === "script") body = body.replace(/<\/(script)/gi, "<\\/$1");else if (tag === "style") body = escapeStyleContent(body);else body = escape(body);
932
1021
  return `<${tag}${attrs}>${body}</${tag}>`;
933
1022
  }
934
1023
  const REPLACE_SCRIPT = `function $df(e){return _$HY.f?_$HY.f(e):$dfr(e)}function $dfr(e,n,o,t){if(!(n=document.getElementById(e)))return 0;if(!(o=document.getElementById("pl-"+e)))return(_$HY.dq=_$HY.dq||{})[e]=1,0;for(;o&&(8!==o.nodeType||o.nodeValue!=="pl-"+e);)t=o.nextSibling,o.remove(),o=t;t=o.parentNode,o.replaceWith(n.content),n.remove(),(_$HY.v=_$HY.v||{})[e]=1,_$HY.fe(e,t),_$HY.hp&&_$HY.hp[e]&&($dh(_$HY.hp[e]),delete _$HY.hp[e]),$dfd();return 1}function $dfl(e,o,n){if(!(o=document.getElementById("pl-"+e)))return(_$HY.dlq=_$HY.dlq||{})[e]=1,0;if(o._$fl)return 1;for(n=o.nextSibling;n;){if(8===n.nodeType&&n.nodeValue==="pl-"+e){o.parentNode&&o.parentNode.insertBefore(o.content.cloneNode(!0),n),o._$fl=1,$dfd();return 1}n=n.nextSibling}return 0}function $dflj(e,i){for(i=0;i<e.length;i++)$dfl(e[i])}function $dfd(e,i){if(e=_$HY.dq){_$HY.dq=0;for(i in e)$df(i)}if(e=_$HY.dlq){_$HY.dlq=0;for(i in e)$dfl(i)}}function $dfs(e,c,d){(_$HY.sc=_$HY.sc||{})[e]=c,d&&((_$HY.sd=_$HY.sd||{})[e]=1)}function $dfg(e,g,i,k){if(!(g=_$HY.sg&&_$HY.sg[e]))return;for(i=0;i<g.length;i++)if(_$HY.sc&&_$HY.sc[g[i]]>0)return;for(i=0;i<g.length;i++)k=g[i],delete _$HY.sg[k],$df(k)}function $dfc(e){if(--_$HY.sc[e]<=0){delete _$HY.sc[e],_$HY.sg&&_$HY.sg[e]?$dfg(e):!(_$HY.sd&&_$HY.sd[e])&&$df(e);_$HY.sd&&delete _$HY.sd[e]}}function $dfj(e,i,n){for(i=0;i<e.length;i++)if(_$HY.sc&&_$HY.sc[e[i]]>0){for(n=0;n<e.length;n++)(_$HY.sg=_$HY.sg||{})[e[n]]=e;return}for(i=0;i<e.length;i++)$df(e[i])}`;
935
- const HEAD_SCRIPT = `function $dha(o,i,e,n){for(i=0;i<o.length;i++)e=o[i],"t"==e[0]?((n=document.querySelector("title"))||(n=document.createElement("title"),document.head.appendChild(n)),n.textContent=e[1],n.setAttribute("data-dh","title")):"r"==e[0]?$dhr(e[1]):(n=document.createElement(e[2]),Object.keys(e[3]).forEach(function(a){n.setAttribute(a,e[3][a])}),null!=e[4]&&(n.textContent=e[4]),n.setAttribute("data-dh",e[1]),document.head.appendChild(n))}function $dhr(v,l,i){for(l=document.head.querySelectorAll("[data-dh]"),i=0;i<l.length;i++)l[i].getAttribute("data-dh")==v&&l[i].remove()}function $dh(o){_$HY.h?_$HY.h(o):$dha(o)}`;
1024
+ const HEAD_SCRIPT = `function $dha(o,i,e,n){for(i=0;i<o.length;i++)e=o[i],"t"==e[0]?((n=document.querySelector("title"))?n.hasAttribute("data-dh")||n.setAttribute("data-dhf",n.textContent):(n=document.createElement("title"),document.head.appendChild(n)),n.textContent=e[1],n.setAttribute("data-dh","title")):"r"==e[0]?$dhr(e[1]):(n=document.createElement(e[2]),Object.keys(e[3]).forEach(function(a){n.setAttribute(a,e[3][a])}),null!=e[4]&&(n.textContent=e[4]),n.setAttribute("data-dh",e[1]),document.head.appendChild(n))}function $dhr(v,l,i){for(l=document.head.querySelectorAll("[data-dh]"),i=0;i<l.length;i++)l[i].getAttribute("data-dh")==v&&l[i].remove()}function $dh(o){_$HY.h?_$HY.h(o):$dha(o)}`;
936
1025
  function renderToStream(code, options = {}) {
937
1026
  let {
938
- nonce,
939
1027
  onCompleteShell,
940
1028
  onCompleteAll,
941
1029
  renderId = "",
@@ -943,6 +1031,7 @@ function renderToStream(code, options = {}) {
943
1031
  manifest,
944
1032
  onHead
945
1033
  } = options;
1034
+ const nonce = normalizeNonce(options.nonce);
946
1035
  let dispose;
947
1036
  let dead = false;
948
1037
  const abandon = () => {
@@ -967,6 +1056,32 @@ function renderToStream(code, options = {}) {
967
1056
  } catch (_) {}
968
1057
  abandon();
969
1058
  };
1059
+ const coalesceWrites = (writeRaw, endRaw) => {
1060
+ let buf = "";
1061
+ let scheduled = false;
1062
+ const flush = () => {
1063
+ scheduled = false;
1064
+ if (!buf) return;
1065
+ const out = buf;
1066
+ buf = "";
1067
+ writeRaw(out);
1068
+ };
1069
+ return {
1070
+ write(payload) {
1071
+ buf += payload;
1072
+ if (buf.length >= 16384) return flush();
1073
+ if (!scheduled) {
1074
+ scheduled = true;
1075
+ deferFlush(flush);
1076
+ }
1077
+ },
1078
+ flush,
1079
+ end() {
1080
+ flush();
1081
+ endRaw();
1082
+ }
1083
+ };
1084
+ };
970
1085
  const guardSink = w => ({
971
1086
  write(payload) {
972
1087
  if (dead) return;
@@ -986,6 +1101,23 @@ function renderToStream(code, options = {}) {
986
1101
  }
987
1102
  });
988
1103
  const blockingPromises = new Set();
1104
+ const canBatchStubs = !options.serializer && !options.sink;
1105
+ let stubBatch = null;
1106
+ const STUB_BATCH_KEY = "$B";
1107
+ const flushStubBatch = () => {
1108
+ if (!stubBatch) return;
1109
+ const batch = stubBatch;
1110
+ stubBatch = null;
1111
+ if (batch.size === 1) {
1112
+ const [id, p] = batch.entries().next().value;
1113
+ serializer.write(id, p);
1114
+ return;
1115
+ }
1116
+ const obj = {};
1117
+ for (const [id, p] of batch) obj[id] = p;
1118
+ serializer.write(STUB_BATCH_KEY, obj);
1119
+ pushTask(`(b=>{for(var k in b)_$HY.r[k]=b[k];delete _$HY.r["${STUB_BATCH_KEY}"]})(_$HY.r["${STUB_BATCH_KEY}"])`);
1120
+ };
989
1121
  let headerEmitted = false;
990
1122
  const pushTask = task => {
991
1123
  if (noScripts) return;
@@ -1022,10 +1154,11 @@ function renderToStream(code, options = {}) {
1022
1154
  buffer.write(renderInlineStyle(styles.inline[i], nonce));
1023
1155
  }
1024
1156
  if (styles.links.length) {
1157
+ const styleAttr = nonceAttr(nonce, "style");
1025
1158
  emitTask(`$dfs("${key}",${styles.links.length},${deferActivation ? 1 : 0})`);
1026
1159
  writeTasks();
1027
1160
  for (const entry of styles.links) {
1028
- buffer.write(typeof entry === "string" ? `<link rel="stylesheet" href="${entry}" onload="$dfc('${key}')" onerror="$dfc('${key}')">` : `<link${entry.attrHtml} onload="$dfc('${key}')" onerror="$dfc('${key}')">`);
1161
+ buffer.write(typeof entry === "string" ? `<link rel="stylesheet" href="${entry}"${styleAttr} onload="$dfc('${key}')" onerror="$dfc('${key}')">` : `<link${entry.attrHtml}${styleAttr} onload="$dfc('${key}')" onerror="$dfc('${key}')">`);
1029
1162
  }
1030
1163
  buffer.write(`<template id="${key}">${value}</template>`);
1031
1164
  } else {
@@ -1040,7 +1173,7 @@ function renderToStream(code, options = {}) {
1040
1173
  },
1041
1174
  asset(type, value) {
1042
1175
  if (type === "module") {
1043
- buffer.write(`<link rel="modulepreload" href="${value}">`);
1176
+ buffer.write(`<link rel="modulepreload" href="${value}"${nonceAttr(nonce, "script")}>`);
1044
1177
  } else if (type === "inline-style") {
1045
1178
  buffer.write(renderInlineStyle(value, nonce));
1046
1179
  } else if (type === "head-tag") {
@@ -1075,6 +1208,7 @@ function renderToStream(code, options = {}) {
1075
1208
  context.live.end = null;
1076
1209
  end();
1077
1210
  }
1211
+ flushStubBatch();
1078
1212
  serializer.flush();
1079
1213
  }));
1080
1214
  }
@@ -1082,7 +1216,7 @@ function renderToStream(code, options = {}) {
1082
1216
  const registry = new Map();
1083
1217
  const writeTasks = () => {
1084
1218
  if (tasks.length && !completed && firstFlushed) {
1085
- buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
1219
+ buffer.write(`<script${nonceAttr(nonce, "script")}>${tasks}</script>`);
1086
1220
  tasks = "";
1087
1221
  }
1088
1222
  timer = null;
@@ -1136,7 +1270,7 @@ function renderToStream(code, options = {}) {
1136
1270
  };
1137
1271
  sharedConfig.context = context = {
1138
1272
  async: true,
1139
- nonce,
1273
+ nonce: options.nonce,
1140
1274
  live: {},
1141
1275
  registerHeadTags(tags) {
1142
1276
  registerHeadTags(headRegistry, context, tracking,
@@ -1194,10 +1328,18 @@ function renderToStream(code, options = {}) {
1194
1328
  },
1195
1329
  serialize(id, p, deferStream) {
1196
1330
  if (sharedConfig.context.noHydrate) return;
1197
- if (!firstFlushed && deferStream && typeof p === "object" && "then" in p) {
1198
- blockingPromises.add(p);
1199
- p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
1200
- } else serializer.write(id, p);
1331
+ if (!firstFlushed && p && typeof p === "object" && "then" in p) {
1332
+ if (deferStream) {
1333
+ blockingPromises.add(p);
1334
+ p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
1335
+ return;
1336
+ }
1337
+ if (canBatchStubs && !shellCompleted) {
1338
+ (stubBatch ||= new Map()).set(id, p);
1339
+ return;
1340
+ }
1341
+ }
1342
+ serializer.write(id, p);
1201
1343
  },
1202
1344
  escape: escape,
1203
1345
  resolve: resolveSSRNode,
@@ -1231,7 +1373,7 @@ function renderToStream(code, options = {}) {
1231
1373
  queue(flushEnd);
1232
1374
  }))
1233
1375
  });
1234
- serializer.write(key + "_fr", p);
1376
+ if (canBatchStubs && !shellCompleted) (stubBatch ||= new Map()).set(key + "_fr", p);else serializer.write(key + "_fr", p);
1235
1377
  }
1236
1378
  return (value, error) => {
1237
1379
  if (registry.has(key)) {
@@ -1262,9 +1404,9 @@ function renderToStream(code, options = {}) {
1262
1404
  } else {
1263
1405
  serializeFragmentAssets(key, tracking.boundaryModules, context);
1264
1406
  const styles = collectStreamStyles(key, tracking, headStyles);
1265
- const headOps = error ? null : flushHeadFragment(headRegistry, key);
1407
+ const headOps = error ? null : flushHeadFragment(headRegistry, key, nonce);
1266
1408
  if (headOps) emitHeadOps(key, headOps);
1267
- sink.fragment(key, value !== undefined ? value : " ", {
1409
+ sink.fragment(key, resolveSSRSelectValues(value !== undefined ? value : " "), {
1268
1410
  styles,
1269
1411
  revealGroup,
1270
1412
  error
@@ -1348,15 +1490,18 @@ function renderToStream(code, options = {}) {
1348
1490
  function doShell() {
1349
1491
  if (shellCompleted) return;
1350
1492
  sharedConfig.context = context;
1493
+ const blockersBefore = blockingPromises.size;
1351
1494
  if (!resolveRootHoles()) return;
1495
+ if (blockingPromises.size !== blockersBefore) return;
1352
1496
  if (!headShellReady(headRegistry, p => blockingPromises.add(p))) return;
1353
1497
  headStyles = new Set();
1354
1498
  for (const url of tracking.emittedAssets) {
1355
1499
  if (isCssUrl(url)) headStyles.add(url);
1356
1500
  }
1357
1501
  serializeRootAssets();
1358
- const head = renderShellHead(headRegistry, nonce, k => registry.has(k));
1359
- sink.shell(html, {
1502
+ flushStubBatch();
1503
+ const head = renderShellHead(headRegistry, nonce, k => registry.has(k), noScripts);
1504
+ sink.shell(resolveSSRSelectValues(html), {
1360
1505
  preloads: tracking.emittedAssets,
1361
1506
  inlineStyles: tracking.inlineStyles,
1362
1507
  tasks,
@@ -1376,6 +1521,7 @@ function renderToStream(code, options = {}) {
1376
1521
  let drainTurn = 0;
1377
1522
  const scheduleFlush = fn => {
1378
1523
  const attempt = () => {
1524
+ flushStubBatch();
1379
1525
  if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
1380
1526
  if (registry.size !== lastRegistrySize) drainTurn = 0;
1381
1527
  lastRegistrySize = registry.size;
@@ -1423,22 +1569,18 @@ function renderToStream(code, options = {}) {
1423
1569
  }
1424
1570
  };
1425
1571
  writer.closed && writer.closed.catch(failed);
1426
- writable = {
1427
- end() {
1428
- pendingWrites.then(() => {
1429
- ended = true;
1430
- writer.releaseLock();
1431
- w.close().catch(() => {});
1432
- resolve();
1433
- });
1434
- }
1435
- };
1436
- buffer = {
1437
- write(payload) {
1438
- pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(failed);
1439
- }
1440
- };
1572
+ buffer = writable = coalesceWrites(payload => {
1573
+ pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(failed);
1574
+ }, () => {
1575
+ pendingWrites.then(() => {
1576
+ ended = true;
1577
+ writer.releaseLock();
1578
+ w.close().catch(() => {});
1579
+ resolve();
1580
+ });
1581
+ });
1441
1582
  buffer.write(tmp);
1583
+ buffer.flush();
1442
1584
  firstFlushed = true;
1443
1585
  if (completed) {
1444
1586
  dispose();
@@ -1468,7 +1610,8 @@ function renderToStream(code, options = {}) {
1468
1610
  allSettled(blockingPromises).then(() => {
1469
1611
  scheduleFlush(() => {
1470
1612
  try {
1471
- if (!resolveRootHoles() || !headShellReady(headRegistry, p => blockingPromises.add(p))) return flush();
1613
+ const blockersBefore = blockingPromises.size;
1614
+ if (!resolveRootHoles() || blockingPromises.size !== blockersBefore || !headShellReady(headRegistry, p => blockingPromises.add(p))) return flush();
1472
1615
  } catch (err) {
1473
1616
  failRender(err);
1474
1617
  return resolve(tmp);
@@ -1497,8 +1640,10 @@ function renderToStream(code, options = {}) {
1497
1640
  return;
1498
1641
  }
1499
1642
  if (!shellCompleted) return flush();
1500
- buffer = writable = guardSink(w);
1643
+ const sink = guardSink(w);
1644
+ buffer = writable = coalesceWrites(sink.write, sink.end);
1501
1645
  buffer.write(tmp);
1646
+ buffer.flush();
1502
1647
  firstFlushed = true;
1503
1648
  if (completed) {
1504
1649
  dispose();
@@ -2152,6 +2297,12 @@ function ssr(t) {
2152
2297
  };
2153
2298
  return result;
2154
2299
  }
2300
+ const CLAIM_PROP = /*#__PURE__*/Symbol.for("dom-expressions.claim-prop");
2301
+ const CLAIMS_STREAM = 1;
2302
+ const CLAIMS_DOCUMENT = 2;
2303
+ function resolveSSRSelectValues(html) {
2304
+ return html;
2305
+ }
2155
2306
  function escape(s, attr) {
2156
2307
  const t = typeof s;
2157
2308
  if (t !== "string") {
@@ -2173,22 +2324,20 @@ function escape(s, attr) {
2173
2324
  return escapeSlow(s, attr, i);
2174
2325
  }
2175
2326
  const ESCAPE_CONTENT = /[&<]/;
2176
- const ESCAPE_ATTR = /[&"]/;
2327
+ const ESCAPE_ATTR = /[&"<]/;
2177
2328
  function escapeSlow(s, attr, start) {
2178
- const delim = attr ? '"' : "<";
2179
- const delimCode = attr ? 34 : 60;
2180
- const escDelim = attr ? "&quot;" : "&lt;";
2329
+ if (attr) return escapeAttrSlow(s, start);
2181
2330
  const c0 = s.charCodeAt(start);
2182
- let iDelim = c0 === delimCode ? start : s.indexOf(delim, start);
2331
+ let iDelim = c0 === 60 ? start : s.indexOf("<", start);
2183
2332
  let iAmp = c0 === 38 ? start : s.indexOf("&", start);
2184
2333
  let left = 0,
2185
2334
  out = "";
2186
2335
  while (iDelim >= 0 && iAmp >= 0) {
2187
2336
  if (iDelim < iAmp) {
2188
2337
  if (left < iDelim) out += s.substring(left, iDelim);
2189
- out += escDelim;
2338
+ out += "&lt;";
2190
2339
  left = iDelim + 1;
2191
- iDelim = s.indexOf(delim, left);
2340
+ iDelim = s.indexOf("<", left);
2192
2341
  } else {
2193
2342
  if (left < iAmp) out += s.substring(left, iAmp);
2194
2343
  out += "&amp;";
@@ -2199,9 +2348,9 @@ function escapeSlow(s, attr, start) {
2199
2348
  if (iDelim >= 0) {
2200
2349
  do {
2201
2350
  if (left < iDelim) out += s.substring(left, iDelim);
2202
- out += escDelim;
2351
+ out += "&lt;";
2203
2352
  left = iDelim + 1;
2204
- iDelim = s.indexOf(delim, left);
2353
+ iDelim = s.indexOf("<", left);
2205
2354
  } while (iDelim >= 0);
2206
2355
  } else while (iAmp >= 0) {
2207
2356
  if (left < iAmp) out += s.substring(left, iAmp);
@@ -2211,6 +2360,36 @@ function escapeSlow(s, attr, start) {
2211
2360
  }
2212
2361
  return left < s.length ? out + s.substring(left) : out;
2213
2362
  }
2363
+ function escapeAttrSlow(s, start) {
2364
+ const c0 = s.charCodeAt(start);
2365
+ let iQuot = c0 === 34 ? start : s.indexOf('"', start);
2366
+ let iAmp = c0 === 38 ? start : s.indexOf("&", start);
2367
+ let iLt = c0 === 60 ? start : s.indexOf("<", start);
2368
+ let left = 0,
2369
+ out = "";
2370
+ for (;;) {
2371
+ let i = -1,
2372
+ ent;
2373
+ if (iQuot >= 0) {
2374
+ i = iQuot;
2375
+ ent = "&quot;";
2376
+ }
2377
+ if (iAmp >= 0 && (i < 0 || iAmp < i)) {
2378
+ i = iAmp;
2379
+ ent = "&amp;";
2380
+ }
2381
+ if (iLt >= 0 && (i < 0 || iLt < i)) {
2382
+ i = iLt;
2383
+ ent = "&lt;";
2384
+ }
2385
+ if (i < 0) break;
2386
+ if (left < i) out += s.substring(left, i);
2387
+ out += ent;
2388
+ left = i + 1;
2389
+ if (i === iQuot) iQuot = s.indexOf('"', left);else if (i === iAmp) iAmp = s.indexOf("&", left);else iLt = s.indexOf("<", left);
2390
+ }
2391
+ return left < s.length ? out + s.substring(left) : out;
2392
+ }
2214
2393
  function tryJoinPlainSSRArray(nodes) {
2215
2394
  if (nodes.length === 0) return undefined;
2216
2395
  let out = "";
@@ -2226,6 +2405,7 @@ function tryJoinPlainSSRArray(nodes) {
2226
2405
  function queue(fn) {
2227
2406
  return Promise.resolve().then(fn);
2228
2407
  }
2408
+ const deferFlush = typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
2229
2409
  function allSettled(promises) {
2230
2410
  let size = promises.size;
2231
2411
  return Promise.allSettled(promises).then(() => {
@@ -2234,10 +2414,11 @@ function allSettled(promises) {
2234
2414
  });
2235
2415
  }
2236
2416
  function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
2237
- const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
2238
- const headTagsHtml = headTags ? headTags.html : "";
2417
+ const scriptTag = scripts ? `<script${nonceAttr(nonce, "script")}>${scripts}</script>` : "";
2418
+ const title = headTags ? headTags.title : null;
2419
+ let headTagsHtml = headTags ? headTags.html : "";
2239
2420
  const headPrelude = headTags ? headTags.prelude : "";
2240
- if (!onHead && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2421
+ if (!onHead && title == null && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2241
2422
  if (!scriptTag) return html;
2242
2423
  const xs = html.indexOf("<!--xs-->");
2243
2424
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
@@ -2249,15 +2430,33 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2249
2430
  html = html.slice(0, at) + headPrelude + html.slice(at);
2250
2431
  }
2251
2432
  }
2252
- const headIdx = html.indexOf("</head>");
2433
+ let headIdx = html.indexOf("</head>");
2253
2434
  if (headIdx === -1) {
2254
2435
  if (onHead) {
2255
- onHead(headPrelude + headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2436
+ let titleHtml = "";
2437
+ if (title != null) {
2438
+ 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>`;
2439
+ }
2440
+ onHead(headPrelude + headTagsHtml + titleHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2256
2441
  }
2257
2442
  if (!scriptTag) return html;
2258
2443
  const xs = html.indexOf("<!--xs-->");
2259
2444
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
2260
2445
  }
2446
+ if (title != null) {
2447
+ const winner = escape(title);
2448
+ const open = html.match(/<head(?:\s[^>]*)?>/);
2449
+ const from = open ? open.index + open[0].length : 0;
2450
+ const m = /<title(\s[^>]*)?>([\s\S]*?)<\/title>/.exec(html.slice(from, headIdx));
2451
+ if (m) {
2452
+ const at = from + m.index;
2453
+ const stash = m[2].replace(/"/g, "&quot;");
2454
+ html = html.slice(0, at) + `<title${m[1] || ""} data-dh="title" data-dhf="${stash}">${winner}</title>` + html.slice(at + m[0].length);
2455
+ headIdx = html.indexOf("</head>");
2456
+ } else {
2457
+ headTagsHtml = `<title data-dh="title">${winner}</title>` + headTagsHtml;
2458
+ }
2459
+ }
2261
2460
  const head = headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce);
2262
2461
  if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
2263
2462
  const xsIdx = html.indexOf("<!--xs-->");
@@ -2266,9 +2465,11 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2266
2465
  }
2267
2466
  function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2268
2467
  let head = "";
2468
+ const styleAttr = nonceAttr(nonce, "style");
2469
+ const scriptAttr = nonceAttr(nonce, "script");
2269
2470
  if (emittedAssets && emittedAssets.size) {
2270
2471
  for (const url of emittedAssets) {
2271
- head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}">` : `<link rel="modulepreload" href="${url}">`;
2472
+ head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}"${styleAttr}>` : `<link rel="modulepreload" href="${url}"${scriptAttr}>`;
2272
2473
  }
2273
2474
  }
2274
2475
  if (inlineStyles && inlineStyles.size) {
@@ -2283,7 +2484,9 @@ function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2283
2484
  function serializeFragmentAssets(key, boundaryModules, context, name = key) {
2284
2485
  const map = boundaryModules.get(key);
2285
2486
  if (!map || !Object.keys(map).length) return;
2286
- context.serialize(name + "_assets", map);
2487
+ context.serialize(name + "_assets", {
2488
+ ...map
2489
+ });
2287
2490
  }
2288
2491
  function propagateBoundaryStyles(childKey, parentKey, tracking) {
2289
2492
  const childStyles = tracking.getBoundaryStyles(childKey);
@@ -2328,12 +2531,15 @@ function escapeStyleContent(content) {
2328
2531
  }
2329
2532
  function renderInlineStyle(entry, nonce) {
2330
2533
  let attrs = "";
2534
+ let hasNonce = false;
2331
2535
  if (entry.attrs) {
2332
2536
  for (const name in entry.attrs) {
2537
+ if (name.length === 5 && asciiLowerCase(name) === "nonce") hasNonce = true;
2333
2538
  attrs += ` ${name}="${escape(String(entry.attrs[name]), true)}"`;
2334
2539
  }
2335
2540
  }
2336
- return `<style${nonce ? ` nonce="${nonce}"` : ""} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
2541
+ const nonceHtml = hasNonce ? "" : nonceAttr(nonce, "style");
2542
+ return `<style${nonceHtml} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
2337
2543
  }
2338
2544
  function waitForFragments(registry, key) {
2339
2545
  for (const k of [...registry.keys()].reverse()) {
@@ -2786,6 +2992,7 @@ function renderServerComponent(component, options = {}) {
2786
2992
  ctx.commit = sink.commit;
2787
2993
  ctx.commitEpoch = () => sink.epoch;
2788
2994
  ctx.liveHoles = createLiveHoles(sink);
2995
+ ctx.claims = CLAIMS_STREAM;
2789
2996
  }
2790
2997
  return serverComponentScope(() => component(props));
2791
2998
  };
@@ -3079,6 +3286,7 @@ function createDocumentSlotProps(clientProps, frameId) {
3079
3286
  return out;
3080
3287
  };
3081
3288
  fn.$lhSkip = true;
3289
+ fn[CLAIM_PROP] = prop;
3082
3290
  getters.set(prop, fn);
3083
3291
  }
3084
3292
  return fn;
@@ -3210,6 +3418,7 @@ function frameTransformDirectResult(value, {
3210
3418
  },
3211
3419
  serverOwned(() => {
3212
3420
  armDocumentLiveHoles(sharedConfig.context);
3421
+ sharedConfig.context.claims = CLAIMS_DOCUMENT;
3213
3422
  const slotProps = createDocumentSlotProps(props, id);
3214
3423
  return serverComponentScope(() => component(slotProps));
3215
3424
  }), {
@@ -3453,6 +3662,7 @@ function createSlotProps(sink, frame) {
3453
3662
  return slotRange(occurrence);
3454
3663
  };
3455
3664
  fn.$lhSkip = true;
3665
+ fn[CLAIM_PROP] = prop;
3456
3666
  getters.set(prop, fn);
3457
3667
  }
3458
3668
  return fn;