@solidjs/web 2.0.0-beta.31 → 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 (73) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +285 -60
  3. package/dist/dev.js +267 -57
  4. package/dist/server.cjs +1148 -176
  5. package/dist/server.js +1133 -175
  6. package/dist/web.cjs +285 -60
  7. package/dist/web.js +267 -57
  8. package/frames/dist/client.cjs +397 -175
  9. package/frames/dist/client.dev.cjs +401 -175
  10. package/frames/dist/client.dev.js +399 -173
  11. package/frames/dist/client.js +395 -173
  12. package/frames/dist/server.cjs +1424 -277
  13. package/frames/dist/server.js +1426 -280
  14. package/package.json +69 -7
  15. package/serialization/decode/package.json +20 -0
  16. package/serialization/dist/decode.cjs +110 -0
  17. package/serialization/dist/decode.js +104 -0
  18. package/serialization/dist/serialization.cjs +106 -43
  19. package/serialization/dist/serialization.js +100 -44
  20. package/serialization/types/index.d.ts +85 -60
  21. package/serialization/types/serializer-decode.d.ts +182 -0
  22. package/serialization/types-cjs/index.d.cts +85 -60
  23. package/serialization/types-cjs/serializer-decode.d.cts +182 -0
  24. package/server-functions/dist/client.cjs +131 -98
  25. package/server-functions/dist/client.js +131 -99
  26. package/server-functions/dist/rich-args.cjs +11 -0
  27. package/server-functions/dist/rich-args.js +9 -0
  28. package/server-functions/dist/server.cjs +367 -194
  29. package/server-functions/dist/server.dev.cjs +1077 -0
  30. package/server-functions/dist/server.dev.js +1045 -0
  31. package/server-functions/dist/server.js +365 -195
  32. package/server-functions/package.json +10 -0
  33. package/server-functions/rich-args/package.json +20 -0
  34. package/storage/types/index.d.ts +1 -1
  35. package/storage/types-cjs/index.d.cts +1 -1
  36. package/types/client.d.ts +149 -6
  37. package/types/cookies.d.ts +93 -0
  38. package/types/core.d.ts +4 -2
  39. package/types/frames/client.d.ts +15 -1
  40. package/types/frames/frame-client.d.ts +63 -7
  41. package/types/frames/frame-sink.d.ts +26 -3
  42. package/types/frames/frame-transport.d.ts +40 -8
  43. package/types/frames/serializer.d.ts +85 -60
  44. package/types/frames/server.d.ts +22 -0
  45. package/types/index.d.ts +2 -3
  46. package/types/response.d.ts +45 -0
  47. package/types/serializer-decode.d.ts +182 -0
  48. package/types/serializer.d.ts +85 -60
  49. package/types/server-functions/client.d.ts +2 -1
  50. package/types/server-functions/rich-args.d.ts +10 -0
  51. package/types/server-functions/server.d.ts +99 -1
  52. package/types/server-functions/shared.d.ts +79 -1
  53. package/types/server-mock.d.ts +171 -59
  54. package/types/server.d.ts +209 -37
  55. package/types-cjs/client.d.cts +149 -6
  56. package/types-cjs/cookies.d.cts +93 -0
  57. package/types-cjs/core.d.cts +4 -2
  58. package/types-cjs/frames/client.d.cts +15 -1
  59. package/types-cjs/frames/frame-client.d.cts +63 -7
  60. package/types-cjs/frames/frame-sink.d.cts +26 -3
  61. package/types-cjs/frames/frame-transport.d.cts +40 -8
  62. package/types-cjs/frames/serializer.d.cts +85 -60
  63. package/types-cjs/frames/server.d.cts +22 -0
  64. package/types-cjs/index.d.cts +2 -3
  65. package/types-cjs/response.d.cts +45 -0
  66. package/types-cjs/serializer-decode.d.cts +182 -0
  67. package/types-cjs/serializer.d.cts +85 -60
  68. package/types-cjs/server-functions/client.d.cts +2 -1
  69. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  70. package/types-cjs/server-functions/server.d.cts +99 -1
  71. package/types-cjs/server-functions/shared.d.cts +79 -1
  72. package/types-cjs/server-mock.d.cts +171 -59
  73. package/types-cjs/server.d.cts +209 -37
@@ -1,8 +1,6 @@
1
- import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, createRenderEffect, sharedConfig, createSignal } 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
  }
@@ -94,9 +108,7 @@ function createFrameHost(options = {}) {
94
108
  if (store.version !== undefined && version < store.version) return false;
95
109
  if (store.version === undefined || version > store.version) {
96
110
  store.version = version;
97
- for (const key of Object.keys(store.records)) {
98
- if (key.startsWith("seg:") || key === ":error") delete store.records[key];
99
- }
111
+ clearStreamRecords(store.records);
100
112
  }
101
113
  Object.assign(store.records, records);
102
114
  return true;
@@ -161,7 +173,10 @@ function createFrameHost(options = {}) {
161
173
  },
162
174
  resolve(ref, frameId) {
163
175
  return options.resolve ? options.resolve(ref, frameId) : undefined;
164
- }
176
+ },
177
+ revive: options.revive,
178
+ isContainer: options.isContainer,
179
+ prepareData: options.prepareData
165
180
  };
