@solidjs/web 2.0.0-rc.1 → 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.
@@ -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,7 +1404,7 @@ 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
1409
  sink.fragment(key, resolveSSRSelectValues(value !== undefined ? value : " "), {
1268
1410
  styles,
@@ -1348,14 +1490,17 @@ 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));
1502
+ flushStubBatch();
1503
+ const head = renderShellHead(headRegistry, nonce, k => registry.has(k), noScripts);
1359
1504
  sink.shell(resolveSSRSelectValues(html), {
1360
1505
  preloads: tracking.emittedAssets,
1361
1506
  inlineStyles: tracking.inlineStyles,
@@ -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,92 +2297,11 @@ function ssr(t) {
2152
2297
  };
2153
2298
  return result;
2154
2299
  }
2155
- function decodeSSREntities(s) {
2156
- return s.indexOf("&") < 0 ? s : s.replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&amp;/g, "&");
2157
- }
2158
- const SELECT_VALUE_ATTR = /\svalue="([^"]*)"/;
2159
- function tagEnd(html, i) {
2160
- for (let j = i + 1; j < html.length; j++) {
2161
- const c = html.charCodeAt(j);
2162
- if (c === 34) {
2163
- j = html.indexOf('"', j + 1);
2164
- if (j < 0) return -1;
2165
- } else if (c === 62) return j;
2166
- }
2167
- return -1;
2168
- }
2169
- function optionText(html, from) {
2170
- let t = "";
2171
- let i = from;
2172
- for (;;) {
2173
- const lt = html.indexOf("<", i);
2174
- if (lt < 0) return t + html.slice(i);
2175
- t += html.slice(i, lt);
2176
- if (!html.startsWith("<!--", lt)) return t;
2177
- const ce = html.indexOf("-->", lt);
2178
- if (ce < 0) return t;
2179
- i = ce + 3;
2180
- }
2181
- }
2182
- function tagIs(html, i, name) {
2183
- if (!html.startsWith(name, i + 1)) return false;
2184
- const c = html.charCodeAt(i + 1 + name.length);
2185
- return c === 32 || c === 62 || c === 9 || c === 10 || c === 13;
2186
- }
2300
+ const CLAIM_PROP = /*#__PURE__*/Symbol.for("dom-expressions.claim-prop");
2301
+ const CLAIMS_STREAM = 1;
2302
+ const CLAIMS_DOCUMENT = 2;
2187
2303
  function resolveSSRSelectValues(html) {
2188
- if (html.indexOf("<select") < 0) return html;
2189
- let out = "";
2190
- let idx = 0;
2191
- let sel = null;
2192
- let i = html.indexOf("<");
2193
- while (i >= 0) {
2194
- let e;
2195
- if (html.charCodeAt(i + 1) === 33) {
2196
- e = html.charCodeAt(i + 2) === 45 ? html.indexOf("-->", i) : html.indexOf(">", i);
2197
- if (e < 0) break;
2198
- if (html.charCodeAt(i + 2) === 45) e += 2;
2199
- } else {
2200
- e = tagEnd(html, i);
2201
- if (e < 0) break;
2202
- if (sel) {
2203
- if (html.startsWith("</select>", i)) {
2204
- out += html.slice(idx, sel.strip) + html.slice(sel.stripEnd, sel.body);
2205
- let seg = sel.body;
2206
- if (!sel.defaulted) {
2207
- for (let k = 0; k < sel.marks.length; k++) {
2208
- out += html.slice(seg, sel.marks[k]) + " selected";
2209
- seg = sel.marks[k];
2210
- }
2211
- }
2212
- out += html.slice(seg, i);
2213
- idx = i;
2214
- sel = null;
2215
- } else if (tagIs(html, i, "option")) {
2216
- const attrs = html.slice(i + 7, e);
2217
- if (/\sselected(?=[\s=]|$)/.test(attrs)) sel.defaulted = true;else {
2218
- const vm = SELECT_VALUE_ATTR.exec(attrs);
2219
- const value = vm ? decodeSSREntities(vm[1]) : decodeSSREntities(optionText(html, e + 1)).replace(/\s+/g, " ").trim();
2220
- if (sel.values.includes(value)) sel.marks.push(e);
2221
- }
2222
- }
2223
- } else if (tagIs(html, i, "select")) {
2224
- const m = SELECT_VALUE_ATTR.exec(html.slice(i, e + 1));
2225
- if (m) {
2226
- const bound = decodeSSREntities(m[1]);
2227
- sel = {
2228
- values: /\smultiple(?=[\s>=])/.test(html.slice(i, e + 1)) ? bound.split(",") : [bound],
2229
- strip: i + m.index,
2230
- stripEnd: i + m.index + m[0].length,
2231
- body: e + 1,
2232
- marks: [],
2233
- defaulted: false
2234
- };
2235
- }
2236
- }
2237
- }
2238
- i = html.indexOf("<", e + 1);
2239
- }
2240
- return out + html.slice(idx);
2304
+ return html;
2241
2305
  }
