@solidjs/web 2.0.0-beta.30 → 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 (66) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +284 -82
  3. package/dist/dev.js +271 -79
  4. package/dist/server.cjs +555 -135
  5. package/dist/server.js +544 -132
  6. package/dist/web.cjs +281 -79
  7. package/dist/web.js +268 -76
  8. package/frames/dist/client.cjs +413 -360
  9. package/frames/dist/client.dev.cjs +413 -360
  10. package/frames/dist/client.dev.js +414 -361
  11. package/frames/dist/client.js +414 -361
  12. package/frames/dist/server.cjs +604 -199
  13. package/frames/dist/server.js +605 -201
  14. package/package.json +56 -6
  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 +134 -11
  32. package/types/core.d.ts +3 -1
  33. package/types/frames/client.d.ts +24 -8
  34. package/types/frames/frame-client.d.ts +49 -7
  35. package/types/frames/frame-sink.d.ts +32 -6
  36. package/types/frames/frame-transport.d.ts +88 -62
  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/jsx.d.ts +3 -3
  41. package/types/response.d.ts +45 -0
  42. package/types/serializer.d.ts +173 -6
  43. package/types/server-functions/client.d.ts +1 -0
  44. package/types/server-functions/rich-args.d.ts +10 -0
  45. package/types/server-functions/server.d.ts +98 -0
  46. package/types/server-functions/shared.d.ts +22 -0
  47. package/types/server-mock.d.ts +171 -59
  48. package/types/server.d.ts +196 -42
  49. package/types-cjs/client.d.cts +134 -11
  50. package/types-cjs/core.d.cts +3 -1
  51. package/types-cjs/frames/client.d.cts +24 -8
  52. package/types-cjs/frames/frame-client.d.cts +49 -7
  53. package/types-cjs/frames/frame-sink.d.cts +32 -6
  54. package/types-cjs/frames/frame-transport.d.cts +88 -62
  55. package/types-cjs/frames/serializer.d.cts +173 -6
  56. package/types-cjs/frames/server.d.cts +22 -0
  57. package/types-cjs/index.d.cts +2 -3
  58. package/types-cjs/jsx.d.cts +3 -3
  59. package/types-cjs/response.d.cts +45 -0
  60. package/types-cjs/serializer.d.cts +173 -6
  61. package/types-cjs/server-functions/client.d.cts +1 -0
  62. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  63. package/types-cjs/server-functions/server.d.cts +98 -0
  64. package/types-cjs/server-functions/shared.d.cts +22 -0
  65. package/types-cjs/server-mock.d.cts +171 -59
  66. package/types-cjs/server.d.cts +196 -42
@@ -1,4 +1,4 @@
1
- import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, 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';
@@ -81,98 +81,73 @@ function chunkToRecords(chunk) {
81
81
  }
82
82
  function createFrameHost(options = {}) {
83
83
  const frames = new Map();
84
- const pending = new Map();
85
- const retained = new Map();
86
- const deliver = (frame, chunk) => {
87
- if (chunk.type === "data") {
88
- options.applyData && options.applyData(chunk);
89
- return;
90
- }
91
- frame.apply({
92
- version: chunk.version,
93
- r: chunkToRecords(chunk)
84
+ const stores = new Map();
85
+ const storeFor = id => {
86
+ let store = stores.get(id);
87
+ if (!store) stores.set(id, store = {
88
+ version: undefined,
89
+ records: {}
94
90
  });
91
+ return store;
92
+ };
93
+ const write = (store, version, records) => {
94
+ if (store.version !== undefined && version < store.version) return false;
95
+ if (store.version === undefined || version > store.version) {
96
+ store.version = version;
97
+ clearStreamRecords(store.records);
98
+ }
99
+ Object.assign(store.records, records);
100
+ return true;
95
101
  };
96
102
  return {
97
103
  register(id, frame) {
98
104
  let set = frames.get(id);
99
105
  if (!set) frames.set(id, set = new Set());
100
- const sibling = set.size ? set.values().next().value : undefined;
101
106
  set.add(frame);
102
- if (sibling) {
107
+ const store = stores.get(id);
108
+ if (store && store.version !== undefined) {
103
109
  frame.apply({
104
- version: sibling.version ?? 0,
105
- r: sibling.store
106
- });
107
- if (sibling.version === undefined) frame.rebase && frame.rebase();
108
- return;
109
- }
110
- const kept = retained.get(id);
111
- if (kept) {
112
- retained.delete(id);
113
- frame.apply({
114
- version: kept.version,
115
- r: kept.records
110
+ version: store.version,
111
+ r: store.records
116
112
  });
117
113
  frame.rebase && frame.rebase();
118
114
  }
119
- const buffered = pending.get(id);
120
- if (buffered) {
121
- pending.delete(id);
122
- const records = {};
123
- let version;
124
- for (const chunk of buffered.chunks) {
125
- if (chunk.type === "data") {
126
- options.applyData && options.applyData(chunk);
127
- continue;
128
- }
129
- version = chunk.version;
130
- Object.assign(records, chunkToRecords(chunk));
131
- }
132
- if (version !== undefined) frame.apply({
133
- version,
134
- r: records
135
- });
136
- }
137
115
  },
138
116
  unregister(id, frame) {
139
117
  const set = frames.get(id);
140
118
  if (set && frame) set.delete(frame);
141
119
  if (!set || !frame || !set.size) {
142
- if (frame) {
143
- const kept = frame.snapshot && frame.snapshot();
144
- if (kept) retained.set(id, kept);
145
- } else {
146
- retained.delete(id);
147
- }
148
120
  frames.delete(id);
149
- pending.delete(id);
121
+ if (frame && frame.contentHTML) {
122
+ const store = storeFor(id);
123
+ if (!store.records[""]) {
124
+ const html = frame.contentHTML();
125
+ if (html != null) {
126
+ store.records[""] = {
127
+ kind: "html",
128
+ value: html
129
+ };
130
+ if (store.version === undefined) store.version = 0;
131
+ }
132
+ }
133
+ }
150
134
  }
135
+ if (!frame) stores.delete(id);
151
136
  },
152
137
  apply(chunk) {
153
138
  if (chunk.type === "data") {
154
139
  options.applyData && options.applyData(chunk);
155
140
  return;
156
141
  }
142
+ const records = chunkToRecords(chunk);
143
+ if (!write(storeFor(chunk.id), chunk.version, records)) return;
157
144
  const set = frames.get(chunk.id);
158
- if (set && set.size) {
159
- for (const frame of set) deliver(frame, chunk);
160
- return;
161
- }
162
- const buffered = pending.get(chunk.id);
163
- if (!buffered) {
164
- pending.set(chunk.id, {
145
+ if (set) {
146
+ for (const frame of set) frame.apply({
165
147
  version: chunk.version,
166
- chunks: [chunk]
148
+ r: records
167
149
  });
168
- return;
169
- }
170
- if (chunk.version < buffered.version) return;
171
- if (chunk.version > buffered.version) {
172
- buffered.version = chunk.version;
173
- buffered.chunks.length = 0;
174
150
  }
175
- buffered.chunks.push(chunk);
176
151
  },
177
152
  get(id) {
178
153
  const set = frames.get(id);
@@ -197,6 +172,7 @@ class FrameImpl {
197
172
  #store = Object.create(null);
198
173
  #appliedRootValue;
199
174
  #hasContent = false;
175
+ #errorNotified = false;
200
176
  #revealed = new Set();
201
177
  #fallbackShown = new Set();
202
178
  #slots;
@@ -208,6 +184,7 @@ class FrameImpl {
208
184
  #slotResolvedRefs = new Map();
209
185
  #slotNodes = new Map();
210
186
  #processedAssets = new Set();
187
+ #recordRefresh = null;
211
188
  #disposed = false;
212
189
  #styleFlush = () => {
213
190
  if (!this.#disposed) this.#flush();
@@ -215,10 +192,12 @@ class FrameImpl {
215
192
  #claimTree = (node, direct) => {
216
193
  const handlers = claimHandlers();
217
194
  if (!handlers) return;
218
- const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
219
- const scope = this.#options.ownerScope;
220
- scope ? scope(run) : run();
195
+ this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
221
196
  };
197
+ #scoped(fn) {
198
+ const scope = this.#options.ownerScope;
199
+ return scope ? scope(fn) : fn();
200
+ }
222
201
  constructor(element, start, end, options = {}) {
223
202
  this.#element = element;
224
203
  this.#start = start;
@@ -279,15 +258,9 @@ class FrameImpl {
279
258
  return;
280
259
  } else if (v > this.#version) {
281
260
  this.#version = v;
282
- this.#revealed.clear();
283
- this.#fallbackShown.clear();
284
- for (const key of Object.keys(this.#store)) {
285
- if (key.startsWith("seg:") || key === ":error") {
286
- delete this.#store[key];
287
- }
288
- }
261
+ this.#resetStreamState();
289
262
  }
290
- for (const key of Object.keys(write.r)) {
263
+ for (const key in write.r) {
291
264
  const incoming = write.r[key];
292
265
  if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {
293
266
  const existing = this.#store[key];
@@ -299,6 +272,12 @@ class FrameImpl {
299
272
  }
300
273
  this.#flush();
301
274
  }
275
+ #resetStreamState(root) {
276
+ this.#revealed.clear();
277
+ this.#fallbackShown.clear();
278
+ this.#errorNotified = false;
279
+ clearStreamRecords(this.#store, root);
280
+ }
302
281
  #flush() {
303
282
  if (this.#disposed) return;
304
283
  const version = this.#version;
@@ -309,10 +288,14 @@ class FrameImpl {
309
288
  this.#appliedRootValue = root.value;
310
289
  this.#applied(version, reason);
311
290
  }
291
+ if (this.#store[":error"] && !this.#errorNotified) {
292
+ this.#errorNotified = true;
293
+ this.#applied(version, "error");
294
+ }
312
295
  let progressed = true;
313
296
  while (progressed) {
314
297
  progressed = false;
315
- for (const key of Object.keys(this.#store)) {
298
+ for (const key in this.#store) {
316
299
  const name = segmentName(key);
317
300
  if (name === null || this.#revealed.has(name)) continue;
318
301
  if (this.#segmentReady(name)) {
@@ -321,7 +304,7 @@ class FrameImpl {
321
304
  progressed = true;
322
305
  }
323
306
  }
324
- for (const key of Object.keys(this.#store)) {
307
+ for (const key in this.#store) {
325
308
  const m = /^seg:([^:]+):fallback$/.exec(key);
326
309
  if (!m) continue;
327
310
  const name = m[1];
@@ -333,7 +316,7 @@ class FrameImpl {
333
316
  }
334
317
  }
335
318
  }
336
- for (const key of Object.keys(this.#store)) {
319
+ for (const key in this.#store) {
337
320
  if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;
338
321
  this.#processedAssets.add(key);
339
322
  const record = this.#store[key];
@@ -353,7 +336,9 @@ class FrameImpl {
353
336
  }
354
337
  #removeSlotRecord(occurrence) {
355
338
  const key = `slot:${occurrence}`;
356
- if (key in this.#store) delete this.#store[key];else this.#options.removeSlotRecord?.(occurrence);
339
+ if (key in this.#store) {
340
+ if (!this.#mountedSlots.has(occurrence)) delete this.#store[key];
341
+ } else this.#options.removeSlotRecord?.(occurrence);
357
342
  }
358
343
  #syncSlots(root) {
359
344
  if (!this.#slots && !this.#options.resolveSlot) return;
@@ -363,6 +348,7 @@ class FrameImpl {
363
348
  const callback = this.#resolveSlot(propOf(occurrence));
364
349
  if (!callback) continue;
365
350
  const record = this.#resolveSlotRecord(occurrence);
351
+ if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
366
352
  const prev = this.#slotNodes.get(occurrence);
367
353
  const prevFirst = Array.isArray(prev) ? prev[0] : prev;
368
354
  const zombie = this.#mountedSlots.has(occurrence) && prevFirst && !prevFirst.parentNode;
@@ -371,6 +357,15 @@ class FrameImpl {
371
357
  this.#runSlotCleanups(occurrence);
372
358
  }
373
359
  if (!this.#mountedSlots.has(occurrence)) {
360
+ if (record === undefined && !root && this.#options.adopt && this.#options.recordsPending?.()) {
361
+ this.#recordRefresh ??= setTimeout(() => {
362
+ this.#recordRefresh = null;
363
+ if (this.#disposed) return;
364
+ this.#options.drainRecords?.();
365
+ this.#syncSlots();
366
+ });
367
+ continue;
368
+ }
374
369
  if (this.#options.adopt) this.#discoverRegions(occurrence, start);
375
370
  const nodes = this.#invokeSlot(occurrence, callback, record, start, this.#options.adopt);
376
371
  if (nodes) this.#replaceRange(occurrence, start, nodes);
@@ -387,12 +382,6 @@ class FrameImpl {
387
382
  const update = this.#slotUpdaters.get(occurrence);
388
383
  if (update) {
389
384
  const props = this.#resolveArgs(occurrence, record.args);
390
- const regions = this.#slotRegions.get(occurrence);
391
- if (regions) {
392
- for (const [argKey, entry] of regions) {
393
- if (!(argKey in props)) props[argKey] = entry.element;
394
- }
395
- }
396
385
  this.#slotArgs.set(occurrence, record);
397
386
  update(props);
398
387
  this.#bindRegions(occurrence);
@@ -415,15 +404,7 @@ class FrameImpl {
415
404
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
416
405
  let existing = [];
417
406
  let end = null;
418
- if (start) {
419
- const endData = slotEnd(occurrence);
420
- let n = start.nextSibling;
421
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
422
- existing.push(n);
423
- n = n.nextSibling;
424
- }
425
- end = n;
426
- }
407
+ if (start) end = eachInRange(start, occurrence, n => existing.push(n));
427
408
  const ctx = {
428
409
  frame: this.#options.claimScope ?? this.#options.id,
429
410
  key: occurrence,
@@ -438,29 +419,16 @@ class FrameImpl {
438
419
  } : undefined
439
420
  };
440
421
  const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
441
- const regions = this.#slotRegions.get(occurrence);
442
- if (regions) {
443
- for (const [argKey, entry] of regions) {
444
- if (!(argKey in props)) props[argKey] = entry.element;
445
- }
446
- }
447
- const scope = this.#options.ownerScope;
448
- const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
422
+ const content = this.#scoped(() => callback(props, ctx));
449
423
  this.#slotArgs.set(occurrence, record);
450
424
  if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);
451
425
  if (content == null) return null;
452
426
  return Array.isArray(content) ? content : [content];
453
427
  }
454
428
  #replaceRange(key, start, nodes) {
455
- const end = slotEnd(key);
456
429
  const parent = start.parentNode;
457
- let n = start.nextSibling;
458
- while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
459
- const next = n.nextSibling;
460
- parent.removeChild(n);
461
- n = next;
462
- }
463
- 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);
464
432
  }
465
433
  #unmountSlot(key) {
466
434
  this.#mountedSlots.delete(key);
@@ -472,9 +440,7 @@ class FrameImpl {
472
440
  this.#runSlotCleanups(key);
473
441
  const regions = this.#slotRegions.get(key);
474
442
  if (regions) {
475
- for (const {
476
- frame
477
- } of regions.values()) frame?.dispose();
443
+ disposeRegions(regions);
478
444
  this.#slotRegions.delete(key);
479
445
  }
480
446
  }
@@ -484,15 +450,26 @@ class FrameImpl {
484
450
  this.#slotCleanups.delete(key);
485
451
  for (const fn of cleanups) fn();
486
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
+ }
487
468
  #resolveArgs(slotKey, args) {
488
469
  const host = this.#options.host;
489
- let regions = this.#slotRegions.get(slotKey);
490
- if (!regions) {
491
- regions = new Map();
492
- this.#slotRegions.set(slotKey, regions);
493
- }
470
+ const regions = this.#regionsFor(slotKey);
494
471
  const props = {};
495
- for (const key of Object.keys(args)) {
472
+ for (const key in args) {
496
473
  const value = args[key];
497
474
  if (isDataRef(value)) {
498
475
  const resolved = host ? host.resolve(value, this.#options.id) : undefined;
@@ -522,18 +499,8 @@ class FrameImpl {
522
499
  }
523
500
  #discoverRegions(slotKey, start) {
524
501
  if (!start) return;
525
- let regions = this.#slotRegions.get(slotKey);
526
- if (!regions) {
527
- regions = new Map();
528
- this.#slotRegions.set(slotKey, regions);
529
- }
530
- const endData = slotEnd(slotKey);
531
- const prefix = `${this.#options.id}.`;
532
- let n = start.nextSibling;
533
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
534
- collectRegionElements(n, regions, prefix);
535
- n = n.nextSibling;
536
- }
502
+ const regions = this.#regionsFor(slotKey);
503
+ eachInRange(start, slotKey, n => collectRegionElements(n, regions));
537
504
  }
538
505
  #refArgsUnchanged(occurrence, record) {
539
506
  const old = this.#slotArgs.get(occurrence);
@@ -543,26 +510,17 @@ class FrameImpl {
543
510
  const b = record.args || {};
544
511
  const ka = Object.keys(a);
545
512
  const kb = Object.keys(b);
546
- if (kb.length < ka.length) return false;
547
- if (kb.length !== ka.length) {
548
- const regions = this.#slotRegions.get(occurrence);
549
- for (const key of kb) {
550
- if (key in a) continue;
551
- const vb = b[key];
552
- if (!vb || typeof vb !== "object" || typeof vb.$frame !== "string") return false;
553
- if (!regions || !regions.has(key)) return false;
554
- }
555
- }
513
+ if (kb.length !== ka.length) return false;
556
514
  const cache = this.#slotResolvedRefs.get(occurrence);
557
515
  for (const key of ka) {
558
516
  const va = a[key];
559
517
  const vb = b[key];
560
518
  if (va === vb) continue;
561
- if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
562
- if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
563
- 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) {
564
521
  const host = this.#options.host;
565
522
  const next = host ? host.resolve(vb, this.#options.id) : undefined;
523
+ if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
566
524
  try {
567
525
  if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;
568
526
  } catch (e) {
@@ -578,7 +536,7 @@ class FrameImpl {
578
536
  if (!args) return;
579
537
  const regions = this.#slotRegions.get(occurrence);
580
538
  if (!regions) return;
581
- for (const key of Object.keys(args)) {
539
+ for (const key in args) {
582
540
  const value = args[key];
583
541
  if (!isFrameRef(value)) continue;
584
542
  const entry = regions.get(key);
@@ -609,22 +567,9 @@ class FrameImpl {
609
567
  #findPlaceholder(name) {
610
568
  return findPlaceholder(this.#firstContent(), this.#end, placeholderId(name));
611
569
  }
612
- snapshot() {
613
- if (this.#disposed) return null;
614
- const records = {
615
- ...this.#store
616
- };
617
- if (!records[""] && this.#element && this.#hasContent) {
618
- records[""] = {
619
- kind: "html",
620
- value: this.#element.innerHTML
621
- };
622
- }
623
- if (!records[""] && this.#version === undefined) return null;
624
- return {
625
- version: this.#version ?? 0,
626
- records
627
- };
570
+ contentHTML() {
571
+ if (this.#disposed || !this.#element || !this.#hasContent) return null;
572
+ return this.#element.innerHTML;
628
573
  }
629
574
  rebind(id) {
630
575
  if (this.#disposed || id === this.#options.id) return;
@@ -641,11 +586,8 @@ class FrameImpl {
641
586
  this.#element.setAttribute(FRAME_ID_ATTR, id);
642
587
  }
643
588
  this.#version = undefined;
644
- this.#revealed.clear();
645
- this.#fallbackShown.clear();
646
- for (const key of Object.keys(this.#store)) {
647
- if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
648
- }
589
+ this.#appliedRootValue = undefined;
590
+ this.#resetStreamState(true);
649
591
  if (host) host.register(id, this);
650
592
  }
651
593
  rebase() {
@@ -659,13 +601,13 @@ class FrameImpl {
659
601
  } = this.#options;
660
602
  if (host && id !== undefined) host.unregister(id, this);
661
603
  this.#disposed = true;
604
+ if (this.#recordRefresh) {
605
+ clearTimeout(this.#recordRefresh);
606
+ this.#recordRefresh = null;
607
+ }
662
608
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
663
609
  for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
664
- for (const regions of this.#slotRegions.values()) {
665
- for (const {
666
- frame
667
- } of regions.values()) frame?.dispose();
668
- }
610
+ for (const regions of this.#slotRegions.values()) disposeRegions(regions);
669
611
  this.#slotRegions.clear();
670
612
  this.#mountedSlots.clear();
671
613
  }
@@ -681,18 +623,13 @@ class FrameImpl {
681
623
  const claim = claimHandlers() ? this.#claimTree : null;
682
624
  const ranges = new Map();
683
625
  this.#collectSlots(ranges);
684
- reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges);
685
- if (ranges.size) restoreDisplacedRanges(this.#firstContent(), this.#end, ranges);
626
+ const grafts = [];
627
+ reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges, grafts);
628
+ if (ranges.size) for (const root of grafts) flushGrafts(root, ranges);
686
629
  }
687
630
  }
688
631
  #clearContent() {
689
- const parent = this.#parent();
690
- let n = this.#firstContent();
691
- while (n && n !== this.#end) {
692
- const next = n.nextSibling;
693
- parent.removeChild(n);
694
- n = next;
695
- }
632
+ removeUntil(this.#parent(), this.#firstContent(), this.#end);
696
633
  }
697
634
  #claimContent() {
698
635
  if (!claimHandlers()) return;
@@ -709,9 +646,7 @@ class FrameImpl {
709
646
  const assets = this.#store[`seg:${name}:assets`];
710
647
  if (assets && assets.styles) {
711
648
  let ready = true;
712
- for (const entry of assets.styles) {
713
- if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
714
- }
649
+ for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
715
650
  if (!ready) return false;
716
651
  }
717
652
  if (!this.#findPlaceholder(name)) return false;
@@ -728,12 +663,7 @@ class FrameImpl {
728
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);
729
664
  }
730
665
  const parent = tpl.parentNode;
731
- let n = tpl.nextSibling;
732
- while (n && n !== closing) {
733
- const next = n.nextSibling;
734
- parent.removeChild(n);
735
- n = next;
736
- }
666
+ removeUntil(parent, tpl.nextSibling, closing);
737
667
  if (this.#options.reveal) {
738
668
  const fallbackFrag = tpl.content.cloneNode(true);
739
669
  this.#claimTree(fallbackFrag);
@@ -782,9 +712,7 @@ function createFrameElement(options) {
782
712
  const frame = new FrameImpl(el, null, null, options);
783
713
  return {
784
714
  element: el,
785
- get frame() {
786
- return frame;
787
- },
715
+ frame,
788
716
  dispose() {
789
717
  frame.dispose();
790
718
  el.remove();
@@ -804,15 +732,30 @@ function segmentName(key) {
804
732
  const m = /^seg:([^:]+)$/.exec(key);
805
733
  return m ? m[1] : null;
806
734
  }
735
+ function isAsyncLike(v) {
736
+ return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
737
+ }
807
738
  function propOf(occurrence) {
808
739
  const hash = occurrence.indexOf("#");
809
740
  return hash === -1 ? occurrence : occurrence.slice(0, hash);
810
741
  }
811
742
  function isDataRef(value) {
812
- return typeof value === "object" && value !== null && typeof value.$ref === "string";
743
+ return !!value && typeof value.$ref === "string";
813
744
  }
814
745
  function isFrameRef(value) {
815
- 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
+ }
816
759
  }
817
760
  function parseFragment(html) {
818
761
  const template = document.createElement("template");
@@ -820,9 +763,8 @@ function parseFragment(html) {
820
763
  return template.content;
821
764
  }
822
765
  function findHeadElement(selector, attr, value) {
823
- const nodes = document.head.querySelectorAll(selector);
824
- for (let i = 0; i < nodes.length; i++) {
825
- 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;
826
768
  }
827
769
  return null;
828
770
  }
@@ -871,7 +813,7 @@ function applyInlineStyles(inlineStyles) {
871
813
  }
872
814
  }
873
815
  function isPlaceholderStart(node, id) {
874
- return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
816
+ return node.tagName === "TEMPLATE" && node.id === id;
875
817
  }
876
818
  function rangeClose(start, id) {
877
819
  let n = start.nextSibling;
@@ -905,11 +847,11 @@ function collectSlots(n, end, out) {
905
847
  n = n.nextSibling;
906
848
  }
907
849
  }
908
- function collectRegionElements(node, regions, prefix) {
850
+ function collectRegionElements(node, regions) {
909
851
  if (node.nodeType !== ELEMENT_NODE) return;
910
852
  if (isFrameElement(node)) {
911
853
  const childId = node.getAttribute(FRAME_ID_ATTR);
912
- if (childId && childId.startsWith(prefix)) {
854
+ if (childId && childId.includes(".")) {
913
855
  const argKey = childId.slice(childId.lastIndexOf(".") + 1);
914
856
  if (!regions.has(argKey)) {
915
857
  regions.set(argKey, {
@@ -922,12 +864,27 @@ function collectRegionElements(node, regions, prefix) {
922
864
  }
923
865
  return;
924
866
  }
925
- for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions, prefix);
867
+ for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions);
926
868
  }
927
869
  function renameRegion(entry, childId) {
928
870
  entry.childId = childId;
929
871
  if (entry.frame) entry.frame.rebind(childId);else entry.element.setAttribute(FRAME_ID_ATTR, childId);
930
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
+ }
931
888
  function argsEquivalent(a, b) {
932
889
  if (a === b) return true;
933
890
  if (!a || !b) return false;
@@ -938,9 +895,7 @@ function argsEquivalent(a, b) {
938
895
  const va = a[key];
939
896
  const vb = b[key];
940
897
  if (va === vb) continue;
941
- if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
942
- continue;
943
- }
898
+ if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
944
899
  return false;
945
900
  }
946
901
  return true;
@@ -990,11 +945,11 @@ function morphAttributes(oldEl, newEl, claim) {
990
945
  }
991
946
  if (claim && (reclaim || changed && oldEl.matches(CLAIMED_ELEMENTS))) claim(oldEl, true);
992
947
  }
993
- function morphNode(oldNode, newNode, claim, ranges) {
948
+ function morphNode(oldNode, newNode, claim, ranges, grafts) {
994
949
  if (oldNode.nodeType === ELEMENT_NODE) {
995
950
  if (oldNode.hasAttribute("data-preserve")) return;
996
951
  morphAttributes(oldNode, newNode, claim);
997
- reconcileChildren(oldNode, newNode, null, null, claim, ranges);
952
+ reconcileChildren(oldNode, newNode, null, null, claim, ranges, grafts);
998
953
  } else if (oldNode.data !== newNode.data) {
999
954
  oldNode.data = newNode.data;
1000
955
  }
@@ -1037,6 +992,13 @@ function moveRangeBefore(parent, start, id, ref) {
1037
992
  n = next;
1038
993
  }
1039
994
  }
995
+ function placeRange(parent, range, id, ref) {
996
+ if (range.nodeType === 11 ) {
997
+ parent.insertBefore(range, ref);
998
+ } else {
999
+ moveRangeBefore(parent, range, id, ref);
1000
+ }
1001
+ }
1040
1002
  function adoptRange(parent, start, id, ref, claim) {
1041
1003
  const end = slotEnd(id);
1042
1004
  let n = start;
@@ -1054,7 +1016,7 @@ function adoptRange(parent, start, id, ref, claim) {
1054
1016
  }
1055
1017
  return after;
1056
1018
  }
1057
- function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null) {
1019
+ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null, grafts = null) {
1058
1020
  let oldChild = boundStart ? boundStart.nextSibling : parent.firstChild;
1059
1021
  let newChild = source.firstChild;
1060
1022
  while (newChild) {
@@ -1070,11 +1032,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1070
1032
  const existing = displaced || findRangeStart(old, pid, boundEnd);
1071
1033
  if (existing) {
1072
1034
  if (ranges) ranges.delete(pid);
1073
- if (existing.nodeType === 11 ) {
1074
- parent.insertBefore(existing, old ?? boundEnd);
1075
- } else {
1076
- moveRangeBefore(parent, existing, pid, old ?? boundEnd);
1077
- }
1035
+ placeRange(parent, existing, pid, old ?? boundEnd);
1078
1036
  newChild = afterRange(newChild, pid);
1079
1037
  } else {
1080
1038
  newChild = adoptRange(parent, newChild, pid, old ?? boundEnd, claim);
@@ -1085,17 +1043,19 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1085
1043
  if (!old) {
1086
1044
  parent.insertBefore(newChild, boundEnd);
1087
1045
  if (claim) claim(newChild);
1046
+ if (grafts) grafts.push(newChild);
1088
1047
  newChild = nextNew;
1089
1048
  continue;
1090
1049
  }
1091
1050
  if (isSlotMarker(old)) {
1092
1051
  parent.insertBefore(newChild, old);
1093
1052
  if (claim) claim(newChild);
1053
+ if (grafts) grafts.push(newChild);
1094
1054
  newChild = nextNew;
1095
1055
  continue;
1096
1056
  }
1097
1057
  if (compatible(old, newChild)) {
1098
- morphNode(old, newChild, claim, ranges);
1058
+ morphNode(old, newChild, claim, ranges, grafts);
1099
1059
  oldChild = old.nextSibling;
1100
1060
  newChild = nextNew;
1101
1061
  continue;
@@ -1108,13 +1068,14 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1108
1068
  }
1109
1069
  if (ahead && ahead !== boundEnd) {
1110
1070
  parent.insertBefore(ahead, old);
1111
- morphNode(ahead, newChild, claim, ranges);
1071
+ morphNode(ahead, newChild, claim, ranges, grafts);
1112
1072
  newChild = nextNew;
1113
1073
  continue;
1114
1074
  }
1115
1075
  }
1116
1076
  parent.insertBefore(newChild, old);
1117
1077
  if (claim) claim(newChild);
1078
+ if (grafts) grafts.push(newChild);
1118
1079
  newChild = nextNew;
1119
1080
  }
1120
1081
  while (oldChild && oldChild !== boundEnd) {
@@ -1131,20 +1092,24 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1131
1092
  oldChild = next;
1132
1093
  }
1133
1094
  }
1134
- function restoreDisplacedRanges(first, end, ranges) {
1135
- const found = new Map();
1136
- collectSlots(first, end, found);
1137
- for (const [id, start] of found) {
1138
- const displaced = ranges.get(id);
1139
- if (!displaced || displaced === start) continue;
1140
- ranges.delete(id);
1141
- const parent = start.parentNode;
1142
- if (displaced.nodeType === 11 ) {
1143
- parent.insertBefore(displaced, start);
1144
- } else {
1145
- moveRangeBefore(parent, displaced, id, start);
1095
+ function flushGrafts(node, ranges) {
1096
+ if (node.nodeType !== ELEMENT_NODE || isFrameElement(node)) return;
1097
+ let n = node.firstChild;
1098
+ while (n) {
1099
+ const id = slotStartId(n);
1100
+ if (id !== null) {
1101
+ const next = afterRange(n, id);
1102
+ const displaced = ranges.get(id);
1103
+ if (displaced) {
1104
+ ranges.delete(id);
1105
+ placeRange(node, displaced, id, n);
1106
+ stashRange(document.createDocumentFragment(), n, id);
1107
+ }
1108
+ n = next;
1109
+ continue;
1146
1110
  }
1147
- stashRange(document.createDocumentFragment(), start, id);
1111
+ flushGrafts(n, ranges);
1112
+ n = n.nextSibling;
1148
1113
  }
1149
1114
  }
1150
1115
  function stashRange(frag, start, id) {
@@ -1176,7 +1141,7 @@ async function applyFrameResponse(response, host, options = {}) {
1176
1141
  if (chunk.type === "outcome") {
1177
1142
  if (options.onOutcome) options.onOutcome(chunk.payload);
1178
1143
  } else {
1179
- if (as !== undefined && chunk.id === rootId) chunk.id = as;else if (options.route) chunk.id = options.route(chunk.id);
1144
+ if (as !== undefined && chunk.id === rootId) chunk.id = as;
1180
1145
  if (perFrame) {
1181
1146
  let v = perFrame.get(chunk.id);
1182
1147
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
@@ -1190,7 +1155,7 @@ async function applyFrameResponse(response, host, options = {}) {
1190
1155
  }
1191
1156
  const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1192
1157
  const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1193
- const COMPONENT_HANDOFF = /*#__PURE__*/Symbol.for("dom-expressions.component-handoff");
1158
+ const COMPONENT_BINDING = /*#__PURE__*/Symbol.for("dom-expressions.component-binding");
1194
1159
  let resolveServerComponent;
1195
1160
  function parseServerComponent(value, ctx) {
1196
1161
  return {
@@ -1214,7 +1179,8 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1214
1179
  stream: parseServerComponent
1215
1180
  },
1216
1181
  serialize(node, ctx) {
1217
- return "self._$SC.r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1182
+ const registry = "self._$SC";
1183
+ return registry + ".r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1218
1184
  },
1219
1185
  deserialize(node, ctx) {
1220
1186
  const id = ctx.deserialize(node.id);
@@ -1239,120 +1205,79 @@ function createServerComponentHandler({
1239
1205
  host,
1240
1206
  component,
1241
1207
  onStream,
1242
- documentComponent,
1243
1208
  intercept,
1244
1209
  consumer = getFlightDataConsumer,
1245
1210
  codec = getServerFunctionsCodec
1246
1211
  }) {
1247
- const byAddress = new Map();
1248
- const forwards = new Map();
1249
- const brand = (comp, fnId, frameId) => {
1250
- const brandable = typeof comp === "function" || typeof comp === "object" && comp !== null;
1251
- if (brandable && !comp[COMPONENT_HANDOFF]) {
1252
- comp[COMPONENT_HANDOFF] = {
1253
- fnId,
1254
- frameId,
1255
- take(prev) {
1256
- const meta = prev !== null && (typeof prev === "function" || typeof prev === "object") && prev[COMPONENT_HANDOFF];
1257
- if (!meta || meta.fnId !== fnId) return false;
1258
- const root = meta.frameId;
1259
- let cur = forwards.get(root) ?? root;
1260
- if (cur !== root && !host.get(cur)) {
1261
- forwards.delete(root);
1262
- cur = root;
1263
- }
1264
- if (cur === frameId) return true;
1265
- let frame = host.get(cur);
1266
- if (!frame) return false;
1267
- while (frame) {
1268
- frame.rebind(frameId);
1269
- frame = host.get(cur);
1270
- }
1271
- if (frameId === root) forwards.delete(root);else forwards.set(root, frameId);
1272
- return true;
1273
- }
1274
- };
1275
- }
1212
+ const byFn = new Map();
1213
+ const componentFor = fnId => {
1214
+ let comp = byFn.get(fnId);
1215
+ if (comp === undefined) byFn.set(fnId, comp = component(fnId));
1276
1216
  return comp;
1277
1217
  };
1278
- const boundaryFor = (address, fnId) => {
1279
- let entry = byAddress.get(address);
1280
- if (!entry) {
1281
- entry = {
1282
- frameId: address,
1283
- component: brand(component(address), fnId, address)
1218
+ const byAddress = new Map();
1219
+ const bindingFor = (address, fnId) => {
1220
+ let binding = byAddress.get(address);
1221
+ if (!binding) {
1222
+ const comp = componentFor(fnId);
1223
+ binding = props => comp(props, () => address);
1224
+ binding[COMPONENT_BINDING] = {
1225
+ component: comp,
1226
+ address
1284
1227
  };
1285
- byAddress.set(address, entry);
1228
+ byAddress.set(address, binding);
1286
1229
  }
1287
- return entry;
1230
+ return binding;
1288
1231
  };
1289
- const resolveAddress = (id, address) => boundaryFor(address, id).component;
1290
- resolveServerComponent = resolveAddress;
1232
+ resolveServerComponent = (id, address) => bindingFor(address, id);
1291
1233
  const versions = new Map();
1292
- const bump = frameId => {
1293
- const version = (versions.get(frameId) || 0) + 1;
1294
- versions.set(frameId, version);
1234
+ const bump = address => {
1235
+ const version = (versions.get(address) || 0) + 1;
1236
+ versions.set(address, version);
1295
1237
  return version;
1296
1238
  };
1297
1239
  return {
1298
1240
  intercept: intercept && (info => {
1299
1241
  const hit = intercept(info);
1300
- if (hit !== undefined) {
1301
- const address = frameAddress(info.id, info.args);
1302
- byAddress.set(address, {
1303
- frameId: info.id,
1304
- component: brand(hit, info.id, info.id)
1305
- });
1306
- }
1307
- return hit;
1242
+ if (hit === undefined) return undefined;
1243
+ return bindingFor(frameAddress(info.id, info.args), info.id);
1308
1244
  }),
1309
1245
  handle(response, ctx) {
1310
1246
  if (!isFrameStreamResponse(response)) return undefined;
1311
1247
  const address = frameAddress(ctx.id, ctx.args);
1312
- let entry = byAddress.get(address);
1313
- if (!entry) {
1314
- const adopted = documentComponent && documentComponent(ctx.id);
1315
- if (adopted) {
1316
- entry = {
1317
- frameId: ctx.id,
1318
- component: brand(adopted, ctx.id, ctx.id)
1319
- };
1320
- byAddress.set(address, entry);
1321
- } else {
1322
- entry = boundaryFor(address, ctx.id);
1323
- }
1324
- }
1248
+ const binding = bindingFor(address, ctx.id);
1325
1249
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
1326
- return applyFlightResponse(response, entry);
1250
+ return applyFlightResponse(response, address, binding);
1327
1251
  }
1328
- const version = bump(entry.frameId);
1329
- if (onStream) onStream(entry.frameId, version, response);
1252
+ const version = bump(address);
1253
+ if (onStream) onStream(address, version, response);
1330
1254
  applyFrameResponse(response, host, {
1331
- as: entry.frameId,
1255
+ as: address,
1332
1256
  version
1333
1257
  }).catch(err => host.apply({
1334
1258
  type: "error",
1335
- id: entry.frameId,
1259
+ id: address,
1336
1260
  version,
1337
1261
  error: {
1338
1262
  message: String(err && err.message)
1339
1263
  }
1340
1264
  }));
1341
- return entry.component;
1265
+ return binding;
1342
1266
  },
1343
- showing(address, functionId, component) {
1344
- if (!byAddress.has(address)) {
1345
- byAddress.set(address, {
1346
- frameId: functionId,
1347
- component: brand(component, functionId, functionId),
1267
+ showing(address, functionId) {
1268
+ bindingFor(address, functionId);
1269
+ const comp = componentFor(functionId);
1270
+ if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
1271
+ comp[COMPONENT_BINDING] = {
1272
+ component: comp,
1348
1273
  address
1349
- });
1274
+ };
1350
1275
  }
1351
1276
  }
1352
1277
  };
1353
- async function applyFlightResponse(response, entry) {
1278
+ async function applyFlightResponse(response, address, binding) {
1354
1279
  const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1355
- const as = rootId ? entry.frameId : undefined;
1280
+ const as = rootId ? address : undefined;
1356
1281
  let feed;
1357
1282
  const source = new ReadableStream({
1358
1283
  start(controller) {
@@ -1363,10 +1288,6 @@ function createServerComponentHandler({
1363
1288
  let carried = false;
1364
1289
  await applyFrameResponse(response, host, {
1365
1290
  as,
1366
- route: id => {
1367
- const local = byAddress.get(id);
1368
- return local ? local.frameId : id;
1369
- },
1370
1291
  version: frameId => {
1371
1292
  const version = bump(frameId);
1372
1293
  if (onStream) onStream(frameId, version, response);
@@ -1387,7 +1308,7 @@ function createServerComponentHandler({
1387
1308
  if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
1388
1309
  throw envelope.value;
1389
1310
  }
1390
- return rootId ? entry.component : envelope.value;
1311
+ return rootId ? binding : envelope.value;
1391
1312
  }
1392
1313
  }
1393
1314
 
@@ -1438,6 +1359,9 @@ function createJSONDataTable(options) {
1438
1359
  };
1439
1360
  }
1440
1361
 
1362
+ function asyncArg(value) {
1363
+ return value;
1364
+ }
1441
1365
  let sharedHost;
1442
1366
  const tables = new Map();
1443
1367
  function tableFor(id) {
@@ -1514,8 +1438,26 @@ function claimRender(prefix, existing, render) {
1514
1438
  function liveSlotProps(initial, ctx) {
1515
1439
  const [args, setArgs] = createSignal(initial);
1516
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();
1517
1449
  return new Proxy({}, {
1518
- 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
+ },
1519
1461
  has: (_, key) => key in args(),
1520
1462
  ownKeys: () => Reflect.ownKeys(args()),
1521
1463
  getOwnPropertyDescriptor: (_, key) => key in args() ? {
@@ -1533,6 +1475,7 @@ function isReactiveContent(value) {
1533
1475
  }
1534
1476
  function slotsFor(props) {
1535
1477
  const bindings = new Map();
1478
+ const fillScopes = new Map();
1536
1479
  return new Proxy({}, {
1537
1480
  get(_, prop) {
1538
1481
  if (typeof prop !== "string" || !(prop in props)) return undefined;
@@ -1543,6 +1486,19 @@ function slotsFor(props) {
1543
1486
  bindings.delete(key);
1544
1487
  prev.dispose();
1545
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
+ }
1546
1502
  const settle = out => {
1547
1503
  const existing = ctx && ctx.existing || [];
1548
1504
  if (existing.length) {
@@ -1555,26 +1511,18 @@ function slotsFor(props) {
1555
1511
  const evaluate = () => {
1556
1512
  const v = props[prop];
1557
1513
  if (typeof v === "function" && ctx && ctx.invoked) {
1558
- return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
1514
+ return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
1559
1515
  }
1560
1516
  return v;
1561
1517
  };
1562
1518
  const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1563
1519
  const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1564
- 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();
1565
1521
  if (!isReactiveContent(value)) {
1566
1522
  return settle(normalizeSlotContent(value));
1567
1523
  }
1568
1524
  if (ctx && ctx.range) {
1569
- let claim = adopted;
1570
1525
  const source = value;
1571
- const accessor = () => {
1572
- if (claim) {
1573
- claim = false;
1574
- return claimRender(prefix, ctx.existing, () => typeof source === "function" ? source() : source);
1575
- }
1576
- return typeof source === "function" ? source() : source;
1577
- };
1578
1526
  const owner = createOwner();
1579
1527
  bindings.set(key, owner);
1580
1528
  ctx.onCleanup(() => {
@@ -1582,7 +1530,8 @@ function slotsFor(props) {
1582
1530
  owner.dispose();
1583
1531
  });
1584
1532
  const end = ctx.range.end;
1585
- runWithOwner(owner, () => insert(end.parentNode, accessor, end, [...ctx.existing]));
1533
+ const bind = () => insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
1534
+ runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());
1586
1535
  return undefined;
1587
1536
  }
1588
1537
  return settle(normalizeSlotContent(value));
@@ -1593,24 +1542,61 @@ function slotsFor(props) {
1593
1542
  function revealSeam(owner) {
1594
1543
  return seam => runWithOwner(owner, () => insert(seam.before.parentNode, createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1595
1544
  }
1545
+ let streamInvoke = false;
1596
1546
  function boundaryScope(owner) {
1597
- return fn => getOwner() ? fn() : runWithOwner(owner, fn);
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
+ };
1598
1556
  }
1599
- function boundaryComponent(host, id) {
1600
- return props => {
1557
+ function boundaryComponent(host, fnId) {
1558
+ return (props, binding) => {
1601
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;
1602
1566
  const {
1603
1567
  element,
1568
+ frame,
1604
1569
  dispose
1605
1570
  } = createFrameElement({
1606
1571
  host,
1607
1572
  id,
1608
1573
  slots: slotsFor(props),
1609
1574
  ownerScope: boundaryScope(owner),
1610
- 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
+ }
1611
1584
  });
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
+ }
1612
1596
  onCleanup(dispose);
1613
- return element;
1597
+ if (applied && !binding) return element;
1598
+ const gate = createMemo(() => gatePromise());
1599
+ return createMemo(() => (gate(), element));
1614
1600
  };
1615
1601
  }
1616
1602
  const claimedBoundaries = new Set();
@@ -1630,51 +1616,76 @@ function findBoundaryElement(id) {
1630
1616
  return boundaryIndex.get(id);
1631
1617
  }
1632
1618
  const boundaryWaiters = new Map();
1633
- function documentStreaming() {
1619
+ function boundaryMayArrive() {
1634
1620
  const hy = globalThis._$HY;
1635
- return !!hy && !hy.done;
1621
+ if (!hy) return false;
1622
+ return !hy.done || !!(hy.fr && hy.fr.pending());
1636
1623
  }
1637
1624
  function installRevealHook() {
1638
1625
  const hy = globalThis._$HY;
1639
- if (!hy || hy.$sc) return;
1626
+ if (!hy || hy.$sc || !hy.fr) return;
1640
1627
  hy.$sc = true;
1641
- const prev = hy.fe;
1642
- hy.fe = (fragmentId, parent) => {
1643
- if (prev) prev(fragmentId, parent);
1628
+ hy.fr.subscribe((_id, parent) => {
1644
1629
  if (!boundaryIndex) return;
1645
1630
  const root = parent || (typeof document !== "undefined" ? document.body : null);
1646
- if (!root) return;
1647
- indexBoundaries(root);
1631
+ if (root) indexBoundaries(root);
1632
+ if (!boundaryWaiters.size) return;
1633
+ const exhausted = hy.done && !hy.fr.pending();
1648
1634
  for (const [id, notify] of boundaryWaiters) {
1649
- const el = boundaryIndex.get(id);
1650
- if (!el) continue;
1635
+ const el = boundaryIndex && boundaryIndex.get(id);
1636
+ if (!el && !exhausted) continue;
1651
1637
  boundaryWaiters.delete(id);
1652
1638
  notify(el);
1653
1639
  }
1654
- };
1640
+ });
1655
1641
  }
1656
- function documentBoundary(host, id, props) {
1642
+ function documentBoundary(host, id, props, binding) {
1643
+ installRevealHook();
1657
1644
  const claimed = claimedBoundaries.has(id);
1658
1645
  const el = !claimed ? findBoundaryElement(id) : undefined;
1659
- if (el) return adoptBoundary(host, id, el, props);
1660
- if (!claimed && !boundaryWaiters.has(id) && documentStreaming()) {
1646
+ if (el) return adoptBoundary(host, id, el, props, binding);
1647
+ if (!claimed && !boundaryWaiters.has(id) && boundaryMayArrive()) {
1661
1648
  const owner = getOwner();
1662
1649
  const arrival = new Promise(resolve => boundaryWaiters.set(id, resolve));
1663
1650
  onCleanup(() => boundaryWaiters.delete(id));
1664
- return createMemo(() => arrival.then(node => runWithOwner(owner, () => adoptBoundary(host, id, node, props))));
1651
+ return createMemo(() => arrival.then(node => runWithOwner(owner, () =>
1652
+ node ? adoptBoundary(host, id, node, props, binding) : boundaryComponent(host, id)(props, binding))));
1665
1653
  }
1666
- return boundaryComponent(host, id)(props);
1654
+ return boundaryComponent(host, id)(props, binding);
1667
1655
  }
1668
- function adoptBoundary(host, id, el, props) {
1656
+ function documentAddress(id) {
1657
+ const records = globalThis._$SC?.a;
1658
+ if (records) {
1659
+ for (const address in records) if (records[address] === id) return address;
1660
+ }
1661
+ return id;
1662
+ }
1663
+ function adoptBoundary(host, id, el, props, binding) {
1669
1664
  claimedBoundaries.add(id);
1670
- const hy = globalThis._$HY;
1671
- if (hy && hy.r) {
1665
+ const address = binding ? binding() : documentAddress(id);
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
+ };
1678
+ const drainRecords = () => {
1679
+ const hy = globalThis._$HY;
1680
+ if (!hy || !hy.r) return;
1672
1681
  const slotPrefix = `sc:slot:${id}:`;
1673
1682
  for (const key of Object.keys(hy.r)) {
1683
+ if (appliedRecords.has(key)) continue;
1674
1684
  if (key.startsWith(slotPrefix)) {
1685
+ appliedRecords.add(key);
1675
1686
  host.apply({
1676
1687
  type: "slot",
1677
- id,
1688
+ id: address,
1678
1689
  version: 0,
1679
1690
  key: key.slice(slotPrefix.length),
1680
1691
  args: hy.r[key]
@@ -1682,6 +1693,7 @@ function adoptBoundary(host, id, el, props) {
1682
1693
  } else if (key.startsWith("sc:region:")) {
1683
1694
  const childId = key.slice("sc:region:".length);
1684
1695
  if (childId.startsWith(id + ".")) {
1696
+ appliedRecords.add(key);
1685
1697
  const val = hy.r[key];
1686
1698
  const apply = html => host.apply({
1687
1699
  type: "html",
@@ -1693,16 +1705,58 @@ function adoptBoundary(host, id, el, props) {
1693
1705
  }
1694
1706
  }
1695
1707
  }
1696
- }
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
+ });
1719
+ drainRecords();
1697
1720
  const owner = getOwner();
1721
+ let release;
1722
+ let setGate;
1698
1723
  const frame = createFrame(el, {
1699
1724
  adopt: true,
1700
1725
  host,
1701
- id,
1726
+ id: address,
1702
1727
  slots: slotsFor(props),
1703
1728
  ownerScope: boundaryScope(owner),
1704
- reveal: revealSeam(owner)
1729
+ reveal: revealSeam(owner),
1730
+ onApply: () => {
1731
+ if (release) {
1732
+ release();
1733
+ release = undefined;
1734
+ }
1735
+ setGate && setGate(undefined);
1736
+ },
1737
+ ...{
1738
+ recordsPending: () => {
1739
+ if (document.readyState === "loading") return true;
1740
+ const hy = globalThis._$HY;
1741
+ return !!(hy && hy.fr && hy.fr.pending());
1742
+ },
1743
+ drainRecords,
1744
+ claimScope: id
1745
+ }
1705
1746
  });
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
+ }
1706
1760
  onCleanup(() => frame.dispose());
1707
1761
  return el;
1708
1762
  }
@@ -1717,17 +1771,16 @@ function installServerComponents(host = getFrameHost()) {
1717
1771
  g._$SC.a[a] = i;
1718
1772
  g._$SC.reg && g._$SC.reg(a, i);
1719
1773
  }
1720
- return g._$SC.c[i] || (g._$SC.c[i] = p => g._$SC.impl(i, p));
1774
+ return g._$SC.c[i] || (g._$SC.c[i] = (p, b) => g._$SC.impl(i, p, b));
1721
1775
  }
1722
1776
  };
1723
1777
  }
1724
- g._$SC.impl = (id, props) => documentBoundary(host, id, props);
1778
+ g._$SC.impl = (id, props, binding) => documentBoundary(host, id, props, binding);
1725
1779
  installRevealHook();
1726
1780
  const handler = createServerComponentHandler({
1727
1781
  host,
1728
- component: frameId => boundaryComponent(host, frameId),
1729
- onStream: frameId => beginStream(frameId),
1730
- documentComponent: functionId => claimedBoundaries.has(functionId) ? undefined : g._$SC.c[functionId],
1782
+ component: fnId => g._$SC.r(fnId),
1783
+ onStream: address => beginStream(address),
1731
1784
  intercept: ({
1732
1785
  id
1733
1786
  }) => {
@@ -1735,7 +1788,7 @@ function installServerComponents(host = getFrameHost()) {
1735
1788
  return g._$SC.r(id);
1736
1789
  }
1737
1790
  });
1738
- const showing = (address, id) => handler.showing(address, id, g._$SC.r(id));
1791
+ const showing = (address, id) => handler.showing(address, id);
1739
1792
  const records = g._$SC.a || (g._$SC.a = {});
1740
1793
  for (const address in records) showing(address, records[address]);
1741
1794
  g._$SC.reg = showing;
@@ -1744,4 +1797,4 @@ function installServerComponents(host = getFrameHost()) {
1744
1797
  });
1745
1798
  }
1746
1799
 
1747
- 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 };