166
181
  }
167
182
  const FRAME_APPLIED_EVENT = "frame:applied";
@@ -174,8 +189,10 @@ class FrameImpl {
174
189
  #store = Object.create(null);
175
190
  #appliedRootValue;
176
191
  #hasContent = false;
192
+ #errorNotified = false;
177
193
  #revealed = new Set();
178
194
  #fallbackShown = new Set();
195
+ #appliedHoles = new Map();
179
196
  #slots;
180
197
  #mountedSlots = new Set();
181
198
  #slotCleanups = new Map();
@@ -193,10 +210,12 @@ class FrameImpl {
193
210
  #claimTree = (node, direct) => {
194
211
  const handlers = claimHandlers();
195
212
  if (!handlers) return;
196
- const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
197
- const scope = this.#options.ownerScope;
198
- scope ? scope(run) : run();
213
+ this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
199
214
  };
215
+ #scoped(fn) {
216
+ const scope = this.#options.ownerScope;
217
+ return scope ? scope(fn) : fn();
218
+ }
200
219
  constructor(element, start, end, options = {}) {
201
220
  this.#element = element;
202
221
  this.#start = start;
@@ -257,15 +276,9 @@ class FrameImpl {
257
276
  return;
258
277
  } else if (v > this.#version) {
259
278
  this.#version = v;
260
- this.#revealed.clear();
261
- this.#fallbackShown.clear();
262
- for (const key of Object.keys(this.#store)) {
263
- if (key.startsWith("seg:") || key === ":error") {
264
- delete this.#store[key];
265
- }
266
- }
279
+ this.#resetStreamState();
267
280
  }
268
- for (const key of Object.keys(write.r)) {
281
+ for (const key in write.r) {
269
282
  const incoming = write.r[key];
270
283
  if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {
271
284
  const existing = this.#store[key];
@@ -277,6 +290,13 @@ class FrameImpl {
277
290
  }
278
291
  this.#flush();
279
292
  }
293
+ #resetStreamState(root) {
294
+ this.#revealed.clear();
295
+ this.#fallbackShown.clear();
296
+ this.#appliedHoles.clear();
297
+ this.#errorNotified = false;
298
+ clearStreamRecords(this.#store, root);
299
+ }
280
300
  #flush() {
281
301
  if (this.#disposed) return;
282
302
  const version = this.#version;
@@ -287,10 +307,14 @@ class FrameImpl {
287
307
  this.#appliedRootValue = root.value;
288
308
  this.#applied(version, reason);
289
309
  }
310
+ if (this.#store[":error"] && !this.#errorNotified) {
311
+ this.#errorNotified = true;
312
+ this.#applied(version, "error");
313
+ }
290
314
  let progressed = true;
291
315
  while (progressed) {
292
316
  progressed = false;
293
- for (const key of Object.keys(this.#store)) {
317
+ for (const key in this.#store) {
294
318
  const name = segmentName(key);
295
319
  if (name === null || this.#revealed.has(name)) continue;
296
320
  if (this.#segmentReady(name)) {
@@ -299,7 +323,7 @@ class FrameImpl {
299
323
  progressed = true;
300
324
  }
301
325
  }
302
- for (const key of Object.keys(this.#store)) {
326
+ for (const key in this.#store) {
303
327
  const m = /^seg:([^:]+):fallback$/.exec(key);
304
328
  if (!m) continue;
305
329
  const name = m[1];
@@ -311,7 +335,25 @@ class FrameImpl {
311
335
  }
312
336
  }
313
337
  }
314
- for (const key of Object.keys(this.#store)) {
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
+ console.error(`Live hole ${key.slice(5, -6)} failed on the server; latched:`, record);
345
+ } else if (this.#applyHole(key.slice(5), record.value)) {
346
+ this.#appliedHoles.set(key, record);
347
+ this.#applied(version, "morph");
348
+ }
349
+ } else if (key.startsWith("attr:")) {
350
+ if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
351
+ this.#appliedHoles.set(key, record);
352
+ this.#applied(version, "morph");
353
+ }
354
+ }
355
+ }
356
+ for (const key in this.#store) {
315
357
  if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;
316
358
  this.#processedAssets.add(key);
317
359
  const record = this.#store[key];
@@ -343,6 +385,7 @@ class FrameImpl {
343
385
  const callback = this.#resolveSlot(propOf(occurrence));
344
386
  if (!callback) continue;
345
387
  const record = this.#resolveSlotRecord(occurrence);
388
+ if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
346
389
  const prev = this.#slotNodes.get(occurrence);
347
390
  const prevFirst = Array.isArray(prev) ? prev[0] : prev;
348
391
  const zombie = this.#mountedSlots.has(occurrence) && prevFirst && !prevFirst.parentNode;
@@ -398,15 +441,7 @@ class FrameImpl {
398
441
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
399
442
  let existing = [];
400
443
  let end = null;
401
- if (start) {
402
- const endData = slotEnd(occurrence);
403
- let n = start.nextSibling;
404
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
405
- existing.push(n);
406
- n = n.nextSibling;
407
- }
408
- end = n;
409
- }
444
+ if (start) end = eachInRange(start, occurrence, n => existing.push(n));
410
445
  const ctx = {
411
446
  frame: this.#options.claimScope ?? this.#options.id,
412
447
  key: occurrence,
@@ -421,23 +456,16 @@ class FrameImpl {
421
456
  } : undefined
422
457
  };
423
458
  const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
424
- const scope = this.#options.ownerScope;
425
- const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
459
+ const content = this.#scoped(() => callback(props, ctx));
426
460
  this.#slotArgs.set(occurrence, record);
427
461
  if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);
428
462
  if (content == null) return null;
429
463
  return Array.isArray(content) ? content : [content];
430
464
  }
431
465
  #replaceRange(key, start, nodes) {
432
- const end = slotEnd(key);
433
466
  const parent = start.parentNode;
434
- let n = start.nextSibling;
435
- while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
436
- const next = n.nextSibling;
437
- parent.removeChild(n);
438
- n = next;
439
- }
440
- for (const node of nodes) parent.insertBefore(node, n);
467
+ const end = eachInRange(start, key, n => parent.removeChild(n));
468
+ for (const node of nodes) parent.insertBefore(node, end);
441
469
  }
442
470
  #unmountSlot(key) {
443
471
  this.#mountedSlots.delete(key);
@@ -449,9 +477,7 @@ class FrameImpl {
449
477
  this.#runSlotCleanups(key);
450
478
  const regions = this.#slotRegions.get(key);
451
479
  if (regions) {
452
- for (const {
453
- frame
454
- } of regions.values()) frame?.dispose();
480
+ disposeRegions(regions);
455
481
  this.#slotRegions.delete(key);
456
482
  }
457
483
  }
@@ -461,15 +487,26 @@ class FrameImpl {
461
487
  this.#slotCleanups.delete(key);
462
488
  for (const fn of cleanups) fn();
463
489
  }
490
+ #refsUnresolved(args) {
491
+ const {
492
+ host,
493
+ id
494
+ } = this.#options;
495
+ if (host) for (const key in args) {
496
+ if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
497
+ }
498
+ return false;
499
+ }
500
+ #regionsFor(slotKey) {
501
+ let regions = this.#slotRegions.get(slotKey);
502
+ if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
503
+ return regions;
504
+ }
464
505
  #resolveArgs(slotKey, args) {
465
506
  const host = this.#options.host;
466
- let regions = this.#slotRegions.get(slotKey);
467
- if (!regions) {
468
- regions = new Map();
469
- this.#slotRegions.set(slotKey, regions);
470
- }
507
+ const regions = this.#regionsFor(slotKey);
471
508
  const props = {};
472
- for (const key of Object.keys(args)) {
509
+ for (const key in args) {
473
510
  const value = args[key];
474
511
  if (isDataRef(value)) {
475
512
  const resolved = host ? host.resolve(value, this.#options.id) : undefined;
@@ -492,24 +529,15 @@ class FrameImpl {
492
529
  }
493
530
  props[key] = entry.element;
494
531
  } else {
495
- props[key] = value;
532
+ props[key] = host && host.revive ? host.revive(value) : value;
496
533
  }
497
534
  }
498
535
  return props;
499
536
  }
500
537
  #discoverRegions(slotKey, start) {
501
538
  if (!start) return;
502
- let regions = this.#slotRegions.get(slotKey);
503
- if (!regions) {
504
- regions = new Map();
505
- this.#slotRegions.set(slotKey, regions);
506
- }
507
- const endData = slotEnd(slotKey);
508
- let n = start.nextSibling;
509
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
510
- collectRegionElements(n, regions);
511
- n = n.nextSibling;
512
- }
539
+ const regions = this.#regionsFor(slotKey);
540
+ eachInRange(start, slotKey, n => collectRegionElements(n, regions));
513
541
  }
514
542
  #refArgsUnchanged(occurrence, record) {
515
543
  const old = this.#slotArgs.get(occurrence);
@@ -525,11 +553,15 @@ class FrameImpl {
525
553
  const va = a[key];
526
554
  const vb = b[key];
527
555
  if (va === vb) continue;
528
- if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
529
- if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
530
- if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
556
+ if (isFrameRef(va) && isFrameRef(vb)) continue;
557
+ if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
531
558
  const host = this.#options.host;
532
559
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
560
+ if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
561
+ if (next === cache[key]) continue;
562
+ return false;
563
+ }
564
+ if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
533
565
  try {
534
566
  if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;
535
567
  } catch (e) {
@@ -545,7 +577,7 @@ class FrameImpl {
545
577
  if (!args) return;
546
578
  const regions = this.#slotRegions.get(occurrence);
547
579
  if (!regions) return;
548
- for (const key of Object.keys(args)) {
580
+ for (const key in args) {
549
581
  const value = args[key];
550
582
  if (!isFrameRef(value)) continue;
551
583
  const entry = regions.get(key);
@@ -595,11 +627,8 @@ class FrameImpl {
595
627
  this.#element.setAttribute(FRAME_ID_ATTR, id);
596
628
  }
597
629
  this.#version = undefined;
598
- this.#revealed.clear();
599
- this.#fallbackShown.clear();
600
- for (const key of Object.keys(this.#store)) {
601
- if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
602
- }
630
+ this.#appliedRootValue = undefined;
631
+ this.#resetStreamState(true);
603
632
  if (host) host.register(id, this);
604
633
  }
605
634
  rebase() {
@@ -619,11 +648,7 @@ class FrameImpl {
619
648
  }
620
649
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
621
650
  for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
622
- for (const regions of this.#slotRegions.values()) {
623
- for (const {
624
- frame
625
- } of regions.values()) frame?.dispose();
626
- }
651
+ for (const regions of this.#slotRegions.values()) disposeRegions(regions);
627
652
  this.#slotRegions.clear();
628
653
  this.#mountedSlots.clear();
629
654
  }
@@ -644,14 +669,40 @@ class FrameImpl {
644
669
  if (ranges.size) for (const root of grafts) flushGrafts(root, ranges);
645
670
  }
646
671
  }
647
- #clearContent() {
648
- const parent = this.#parent();
649
- let n = this.#firstContent();
650
- while (n && n !== this.#end) {
651
- const next = n.nextSibling;
652
- parent.removeChild(n);
653
- n = next;
672
+ #applyHole(marker, html) {
673
+ if (!this.#hasContent) return false;
674
+ const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
675
+ if (!open) return false;
676
+ const close = rangeClose(open, "lh:/" + marker.slice(3));
677
+ if (!close) {
678
+ {
679
+ console.error(`Live hole "${marker}" is missing its closing comment; update dropped. ` + `Likely an HTML-rewriting layer stripped it.`, open);
680
+ }
681
+ return false;
682
+ }
683
+ const claim = claimHandlers() ? this.#claimTree : null;
684
+ reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
685
+ return true;
686
+ }
687
+ #applyAttrs(addr, text, removed) {
688
+ if (!this.#hasContent) return false;
689
+ const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
690
+ if (!el) return false;
691
+ const parsed = parseFragment(`<i${text}></i>`).firstChild;
692
+ if (parsed) {
693
+ for (let i = 0; i < parsed.attributes.length; i++) {
694
+ const {
695
+ name,
696
+ value
697
+ } = parsed.attributes[i];
698
+ if (el.getAttribute(name) !== value) el.setAttribute(name, value);
699
+ }
654
700
  }
701
+ if (removed) for (const name of removed) el.removeAttribute(name);
702
+ return true;
703
+ }
704
+ #clearContent() {
705
+ removeUntil(this.#parent(), this.#firstContent(), this.#end);
655
706
  }
656
707
  #claimContent() {
657
708
  if (!claimHandlers()) return;
@@ -668,9 +719,7 @@ class FrameImpl {
668
719
  const assets = this.#store[`seg:${name}:assets`];
669
720
  if (assets && assets.styles) {
670
721
  let ready = true;
671
- for (const entry of assets.styles) {
672
- if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
673
- }
722
+ for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
674
723
  if (!ready) return false;
675
724
  }
676
725
  if (!this.#findPlaceholder(name)) return false;
@@ -687,12 +736,7 @@ class FrameImpl {
687
736
  console.error(`Frame fragment placeholder "${name}" is missing its closing comment ` + `(<!--${placeholderId(name)}-->); revealed content will be appended at the end of ` + `its parent instead of in place. Likely an HTML-rewriting layer stripped the ` + `comment, or invalid nesting split the placeholder range.`, tpl);
688
737
  }
689
738
  const parent = tpl.parentNode;
690
- let n = tpl.nextSibling;
691
- while (n && n !== closing) {
692
- const next = n.nextSibling;
693
- parent.removeChild(n);
694
- n = next;
695
- }
739
+ removeUntil(parent, tpl.nextSibling, closing);
696
740
  if (this.#options.reveal) {
697
741
  const fallbackFrag = tpl.content.cloneNode(true);
698
742
  this.#claimTree(fallbackFrag);
@@ -741,9 +785,7 @@ function createFrameElement(options) {
741
785
  const frame = new FrameImpl(el, null, null, options);
742
786
  return {
743
787
  element: el,
744
- get frame() {
745
- return frame;
746
- },
788
+ frame,
747
789
  dispose() {
748
790
  frame.dispose();
749
791
  el.remove();
@@ -763,15 +805,30 @@ function segmentName(key) {
763
805
  const m = /^seg:([^:]+)$/.exec(key);
764
806
  return m ? m[1] : null;
765
807
  }
808
+ function isAsyncLike(v) {
809
+ return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
810
+ }
766
811
  function propOf(occurrence) {
767
812
  const hash = occurrence.indexOf("#");
768
813
  return hash === -1 ? occurrence : occurrence.slice(0, hash);
769
814
  }
770
815
  function isDataRef(value) {
771
- return typeof value === "object" && value !== null && typeof value.$ref === "string";
816
+ return !!value && typeof value.$ref === "string";
772
817
  }
773
818
  function isFrameRef(value) {
774
- return typeof value === "object" && value !== null && typeof value.$frame === "string";
819
+ return !!value && typeof value.$frame === "string";
820
+ }
821
+ function disposeRegions(regions) {
822
+ for (const {
823
+ frame
824
+ } of regions.values()) frame?.dispose();
825
+ }
826
+ function removeUntil(parent, n, stop) {
827
+ while (n && n !== stop) {
828
+ const next = n.nextSibling;
829
+ parent.removeChild(n);
830
+ n = next;
831
+ }
775
832
  }
776
833
  function parseFragment(html) {
777
834
  const template = document.createElement("template");
@@ -779,9 +836,8 @@ function parseFragment(html) {
779
836
  return template.content;
780
837
  }
781
838
  function findHeadElement(selector, attr, value) {
782
- const nodes = document.head.querySelectorAll(selector);
783
- for (let i = 0; i < nodes.length; i++) {
784
- if (nodes[i].getAttribute(attr) === value) return nodes[i];
839
+ for (const node of document.head.querySelectorAll(selector)) {
840
+ if (node.getAttribute(attr) === value) return node;
785
841
  }
786
842
  return null;
787
843
  }
@@ -830,7 +886,7 @@ function applyInlineStyles(inlineStyles) {
830
886
  }
831
887
  }
832
888
  function isPlaceholderStart(node, id) {
833
- return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
889
+ return node.tagName === "TEMPLATE" && node.id === id;
834
890
  }
835
891
  function rangeClose(start, id) {
836
892
  let n = start.nextSibling;
@@ -840,6 +896,20 @@ function rangeClose(start, id) {
840
896
  }
841
897
  return null;
842
898
  }
899
+ function findLiveTarget(n, end, test) {
900
+ while (n && n !== end) {
901
+ if (n.nodeType === ELEMENT_NODE) {
902
+ const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
903
+ if (fid === null || fid.includes(".")) {
904
+ if (test(n)) return n;
905
+ const found = findLiveTarget(n.firstChild, null, test);
906
+ if (found) return found;
907
+ }
908
+ } else if (test(n)) return n;
909
+ n = n.nextSibling;
910
+ }
911
+ return null;
912
+ }
843
913
  function findPlaceholder(n, end, id) {
844
914
  while (n && n !== end) {
845
915
  if (isPlaceholderStart(n, id)) return n;
@@ -887,6 +957,23 @@ function renameRegion(entry, childId) {
887
957
  entry.childId = childId;
888
958
  if (entry.frame) entry.frame.rebind(childId);else entry.element.setAttribute(FRAME_ID_ATTR, childId);
889
959
  }
960
+ function eachInRange(start, key, cb) {
961
+ const end = slotEnd(key);
962
+ let n = start.nextSibling;
963
+ while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
964
+ const next = n.nextSibling;
965
+ cb(n);
966
+ n = next;
967
+ }
968
+ return n;
969
+ }
970
+ function clearStreamRecords(records, root) {
971
+ for (const key in records) {
972
+ if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
973
+ delete records[key];
974
+ }
975
+ }
976
+ }
890
977
  function argsEquivalent(a, b) {
891
978
  if (a === b) return true;
892
979
  if (!a || !b) return false;
@@ -897,9 +984,7 @@ function argsEquivalent(a, b) {
897
984
  const va = a[key];
898
985
  const vb = b[key];
899
986
  if (va === vb) continue;
900
- if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
901
- continue;
902
- }
987
+ if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
903
988
  return false;
904
989
  }
905
990
  return true;
@@ -1151,6 +1236,7 @@ async function applyFrameResponse(response, host, options = {}) {
1151
1236
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
1152
1237
  chunk.version = v;
1153
1238
  } else if (version !== undefined) chunk.version = version;
1239
+ if (chunk.type === "data" && host.prepareData) await host.prepareData();
1154
1240
  host.apply(chunk);
1155
1241
  }
1156
1242
  result = await reader.next();
@@ -1167,7 +1253,7 @@ function parseServerComponent(value, ctx) {
1167
1253
  address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1168
1254
  };
1169
1255
  }
1170
- const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1256
+ const ServerComponentPlugin = {
1171
1257
  tag: "dom-expressions/server-component",
1172
1258
  test(value) {
1173
1259
  return typeof value === "function" && SERVER_COMPONENT in value;
@@ -1194,12 +1280,10 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1194
1280
  }
1195
1281
  return globalThis._$SC.r(id);
1196
1282
  }
1197
- });
1283
+ };
1198
1284
  function flightCodec(codec) {
1199
1285
  const plugins = codec && codec.plugins || [];
1200
- for (const plugin of plugins) {
1201
- if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1202
- }
1286
+ if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
1203
1287
  return {
1204
1288
  ...codec,
1205
1289
  plugins: [...plugins, ServerComponentPlugin]
@@ -1271,7 +1355,7 @@ function createServerComponentHandler({
1271
1355
  showing(address, functionId) {
1272
1356
  bindingFor(address, functionId);
1273
1357
  const comp = componentFor(functionId);
1274
- if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
1358
+ if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
1275
1359
  comp[COMPONENT_BINDING] = {
1276
1360
  component: comp,
1277
1361
  address
@@ -1316,69 +1400,74 @@ function createServerComponentHandler({
1316
1400
  }
1317
1401
  }
1318
1402
 
1319
- Feature.AggregateError | Feature.BigIntTypedArray;
1320
- const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
1321
- CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
1322
- FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
1323
- function resolveSerializerPlugins(customPlugins) {
1324
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1325
- }
1326
- const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
1327
- const JSON_CODEC_DEPTH_LIMIT = 64;
1328
- function resolveCodecOptions({
1329
- plugins,
1330
- disabledFeatures,
1331
- depthLimit
1332
- } = {}) {
1333
- return {
1334
- plugins: resolveSerializerPlugins(plugins),
1335
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1336
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1337
- };
1403
+ const STATE = Symbol.for("dom-expressions.container-trace-state");
1404
+ const state = globalThis[STATE] || (globalThis[STATE] = {
1405
+ materialized: new WeakMap(),
1406
+ materializedValues: new WeakSet()
1407
+ });
1408
+ function setContainerTraceMaterializer(fn) {
1409
+ state.materializeTrace = fn;
1338
1410
  }
1339
- function createJSONDeserializer(options) {
1340
- const refs = new Map();
1341
- const resolved = resolveCodecOptions(options);
1342
- return function deserializeJSONChunk(node) {
1343
- return fromCrossJSON(node, {
1344
- refs,
1345
- ...resolved
1346
- });
1347
- };
1411
+ function materialize(marker) {
1412
+ let value = state.materialized.get(marker.$tr);
1413
+ if (value === undefined) {
1414
+ value = state.materializeTrace(marker);
1415
+ state.materialized.set(marker.$tr, value);
1416
+ if (value !== null && typeof value === "object") state.materializedValues.add(value);
1417
+ }
1418
+ return value;
1348
1419
  }
1349
- function createJSONDataTable(options) {
1350
- const deserialize = createJSONDeserializer(options);
1351
- const table = new Map();
1352
- return {
1353
- apply(record) {
1354
- const value = deserialize(record.node);
1355
- if (record.initial) table.set(record.key, value);
1356
- },
1357
- get(key) {
1358
- return table.get(key);
1359
- },
1360
- resolve(ref) {
1361
- return table.get(ref.$ref);
1362
- }
1363
- };
1420
+ function isMaterializedContainer(value) {
1421
+ return value !== null && typeof value === "object" && state.materializedValues.has(value);
1422
+ }
1423
+ function isContainerTraceMarker(value) {
1424
+ return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
1425
+ }
1426
+ function reviveContainerTraces(value) {
1427
+ if (!state.materializeTrace || value == null || typeof value !== "object") return value;
1428
+ if (isContainerTraceMarker(value)) return materialize(value);
1429
+ if (Array.isArray(value)) {
1430
+ for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
1431
+ } else if (Object.getPrototypeOf(value) === Object.prototype) {
1432
+ for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
1433
+ }
1434
+ return value;
1364
1435
  }
1365
1436
 
1437
+ setContainerTraceMaterializer(materializeContainerTrace);
1438
+ function asyncArg(value) {
1439
+ return value;
1440
+ }
1366
1441
  let sharedHost;
1442
+ let codec;
1443
+ let codecLoading;
1444
+ function loadCodec() {
1445
+ return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
1446
+ codec = m;
1447
+ });
1448
+ }
1367
1449
  const tables = new Map();
1450
+ function ensureTable(root) {
1451
+ let table = tables.get(root);
1452
+ if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
1453
+ return table;
1454
+ }
1368
1455
  function tableFor(id) {
1369
- const table = tables.get(id);
1370
- if (table) return table;
1371
- for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
1456
+ if (tables.has(id)) return ensureTable(id);
1457
+ for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
1372
1458
  return undefined;
1373
1459
  }
1374
1460
  function beginStream(frameId) {
1375
- tables.set(frameId, createJSONDataTable());
1461
+ tables.set(frameId, undefined);
1376
1462
  }
1377
1463
  function getFrameHost() {
1378
1464
  if (!sharedHost) {
1379
1465
  sharedHost = createFrameHost({
1466
+ prepareData: loadCodec,
1380
1467
  applyData: c => tableFor(c.id)?.apply(c),
1381
- resolve: (ref, id) => tableFor(id)?.resolve(ref)
1468
+ resolve: (ref, id) => tableFor(id)?.resolve(ref),
1469
+ revive: reviveContainerTraces,
1470
+ isContainer: isMaterializedContainer
1382
1471
  });
1383
1472
  }
1384
1473
  return sharedHost;
@@ -1439,8 +1528,27 @@ function claimRender(prefix, existing, render) {
1439
1528
  function liveSlotProps(initial, ctx) {
1440
1529
  const [args, setArgs] = createSignal(initial);
1441
1530
  ctx.onUpdate(next => setArgs(() => next));
1531
+ return slotArgsProxy(args);
1532
+ }
1533
+ function isAsyncValue(v) {
1534
+ return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
1535
+ }
1536
+ function slotArgsProxy(args) {
1537
+ const owner = getOwner();
1538
+ const asyncReads = new Map();
1442
1539
  return new Proxy({}, {
1443
- get: (_, key) => args()[key],
1540
+ get: (_, key) => {
1541
+ const v = args()[key];
1542
+ if (isMaterializedContainer(v)) return v;
1543
+ if (!isAsyncValue(v)) return v;
1544
+ let read = asyncReads.get(key);
1545
+ if (!read) {
1546
+ const make = () => createMemo(() => args()[key]);
1547
+ read = owner ? runWithOwner(owner, make) : make();
1548
+ asyncReads.set(key, read);
1549
+ }
1550
+ return read();
1551
+ },
1444
1552
  has: (_, key) => key in args(),
1445
1553
  ownKeys: () => Reflect.ownKeys(args()),
1446
1554
  getOwnPropertyDescriptor: (_, key) => key in args() ? {
@@ -1458,6 +1566,7 @@ function isReactiveContent(value) {
1458
1566
  }
1459
1567
  function slotsFor(props) {
1460
1568
  const bindings = new Map();
1569
+ const fillScopes = new Map();
1461
1570
  return new Proxy({}, {
1462
1571
  get(_, prop) {
1463
1572
  if (typeof prop !== "string" || !(prop in props)) return undefined;
@@ -1468,6 +1577,19 @@ function slotsFor(props) {
1468
1577
  bindings.delete(key);
1469
1578
  prev.dispose();
1470
1579
  }
1580
+ const prevFill = key !== undefined && fillScopes.get(key);
1581
+ if (prevFill) {
1582
+ fillScopes.delete(key);
1583
+ prevFill.dispose();
1584
+ }
1585
+ const fillOwner = streamInvoke ? createOwner() : null;
1586
+ if (fillOwner && key !== undefined && ctx) {
1587
+ fillScopes.set(key, fillOwner);
1588
+ ctx.onCleanup(() => {
1589
+ if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
1590
+ fillOwner.dispose();
1591
+ });
1592
+ }
1471
1593
  const settle = out => {
1472
1594
  const existing = ctx && ctx.existing || [];
1473
1595
  if (existing.length) {
@@ -1480,13 +1602,13 @@ function slotsFor(props) {
1480
1602
  const evaluate = () => {
1481
1603
  const v = props[prop];
1482
1604
  if (typeof v === "function" && ctx && ctx.invoked) {
1483
- return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
1605
+ return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
1484
1606
  }
1485
1607
  return v;
1486
1608
  };
1487
1609
  const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1488
1610
  const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1489
- const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1611
+ const value = fillOwner ? runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1490
1612
  if (!isReactiveContent(value)) {
1491
1613
  return settle(normalizeSlotContent(value));
1492
1614
  }
@@ -1511,32 +1633,82 @@ function slotsFor(props) {
1511
1633
  function revealSeam(owner) {
1512
1634
  return seam => runWithOwner(owner, () => insert(seam.before.parentNode, createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1513
1635
  }
1636
+ let streamInvoke = false;
1514
1637
  function boundaryScope(owner) {
1515
- return fn => getOwner() ? fn() : runWithOwner(owner, fn);
1516
- }
1517
- function followBinding(frame, binding) {
1518
- createRenderEffect(binding, address => frame.rebind(address));
1638
+ return fn => {
1639
+ if (getOwner()) return fn();
1640
+ streamInvoke = true;
1641
+ try {
1642
+ return runWithOwner(owner, fn);
1643
+ } finally {
1644
+ streamInvoke = false;
1645
+ }
1646
+ };
1519
1647
  }
1520
1648
  function boundaryComponent(host, fnId) {
1521
1649
  return (props, binding) => {
1522
1650
  const owner = getOwner();
1651
+ const id = binding ? binding() : fnId;
1652
+ let applied = !tables.has(id);
1653
+ let release;
1654
+ const arm = () => new Promise(r => release = r);
1655
+ const mountGate = applied ? undefined : arm();
1656
+ let setGate;
1523
1657
  const {
1524
1658
  element,
1525
1659
  frame,
1526
1660
  dispose
1527
1661
  } = createFrameElement({
1528
1662
  host,
1529
- id: binding ? binding() : fnId,
1663
+ id,
1530
1664
  slots: slotsFor(props),
1531
1665
  ownerScope: boundaryScope(owner),
1532
- reveal: revealSeam(owner)
1666
+ reveal: revealSeam(owner),
1667
+ onApply: () => {
1668
+ applied = true;
1669
+ if (release) {
1670
+ release();
1671
+ release = undefined;
1672
+ }
1673
+ setGate && setGate(undefined);
1674
+ }
1533
1675
  });
1534
- if (binding) followBinding(frame, binding);
1676
+ const [gatePromise, setGatePromise] = createSignal(applied ? undefined : mountGate);
1677
+ setGate = setGatePromise;
1678
+ if (binding) {
1679
+ createRenderEffect(binding, (address, prev) => {
1680
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1681
+ applied = false;
1682
+ setGatePromise(arm());
1683
+ }
1684
+ frame.rebind(address);
1685
+ });
1686
+ }
1535
1687
  onCleanup(dispose);
1536
- return element;
1688
+ if (applied && !binding) return element;
1689
+ const gate = createMemo(() => gatePromise());
1690
+ return createMemo(() => (gate(), element));
1537
1691
  };
1538
1692
  }
1539
1693
  const claimedBoundaries = new Set();
1694
+ const liveOps = new Map();
1695
+ const liveAppliers = new Set();
1696
+ let livePumped = null;
1697
+ function pumpLiveChannel() {
1698
+ const stream = globalThis._$HY?.r?.["sc:live"];
1699
+ if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
1700
+ livePumped = stream;
1701
+ const reader = stream.getReader();
1702
+ const pump = () => reader.read().then(r => {
1703
+ if (r.done) return;
1704
+ const op = r.value;
1705
+ liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
1706
+ for (const apply of liveAppliers) apply(op);
1707
+ return pump();
1708
+ });
1709
+ pump().catch(() => {
1710
+ });
1711
+ }
1540
1712
  let boundaryIndex = null;
1541
1713
  const isBoundaryId = id => !id.includes(".");
1542
1714
  function indexBoundaries(root) {
@@ -1601,9 +1773,21 @@ function adoptBoundary(host, id, el, props, binding) {
1601
1773
  claimedBoundaries.add(id);
1602
1774
  const address = binding ? binding() : documentAddress(id);
1603
1775
  const appliedRecords = new Set();
1776
+ const claimedFragments = new Set();
1777
+ const claimRegionFragments = root => {
1778
+ const fr = globalThis._$HY?.fr;
1779
+ if (!fr || !fr.claim) return;
1780
+ root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
1781
+ const fragId = tpl.id.slice(3);
1782
+ if (claimedFragments.has(fragId)) return;
1783
+ claimedFragments.add(fragId);
1784
+ fr.claim(fragId);
1785
+ });
1786
+ };
1604
1787
  const drainRecords = () => {
1605
1788
  const hy = globalThis._$HY;
1606
1789
  if (!hy || !hy.r) return;
1790
+ pumpLiveChannel();
1607
1791
  const slotPrefix = `sc:slot:${id}:`;
1608
1792
  for (const key of Object.keys(hy.r)) {
1609
1793
  if (appliedRecords.has(key)) continue;
@@ -1632,8 +1816,31 @@ function adoptBoundary(host, id, el, props, binding) {
1632
1816
  }
1633
1817
  }
1634
1818
  };
1819
+ claimRegionFragments(el);
1820
+ const fr = globalThis._$HY?.fr;
1821
+ const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
1822
+ if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
1823
+ drainRecords();
1824
+ }) : undefined;
1825
+ const applyLiveOp = op => {
1826
+ if (op.type === "slot" && op.fid !== id) return;
1827
+ host.apply({
1828
+ ...op,
1829
+ id: address,
1830
+ version: 0
1831
+ });
1832
+ };
1833
+ liveAppliers.add(applyLiveOp);
1834
+ onCleanup(() => {
1835
+ liveAppliers.delete(applyLiveOp);
1836
+ unsubscribe && unsubscribe();
1837
+ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
1838
+ });
1635
1839
  drainRecords();
1840
+ for (const op of liveOps.values()) applyLiveOp(op);
1636
1841
  const owner = getOwner();
1842
+ let release;
1843
+ let setGate;
1637
1844
  const frame = createFrame(el, {
1638
1845
  adopt: true,
1639
1846
  host,
@@ -1641,6 +1848,13 @@ function adoptBoundary(host, id, el, props, binding) {
1641
1848
  slots: slotsFor(props),
1642
1849
  ownerScope: boundaryScope(owner),
1643
1850
  reveal: revealSeam(owner),
1851
+ onApply: () => {
1852
+ if (release) {
1853
+ release();
1854
+ release = undefined;
1855
+ }
1856
+ setGate && setGate(undefined);
1857
+ },
1644
1858
  ...{
1645
1859
  recordsPending: () => {
1646
1860
  if (document.readyState === "loading") return true;
@@ -1651,7 +1865,19 @@ function adoptBoundary(host, id, el, props, binding) {
1651
1865
  claimScope: id
1652
1866
  }
1653
1867
  });
1654
- if (binding) followBinding(frame, binding);
1868
+ if (binding) {
1869
+ const arm = () => new Promise(r => release = r);
1870
+ const [gatePromise, setGatePromise] = createSignal(undefined);
1871
+ setGate = setGatePromise;
1872
+ const gate = createMemo(() => gatePromise());
1873
+ createRenderEffect(binding, (address, prev) => {
1874
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1875
+ setGatePromise(arm());
1876
+ }
1877
+ frame.rebind(address);
1878
+ });
1879
+ createRenderEffect(() => (gate(), undefined), () => {});
1880
+ }
1655
1881
  onCleanup(() => frame.dispose());
1656
1882
  return el;
1657
1883
  }
@@ -1692,4 +1918,4 @@ function installServerComponents(host = getFrameHost()) {
1692
1918
  });
1693
1919
  }
1694
1920
 
1695
- export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, createFrame, createFrameElement, createFrameHost, createJSONDataTable, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
1921
+ export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, asyncArg, createFrame, createFrameElement, createFrameHost, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };