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

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 +58 -16
  2. package/dist/dev.js +54 -17
  3. package/dist/server.cjs +714 -75
  4. package/dist/server.js +711 -78
  5. package/dist/web.cjs +58 -16
  6. package/dist/web.js +54 -17
  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 +972 -136
  12. package/frames/dist/server.js +974 -138
  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,8 +1,6 @@
1
- import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig } from 'solid-js';
1
+ import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig, materializeContainerTrace } from 'solid-js';
2
2
  import { insert } from '@solidjs/web';
3
- import { createPlugin, fromCrossJSON, Feature } from 'seroval';
4
3
  import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
5
- import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
6
4
 
7
5
  const ELEMENT_NODE = 1;
8
6
  const TEXT_NODE = 3;
@@ -65,16 +63,32 @@ function chunkToRecords(chunk) {
65
63
  args: chunk.args
66
64
  }
67
65
  };
66
+ case "hole":
67
+ return {
68
+ [`hole:${chunk.key}`]: {
69
+ kind: "html",
70
+ value: chunk.html
71
+ }
72
+ };
73
+ case "attr":
74
+ return {
75
+ [`attr:${chunk.key}`]: {
76
+ kind: "attrs",
77
+ value: chunk.attrs,
78
+ removed: chunk.removed
79
+ }
80
+ };
68
81
  case "complete":
69
82
  return {
70
83
  ":complete": true
71
84
  };
72
85
  case "error":
73
- return chunk.key ? {
74
- [`seg:${chunk.key}:error`]: chunk.error
75
- } : {
86
+ if (!chunk.key) return {
76
87
  ":error": chunk.error
77
88
  };
89
+ return {
90
+ [`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
91
+ };
78
92
  default:
79
93
  return {};
80
94
  }
@@ -159,7 +173,10 @@ function createFrameHost(options = {}) {
159
173
  },
160
174
  resolve(ref, frameId) {
161
175
  return options.resolve ? options.resolve(ref, frameId) : undefined;
162
- }
176
+ },
177
+ revive: options.revive,
178
+ isContainer: options.isContainer,
179
+ prepareData: options.prepareData
163
180
  };
164
181
  }
165
182
  const FRAME_APPLIED_EVENT = "frame:applied";
@@ -175,6 +192,7 @@ class FrameImpl {
175
192
  #errorNotified = false;
176
193
  #revealed = new Set();
177
194
  #fallbackShown = new Set();
195
+ #appliedHoles = new Map();
178
196
  #slots;
179
197
  #mountedSlots = new Set();
180
198
  #slotCleanups = new Map();
@@ -275,6 +293,7 @@ class FrameImpl {
275
293
  #resetStreamState(root) {
276
294
  this.#revealed.clear();
277
295
  this.#fallbackShown.clear();
296
+ this.#appliedHoles.clear();
278
297
  this.#errorNotified = false;
279
298
  clearStreamRecords(this.#store, root);
280
299
  }
@@ -316,6 +335,23 @@ class FrameImpl {
316
335
  }
317
336
  }
318
337
  }
338
+ for (const key in this.#store) {
339
+ const record = this.#store[key];
340
+ if (!record || this.#appliedHoles.get(key) === record) continue;
341
+ if (key.startsWith("hole:")) {
342
+ if (key.endsWith(":error")) {
343
+ this.#appliedHoles.set(key, record);
344
+ } else if (this.#applyHole(key.slice(5), record.value)) {
345
+ this.#appliedHoles.set(key, record);
346
+ this.#applied(version, "morph");
347
+ }
348
+ } else if (key.startsWith("attr:")) {
349
+ if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
350
+ this.#appliedHoles.set(key, record);
351
+ this.#applied(version, "morph");
352
+ }
353
+ }
354
+ }
319
355
  for (const key in this.#store) {
320
356
  if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;
321
357
  this.#processedAssets.add(key);
@@ -492,7 +528,7 @@ class FrameImpl {
492
528
  }
493
529
  props[key] = entry.element;
494
530
  } else {
495
- props[key] = value;
531
+ props[key] = host && host.revive ? host.revive(value) : value;
496
532
  }
497
533
  }
498
534
  return props;
@@ -520,6 +556,10 @@ class FrameImpl {
520
556
  if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
521
557
  const host = this.#options.host;
522
558
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
559
+ if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
560
+ if (next === cache[key]) continue;
561
+ return false;
562
+ }
523
563
  if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
524
564
  try {
525
565
  if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;
@@ -628,6 +668,35 @@ class FrameImpl {
628
668
  if (ranges.size) for (const root of grafts) flushGrafts(root, ranges);
629
669
  }
630
670
  }
671
+ #applyHole(marker, html) {
672
+ if (!this.#hasContent) return false;
673
+ const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
674
+ if (!open) return false;
675
+ const close = rangeClose(open, "lh:/" + marker.slice(3));
676
+ if (!close) {
677
+ return false;
678
+ }
679
+ const claim = claimHandlers() ? this.#claimTree : null;
680
+ reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
681
+ return true;
682
+ }
683
+ #applyAttrs(addr, text, removed) {
684
+ if (!this.#hasContent) return false;
685
+ const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
686
+ if (!el) return false;
687
+ const parsed = parseFragment(`<i${text}></i>`).firstChild;
688
+ if (parsed) {
689
+ for (let i = 0; i < parsed.attributes.length; i++) {
690
+ const {
691
+ name,
692
+ value
693
+ } = parsed.attributes[i];
694
+ if (el.getAttribute(name) !== value) el.setAttribute(name, value);
695
+ }
696
+ }
697
+ if (removed) for (const name of removed) el.removeAttribute(name);
698
+ return true;
699
+ }
631
700
  #clearContent() {
632
701
  removeUntil(this.#parent(), this.#firstContent(), this.#end);
633
702
  }
@@ -820,6 +889,20 @@ function rangeClose(start, id) {
820
889
  }
821
890
  return null;
822
891
  }
892
+ function findLiveTarget(n, end, test) {
893
+ while (n && n !== end) {
894
+ if (n.nodeType === ELEMENT_NODE) {
895
+ const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
896
+ if (fid === null || fid.includes(".")) {
897
+ if (test(n)) return n;
898
+ const found = findLiveTarget(n.firstChild, null, test);
899
+ if (found) return found;
900
+ }
901
+ } else if (test(n)) return n;
902
+ n = n.nextSibling;
903
+ }
904
+ return null;
905
+ }
823
906
  function findPlaceholder(n, end, id) {
824
907
  while (n && n !== end) {
825
908
  if (isPlaceholderStart(n, id)) return n;
@@ -878,7 +961,9 @@ function eachInRange(start, key, cb) {
878
961
  }
879
962
  function clearStreamRecords(records, root) {
880
963
  for (const key in records) {
881
- if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
964
+ if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
965
+ delete records[key];
966
+ }
882
967
  }
883
968
  }
884
969
  function argsEquivalent(a, b) {
@@ -1134,6 +1219,7 @@ async function applyFrameResponse(response, host, options = {}) {
1134
1219
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
1135
1220
  chunk.version = v;
1136
1221
  } else if (version !== undefined) chunk.version = version;
1222
+ if (chunk.type === "data" && host.prepareData) await host.prepareData();
1137
1223
  host.apply(chunk);
1138
1224
  }
1139
1225
  result = await reader.next();
@@ -1150,7 +1236,7 @@ function parseServerComponent(value, ctx) {
1150
1236
  address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1151
1237
  };
1152
1238
  }
1153
- const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1239
+ const ServerComponentPlugin = {
1154
1240
  tag: "dom-expressions/server-component",
1155
1241
  test(value) {
1156
1242
  return typeof value === "function" && SERVER_COMPONENT in value;
@@ -1177,12 +1263,10 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1177
1263
  }
1178
1264
  return globalThis._$SC.r(id);
1179
1265
  }
1180
- });
1266
+ };
1181
1267
  function flightCodec(codec) {
1182
1268
  const plugins = codec && codec.plugins || [];
1183
- for (const plugin of plugins) {
1184
- if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1185
- }
1269
+ if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
1186
1270
  return {
1187
1271
  ...codec,
1188
1272
  plugins: [...plugins, ServerComponentPlugin]
@@ -1299,72 +1383,74 @@ function createServerComponentHandler({
1299
1383
  }
1300
1384
  }
1301
1385
 
1302
- Feature.AggregateError | Feature.BigIntTypedArray;
1303
- const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
1304
- CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
1305
- FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
1306
- function resolveSerializerPlugins(customPlugins) {
1307
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1308
- }
1309
- const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
1310
- const JSON_CODEC_DEPTH_LIMIT = 64;
1311
- function resolveCodecOptions({
1312
- plugins,
1313
- disabledFeatures,
1314
- depthLimit
1315
- } = {}) {
1316
- return {
1317
- plugins: resolveSerializerPlugins(plugins),
1318
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1319
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1320
- };
1386
+ const STATE = Symbol.for("dom-expressions.container-trace-state");
1387
+ const state = globalThis[STATE] || (globalThis[STATE] = {
1388
+ materialized: new WeakMap(),
1389
+ materializedValues: new WeakSet()
1390
+ });
1391
+ function setContainerTraceMaterializer(fn) {
1392
+ state.materializeTrace = fn;
1321
1393
  }
1322
- function createJSONDeserializer(options) {
1323
- const refs = new Map();
1324
- const resolved = resolveCodecOptions(options);
1325
- return function deserializeJSONChunk(node) {
1326
- return fromCrossJSON(node, {
1327
- refs,
1328
- ...resolved
1329
- });
1330
- };
1394
+ function materialize(marker) {
1395
+ let value = state.materialized.get(marker.$tr);
1396
+ if (value === undefined) {
1397
+ value = state.materializeTrace(marker);
1398
+ state.materialized.set(marker.$tr, value);
1399
+ if (value !== null && typeof value === "object") state.materializedValues.add(value);
1400
+ }
1401
+ return value;
1331
1402
  }
1332
- function createJSONDataTable(options) {
1333
- const deserialize = createJSONDeserializer(options);
1334
- const table = new Map();
1335
- return {
1336
- apply(record) {
1337
- const value = deserialize(record.node);
1338
- if (record.initial) table.set(record.key, value);
1339
- },
1340
- get(key) {
1341
- return table.get(key);
1342
- },
1343
- resolve(ref) {
1344
- return table.get(ref.$ref);
1345
- }
1346
- };
1403
+ function isMaterializedContainer(value) {
1404
+ return value !== null && typeof value === "object" && state.materializedValues.has(value);
1405
+ }
1406
+ function isContainerTraceMarker(value) {
1407
+ return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
1408
+ }
1409
+ function reviveContainerTraces(value) {
1410
+ if (!state.materializeTrace || value == null || typeof value !== "object") return value;
1411
+ if (isContainerTraceMarker(value)) return materialize(value);
1412
+ if (Array.isArray(value)) {
1413
+ for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
1414
+ } else if (Object.getPrototypeOf(value) === Object.prototype) {
1415
+ for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
1416
+ }
1417
+ return value;
1347
1418
  }
1348
1419
 
1420
+ setContainerTraceMaterializer(materializeContainerTrace);
1349
1421
  function asyncArg(value) {
1350
1422
  return value;
1351
1423
  }
1352
1424
  let sharedHost;
1425
+ let codec;
1426
+ let codecLoading;
1427
+ function loadCodec() {
1428
+ return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
1429
+ codec = m;
1430
+ });
1431
+ }
1353
1432
  const tables = new Map();
1433
+ function ensureTable(root) {
1434
+ let table = tables.get(root);
1435
+ if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
1436
+ return table;
1437
+ }
1354
1438
  function tableFor(id) {
1355
- const table = tables.get(id);
1356
- if (table) return table;
1357
- for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
1439
+ if (tables.has(id)) return ensureTable(id);
1440
+ for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
1358
1441
  return undefined;
1359
1442
  }
1360
1443
  function beginStream(frameId) {
1361
- tables.set(frameId, createJSONDataTable());
1444
+ tables.set(frameId, undefined);
1362
1445
  }
1363
1446
  function getFrameHost() {
1364
1447
  if (!sharedHost) {
1365
1448
  sharedHost = createFrameHost({
1449
+ prepareData: loadCodec,
1366
1450
  applyData: c => tableFor(c.id)?.apply(c),
1367
- resolve: (ref, id) => tableFor(id)?.resolve(ref)
1451
+ resolve: (ref, id) => tableFor(id)?.resolve(ref),
1452
+ revive: reviveContainerTraces,
1453
+ isContainer: isMaterializedContainer
1368
1454
  });
1369
1455
  }
1370
1456
  return sharedHost;
@@ -1436,6 +1522,7 @@ function slotArgsProxy(args) {
1436
1522
  return new Proxy({}, {
1437
1523
  get: (_, key) => {
1438
1524
  const v = args()[key];
1525
+ if (isMaterializedContainer(v)) return v;
1439
1526
  if (!isAsyncValue(v)) return v;
1440
1527
  let read = asyncReads.get(key);
1441
1528
  if (!read) {
@@ -1587,6 +1674,24 @@ function boundaryComponent(host, fnId) {
1587
1674
  };
1588
1675
  }
1589
1676
  const claimedBoundaries = new Set();
1677
+ const liveOps = new Map();
1678
+ const liveAppliers = new Set();
1679
+ let livePumped = null;
1680
+ function pumpLiveChannel() {
1681
+ const stream = globalThis._$HY?.r?.["sc:live"];
1682
+ if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
1683
+ livePumped = stream;
1684
+ const reader = stream.getReader();
1685
+ const pump = () => reader.read().then(r => {
1686
+ if (r.done) return;
1687
+ const op = r.value;
1688
+ liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
1689
+ for (const apply of liveAppliers) apply(op);
1690
+ return pump();
1691
+ });
1692
+ pump().catch(() => {
1693
+ });
1694
+ }
1590
1695
  let boundaryIndex = null;
1591
1696
  const isBoundaryId = id => !id.includes(".");
1592
1697
  function indexBoundaries(root) {
@@ -1665,6 +1770,7 @@ function adoptBoundary(host, id, el, props, binding) {
1665
1770
  const drainRecords = () => {
1666
1771
  const hy = globalThis._$HY;
1667
1772
  if (!hy || !hy.r) return;
1773
+ pumpLiveChannel();
1668
1774
  const slotPrefix = `sc:slot:${id}:`;
1669
1775
  for (const key of Object.keys(hy.r)) {
1670
1776
  if (appliedRecords.has(key)) continue;
@@ -1699,11 +1805,22 @@ function adoptBoundary(host, id, el, props, binding) {
1699
1805
  if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
1700
1806
  drainRecords();
1701
1807
  }) : undefined;
1808
+ const applyLiveOp = op => {
1809
+ if (op.type === "slot" && op.fid !== id) return;
1810
+ host.apply({
1811
+ ...op,
1812
+ id: address,
1813
+ version: 0
1814
+ });
1815
+ };
1816
+ liveAppliers.add(applyLiveOp);
1702
1817
  onCleanup(() => {
1818
+ liveAppliers.delete(applyLiveOp);
1703
1819
  unsubscribe && unsubscribe();
1704
1820
  if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
1705
1821
  });
1706
1822
  drainRecords();
1823
+ for (const op of liveOps.values()) applyLiveOp(op);
1707
1824
  const owner = getOwner();
1708
1825
  let release;
1709
1826
  let setGate;