@solidjs/web 2.0.0-beta.32 → 2.0.0-beta.33

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 (52) hide show
  1. package/dist/dev.cjs +51 -15
  2. package/dist/dev.js +47 -16
  3. package/dist/server.cjs +710 -71
  4. package/dist/server.js +707 -74
  5. package/dist/web.cjs +51 -15
  6. package/dist/web.js +47 -16
  7. package/frames/dist/client.cjs +181 -64
  8. package/frames/dist/client.dev.cjs +185 -64
  9. package/frames/dist/client.dev.js +183 -62
  10. package/frames/dist/client.js +179 -62
  11. package/frames/dist/server.cjs +969 -133
  12. package/frames/dist/server.js +971 -135
  13. package/package.json +17 -6
  14. package/serialization/decode/package.json +20 -0
  15. package/serialization/dist/decode.cjs +110 -0
  16. package/serialization/dist/decode.js +104 -0
  17. package/serialization/dist/serialization.cjs +98 -43
  18. package/serialization/dist/serialization.js +99 -44
  19. package/serialization/types/index.d.ts +18 -160
  20. package/serialization/types/serializer-decode.d.ts +182 -0
  21. package/serialization/types-cjs/index.d.cts +18 -160
  22. package/serialization/types-cjs/serializer-decode.d.cts +182 -0
  23. package/server-functions/dist/client.cjs +101 -105
  24. package/server-functions/dist/client.js +101 -105
  25. package/server-functions/dist/server.cjs +131 -107
  26. package/server-functions/dist/server.dev.cjs +131 -107
  27. package/server-functions/dist/server.dev.js +132 -108
  28. package/server-functions/dist/server.js +132 -108
  29. package/types/client.d.ts +23 -1
  30. package/types/cookies.d.ts +93 -0
  31. package/types/core.d.ts +1 -1
  32. package/types/frames/frame-client.d.ts +26 -0
  33. package/types/frames/frame-transport.d.ts +1 -1
  34. package/types/frames/serializer.d.ts +18 -160
  35. package/types/serializer-decode.d.ts +182 -0
  36. package/types/serializer.d.ts +18 -160
  37. package/types/server-functions/client.d.ts +1 -1
  38. package/types/server-functions/server.d.ts +1 -1
  39. package/types/server-functions/shared.d.ts +57 -1
  40. package/types/server.d.ts +21 -1
  41. package/types-cjs/client.d.cts +23 -1
  42. package/types-cjs/cookies.d.cts +93 -0
  43. package/types-cjs/core.d.cts +1 -1
  44. package/types-cjs/frames/frame-client.d.cts +26 -0
  45. package/types-cjs/frames/frame-transport.d.cts +1 -1
  46. package/types-cjs/frames/serializer.d.cts +18 -160
  47. package/types-cjs/serializer-decode.d.cts +182 -0
  48. package/types-cjs/serializer.d.cts +18 -160
  49. package/types-cjs/server-functions/client.d.cts +1 -1
  50. package/types-cjs/server-functions/server.d.cts +1 -1
  51. package/types-cjs/server-functions/shared.d.cts +57 -1
  52. package/types-cjs/server.d.cts +21 -1
@@ -1,10 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var solidJs = require('solid-js');
4
- var web$1 = require('@solidjs/web');
5
- var seroval = require('seroval');
4
+ var web = require('@solidjs/web');
6
5
  var client = require('@solidjs/web/server-functions/client');
7
- var web = require('seroval-plugins/web');
8
6
 
9
7
  const ELEMENT_NODE = 1;
10
8
  const TEXT_NODE = 3;
@@ -67,16 +65,32 @@ function chunkToRecords(chunk) {
67
65
  args: chunk.args
68
66
  }
69
67
  };
