@solidjs/web 2.0.0-beta.25 → 2.0.0-beta.27

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 (43) hide show
  1. package/dist/dev.cjs +14 -9
  2. package/dist/dev.js +14 -10
  3. package/dist/server.cjs +41 -78
  4. package/dist/server.js +42 -80
  5. package/dist/web.cjs +14 -9
  6. package/dist/web.js +14 -10
  7. package/frames/dist/client.cjs +180 -224
  8. package/frames/dist/client.dev.cjs +1456 -0
  9. package/frames/dist/client.dev.js +1444 -0
  10. package/frames/dist/client.js +181 -225
  11. package/frames/dist/server.cjs +139 -123
  12. package/frames/dist/server.js +139 -123
  13. package/package.json +24 -4
  14. package/serialization/dist/serialization.cjs +6 -3
  15. package/serialization/dist/serialization.js +6 -3
  16. package/serialization/types/index.d.ts +7 -1
  17. package/serialization/types-cjs/index.d.cts +7 -1
  18. package/server-functions/dist/client.cjs +18 -5
  19. package/server-functions/dist/client.js +16 -6
  20. package/server-functions/dist/server.cjs +164 -8
  21. package/server-functions/dist/server.js +158 -9
  22. package/types/client.d.ts +3 -3
  23. package/types/frames/client.d.ts +1 -1
  24. package/types/frames/frame-client.d.ts +34 -28
  25. package/types/frames/serializer.d.ts +7 -1
  26. package/types/jsx.d.ts +1 -1
  27. package/types/response.d.ts +10 -0
  28. package/types/serializer.d.ts +7 -1
  29. package/types/server-functions/client.d.ts +3 -0
  30. package/types/server-functions/flash.d.ts +38 -0
  31. package/types/server-functions/server.d.ts +79 -4
  32. package/types/server-functions/shared.d.ts +35 -0
  33. package/types-cjs/client.d.cts +3 -3
  34. package/types-cjs/frames/client.d.cts +1 -1
  35. package/types-cjs/frames/frame-client.d.cts +34 -28
  36. package/types-cjs/frames/serializer.d.cts +7 -1
  37. package/types-cjs/jsx.d.cts +1 -1
  38. package/types-cjs/response.d.cts +10 -0
  39. package/types-cjs/serializer.d.cts +7 -1
  40. package/types-cjs/server-functions/client.d.cts +3 -0
  41. package/types-cjs/server-functions/flash.d.cts +38 -0
  42. package/types-cjs/server-functions/server.d.cts +79 -4
  43. package/types-cjs/server-functions/shared.d.cts +35 -0
@@ -7,6 +7,7 @@ const runWithHydrationScope = (id, fn) => runWithOwner(createOwner({
7
7
  }), fn);
8
8
 
9
9
  const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
10
+ const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
10
11
  const HYDRATION_GLOBAL = "_$HY.r";
11
12
  const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
12
13
  CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
@@ -18,7 +19,7 @@ function createSerializer(options) {
18
19
  return new Serializer({
19
20
  ...options,
20
21
  plugins: resolveSerializerPlugins(options.plugins),
21
- disabledFeatures: options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures
22
+ disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
22
23
  });
23
24
  }
