@solidjs/web 2.0.0-beta.28 → 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 +90 -31
  4. package/dist/server.js +89 -33
  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 +351 -69
  12. package/frames/dist/server.js +351 -70
  13. package/package.json +3 -3
  14. package/server-functions/dist/client.cjs +87 -3
  15. package/server-functions/dist/client.js +80 -4
  16. package/server-functions/dist/server.cjs +52 -17
  17. package/server-functions/dist/server.js +51 -17
  18. package/types/client.d.ts +15 -0
  19. package/types/core.d.ts +1 -1
  20. package/types/frames/client.d.ts +7 -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 +1 -1
  25. package/types/index.d.ts +74 -0
  26. package/types/server-functions/client.d.ts +24 -0
  27. package/types/server-functions/server.d.ts +75 -16
  28. package/types/server-functions/shared.d.ts +9 -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 +7 -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 +1 -1
  38. package/types-cjs/index.d.cts +74 -0
  39. package/types-cjs/server-functions/client.d.cts +24 -0
  40. package/types-cjs/server-functions/server.d.cts +75 -16
  41. package/types-cjs/server-functions/shared.d.cts +9 -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,36 +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
- devCheckRange(n, id);
551
- if (!found.has(id)) found.set(id, n);
552
- n = afterRange(n, id);
553
- continue;
554
- }
555
- if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n, found);
556
- n = n.nextSibling;
557
- }
576
+ collectSlots(this.#firstContent(), this.#end, found);
558
577
  }
559
578
  #findPlaceholder(name) {
560
- const id = placeholderId(name);
561
- let n = this.#firstContent();
562
- const end = this.#end;
563
- while (n && n !== end) {
564
- if (isPlaceholderStart(n, id)) return n;
565
- if (n.nodeType === ELEMENT_NODE) {
566
- const found = findPlaceholder(n, id);
567
- if (found) return found;
568
- }
569
- 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
+ };
570
591
  }
571
- return null;
592
+ if (!records[""] && this.#version === undefined) return null;
593
+ return {
594
+ version: this.#version ?? 0,
595
+ records
596
+ };
572
597
  }
573
598
  dispose() {
574
599
  if (this.#disposed) return;
600
+ const {
601
+ host,
602
+ id
603
+ } = this.#options;
604
+ if (host && id !== undefined) host.unregister(id, this);
575
605
  this.#disposed = true;
576
606
  for (const key of [...this.#slotCleanups.keys()]) this.#runSlotCleanups(key);
577
607
  for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
@@ -582,11 +612,6 @@ class FrameImpl {
582
612
  }
583
613
  this.#slotRegions.clear();
584
614
  this.#mountedSlots.clear();
585
- const {
586
- host,
587
- id
588
- } = this.#options;
589
- if (host && id !== undefined) host.unregister(id, this);
590
615
  }
591
616
  #applyRoot(html) {
592
617
  const fragment = parseFragment(html);
@@ -598,7 +623,9 @@ class FrameImpl {
598
623
  this.#hasContent = true;
599
624
  } else {
600
625
  const claim = claimHandlers() ? this.#claimTree : null;
601
- 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);
602
629
  }
603
630
  }
604
631
  #clearContent() {
@@ -793,21 +820,19 @@ function rangeClose(start, id) {
793
820
  }
794
821
  return null;
795
822
  }
