@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
@@ -12,6 +12,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
12
12
  function setContainerTraceResolver(fn) {
13
13
  state.resolveTrace = fn;
14
14
  }
15
+ function setContainerTraceStreamMint(fn) {
16
+ state.streamOf = fn;
17
+ }
15
18
  function isContainerTraced(value) {
16
19
  const resolve = state.resolveTrace;
17
20
  return !!(resolve && value && typeof value === "object" && resolve(value));
@@ -60,9 +63,10 @@ function materialize(marker) {
60
63
  }
61
64
  function parseTrace(value, ctx) {
62
65
  const trace = value[TRACE];
66
+ const sub = trace.subscribe();
63
67
  return {
64
68
  a: trace.array ? 1 : 0,
65
- i: ctx.parse(trace.subscribe())
69
+ i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
66
70
  };
67
71
  }
68
72
  const ContainerTracePlugin = {
@@ -76,9 +80,10 @@ const ContainerTracePlugin = {
76
80
  },
77
81
  async async(value, ctx) {
78
82
  const trace = value[TRACE];
83
+ const sub = trace.subscribe();
79
84
  return {
80
85
  a: trace.array ? 1 : 0,
81
- i: await ctx.parse(trace.subscribe())
86
+ i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
82
87
  };
83
88
  },
84
89
  stream: parseTrace
@@ -103,6 +108,18 @@ const ssrAsyncValue = value => solidJs.createMemo(() => value, {
103
108
  serialize: false
104
109
  });
105
110
 
111
+ setContainerTraceStreamMint(iterable => {
112
+ const stream = seroval.createStream();
113
+ (async () => {
114
+ try {
115
+ for await (const value of iterable) stream.next(value);
116
+ stream.return(undefined);
117
+ } catch (error) {
118
+ stream.throw(error);
119
+ }
120
+ })();
121
+ return stream;
122
+ });
106
123
  const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
107
124
  web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
108
125
  web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin,
@@ -482,6 +499,7 @@ function registerEntryAssets(manifest) {
482
499
  const assets = resolveAssets(key, manifest);
483
500
  if (assets) {
484
501
  for (let i = 0; i < assets.css.length; i++) ctx.registerAsset("style", assets.css[i]);
502
+ for (let i = 1; i < assets.js.length; i++) ctx.registerAsset("module", assets.js[i]);
485
503
  }
486
504
  return;
487
505
  }
@@ -741,7 +759,7 @@ function emitHeadResource(registry, context, tracking, emitResource, nonce, desc
741
759
  if (!styles) tracking.boundaryStyles.set(tracking.currentBoundaryId, styles = new Set());
742
760
  styles.add(entry);
743
761
  }
744
- const markup = `<link${attrHtml}>`;
762
+ const markup = `<link${attrHtml}${nonceAttr(nonce, "style")}>`;
745
763
  if (emitResource) emitResource(markup, entry);else {
746
764
  registry.eagerHtml += markup;
747
765
  entry.emitted = true;
@@ -839,7 +857,7 @@ function headGroupSignature(winner) {
839
857
  }
840
858
  return sig;
841
859
  }
842
- function renderShellHead(registry, nonce, isPendingFragment) {
860
+ function renderShellHead(registry, nonce, isPendingFragment, noScripts) {
843
861
  commitHeadBoundary(registry, "", isPendingFragment);
844
862
  registry.shellFlushed = true;
845
863
  const winners = resolveHead(registry.committed);
@@ -849,8 +867,14 @@ function renderShellHead(registry, nonce, isPendingFragment) {
849
867
  let metas = "";
850
868
  let others = "";
851
869
  let scripts = "";
870
+ let title = null;
852
871
  for (const [identity, winner] of winners) {
853
872
  registry.flushed.set(identity, headGroupSignature(winner));
873
+ if (identity === "title") {
874
+ const children = winner.tags[0].props.children;
875
+ title = children == null ? "" : String(children);
876
+ continue;
877
+ }
854
878
  for (let i = 0; i < winner.tags.length; i++) {
855
879
  const t = winner.tags[i];
856
880
  const markup = renderHeadTagMarkup(t.tag, t.props, identity, nonce);
@@ -859,10 +883,12 @@ function renderShellHead(registry, nonce, isPendingFragment) {
859
883
  }
860
884
  return {
861
885
  prelude,
862
- html: registry.eagerHtml + links + metas + others + scripts
886
+ html: registry.eagerHtml + links + metas + others + scripts,
887
+ title,
888
+ noScripts
863
889
  };
864
890
  }
865
- function flushHeadFragment(registry, boundary) {
891
+ function flushHeadFragment(registry, boundary, nonce) {
866
892
  const groups = commitHeadBoundary(registry, boundary);
867
893
  if (!groups.length) return null;
868
894
  const winners = resolveHead(registry.committed);
@@ -893,12 +919,75 @@ function flushHeadFragment(registry, boundary) {
893
919
  if (v == null || v === false) continue;
894
920
  attrs[name] = v === true ? "" : String(v);
895
921
  }
922
+ if (nonce && !hasNonceProp(t.props)) {
923
+ const destination = nonceDestination(t.tag, t.props);
924
+ const value = destination && nonce[destination];
925
+ if (value) attrs.nonce = String(value);
926
+ }
896
927
  const children = t.props.children;
897
928
  ops.push(["a", identity, t.tag, attrs, children == null ? null : String(children)]);
898
929
  }
899
930
  }
900
931
  return ops.length ? ops : null;
901
932
  }
933
+ function normalizeNonce(nonce) {
934
+ if (nonce == null) return undefined;
935
+ if (typeof nonce === "string") {
936
+ const attr = nonce ? ` nonce="${escape(nonce, true)}"` : "";
937
+ return {
938
+ script: nonce,
939
+ style: nonce,
940
+ scriptAttr: attr,
941
+ styleAttr: attr
942
+ };
943
+ }
944
+ const script = nonce.script;
945
+ const style = nonce.style;
946
+ if (!script && !style) return undefined;
947
+ return {
948
+ script,
949
+ style,
950
+ scriptAttr: typeof script === "string" && script ? ` nonce="${escape(script, true)}"` : "",
951
+ styleAttr: typeof style === "string" && style ? ` nonce="${escape(style, true)}"` : ""
952
+ };
953
+ }
954
+ function asciiLowerCase(value) {
955
+ return value.replace(/[A-Z]/g, c => String.fromCharCode(c.charCodeAt(0) + 32));
956
+ }
957
+ function hasNonceProp(props) {
958
+ for (const name in props) if (name.length === 5 && asciiLowerCase(name) === "nonce" && props[name] != null) return true;
959
+ return false;
960
+ }
961
+ function nonceDestination(tag, props) {
962
+ if (tag === "script") return "script";
963
+ if (tag === "style") return "style";
964
+ if (tag !== "link") return null;
965
+ const rel = props.rel;
966
+ if (typeof rel !== "string") return null;
967
+ const rels = asciiLowerCase(rel).split(/[\t\n\f\r ]+/);
968
+ if (rels.includes("stylesheet")) return "style";
969
+ const isModulePreload = rels.includes("modulepreload");
970
+ if (!isModulePreload && !rels.includes("preload")) return null;
971
+ const as = typeof props.as === "string" ? asciiLowerCase(props.as) : "";
972
+ if (as === "style") return "style";
973
+ if (as === "script") return "script";
974
+ if (!isModulePreload) return null;
975
+ switch (as) {
976
+ case "fetch":
977
+ case "font":
978
+ case "image":
979
+ case "json":
980
+ case "text":
981
+ case "track":
982
+ return null;
983
+ default:
984
+ return "script";
985
+ }
986
+ }
987
+ function nonceAttr(nonce, destination) {
988
+ if (!nonce || !destination) return "";
989
+ return destination === "script" ? nonce.scriptAttr : nonce.styleAttr;
990
+ }
902
991
  function renderHeadAttrHtml(props) {
903
992
  let attrs = "";
904
993
  for (const name in props) {
@@ -927,17 +1016,16 @@ function headAttrRecord(props, skipRelHref) {
927
1016
  function renderHeadTagMarkup(tag, props, identity, nonce) {
928
1017
  let attrs = renderHeadAttrHtml(props);
929
1018
  if (identity != null) attrs += ` data-dh="${escape(identity, true)}"`;
930
- if (nonce && (tag === "script" || tag === "style")) attrs += ` nonce="${nonce}"`;
1019
+ if (nonce && !hasNonceProp(props)) attrs += nonceAttr(nonce, nonceDestination(tag, props));
931
1020
  if (tag === "meta" || tag === "link" || tag === "base") return `<${tag}${attrs}>`;
932
1021
  let body = props.children == null ? "" : String(props.children);
933
1022
  if (tag === "script") body = body.replace(/<\/(script)/gi, "<\\/$1");else if (tag === "style") body = escapeStyleContent(body);else body = escape(body);
934
1023
  return `<${tag}${attrs}>${body}</${tag}>`;
935
1024
  }
936
1025
  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])}`;
937
- 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)}`;
1026
+ 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)}`;
938
1027
  function renderToStream(code, options = {}) {
939
1028
  let {
940
- nonce,
941
1029
  onCompleteShell,
942
1030
  onCompleteAll,
943
1031
  renderId = "",
@@ -945,6 +1033,7 @@ function renderToStream(code, options = {}) {
945
1033
  manifest,
946
1034
  onHead
947
1035
  } = options;
1036
+ const nonce = normalizeNonce(options.nonce);
948
1037
  let dispose;
949
1038
  let dead = false;
950
1039
  const abandon = () => {
@@ -969,6 +1058,32 @@ function renderToStream(code, options = {}) {
969
1058
  } catch (_) {}
970
1059
  abandon();
971
1060
  };
1061
+ const coalesceWrites = (writeRaw, endRaw) => {
1062
+ let buf = "";
1063
+ let scheduled = false;
1064
+ const flush = () => {
1065
+ scheduled = false;
1066
+ if (!buf) return;
1067
+ const out = buf;
1068
+ buf = "";
1069
+ writeRaw(out);
1070
+ };
1071
+ return {
1072
+ write(payload) {
1073
+ buf += payload;
1074
+ if (buf.length >= 16384) return flush();
1075
+ if (!scheduled) {
1076
+ scheduled = true;
1077
+ deferFlush(flush);
1078
+ }
1079
+ },
1080
+ flush,
1081
+ end() {
1082
+ flush();
1083
+ endRaw();
1084
+ }
1085
+ };
1086
+ };
972
1087
  const guardSink = w => ({
973
1088
  write(payload) {
974
1089
  if (dead) return;
@@ -988,6 +1103,23 @@ function renderToStream(code, options = {}) {
988
1103
  }
989
1104
  });
990
1105
  const blockingPromises = new Set();
1106
+ const canBatchStubs = !options.serializer && !options.sink;
1107
+ let stubBatch = null;
1108
+ const STUB_BATCH_KEY = "$B";
1109
+ const flushStubBatch = () => {
1110
+ if (!stubBatch) return;
1111
+ const batch = stubBatch;
1112
+ stubBatch = null;
1113
+ if (batch.size === 1) {
1114
+ const [id, p] = batch.entries().next().value;
1115
+ serializer.write(id, p);
1116
+ return;
1117
+ }
1118
+ const obj = {};
1119
+ for (const [id, p] of batch) obj[id] = p;
1120
+ serializer.write(STUB_BATCH_KEY, obj);
1121
+ pushTask(`(b=>{for(var k in b)_$HY.r[k]=b[k];delete _$HY.r["${STUB_BATCH_KEY}"]})(_$HY.r["${STUB_BATCH_KEY}"])`);
1122
+ };
991
1123
  let headerEmitted = false;
992
1124
  const pushTask = task => {
993
1125
  if (noScripts) return;
@@ -1024,10 +1156,11 @@ function renderToStream(code, options = {}) {
1024
1156
  buffer.write(renderInlineStyle(styles.inline[i], nonce));
1025
1157
  }
1026
1158
  if (styles.links.length) {
1159
+ const styleAttr = nonceAttr(nonce, "style");
1027
1160
  emitTask(`$dfs("${key}",${styles.links.length},${deferActivation ? 1 : 0})`);
1028
1161
  writeTasks();
1029
1162
  for (const entry of styles.links) {
1030
- 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}')">`);
1163
+ 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}')">`);
1031
1164
  }
