@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() {
@@ -905,11 +864,11 @@ function collectSlots(n, end, out) {
905
864
  n = n.nextSibling;
906
865
  }
907
866
  }
908
- function collectRegionElements(node, regions, prefix) {
867
+ function collectRegionElements(node, regions) {
909
868
  if (node.nodeType !== ELEMENT_NODE) return;
910
869
  if (isFrameElement(node)) {
911
870
  const childId = node.getAttribute(FRAME_ID_ATTR);
912
- if (childId && childId.startsWith(prefix)) {
871
+ if (childId && childId.includes(".")) {
913
872
  const argKey = childId.slice(childId.lastIndexOf(".") + 1);
914
873
  if (!regions.has(argKey)) {
915
874
  regions.set(argKey, {
@@ -922,7 +881,7 @@ function collectRegionElements(node, regions, prefix) {
922
881
  }
923
882
  return;
924
883
  }
925
- for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions, prefix);
884
+ for (let c = node.firstChild; c; c = c.nextSibling) collectRegionElements(c, regions);
926
885
  }
927
886
  function renameRegion(entry, childId) {
928
887
  entry.childId = childId;
@@ -990,11 +949,11 @@ function morphAttributes(oldEl, newEl, claim) {
990
949
  }
991
950
  if (claim && (reclaim || changed && oldEl.matches(CLAIMED_ELEMENTS))) claim(oldEl, true);
992
951
  }
993
- function morphNode(oldNode, newNode, claim, ranges) {
952
+ function morphNode(oldNode, newNode, claim, ranges, grafts) {
994
953
  if (oldNode.nodeType === ELEMENT_NODE) {
995
954
  if (oldNode.hasAttribute("data-preserve")) return;
996
955
  morphAttributes(oldNode, newNode, claim);
997
- reconcileChildren(oldNode, newNode, null, null, claim, ranges);
956
+ reconcileChildren(oldNode, newNode, null, null, claim, ranges, grafts);
998
957
  } else if (oldNode.data !== newNode.data) {
999
958
  oldNode.data = newNode.data;
1000
959
  }
@@ -1037,6 +996,13 @@ function moveRangeBefore(parent, start, id, ref) {
1037
996
  n = next;
1038
997
  }
1039
998
  }
999
+ function placeRange(parent, range, id, ref) {
1000
+ if (range.nodeType === 11 ) {
1001
+ parent.insertBefore(range, ref);
1002
+ } else {
1003
+ moveRangeBefore(parent, range, id, ref);
1004
+ }
1005
+ }
1040
1006
  function adoptRange(parent, start, id, ref, claim) {
1041
1007
  const end = slotEnd(id);
1042
1008
  let n = start;
@@ -1054,7 +1020,7 @@ function adoptRange(parent, start, id, ref, claim) {
1054
1020
  }
1055
1021
  return after;
1056
1022
  }
1057
- function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null) {
1023
+ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null, grafts = null) {
1058
1024
  let oldChild = boundStart ? boundStart.nextSibling : parent.firstChild;
1059
1025
  let newChild = source.firstChild;
1060
1026
  while (newChild) {
@@ -1070,11 +1036,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1070
1036
  const existing = displaced || findRangeStart(old, pid, boundEnd);
1071
1037
  if (existing) {
1072
1038
  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
- }
1039
+ placeRange(parent, existing, pid, old ?? boundEnd);
1078
1040
  newChild = afterRange(newChild, pid);
1079
1041
  } else {
1080
1042
  newChild = adoptRange(parent, newChild, pid, old ?? boundEnd, claim);
@@ -1085,17 +1047,19 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1085
1047
  if (!old) {
1086
1048
  parent.insertBefore(newChild, boundEnd);
1087
1049
  if (claim) claim(newChild);
1050
+ if (grafts) grafts.push(newChild);
1088
1051
  newChild = nextNew;
1089
1052
  continue;
1090
1053
  }
1091
1054
  if (isSlotMarker(old)) {
1092
1055
  parent.insertBefore(newChild, old);
1093
1056
  if (claim) claim(newChild);
1057
+ if (grafts) grafts.push(newChild);
1094
1058
  newChild = nextNew;
1095
1059
  continue;
1096
1060
  }
1097
1061
  if (compatible(old, newChild)) {
1098
- morphNode(old, newChild, claim, ranges);
1062
+ morphNode(old, newChild, claim, ranges, grafts);
1099
1063
  oldChild = old.nextSibling;
1100
1064
  newChild = nextNew;
1101
1065
  continue;
@@ -1108,13 +1072,14 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1108
1072
  }
1109
1073
  if (ahead && ahead !== boundEnd) {
1110
1074
  parent.insertBefore(ahead, old);
1111
- morphNode(ahead, newChild, claim, ranges);
1075
+ morphNode(ahead, newChild, claim, ranges, grafts);
1112
1076
  newChild = nextNew;
1113
1077
  continue;
1114
1078
  }
1115
1079
  }
1116
1080
  parent.insertBefore(newChild, old);
1117
1081
  if (claim) claim(newChild);
1082
+ if (grafts) grafts.push(newChild);
1118
1083
  newChild = nextNew;
1119
1084
  }
1120
1085
  while (oldChild && oldChild !== boundEnd) {
@@ -1131,20 +1096,24 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1131
1096
  oldChild = next;
1132
1097
  }
1133
1098
  }
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);
1099
+ function flushGrafts(node, ranges) {
1100
+ if (node.nodeType !== ELEMENT_NODE || isFrameElement(node)) return;
1101
+ let n = node.firstChild;
1102
+ while (n) {
1103
+ const id = slotStartId(n);
1104
+ if (id !== null) {
1105
+ const next = afterRange(n, id);
1106
+ const displaced = ranges.get(id);
1107
+ if (displaced) {
1108
+ ranges.delete(id);
1109
+ placeRange(node, displaced, id, n);
1110
+ stashRange(document.createDocumentFragment(), n, id);
1111
+ }
1112
+ n = next;
1113
+ continue;
1146
1114
  }
1147
- stashRange(document.createDocumentFragment(), start, id);
1115
+ flushGrafts(n, ranges);
1116
+ n = n.nextSibling;
1148
1117
  }
1149
1118
  }
1150
1119
  function stashRange(frag, start, id) {
@@ -1176,7 +1145,7 @@ async function applyFrameResponse(response, host, options = {}) {
1176
1145
  if (chunk.type === "outcome") {
1177
1146
  if (options.onOutcome) options.onOutcome(chunk.payload);
1178
1147
  } else {
1179
- if (as !== undefined && chunk.id === rootId) chunk.id = as;else if (options.route) chunk.id = options.route(chunk.id);
1148
+ if (as !== undefined && chunk.id === rootId) chunk.id = as;
1180
1149
  if (perFrame) {
1181
1150
  let v = perFrame.get(chunk.id);
1182
1151
  if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
@@ -1190,7 +1159,7 @@ async function applyFrameResponse(response, host, options = {}) {
1190
1159
  }
1191
1160
  const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1192
1161
  const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1193
- const COMPONENT_HANDOFF = /*#__PURE__*/Symbol.for("dom-expressions.component-handoff");
1162
+ const COMPONENT_BINDING = /*#__PURE__*/Symbol.for("dom-expressions.component-binding");
1194
1163
  let resolveServerComponent;
1195
1164
  function parseServerComponent(value, ctx) {
1196
1165
  return {
@@ -1214,7 +1183,8 @@ const ServerComponentPlugin = /*#__PURE__*/createPlugin({
1214
1183
  stream: parseServerComponent
1215
1184
  },
1216
1185
  serialize(node, ctx) {
1217
- return "self._$SC.r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1186
+ const registry = "self._$SC";
1187
+ return registry + ".r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1218
1188
  },
1219
1189
  deserialize(node, ctx) {
1220
1190
  const id = ctx.deserialize(node.id);
@@ -1239,120 +1209,79 @@ function createServerComponentHandler({
1239
1209
  host,
1240
1210
  component,
1241
1211
  onStream,
1242
- documentComponent,
1243
1212
  intercept,
1244
1213
  consumer = getFlightDataConsumer,
1245
1214
  codec = getServerFunctionsCodec
1246
1215
  }) {
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
- }
1216
+ const byFn = new Map();
1217
+ const componentFor = fnId => {
1218
+ let comp = byFn.get(fnId);
1219
+ if (comp === undefined) byFn.set(fnId, comp = component(fnId));
1276
1220
  return comp;
1277
1221
  };
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)
1222
+ const byAddress = new Map();
1223
+ const bindingFor = (address, fnId) => {
1224
+ let binding = byAddress.get(address);
1225
+ if (!binding) {
1226
+ const comp = componentFor(fnId);
1227
+ binding = props => comp(props, () => address);
1228
+ binding[COMPONENT_BINDING] = {
1229
+ component: comp,
1230
+ address
1284
1231
  };
1285
- byAddress.set(address, entry);
1232
+ byAddress.set(address, binding);
1286
1233
  }
1287
- return entry;
1234
+ return binding;
1288
1235
  };
1289
- const resolveAddress = (id, address) => boundaryFor(address, id).component;
1290
- resolveServerComponent = resolveAddress;
1236
+ resolveServerComponent = (id, address) => bindingFor(address, id);
1291
1237
  const versions = new Map();
1292
- const bump = frameId => {
1293
- const version = (versions.get(frameId) || 0) + 1;
1294
- versions.set(frameId, version);
1238
+ const bump = address => {
1239
+ const version = (versions.get(address) || 0) + 1;
1240
+ versions.set(address, version);
1295
1241
  return version;
1296
1242
  };
1297
1243
  return {
1298
1244
  intercept: intercept && (info => {
1299
1245
  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;
1246
+ if (hit === undefined) return undefined;
1247
+ return bindingFor(frameAddress(info.id, info.args), info.id);
1308
1248
  }),
1309
1249
  handle(response, ctx) {
1310
1250
  if (!isFrameStreamResponse(response)) return undefined;
1311
1251
  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
- }
1252
+ const binding = bindingFor(address, ctx.id);
1325
1253
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
1326
- return applyFlightResponse(response, entry);
1254
+ return applyFlightResponse(response, address, binding);
1327
1255
  }
1328
- const version = bump(entry.frameId);
1329
- if (onStream) onStream(entry.frameId, version, response);
1256
+ const version = bump(address);
1257
+ if (onStream) onStream(address, version, response);
1330
1258
  applyFrameResponse(response, host, {
1331
- as: entry.frameId,
1259
+ as: address,
1332
1260
  version
1333
1261
  }).catch(err => host.apply({
1334
1262
  type: "error",
1335
- id: entry.frameId,
1263
+ id: address,
1336
1264
  version,
1337
1265
  error: {
1338
1266
  message: String(err && err.message)
1339
1267
  }
1340
1268
  }));
1341
- return entry.component;
1269
+ return binding;
1342
1270
  },
1343
- showing(address, functionId, component) {
1344
- if (!byAddress.has(address)) {
1345
- byAddress.set(address, {
1346
- frameId: functionId,
1347
- component: brand(component, functionId, functionId),
1271
+ showing(address, functionId) {
1272
+ bindingFor(address, functionId);
1273
+ const comp = componentFor(functionId);
1274
+ if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
1275
+ comp[COMPONENT_BINDING] = {
1276
+ component: comp,
1348
1277
  address
1349
- });
1278
+ };
1350
1279
  }
1351
1280
  }
1352
1281
  };
1353
- async function applyFlightResponse(response, entry) {
1282
+ async function applyFlightResponse(response, address, binding) {
1354
1283
  const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1355
- const as = rootId ? entry.frameId : undefined;
1284
+ const as = rootId ? address : undefined;
1356
1285
  let feed;
1357
1286
  const source = new ReadableStream({
1358
1287
  start(controller) {
@@ -1363,10 +1292,6 @@ function createServerComponentHandler({
1363
1292
  let carried = false;
1364
1293
  await applyFrameResponse(response, host, {
1365
1294
  as,
1366
- route: id => {
1367
- const local = byAddress.get(id);
1368
- return local ? local.frameId : id;
1369
- },
1370
1295
  version: frameId => {
1371
1296
  const version = bump(frameId);
1372
1297
  if (onStream) onStream(frameId, version, response);
@@ -1387,7 +1312,7 @@ function createServerComponentHandler({
1387
1312
  if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
1388
1313
  throw envelope.value;
1389
1314
  }
1390
- return rootId ? entry.component : envelope.value;
1315
+ return rootId ? binding : envelope.value;
1391
1316
  }
1392
1317
  }
1393
1318
 
@@ -1566,15 +1491,7 @@ function slotsFor(props) {
1566
1491
  return settle(normalizeSlotContent(value));
1567
1492
  }
1568
1493
  if (ctx && ctx.range) {
1569
- let claim = adopted;
1570
1494
  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
1495
  const owner = createOwner();
1579
1496
  bindings.set(key, owner);
1580
1497
  ctx.onCleanup(() => {
@@ -1582,7 +1499,8 @@ function slotsFor(props) {
1582
1499
  owner.dispose();
1583
1500
  });
1584
1501
  const end = ctx.range.end;
1585
- runWithOwner(owner, () => insert(end.parentNode, accessor, end, [...ctx.existing]));
1502
+ const bind = () => insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
1503
+ runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());
1586
1504
  return undefined;
1587
1505
  }
1588
1506
  return settle(normalizeSlotContent(value));
@@ -1596,19 +1514,24 @@ function revealSeam(owner) {
1596
1514
  function boundaryScope(owner) {
1597
1515
  return fn => getOwner() ? fn() : runWithOwner(owner, fn);
1598
1516
  }
1599
- function boundaryComponent(host, id) {
1600
- return props => {
1517
+ function followBinding(frame, binding) {
1518
+ createRenderEffect(binding, address => frame.rebind(address));
1519
+ }
1520
+ function boundaryComponent(host, fnId) {
1521
+ return (props, binding) => {
1601
1522
  const owner = getOwner();
1602
1523
  const {
1603
1524
  element,
1525
+ frame,
1604
1526
  dispose
1605
1527
  } = createFrameElement({
1606
1528
  host,
1607
- id,
1529
+ id: binding ? binding() : fnId,
1608
1530
  slots: slotsFor(props),
1609
1531
  ownerScope: boundaryScope(owner),
1610
1532
  reveal: revealSeam(owner)
1611
1533
  });
1534
+ if (binding) followBinding(frame, binding);
1612
1535
  onCleanup(dispose);
1613
1536
  return element;
1614
1537
  };
@@ -1630,51 +1553,65 @@ function findBoundaryElement(id) {
1630
1553
  return boundaryIndex.get(id);
1631
1554
  }
1632
1555
  const boundaryWaiters = new Map();
1633
- function documentStreaming() {
1556
+ function boundaryMayArrive() {
1634
1557
  const hy = globalThis._$HY;
1635
- return !!hy && !hy.done;
1558
+ if (!hy) return false;
1559
+ return !hy.done || !!(hy.fr && hy.fr.pending());
1636
1560
  }
1637
1561
  function installRevealHook() {
1638
1562
  const hy = globalThis._$HY;
1639
- if (!hy || hy.$sc) return;
1563
+ if (!hy || hy.$sc || !hy.fr) return;
1640
1564
  hy.$sc = true;
1641
- const prev = hy.fe;
1642
- hy.fe = (fragmentId, parent) => {
1643
- if (prev) prev(fragmentId, parent);
1565
+ hy.fr.subscribe((_id, parent) => {
1644
1566
  if (!boundaryIndex) return;
1645
1567
  const root = parent || (typeof document !== "undefined" ? document.body : null);
1646
- if (!root) return;
1647
- indexBoundaries(root);
1568
+ if (root) indexBoundaries(root);
1569
+ if (!boundaryWaiters.size) return;
1570
+ const exhausted = hy.done && !hy.fr.pending();
1648
1571
  for (const [id, notify] of boundaryWaiters) {
1649
- const el = boundaryIndex.get(id);
1650
- if (!el) continue;
1572
+ const el = boundaryIndex && boundaryIndex.get(id);
1573
+ if (!el && !exhausted) continue;
1651
1574
  boundaryWaiters.delete(id);
1652
1575
  notify(el);
1653
1576
  }
1654
- };
1577
+ });
1655
1578
  }
1656
- function documentBoundary(host, id, props) {
1579
+ function documentBoundary(host, id, props, binding) {
1580
+ installRevealHook();
1657
1581
  const claimed = claimedBoundaries.has(id);
1658
1582
  const el = !claimed ? findBoundaryElement(id) : undefined;
1659
- if (el) return adoptBoundary(host, id, el, props);
1660
- if (!claimed && !boundaryWaiters.has(id) && documentStreaming()) {
1583
+ if (el) return adoptBoundary(host, id, el, props, binding);
1584
+ if (!claimed && !boundaryWaiters.has(id) && boundaryMayArrive()) {
1661
1585
  const owner = getOwner();
1662
1586
  const arrival = new Promise(resolve => boundaryWaiters.set(id, resolve));
1663
1587
  onCleanup(() => boundaryWaiters.delete(id));
1664
- return createMemo(() => arrival.then(node => runWithOwner(owner, () => adoptBoundary(host, id, node, props))));
1588
+ return createMemo(() => arrival.then(node => runWithOwner(owner, () =>
1589
+ node ? adoptBoundary(host, id, node, props, binding) : boundaryComponent(host, id)(props, binding))));
1590
+ }
1591
+ return boundaryComponent(host, id)(props, binding);
1592
+ }
1593
+ function documentAddress(id) {
1594
+ const records = globalThis._$SC?.a;
1595
+ if (records) {
1596
+ for (const address in records) if (records[address] === id) return address;
1665
1597
  }
1666
- return boundaryComponent(host, id)(props);
1598
+ return id;
1667
1599
  }
1668
- function adoptBoundary(host, id, el, props) {
1600
+ function adoptBoundary(host, id, el, props, binding) {
1669
1601
  claimedBoundaries.add(id);
1670
- const hy = globalThis._$HY;
1671
- if (hy && hy.r) {
1602
+ const address = binding ? binding() : documentAddress(id);
1603
+ const appliedRecords = new Set();
1604
+ const drainRecords = () => {
1605
+ const hy = globalThis._$HY;
1606
+ if (!hy || !hy.r) return;
1672
1607
  const slotPrefix = `sc:slot:${id}:`;
1673
1608
  for (const key of Object.keys(hy.r)) {
1609
+ if (appliedRecords.has(key)) continue;
1674
1610
  if (key.startsWith(slotPrefix)) {
1611
+ appliedRecords.add(key);
1675
1612
  host.apply({
1676
1613
  type: "slot",
1677
- id,
1614
+ id: address,
1678
1615
  version: 0,
1679
1616
  key: key.slice(slotPrefix.length),
1680
1617
  args: hy.r[key]
@@ -1682,6 +1619,7 @@ function adoptBoundary(host, id, el, props) {
1682
1619
  } else if (key.startsWith("sc:region:")) {
1683
1620
  const childId = key.slice("sc:region:".length);
1684
1621
  if (childId.startsWith(id + ".")) {
1622
+ appliedRecords.add(key);
1685
1623
  const val = hy.r[key];
1686
1624
  const apply = html => host.apply({
1687
1625
  type: "html",
@@ -1693,16 +1631,27 @@ function adoptBoundary(host, id, el, props) {
1693
1631
  }
1694
1632
  }
1695
1633
  }
1696
- }
1634
+ };
1635
+ drainRecords();
1697
1636
  const owner = getOwner();
1698
1637
  const frame = createFrame(el, {
1699
1638
  adopt: true,
1700
1639
  host,
1701
- id,
1640
+ id: address,
1702
1641
  slots: slotsFor(props),
1703
1642
  ownerScope: boundaryScope(owner),
1704
- reveal: revealSeam(owner)
1643
+ reveal: revealSeam(owner),
1644
+ ...{
1645
+ recordsPending: () => {
1646
+ if (document.readyState === "loading") return true;
1647
+ const hy = globalThis._$HY;
1648
+ return !!(hy && hy.fr && hy.fr.pending());
1649
+ },
1650
+ drainRecords,
1651
+ claimScope: id
1652
+ }
1705
1653
  });
1654
+ if (binding) followBinding(frame, binding);
1706
1655
  onCleanup(() => frame.dispose());
1707
1656
  return el;
1708
1657
  }
@@ -1717,17 +1666,16 @@ function installServerComponents(host = getFrameHost()) {
1717
1666
  g._$SC.a[a] = i;
1718
1667
  g._$SC.reg && g._$SC.reg(a, i);
1719
1668
  }
1720
- return g._$SC.c[i] || (g._$SC.c[i] = p => g._$SC.impl(i, p));
1669
+ return g._$SC.c[i] || (g._$SC.c[i] = (p, b) => g._$SC.impl(i, p, b));
1721
1670
  }
1722
1671
  };
1723
1672
  }
1724
- g._$SC.impl = (id, props) => documentBoundary(host, id, props);
1673
+ g._$SC.impl = (id, props, binding) => documentBoundary(host, id, props, binding);
1725
1674
  installRevealHook();
1726
1675
  const handler = createServerComponentHandler({
1727
1676
  host,
1728
- component: frameId => boundaryComponent(host, frameId),
1729
- onStream: frameId => beginStream(frameId),
1730
- documentComponent: functionId => claimedBoundaries.has(functionId) ? undefined : g._$SC.c[functionId],
1677
+ component: fnId => g._$SC.r(fnId),
1678
+ onStream: address => beginStream(address),
1731
1679
  intercept: ({
1732
1680
  id
1733
1681
  }) => {
@@ -1735,7 +1683,7 @@ function installServerComponents(host = getFrameHost()) {
1735
1683
  return g._$SC.r(id);
1736
1684
  }
1737
1685
  });
1738
- const showing = (address, id) => handler.showing(address, id, g._$SC.r(id));
1686
+ const showing = (address, id) => handler.showing(address, id);
1739
1687
  const records = g._$SC.a || (g._$SC.a = {});
1740
1688
  for (const address in records) showing(address, records[address]);
1741
1689
  g._$SC.reg = showing;