@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;
@@ -687,12 +663,7 @@ class FrameImpl {
687
663
  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
664
  }
689
665
  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
- }
666
+ removeUntil(parent, tpl.nextSibling, closing);
696
667
  if (this.#options.reveal) {
697
668
  const fallbackFrag = tpl.content.cloneNode(true);
698
669
  this.#claimTree(fallbackFrag);
@@ -741,9 +712,7 @@ function createFrameElement(options) {
741
712
  const frame = new FrameImpl(el, null, null, options);
742
713
  return {
743
714
  element: el,
744
- get frame() {
745
- return frame;
746
- },
715
+ frame,
747
716
  dispose() {
748
717
  frame.dispose();
749
718
  el.remove();
@@ -763,15 +732,30 @@ function segmentName(key) {
763
732
  const m = /^seg:([^:]+)$/.exec(key);
764
733
  return m ? m[1] : null;
765
734
  }
735
+ function isAsyncLike(v) {
736
+ return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
737
+ }
766
738
  function propOf(occurrence) {
767
739
  const hash = occurrence.indexOf("#");
768
740
  return hash === -1 ? occurrence : occurrence.slice(0, hash);
769
741
  }
770
742
  function isDataRef(value) {
771
- return typeof value === "object" && value !== null && typeof value.$ref === "string";
743
+ return !!value && typeof value.$ref === "string";
772
744
  }
773
745
  function isFrameRef(value) {
774
- return typeof value === "object" && value !== null && typeof value.$frame === "string";
746
+ return !!value && typeof value.$frame === "string";
747
+ }
748
+ function disposeRegions(regions) {
749
+ for (const {
750
+ frame
751
+ } of regions.values()) frame?.dispose();
752
+ }
753
+ function removeUntil(parent, n, stop) {
754
+ while (n && n !== stop) {
755
+ const next = n.nextSibling;
756
+ parent.removeChild(n);
757
+ n = next;
758
+ }
775
759
  }
776
760
  function parseFragment(html) {
777
761
  const template = document.createElement("template");
@@ -779,9 +763,8 @@ function parseFragment(html) {
779
763
  return template.content;
780
764
  }
781
765
  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];
766
+ for (const node of document.head.querySelectorAll(selector)) {
767
+ if (node.getAttribute(attr) === value) return node;
785
768
  }
786
769
  return null;
787
770
  }
@@ -830,7 +813,7 @@ function applyInlineStyles(inlineStyles) {
830
813
  }
831
814
  }
832
815
  function isPlaceholderStart(node, id) {
833
- return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
816
+ return node.tagName === "TEMPLATE" && node.id === id;
834
817
  }
835
818
  function rangeClose(start, id) {
836
819
  let n = start.nextSibling;
@@ -887,6 +870,21 @@ function renameRegion(entry, childId) {
887
870
  entry.childId = childId;
888
871
  if (entry.frame) entry.frame.rebind(childId);else entry.element.setAttribute(FRAME_ID_ATTR, childId);
889
872
  }
873
+ function eachInRange(start, key, cb) {
874
+ const end = slotEnd(key);
875
+ let n = start.nextSibling;
876
+ while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
877
+ const next = n.nextSibling;
878
+ cb(n);
879
+ n = next;
880
+ }
881
+ return n;
882
+ }
883
+ function clearStreamRecords(records, root) {
884
+ for (const key in records) {
885
+ if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
886
+ }
887
+ }
890
888
  function argsEquivalent(a, b) {
891
889
  if (a === b) return true;
892
890
  if (!a || !b) return false;
@@ -897,9 +895,7 @@ function argsEquivalent(a, b) {
897
895
  const va = a[key];
898
896
  const vb = b[key];
899
897
  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
- }
898
+ if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
903
899
  return false;
904
900
  }
905
901
  return true;
@@ -1271,7 +1267,7 @@ function createServerComponentHandler({
1271
1267
  showing(address, functionId) {
1272
1268
  bindingFor(address, functionId);
1273
1269
  const comp = componentFor(functionId);
1274
- if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
1270
+ if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
1275
1271
  comp[COMPONENT_BINDING] = {
1276
1272
  component: comp,
1277
1273
  address
@@ -1363,6 +1359,9 @@ function createJSONDataTable(options) {
1363
1359
  };
1364
1360
  }
1365
1361
 
1362
+ function asyncArg(value) {
1363
+ return value;
1364
+ }
1366
1365
  let sharedHost;
1367
1366
  const tables = new Map();
1368
1367
  function tableFor(id) {
@@ -1439,8 +1438,26 @@ function claimRender(prefix, existing, render) {
1439
1438
  function liveSlotProps(initial, ctx) {
1440
1439
  const [args, setArgs] = createSignal(initial);
1441
1440
  ctx.onUpdate(next => setArgs(() => next));
1441
+ return slotArgsProxy(args);
1442
+ }
1443
+ function isAsyncValue(v) {
1444
+ return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
1445
+ }
1446
+ function slotArgsProxy(args) {
1447
+ const owner = getOwner();
1448
+ const asyncReads = new Map();
1442
1449
  return new Proxy({}, {
1443
- get: (_, key) => args()[key],
1450
+ get: (_, key) => {
1451
+ const v = args()[key];
1452
+ if (!isAsyncValue(v)) return v;
1453
+ let read = asyncReads.get(key);
1454
+ if (!read) {
1455
+ const make = () => createMemo(() => args()[key]);
1456
+ read = owner ? runWithOwner(owner, make) : make();
1457
+ asyncReads.set(key, read);
1458
+ }
1459
+ return read();
1460
+ },
1444
1461
  has: (_, key) => key in args(),
1445
1462
  ownKeys: () => Reflect.ownKeys(args()),
1446
1463
  getOwnPropertyDescriptor: (_, key) => key in args() ? {
@@ -1458,6 +1475,7 @@ function isReactiveContent(value) {
1458
1475
  }
1459
1476
  function slotsFor(props) {
1460
1477
  const bindings = new Map();
1478
+ const fillScopes = new Map();
1461
1479
  return new Proxy({}, {
1462
1480
  get(_, prop) {
1463
1481
  if (typeof prop !== "string" || !(prop in props)) return undefined;
@@ -1468,6 +1486,19 @@ function slotsFor(props) {
1468
1486
  bindings.delete(key);
1469
1487
  prev.dispose();
1470
1488
  }
1489
+ const prevFill = key !== undefined && fillScopes.get(key);
1490
+ if (prevFill) {
1491
+ fillScopes.delete(key);
1492
+ prevFill.dispose();
1493
+ }
1494
+ const fillOwner = streamInvoke ? createOwner() : null;
1495
+ if (fillOwner && key !== undefined && ctx) {
1496
+ fillScopes.set(key, fillOwner);
1497
+ ctx.onCleanup(() => {
1498
+ if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
1499
+ fillOwner.dispose();
1500
+ });
1501
+ }
1471
1502
  const settle = out => {
1472
1503
  const existing = ctx && ctx.existing || [];
1473
1504
  if (existing.length) {
@@ -1480,13 +1511,13 @@ function slotsFor(props) {
1480
1511
  const evaluate = () => {
1481
1512
  const v = props[prop];
1482
1513
  if (typeof v === "function" && ctx && ctx.invoked) {
1483
- return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
1514
+ return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
1484
1515
  }
1485
1516
  return v;
1486
1517
  };
1487
1518
  const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1488
1519
  const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1489
- const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1520
+ const value = fillOwner ? runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1490
1521
  if (!isReactiveContent(value)) {
1491
1522
  return settle(normalizeSlotContent(value));
1492
1523
  }
@@ -1511,29 +1542,61 @@ function slotsFor(props) {
1511
1542
  function revealSeam(owner) {
1512
1543
  return seam => runWithOwner(owner, () => insert(seam.before.parentNode, createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1513
1544
  }
1545
+ let streamInvoke = false;
1514
1546
  function boundaryScope(owner) {
1515
- return fn => getOwner() ? fn() : runWithOwner(owner, fn);
1516
- }
1517
- function followBinding(frame, binding) {
1518
- createRenderEffect(binding, address => frame.rebind(address));
1547
+ return fn => {
1548
+ if (getOwner()) return fn();
1549
+ streamInvoke = true;
1550
+ try {
1551
+ return runWithOwner(owner, fn);
1552
+ } finally {
1553
+ streamInvoke = false;
1554
+ }
1555
+ };
1519
1556
  }
1520
1557
  function boundaryComponent(host, fnId) {
1521
1558
  return (props, binding) => {
1522
1559
  const owner = getOwner();
1560
+ const id = binding ? binding() : fnId;
1561
+ let applied = !tables.has(id);
1562
+ let release;
1563
+ const arm = () => new Promise(r => release = r);
1564
+ const mountGate = applied ? undefined : arm();
1565
+ let setGate;
1523
1566
  const {
1524
1567
  element,
1525
1568
  frame,
1526
1569
  dispose
1527
1570
  } = createFrameElement({
1528
1571
  host,
1529
- id: binding ? binding() : fnId,
1572
+ id,
1530
1573
  slots: slotsFor(props),
1531
1574
  ownerScope: boundaryScope(owner),
1532
- reveal: revealSeam(owner)
1575
+ reveal: revealSeam(owner),
1576
+ onApply: () => {
1577
+ applied = true;
1578
+ if (release) {
1579
+ release();
1580
+ release = undefined;
1581
+ }
1582
+ setGate && setGate(undefined);
1583
+ }
1533
1584
  });
1534
- if (binding) followBinding(frame, binding);
1585
+ const [gatePromise, setGatePromise] = createSignal(applied ? undefined : mountGate);
1586
+ setGate = setGatePromise;
1587
+ if (binding) {
1588
+ createRenderEffect(binding, (address, prev) => {
1589
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1590
+ applied = false;
1591
+ setGatePromise(arm());
1592
+ }
1593
+ frame.rebind(address);
1594
+ });
1595
+ }
1535
1596
  onCleanup(dispose);
1536
- return element;
1597
+ if (applied && !binding) return element;
1598
+ const gate = createMemo(() => gatePromise());
1599
+ return createMemo(() => (gate(), element));
1537
1600
  };
1538
1601
  }
1539
1602
  const claimedBoundaries = new Set();
@@ -1601,6 +1664,17 @@ function adoptBoundary(host, id, el, props, binding) {
1601
1664
  claimedBoundaries.add(id);
1602
1665
  const address = binding ? binding() : documentAddress(id);
1603
1666
  const appliedRecords = new Set();
1667
+ const claimedFragments = new Set();
1668
+ const claimRegionFragments = root => {
1669
+ const fr = globalThis._$HY?.fr;
1670
+ if (!fr || !fr.claim) return;
1671
+ root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
1672
+ const fragId = tpl.id.slice(3);
1673
+ if (claimedFragments.has(fragId)) return;
1674
+ claimedFragments.add(fragId);
1675
+ fr.claim(fragId);
1676
+ });
1677
+ };
1604
1678
  const drainRecords = () => {
1605
1679
  const hy = globalThis._$HY;
1606
1680
  if (!hy || !hy.r) return;
@@ -1632,8 +1706,20 @@ function adoptBoundary(host, id, el, props, binding) {
1632
1706
  }
1633
1707
  }
1634
1708
  };
1709
+ claimRegionFragments(el);
1710
+ const fr = globalThis._$HY?.fr;
1711
+ const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
1712
+ if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
1713
+ drainRecords();
1714
+ }) : undefined;
1715
+ onCleanup(() => {
1716
+ unsubscribe && unsubscribe();
1717
+ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
1718
+ });
1635
1719
  drainRecords();
1636
1720
  const owner = getOwner();
1721
+ let release;
1722
+ let setGate;
1637
1723
  const frame = createFrame(el, {
1638
1724
  adopt: true,
1639
1725
  host,
@@ -1641,6 +1727,13 @@ function adoptBoundary(host, id, el, props, binding) {
1641
1727
  slots: slotsFor(props),
1642
1728
  ownerScope: boundaryScope(owner),
1643
1729
  reveal: revealSeam(owner),
1730
+ onApply: () => {
1731
+ if (release) {
1732
+ release();
1733
+ release = undefined;
1734
+ }
1735
+ setGate && setGate(undefined);
1736
+ },
1644
1737
  ...{
1645
1738
  recordsPending: () => {
1646
1739
  if (document.readyState === "loading") return true;
@@ -1651,7 +1744,19 @@ function adoptBoundary(host, id, el, props, binding) {
1651
1744
  claimScope: id
1652
1745
  }
1653
1746
  });
1654
- if (binding) followBinding(frame, binding);
1747
+ if (binding) {
1748
+ const arm = () => new Promise(r => release = r);
1749
+ const [gatePromise, setGatePromise] = createSignal(undefined);
1750
+ setGate = setGatePromise;
1751
+ const gate = createMemo(() => gatePromise());
1752
+ createRenderEffect(binding, (address, prev) => {
1753
+ if (prev !== undefined && address !== prev && tables.has(address)) {
1754
+ setGatePromise(arm());
1755
+ }
1756
+ frame.rebind(address);
1757
+ });
1758
+ createRenderEffect(() => (gate(), undefined), () => {});
1759
+ }
1655
1760
  onCleanup(() => frame.dispose());
1656
1761
  return el;
1657
1762
  }
@@ -1692,4 +1797,4 @@ function installServerComponents(host = getFrameHost()) {
1692
1797
  });
1693
1798
  }
1694
1799
 
1695
- export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, createFrame, createFrameElement, createFrameHost, createJSONDataTable, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
1800
+ export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, asyncArg, createFrame, createFrameElement, createFrameHost, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };