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

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.
@@ -1,4 +1,4 @@
1
- import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, sharedConfig, createSignal } from 'solid-js';
1
+ import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, createRenderEffect, sharedConfig, createSignal } 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,75 @@ 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
+ for (const key of Object.keys(store.records)) {
98
+ if (key.startsWith("seg:") || key === ":error") delete store.records[key];
99
+ }
100
+ }
101
+ Object.assign(store.records, records);
102
+ return true;
95
103
  };
96
104
  return {
97
105
  register(id, frame) {
98
106
  let set = frames.get(id);
99
107
  if (!set) frames.set(id, set = new Set());
100
- const sibling = set.size ? set.values().next().value : undefined;
101
108
  set.add(frame);
102
- if (sibling) {
109
+ const store = stores.get(id);
110
+ if (store && store.version !== undefined) {
103
111
  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
112
+ version: store.version,
113
+ r: store.records
116
114
  });
117
115
  frame.rebase && frame.rebase();
118
116
  }
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
117
  },
138
118
  unregister(id, frame) {
139
119
  const set = frames.get(id);
140
120
  if (set && frame) set.delete(frame);
141
121
  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
122
  frames.delete(id);
149
- pending.delete(id);
123
+ if (frame && frame.contentHTML) {
124
+ const store = storeFor(id);
125
+ if (!store.records[""]) {
126
+ const html = frame.contentHTML();
127
+ if (html != null) {
128
+ store.records[""] = {
129
+ kind: "html",
130
+ value: html
131
+ };
132
+ if (store.version === undefined) store.version = 0;
133
+ }
134
+ }
135
+ }
150
136
  }
137
+ if (!frame) stores.delete(id);
151
138
  },