1032
1165
  buffer.write(`<template id="${key}">${value}</template>`);
1033
1166
  } else {
@@ -1042,7 +1175,7 @@ function renderToStream(code, options = {}) {
1042
1175
  },
1043
1176
  asset(type, value) {
1044
1177
  if (type === "module") {
1045
- buffer.write(`<link rel="modulepreload" href="${value}">`);
1178
+ buffer.write(`<link rel="modulepreload" href="${value}"${nonceAttr(nonce, "script")}>`);
1046
1179
  } else if (type === "inline-style") {
1047
1180
  buffer.write(renderInlineStyle(value, nonce));
1048
1181
  } else if (type === "head-tag") {
@@ -1077,6 +1210,7 @@ function renderToStream(code, options = {}) {
1077
1210
  context.live.end = null;
1078
1211
  end();
1079
1212
  }
1213
+ flushStubBatch();
1080
1214
  serializer.flush();
1081
1215
  }));
1082
1216
  }
@@ -1084,7 +1218,7 @@ function renderToStream(code, options = {}) {
1084
1218
  const registry = new Map();
1085
1219
  const writeTasks = () => {
1086
1220
  if (tasks.length && !completed && firstFlushed) {
1087
- buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks}</script>`);
1221
+ buffer.write(`<script${nonceAttr(nonce, "script")}>${tasks}</script>`);
1088
1222
  tasks = "";
1089
1223
  }
1090
1224
  timer = null;
@@ -1138,7 +1272,7 @@ function renderToStream(code, options = {}) {
1138
1272
  };
1139
1273
  solidJs.sharedConfig.context = context = {
1140
1274
  async: true,
1141
- nonce,
1275
+ nonce: options.nonce,
1142
1276
  live: {},
1143
1277
  registerHeadTags(tags) {
1144
1278
  registerHeadTags(headRegistry, context, tracking,
@@ -1196,10 +1330,18 @@ function renderToStream(code, options = {}) {
1196
1330
  },
1197
1331
  serialize(id, p, deferStream) {
1198
1332
  if (solidJs.sharedConfig.context.noHydrate) return;
1199
- if (!firstFlushed && deferStream && typeof p === "object" && "then" in p) {
1200
- blockingPromises.add(p);
1201
- p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
1202
- } else serializer.write(id, p);
1333
+ if (!firstFlushed && p && typeof p === "object" && "then" in p) {
1334
+ if (deferStream) {
1335
+ blockingPromises.add(p);
1336
+ p.then(d => serializer.write(id, d)).catch(e => serializer.write(id, e));
1337
+ return;
1338
+ }
1339
+ if (canBatchStubs && !shellCompleted) {
1340
+ (stubBatch ||= new Map()).set(id, p);
1341
+ return;
1342
+ }
1343
+ }
1344
+ serializer.write(id, p);
1203
1345
  },
1204
1346
  escape: escape,
1205
1347
  resolve: resolveSSRNode,
@@ -1233,7 +1375,7 @@ function renderToStream(code, options = {}) {
1233
1375
  queue(flushEnd);
1234
1376
  }))
1235
1377
  });
1236
- serializer.write(key + "_fr", p);
1378
+ if (canBatchStubs && !shellCompleted) (stubBatch ||= new Map()).set(key + "_fr", p);else serializer.write(key + "_fr", p);
1237
1379
  }
1238
1380
  return (value, error) => {
1239
1381
  if (registry.has(key)) {
@@ -1264,9 +1406,9 @@ function renderToStream(code, options = {}) {
1264
1406
  } else {
1265
1407
  serializeFragmentAssets(key, tracking.boundaryModules, context);
1266
1408
  const styles = collectStreamStyles(key, tracking, headStyles);
1267
- const headOps = error ? null : flushHeadFragment(headRegistry, key);
1409
+ const headOps = error ? null : flushHeadFragment(headRegistry, key, nonce);
1268
1410
  if (headOps) emitHeadOps(key, headOps);
1269
- sink.fragment(key, value !== undefined ? value : " ", {
1411
+ sink.fragment(key, resolveSSRSelectValues(value !== undefined ? value : " "), {
1270
1412
  styles,
1271
1413
  revealGroup,
1272
1414
  error
@@ -1350,15 +1492,18 @@ function renderToStream(code, options = {}) {
1350
1492
  function doShell() {
1351
1493
  if (shellCompleted) return;
1352
1494
  solidJs.sharedConfig.context = context;
1495
+ const blockersBefore = blockingPromises.size;
1353
1496
  if (!resolveRootHoles()) return;
1497
+ if (blockingPromises.size !== blockersBefore) return;
1354
1498
  if (!headShellReady(headRegistry, p => blockingPromises.add(p))) return;
1355
1499
  headStyles = new Set();
1356
1500
  for (const url of tracking.emittedAssets) {
1357
1501
  if (isCssUrl(url)) headStyles.add(url);
1358
1502
  }
1359
1503
  serializeRootAssets();
1360
- const head = renderShellHead(headRegistry, nonce, k => registry.has(k));
1361
- sink.shell(html, {
1504
+ flushStubBatch();
1505
+ const head = renderShellHead(headRegistry, nonce, k => registry.has(k), noScripts);
1506
+ sink.shell(resolveSSRSelectValues(html), {
1362
1507
  preloads: tracking.emittedAssets,
1363
1508
  inlineStyles: tracking.inlineStyles,
1364
1509
  tasks,
@@ -1378,6 +1523,7 @@ function renderToStream(code, options = {}) {
1378
1523
  let drainTurn = 0;
1379
1524
  const scheduleFlush = fn => {
1380
1525
  const attempt = () => {
1526
+ flushStubBatch();
1381
1527
  if (registry.size !== lastRegistrySize || drainTurn++ < MIN_DRAIN_TURNS) {
1382
1528
  if (registry.size !== lastRegistrySize) drainTurn = 0;
1383
1529
  lastRegistrySize = registry.size;
@@ -1425,22 +1571,18 @@ function renderToStream(code, options = {}) {
1425
1571
  }
1426
1572
  };
1427
1573
  writer.closed && writer.closed.catch(failed);
1428
- writable = {
1429
- end() {
1430
- pendingWrites.then(() => {
1431
- ended = true;
1432
- writer.releaseLock();
1433
- w.close().catch(() => {});
1434
- resolve();
1435
- });
1436
- }
1437
- };
1438
- buffer = {
1439
- write(payload) {
1440
- pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(failed);
1441
- }
1442
- };
1574
+ buffer = writable = coalesceWrites(payload => {
1575
+ pendingWrites = pendingWrites.then(() => writer.write(encoder.encode(payload))).catch(failed);
1576
+ }, () => {
1577
+ pendingWrites.then(() => {
1578
+ ended = true;
1579
+ writer.releaseLock();
1580
+ w.close().catch(() => {});
1581
+ resolve();
1582
+ });
1583
+ });
1443
1584
  buffer.write(tmp);
1585
+ buffer.flush();
1444
1586
  firstFlushed = true;
1445
1587
  if (completed) {
1446
1588
  dispose();
@@ -1470,7 +1612,8 @@ function renderToStream(code, options = {}) {
1470
1612
  allSettled(blockingPromises).then(() => {
1471
1613
  scheduleFlush(() => {
1472
1614
  try {
1473
- if (!resolveRootHoles() || !headShellReady(headRegistry, p => blockingPromises.add(p))) return flush();
1615
+ const blockersBefore = blockingPromises.size;
1616
+ if (!resolveRootHoles() || blockingPromises.size !== blockersBefore || !headShellReady(headRegistry, p => blockingPromises.add(p))) return flush();
1474
1617
  } catch (err) {
1475
1618
  failRender(err);
1476
1619
  return resolve(tmp);
@@ -1499,8 +1642,10 @@ function renderToStream(code, options = {}) {
1499
1642
  return;
1500
1643
  }
1501
1644
  if (!shellCompleted) return flush();
1502
- buffer = writable = guardSink(w);
1645
+ const sink = guardSink(w);
1646
+ buffer = writable = coalesceWrites(sink.write, sink.end);
1503
1647
  buffer.write(tmp);
1648
+ buffer.flush();
1504
1649
  firstFlushed = true;
1505
1650
  if (completed) {
1506
1651
  dispose();
@@ -2154,6 +2299,12 @@ function ssr(t) {
2154
2299
  };
2155
2300
  return result;
2156
2301
  }
2302
+ const CLAIM_PROP = /*#__PURE__*/Symbol.for("dom-expressions.claim-prop");
2303
+ const CLAIMS_STREAM = 1;
2304
+ const CLAIMS_DOCUMENT = 2;
2305
+ function resolveSSRSelectValues(html) {
2306
+ return html;
2307
+ }
2157
2308
  function escape(s, attr) {
2158
2309
  const t = typeof s;
2159
2310
  if (t !== "string") {
@@ -2175,22 +2326,20 @@ function escape(s, attr) {
2175
2326
  return escapeSlow(s, attr, i);
2176
2327
  }
2177
2328
  const ESCAPE_CONTENT = /[&<]/;
2178
- const ESCAPE_ATTR = /[&"]/;
2329
+ const ESCAPE_ATTR = /[&"<]/;
2179
2330
  function escapeSlow(s, attr, start) {
2180
- const delim = attr ? '"' : "<";
2181
- const delimCode = attr ? 34 : 60;
2182
- const escDelim = attr ? "&quot;" : "&lt;";
2331
+ if (attr) return escapeAttrSlow(s, start);
2183
2332
  const c0 = s.charCodeAt(start);
2184
- let iDelim = c0 === delimCode ? start : s.indexOf(delim, start);
2333
+ let iDelim = c0 === 60 ? start : s.indexOf("<", start);
2185
2334
  let iAmp = c0 === 38 ? start : s.indexOf("&", start);
2186
2335
  let left = 0,
2187
2336
  out = "";
2188
2337
  while (iDelim >= 0 && iAmp >= 0) {
2189
2338
  if (iDelim < iAmp) {
2190
2339
  if (left < iDelim) out += s.substring(left, iDelim);
2191
- out += escDelim;
2340
+ out += "&lt;";
2192
2341
  left = iDelim + 1;
2193
- iDelim = s.indexOf(delim, left);
2342
+ iDelim = s.indexOf("<", left);
2194
2343
  } else {
2195
2344
  if (left < iAmp) out += s.substring(left, iAmp);
2196
2345
  out += "&amp;";
@@ -2201,9 +2350,9 @@ function escapeSlow(s, attr, start) {
2201
2350
  if (iDelim >= 0) {
2202
2351
  do {
2203
2352
  if (left < iDelim) out += s.substring(left, iDelim);
2204
- out += escDelim;
2353
+ out += "&lt;";
2205
2354
  left = iDelim + 1;
2206
- iDelim = s.indexOf(delim, left);
2355
+ iDelim = s.indexOf("<", left);
2207
2356
  } while (iDelim >= 0);
2208
2357
  } else while (iAmp >= 0) {
2209
2358
  if (left < iAmp) out += s.substring(left, iAmp);
@@ -2213,6 +2362,36 @@ function escapeSlow(s, attr, start) {
2213
2362
  }
2214
2363
  return left < s.length ? out + s.substring(left) : out;
2215
2364
  }
2365
+ function escapeAttrSlow(s, start) {
2366
+ const c0 = s.charCodeAt(start);
2367
+ let iQuot = c0 === 34 ? start : s.indexOf('"', start);
2368
+ let iAmp = c0 === 38 ? start : s.indexOf("&", start);
2369
+ let iLt = c0 === 60 ? start : s.indexOf("<", start);
2370
+ let left = 0,
2371
+ out = "";
2372
+ for (;;) {
2373
+ let i = -1,
2374
+ ent;
2375
+ if (iQuot >= 0) {
2376
+ i = iQuot;
2377
+ ent = "&quot;";
2378
+ }
2379
+ if (iAmp >= 0 && (i < 0 || iAmp < i)) {
2380
+ i = iAmp;
2381
+ ent = "&amp;";
2382
+ }
2383
+ if (iLt >= 0 && (i < 0 || iLt < i)) {
2384
+ i = iLt;
2385
+ ent = "&lt;";
2386
+ }
2387
+ if (i < 0) break;
2388
+ if (left < i) out += s.substring(left, i);
2389
+ out += ent;
2390
+ left = i + 1;
2391
+ if (i === iQuot) iQuot = s.indexOf('"', left);else if (i === iAmp) iAmp = s.indexOf("&", left);else iLt = s.indexOf("<", left);
2392
+ }
2393
+ return left < s.length ? out + s.substring(left) : out;
2394
+ }
2216
2395
  function tryJoinPlainSSRArray(nodes) {
2217
2396
  if (nodes.length === 0) return undefined;
2218
2397
  let out = "";
@@ -2228,6 +2407,7 @@ function tryJoinPlainSSRArray(nodes) {
2228
2407
  function queue(fn) {
2229
2408
  return Promise.resolve().then(fn);
2230
2409
  }
2410
+ const deferFlush = typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
2231
2411
  function allSettled(promises) {
2232
2412
  let size = promises.size;
2233
2413
  return Promise.allSettled(promises).then(() => {
@@ -2236,10 +2416,11 @@ function allSettled(promises) {
2236
2416
  });
2237
2417
  }
2238
2418
  function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
2239
- const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
2240
- const headTagsHtml = headTags ? headTags.html : "";
2419
+ const scriptTag = scripts ? `<script${nonceAttr(nonce, "script")}>${scripts}</script>` : "";
2420
+ const title = headTags ? headTags.title : null;
2421
+ let headTagsHtml = headTags ? headTags.html : "";
2241
2422
  const headPrelude = headTags ? headTags.prelude : "";
2242
- if (!onHead && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2423
+ if (!onHead && title == null && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2243
2424
  if (!scriptTag) return html;
2244
2425
  const xs = html.indexOf("<!--xs-->");
2245
2426
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
@@ -2251,15 +2432,33 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2251
2432
  html = html.slice(0, at) + headPrelude + html.slice(at);
2252
2433
  }
2253
2434
  }
2254
- const headIdx = html.indexOf("</head>");
2435
+ let headIdx = html.indexOf("</head>");
2255
2436
  if (headIdx === -1) {
2256
2437
  if (onHead) {
2257
- onHead(headPrelude + headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2438
+ let titleHtml = "";
2439
+ if (title != null) {
2440
+ 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>`;
2441
+ }
2442
+ onHead(headPrelude + headTagsHtml + titleHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce));
2258
2443
  }
2259
2444
  if (!scriptTag) return html;
2260
2445
  const xs = html.indexOf("<!--xs-->");
2261
2446
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
2262
2447
  }
2448
+ if (title != null) {
2449
+ const winner = escape(title);
2450
+ const open = html.match(/<head(?:\s[^>]*)?>/);
2451
+ const from = open ? open.index + open[0].length : 0;
2452
+ const m = /<title(\s[^>]*)?>([\s\S]*?)<\/title>/.exec(html.slice(from, headIdx));
2453
+ if (m) {
2454
+ const at = from + m.index;
2455
+ const stash = m[2].replace(/"/g, "&quot;");
2456
+ html = html.slice(0, at) + `<title${m[1] || ""} data-dh="title" data-dhf="${stash}">${winner}</title>` + html.slice(at + m[0].length);
2457
+ headIdx = html.indexOf("</head>");
2458
+ } else {
2459
+ headTagsHtml = `<title data-dh="title">${winner}</title>` + headTagsHtml;
2460
+ }
2461
+ }
2263
2462
  const head = headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce);
2264
2463
  if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
2265
2464
  const xsIdx = html.indexOf("<!--xs-->");
@@ -2268,9 +2467,11 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2268
2467
  }
2269
2468
  function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2270
2469
  let head = "";