796
- function findPlaceholder(root, id) {
797
- let n = root.firstChild;
798
- while (n) {
823
+ function findPlaceholder(n, end, id) {
824
+ while (n && n !== end) {
799
825
  if (isPlaceholderStart(n, id)) return n;
800
826
  if (n.nodeType === ELEMENT_NODE) {
801
- const found = findPlaceholder(n, id);
827
+ const found = findPlaceholder(n.firstChild, null, id);
802
828
  if (found) return found;
803
829
  }
804
830
  n = n.nextSibling;
805
831
  }
806
832
  return null;
807
833
  }
808
- function collectSlots(root, out) {
809
- let n = root.firstChild;
810
- while (n) {
834
+ function collectSlots(n, end, out) {
835
+ while (n && n !== end) {
811
836
  const id = slotStartId(n);
812
837
  if (id !== null) {
813
838
  devCheckRange(n, id);
@@ -815,7 +840,7 @@ function collectSlots(root, out) {
815
840
  n = afterRange(n, id);
816
841
  continue;
817
842
  }
818
- if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n, out);
843
+ if (n.nodeType === ELEMENT_NODE && !isFrameElement(n)) collectSlots(n.firstChild, null, out);
819
844
  n = n.nextSibling;
820
845
  }
821
846
  }
@@ -852,15 +877,6 @@ function argsEquivalent(a, b) {
852
877
  }
853
878
  return true;
854
879
  }
855
- function rangeInterior(start, endData) {
856
- const nodes = [];
857
- let n = start.nextSibling;
858
- while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
859
- nodes.push(n);
860
- n = n.nextSibling;
861
- }
862
- return nodes;
863
- }
864
880
  function slotStartId(node) {
865
881
  if (node.nodeType !== COMMENT_NODE) return null;
866
882
  const m = SLOT_START.exec(node.data);
@@ -906,11 +922,11 @@ function morphAttributes(oldEl, newEl, claim) {
906
922
  }
907
923
  if (claim && (reclaim || changed && oldEl.matches(CLAIMED_ELEMENTS))) claim(oldEl, true);
908
924
  }
909
- function morphNode(oldNode, newNode, claim) {
925
+ function morphNode(oldNode, newNode, claim, ranges) {
910
926
  if (oldNode.nodeType === ELEMENT_NODE) {
911
927
  if (oldNode.hasAttribute("data-preserve")) return;
912
928
  morphAttributes(oldNode, newNode, claim);
913
- reconcileChildren(oldNode, newNode, null, null, claim);
929
+ reconcileChildren(oldNode, newNode, null, null, claim, ranges);
914
930
  } else if (oldNode.data !== newNode.data) {
915
931
  oldNode.data = newNode.data;
916
932
  }
@@ -933,9 +949,6 @@ function devCheckRange(start, id) {
933
949
  }
934
950
  console.error(`Frame slot range "${id}" is missing its end marker (<!--${end}-->) among its start ` + `marker's siblings. Slots after it in this content cannot be discovered. Likely causes: ` + `invalid HTML nesting split the range during parsing (e.g. a block element inside <p>), ` + `or an HTML-rewriting layer (CDN/minifier/translator) removed or moved the comment — ` + `serve frame documents with Cache-Control: no-transform.`, start);
935
951
  }
936
- function skipRange(start, id) {
937
- return afterRange(start, id);
938
- }
939
952
  function findRangeStart(from, id, bound) {
940
953
  const target = `slot:${id}:start`;
941
954
  let n = from;
@@ -973,7 +986,7 @@ function adoptRange(parent, start, id, ref, claim) {
973
986
  }
974
987
  return after;
975
988
  }
976
- function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null) {
989
+ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, claim = null, ranges = null) {
977
990
  let oldChild = boundStart ? boundStart.nextSibling : parent.firstChild;
978
991
  let newChild = source.firstChild;
979
992
  while (newChild) {
@@ -983,12 +996,18 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
983
996
  if (pid !== null) {
984
997
  if (old && slotStartId(old) === pid) {
985
998
  oldChild = afterRange(old, pid);
986
- newChild = skipRange(newChild, pid);
999
+ newChild = afterRange(newChild, pid);
987
1000
  } else {
988
- const existing = findRangeStart(old, pid, boundEnd);
1001
+ const displaced = ranges && ranges.get(pid);
1002
+ const existing = displaced || findRangeStart(old, pid, boundEnd);
989
1003
  if (existing) {
990
- moveRangeBefore(parent, existing, pid, old ?? boundEnd);
991
- newChild = skipRange(newChild, pid);
1004
+ if (ranges) ranges.delete(pid);
1005
+ if (existing.nodeType === 11 ) {
1006
+ parent.insertBefore(existing, old ?? boundEnd);
1007
+ } else {
1008
+ moveRangeBefore(parent, existing, pid, old ?? boundEnd);
1009
+ }
1010
+ newChild = afterRange(newChild, pid);
992
1011
  } else {
993
1012
  newChild = adoptRange(parent, newChild, pid, old ?? boundEnd, claim);
994
1013
  }
@@ -1008,7 +1027,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1008
1027
  continue;
1009
1028
  }
1010
1029
  if (compatible(old, newChild)) {
1011
- morphNode(old, newChild, claim);
1030
+ morphNode(old, newChild, claim, ranges);
1012
1031
  oldChild = old.nextSibling;
1013
1032
  newChild = nextNew;
1014
1033
  continue;
@@ -1021,7 +1040,7 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1021
1040
  }
1022
1041
  if (ahead && ahead !== boundEnd) {
1023
1042
  parent.insertBefore(ahead, old);
1024
- morphNode(ahead, newChild, claim);
1043
+ morphNode(ahead, newChild, claim, ranges);
1025
1044
  newChild = nextNew;
1026
1045
  continue;
1027
1046
  }
@@ -1032,113 +1051,29 @@ function reconcileChildren(parent, source, boundStart = null, boundEnd = null, c
1032
1051
  }
1033
1052
  while (oldChild && oldChild !== boundEnd) {
1034
1053
  const next = oldChild.nextSibling;
1054
+ const pid = ranges ? slotStartId(oldChild) : null;
1055
+ if (pid !== null && ranges.get(pid) === oldChild) {
1056
+ const frag = document.createDocumentFragment();
1057
+ const after = stashRange(frag, oldChild, pid);
1058
+ ranges.set(pid, frag);
1059
+ oldChild = after;
1060
+ continue;
1061
+ }
1035
1062
  parent.removeChild(oldChild);
1036
1063
  oldChild = next;
1037
1064
  }
1038
1065
  }
1039
-
1040
- seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
1041
- const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
1042
- web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
1043
- web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
1044
- function resolveSerializerPlugins(customPlugins) {
1045
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1046
- }
1047
- const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
1048
- const JSON_CODEC_DEPTH_LIMIT = 64;
1049
- function resolveCodecOptions({
1050
- plugins,
1051
- disabledFeatures,
1052
- depthLimit
1053
- } = {}) {
1054
- return {
1055
- plugins: resolveSerializerPlugins(plugins),
1056
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1057
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1058
- };
1059
- }
1060
- function createJSONDeserializer(options) {
1061
- const refs = new Map();
1062
- const resolved = resolveCodecOptions(options);
1063
- return function deserializeJSONChunk(node) {
1064
- return seroval.fromCrossJSON(node, {
1065
- refs,
1066
- ...resolved
1067
- });
1068
- };
1069
- }
1070
- function createJSONDataTable(options) {
1071
- const deserialize = createJSONDeserializer(options);
1072
- const table = new Map();
1073
- return {
1074
- apply(record) {
1075
- const value = deserialize(record.node);
1076
- if (record.initial) table.set(record.key, value);
1077
- },
1078
- get(key) {
1079
- return table.get(key);
1080
- },
1081
- resolve(ref) {
1082
- return table.get(ref.$ref);
1083
- }
1084
- };
1085
- }
1086
-
1087
- class ChunkReader {
1088
- constructor(stream) {
1089
- this.reader = stream.getReader();
1090
- this.buffer = new Uint8Array(0);
1091
- this.done = false;
1092
- }
1093
- async readChunk() {
1094
- const chunk = await this.reader.read();
1095
- if (!chunk.done) {
1096
- const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
1097
- newBuffer.set(this.buffer);
1098
- newBuffer.set(chunk.value, this.buffer.length);
1099
- this.buffer = newBuffer;
1100
- } else {
1101
- this.done = true;
1102
- }
1103
- }
1104
- async next() {
1105
- while (this.buffer.length < 12) {
1106
- if (this.done) {
1107
- if (this.buffer.length === 0) return {
1108
- done: true,
1109
- value: undefined
1110
- };
1111
- throw new Error("Malformed server function stream.");
1112
- }
1113
- await this.readChunk();
1114
- }
1115
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
1116
- const bytes = Number.parseInt(head, 16);
1117
- if (Number.isNaN(bytes)) {
1118
- throw new Error("Malformed server function stream.");
1119
- }
1120
- while (bytes > this.buffer.length - 12) {
1121
- if (this.done) {
1122
- throw new Error("Malformed server function stream.");
1123
- }
1124
- await this.readChunk();
1125
- }
1126
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
1127
- this.buffer = this.buffer.subarray(12 + bytes);
1128
- return {
1129
- done: false,
1130
- value: partial
1131
- };
1132
- }
1133
- async drain(interpret) {
1134
- while (true) {
1135
- const result = await this.next();
1136
- if (result.done) {
1137
- break;
1138
- }
1139
- interpret(result.value);
1140
- }
1066
+ function stashRange(frag, start, id) {
1067
+ const end = slotEnd(id);
1068
+ let n = start;
1069
+ while (n) {
1070
+ const next = n.nextSibling;
1071
+ const isEnd = n.nodeType === COMMENT_NODE && n.data === end;
1072
+ frag.appendChild(n);
1073
+ if (isEnd) return next;
1074
+ n = next;
1141
1075
  }
1076
+ return null;
1142
1077
  }
1143
1078
 
1144
1079
  const FRAME_STREAM_HEADER = "X-Frame-Stream";
@@ -1149,55 +1084,133 @@ async function applyFrameResponse(response, host, options = {}) {
1149
1084
  const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1150
1085
  const as = options.as;
1151
1086
  const version = options.version;
1152
- const reader = new ChunkReader(response.body);
1087
+ const perFrame = typeof version === "function" ? new Map() : null;
1088
+ const reader = new client.ChunkReader(response.body);
1153
1089
  let result = await reader.next();
1154
1090
  while (!result.done) {
1155
1091
  const chunk = JSON.parse(result.value);
1156
- if (as !== undefined && chunk.id === rootId) chunk.id = as;
1157
- if (version !== undefined) chunk.version = version;
1158
- host.apply(chunk);
1092
+ if (chunk.type === "outcome") {
1093
+ if (options.onOutcome) options.onOutcome(chunk.payload);
1094
+ } else {
1095
+ if (as !== undefined && chunk.id === rootId) chunk.id = as;else if (options.route) chunk.id = options.route(chunk.id);
1096
+ if (perFrame) {
1097
+ let v = perFrame.get(chunk.id);
1098
+ if (v === undefined) perFrame.set(chunk.id, v = version(chunk.id));
1099
+ chunk.version = v;
1100
+ } else if (version !== undefined) chunk.version = version;
1101
+ host.apply(chunk);
1102
+ }
1159
1103
  result = await reader.next();
1160
1104
  }
1161
1105
  return as !== undefined ? as : rootId;
1162
1106
  }
1107
+ const SERVER_COMPONENT = /*#__PURE__*/Symbol.for("dom-expressions.server-component");
1108
+ const SERVER_COMPONENT_ADDRESS = /*#__PURE__*/Symbol.for("dom-expressions.server-component-address");
1109
+ let resolveServerComponent;
1110
+ function parseServerComponent(value, ctx) {
1111
+ return {
1112
+ id: ctx.parse(value[SERVER_COMPONENT]),
1113
+ address: ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1114
+ };
1115
+ }
1116
+ const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
1117
+ tag: "dom-expressions/server-component",
1118
+ test(value) {
1119
+ return typeof value === "function" && SERVER_COMPONENT in value;
1120
+ },
1121
+ parse: {
1122
+ sync: parseServerComponent,
1123
+ async async(value, ctx) {
1124
+ return {
1125
+ id: await ctx.parse(value[SERVER_COMPONENT]),
1126
+ address: await ctx.parse(value[SERVER_COMPONENT_ADDRESS])
1127
+ };
1128
+ },
1129
+ stream: parseServerComponent
1130
+ },
1131
+ serialize(node, ctx) {
1132
+ return "self._$SC.r(" + ctx.serialize(node.id) + "," + ctx.serialize(node.address) + ")";
1133
+ },
1134
+ deserialize(node, ctx) {
1135
+ const id = ctx.deserialize(node.id);
1136
+ const address = ctx.deserialize(node.address);
1137
+ if (address !== undefined && resolveServerComponent) {
1138
+ return resolveServerComponent(id, address);
1139
+ }
1140
+ return globalThis._$SC.r(id);
1141
+ }
1142
+ });
1143
+ function flightCodec(codec) {
1144
+ const plugins = codec && codec.plugins || [];
1145
+ for (const plugin of plugins) {
1146
+ if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
1147
+ }
1148
+ return {
1149
+ ...codec,
1150
+ plugins: [...plugins, ServerComponentPlugin]
1151
+ };
1152
+ }
1163
1153
  function createServerComponentHandler({
1164
1154
  host,
1165
1155
  component,
1166
- capture,
1167
1156
  onStream,
1168
1157
  documentComponent,
1169
- intercept
1158
+ intercept,
1159
+ consumer = client.getFlightDataConsumer,
1160
+ codec = client.getServerFunctionsCodec
1170
1161
  }) {
1171
- const byContext = new WeakMap();
1172
- const byFunction = new Map();
1173
- let boundary = 0;
1162
+ const byAddress = new Map();
1163
+ const boundaryFor = address => {
1164
+ let entry = byAddress.get(address);
1165
+ if (!entry) {
1166
+ entry = {
1167
+ frameId: address,
1168
+ component: component(address)
1169
+ };
1170
+ byAddress.set(address, entry);
1171
+ }
1172
+ return entry;
1173
+ };
1174
+ const resolveAddress = (id, address) => boundaryFor(address).component;
1175
+ resolveServerComponent = resolveAddress;
1176
+ const versions = new Map();
1177
+ const bump = frameId => {
1178
+ const version = (versions.get(frameId) || 0) + 1;
1179
+ versions.set(frameId, version);
1180
+ return version;
1181
+ };
1174
1182
  return {
1175
- capture,
1176
- intercept,
1183
+ intercept: intercept && (info => {
1184
+ const hit = intercept(info);
1185
+ if (hit !== undefined) {
1186
+ const address = client.frameAddress(info.id, info.args);
1187
+ byAddress.set(address, {
1188
+ frameId: info.id,
1189
+ component: hit
1190
+ });
1191
+ }
1192
+ return hit;
1193
+ }),
1177
1194
  handle(response, ctx) {
1178
1195
  if (!isFrameStreamResponse(response)) return undefined;
1179
- const key = ctx.context;
1180
- const keyed = key !== null && typeof key === "object";
1181
- let entry = keyed ? byContext.get(key) : byFunction.get(ctx.id);
1196
+ const address = client.frameAddress(ctx.id, ctx.args);
1197
+ let entry = byAddress.get(address);
1182
1198
  if (!entry) {
1183
1199
  const adopted = documentComponent && documentComponent(ctx.id);
1184
1200
  if (adopted) {
1185
1201
  entry = {
1186
1202
  frameId: ctx.id,
1187
- version: 0,
1188
1203
  component: adopted
1189
1204
  };
1205
+ byAddress.set(address, entry);
1190
1206
  } else {
1191
- const frameId = `sc:${boundary++}`;
1192
- entry = {
1193
- frameId,
1194
- version: 0,
1195
- component: component(frameId)
1196
- };
1207
+ entry = boundaryFor(address);
1197
1208
  }
1198
- keyed ? byContext.set(key, entry) : byFunction.set(ctx.id, entry);
1199
1209
  }
1200
- const version = ++entry.version;
1210
+ if (response.headers.has(client.SINGLE_FLIGHT_HEADER)) {
1211
+ return applyFlightResponse(response, entry);
1212
+ }
1213
+ const version = bump(entry.frameId);
1201
1214
  if (onStream) onStream(entry.frameId, version, response);
1202
1215
  applyFrameResponse(response, host, {
1203
1216
  as: entry.frameId,
@@ -1211,6 +1224,101 @@ function createServerComponentHandler({
1211
1224
  }
1212
1225
  }));
1213
1226
  return entry.component;
1227
+ },
1228
+ showing(address, functionId, component) {
1229
+ if (!byAddress.has(address)) {
1230
+ byAddress.set(address, {
1231
+ frameId: functionId,
1232
+ component,
1233
+ address
1234
+ });
1235
+ }
1236
+ }
1237
+ };
1238
+ async function applyFlightResponse(response, entry) {
1239
+ const rootId = response.headers.get(FRAME_STREAM_HEADER) ?? "";
1240
+ const as = rootId ? entry.frameId : undefined;
1241
+ let feed;
1242
+ const source = new ReadableStream({
1243
+ start(controller) {
1244
+ feed = controller;
1245
+ }
1246
+ });
1247
+ const payload = client.deserializeStream(new Response(source), flightCodec(codec()));
1248
+ let carried = false;
1249
+ await applyFrameResponse(response, host, {
1250
+ as,
1251
+ route: id => {
1252
+ const local = byAddress.get(id);
1253
+ return local ? local.frameId : id;
1254
+ },
1255
+ version: frameId => {
1256
+ const version = bump(frameId);
1257
+ if (onStream) onStream(frameId, version, response);
1258
+ return version;
1259
+ },
1260
+ onOutcome: text => {
1261
+ carried = true;
1262
+ feed.enqueue(client.createChunk(text));
1263
+ }
1264
+ });
1265
+ feed.close();
1266
+ if (!carried) throw new Error("Single-flight frame response carried no outcome");
1267
+ const envelope = await payload;
1268
+ const deliver = consumer();
1269
+ if (deliver) await deliver(envelope.data, {
1270
+ response
1271
+ });
1272
+ if (response.headers.has(client.ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(client.REVALIDATE_HEADER)) {
1273
+ throw envelope.value;
1274
+ }
1275
+ return rootId ? entry.component : envelope.value;
1276
+ }
1277
+ }
1278
+
1279
+ seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
1280
+ const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
1281
+ web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
1282
+ web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
1283
+ function resolveSerializerPlugins(customPlugins) {
1284
+ return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
1285
+ }
1286
+ const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
1287
+ const JSON_CODEC_DEPTH_LIMIT = 64;
1288
+ function resolveCodecOptions({
1289
+ plugins,
1290
+ disabledFeatures,
1291
+ depthLimit
1292
+ } = {}) {
1293
+ return {
1294
+ plugins: resolveSerializerPlugins(plugins),
1295
+ disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
1296
+ depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
1297
+ };
1298
+ }
1299
+ function createJSONDeserializer(options) {
1300
+ const refs = new Map();
1301
+ const resolved = resolveCodecOptions(options);
1302
+ return function deserializeJSONChunk(node) {
1303
+ return seroval.fromCrossJSON(node, {
1304
+ refs,
1305
+ ...resolved
1306
+ });
1307
+ };
1308
+ }
1309
+ function createJSONDataTable(options) {
1310
+ const deserialize = createJSONDeserializer(options);
1311
+ const table = new Map();
1312
+ return {
1313
+ apply(record) {
1314
+ const value = deserialize(record.node);
1315
+ if (record.initial) table.set(record.key, value);
1316
+ },
1317
+ get(key) {
1318
+ return table.get(key);
1319
+ },
1320
+ resolve(ref) {
1321
+ return table.get(ref.$ref);
1214
1322
  }
1215
1323
  };
1216
1324
  }
@@ -1264,8 +1372,10 @@ function claimRender(prefix, existing, render) {
1264
1372
  if (!registry.size) return render();
1265
1373
  const prevRegistry = sc.registry;
1266
1374
  const prevHydrating = sc.hydrating;
1375
+ const prevClaimRoots = sc.claimRoots;
1267
1376
  sc.registry = registry;
1268
1377
  sc.hydrating = true;
1378
+ sc.claimRoots = existing;
1269
1379
  try {
1270
1380
  return solidJs.runWithOwner(solidJs.createOwner({
1271
1381
  id: prefix
@@ -1273,13 +1383,28 @@ function claimRender(prefix, existing, render) {
1273
1383
  } finally {
1274
1384
  sc.registry = prevRegistry;
1275
1385
  sc.hydrating = prevHydrating;
1386
+ sc.claimRoots = prevClaimRoots;
1387
+ }
1388
+ }
1389
+ function isReactiveContent(value) {
1390
+ if (typeof value === "function") return true;
1391
+ if (Array.isArray(value)) {
1392
+ for (const v of value) if (isReactiveContent(v)) return true;
1276
1393
  }
1394
+ return false;
1277
1395
  }
1278
1396
  function slotsFor(props) {
1397
+ const bindings = new Map();
1279
1398
  return new Proxy({}, {
1280
1399
  get(_, prop) {
1281
1400
  if (typeof prop !== "string" || !(prop in props)) return undefined;
1282
1401
  return (slotProps, ctx) => {
1402
+ const key = ctx && ctx.key;
1403
+ const prev = key !== undefined && bindings.get(key);
1404
+ if (prev) {
1405
+ bindings.delete(key);
1406
+ prev.dispose();
1407
+ }
1283
1408
  const settle = out => {
1284
1409
  const existing = ctx && ctx.existing || [];
1285
1410
  if (existing.length) {
@@ -1289,14 +1414,37 @@ function slotsFor(props) {
1289
1414
  }
1290
1415
  return out;
1291
1416
  };
1292
- const render = () => {
1417
+ const evaluate = () => {
1293
1418
  const v = props[prop];
1294
- return settle(normalizeSlotContent(typeof v === "function" ? v(slotProps) : v));
1419
+ return typeof v === "function" && ctx && ctx.invoked ? v(slotProps) : v;
1295
1420
  };
1296
- if (ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length) {
1297
- return claimRender(`sc-${ctx.frame}-${ctx.key}-`, ctx.existing, render);
1421
+ const adopted = !!(ctx && ctx.frame && ctx.adopted && ctx.existing && ctx.existing.length);
1422
+ const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
1423
+ const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
1424
+ if (!isReactiveContent(value)) {
1425
+ return settle(normalizeSlotContent(value));
1298
1426
  }
1299
- return render();
1427
+ if (ctx && ctx.range) {
1428
+ let claim = adopted;
1429
+ const source = value;
1430
+ const accessor = () => {
1431
+ if (claim) {
1432
+ claim = false;
1433
+ return claimRender(prefix, ctx.existing, () => typeof source === "function" ? source() : source);
1434
+ }
1435
+ return typeof source === "function" ? source() : source;
1436
+ };
1437
+ const owner = solidJs.createOwner();
1438
+ bindings.set(key, owner);
1439
+ ctx.onCleanup(() => {
1440
+ if (bindings.get(key) === owner) bindings.delete(key);
1441
+ owner.dispose();
1442
+ });
1443
+ const end = ctx.range.end;
1444
+ solidJs.runWithOwner(owner, () => web$1.insert(end.parentNode, accessor, end, [...ctx.existing]));
1445
+ return undefined;
1446
+ }
1447
+ return settle(normalizeSlotContent(value));
1300
1448
  };
1301
1449
  }
1302
1450
  });
@@ -1304,6 +1452,9 @@ function slotsFor(props) {
1304
1452
  function revealSeam(owner) {
1305
1453
  return seam => solidJs.runWithOwner(owner, () => web$1.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
1306
1454
  }
1455
+ function boundaryScope(owner) {
1456
+ return fn => solidJs.getOwner() ? fn() : solidJs.runWithOwner(owner, fn);
1457
+ }
1307
1458
  function boundaryComponent(host, id) {
1308
1459
  return props => {
1309
1460
  const owner = solidJs.getOwner();
@@ -1314,7 +1465,7 @@ function boundaryComponent(host, id) {
1314
1465
  host,
1315
1466
  id,
1316
1467
  slots: slotsFor(props),
1317
- ownerScope: fn => solidJs.runWithOwner(owner, fn),
1468
+ ownerScope: boundaryScope(owner),
1318
1469
  reveal: revealSeam(owner)
1319
1470
  });
1320
1471
  solidJs.onCleanup(dispose);
@@ -1408,7 +1559,7 @@ function adoptBoundary(host, id, el, props) {
1408
1559
  host,
1409
1560
  id,
1410
1561
  slots: slotsFor(props),
1411
- ownerScope: fn => solidJs.runWithOwner(owner, fn),
1562
+ ownerScope: boundaryScope(owner),
1412
1563
  reveal: revealSeam(owner)
1413
1564
  });
1414
1565
  solidJs.onCleanup(() => frame.dispose());
@@ -1419,27 +1570,36 @@ function installServerComponents(host = getFrameHost()) {
1419
1570
  if (!g._$SC) {
1420
1571
  g._$SC = {
1421
1572
  c: {},
1422
- r(i) {
1573
+ a: {},
1574
+ r(i, a) {
1575
+ if (a) {
1576
+ g._$SC.a[a] = i;
1577
+ g._$SC.reg && g._$SC.reg(a, i);
1578
+ }
1423
1579
  return g._$SC.c[i] || (g._$SC.c[i] = p => g._$SC.impl(i, p));
1424
1580
  }
1425
1581
  };
1426
1582
  }
1427
1583
  g._$SC.impl = (id, props) => documentBoundary(host, id, props);
1428
1584
  installRevealHook();
1585
+ const handler = createServerComponentHandler({
1586
+ host,
1587
+ component: frameId => boundaryComponent(host, frameId),
1588
+ onStream: frameId => beginStream(frameId),
1589
+ documentComponent: functionId => claimedBoundaries.has(functionId) ? undefined : g._$SC.c[functionId],
1590
+ intercept: ({
1591
+ id
1592
+ }) => {
1593
+ if (claimedBoundaries.has(id) || !findBoundaryElement(id)) return undefined;
1594
+ return g._$SC.r(id);
1595
+ }
1596
+ });
1597
+ const showing = (address, id) => handler.showing(address, id, g._$SC.r(id));
1598
+ const records = g._$SC.a || (g._$SC.a = {});
1599
+ for (const address in records) showing(address, records[address]);
1600
+ g._$SC.reg = showing;
1429
1601
  client.configureServerFunctionsClient({
1430
- responseHandler: createServerComponentHandler({
1431
- host,
1432
- capture: () => solidJs.getOwner() ?? undefined,
1433
- component: frameId => boundaryComponent(host, frameId),
1434
- onStream: frameId => beginStream(frameId),
1435
- documentComponent: functionId => g._$SC.c[functionId],
1436
- intercept: ({
1437
- id
1438
- }) => {
1439
- if (claimedBoundaries.has(id) || !findBoundaryElement(id)) return undefined;
1440
- return g._$SC.r(id);
1441
- }
1442
- })
1602
+ responseHandler: handler
1443
1603
  });
1444
1604
  }
1445
1605