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

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 (64) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +234 -45
  3. package/dist/dev.js +221 -42
  4. package/dist/server.cjs +456 -123
  5. package/dist/server.js +445 -120
  6. package/dist/web.cjs +234 -45
  7. package/dist/web.js +221 -42
  8. package/frames/dist/client.cjs +217 -112
  9. package/frames/dist/client.dev.cjs +217 -112
  10. package/frames/dist/client.dev.js +218 -113
  11. package/frames/dist/client.js +218 -113
  12. package/frames/dist/server.cjs +488 -177
  13. package/frames/dist/server.js +489 -179
  14. package/package.json +55 -4
  15. package/serialization/dist/serialization.cjs +8 -0
  16. package/serialization/dist/serialization.js +1 -0
  17. package/serialization/types/index.d.ts +173 -6
  18. package/serialization/types-cjs/index.d.cts +173 -6
  19. package/server-functions/dist/client.cjs +46 -9
  20. package/server-functions/dist/client.js +47 -11
  21. package/server-functions/dist/rich-args.cjs +11 -0
  22. package/server-functions/dist/rich-args.js +9 -0
  23. package/server-functions/dist/server.cjs +275 -126
  24. package/server-functions/dist/server.dev.cjs +1053 -0
  25. package/server-functions/dist/server.dev.js +1021 -0
  26. package/server-functions/dist/server.js +273 -127
  27. package/server-functions/package.json +10 -0
  28. package/server-functions/rich-args/package.json +20 -0
  29. package/storage/types/index.d.ts +1 -1
  30. package/storage/types-cjs/index.d.cts +1 -1
  31. package/types/client.d.ts +127 -6
  32. package/types/core.d.ts +3 -1
  33. package/types/frames/client.d.ts +15 -1
  34. package/types/frames/frame-client.d.ts +37 -7
  35. package/types/frames/frame-sink.d.ts +26 -3
  36. package/types/frames/frame-transport.d.ts +39 -7
  37. package/types/frames/serializer.d.ts +173 -6
  38. package/types/frames/server.d.ts +22 -0
  39. package/types/index.d.ts +2 -3
  40. package/types/response.d.ts +45 -0
  41. package/types/serializer.d.ts +173 -6
  42. package/types/server-functions/client.d.ts +1 -0
  43. package/types/server-functions/rich-args.d.ts +10 -0
  44. package/types/server-functions/server.d.ts +98 -0
  45. package/types/server-functions/shared.d.ts +22 -0
  46. package/types/server-mock.d.ts +171 -59
  47. package/types/server.d.ts +188 -36
  48. package/types-cjs/client.d.cts +127 -6
  49. package/types-cjs/core.d.cts +3 -1
  50. package/types-cjs/frames/client.d.cts +15 -1
  51. package/types-cjs/frames/frame-client.d.cts +37 -7
  52. package/types-cjs/frames/frame-sink.d.cts +26 -3
  53. package/types-cjs/frames/frame-transport.d.cts +39 -7
  54. package/types-cjs/frames/serializer.d.cts +173 -6
  55. package/types-cjs/frames/server.d.cts +22 -0
  56. package/types-cjs/index.d.cts +2 -3
  57. package/types-cjs/response.d.cts +45 -0
  58. package/types-cjs/serializer.d.cts +173 -6
  59. package/types-cjs/server-functions/client.d.cts +1 -0
  60. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  61. package/types-cjs/server-functions/server.d.cts +98 -0
  62. package/types-cjs/server-functions/shared.d.cts +22 -0
  63. package/types-cjs/server-mock.d.cts +171 -59
  64. package/types-cjs/server.d.cts +188 -36
@@ -1,4 +1,4 @@
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 } from 'solid-js';
2
2
  import { insert } from '@solidjs/web';
3
3
  import { createPlugin, fromCrossJSON, Feature } from 'seroval';
4
4
  import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
