@solidjs/web 2.0.0-beta.27 → 2.0.0-beta.29

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 (43) hide show
  1. package/dist/dev.cjs +40 -2
  2. package/dist/dev.js +39 -4
  3. package/dist/server.cjs +113 -37
  4. package/dist/server.js +112 -39
  5. package/dist/web.cjs +40 -2
  6. package/dist/web.js +39 -4
  7. package/frames/dist/client.cjs +370 -209
  8. package/frames/dist/client.dev.cjs +370 -210
  9. package/frames/dist/client.dev.js +371 -211
  10. package/frames/dist/client.js +371 -210
  11. package/frames/dist/server.cjs +373 -74
  12. package/frames/dist/server.js +373 -75
  13. package/package.json +3 -3
  14. package/server-functions/dist/client.cjs +100 -3
  15. package/server-functions/dist/client.js +92 -4
  16. package/server-functions/dist/server.cjs +85 -17
  17. package/server-functions/dist/server.js +83 -17
  18. package/types/client.d.ts +15 -0
  19. package/types/core.d.ts +1 -1
  20. package/types/frames/client.d.ts +8 -5
  21. package/types/frames/frame-client.d.ts +17 -0
  22. package/types/frames/frame-sink.d.ts +29 -6
  23. package/types/frames/frame-transport.d.ts +76 -12
  24. package/types/frames/server.d.ts +29 -1
  25. package/types/index.d.ts +74 -0
  26. package/types/server-functions/client.d.ts +25 -0
  27. package/types/server-functions/server.d.ts +105 -18
  28. package/types/server-functions/shared.d.ts +22 -0
  29. package/types/server-mock.d.ts +6 -2
  30. package/types/server.d.ts +39 -1
  31. package/types-cjs/client.d.cts +15 -0
  32. package/types-cjs/core.d.cts +1 -1
  33. package/types-cjs/frames/client.d.cts +8 -5
  34. package/types-cjs/frames/frame-client.d.cts +17 -0
  35. package/types-cjs/frames/frame-sink.d.cts +29 -6
  36. package/types-cjs/frames/frame-transport.d.cts +76 -12
  37. package/types-cjs/frames/server.d.cts +29 -1
  38. package/types-cjs/index.d.cts +74 -0
  39. package/types-cjs/server-functions/client.d.cts +25 -0
  40. package/types-cjs/server-functions/server.d.cts +105 -18
  41. package/types-cjs/server-functions/shared.d.cts +22 -0
  42. package/types-cjs/server-mock.d.cts +6 -2
  43. package/types-cjs/server.d.cts +39 -1
@@ -2,8 +2,8 @@
2
2
 
3
3
  var solidJs = require('solid-js');
4
4
  var web$1 = require('@solidjs/web');
5
- var client = require('@solidjs/web/server-functions/client');
6
5
  var seroval = require('seroval');
6
+ var client = require('@solidjs/web/server-functions/client');
7
7
  var web = require('seroval-plugins/web');
8
8
 
9
9
  const ELEMENT_NODE = 1;
@@ -84,6 +84,7 @@ function chunkToRecords(chunk) {
84
84
  function createFrameHost(options = {}) {
85
85
  const frames = new Map();
86
86
  const pending = new Map();
87
+ const retained = new Map();
87
88
  const deliver = (frame, chunk) => {
88
89
  if (chunk.type === "data") {
89
90
  options.applyData && options.applyData(chunk);
@@ -109,6 +110,14 @@ function createFrameHost(options = {}) {
109
110
  }
110
111
  return;
111
112
  }
113
+ const kept = retained.get(id);
114
+ if (kept) {
115
+ retained.delete(id);
116
+ frame.apply({
117
+ version: kept.version,
118
+ r: kept.records
119
+ });
120
+ }
112
121
  const buffered = pending.get(id);
113
122
  if (buffered) {
114
123
  pending.delete(id);
@@ -132,6 +141,12 @@ function createFrameHost(options = {}) {
132
141
  const set = frames.get(id);
133
142
  if (set && frame) set.delete(frame);
134
143
  if (!set || !frame || !set.size) {
144
+ if (frame) {
145
+ const kept = frame.snapshot && frame.snapshot();
146
+ if (kept) retained.set(id, kept);
147
+ } else {
148
+ retained.delete(id);
149
+ }
135
150
  frames.delete(id);
136
151
  pending.delete(id);
137
152
  }
@@ -344,7 +359,7 @@ class FrameImpl {
344
359
  #syncSlots(root) {
345
360
  if (!this.#slots && !this.#options.resolveSlot) return;
346
361
  const found = new Map();
347
- if (root) collectSlots(root, found);else this.#collectSlots(found);
362
+ if (root) collectSlots(root.firstChild, null, found);else this.#collectSlots(found);
348
363
  for (const [occurrence, start] of found) {
349
364
  const callback = this.#resolveSlot(propOf(occurrence));
350
365
  if (!callback) continue;
@@ -383,12 +398,28 @@ class FrameImpl {
383
398
  }
384
399
  #invokeSlot(occurrence, callback, record, start, adopted) {
385
400
  const cleanups = this.#slotCleanups.get(occurrence) ?? [];
401
+ let existing = [];
402
+ let end = null;
403
+ if (start) {
404
+ const endData = slotEnd(occurrence);
405
+ let n = start.nextSibling;
406
+ while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
407
+ existing.push(n);
408
+ n = n.nextSibling;
409
+ }
410
+ end = n;
411
+ }
386
412
  const ctx = {
387
413
  frame: this.#options.claimScope ?? this.#options.id,
388
414
  key: occurrence,
389
415
  adopted: !!adopted,
416
+ invoked: !!(record && record.kind === "slot"),
390
417
  onCleanup: fn => cleanups.push(fn),
391
- existing: start ? rangeInterior(start, slotEnd(occurrence)) : []
418
+ existing,
419
+ range: end ? {
420
+ start,
421
+ end
422
+ } : undefined
392
423
  };
393
424
  const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
394
425
  const regions = this.#slotRegions.get(occurrence);
@@ -398,7 +429,8 @@ class FrameImpl {
398
429
  if (!(argKey in props)) props[argKey] = entry.element;
399
430
  }
400
431
  }
401
- const content = callback(props, ctx);
432
+ const scope = this.#options.ownerScope;
433
+ const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
402
434
  this.#slotArgs.set(occurrence, record);
403
435
  if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);
404
436
  if (content == null) return null;
@@ -429,7 +461,6 @@ class FrameImpl {
429
461
  } of regions.values()) frame?.dispose();
430
462
  this.#slotRegions.delete(key);
431
463
  }
432
- this.#slotArgs.delete(key);
433
464
  }
434
465
  #runSlotCleanups(key) {
435
466
  const cleanups = this.#slotCleanups.get(key);
@@ -542,35 +573,35 @@ class FrameImpl {
542
573
  }
543
574
  }
544
575
  #collectSlots(found) {
545
- let n = this.#firstContent();
546
- const end = this.#end;
547
- while (n && n !== end) {
548
- const id = slotStartId(n);
549
- if (id !== null) {
550
- if (!found.has(id)) found.set(id, n);
551
- n = afterRange(n, id);
552
- continue;
553
- }
554
- if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n, found);
555
- n = n.nextSibling;
556
- }
576
+ collectSlots(this.#firstContent(), this.#end, found);
557
577
  }
558
578
  #findPlaceholder(name) {