2242
2306
  function escape(s, attr) {
2243
2307
  const t = typeof s;
@@ -2260,22 +2324,20 @@ function escape(s, attr) {
2260
2324
  return escapeSlow(s, attr, i);
2261
2325
  }
2262
2326
  const ESCAPE_CONTENT = /[&<]/;
2263
- const ESCAPE_ATTR = /[&"]/;
2327
+ const ESCAPE_ATTR = /[&"<]/;
2264
2328
  function escapeSlow(s, attr, start) {
2265
- const delim = attr ? '"' : "<";
2266
- const delimCode = attr ? 34 : 60;
2267
- const escDelim = attr ? "&quot;" : "&lt;";
2329
+ if (attr) return escapeAttrSlow(s, start);
2268
2330
  const c0 = s.charCodeAt(start);
2269
- let iDelim = c0 === delimCode ? start : s.indexOf(delim, start);
2331
+ let iDelim = c0 === 60 ? start : s.indexOf("<", start);
2270
2332
  let iAmp = c0 === 38 ? start : s.indexOf("&", start);
2271
2333
  let left = 0,
2272
2334
  out = "";
2273
2335
  while (iDelim >= 0 && iAmp >= 0) {
2274
2336
  if (iDelim < iAmp) {
2275
2337
  if (left < iDelim) out += s.substring(left, iDelim);
2276
- out += escDelim;
2338
+ out += "&lt;";
2277
2339
  left = iDelim + 1;
2278
- iDelim = s.indexOf(delim, left);
2340
+ iDelim = s.indexOf("<", left);
2279
2341
  } else {
2280
2342
  if (left < iAmp) out += s.substring(left, iAmp);
2281
2343
  out += "&amp;";
@@ -2286,9 +2348,9 @@ function escapeSlow(s, attr, start) {
2286
2348
  if (iDelim >= 0) {
2287
2349
  do {
2288
2350
  if (left < iDelim) out += s.substring(left, iDelim);
2289
- out += escDelim;
2351
+ out += "&lt;";
2290
2352
  left = iDelim + 1;
2291
- iDelim = s.indexOf(delim, left);
2353
+ iDelim = s.indexOf("<", left);
2292
2354
  } while (iDelim >= 0);
2293
2355
  } else while (iAmp >= 0) {
2294
2356
  if (left < iAmp) out += s.substring(left, iAmp);
@@ -2298,6 +2360,36 @@ function escapeSlow(s, attr, start) {
2298
2360
  }
2299
2361
  return left < s.length ? out + s.substring(left) : out;
2300
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
+ }
2301
2393
  function tryJoinPlainSSRArray(nodes) {
2302
2394
  if (nodes.length === 0) return undefined;
2303
2395
  let out = "";
@@ -2313,6 +2405,7 @@ function tryJoinPlainSSRArray(nodes) {
2313
2405
  function queue(fn) {
2314
2406
  return Promise.resolve().then(fn);
2315
2407
  }
2408
+ const deferFlush = typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
2316
2409
  function allSettled(promises) {
2317
2410
  let size = promises.size;
2318
2411
  return Promise.allSettled(promises).then(() => {
@@ -2321,10 +2414,11 @@ function allSettled(promises) {
2321
2414
  });
2322
2415
  }
2323
2416
  function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, headTags, onHead) {
2324
- const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
2325
- 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 : "";
2326
2420
  const headPrelude = headTags ? headTags.prelude : "";
2327
- if (!onHead && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2421
+ if (!onHead && title == null && !headTagsHtml && !headPrelude && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
2328
2422
  if (!scriptTag) return html;
2329
2423
  const xs = html.indexOf("<!--xs-->");
2330
2424
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
@@ -2336,15 +2430,33 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2336
2430
  html = html.slice(0, at) + headPrelude + html.slice(at);
2337
2431
  }
2338
2432
  }
2339
- const headIdx = html.indexOf("</head>");
2433
+ let headIdx = html.indexOf("</head>");
2340
2434
  if (headIdx === -1) {
2341
2435
  if (onHead) {
2342
- 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));
2343
2441
  }
2344
2442
  if (!scriptTag) return html;
2345
2443
  const xs = html.indexOf("<!--xs-->");
2346
2444
  return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
2347
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
+ }
2348
2460
  const head = headTagsHtml + renderHeadAssets(emittedAssets, inlineStyles, nonce);
2349
2461
  if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
2350
2462
  const xsIdx = html.indexOf("<!--xs-->");
@@ -2353,9 +2465,11 @@ function assembleDocument(html, emittedAssets, inlineStyles, scripts, nonce, hea
2353
2465
  }
2354
2466
  function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
2355
2467
  let head = "";
2468
+ const styleAttr = nonceAttr(nonce, "style");
2469
+ const scriptAttr = nonceAttr(nonce, "script");
2356
2470
  if (emittedAssets && emittedAssets.size) {
2357
2471
  for (const url of emittedAssets) {
2358
- 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}>`;
2359
2473
  }
2360
2474
  }
2361
2475
  if (inlineStyles && inlineStyles.size) {
@@ -2417,12 +2531,15 @@ function escapeStyleContent(content) {
2417
2531
  }
2418
2532
  function renderInlineStyle(entry, nonce) {
2419
2533
  let attrs = "";
2534
+ let hasNonce = false;
2420
2535
  if (entry.attrs) {
2421
2536
  for (const name in entry.attrs) {
2537
+ if (name.length === 5 && asciiLowerCase(name) === "nonce") hasNonce = true;
2422
2538
  attrs += ` ${name}="${escape(String(entry.attrs[name]), true)}"`;
2423
2539
  }
2424
2540
  }
2425
- 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>`;
2426
2543
  }
2427
2544
  function waitForFragments(registry, key) {
2428
2545
  for (const k of [...registry.keys()].reverse()) {
@@ -2875,6 +2992,7 @@ function renderServerComponent(component, options = {}) {
2875
2992
  ctx.commit = sink.commit;
2876
2993
  ctx.commitEpoch = () => sink.epoch;
2877
2994
  ctx.liveHoles = createLiveHoles(sink);
2995
+ ctx.claims = CLAIMS_STREAM;
2878
2996
  }
2879
2997
  return serverComponentScope(() => component(props));
2880
2998
  };
@@ -3168,6 +3286,7 @@ function createDocumentSlotProps(clientProps, frameId) {
3168
3286
  return out;
3169
3287
  };
3170
3288
  fn.$lhSkip = true;
3289
+ fn[CLAIM_PROP] = prop;
3171
3290
  getters.set(prop, fn);
3172
3291
  }
3173
3292
  return fn;
@@ -3299,6 +3418,7 @@ function frameTransformDirectResult(value, {
3299
3418
  },
3300
3419
  serverOwned(() => {
3301
3420
  armDocumentLiveHoles(sharedConfig.context);
3421
+ sharedConfig.context.claims = CLAIMS_DOCUMENT;
3302
3422
  const slotProps = createDocumentSlotProps(props, id);
3303
3423
  return serverComponentScope(() => component(slotProps));
3304
3424
  }), {
@@ -3542,6 +3662,7 @@ function createSlotProps(sink, frame) {
3542
3662
  return slotRange(occurrence);
3543
3663
  };
3544
3664
  fn.$lhSkip = true;
3665
+ fn[CLAIM_PROP] = prop;
3545
3666
  getters.set(prop, fn);
3546
3667
  }
3547
3668
  return fn;