@@ -94,9 +94,7 @@ function createFrameHost(options = {}) {
94
94
  if (store.version !== undefined && version < store.version) return false;
95
95
  if (store.version === undefined || version > store.version) {
96
96
  store.version = version;
97
- for (const key of Object.keys(store.records)) {
98
- if (key.startsWith("seg:") || key === ":error") delete store.records[key];
99
- }
97
+ clearStreamRecords(store.records);
100
98
  }
101
99
  Object.assign(store.records, records);
102
100
  return true;
@@ -174,6 +172,7 @@ class FrameImpl {
174
172
  #store = Object.create(null);
175
173
  #appliedRootValue;
176
174
  #hasContent = false;
175
+ #errorNotified = false;
177
176
  #revealed = new Set();
178
177
  #fallbackShown = new Set();
179
178
  #slots;
@@ -193,10 +192,12 @@ class FrameImpl {
193
192
  #claimTree = (node, direct) => {
194
193
  const handlers = claimHandlers();
195
194
  if (!handlers) return;
196
- const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
197
- const scope = this.#options.ownerScope;
198
- scope ? scope(run) : run();
195
+ this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
199
196
  };
197
+ #scoped(fn) {
198
+ const scope = this.#options.ownerScope;
199
+ return scope ? scope(fn) : fn();
200
+ }
200
201
  constructor(element, start, end, options = {}) {
201
202
  this.#element = element;
202
203
  this.#start = start;
@@ -257,15 +258,9 @@ class FrameImpl {
257
258
  return;
258
259
  } else if (v > this.#version) {
259
260
  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
- }
261
+ this.#resetStreamState();
267
262
  }
268
- for (const key of Object.keys(write.r)) {
263
+ for (const key in write.r) {
269
264
  const incoming = write.r[key];
270
265
  if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {
271
266
  const existing = this.#store[key];
@@ -277,6 +272,12 @@ class FrameImpl {
277
272
  }
278
273
  this.#flush();
279
274
  }
275
+ #resetStreamState(root) {
276
+ this.#revealed.clear();
277
+ this.#fallbackShown.clear();
278
+ this.#errorNotified = false;
279
+ clearStreamRecords(this.#store, root);
280
+ }
280
281
  #flush() {
281
282
  if (this.#disposed) return;
282
283
  const version = this.#version;
@@ -287,10 +288,14 @@ class FrameImpl {
287
288
  this.#appliedRootValue = root.value;
288
289
  this.#applied(version, reason);
289
290
  }
291
+ if (this.#store[":error"] && !this.#errorNotified) {
292
+ this.#errorNotified = true;
293
+ this.#applied(version, "error");
294
+ }
290
295
  let progressed = true;
291
296
  while (progressed) {
292
297
  progressed = false;
293
- for (const key of Object.keys(this.#store)) {
298
+ for (const key in this.#store) {
294
299
  const name = segmentName(key);
295
300
  if (name === null || this.#revealed.has(name)) continue;
296
301
  if (this.#segmentReady(name)) {
@@ -299,7 +304,7 @@ class FrameImpl {
299
304
  progressed = true;
300
305
  }
301
306
  }
302
- for (const key of Object.keys(this.#store)) {
307
+ for (const key in this.#store) {
303
308
  const m = /^seg:([^:]+):fallback$/.exec(key);
304
309
  if (!m) continue;
305
310
  const name = m[1];
@@ -311,7 +316,7 @@ class FrameImpl {
311
316
  }
312
317
  }
313
318
  }
314
- for (const key of Object.keys(this.#store)) {
319
+ for (const key in this.#store) {
315
320
  if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;
316
321
  this.#processedAssets.add(key);
317
322
  const record = this.#store[key];
@@ -343,6 +348,7 @@ class FrameImpl {
343
348
  const callback = this.#resolveSlot(propOf(occurrence));
344
349
  if (!callback) continue;
345
350
  const record = this.#resolveSlotRecord(occurrence);
351
+ if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
346
352
  const prev = this.#slotNodes.get(occurrence);
347
353
  const prevFirst = Array.isArray(prev) ? prev[0] : prev;
348
354
  const zombie = this.#mountedSlots.has(occurrence) && prevFirst && !prevFirst.parentNode;
@@ -398,15 +404,7 @@ class FrameImpl {
398
404
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
399
405
  let existing = [];
400
406
  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
- }
407
+ if (start) end = eachInRange(start, occurrence, n => existing.push(n));
410
408
  const ctx = {
411
409
  frame: this.#options.claimScope ?? this.#options.id,
412
410
  key: occurrence,
@@ -421,23 +419,16 @@ class FrameImpl {
421
419
  } : undefined
422
420
  };
423
421
  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);
422
+ const content = this.#scoped(() => callback(props, ctx));
426
423
  this.#slotArgs.set(occurrence, record);
427
424
  if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);
428
425
  if (content == null) return null;
429
426
  return Array.isArray(content) ? content : [content];
430
427
  }
431
428
  #replaceRange(key, start, nodes) {
432
- const end = slotEnd(key);
433
429
  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);
430
+ const end = eachInRange(start, key, n => parent.removeChild(n));
431
+ for (const node of nodes) parent.insertBefore(node, end);
441
432
  }
442
433
  #unmountSlot(key) {
443
434
  this.#mountedSlots.delete(key);
@@ -449,9 +440,7 @@ class FrameImpl {
449
440
  this.#runSlotCleanups(key);
450
441
  const regions = this.#slotRegions.get(key);
451
442
  if (regions) {
452
- for (const {
453
- frame
454
- } of regions.values()) frame?.dispose();
443
+ disposeRegions(regions);
455
444
  this.#slotRegions.delete(key);
456
445
  }
457
446
  }
@@ -461,15 +450,26 @@ class FrameImpl {
461
450
  this.#slotCleanups.delete(key);
462
451
  for (const fn of cleanups) fn();
463
452
  }
453
+ #refsUnresolved(args) {
454
+ const {
455
+ host,
456
+ id
457
+ } = this.#options;
458
+ if (host) for (const key in args) {
459
+ if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
460
+ }
461
+ return false;
462
+ }
463
+ #regionsFor(slotKey) {
464
+ let regions = this.#slotRegions.get(slotKey);
465
+ if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
466
+ return regions;
467
+ }
464
468
  #resolveArgs(slotKey, args) {
465
469
  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
- }
470
+ const regions = this.#regionsFor(slotKey);
471
471
  const props = {};
472
- for (const key of Object.keys(args)) {
472
+ for (const key in args) {
473
473
  const value = args[key];
474
474
  if (isDataRef(value)) {
475
475
  const resolved = host ? host.resolve(value, this.#options.id) : undefined;
@@ -499,17 +499,8 @@ class FrameImpl {
499
499
  }
500
500
  #discoverRegions(slotKey, start) {
501
501
  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
- }
502
+ const regions = this.#regionsFor(slotKey);
503
+ eachInRange(start, slotKey, n => collectRegionElements(n, regions));
513
504
  }
514
505
  #refArgsUnchanged(occurrence, record) {
515
506
  const old = this.#slotArgs.get(occurrence);
@@ -525,11 +516,11 @@ class FrameImpl {
525
516
  const va = a[key];
526
517
  const vb = b[key];
527
518
  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) {
519
+ if (isFrameRef(va) && isFrameRef(vb)) continue;
520
+ if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
531
521
  const host = this.#options.host;
532
522
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
523
+ if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
533
524
  try {
534
525
  if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;
535
526
  } catch (e) {
@@ -545,7 +536,7 @@ class FrameImpl {
545
536
  if (!args) return;
546
537
  const regions = this.#slotRegions.get(occurrence);
547
538
  if (!regions) return;
548
- for (const key of Object.keys(args)) {
539
+ for (const key in args) {
549
540
  const value = args[key];
550
541
  if (!isFrameRef(value)) continue;
551
542
  const entry = regions.get(key);
@@ -595,11 +586,8 @@ class FrameImpl {
595
586
  this.#element.setAttribute(FRAME_ID_ATTR, id);
596
587
  }
597
588
  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
- }
589
+ this.#appliedRootValue = undefined;
590
+ this.#resetStreamState(true);
603
591
  if (host) host.register(id, this);
604
592
  }
605
593
  rebase() {
@@ -619,11 +607,7 @@ class FrameImpl {
619
607
  }
620
608
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
621
609
  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
- }
610
+ for (const regions of this.#slotRegions.values()) disposeRegions(regions);
627
611
  this.#slotRegions.clear();
628
612
  this.#mountedSlots.clear();
629
613
  }
@@ -645,13 +629,7 @@ class FrameImpl {
645
629
  }
646
630
  }
647
631
  #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;
654
- }
632
+ removeUntil(this.#parent(), this.#firstContent(), this.#end);
655
633
  }
656
634
  #claimContent() {
657
635
  if (!claimHandlers()) return;
@@ -668,9 +646,7 @@ class FrameImpl {
668
646
  const assets = this.#store[`seg:${name}:assets`];
669
647
  if (assets && assets.styles) {
670
648
  let ready = true;
671
- for (const entry of assets.styles) {
672
- if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
673
- }
649
+ for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
674
650
  if (!ready) return false;
675
651
  }
676
652
  if (!this.#findPlaceholder(name)) return false;
@@ -684,12 +660,7 @@ class FrameImpl {
684
660
  const content = this.#store[`seg:${name}`];
685
661
  const closing = rangeClose(tpl, placeholderId(name));
686
662
  const parent = tpl.parentNode;
687
- let n = tpl.nextSibling;
688
- while (n && n !== closing) {
689
- const next = n.nextSibling;
690
- parent.removeChild(n);
691
- n = next;
692
- }
663
+ removeUntil(parent, tpl.nextSibling, closing);
693
664
  if (this.#options.reveal) {
694
665
  const fallbackFrag = tpl.content.cloneNode(true);
695
666
  this.#claimTree(fallbackFrag);
@@ -738,9 +709,7 @@ function createFrameElement(options) {
738
709
  const frame = new FrameImpl(el, null, null, options);
739
710
  return {
740
711
  element: el,
741
- get frame() {
742
- return frame;
743
- },
712
+ frame,
744
713
  dispose() {
745
714
  frame.dispose();
746
715
  el.remove();
@@ -760,15 +729,30 @@ function segmentName(key) {
760
729
  const m = /^seg:([^:]+)$/.exec(key);
761
730
  return m ? m[1] : null;
762
731
  }
732
+ function isAsyncLike(v) {
733
+ return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
734
+ }
763
735
  function propOf(occurrence) {
764
736
  const hash = occurrence.indexOf("#");
765
737
  return hash === -1 ? occurrence : occurrence.slice(0, hash);
766
738
  }
767
739
  function isDataRef(value) {
768
- return typeof value === "object" && value !== null && typeof value.$ref === "string";
740
+ return !!value && typeof value.$ref === "string";
769
741
  }
770
742
  function isFrameRef(value) {
771
- return typeof value === "object" && value !== null && typeof value.$frame === "string";
743
+ return !!value && typeof value.$frame === "string";
744
+ }
745
+ function disposeRegions(regions) {
746
+ for (const {
747
+ frame
748
+ } of regions.values()) frame?.dispose();
749
+ }
750
+ function removeUntil(parent, n, stop) {
751
+ while (n && n !== stop) {
752
+ const next = n.nextSibling;
753
+ parent.removeChild(n);
754
+ n = next;
755
+ }
772
756
  }
773
757
  function parseFragment(html) {
774
758
  const template = document.createElement("template");
@@ -776,9 +760,8 @@ function parseFragment(html) {
776
760
  return template.content;
777
761
  }
778
762
  function findHeadElement(selector, attr, value) {
779
- const nodes = document.head.querySelectorAll(selector);
780
- for (let i = 0; i < nodes.length; i++) {
781
- if (nodes[i].getAttribute(attr) === value) return nodes[i];
763
+ for (const node of document.head.querySelectorAll(selector)) {
764
+ if (node.getAttribute(attr) === value) return node;
782
765
  }
783
766
  return null;
784
767
  }
@@ -827,7 +810,7 @@ function applyInlineStyles(inlineStyles) {
827
810
  }
828
811
  }
829
812
  function isPlaceholderStart(node, id) {
830
- return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
813
+ return node.tagName === "TEMPLATE" && node.id === id;
831
814
  }
832
815
  function rangeClose(start, id) {
833
816
  let n = start.nextSibling;
@@ -883,6 +866,21 @@ function renameRegion(entry, childId) {
883
866
  entry.childId = childId;
884
867
  if (entry.frame) entry.frame.rebind(childId);else entry.element.setAttribute(FRAME_ID_ATTR, childId);
885
868
  }
869
+ function eachInRange(start, key, cb) {
870
+ const end = slotEnd(key);
871
+ let n = start.nextSibling;
872
+ while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
873
+ const next = n.nextSibling;
874
+ cb(n);
875
+ n = next;
876
+ }
877
+ return n;
878
+ }
879
+ function clearStreamRecords(records, root) {
880
+ for (const key in records) {
881
+ if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
882
+ }
883
+ }
886
884
  function argsEquivalent(a, b) {
887
885
  if (a === b) return true;
888
886
  if (!a || !b) return false;
@@ -893,9 +891,7 @@ function argsEquivalent(a, b) {
893
891
  const va = a[key];
894
892
  const vb = b[key];
895
893
  if (va === vb) continue;
896
- if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
897
- continue;
898
- }
894
+ if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
899
895
  return false;
900
896
  }
901
897
  return true;
@@ -1258,7 +1254,7 @@ function createServerComponentHandler({
1258
1254
  showing(address, functionId) {
1259
1255
  bindingFor(address, functionId);
1260
1256
  const comp = componentFor(functionId);
1261
- if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
1257
+ if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
1262
1258
  comp[COMPONENT_BINDING] = {
1263
1259
  component: comp,
1264
1260
  address
@@ -1350,6 +1346,9 @@ function createJSONDataTable(options) {
1350
1346
  };
1351
1347
  }
1352
1348
 
1349
+ function asyncArg(value) {
1350
+ return value;
1351
+ }
1353
1352
  let sharedHost;
1354
1353
  const tables = new Map();
1355
1354
  function tableFor(id) {
@@ -1426,8 +1425,26 @@ function claimRender(prefix, existing, render) {
1426
1425
  function liveSlotProps(initial, ctx) {
1427
1426
  const [args, setArgs] = createSignal(initial);
1428
1427
  ctx.onUpdate(next => setArgs(() => next));
1428
+ return slotArgsProxy(args);
1429
+ }
1430
+ function isAsyncValue(v) {
1431
+ return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
1432
+ }
1433
+ function slotArgsProxy(args) {
1434
+ const owner = getOwner();
1435
+ const asyncReads = new Map();
1429
1436
  return new Proxy({}, {
1430
- get: (_, key) => args()[key],
1437
+ get: (_, key) => {
1438
+ const v = args()[key];
1439
+ if (!isAsyncValue(v)) return v;
1440
+ let read = asyncReads.get(key);
1441
+ if (!read) {
1442
+ const make = () => createMemo(() => args()[key]);
1443
+ read = owner ? runWithOwner(owner, make) : make();
1444
+ asyncReads.set(key, read);
1445
+ }
1446
+ return read();
1447
+ },
1431
1448
  has: (_, key) => key in args(),
1432
1449
  ownKeys: () => Reflect.ownKeys(args()),
1433
1450
  getOwnPropertyDescriptor: (_, key) => key in args() ? {
@@ -1445,6 +1462,7 @@ function isReactiveContent(value) {
1445
1462
  }
1446
1463
  function slotsFor(props) {
1447
1464
  const bindings = new Map();
1465
+ const fillScopes = new Map();
1448
1466
  return new Proxy({}, {
1449
1467
  get(_, prop) {
1450
1468
  if (typeof prop !== "string" || !(prop in props)) return undefined;
@@ -1455,6 +1473,19 @@ function slotsFor(props) {
1455
1473
  bindings.delete(key);
1456
1474
  prev.dispose();
1457
1475
  }
1476
+ const prevFill = key !== undefined && fillScopes.get(key);
1477
+ if (prevFill) {
1478
+ fillScopes.delete(key);
1479
+ prevFill.dispose();
1480
+ }
1481
+ const fillOwner = streamInvoke ? createOwner() : null;
1482
+ if (fillOwner && key !== undefined && ctx) {
1483
+ fillScopes.set(key, fillOwner);
1484
+ ctx.onCleanup(() => {
1485
+ if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
1486
+ fillOwner.dispose();
1487
+ });
1488
+ }
1458
1489
  const settle = out => {
1459
1490
  const existing = ctx && ctx.existing || [];
1460
1491
  if (existing.length) {
@@ -1467,13 +1498,13 @@ function slotsFor(props) {
1467
1498
  const evaluate = () => {
1468
1499
  const v = props[prop];
1469
1500
  if (typeof v === "function" && ctx && ctx.invoked) {
1470
- return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
1501
+ return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
1471
1502
  }
1472
1503
  return v;
1473
1504
  };
1474
1505
  const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1475
1506
  const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1476
- const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1507
+ const value = fillOwner ? runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1477
1508
  if (!isReactiveContent(value)) {
1478
1509
  return settle(normalizeSlotContent(value));
1479
1510
  }
@@ -1498,29 +1529,61 @@ function slotsFor(props) {
1498
1529
  function revealSeam(owner) {
1499
1530
  return seam => runWithOwner(owner, () => insert(seam.before.parentNode, createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1500
1531
  }
1532
+ let streamInvoke = false;
1501
1533
  function boundaryScope(owner) {
1502
- return fn => getOwner() ? fn() : runWithOwner(owner, fn);
1503
- }
1504
- function followBinding(frame, binding) {
1505
- createRenderEffect(binding, address => frame.rebind(address));
1534
+ return fn => {
1535
+ if (getOwner()) return fn();
1536
+ streamInvoke = true;
1537
+ try {
1538
+ return runWithOwner(owner, fn);
1539
+ } finally {
1540
+ streamInvoke = false;
1541
+ }
1542
+ };
1506
1543
  }
1507
1544
  function boundaryComponent(host, fnId) {
1508
1545
  return (props, binding) => {
1509
1546
  const owner = getOwner();
1547
+ const id = binding ? binding() : fnId;
1548
+ let applied = !tables.has(id);
1549
+ let release;
1550
+ const arm = () => new Promise(r => release = r);
1551
+ const mountGate = applied ? undefined : arm();
1552
+ let setGate;
1510
1553
  const {
1511
1554
  element,
1512
1555
  frame,
1513
1556
  dispose
1514
1557
  } = createFrameElement({
1515
1558
  host,
1516
- id: binding ? binding() : fnId,
1559
+ id,
1517
1560
  slots: slotsFor(props),
1518
1561
  ownerScope: boundaryScope(owner),
1519
- reveal: revealSeam(owner)
1562
+ reveal: revealSeam(owner),
1563
+ onApply: () => {
1564
+ applied = true;
1565
+ if (release) {
1566
+ release();
1567
+ release = undefined;
1568
+ }
1569
+ setGate && setGate(undefined);
1570
+ }
1520
1571
  });
1521
- if (binding) followBinding(frame, binding);
1572
+ const [gatePromise, setGatePromise] = createSignal(applied ? undefined : mountGate);
1573
+ setGate = setGatePromise;
1574
+ if (binding) {
1575
+ createRenderEffect(binding, (address, prev) => {
1576
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1577
+ applied = false;
1578
+ setGatePromise(arm());
1579
+ }
1580
+ frame.rebind(address);
1581
+ });
1582
+ }
1522
1583
  onCleanup(dispose);
1523
- return element;
1584
+ if (applied && !binding) return element;
1585
+ const gate = createMemo(() => gatePromise());
1586
+ return createMemo(() => (gate(), element));
1524
1587
  };
1525
1588
  }
1526
1589
  const claimedBoundaries = new Set();
@@ -1588,6 +1651,17 @@ function adoptBoundary(host, id, el, props, binding) {
1588
1651
  claimedBoundaries.add(id);
1589
1652
  const address = binding ? binding() : documentAddress(id);
1590
1653
  const appliedRecords = new Set();
1654
+ const claimedFragments = new Set();
1655
+ const claimRegionFragments = root => {
1656
+ const fr = globalThis._$HY?.fr;
1657
+ if (!fr || !fr.claim) return;
1658
+ root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
1659
+ const fragId = tpl.id.slice(3);
1660
+ if (claimedFragments.has(fragId)) return;
1661
+ claimedFragments.add(fragId);
1662
+ fr.claim(fragId);
1663
+ });
1664
+ };
1591
1665
  const drainRecords = () => {
1592
1666
  const hy = globalThis._$HY;
1593
1667
  if (!hy || !hy.r) return;
@@ -1619,8 +1693,20 @@ function adoptBoundary(host, id, el, props, binding) {
1619
1693
  }
1620
1694
  }
1621
1695
  };
1696
+ claimRegionFragments(el);
1697
+ const fr = globalThis._$HY?.fr;
1698
+ const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
1699
+ if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
1700
+ drainRecords();
1701
+ }) : undefined;
1702
+ onCleanup(() => {
1703
+ unsubscribe && unsubscribe();
1704
+ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
1705
+ });
1622
1706
  drainRecords();
1623
1707
  const owner = getOwner();
1708
+ let release;
1709
+ let setGate;
1624
1710
  const frame = createFrame(el, {
1625
1711
  adopt: true,
1626
1712
  host,
@@ -1628,6 +1714,13 @@ function adoptBoundary(host, id, el, props, binding) {
1628
1714
  slots: slotsFor(props),
1629
1715
  ownerScope: boundaryScope(owner),
1630
1716
  reveal: revealSeam(owner),
1717
+ onApply: () => {
1718
+ if (release) {
1719
+ release();
1720
+ release = undefined;
1721
+ }
1722
+ setGate && setGate(undefined);
1723
+ },
1631
1724
  ...{
1632
1725
  recordsPending: () => {
1633
1726
  if (document.readyState === "loading") return true;
@@ -1638,7 +1731,19 @@ function adoptBoundary(host, id, el, props, binding) {
1638
1731
  claimScope: id
1639
1732
  }
1640
1733
  });
1641
- if (binding) followBinding(frame, binding);
1734
+ if (binding) {
1735
+ const arm = () => new Promise(r => release = r);
1736
+ const [gatePromise, setGatePromise] = createSignal(undefined);
1737
+ setGate = setGatePromise;
1738
+ const gate = createMemo(() => gatePromise());
1739
+ createRenderEffect(binding, (address, prev) => {
1740
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1741
+ setGatePromise(arm());
1742
+ }
1743
+ frame.rebind(address);
1744
+ });
1745
+ createRenderEffect(() => (gate(), undefined), () => {});
1746
+ }
1642
1747
  onCleanup(() => frame.dispose());
1643
1748
  return el;
1644
1749
  }
@@ -1679,4 +1784,4 @@ function installServerComponents(host = getFrameHost()) {
1679
1784
  });
1680
1785
  }
1681
1786
 
1682
- export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, createFrame, createFrameElement, createFrameHost, createJSONDataTable, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
1787
+ export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, asyncArg, createFrame, createFrameElement, createFrameHost, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };