@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.
- package/dist/dev.cjs +40 -2
- package/dist/dev.js +39 -4
- package/dist/server.cjs +113 -37
- package/dist/server.js +112 -39
- package/dist/web.cjs +40 -2
- package/dist/web.js +39 -4
- package/frames/dist/client.cjs +370 -209
- package/frames/dist/client.dev.cjs +370 -210
- package/frames/dist/client.dev.js +371 -211
- package/frames/dist/client.js +371 -210
- package/frames/dist/server.cjs +373 -74
- package/frames/dist/server.js +373 -75
- package/package.json +3 -3
- package/server-functions/dist/client.cjs +100 -3
- package/server-functions/dist/client.js +92 -4
- package/server-functions/dist/server.cjs +85 -17
- package/server-functions/dist/server.js +83 -17
- package/types/client.d.ts +15 -0
- package/types/core.d.ts +1 -1
- package/types/frames/client.d.ts +8 -5
- package/types/frames/frame-client.d.ts +17 -0
- package/types/frames/frame-sink.d.ts +29 -6
- package/types/frames/frame-transport.d.ts +76 -12
- package/types/frames/server.d.ts +29 -1
- package/types/index.d.ts +74 -0
- package/types/server-functions/client.d.ts +25 -0
- package/types/server-functions/server.d.ts +105 -18
- package/types/server-functions/shared.d.ts +22 -0
- package/types/server-mock.d.ts +6 -2
- package/types/server.d.ts +39 -1
- package/types-cjs/client.d.cts +15 -0
- package/types-cjs/core.d.cts +1 -1
- package/types-cjs/frames/client.d.cts +8 -5
- package/types-cjs/frames/frame-client.d.cts +17 -0
- package/types-cjs/frames/frame-sink.d.cts +29 -6
- package/types-cjs/frames/frame-transport.d.cts +76 -12
- package/types-cjs/frames/server.d.cts +29 -1
- package/types-cjs/index.d.cts +74 -0
- package/types-cjs/server-functions/client.d.cts +25 -0
- package/types-cjs/server-functions/server.d.cts +105 -18
- package/types-cjs/server-functions/shared.d.cts +22 -0
- package/types-cjs/server-mock.d.cts +6 -2
- package/types-cjs/server.d.cts +39 -1
package/frames/dist/client.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
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(
|
|
793
|
-
|
|
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(
|
|
805
|
-
|
|
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 =
|
|
986
|
+
newChild = afterRange(newChild, pid);
|
|
973
987
|
} else {
|
|
974
|
-
const
|
|
988
|
+
const displaced = ranges && ranges.get(pid);
|
|
989
|
+
const existing = displaced || findRangeStart(old, pid, boundEnd);
|
|
975
990
|
if (existing) {
|
|
976
|
-
|
|
977
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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
|
|
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 (
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
|
1158
|
-
const
|
|
1159
|
-
|
|
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
|
-
|
|
1162
|
-
|
|
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
|
|
1166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1404
|
+
const evaluate = () => {
|
|
1279
1405
|
const v = props[prop];
|
|
1280
|
-
return
|
|
1406
|
+
return typeof v === "function" && ctx && ctx.invoked ? v(slotProps) : v;
|
|
1281
1407
|
};
|
|
1282
|
-
|
|
1283
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
|