68
+ case "hole":
69
+ return {
70
+ [`hole:${chunk.key}`]: {
71
+ kind: "html",
72
+ value: chunk.html
73
+ }
74
+ };
75
+ case "attr":
76
+ return {
77
+ [`attr:${chunk.key}`]: {
78
+ kind: "attrs",
79
+ value: chunk.attrs,
80
+ removed: chunk.removed
81
+ }
82
+ };
70
83
  case "complete":
71
84
  return {
72
85
  ":complete": true
73
86
  };
74
87
  case "error":
75
- return chunk.key ? {
76
- [`seg:${chunk.key}:error`]: chunk.error
77
- } : {
88
+ if (!chunk.key) return {
78
89
  ":error": chunk.error
79
90
  };
91
+ return {
92
+ [`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
93
+ };
80
94
  default:
81
95
  return {};
82
96
  }
@@ -161,7 +175,10 @@ function createFrameHost(options = {}) {
161
175
  },
162
176
  resolve(ref, frameId) {
163
177
  return options.resolve ? options.resolve(ref, frameId) : undefined;
164
- }
178
+ },
179
+ revive: options.revive,
180
+ isContainer: options.isContainer,
181
+ prepareData: options.prepareData
165
182
  };
166
183
  }
167
184
  const FRAME_APPLIED_EVENT = "frame:applied";
@@ -177,6 +194,7 @@ class FrameImpl {
177
194
  #errorNotified = false;
178
195
  #revealed = new Set();
179
196
  #fallbackShown = new Set();
197
+ #appliedHoles = new Map();
180
198
  #slots;
181
199
  #mountedSlots = new Set();
182
200
  #slotCleanups = new Map();
@@ -277,6 +295,7 @@ class FrameImpl {
277
295
  #resetStreamState(root) {
278
296
  this.#revealed.clear();
279
297
  this.#fallbackShown.clear();
298
+ this.#appliedHoles.clear();
280
299
  this.#errorNotified = false;
281
300
  clearStreamRecords(this.#store, root);
282
301
  }
@@ -318,6 +337,24 @@ class FrameImpl {
318
337
  }
319
338
  }
320
339
  }
340
+ for (const key in this.#store) {
341
+ const record = this.#store[key];
342
+ if (!record || this.#appliedHoles.get(key) === record) continue;
343
+ if (key.startsWith("hole:")) {
344
+ if (key.endsWith(":error")) {
345
+ this.#appliedHoles.set(key, record);
346
+ console.error(`Live hole ${key.slice(5, -6)} failed on the server; latched:`, record);
347
+ } else if (this.#applyHole(key.slice(5), record.value)) {
348
+ this.#appliedHoles.set(key, record);
349
+ this.#applied(version, "morph");
350
+ }
351
+ } else if (key.startsWith("attr:")) {
352
+ if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
353
+ this.#appliedHoles.set(key, record);
354
+ this.#applied(version, "morph");
355
+ }
356
+ }
357
+ }
321
358
  for (const key in this.#store) {
322
359
  if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;
323
360
  this.#processedAssets.add(key);
@@ -494,7 +531,7 @@ class FrameImpl {
494
531
  }
495
532
  props[key] = entry.element;
496
533
  } else {
497
- props[key] = value;
534
+ props[key] = host && host.revive ? host.revive(value) : value;
498
535
  }
499
536
  }
500
537
  return props;
@@ -522,6 +559,10 @@ class FrameImpl {
522
559
  if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
523
560
  const host = this.#options.host;
524
561
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
562
+ if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
563
+ if (next === cache[key]) continue;
564
+ return false;
565
+ }
525
566
  if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
526
567
  try {
527
568
  if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;
@@ -630,6 +671,38 @@ class FrameImpl {
630
671
  if (ranges.size) for (const root of grafts) flushGrafts(root, ranges);
631
672
  }
632
673
  }
674
+ #applyHole(marker, html) {
675
+ if (!this.#hasContent) return false;
676
+ const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
677
+ if (!open) return false;
678
+ const close = rangeClose(open, "lh:/" + marker.slice(3));
679
+ if (!close) {
680
+ {
681
+ console.error(`Live hole "${marker}" is missing its closing comment; update dropped. ` + `Likely an HTML-rewriting layer stripped it.`, open);
682
+ }
683
+ return false;
684
+ }
685
+ const claim = claimHandlers() ? this.#claimTree : null;
686
+ reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
687
+ return true;
688
+ }
689
+ #applyAttrs(addr, text, removed) {
690
+ if (!this.#hasContent) return false;
691
+ const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
692
+ if (!el) return false;
693
+ const parsed = parseFragment(`<i${text}></i>`).firstChild;
694
+ if (parsed) {
695
+ for (let i = 0; i < parsed.attributes.length; i++) {
696
+ const {
697
+ name,
698
+ value
699
+ } = parsed.attributes[i];
700
+ if (el.getAttribute(name) !== value) el.setAttribute(name, value);
701
+ }
702
+ }
703
+ if (removed) for (const name of removed) el.removeAttribute(name);
704
+ return true;
705
+ }
633
706
  #clearContent() {
634
707
  removeUntil(this.#parent(), this.#firstContent(), this.#end);
635
708
  }
@@ -825,6 +898,20 @@ function rangeClose(start, id) {
825
898
  }
826
899
  return null;
827
900
  }
901
+ function findLiveTarget(n, end, test) {
902
+ while (n && n !== end) {
903
+ if (n.nodeType === ELEMENT_NODE) {
904
+ const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
905
+ if (fid === null || fid.includes(".")) {
906
+ if (test(n)) return n;
907
+ const found = findLiveTarget(n.firstChild, null, test);
908
+ if (found) return found;
909
+ }
910
+ } else if (test(n)) return n;
911
+ n = n.nextSibling;
912
+ }
913
+ return null;
914
+ }
828
915
  function findPlaceholder(n, end, id) {
829
916
  while (n && n !== end) {
830
917
  if (isPlaceholderStart(n, id)) return n;
@@ -884,7 +971,9 @@ function eachInRange(start, key, cb) {
884
971
  }
885
972
  function clearStreamRecords(records, root) {
886
973
  for (const key in records) {
887
- if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
974
+ if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
975
+ delete records[key];
976
+ }
888
977
  }
889
978
  }
890
979
  function argsEquivalent(a, b) {
@@ -1149,6 +1238,7 @@ async function applyFrameResponse(response, host, options = {}) {
1149
1238
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
1150
1239
  chunk.version = v;
1151
1240
  } else if (version !== undefined) chunk.version = version;
1241
+ if (chunk.type === "data" && host.prepareData) await host.prepareData();
1152
1242
  host.apply(chunk);
1153
1243
  }
1154
1244
  result = await reader.next();
@@ -1165,7 +1255,7 @@ function parseServerComponent(value, ctx) {
1165
1255
  address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1166
1256
  };
1167
1257
  }
1168
- const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
1258
+ const ServerComponentPlugin = {
1169
1259
  tag: "dom-expressions/server-component",
1170
1260
  test(value) {
1171
1261
  return typeof value === "function" && SERVER_COMPONENT in value;
@@ -1192,12 +1282,10 @@ const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
1192
1282
  }
1193
1283
  return globalThis._$SC.r(id);
1194
1284
  }
1195
- });
1285
+ };
1196
1286
  function flightCodec(codec) {
1197
1287
  const plugins = codec && codec.plugins || [];
1198
- for (const plugin of plugins) {
1199
- if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1200
- }
1288
+ if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
1201
1289
  return {
1202
1290
  ...codec,
1203
1291
  plugins: [...plugins, ServerComponentPlugin]
@@ -1314,72 +1402,74 @@ function createServerComponentHandler({
1314
1402
  }
1315
1403
  }
1316
1404
 
1317
- seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
1318
- const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
1319
- web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
1320
- web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
1321
- function resolveSerializerPlugins(customPlugins) {
1322
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1323
- }
1324
- const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
1325
- const JSON_CODEC_DEPTH_LIMIT = 64;
1326
- function resolveCodecOptions({
1327
- plugins,
1328
- disabledFeatures,
1329
- depthLimit
1330
- } = {}) {
1331
- return {
1332
- plugins: resolveSerializerPlugins(plugins),
1333
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1334
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1335
- };
1405
+ const STATE = Symbol.for("dom-expressions.container-trace-state");
1406
+ const state = globalThis[STATE] || (globalThis[STATE] = {
1407
+ materialized: new WeakMap(),
1408
+ materializedValues: new WeakSet()
1409
+ });
1410
+ function setContainerTraceMaterializer(fn) {
1411
+ state.materializeTrace = fn;
1336
1412
  }
1337
- function createJSONDeserializer(options) {
1338
- const refs = new Map();
1339
- const resolved = resolveCodecOptions(options);
1340
- return function deserializeJSONChunk(node) {
1341
- return seroval.fromCrossJSON(node, {
1342
- refs,
1343
- ...resolved
1344
- });
1345
- };
1413
+ function materialize(marker) {
1414
+ let value = state.materialized.get(marker.$tr);
1415
+ if (value === undefined) {
1416
+ value = state.materializeTrace(marker);
1417
+ state.materialized.set(marker.$tr, value);
1418
+ if (value !== null && typeof value === "object") state.materializedValues.add(value);
1419
+ }
1420
+ return value;
1346
1421
  }
1347
- function createJSONDataTable(options) {
1348
- const deserialize = createJSONDeserializer(options);
1349
- const table = new Map();
1350
- return {
1351
- apply(record) {
1352
- const value = deserialize(record.node);
1353
- if (record.initial) table.set(record.key, value);
1354
- },
1355
- get(key) {
1356
- return table.get(key);
1357
- },
1358
- resolve(ref) {
1359
- return table.get(ref.$ref);
1360
- }
1361
- };
1422
+ function isMaterializedContainer(value) {
1423
+ return value !== null && typeof value === "object" && state.materializedValues.has(value);
1424
+ }
1425
+ function isContainerTraceMarker(value) {
1426
+ return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
1427
+ }
1428
+ function reviveContainerTraces(value) {
1429
+ if (!state.materializeTrace || value == null || typeof value !== "object") return value;
1430
+ if (isContainerTraceMarker(value)) return materialize(value);
1431
+ if (Array.isArray(value)) {
1432
+ for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
1433
+ } else if (Object.getPrototypeOf(value) === Object.prototype) {
1434
+ for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
1435
+ }
1436
+ return value;
1362
1437
  }
1363
1438
 
1439
+ setContainerTraceMaterializer(solidJs.materializeContainerTrace);
1364
1440
  function asyncArg(value) {
1365
1441
  return value;
1366
1442
  }
1367
1443
  let sharedHost;
1444
+ let codec;
1445
+ let codecLoading;
1446
+ function loadCodec() {
1447
+ return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
1448
+ codec = m;
1449
+ });
1450
+ }
1368
1451
  const tables = new Map();
1452
+ function ensureTable(root) {
1453
+ let table = tables.get(root);
1454
+ if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
1455
+ return table;
1456
+ }
1369
1457
  function tableFor(id) {
1370
- const table = tables.get(id);
1371
- if (table) return table;
1372
- for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
1458
+ if (tables.has(id)) return ensureTable(id);
1459
+ for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
1373
1460
  return undefined;
1374
1461
  }
1375
1462
  function beginStream(frameId) {
1376
- tables.set(frameId, createJSONDataTable());
1463
+ tables.set(frameId, undefined);
1377
1464
  }
1378
1465
  function getFrameHost() {
1379
1466
  if (!sharedHost) {
1380
1467
  sharedHost = createFrameHost({
1468
+ prepareData: loadCodec,
1381
1469
  applyData: c => tableFor(c.id)?.apply(c),
1382
- resolve: (ref, id) => tableFor(id)?.resolve(ref)
1470
+ resolve: (ref, id) => tableFor(id)?.resolve(ref),
1471
+ revive: reviveContainerTraces,
1472
+ isContainer: isMaterializedContainer
1383
1473
  });
1384
1474
  }
1385
1475
  return sharedHost;
@@ -1451,6 +1541,7 @@ function slotArgsProxy(args) {
1451
1541
  return new Proxy({}, {
1452
1542
  get: (_, key) => {
1453
1543
  const v = args()[key];
1544
+ if (isMaterializedContainer(v)) return v;
1454
1545
  if (!isAsyncValue(v)) return v;
1455
1546
  let read = asyncReads.get(key);
1456
1547
  if (!read) {
@@ -1532,7 +1623,7 @@ function slotsFor(props) {
1532
1623
  owner.dispose();
1533
1624
  });
1534
1625
  const end = ctx.range.end;
1535
- const bind = () => web$1.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
1626
+ const bind = () => web.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
1536
1627
  solidJs.runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());
1537
1628
  return undefined;
1538
1629
  }
@@ -1542,7 +1633,7 @@ function slotsFor(props) {
1542
1633
  });
1543
1634
  }
1544
1635
  function revealSeam(owner) {
1545
- return seam => solidJs.runWithOwner(owner, () => web$1.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1636
+ return seam => solidJs.runWithOwner(owner, () => web.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1546
1637
  }
1547
1638
  let streamInvoke = false;
1548
1639
  function boundaryScope(owner) {
@@ -1602,6 +1693,24 @@ function boundaryComponent(host, fnId) {
1602
1693
  };
1603
1694
  }
1604
1695
  const claimedBoundaries = new Set();
1696
+ const liveOps = new Map();
1697
+ const liveAppliers = new Set();
1698
+ let livePumped = null;
1699
+ function pumpLiveChannel() {
1700
+ const stream = globalThis._$HY?.r?.["sc:live"];
1701
+ if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
1702
+ livePumped = stream;
1703
+ const reader = stream.getReader();
1704
+ const pump = () => reader.read().then(r => {
1705
+ if (r.done) return;
1706
+ const op = r.value;
1707
+ liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
1708
+ for (const apply of liveAppliers) apply(op);
1709
+ return pump();
1710
+ });
1711
+ pump().catch(() => {
1712
+ });
1713
+ }
1605
1714
  let boundaryIndex = null;
1606
1715
  const isBoundaryId = id => !id.includes(".");
1607
1716
  function indexBoundaries(root) {
@@ -1680,6 +1789,7 @@ function adoptBoundary(host, id, el, props, binding) {
1680
1789
  const drainRecords = () => {
1681
1790
  const hy = globalThis._$HY;
1682
1791
  if (!hy || !hy.r) return;
1792
+ pumpLiveChannel();
1683
1793
  const slotPrefix = `sc:slot:${id}:`;
1684
1794
  for (const key of Object.keys(hy.r)) {
1685
1795
  if (appliedRecords.has(key)) continue;
@@ -1714,11 +1824,22 @@ function adoptBoundary(host, id, el, props, binding) {
1714
1824
  if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
1715
1825
  drainRecords();
1716
1826
  }) : undefined;
1827
+ const applyLiveOp = op => {
1828
+ if (op.type === "slot" && op.fid !== id) return;
1829
+ host.apply({
1830
+ ...op,
1831
+ id: address,
1832
+ version: 0
1833
+ });
1834
+ };
1835
+ liveAppliers.add(applyLiveOp);
1717
1836
  solidJs.onCleanup(() => {
1837
+ liveAppliers.delete(applyLiveOp);
1718
1838
  unsubscribe && unsubscribe();
1719
1839
  if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
1720
1840
  });
1721
1841
  drainRecords();
1842
+ for (const op of liveOps.values()) applyLiveOp(op);
1722
1843
  const owner = solidJs.getOwner();
1723
1844
  let release;
1724
1845
  let setGate;