152
139
  apply(chunk) {
153
140
  if (chunk.type === "data") {
154
141
  options.applyData && options.applyData(chunk);
155
142
  return;
156
143
  }
144
+ const records = chunkToRecords(chunk);
145
+ if (!write(storeFor(chunk.id), chunk.version, records)) return;
157
146
  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, {
147
+ if (set) {
148
+ for (const frame of set) frame.apply({
165
149
  version: chunk.version,
166
- chunks: [chunk]
150
+ r: records
167
151
  });
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
152
  }
175
- buffered.chunks.push(chunk);
176
153
  },
177
154
  get(id) {
178
155
  const set = frames.get(id);
@@ -208,6 +185,7 @@ class FrameImpl {
208
185
  #slotResolvedRefs = new Map();
209
186
  #slotNodes = new Map();
210
187
  #processedAssets = new Set();
188
+ #recordRefresh = null;
211
189
  #disposed = false;
212
190
  #styleFlush = () => {
213
191
  if (!this.#disposed) this.#flush();
@@ -353,7 +331,9 @@ class FrameImpl {
353
331
  }
354
332
  #removeSlotRecord(occurrence) {
355
333
  const key = `slot:${occurrence}`;
356
- if (key in this.#store) delete this.#store[key];else this.#options.removeSlotRecord?.(occurrence);
334
+ if (key in this.#store) {
335
+ if (!this.#mountedSlots.has(occurrence)) delete this.#store[key];
336
+ } else this.#options.removeSlotRecord?.(occurrence);
357
337
  }
358
338
  #syncSlots(root) {
359
339
  if (!this.#slots && !this.#options.resolveSlot) return;
@@ -371,6 +351,15 @@ class FrameImpl {
371
351
  this.#runSlotCleanups(occurrence);
372
352
  }
373
353
  if (!this.#mountedSlots.has(occurrence)) {
354
+ if (record === undefined && !root && this.#options.adopt && this.#options.recordsPending?.()) {
355
+ this.#recordRefresh ??= setTimeout(() => {
356
+ this.#recordRefresh = null;
357
+ if (this.#disposed) return;
358
+ this.#options.drainRecords?.();
359
+ this.#syncSlots();
360
+ });
361
+ continue;
362
+ }
374
363
  if (this.#options.adopt) this.#discoverRegions(occurrence, start);
375
364
  const nodes = this.#invokeSlot(occurrence, callback, record, start, this.#options.adopt);
376
365
  if (nodes) this.#replaceRange(occurrence, start, nodes);
@@ -387,12 +376,6 @@ class FrameImpl {
387
376
  const update = this.#slotUpdaters.get(occurrence);
388
377
  if (update) {
389
378
  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
379
  this.#slotArgs.set(occurrence, record);
397
380
  update(props);
398
381
  this.#bindRegions(occurrence);
@@ -438,12 +421,6 @@ class FrameImpl {
438
421
  } : undefined
439
422
  };
440
423
  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
424
  const scope = this.#options.ownerScope;
448
425
  const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
449
426
  this.#slotArgs.set(occurrence, record);
@@ -528,10 +505,9 @@ class FrameImpl {
528
505
  this.#slotRegions.set(slotKey, regions);
529
506
  }
530
507
  const endData = slotEnd(slotKey);
531
- const prefix = `${this.#options.id}.`;
532
508
  let n = start.nextSibling;
533
509
  while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
534
- collectRegionElements(n, regions, prefix);
510
+ collectRegionElements(n, regions);
535
511
  n = n.nextSibling;
536
512
  }
537
513
  }
@@ -543,16 +519,7 @@ class FrameImpl {
543
519
  const b = record.args || {};
544
520
  const ka = Object.keys(a);
545
521
  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
- }
522
+ if (kb.length !== ka.length) return false;
556
523
  const cache = this.#slotResolvedRefs.get(occurrence);
557
524
  for (const key of ka) {
558
525
  const va = a[key];
@@ -609,22 +576,9 @@ class FrameImpl {
609
576
  #findPlaceholder(name) {
610
577
  return findPlaceholder(this.#firstContent(), this.#end, placeholderId(name));
611
578
  }
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
- };
579
+ contentHTML() {
580
+ if (this.#disposed || !this.#element || !this.#hasContent) return null;
581
+ return this.#element.innerHTML;
628
582
  }
629
583
  rebind(id) {
630
584
  if (this.#disposed || id === this.#options.id) return;
@@ -659,6 +613,10 @@ class FrameImpl {
659
613
  } = this.#options;
660
614
  if (host && id !== undefined) host.unregister(id, this);
661
615
  this.#disposed = true;
616
+ if (this.#recordRefresh) {
617
+ clearTimeout(this.#recordRefresh);
618
+ this.#recordRefresh = null;
619
+ }
662
620
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
663
621
  for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
664
622
  for (const regions of this.#slotRegions.values()) {
@@ -681,8 +639,9 @@ class FrameImpl {
681
639
  const claim = claimHandlers() ? this.#claimTree : null;
682
640
  const ranges = new Map();
683
641
  this.#collectSlots(ranges);
684
- reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges);
685
- if (ranges.size) restoreDisplacedRanges(this.#firstContent(), this.#end, ranges);
642
+ const grafts = [];
643
+ reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges, grafts);
644
+ if (ranges.size) for (const root of grafts) flushGrafts(root, ranges);
686
645
  }
687
646
  }
688
647
  #clearContent() {
@@ -901,11 +860,11 @@ function collectSlots(n, end, out) {
901
860
  n = n.nextSibling;
902
861
  }
903
862
  }
904
- function collectRegionElements(node, regions, prefix) {
863
+ function collectRegionElements(node, regions) {
905
864
  if (node.nodeType !== ELEMENT_NODE) return;
906
865
  if (isFrameElement(node)) {
907
866
  const childId = node.getAttribute(FRAME_ID_ATTR);
908
- if (childId && childId.startsWith(prefix)) {
867
+ if (childId && childId.includes(".")) {
909
868
  const argKey = childId.slice(childId.lastIndexOf(".") + 1);
910
869
  if (!regions.has(argKey)) {
911
870
  regions.set(argKey, {
@@ -918,7 +877,7 @@ function collectRegionElements(node, regions, prefix) {
918
877
  }
919
878
  return;
920
879
  }
921
- for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions, prefix);
880
+ for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions);
922
881
  }
923
882
  function renameRegion(entry, childId) {
924
883
  entry.childId = childId;
@@ -986,11 +945,11 @@ function morphAttributes(oldEl, newEl, claim) {
986
945
  }
987
946
  if (claim && (reclaim || changed && oldEl.matches(CLAIMED_ELEMENTS))) claim(oldEl, true);
988
947
  }
989
- function morphNode(oldNode, newNode, claim, ranges) {
948
+ function morphNode(oldNode, newNode, claim, ranges, grafts) {
990
949
  if (oldNode.nodeType === ELEMENT_NODE) {
991
950
  if (oldNode.hasAttribute("data-preserve")) return;
992
951
  morphAttributes(oldNode, newNode, claim);
993
- reconcileChildren(oldNode, newNode, null, null, claim, ranges);
952
+ reconcileChildren(oldNode, newNode, null, null, claim, ranges, grafts);
994
953
  } else if (oldNode.data !== newNode.data) {
995
954
  oldNode.data = newNode.data;
996
955
  }
@@ -1024,6 +983,13 @@ function moveRangeBefore(parent, start, id, ref) {
1024
983
  n = next;
1025
984
  }
1026
985
  }
986
+ function placeRange(parent, range, id, ref) {
987
+ if (range.nodeType === 11 ) {
988
+ parent.insertBefore(range, ref);
989
+ } else {
990
+ moveRangeBefore(parent, range, id, ref);
991
+ }
992
+ }
1027
993
  function adoptRange(parent, start, id, ref, claim) {
1028
994
  const end = slotEnd(id);
1029
995
  let n = start;
@@ -1041,7 +1007,7 @@ function adoptRange(parent, start, id, ref, claim) {
1041
1007
  }
1042
1008
  return after;
1043
1009
  }
1044
- function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null) {
1010
+ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null, grafts = null) {
1045
1011
  let oldChild = boundStart ? boundStart.nextSibling : parent.firstChild;
1046
1012
  let newChild = source.firstChild;
1047
1013
  while (newChild) {
@@ -1057,11 +1023,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1057
1023
  const existing = displaced || findRangeStart(old, pid, boundEnd);
1058
1024
  if (existing) {
1059
1025
  if (ranges) ranges.delete(pid);
1060
- if (existing.nodeType === 11 ) {
1061
- parent.insertBefore(existing, old ?? boundEnd);
1062
- } else {
1063
- moveRangeBefore(parent, existing, pid, old ?? boundEnd);
1064
- }
1026
+ placeRange(parent, existing, pid, old ?? boundEnd);
1065
1027
  newChild = afterRange(newChild, pid);
1066
1028
  } else {
1067
1029
  newChild = adoptRange(parent, newChild, pid, old ?? boundEnd, claim);
@@ -1072,17 +1034,19 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1072
1034
  if (!old) {
1073
1035
  parent.insertBefore(newChild, boundEnd);
1074
1036
  if (claim) claim(newChild);
1037
+ if (grafts) grafts.push(newChild);
1075
1038
  newChild = nextNew;
1076
1039
  continue;
1077
1040
  }
1078
1041
  if (isSlotMarker(old)) {
1079
1042
  parent.insertBefore(newChild, old);
1080
1043
  if (claim) claim(newChild);
1044
+ if (grafts) grafts.push(newChild);
1081
1045
  newChild = nextNew;
1082
1046
  continue;
1083
1047
  }
1084
1048
  if (compatible(old, newChild)) {
1085
- morphNode(old, newChild, claim, ranges);
1049
+ morphNode(old, newChild, claim, ranges, grafts);
1086
1050
  oldChild = old.nextSibling;
1087
1051
  newChild = nextNew;
1088
1052
  continue;
@@ -1095,13 +1059,14 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1095
1059
  }
1096
1060
  if (ahead && ahead !== boundEnd) {
1097
1061
  parent.insertBefore(ahead, old);
1098
- morphNode(ahead, newChild, claim, ranges);
1062
+ morphNode(ahead, newChild, claim, ranges, grafts);
1099
1063
  newChild = nextNew;
1100
1064
  continue;
1101
1065
  }
1102
1066
  }
1103
1067
  parent.insertBefore(newChild, old);
1104
1068
  if (claim) claim(newChild);
1069
+ if (grafts) grafts.push(newChild);
1105
1070
  newChild = nextNew;
1106
1071
  }
1107
1072
  while (oldChild && oldChild !== boundEnd) {
@@ -1118,20 +1083,24 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1118
1083
  oldChild = next;
1119
1084
  }
1120
1085
  }
1121
- function restoreDisplacedRanges(first, end, ranges) {
1122
- const found = new Map();
1123
- collectSlots(first, end, found);
1124
- for (const [id, start] of found) {
1125
- const displaced = ranges.get(id);
1126
- if (!displaced || displaced === start) continue;
1127
- ranges.delete(id);
1128
- const parent = start.parentNode;
1129
- if (displaced.nodeType === 11 ) {
1130
- parent.insertBefore(displaced, start);
1131
- } else {
1132
- moveRangeBefore(parent, displaced, id, start);
1086
+ function flushGrafts(node, ranges) {
1087
+ if (node.nodeType !== ELEMENT_NODE || isFrameElement(node)) return;
1088
+ let n = node.firstChild;
1089
+ while (n) {
1090
+ const id = slotStartId(n);
1091
+ if (id !== null) {
1092
+ const next = afterRange(n, id);
1093
+ const displaced = ranges.get(id);
1094
+ if (displaced) {
1095
+ ranges.delete(id);
1096
+ placeRange(node, displaced, id, n);
1097
+ stashRange(document.createDocumentFragment(), n, id);
1098
+ }
1099
+ n = next;
1100
+ continue;
1133
1101
  }
1134
- stashRange(document.createDocumentFragment(), start, id);
1102
+ flushGrafts(n, ranges);
1103
+ n = n.nextSibling;
1135
1104
  }
1136
1105
  }
1137
1106
  function stashRange(frag, start, id) {
@@ -1163,7 +1132,7 @@ async function applyFrameResponse(response, host, options = {}) {
1163
1132
  if (chunk.type === "outcome") {
1164
1133
  if (options.onOutcome) options.onOutcome(chunk.payload);
1165
1134
  } else {
1166
- if (as !== undefined && chunk.id === rootId) chunk.id = as;else if (options.route) chunk.id = options.route(chunk.id);
1135
+ if (as !== undefined && chunk.id === rootId) chunk.id = as;
1167
1136
  if (perFrame) {
1168
1137
  let v = perFrame.get(chunk.id);
1169
1138
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
@@ -1177,7 +1146,7 @@ async function applyFrameResponse(response, host, options = {}) {
1177
1146
  }
1178
1147
  const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1179
1148
  const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1180
- const COMPONENT_HANDOFF = /*#__PURE__*/Symbol.for("dom-expressions.component-handoff");
1149
+ const COMPONENT_BINDING = /*#__PURE__*/Symbol.for("dom-expressions.component-binding");
1181
1150
  let resolveServerComponent;
1182
1151
  function parseServerComponent(value, ctx) {
1183
1152
  return {
@@ -1201,7 +1170,8 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1201
1170
  stream: parseServerComponent
1202
1171
  },
1203
1172
  serialize(node, ctx) {
1204
- return "self._$SC.r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1173
+ const registry = "self._$SC";
1174
+ return registry + ".r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1205
1175
  },
1206
1176
  deserialize(node, ctx) {
1207
1177
  const id = ctx.deserialize(node.id);
@@ -1226,120 +1196,79 @@ function createServerComponentHandler({
1226
1196
  host,
1227
1197
  component,
1228
1198
  onStream,
1229
- documentComponent,
1230
1199
  intercept,
1231
1200
  consumer = getFlightDataConsumer,
1232
1201
  codec = getServerFunctionsCodec
1233
1202
  }) {
1234
- const byAddress = new Map();
1235
- const forwards = new Map();
1236
- const brand = (comp, fnId, frameId) => {
1237
- const brandable = typeof comp === "function" || typeof comp === "object" && comp !== null;
1238
- if (brandable && !comp[COMPONENT_HANDOFF]) {
1239
- comp[COMPONENT_HANDOFF] = {
1240
- fnId,
1241
- frameId,
1242
- take(prev) {
1243
- const meta = prev !== null && (typeof prev === "function" || typeof prev === "object") && prev[COMPONENT_HANDOFF];
1244
- if (!meta || meta.fnId !== fnId) return false;
1245
- const root = meta.frameId;
1246
- let cur = forwards.get(root) ?? root;
1247
- if (cur !== root && !host.get(cur)) {
1248
- forwards.delete(root);
1249
- cur = root;
1250
- }
1251
- if (cur === frameId) return true;
1252
- let frame = host.get(cur);
1253
- if (!frame) return false;
1254
- while (frame) {
1255
- frame.rebind(frameId);
1256
- frame = host.get(cur);
1257
- }
1258
- if (frameId === root) forwards.delete(root);else forwards.set(root, frameId);
1259
- return true;
1260
- }
1261
- };
1262
- }
1203
+ const byFn = new Map();
1204
+ const componentFor = fnId => {
1205
+ let comp = byFn.get(fnId);
1206
+ if (comp === undefined) byFn.set(fnId, comp = component(fnId));
1263
1207
  return comp;
1264
1208
  };
1265
- const boundaryFor = (address, fnId) => {
1266
- let entry = byAddress.get(address);
1267
- if (!entry) {
1268
- entry = {
1269
- frameId: address,
1270
- component: brand(component(address), fnId, address)
1209
+ const byAddress = new Map();
1210
+ const bindingFor = (address, fnId) => {
1211
+ let binding = byAddress.get(address);
1212
+ if (!binding) {
1213
+ const comp = componentFor(fnId);
1214
+ binding = props => comp(props, () => address);
1215
+ binding[COMPONENT_BINDING] = {
1216
+ component: comp,
1217
+ address
1271
1218
  };
1272
- byAddress.set(address, entry);
1219
+ byAddress.set(address, binding);
1273
1220
  }
1274
- return entry;
1221
+ return binding;
1275
1222
  };
1276
- const resolveAddress = (id, address) => boundaryFor(address, id).component;
1277
- resolveServerComponent = resolveAddress;
1223
+ resolveServerComponent = (id, address) => bindingFor(address, id);
1278
1224
  const versions = new Map();
1279
- const bump = frameId => {
1280
- const version = (versions.get(frameId) || 0) + 1;
1281
- versions.set(frameId, version);
1225
+ const bump = address => {
1226
+ const version = (versions.get(address) || 0) + 1;
1227
+ versions.set(address, version);
1282
1228
  return version;
1283
1229
  };
1284
1230
  return {
1285
1231
  intercept: intercept && (info => {
1286
1232
  const hit = intercept(info);
1287
- if (hit !== undefined) {
1288
- const address = frameAddress(info.id, info.args);
1289
- byAddress.set(address, {
1290
- frameId: info.id,
1291
- component: brand(hit, info.id, info.id)
1292
- });
1293
- }
1294
- return hit;
1233
+ if (hit === undefined) return undefined;
1234
+ return bindingFor(frameAddress(info.id, info.args), info.id);
1295
1235
  }),
1296
1236
  handle(response, ctx) {
1297
1237
  if (!isFrameStreamResponse(response)) return undefined;
1298
1238
  const address = frameAddress(ctx.id, ctx.args);
1299
- let entry = byAddress.get(address);
1300
- if (!entry) {
1301
- const adopted = documentComponent && documentComponent(ctx.id);
1302
- if (adopted) {
1303
- entry = {
1304
- frameId: ctx.id,
1305
- component: brand(adopted, ctx.id, ctx.id)
1306
- };
1307
- byAddress.set(address, entry);
1308
- } else {
1309
- entry = boundaryFor(address, ctx.id);
1310
- }
1311
- }
1239
+ const binding = bindingFor(address, ctx.id);
1312
1240
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
1313
- return applyFlightResponse(response, entry);
1241
+ return applyFlightResponse(response, address, binding);
1314
1242
  }
1315
- const version = bump(entry.frameId);
1316
- if (onStream) onStream(entry.frameId, version, response);
1243
+ const version = bump(address);
1244
+ if (onStream) onStream(address, version, response);
1317
1245
  applyFrameResponse(response, host, {
1318
- as: entry.frameId,
1246
+ as: address,
1319
1247
  version
1320
1248
  }).catch(err => host.apply({
1321
1249
  type: "error",
1322
- id: entry.frameId,
1250
+ id: address,
1323
1251
  version,
1324
1252
  error: {
1325
1253
  message: String(err && err.message)
1326
1254
  }
1327
1255
  }));
1328
- return entry.component;
1256
+ return binding;
1329
1257
  },
1330
- showing(address, functionId, component) {
1331
- if (!byAddress.has(address)) {
1332
- byAddress.set(address, {
1333
- frameId: functionId,
1334
- component: brand(component, functionId, functionId),
1258
+ showing(address, functionId) {
1259
+ bindingFor(address, functionId);
1260
+ const comp = componentFor(functionId);
1261
+ if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
1262
+ comp[COMPONENT_BINDING] = {
1263
+ component: comp,
1335
1264
  address
1336
- });
1265
+ };
1337
1266
  }
1338
1267
  }
1339
1268
  };
1340
- async function applyFlightResponse(response, entry) {
1269
+ async function applyFlightResponse(response, address, binding) {
1341
1270
  const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1342
- const as = rootId ? entry.frameId : undefined;
1271
+ const as = rootId ? address : undefined;
1343
1272
  let feed;
1344
1273
  const source = new ReadableStream({
1345
1274
  start(controller) {
@@ -1350,10 +1279,6 @@ function createServerComponentHandler({
1350
1279
  let carried = false;
1351
1280
  await applyFrameResponse(response, host, {
1352
1281
  as,
1353
- route: id => {
1354
- const local = byAddress.get(id);
1355
- return local ? local.frameId : id;
1356
- },
1357
1282
  version: frameId => {
1358
1283
  const version = bump(frameId);
1359
1284
  if (onStream) onStream(frameId, version, response);
@@ -1374,7 +1299,7 @@ function createServerComponentHandler({
1374
1299
  if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
1375
1300
  throw envelope.value;
1376
1301
  }
1377
- return rootId ? entry.component : envelope.value;
1302
+ return rootId ? binding : envelope.value;
1378
1303
  }
1379
1304
  }
1380
1305
 
@@ -1553,15 +1478,7 @@ function slotsFor(props) {
1553
1478
  return settle(normalizeSlotContent(value));
1554
1479
  }
1555
1480
  if (ctx && ctx.range) {
1556
- let claim = adopted;
1557
1481
  const source = value;
1558
- const accessor = () => {
1559
- if (claim) {
1560
- claim = false;
1561
- return claimRender(prefix, ctx.existing, () => typeof source === "function" ? source() : source);
1562
- }
1563
- return typeof source === "function" ? source() : source;
1564
- };
1565
1482
  const owner = createOwner();
1566
1483
  bindings.set(key, owner);
1567
1484
  ctx.onCleanup(() => {
@@ -1569,7 +1486,8 @@ function slotsFor(props) {
1569
1486
  owner.dispose();
1570
1487
  });
1571
1488
  const end = ctx.range.end;
1572
- runWithOwner(owner, () => insert(end.parentNode, accessor, end, [...ctx.existing]));
1489
+ const bind = () => insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
1490
+ runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());
1573
1491
  return undefined;
1574
1492
  }
1575
1493
  return settle(normalizeSlotContent(value));
@@ -1583,19 +1501,24 @@ function revealSeam(owner) {
1583
1501
  function boundaryScope(owner) {
1584
1502
  return fn => getOwner() ? fn() : runWithOwner(owner, fn);
1585
1503
  }
1586
- function boundaryComponent(host, id) {
1587
- return props => {
1504
+ function followBinding(frame, binding) {
1505
+ createRenderEffect(binding, address => frame.rebind(address));
1506
+ }
1507
+ function boundaryComponent(host, fnId) {
1508
+ return (props, binding) => {
1588
1509
  const owner = getOwner();
1589
1510
  const {
1590
1511
  element,
1512
+ frame,
1591
1513
  dispose
1592
1514
  } = createFrameElement({
1593
1515
  host,
1594
- id,
1516
+ id: binding ? binding() : fnId,
1595
1517
  slots: slotsFor(props),
1596
1518
  ownerScope: boundaryScope(owner),
1597
1519
  reveal: revealSeam(owner)
1598
1520
  });
1521
+ if (binding) followBinding(frame, binding);
1599
1522
  onCleanup(dispose);
1600
1523
  return element;
1601
1524
  };
@@ -1617,51 +1540,65 @@ function findBoundaryElement(id) {
1617
1540
  return boundaryIndex.get(id);
1618
1541
  }
1619
1542
  const boundaryWaiters = new Map();
1620
- function documentStreaming() {
1543
+ function boundaryMayArrive() {
1621
1544
  const hy = globalThis._$HY;
1622
- return !!hy && !hy.done;
1545
+ if (!hy) return false;
1546
+ return !hy.done || !!(hy.fr && hy.fr.pending());
1623
1547
  }
1624
1548
  function installRevealHook() {
1625
1549
  const hy = globalThis._$HY;
1626
- if (!hy || hy.$sc) return;
1550
+ if (!hy || hy.$sc || !hy.fr) return;
1627
1551
  hy.$sc = true;
1628
- const prev = hy.fe;
1629
- hy.fe = (fragmentId, parent) => {
1630
- if (prev) prev(fragmentId, parent);
1552
+ hy.fr.subscribe((_id, parent) => {
1631
1553
  if (!boundaryIndex) return;
1632
1554
  const root = parent || (typeof document !== "undefined" ? document.body : null);
1633
- if (!root) return;
1634
- indexBoundaries(root);
1555
+ if (root) indexBoundaries(root);
1556
+ if (!boundaryWaiters.size) return;
1557
+ const exhausted = hy.done && !hy.fr.pending();
1635
1558
  for (const [id, notify] of boundaryWaiters) {
1636
- const el = boundaryIndex.get(id);
1637
- if (!el) continue;
1559
+ const el = boundaryIndex && boundaryIndex.get(id);
1560
+ if (!el && !exhausted) continue;
1638
1561
  boundaryWaiters.delete(id);
1639
1562
  notify(el);
1640
1563
  }
1641
- };
1564
+ });
1642
1565
  }
1643
- function documentBoundary(host, id, props) {
1566
+ function documentBoundary(host, id, props, binding) {
1567
+ installRevealHook();
1644
1568
  const claimed = claimedBoundaries.has(id);
1645
1569
  const el = !claimed ? findBoundaryElement(id) : undefined;
1646
- if (el) return adoptBoundary(host, id, el, props);
1647
- if (!claimed && !boundaryWaiters.has(id) && documentStreaming()) {
1570
+ if (el) return adoptBoundary(host, id, el, props, binding);
1571
+ if (!claimed && !boundaryWaiters.has(id) && boundaryMayArrive()) {
1648
1572
  const owner = getOwner();
1649
1573
  const arrival = new Promise(resolve => boundaryWaiters.set(id, resolve));
1650
1574
  onCleanup(() => boundaryWaiters.delete(id));
1651
- return createMemo(() => arrival.then(node => runWithOwner(owner, () => adoptBoundary(host, id, node, props))));
1575
+ return createMemo(() => arrival.then(node => runWithOwner(owner, () =>
1576
+ node ? adoptBoundary(host, id, node, props, binding) : boundaryComponent(host, id)(props, binding))));
1577
+ }
1578
+ return boundaryComponent(host, id)(props, binding);
1579
+ }
1580
+ function documentAddress(id) {
1581
+ const records = globalThis._$SC?.a;
1582
+ if (records) {
1583
+ for (const address in records) if (records[address] === id) return address;
1652
1584
  }
1653
- return boundaryComponent(host, id)(props);
1585
+ return id;
1654
1586
  }
1655
- function adoptBoundary(host, id, el, props) {
1587
+ function adoptBoundary(host, id, el, props, binding) {
1656
1588
  claimedBoundaries.add(id);
1657
- const hy = globalThis._$HY;
1658
- if (hy && hy.r) {
1589
+ const address = binding ? binding() : documentAddress(id);
1590
+ const appliedRecords = new Set();
1591
+ const drainRecords = () => {
1592
+ const hy = globalThis._$HY;
1593
+ if (!hy || !hy.r) return;
1659
1594
  const slotPrefix = `sc:slot:${id}:`;
1660
1595
  for (const key of Object.keys(hy.r)) {
1596
+ if (appliedRecords.has(key)) continue;
1661
1597
  if (key.startsWith(slotPrefix)) {
1598
+ appliedRecords.add(key);
1662
1599
  host.apply({
1663
1600
  type: "slot",
1664
- id,
1601
+ id: address,
1665
1602
  version: 0,
1666
1603
  key: key.slice(slotPrefix.length),
1667
1604
  args: hy.r[key]
@@ -1669,6 +1606,7 @@ function adoptBoundary(host, id, el, props) {
1669
1606
  } else if (key.startsWith("sc:region:")) {
1670
1607
  const childId = key.slice("sc:region:".length);
1671
1608
  if (childId.startsWith(id + ".")) {
1609
+ appliedRecords.add(key);
1672
1610
  const val = hy.r[key];
1673
1611
  const apply = html => host.apply({
1674
1612
  type: "html",
@@ -1680,16 +1618,27 @@ function adoptBoundary(host, id, el, props) {
1680
1618
  }
1681
1619
  }
1682
1620
  }
1683
- }
1621
+ };
1622
+ drainRecords();
1684
1623
  const owner = getOwner();
1685
1624
  const frame = createFrame(el, {
1686
1625
  adopt: true,
1687
1626
  host,
1688
- id,
1627
+ id: address,
1689
1628
  slots: slotsFor(props),
1690
1629
  ownerScope: boundaryScope(owner),
1691
- reveal: revealSeam(owner)
1630
+ reveal: revealSeam(owner),
1631
+ ...{
1632
+ recordsPending: () => {
1633
+ if (document.readyState === "loading") return true;
1634
+ const hy = globalThis._$HY;
1635
+ return !!(hy && hy.fr && hy.fr.pending());
1636
+ },
1637
+ drainRecords,
1638
+ claimScope: id
1639
+ }
1692
1640
  });
1641
+ if (binding) followBinding(frame, binding);
1693
1642
  onCleanup(() => frame.dispose());
1694
1643
  return el;
1695
1644
  }
@@ -1704,17 +1653,16 @@ function installServerComponents(host = getFrameHost()) {
1704
1653
  g._$SC.a[a] = i;
1705
1654
  g._$SC.reg && g._$SC.reg(a, i);
1706
1655
  }
1707
- return g._$SC.c[i] || (g._$SC.c[i] = p => g._$SC.impl(i, p));
1656
+ return g._$SC.c[i] || (g._$SC.c[i] = (p, b) => g._$SC.impl(i, p, b));
1708
1657
  }
1709
1658
  };
1710
1659
  }
1711
- g._$SC.impl = (id, props) => documentBoundary(host, id, props);
1660
+ g._$SC.impl = (id, props, binding) => documentBoundary(host, id, props, binding);
1712
1661
  installRevealHook();
1713
1662
  const handler = createServerComponentHandler({
1714
1663
  host,
1715
- component: frameId => boundaryComponent(host, frameId),
1716
- onStream: frameId => beginStream(frameId),
1717
- documentComponent: functionId => claimedBoundaries.has(functionId) ? undefined : g._$SC.c[functionId],
1664
+ component: fnId => g._$SC.r(fnId),
1665
+ onStream: address => beginStream(address),
1718
1666
  intercept: ({
1719
1667
  id
1720
1668
  }) => {
@@ -1722,7 +1670,7 @@ function installServerComponents(host = getFrameHost()) {
1722
1670
  return g._$SC.r(id);
1723
1671
  }
1724
1672
  });
1725
- const showing = (address, id) => handler.showing(address, id, g._$SC.r(id));
1673
+ const showing = (address, id) => handler.showing(address, id);
1726
1674
  const records = g._$SC.a || (g._$SC.a = {});
1727
1675
  for (const address in records) showing(address, records[address]);
1728
1676
  g._$SC.reg = showing;