2470
+ const styleAttr = nonceAttr(nonce, "style");
2471
+ const scriptAttr = nonceAttr(nonce, "script");
2271
2472
  if (emittedAssets && emittedAssets.size) {
2272
2473
  for (const url of emittedAssets) {
2273
- head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}">` : `<link rel="modulepreload" href="${url}">`;
2474
+ head += isCssUrl(url) ? `<link rel="stylesheet" href="${url}"${styleAttr}>` : `<link rel="modulepreload" href="${url}"${scriptAttr}>`;
2274
2475
  }
2275
2476
  }
2276
2477
  if (inlineStyles && inlineStyles.size) {
@@ -2285,7 +2486,9 @@ function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2285
2486
  function serializeFragmentAssets(key, boundaryModules, context, name = key) {
2286
2487
  const map = boundaryModules.get(key);
2287
2488
  if (!map || !Object.keys(map).length) return;
2288
- context.serialize(name + "_assets", map);
2489
+ context.serialize(name + "_assets", {
2490
+ ...map
2491
+ });
2289
2492
  }
2290
2493
  function propagateBoundaryStyles(childKey, parentKey, tracking) {
2291
2494
  const childStyles = tracking.getBoundaryStyles(childKey);
@@ -2330,12 +2533,15 @@ function escapeStyleContent(content) {
2330
2533
  }
2331
2534
  function renderInlineStyle(entry, nonce) {
2332
2535
  let attrs = "";
2536
+ let hasNonce = false;
2333
2537
  if (entry.attrs) {
2334
2538
  for (const name in entry.attrs) {
2539
+ if (name.length === 5 && asciiLowerCase(name) === "nonce") hasNonce = true;
2335
2540
  attrs += ` ${name}="${escape(String(entry.attrs[name]), true)}"`;
2336
2541
  }
2337
2542
  }
2338
- return `<style${nonce ? ` nonce="${nonce}"` : ""} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
2543
+ const nonceHtml = hasNonce ? "" : nonceAttr(nonce, "style");
2544
+ return `<style${nonceHtml} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
2339
2545
  }
2340
2546
  function waitForFragments(registry, key) {
2341
2547
  for (const k of [...registry.keys()].reverse()) {
@@ -2788,6 +2994,7 @@ function renderServerComponent(component, options = {}) {
2788
2994
  ctx.commit = sink.commit;
2789
2995
  ctx.commitEpoch = () => sink.epoch;
2790
2996
  ctx.liveHoles = createLiveHoles(sink);
2997
+ ctx.claims = CLAIMS_STREAM;
2791
2998
  }
2792
2999
  return serverComponentScope(() => component(props));
2793
3000
  };
@@ -3081,6 +3288,7 @@ function createDocumentSlotProps(clientProps, frameId) {
3081
3288
  return out;
3082
3289
  };
3083
3290
  fn.$lhSkip = true;
3291
+ fn[CLAIM_PROP] = prop;
3084
3292
  getters.set(prop, fn);
3085
3293
  }
3086
3294
  return fn;
@@ -3212,6 +3420,7 @@ function frameTransformDirectResult(value, {
3212
3420
  },
3213
3421
  serverOwned(() => {
3214
3422
  armDocumentLiveHoles(solidJs.sharedConfig.context);
3423
+ solidJs.sharedConfig.context.claims = CLAIMS_DOCUMENT;
3215
3424
  const slotProps = createDocumentSlotProps(props, id);
3216
3425
  return serverComponentScope(() => component(slotProps));
3217
3426
  }), {
@@ -3455,6 +3664,7 @@ function createSlotProps(sink, frame) {
3455
3664
  return slotRange(occurrence);
3456
3665
  };
3457
3666
  fn.$lhSkip = true;
3667
+ fn[CLAIM_PROP] = prop;
3458
3668
  getters.set(prop, fn);
3459
3669
  }
3460
3670
  return fn;