559
- const id = placeholderId(name);
560
- let n = this.#firstContent();
561
- const end = this.#end;
562
- while (n && n !== end) {
563
- if (isPlaceholderStart(n, id)) return n;
564
- if (n.nodeType === ELEMENT_NODE) {
565
- const found = findPlaceholder(n, id);
566
- if (found) return found;
567
- }
568
- n = n.nextSibling;
579
+ return findPlaceholder(this.#firstContent(), this.#end, placeholderId(name));
580
+ }
581
+ snapshot() {
582
+ if (this.#disposed) return null;
583
+ const records = {
584
+ ...this.#store
585
+ };
586
+ if (!records[""] && this.#element && this.#hasContent) {
587
+ records[""] = {
588
+ kind: "html",
589
+ value: this.#element.innerHTML
590
+ };
569
591
  }
570
- return null;
592
+ if (!records[""] && this.#version === undefined) return null;
593
+ return {
594
+ version: this.#version ?? 0,
595
+ records
596
+ };
571
597
  }
572
598
  dispose() {
573
599
  if (this.#disposed) return;
600
+ const {
601
+ host,
602
+ id
603
+ } = this.#options;
604
+ if (host && id !== undefined) host.unregister(id, this);
574
605
  this.#disposed = true;
575
606
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
576
607
  for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
@@ -581,11 +612,6 @@ class FrameImpl {
581
612
  }
582
613
  this.#slotRegions.clear();
583
614
  this.#mountedSlots.clear();
584
- const {
585
- host,
586
- id
587
- } = this.#options;
588
- if (host && id !== undefined) host.unregister(id, this);
589
615
  }
590
616
  #applyRoot(html) {
591
617
  const fragment = parseFragment(html);
@@ -597,7 +623,9 @@ class FrameImpl {
597
623
  this.#hasContent = true;
598
624
  } else {
599
625
  const claim = claimHandlers() ? this.#claimTree : null;
600
- reconcileChildren(parent, fragment, this.#start, this.#end, claim);
626
+ const ranges = new Map();
627
+ this.#collectSlots(ranges);
628
+ reconcileChildren(parent, fragment, this.#start, this.#end, claim, ranges);
601
629
  }
602
630
  }
603
631
  #clearContent() {
@@ -789,28 +817,26 @@ function rangeClose(start, id) {
789
817
  }
790
818
  return null;
791
819
  }
792
- function findPlaceholder(root, id) {
793
- let n = root.firstChild;
794
- while (n) {
820
+ function findPlaceholder(n, end, id) {
821
+ while (n && n !== end) {
795
822
  if (isPlaceholderStart(n, id)) return n;
796
823
  if (n.nodeType === ELEMENT_NODE) {
797
- const found = findPlaceholder(n, id);
824
+ const found = findPlaceholder(n.firstChild, null, id);
798
825
  if (found) return found;
799
826
  }
800
827
  n = n.nextSibling;
801
828
  }
802
829
  return null;
803
830
  }
804
- function collectSlots(root, out) {
805
- let n = root.firstChild;
806
- while (n) {
831
+ function collectSlots(n, end, out) {
832
+ while (n && n !== end) {
807
833
  const id = slotStartId(n);
808
834
  if (id !== null) {
809
835
  if (!out.has(id)) out.set(id, n);
810
836
  n = afterRange(n, id);
811
837
  continue;
812
838
  }
813
- if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n, out);
839
+ if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n.firstChild, null, out);
814
840
  n = n.nextSibling;
815
841
  }
816
842
  }
@@ -847,15 +873,6 @@ function argsEquivalent(a, b) {
847
873
  }
848
874
  return true;
849
875
  }
850
- function rangeInterior(start, endData) {
851
- const nodes = [];
852
- let n = start.nextSibling;
853
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
854
- nodes.push(n);
855
- n = n.nextSibling;
856
- }
857
- return nodes;
858
- }
859
876
  function slotStartId(node) {
860
877
  if (node.nodeType !== COMMENT_NODE) return null;
861
878
  const m = SLOT_START.exec(node.data);
@@ -901,11 +918,11 @@ function morphAttributes(oldEl, newEl, claim) {
901
918
  }
902
919
  if (claim && (reclaim || changed && oldEl.matches(CLAIMED_ELEMENTS))) claim(oldEl, true);
903
920
  }
904
- function morphNode(oldNode, newNode, claim) {
921
+ function morphNode(oldNode, newNode, claim, ranges) {
905
922
  if (oldNode.nodeType === ELEMENT_NODE) {
906
923
  if (oldNode.hasAttribute("data-preserve")) return;
907
924
  morphAttributes(oldNode, newNode, claim);
908
- reconcileChildren(oldNode, newNode, null, null, claim);
925
+ reconcileChildren(oldNode, newNode, null, null, claim, ranges);
909
926
  } else if (oldNode.data !== newNode.data) {
910
927
  oldNode.data = newNode.data;
911
928
  }
@@ -919,9 +936,6 @@ function afterRange(start, id) {
919
936
  }
920
937
  return null;
921
938
  }
922
- function skipRange(start, id) {
923
- return afterRange(start, id);
924
- }
925
939
  function findRangeStart(from, id, bound) {
926
940
  const target = `slot:${id}:start`;
927
941
  let n = from;
@@ -959,7 +973,7 @@ function adoptRange(parent, start, id, ref, claim) {
959
973
  }
960
974
  return after;
961
975
  }
962
- function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null) {
976
+ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null) {
963
977
  let oldChild = boundStart ? boundStart.nextSibling : parent.firstChild;
964
978
  let newChild = source.firstChild;
965
979
  while (newChild) {
@@ -969,12 +983,18 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
969
983
  if (pid !== null) {
970
984
  if (old && slotStartId(old) === pid) {
971
985
  oldChild = afterRange(old, pid);
972
- newChild = skipRange(newChild, pid);
986
+ newChild = afterRange(newChild, pid);
973
987
  } else {
974
- const existing = findRangeStart(old, pid, boundEnd);
988
+ const displaced = ranges && ranges.get(pid);
989
+ const existing = displaced || findRangeStart(old, pid, boundEnd);
975
990
  if (existing) {
976
- moveRangeBefore(parent, existing, pid, old ?? boundEnd);
977
- newChild = skipRange(newChild, pid);
991
+ if (ranges) ranges.delete(pid);
992
+ if (existing.nodeType === 11 ) {
993
+ parent.insertBefore(existing, old ?? boundEnd);
994
+ } else {
995
+ moveRangeBefore(parent, existing, pid, old ?? boundEnd);
996
+ }
997
+ newChild = afterRange(newChild, pid);
978
998
  } else {
979
999
  newChild = adoptRange(parent, newChild, pid, old ?? boundEnd, claim);
980
1000
  }
@@ -994,7 +1014,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
994
1014
  continue;
995
1015
  }
996
1016
  if (compatible(old, newChild)) {
997
- morphNode(old, newChild, claim);
1017
+ morphNode(old, newChild, claim, ranges);
998
1018
  oldChild = old.nextSibling;
999
1019
  newChild = nextNew;
1000
1020
  continue;
@@ -1007,7 +1027,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1007
1027
  }
1008
1028
  if (ahead && ahead !== boundEnd) {
1009
1029
  parent.insertBefore(ahead, old);
1010
- morphNode(ahead, newChild, claim);
1030
+ morphNode(ahead, newChild, claim, ranges);
1011
1031
  newChild = nextNew;
1012
1032
  continue;
1013
1033
  }
@@ -1018,113 +1038,29 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1018
1038
  }
1019
1039
  while (oldChild && oldChild !== boundEnd) {
1020
1040
  const next = oldChild.nextSibling;
1041
+ const pid = ranges ? slotStartId(oldChild) : null;
1042
+ if (pid !== null && ranges.get(pid) === oldChild) {
1043
+ const frag = document.createDocumentFragment();
1044
+ const after = stashRange(frag, oldChild, pid);
1045
+ ranges.set(pid, frag);
1046
+ oldChild = after;
1047
+ continue;
1048
+ }
1021
1049
  parent.removeChild(oldChild);
1022
1050
  oldChild = next;
1023
1051
  }
1024
1052
  }
1025
-
1026
- seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
1027
- const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
1028
- web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
1029
- web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
1030
- function resolveSerializerPlugins(customPlugins) {
1031
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1032
- }
1033
- const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
1034
- const JSON_CODEC_DEPTH_LIMIT = 64;
1035
- function resolveCodecOptions({
1036
- plugins,
1037
- disabledFeatures,
1038
- depthLimit
1039
- } = {}) {
1040
- return {
1041
- plugins: resolveSerializerPlugins(plugins),
1042
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1043
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1044
- };
1045
- }
1046
- function createJSONDeserializer(options) {
1047
- const refs = new Map();
1048
- const resolved = resolveCodecOptions(options);
1049
- return function deserializeJSONChunk(node) {
1050
- return seroval.fromCrossJSON(node, {
1051
- refs,
1052
- ...resolved
1053
- });
1054
- };
1055
- }
1056
- function createJSONDataTable(options) {
1057
- const deserialize = createJSONDeserializer(options);
1058
- const table = new Map();
1059
- return {
1060
- apply(record) {
1061
- const value = deserialize(record.node);
1062
- if (record.initial) table.set(record.key, value);
1063
- },
1064
- get(key) {
1065
- return table.get(key);
1066
- },
1067
- resolve(ref) {
1068
- return table.get(ref.$ref);
1069
- }
1070
- };
1071
- }
1072
-
1073
- class ChunkReader {
1074
- constructor(stream) {
1075
- this.reader = stream.getReader();
1076
- this.buffer = new Uint8Array(0);
1077
- this.done = false;
1078
- }
1079
- async readChunk() {
1080
- const chunk = await this.reader.read();
1081
- if (!chunk.done) {
1082
- const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
1083
- newBuffer.set(this.buffer);
1084
- newBuffer.set(chunk.value, this.buffer.length);
1085
- this.buffer = newBuffer;
1086
- } else {
1087
- this.done = true;
1088
- }
1089
- }
1090
- async next() {
1091
- while (this.buffer.length < 12) {
1092
- if (this.done) {
1093
- if (this.buffer.length === 0) return {
1094
- done: true,
1095
- value: undefined
1096
- };
1097
- throw new Error("Malformed server function stream.");
1098
- }
1099
- await this.readChunk();
1100
- }
1101
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
1102
- const bytes = Number.parseInt(head, 16);
1103
- if (Number.isNaN(bytes)) {
1104
- throw new Error("Malformed server function stream.");
1105
- }
1106
- while (bytes > this.buffer.length - 12) {
1107
- if (this.done) {
1108
- throw new Error("Malformed server function stream.");
1109
- }
1110
- await this.readChunk();
1111
- }
1112
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
1113
- this.buffer = this.buffer.subarray(12 + bytes);
1114
- return {
1115
- done: false,
1116
- value: partial
1117
- };
1118
- }
1119
- async drain(interpret) {
1120
- while (true) {
1121
- const result = await this.next();
1122
- if (result.done) {
1123
- break;
1124
- }
1125
- interpret(result.value);
1126
- }
1053
+ function stashRange(frag, start, id) {
1054
+ const end = slotEnd(id);
1055
+ let n = start;
1056
+ while (n) {
1057
+ const next = n.nextSibling;
1058
+ const isEnd = n.nodeType === COMMENT_NODE && n.data === end;
1059
+ frag.appendChild(n);
1060
+ if (isEnd) return next;
1061
+ n = next;
1127
1062
  }
1063
+ return null;
1128
1064
  }
1129
1065
 
1130
1066
  const FRAME_STREAM_HEADER = "X-Frame-Stream";
@@ -1135,55 +1071,133 @@ async function applyFrameResponse(response, host, options = {}) {
1135
1071
  const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1136
1072
  const as = options.as;
1137
1073
  const version = options.version;
1138
- const reader = new ChunkReader(response.body);
1074
+ const perFrame = typeof version === "function" ? new Map() : null;
1075
+ const reader = new client.ChunkReader(response.body);
1139
1076
  let result = await reader.next();
1140
1077
  while (!result.done) {
1141
1078
  const chunk = JSON.parse(result.value);
1142
- if (as !== undefined && chunk.id === rootId) chunk.id = as;
1143
- if (version !== undefined) chunk.version = version;
1144
- host.apply(chunk);
1079
+ if (chunk.type === "outcome") {
1080
+ if (options.onOutcome) options.onOutcome(chunk.payload);
1081
+ } else {
1082
+ if (as !== undefined && chunk.id === rootId) chunk.id = as;else if (options.route) chunk.id = options.route(chunk.id);
1083
+ if (perFrame) {
1084
+ let v = perFrame.get(chunk.id);
1085
+ if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
1086
+ chunk.version = v;
1087
+ } else if (version !== undefined) chunk.version = version;
1088
+ host.apply(chunk);
1089
+ }
1145
1090
  result = await reader.next();
1146
1091
  }
1147
1092
  return as !== undefined ? as : rootId;
1148
1093
  }
1094
+ const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1095
+ const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1096
+ let resolveServerComponent;
1097
+ function parseServerComponent(value, ctx) {
1098
+ return {
1099
+ id: ctx.parse(value[SERVER_COMPONENT]),
1100
+ address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1101
+ };
1102
+ }
1103
+ const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
1104
+ tag: "dom-expressions/server-component",
1105
+ test(value) {
1106
+ return typeof value === "function" && SERVER_COMPONENT in value;
1107
+ },
1108
+ parse: {
1109
+ sync: parseServerComponent,
1110
+ async async(value, ctx) {
1111
+ return {
1112
+ id: await ctx.parse(value[SERVER_COMPONENT]),
1113
+ address: await ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1114
+ };
1115
+ },
1116
+ stream: parseServerComponent
1117
+ },
1118
+ serialize(node, ctx) {
1119
+ return "self._$SC.r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1120
+ },
1121
+ deserialize(node, ctx) {
1122
+ const id = ctx.deserialize(node.id);
1123
+ const address = ctx.deserialize(node.address);
1124
+ if (address !== undefined && resolveServerComponent) {
1125
+ return resolveServerComponent(id, address);
1126
+ }
1127
+ return globalThis._$SC.r(id);
1128
+ }
1129
+ });
1130
+ function flightCodec(codec) {
1131
+ const plugins = codec && codec.plugins || [];
1132
+ for (const plugin of plugins) {
1133
+ if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1134
+ }
1135
+ return {
1136
+ ...codec,
1137
+ plugins: [...plugins, ServerComponentPlugin]
1138
+ };
1139
+ }
1149
1140
  function createServerComponentHandler({
1150
1141
  host,
1151
1142
  component,
1152
- capture,
1153
1143
  onStream,
1154
1144
  documentComponent,
1155
- intercept
1145
+ intercept,
1146
+ consumer = client.getFlightDataConsumer,
1147
+ codec = client.getServerFunctionsCodec
1156
1148
  }) {
1157
- const byContext = new WeakMap();
1158
- const byFunction = new Map();
1159
- let boundary = 0;
1149
+ const byAddress = new Map();
1150
+ const boundaryFor = address => {
1151
+ let entry = byAddress.get(address);
1152
+ if (!entry) {
1153
+ entry = {
1154
+ frameId: address,
1155
+ component: component(address)
1156
+ };
1157
+ byAddress.set(address, entry);
1158
+ }
1159
+ return entry;
1160
+ };
1161
+ const resolveAddress = (id, address) => boundaryFor(address).component;
1162
+ resolveServerComponent = resolveAddress;
1163
+ const versions = new Map();
1164
+ const bump = frameId => {
1165
+ const version = (versions.get(frameId) || 0) + 1;
1166
+ versions.set(frameId, version);
1167
+ return version;
1168
+ };
1160
1169
  return {
1161
- capture,
1162
- intercept,
1170
+ intercept: intercept && (info => {
1171
+ const hit = intercept(info);
1172
+ if (hit !== undefined) {
1173
+ const address = client.frameAddress(info.id, info.args);
1174
+ byAddress.set(address, {
1175
+ frameId: info.id,
1176
+ component: hit
1177
+ });
1178
+ }
1179
+ return hit;
1180
+ }),
1163
1181
  handle(response, ctx) {
1164
1182
  if (!isFrameStreamResponse(response)) return undefined;
1165
- const key = ctx.context;
1166
- const keyed = key !== null && typeof key === "object";
1167
- let entry = keyed ? byContext.get(key) : byFunction.get(ctx.id);
1183
+ const address = client.frameAddress(ctx.id, ctx.args);
1184
+ let entry = byAddress.get(address);
1168
1185
  if (!entry) {
1169
1186
  const adopted = documentComponent && documentComponent(ctx.id);
1170
1187
  if (adopted) {
1171
1188
  entry = {
1172
1189
  frameId: ctx.id,
1173
- version: 0,
1174
1190
  component: adopted
1175
1191
  };
1192
+ byAddress.set(address, entry);
1176
1193
  } else {
1177
- const frameId = `sc:${boundary++}`;
1178
- entry = {
1179
- frameId,
1180
- version: 0,
1181
- component: component(frameId)
1182
- };
1194
+ entry = boundaryFor(address);
1183
1195
  }
1184
- keyed ? byContext.set(key, entry) : byFunction.set(ctx.id, entry);
1185
1196
  }
1186
- const version = ++entry.version;
1197
+ if (response.headers.has(client.SINGLE_FLIGHT_HEADER)) {
1198
+ return applyFlightResponse(response, entry);
1199
+ }
1200
+ const version = bump(entry.frameId);
1187
1201
  if (onStream) onStream(entry.frameId, version, response);
1188
1202
  applyFrameResponse(response, host, {
1189
1203
  as: entry.frameId,
@@ -1197,6 +1211,101 @@ function createServerComponentHandler({
1197
1211
  }
1198
1212
  }));
1199
1213
  return entry.component;
1214
+ },
1215
+ showing(address, functionId, component) {
1216
+ if (!byAddress.has(address)) {
1217
+ byAddress.set(address, {
1218
+ frameId: functionId,
1219
+ component,
1220
+ address
1221
+ });
1222
+ }
1223
+ }
1224
+ };
1225
+ async function applyFlightResponse(response, entry) {
1226
+ const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1227
+ const as = rootId ? entry.frameId : undefined;
1228
+ let feed;
1229
+ const source = new ReadableStream({
1230
+ start(controller) {
1231
+ feed = controller;
1232
+ }
1233
+ });
1234
+ const payload = client.deserializeStream(new Response(source), flightCodec(codec()));
1235
+ let carried = false;
1236
+ await applyFrameResponse(response, host, {
1237
+ as,
1238
+ route: id => {
1239
+ const local = byAddress.get(id);
1240
+ return local ? local.frameId : id;
1241
+ },
1242
+ version: frameId => {
1243
+ const version = bump(frameId);
1244
+ if (onStream) onStream(frameId, version, response);
1245
+ return version;
1246
+ },
1247
+ onOutcome: text => {
1248
+ carried = true;
1249
+ feed.enqueue(client.createChunk(text));
1250
+ }
1251
+ });
1252
+ feed.close();
1253
+ if (!carried) throw new Error("Single-flight frame response carried no outcome");
1254
+ const envelope = await payload;
1255
+ const deliver = consumer();
1256
+ if (deliver) await deliver(envelope.data, {
1257
+ response
1258
+ });
1259
+ if (response.headers.has(client.ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(client.REVALIDATE_HEADER)) {
1260
+ throw envelope.value;
1261
+ }
1262
+ return rootId ? entry.component : envelope.value;
1263
+ }
1264
+ }
1265
+
1266
+ seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
1267
+ const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
1268
+ web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
1269
+ web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
1270
+ function resolveSerializerPlugins(customPlugins) {
1271
+ return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1272
+ }
1273
+ const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
1274
+ const JSON_CODEC_DEPTH_LIMIT = 64;
1275
+ function resolveCodecOptions({
1276
+ plugins,
1277
+ disabledFeatures,
1278
+ depthLimit
1279
+ } = {}) {
1280
+ return {
1281
+ plugins: resolveSerializerPlugins(plugins),
1282
+ disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1283
+ depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1284
+ };
1285
+ }
1286
+ function createJSONDeserializer(options) {
1287
+ const refs = new Map();
1288
+ const resolved = resolveCodecOptions(options);
1289
+ return function deserializeJSONChunk(node) {
1290
+ return seroval.fromCrossJSON(node, {
1291
+ refs,
1292
+ ...resolved
1293
+ });
1294
+ };
1295
+ }
1296
+ function createJSONDataTable(options) {
1297
+ const deserialize = createJSONDeserializer(options);
1298
+ const table = new Map();
1299
+ return {
1300
+ apply(record) {
1301
+ const value = deserialize(record.node);
1302
+ if (record.initial) table.set(record.key, value);
1303
+ },
1304
+ get(key) {
1305
+ return table.get(key);
1306
+ },
1307
+ resolve(ref) {
1308
+ return table.get(ref.$ref);
1200
1309
  }
1201
1310
  };
1202
1311
  }
@@ -1250,8 +1359,10 @@ function claimRender(prefix, existing, render) {
1250
1359
  if (!registry.size) return render();
1251
1360
  const prevRegistry = sc.registry;
1252
1361
  const prevHydrating = sc.hydrating;
1362
+ const prevClaimRoots = sc.claimRoots;
1253
1363
  sc.registry = registry;
1254
1364
  sc.hydrating = true;
1365
+ sc.claimRoots = existing;
1255
1366
  try {
1256
1367
  return solidJs.runWithOwner(solidJs.createOwner({
1257
1368
  id: prefix
@@ -1259,13 +1370,28 @@ function claimRender(prefix, existing, render) {
1259
1370
  } finally {
1260
1371
  sc.registry = prevRegistry;
1261
1372
  sc.hydrating = prevHydrating;
1373
+ sc.claimRoots = prevClaimRoots;
1374
+ }
1375
+ }
1376
+ function isReactiveContent(value) {
1377
+ if (typeof value === "function") return true;
1378
+ if (Array.isArray(value)) {
1379
+ for (const v of value) if (isReactiveContent(v)) return true;
1262
1380
  }
1381
+ return false;
1263
1382
  }
1264
1383
  function slotsFor(props) {
1384
+ const bindings = new Map();
1265
1385
  return new Proxy({}, {
1266
1386
  get(_, prop) {
1267
1387
  if (typeof prop !== "string" || !(prop in props)) return undefined;
1268
1388
  return (slotProps, ctx) => {
1389
+ const key = ctx && ctx.key;
1390
+ const prev = key !== undefined && bindings.get(key);
1391
+ if (prev) {
1392
+ bindings.delete(key);
1393
+ prev.dispose();
1394
+ }
1269
1395
  const settle = out => {
1270
1396
  const existing = ctx && ctx.existing || [];
1271
1397
  if (existing.length) {
@@ -1275,14 +1401,37 @@ function slotsFor(props) {
1275
1401
  }
1276
1402
  return out;
1277
1403
  };
1278
- const render = () => {
1404
+ const evaluate = () => {
1279
1405
  const v = props[prop];
1280
- return settle(normalizeSlotContent(typeof v === "function" ? v(slotProps) : v));
1406
+ return typeof v === "function" && ctx && ctx.invoked ? v(slotProps) : v;
1281
1407
  };
1282
- if (ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length) {
1283
- return claimRender(`sc-${ctx.frame}-${ctx.key}-`, ctx.existing, render);
1408
+ const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1409
+ const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1410
+ const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1411
+ if (!isReactiveContent(value)) {
1412
+ return settle(normalizeSlotContent(value));
1284
1413
  }
1285
- return render();
1414
+ if (ctx && ctx.range) {
1415
+ let claim = adopted;
1416
+ const source = value;
1417
+ const accessor = () => {
1418
+ if (claim) {
1419
+ claim = false;
1420
+ return claimRender(prefix, ctx.existing, () => typeof source === "function" ? source() : source);
1421
+ }
1422
+ return typeof source === "function" ? source() : source;
1423
+ };
1424
+ const owner = solidJs.createOwner();
1425
+ bindings.set(key, owner);
1426
+ ctx.onCleanup(() => {
1427
+ if (bindings.get(key) === owner) bindings.delete(key);
1428
+ owner.dispose();
1429
+ });
1430
+ const end = ctx.range.end;
1431
+ solidJs.runWithOwner(owner, () => web$1.insert(end.parentNode, accessor, end, [...ctx.existing]));
1432
+ return undefined;
1433
+ }
1434
+ return settle(normalizeSlotContent(value));
1286
1435
  };
1287
1436
  }
1288
1437
  });
@@ -1290,6 +1439,9 @@ function slotsFor(props) {
1290
1439
  function revealSeam(owner) {
1291
1440
  return seam => solidJs.runWithOwner(owner, () => web$1.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1292
1441
  }
1442
+ function boundaryScope(owner) {
1443
+ return fn => solidJs.getOwner() ? fn() : solidJs.runWithOwner(owner, fn);
1444
+ }
1293
1445
  function boundaryComponent(host, id) {
1294
1446
  return props => {
1295
1447
  const owner = solidJs.getOwner();
@@ -1300,7 +1452,7 @@ function boundaryComponent(host, id) {
1300
1452
  host,
1301
1453
  id,
1302
1454
  slots: slotsFor(props),
1303
- ownerScope: fn => solidJs.runWithOwner(owner, fn),
1455
+ ownerScope: boundaryScope(owner),
1304
1456
  reveal: revealSeam(owner)
1305
1457
  });
1306
1458
  solidJs.onCleanup(dispose);
@@ -1394,7 +1546,7 @@ function adoptBoundary(host, id, el, props) {
1394
1546
  host,
1395
1547
  id,
1396
1548
  slots: slotsFor(props),
1397
- ownerScope: fn => solidJs.runWithOwner(owner, fn),
1549
+ ownerScope: boundaryScope(owner),
1398
1550
  reveal: revealSeam(owner)
1399
1551
  });
1400
1552
  solidJs.onCleanup(() => frame.dispose());
@@ -1405,27 +1557,36 @@ function installServerComponents(host = getFrameHost()) {
1405
1557
  if (!g._$SC) {
1406
1558
  g._$SC = {
1407
1559
  c: {},
1408
- r(i) {
1560
+ a: {},
1561
+ r(i, a) {
1562
+ if (a) {
1563
+ g._$SC.a[a] = i;
1564
+ g._$SC.reg && g._$SC.reg(a, i);
1565
+ }
1409
1566
  return g._$SC.c[i] || (g._$SC.c[i] = p => g._$SC.impl(i, p));
1410
1567
  }
1411
1568
  };
1412
1569
  }
1413
1570
  g._$SC.impl = (id, props) => documentBoundary(host, id, props);
1414
1571
  installRevealHook();
1572
+ const handler = createServerComponentHandler({
1573
+ host,
1574
+ component: frameId => boundaryComponent(host, frameId),
1575
+ onStream: frameId => beginStream(frameId),
1576
+ documentComponent: functionId => claimedBoundaries.has(functionId) ? undefined : g._$SC.c[functionId],
1577
+ intercept: ({
1578
+ id
1579
+ }) => {
1580
+ if (claimedBoundaries.has(id) || !findBoundaryElement(id)) return undefined;
1581
+ return g._$SC.r(id);
1582
+ }
1583
+ });
1584
+ const showing = (address, id) => handler.showing(address, id, g._$SC.r(id));
1585
+ const records = g._$SC.a || (g._$SC.a = {});
1586
+ for (const address in records) showing(address, records[address]);
1587
+ g._$SC.reg = showing;
1415
1588
  client.configureServerFunctionsClient({
1416
- responseHandler: createServerComponentHandler({
1417
- host,
1418
- capture: () => solidJs.getOwner() ?? undefined,
1419
- component: frameId => boundaryComponent(host, frameId),
1420
- onStream: frameId => beginStream(frameId),
1421
- documentComponent: functionId => g._$SC.c[functionId],
1422
- intercept: ({
1423
- id
1424
- }) => {
1425
- if (claimedBoundaries.has(id) || !findBoundaryElement(id)) return undefined;
1426
- return g._$SC.r(id);
1427
- }
1428
- })
1589
+ responseHandler: handler
1429
1590
  });
1430
1591
  }
1431
1592