24
25
  function createHydrationSerializer({
@@ -86,7 +87,7 @@ function createJSONSerializer({
86
87
  const stream = toCrossJSONStream(value, {
87
88
  refs,
88
89
  plugins: resolved.plugins,
89
- disabledFeatures: resolved.disabledFeatures,
90
+ disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(),
90
91
  onParse(node, initial) {
91
92
  onData({
92
93
  key,
@@ -243,7 +244,7 @@ function applyAssetTracking(context, tracking, manifest) {
243
244
  context.resolveAssetsSync = resolve;
244
245
  }
245
246
  }
246
- const REPLACE_SCRIPT = `function $df(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;_$HY.done?o.remove():o.replaceWith(n.content),n.remove(),_$HY.fe(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])}`;
247
+ const REPLACE_SCRIPT = `function $df(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,_$HY.done?o.remove():o.replaceWith(n.content),n.remove(),_$HY.fe(e,t),$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])}`;
247
248
  function renderToStream(code, options = {}) {
248
249
  let {
249
250
  nonce,
@@ -315,11 +316,7 @@ function renderToStream(code, options = {}) {
315
316
  }
316
317
  },
317
318
  shell(shellHtml, meta) {
318
- shellHtml = injectBeforeHead(shellHtml, meta.assets);
319
- shellHtml = injectPreloadLinks(meta.preloads, shellHtml);
320
- shellHtml = injectInlineStyles(meta.inlineStyles, shellHtml, nonce);
321
- if (meta.tasks.length) shellHtml = injectScripts(shellHtml, meta.tasks, nonce);
322
- buffer.write(shellHtml);
319
+ buffer.write(assembleDocument(shellHtml, meta.assets, meta.preloads, meta.inlineStyles, meta.tasks.length ? meta.tasks : "", nonce));
323
320
  },
324
321
  ...options.sink
325
322
  };
@@ -950,25 +947,36 @@ function resolveAssetsHtml(assets) {
950
947
  for (let i = 0, len = assets.length; i < len; i++) out += assets[i]();
951
948
  return out;
952
949
  }
953
- function injectBeforeHead(html, content) {
954
- if (!content) return html;
955
- const index = html.indexOf("</head>");
956
- if (index === -1) return html;
957
- return html.slice(0, index) + content + html.slice(index);
958
- }
959
- function injectPreloadLinks(emittedAssets, html, nonce) {
960
- if (!emittedAssets.size) return html;
961
- let links = "";
962
- for (const url of emittedAssets) {
963
- if (url.endsWith(".css")) {
964
- links += `<link rel="stylesheet" href="${url}">`;
965
- } else {
966
- links += `<link rel="modulepreload" href="${url}">`;
950
+ function assembleDocument(html, assetsHtml, emittedAssets, inlineStyles, scripts, nonce) {
951
+ const scriptTag = scripts ? `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>` : "";
952
+ if (!assetsHtml && !(emittedAssets && emittedAssets.size) && !(inlineStyles && inlineStyles.size)) {
953
+ if (!scriptTag) return html;
954
+ const xs = html.indexOf("<!--xs-->");
955
+ return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
956
+ }
957
+ const headIdx = html.indexOf("</head>");
958
+ if (headIdx === -1) {
959
+ if (!scriptTag) return html;
960
+ const xs = html.indexOf("<!--xs-->");
961
+ return xs === -1 ? html + scriptTag : html.slice(0, xs) + scriptTag + html.slice(xs);
962
+ }
963
+ let head = assetsHtml || "";
964
+ if (emittedAssets && emittedAssets.size) {
965
+ for (const url of emittedAssets) {
966
+ head += url.endsWith(".css") ? `<link rel="stylesheet" href="${url}">` : `<link rel="modulepreload" href="${url}">`;
967
+ }
968
+ }
969
+ if (inlineStyles && inlineStyles.size) {
970
+ for (const entry of inlineStyles.values()) {
971
+ if (entry.emitted) continue;
972
+ entry.emitted = true;
973
+ head += renderInlineStyle(entry, nonce);
967
974
  }
968
975
  }
969
- const index = html.indexOf("</head>");
970
- if (index === -1) return html;
971
- return html.slice(0, index) + links + html.slice(index);
976
+ if (!scriptTag) return html.slice(0, headIdx) + head + html.slice(headIdx);
977
+ const xsIdx = html.indexOf("<!--xs-->");
978
+ if (xsIdx === -1) return html.slice(0, headIdx) + head + html.slice(headIdx) + scriptTag;
979
+ return xsIdx < headIdx ? html.slice(0, xsIdx) + scriptTag + html.slice(xsIdx, headIdx) + head + html.slice(headIdx) : html.slice(0, headIdx) + head + html.slice(headIdx, xsIdx) + scriptTag + html.slice(xsIdx);
972
980
  }
973
981
  function serializeFragmentAssets(key, boundaryModules, context) {
974
982
  const map = boundaryModules.get(key);
@@ -1020,26 +1028,6 @@ function renderInlineStyle(entry, nonce) {
1020
1028
  }
1021
1029
  return `<style${nonce ? ` nonce="${nonce}"` : ""} data-asset="${escape(entry.id, true)}"${attrs}>${escapeStyleContent(entry.content)}</style>`;
1022
1030
  }
1023
- function injectInlineStyles(inlineStyles, html, nonce) {
1024
- if (!inlineStyles.size) return html;
1025
- const index = html.indexOf("</head>");
1026
- if (index === -1) return html;
1027
- let out = "";
1028
- for (const entry of inlineStyles.values()) {
1029
- if (entry.emitted) continue;
1030
- entry.emitted = true;
1031
- out += renderInlineStyle(entry, nonce);
1032
- }
1033
- return out ? html.slice(0, index) + out + html.slice(index) : html;
1034
- }
1035
- function injectScripts(html, scripts, nonce) {
1036
- const tag = `<script${nonce ? ` nonce="${nonce}"` : ""}>${scripts}</script>`;
1037
- const index = html.indexOf("<!--xs-->");
1038
- if (index > -1) {
1039
- return html.slice(0, index) + tag + html.slice(index);
1040
- }
1041
- return html + tag;
1042
- }
1043
1031
  function waitForFragments(registry, key) {
1044
1032
  for (const k of [...registry.keys()].reverse()) {
1045
1033
  if (key.startsWith(k)) return k;
@@ -1181,7 +1169,12 @@ function createFrameSink(emit, frame) {
1181
1169
  version
1182
1170
  } = frame;
1183
1171
  const styledKeys = new Set();
1172
+ const regionKeys = new Map();
1173
+ const frameOf = key => regionKeys.get(key) || id;
1184
1174
  return {
1175
+ tagRegion(key, childId) {
1176
+ if (!regionKeys.has(key)) regionKeys.set(key, childId);
1177
+ },
1185
1178
  shell(html, meta = {}) {
1186
1179
  if (meta.preloads && meta.preloads.size) {
1187
1180
  const styles = [];
@@ -1229,13 +1222,14 @@ function createFrameSink(emit, frame) {
1229
1222
  }
1230
1223
  },
1231
1224
  fragment(key, value, meta = {}) {
1225
+ const fid = frameOf(key);
1232
1226
  const links = meta.styles && meta.styles.links || [];
1233
1227
  const inline = meta.styles && meta.styles.inline || [];
1234
1228
  if (links.length || inline.length) {
1235
1229
  if (links.length) styledKeys.add(key);
1236
1230
  const chunk = {
1237
1231
  type: "assets",
1238
- id,
1232
+ id: fid,
1239
1233
  version,
1240
1234
  key
1241
1235
  };
@@ -1251,7 +1245,7 @@ function createFrameSink(emit, frame) {
1251
1245
  }
1252
1246
  emit({
1253
1247
  type: "fragment",
1254
- id,
1248
+ id: fid,
1255
1249
  version,
1256
1250
  key,
1257
1251
  html: value
@@ -1259,7 +1253,7 @@ function createFrameSink(emit, frame) {
1259
1253
  if (meta.error) {
1260
1254
  emit({
1261
1255
  type: "error",
1262
- id,
1256
+ id: fid,
1263
1257
  version,
1264
1258
  key,
1265
1259
  error: {
@@ -1270,7 +1264,7 @@ function createFrameSink(emit, frame) {
1270
1264
  if (!meta.revealGroup) {
1271
1265
  emit({
1272
1266
  type: "reveal",
1273
- id,
1267
+ id: fid,
1274
1268
  version,
1275
1269
  keys: [key],
1276
1270
  waitForStyles: !!links.length
@@ -1278,17 +1272,26 @@ function createFrameSink(emit, frame) {
1278
1272
  }
1279
1273
  },
1280
1274
  reveal(keys, meta = {}) {
1281
- let waitForStyles = false;
1282
- for (const key of keys) if (styledKeys.has(key)) waitForStyles = true;
1283
- const chunk = {
1284
- type: "reveal",
1285
- id,
1286
- version,
1287
- keys,
1288
- waitForStyles
1289
- };
1290
- if (meta.fallback) chunk.fallback = true;
1291
- emit(chunk);
1275
+ const byFrame = new Map();
1276
+ for (const key of keys) {
1277
+ const fid = frameOf(key);
1278
+ let group = byFrame.get(fid);
1279
+ if (!group) byFrame.set(fid, group = []);
1280
+ group.push(key);
1281
+ }
1282
+ for (const [fid, groupKeys] of byFrame) {
1283
+ let waitForStyles = false;
1284
+ for (const key of groupKeys) if (styledKeys.has(key)) waitForStyles = true;
1285
+ const chunk = {
1286
+ type: "reveal",
1287
+ id: fid,
1288
+ version,
1289
+ keys: groupKeys,
1290
+ waitForStyles
1291
+ };
1292
+ if (meta.fallback) chunk.fallback = true;
1293
+ emit(chunk);
1294
+ }
1292
1295
  },
1293
1296
  asset(type, url) {
1294
1297
  if (type !== "module") return;
@@ -1402,6 +1405,22 @@ function slotRange(occurrence) {
1402
1405
  t: `<!--slot:${occurrence}:start--><!--slot:${occurrence}:end-->`
1403
1406
  };
1404
1407
  }
1408
+ const OCCURRENCE_UNSAFE = /[^A-Za-z0-9_.-]/g;
1409
+ function encodeOccurrenceKey(key) {
1410
+ return String(key).replace(OCCURRENCE_UNSAFE, c => {
1411
+ const code = c.codePointAt(0);
1412
+ return "%" + (code < 16 ? "0" : "") + code.toString(16);
1413
+ });
1414
+ }
1415
+ function occurrenceId(prop, raw, counts) {
1416
+ const k = raw.$key;
1417
+ if (typeof k === "string" || typeof k === "number") {
1418
+ return `${prop}#${encodeOccurrenceKey(k)}`;
1419
+ }
1420
+ const n = counts[prop] || 0;
1421
+ counts[prop] = n + 1;
1422
+ return `${prop}#${n}`;
1423
+ }
1405
1424
  function createDocumentSlotProps(clientProps, frameId) {
1406
1425
  const counts = Object.create(null);
1407
1426
  const getters = new Map();
@@ -1438,36 +1457,37 @@ function createDocumentSlotProps(clientProps, frameId) {
1438
1457
  });
1439
1458
  }
1440
1459
  const raw = callArgs[0];
1441
- let occurrence;
1442
- const k = raw.$key;
1443
- if (typeof k === "string" || typeof k === "number") {
1444
- occurrence = `${prop}#${k}`;
1445
- } else {
1446
- const n = counts[prop] || 0;
1447
- counts[prop] = n + 1;
1448
- occurrence = `${prop}#${n}`;
1449
- }
1460
+ const occurrence = occurrenceId(prop, raw, counts);
1450
1461
  const slot = clientProps[prop];
1451
1462
  if (typeof slot !== "function") return range(occurrence, undefined);
1452
1463
  const resolved = {};
1453
- const regions = [];
1464
+ const vals = {};
1454
1465
  for (const key of Object.keys(raw)) {
1455
- const value = raw[key];
1456
- if (key !== "$key" && isServerContent(value)) {
1466
+ if (key === "$key") continue;
1467
+ let value = raw[key];
1468
+ for (let d = 0; typeof value === "function" && d < 16; d++) value = value();
1469
+ vals[key] = value;
1470
+ }
1471
+ const regions = [];
1472
+ for (const key of Object.keys(vals)) {
1473
+ const value = vals[key];
1474
+ if (isServerContent(value)) {
1457
1475
  const childId = `${frameId}.${occurrence}.${key}`;
1458
1476
  const region = {
1459
1477
  key,
1460
1478
  childId,
1461
1479
  value,
1462
- used: false
1480
+ used: false,
1481
+ locked: false
1463
1482
  };
1464
1483
  regions.push(region);
1465
1484
  resolved[key] = () => {
1485
+ if (region.locked) return [];
1466
1486
  region.used = true;
1467
1487
  return [{
1468
- t: `<!--frame:${childId}:start-->`
1488
+ t: frameElementOpen(childId)
1469
1489
  }, value, {
1470
- t: `<!--frame:${childId}:end-->`
1490
+ t: FRAME_ELEMENT_CLOSE
1471
1491
  }];
1472
1492
  };
1473
1493
  } else {
@@ -1477,12 +1497,10 @@ function createDocumentSlotProps(clientProps, frameId) {
1477
1497
  const out = scoped(occurrence, () => range(occurrence, slot(resolved)));
1478
1498
  const unused = regions.filter(r => !r.used);
1479
1499
  if (sharedConfig.context) {
1480
- const rendered = renderedHtmlOf(out);
1481
1500
  const args = {};
1482
1501
  let any = false;
1483
- for (const key of Object.keys(raw)) {
1484
- const value = raw[key];
1485
- if (key === "$key") continue;
1502
+ for (const key of Object.keys(vals)) {
1503
+ const value = vals[key];
1486
1504
  const region = regions.find(r => r.key === key);
1487
1505
  if (region) {
1488
1506
  if (!region.used) {
@@ -1494,16 +1512,12 @@ function createDocumentSlotProps(clientProps, frameId) {
1494
1512
  continue;
1495
1513
  }
1496
1514
  if (isServerContent(value)) continue;
1497
- const t = typeof value;
1498
- if ((t === "string" || t === "number") && rendered !== null) {
1499
- const needle = t === "string" ? sharedConfig.context.escape(String(value)) : String(value);
1500
- if (needle !== "" && rendered.includes(needle)) continue;
1501
- }
1502
1515
  args[key] = value;
1503
1516
  any = true;
1504
1517
  }
1505
1518
  if (any) sharedConfig.context.serialize(`sc:slot:${frameId}:${occurrence}`, args);
1506
1519
  for (const region of unused) {
1520
+ region.locked = true;
1507
1521
  sharedConfig.context.serialize(`sc:region:${region.childId}`, resolveRegionHtml(sharedConfig.context, region.value));
1508
1522
  }
1509
1523
  }
@@ -1515,28 +1529,26 @@ function createDocumentSlotProps(clientProps, frameId) {
1515
1529
  }
1516
1530
  });
1517
1531
  }
1532
+ const FRAME_TAG = "dx-frame";
1533
+ const FRAME_ID_ATTR = "data-fid";
1534
+ function frameElementOpen(id) {
1535
+ const escaped = sharedConfig.context ? sharedConfig.context.escape(String(id), true) : String(id);
1536
+ return `<${FRAME_TAG} ${FRAME_ID_ATTR}="${escaped}" style="display:contents">`;
1537
+ }
1538
+ const FRAME_ELEMENT_CLOSE = `</${FRAME_TAG}>`;
1518
1539
  function frameTransformDirectResult(value, {
1519
1540
  id
1520
1541
  }) {
1521
1542
  if (typeof value !== "function") return value;
1522
1543
  const component = value;
1523
1544
  const wrapped = props => [{
1524
- t: `<!--frame:${id}:start-->`
1545
+ t: frameElementOpen(id)
1525
1546
  }, serverOwned(() => component(createDocumentSlotProps(props, id))), {
1526
- t: `<!--frame:${id}:end-->`
1547
+ t: FRAME_ELEMENT_CLOSE
1527
1548
  }];
1528
1549
  wrapped[SERVER_COMPONENT] = id;
1529
1550
  return wrapped;
1530
1551
  }
1531
- function renderedHtmlOf(out) {
1532
- try {
1533
- const res = sharedConfig.context.resolve(out);
1534
- if (res && res.t && (!res.h || !res.h.length)) {
1535
- return res.t[0].replace(/<!--[^>]*-->/g, "").replace(/ _hk=("[^"]*"|[^\s>]+)/g, "");
1536
- }
1537
- } catch (e) {}
1538
- return null;
1539
- }
1540
1552
  function resolveRegionHtml(ctx, node) {
1541
1553
  const res = ctx.resolve(node);
1542
1554
  if (!res || !res.t) return String(res ?? "");
@@ -1611,37 +1623,41 @@ function createSlotProps(sink, frame) {
1611
1623
  return slotRange(prop);
1612
1624
  }
1613
1625
  const raw = callArgs[0];
1614
- let occurrence;
1615
- const k = raw.$key;
1616
- if (typeof k === "string" || typeof k === "number") {
1617
- occurrence = `${prop}#${k}`;
1618
- } else {
1619
- const n = counts[prop] || 0;
1620
- counts[prop] = n + 1;
1621
- occurrence = `${prop}#${n}`;
1622
- }
1626
+ const occurrence = occurrenceId(prop, raw, counts);
1623
1627
  const args = {};
1624
1628
  for (const key of Object.keys(raw)) {
1625
- const value = raw[key];
1626
- const t = typeof value;
1627
- if (value == null || t === "string" || t === "number" || t === "boolean") {
1628
- args[key] = value;
1629
- } else if (isServerContent(value)) {
1630
- const resolved = sharedConfig.context.resolve(value);
1631
- if (resolved.h.length) {
1632
- throw new Error("Async server content in slot args is not supported yet (arg '" + key + "' of " + occurrence + "). Move the async read above the slot or into a fragment.");
1629
+ if (key === "$key") continue;
1630
+ const childId = `${frame.id}.${occurrence}.${key}`;
1631
+ const ctx = sharedConfig.context;
1632
+ const origRegister = ctx.registerFragment;
1633
+ ctx.registerFragment = (fragKey, fragOptions) => {
1634
+ sink.tagRegion(fragKey, childId);
1635
+ return origRegister.call(ctx, fragKey, fragOptions);
1636
+ };
1637
+ try {
1638
+ let value = raw[key];
1639
+ for (let d = 0; typeof value === "function" && d < 16; d++) value = value();
1640
+ const t = typeof value;
1641
+ if (value == null || t === "string" || t === "number" || t === "boolean") {
1642
+ args[key] = value;
1643
+ } else if (isServerContent(value)) {
1644
+ const resolved = ctx.resolve(value);
1645
+ if (resolved.h.length) {
1646
+ throw new Error("Async server content in a slot arg needs a boundary (arg '" + key + "' of " + occurrence + "). Wrap the async read in a <Suspense>, or move it above the slot.");
1647
+ }
1648
+ sink.region(childId, resolved.t[0]);
1649
+ args[key] = {
1650
+ $frame: childId
1651
+ };
1652
+ } else {
1653
+ const ref = `arg:${occurrence}:${key}`;
1654
+ ctx.serialize(ref, value);
1655
+ args[key] = {
1656
+ $ref: ref
1657
+ };
1633
1658
  }
1634
- const childId = `${frame.id}.${occurrence}.${key}`;
1635
- sink.region(childId, resolved.t[0]);
1636
- args[key] = {
1637
- $frame: childId
1638
- };
1639
- } else {
1640
- const ref = `arg:${occurrence}:${key}`;
1641
- sharedConfig.context.serialize(ref, value);
1642
- args[key] = {
1643
- $ref: ref
1644
- };
1659
+ } finally {
1660
+ ctx.registerFragment = origRegister;
1645
1661
  }
1646
1662
  }
1647
1663
  sink.slot(occurrence, args);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solidjs/web",
3
3
  "description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
4
- "version": "2.0.0-beta.25",
4
+ "version": "2.0.0-beta.27",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "homepage": "https://solidjs.com",
@@ -231,6 +231,16 @@
231
231
  }
232
232
  },
233
233
  "browser": {
234
+ "development": {
235
+ "import": {
236
+ "types": "./types/frames/client.d.ts",
237
+ "default": "./frames/dist/client.dev.js"
238
+ },
239
+ "require": {
240
+ "types": "./types-cjs/frames/client.d.cts",
241
+ "default": "./frames/dist/client.dev.cjs"
242
+ }
243
+ },
234
244
  "import": {
235
245
  "types": "./types/frames/client.d.ts",
236
246
  "default": "./frames/dist/client.js"
@@ -260,6 +270,16 @@
260
270
  "default": "./frames/dist/server.cjs"
261
271
  }
262
272
  },
273
+ "development": {
274
+ "import": {
275
+ "types": "./types/frames/client.d.ts",
276
+ "default": "./frames/dist/client.dev.js"
277
+ },
278
+ "require": {
279
+ "types": "./types-cjs/frames/client.d.cts",
280
+ "default": "./frames/dist/client.dev.cjs"
281
+ }
282
+ },
263
283
  "import": {
264
284
  "types": "./types/frames/client.d.ts",
265
285
  "default": "./frames/dist/client.js"
@@ -296,10 +316,10 @@
296
316
  "seroval-plugins": "~1.5.4"
297
317
  },
298
318
  "peerDependencies": {
299
- "solid-js": "^2.0.0-beta.25"
319
+ "solid-js": "^2.0.0-beta.27"
300
320
  },
301
321
  "devDependencies": {
302
- "solid-js": "2.0.0-beta.25"
322
+ "solid-js": "2.0.0-beta.27"
303
323
  },
304
324
  "scripts": {
305
325
  "build": "npm-run-all -nl build:clean types:copy-jsx build:js",
@@ -314,7 +334,7 @@
314
334
  "types:web-storage": "tsc --project ./storage/tsconfig.build.json",
315
335
  "types:web-frames": "tsc --project ./frames/tsconfig.build.json",
316
336
  "types:copy-serialization": "node -e \"fs.mkdirSync('./serialization/types', { recursive: true }); fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/serializer.d.ts', './serialization/types/index.d.ts');\"",
317
- "types:copy-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'client', 'server']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.d.ts');\"",
337
+ "types:copy-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'flash', 'client', 'server']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.d.ts');\"",
318
338
  "types:copy-frames": "node -e \"fs.mkdirSync('./types/frames', { recursive: true }); for (const f of ['frame-client', 'frame-transport', 'frame-sink', 'serializer']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/' + f + '.d.ts', './types/frames/' + f + '.d.ts'); for (const f of ['client', 'server']) fs.writeFileSync('./types/frames/' + f + '.d.ts', fs.readFileSync('./frames/types/' + f + '.d.ts', 'utf8').replaceAll('@dom-expressions/runtime/src/', './'));\"",
319
339
  "types:cjs": "node ../../scripts/sync-dual-types.mjs ./types ./types-cjs ./storage/types ./storage/types-cjs ./serialization/types ./serialization/types-cjs",
320
340
  "test": "vitest run && vitest run --config vite.config.server.mjs && vitest run --config vite.config.hydrate.mjs",
@@ -4,6 +4,7 @@ var seroval = require('seroval');
4
4
  var web = require('seroval-plugins/web');
5
5
 
6
6
  const DEFAULT_DISABLED_FEATURES = seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
7
+ const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
7
8
  const HYDRATION_GLOBAL = "_$HY.r";
8
9
  const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
9
10
  web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
@@ -15,7 +16,7 @@ function createSerializer(options) {
15
16
  return new seroval.Serializer({
16
17
  ...options,
17
18
  plugins: resolveSerializerPlugins(options.plugins),
18
- disabledFeatures: options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures
19
+ disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
19
20
  });
20
21
  }
21
22
  function createHydrationSerializer({
@@ -56,11 +57,13 @@ function serializeJSON(value, {
56
57
  onError,
57
58
  ...codecOptions
58
59
  }) {
60
+ const resolved = resolveCodecOptions(codecOptions);
59
61
  return seroval.toCrossJSONStream(value, {
60
62
  onParse,
61
63
  onDone,
62
64
  onError,
63
- ...resolveCodecOptions(codecOptions)
65
+ ...resolved,
66
+ disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
64
67
  });
65
68
  }
66
69
  function createJSONDeserializer(options) {
@@ -106,7 +109,7 @@ function createJSONSerializer({
106
109
  const stream = seroval.toCrossJSONStream(value, {
107
110
  refs,
108
111
  plugins: resolved.plugins,
109
- disabledFeatures: resolved.disabledFeatures,
112
+ disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(),
110
113
  onParse(node, initial) {
111
114
  onData({
112
115
  key,
@@ -2,6 +2,7 @@ import { Feature, Serializer, getCrossReferenceHeader, toCrossJSONStream, fromCr
2
2
  import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
3
3
 
4
4
  const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
5
+ const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
5
6
  const HYDRATION_GLOBAL = "_$HY.r";
6
7
  const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
7
8
  CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
@@ -13,7 +14,7 @@ function createSerializer(options) {
13
14
  return new Serializer({
14
15
  ...options,
15
16
  plugins: resolveSerializerPlugins(options.plugins),
16
- disabledFeatures: options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures
17
+ disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
17
18
  });
18
19
  }
19
20
  function createHydrationSerializer({
@@ -54,11 +55,13 @@ function serializeJSON(value, {
54
55
  onError,
55
56
  ...codecOptions
56
57
  }) {
58
+ const resolved = resolveCodecOptions(codecOptions);
57
59
  return toCrossJSONStream(value, {
58
60
  onParse,
59
61
  onDone,
60
62
  onError,
61
- ...resolveCodecOptions(codecOptions)
63
+ ...resolved,
64
+ disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
62
65
  });
63
66
  }
64
67
  function createJSONDeserializer(options) {
@@ -104,7 +107,7 @@ function createJSONSerializer({
104
107
  const stream = toCrossJSONStream(value, {
105
108
  refs,
106
109
  plugins: resolved.plugins,
107
- disabledFeatures: resolved.disabledFeatures,
110
+ disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(),
108
111
  onParse(node, initial) {
109
112
  onData({
110
113
  key,
@@ -37,7 +37,9 @@ export interface WebSerializerOptions {
37
37
  scopeId?: string;
38
38
  /**
39
39
  * Seroval feature bitflags to exclude from output. Defaults to disabling
40
- * post-ES2017 features (AggregateError, BigInt typed arrays).
40
+ * post-ES2017 features (AggregateError, BigInt typed arrays). Outside
41
+ * development, `Error.prototype.stack` is additionally stripped on top of
42
+ * any override — serialized stacks leak server paths to the client.
41
43
  */
42
44
  disabledFeatures?: number;
43
45
  /** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
@@ -101,6 +103,10 @@ export interface JSONCodecOptions {
101
103
  /**
102
104
  * Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
103
105
  * (payloads may come from an untrusted peer). Must match on both peers.
106
+ * Outside development, the encoding side additionally strips
107
+ * `Error.prototype.stack` on top of any override — serialized stacks leak
108
+ * server paths to the client. Decoding stays permissive, so payloads from
109
+ * a development peer still round-trip.
104
110
  */
105
111
  disabledFeatures?: number;
106
112
  /** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
@@ -37,7 +37,9 @@ export interface WebSerializerOptions {
37
37
  scopeId?: string;
38
38
  /**
39
39
  * Seroval feature bitflags to exclude from output. Defaults to disabling
40
- * post-ES2017 features (AggregateError, BigInt typed arrays).
40
+ * post-ES2017 features (AggregateError, BigInt typed arrays). Outside
41
+ * development, `Error.prototype.stack` is additionally stripped on top of
42
+ * any override — serialized stacks leak server paths to the client.
41
43
  */
42
44
  disabledFeatures?: number;
43
45
  /** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
@@ -101,6 +103,10 @@ export interface JSONCodecOptions {
101
103
  /**
102
104
  * Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
103
105
  * (payloads may come from an untrusted peer). Must match on both peers.
106
+ * Outside development, the encoding side additionally strips
107
+ * `Error.prototype.stack` on top of any override — serialized stacks leak
108
+ * server paths to the client. Decoding stays permissive, so payloads from
109
+ * a development peer still round-trip.
104
110
  */
105
111
  disabledFeatures?: number;
106
112
  /** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */