@solidjs/web 2.0.0-rc.0 → 2.0.0-rc.1
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/README.md +1 -1
- package/dist/dev.cjs +9 -5
- package/dist/dev.js +9 -5
- package/dist/server.cjs +94 -4
- package/dist/server.js +94 -5
- package/dist/web.cjs +6 -5
- package/dist/web.js +6 -5
- package/frames/dist/client.cjs +1 -1
- package/frames/dist/client.dev.cjs +1 -1
- package/frames/dist/client.dev.js +1 -1
- package/frames/dist/client.js +1 -1
- package/frames/dist/server.cjs +92 -3
- package/frames/dist/server.js +92 -3
- package/package.json +4 -3
- package/serialization/dist/decode.cjs +13 -1
- package/serialization/dist/decode.js +13 -1
- package/serialization/dist/serialization.cjs +13 -1
- package/serialization/dist/serialization.js +13 -1
- package/server-functions/dist/client.cjs +150 -3
- package/server-functions/dist/client.js +150 -4
- package/server-functions/dist/server.cjs +125 -31
- package/server-functions/dist/server.dev.cjs +125 -31
- package/server-functions/dist/server.dev.js +124 -32
- package/server-functions/dist/server.js +124 -32
- package/types/index.d.ts +16 -2
- package/types/jsx.d.ts +9 -0
- package/types/server-functions/client.d.ts +30 -0
- package/types/server-functions/server.d.ts +28 -0
- package/types-cjs/index.d.cts +16 -2
- package/types-cjs/jsx.d.cts +9 -0
- package/types-cjs/server-functions/client.d.cts +30 -0
- package/types-cjs/server-functions/server.d.cts +28 -0
package/frames/dist/server.js
CHANGED
|
@@ -1264,7 +1264,7 @@ function renderToStream(code, options = {}) {
|
|
|
1264
1264
|
const styles = collectStreamStyles(key, tracking, headStyles);
|
|
1265
1265
|
const headOps = error ? null : flushHeadFragment(headRegistry, key);
|
|
1266
1266
|
if (headOps) emitHeadOps(key, headOps);
|
|
1267
|
-
sink.fragment(key, value !== undefined ? value : " ", {
|
|
1267
|
+
sink.fragment(key, resolveSSRSelectValues(value !== undefined ? value : " "), {
|
|
1268
1268
|
styles,
|
|
1269
1269
|
revealGroup,
|
|
1270
1270
|
error
|
|
@@ -1356,7 +1356,7 @@ function renderToStream(code, options = {}) {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
serializeRootAssets();
|
|
1358
1358
|
const head = renderShellHead(headRegistry, nonce, k => registry.has(k));
|
|
1359
|
-
sink.shell(html, {
|
|
1359
|
+
sink.shell(resolveSSRSelectValues(html), {
|
|
1360
1360
|
preloads: tracking.emittedAssets,
|
|
1361
1361
|
inlineStyles: tracking.inlineStyles,
|
|
1362
1362
|
tasks,
|
|
@@ -2152,6 +2152,93 @@ function ssr(t) {
|
|
|
2152
2152
|
};
|
|
2153
2153
|
return result;
|
|
2154
2154
|
}
|
|
2155
|
+
function decodeSSREntities(s) {
|
|
2156
|
+
return s.indexOf("&") < 0 ? s : s.replace(/"/g, '"').replace(/</g, "<").replace(/&/g, "&");
|
|
2157
|
+
}
|
|
2158
|
+
const SELECT_VALUE_ATTR = /\svalue="([^"]*)"/;
|
|
2159
|
+
function tagEnd(html, i) {
|
|
2160
|
+
for (let j = i + 1; j < html.length; j++) {
|
|
2161
|
+
const c = html.charCodeAt(j);
|
|
2162
|
+
if (c === 34) {
|
|
2163
|
+
j = html.indexOf('"', j + 1);
|
|
2164
|
+
if (j < 0) return -1;
|
|
2165
|
+
} else if (c === 62) return j;
|
|
2166
|
+
}
|
|
2167
|
+
return -1;
|
|
2168
|
+
}
|
|
2169
|
+
function optionText(html, from) {
|
|
2170
|
+
let t = "";
|
|
2171
|
+
let i = from;
|
|
2172
|
+
for (;;) {
|
|
2173
|
+
const lt = html.indexOf("<", i);
|
|
2174
|
+
if (lt < 0) return t + html.slice(i);
|
|
2175
|
+
t += html.slice(i, lt);
|
|
2176
|
+
if (!html.startsWith("<!--", lt)) return t;
|
|
2177
|
+
const ce = html.indexOf("-->", lt);
|
|
2178
|
+
if (ce < 0) return t;
|
|
2179
|
+
i = ce + 3;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
function tagIs(html, i, name) {
|
|
2183
|
+
if (!html.startsWith(name, i + 1)) return false;
|
|
2184
|
+
const c = html.charCodeAt(i + 1 + name.length);
|
|
2185
|
+
return c === 32 || c === 62 || c === 9 || c === 10 || c === 13;
|
|
2186
|
+
}
|
|
2187
|
+
function resolveSSRSelectValues(html) {
|
|
2188
|
+
if (html.indexOf("<select") < 0) return html;
|
|
2189
|
+
let out = "";
|
|
2190
|
+
let idx = 0;
|
|
2191
|
+
let sel = null;
|
|
2192
|
+
let i = html.indexOf("<");
|
|
2193
|
+
while (i >= 0) {
|
|
2194
|
+
let e;
|
|
2195
|
+
if (html.charCodeAt(i + 1) === 33) {
|
|
2196
|
+
e = html.charCodeAt(i + 2) === 45 ? html.indexOf("-->", i) : html.indexOf(">", i);
|
|
2197
|
+
if (e < 0) break;
|
|
2198
|
+
if (html.charCodeAt(i + 2) === 45) e += 2;
|
|
2199
|
+
} else {
|
|
2200
|
+
e = tagEnd(html, i);
|
|
2201
|
+
if (e < 0) break;
|
|
2202
|
+
if (sel) {
|
|
2203
|
+
if (html.startsWith("</select>", i)) {
|
|
2204
|
+
out += html.slice(idx, sel.strip) + html.slice(sel.stripEnd, sel.body);
|
|
2205
|
+
let seg = sel.body;
|
|
2206
|
+
if (!sel.defaulted) {
|
|
2207
|
+
for (let k = 0; k < sel.marks.length; k++) {
|
|
2208
|
+
out += html.slice(seg, sel.marks[k]) + " selected";
|
|
2209
|
+
seg = sel.marks[k];
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
out += html.slice(seg, i);
|
|
2213
|
+
idx = i;
|
|
2214
|
+
sel = null;
|
|
2215
|
+
} else if (tagIs(html, i, "option")) {
|
|
2216
|
+
const attrs = html.slice(i + 7, e);
|
|
2217
|
+
if (/\sselected(?=[\s=]|$)/.test(attrs)) sel.defaulted = true;else {
|
|
2218
|
+
const vm = SELECT_VALUE_ATTR.exec(attrs);
|
|
2219
|
+
const value = vm ? decodeSSREntities(vm[1]) : decodeSSREntities(optionText(html, e + 1)).replace(/\s+/g, " ").trim();
|
|
2220
|
+
if (sel.values.includes(value)) sel.marks.push(e);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
} else if (tagIs(html, i, "select")) {
|
|
2224
|
+
const m = SELECT_VALUE_ATTR.exec(html.slice(i, e + 1));
|
|
2225
|
+
if (m) {
|
|
2226
|
+
const bound = decodeSSREntities(m[1]);
|
|
2227
|
+
sel = {
|
|
2228
|
+
values: /\smultiple(?=[\s>=])/.test(html.slice(i, e + 1)) ? bound.split(",") : [bound],
|
|
2229
|
+
strip: i + m.index,
|
|
2230
|
+
stripEnd: i + m.index + m[0].length,
|
|
2231
|
+
body: e + 1,
|
|
2232
|
+
marks: [],
|
|
2233
|
+
defaulted: false
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
i = html.indexOf("<", e + 1);
|
|
2239
|
+
}
|
|
2240
|
+
return out + html.slice(idx);
|
|
2241
|
+
}
|
|
2155
2242
|
function escape(s, attr) {
|
|
2156
2243
|
const t = typeof s;
|
|
2157
2244
|
if (t !== "string") {
|
|
@@ -2283,7 +2370,9 @@ function renderHeadAssets(emittedAssets, inlineStyles, nonce) {
|
|
|
2283
2370
|
function serializeFragmentAssets(key, boundaryModules, context, name = key) {
|
|
2284
2371
|
const map = boundaryModules.get(key);
|
|
2285
2372
|
if (!map || !Object.keys(map).length) return;
|
|
2286
|
-
context.serialize(name + "_assets",
|
|
2373
|
+
context.serialize(name + "_assets", {
|
|
2374
|
+
...map
|
|
2375
|
+
});
|
|
2287
2376
|
}
|
|
2288
2377
|
function propagateBoundaryStyles(childKey, parentKey, tracking) {
|
|
2289
2378
|
const childStyles = tracking.getBoundaryStyles(childKey);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidjs/web",
|
|
3
3
|
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
|
|
4
|
-
"version": "2.0.0-rc.
|
|
4
|
+
"version": "2.0.0-rc.1",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://solidjs.com",
|
|
@@ -378,10 +378,11 @@
|
|
|
378
378
|
"seroval-plugins": "~1.5.4"
|
|
379
379
|
},
|
|
380
380
|
"peerDependencies": {
|
|
381
|
-
"solid-js": "^2.0.0-rc.
|
|
381
|
+
"solid-js": "^2.0.0-rc.1"
|
|
382
382
|
},
|
|
383
383
|
"devDependencies": {
|
|
384
|
-
"
|
|
384
|
+
"@codspeed/vitest-plugin": "^5.4.0",
|
|
385
|
+
"solid-js": "2.0.0-rc.1"
|
|
385
386
|
},
|
|
386
387
|
"scripts": {
|
|
387
388
|
"build": "npm-run-all -nl build:clean types:copy-jsx build:js",
|
|
@@ -79,12 +79,24 @@ function resolveCodecOptions({
|
|
|
79
79
|
function createJSONDeserializer(options) {
|
|
80
80
|
const refs = new Map();
|
|
81
81
|
const resolved = resolveCodecOptions(options);
|
|
82
|
-
|
|
82
|
+
function deserializeJSONChunk(node) {
|
|
83
83
|
return seroval.fromCrossJSON(node, {
|
|
84
84
|
refs,
|
|
85
85
|
...resolved
|
|
86
86
|
});
|
|
87
|
+
}
|
|
88
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
89
|
+
for (const value of refs.values()) {
|
|
90
|
+
if (value === null || typeof value !== "object") continue;
|
|
91
|
+
if (value.__SEROVAL_STREAM__) {
|
|
92
|
+
value.throw(error);
|
|
93
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
94
|
+
value.p.then(undefined, () => {});
|
|
95
|
+
value.f(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
87
98
|
};
|
|
99
|
+
return deserializeJSONChunk;
|
|
88
100
|
}
|
|
89
101
|
function createJSONDataTable(options) {
|
|
90
102
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -77,12 +77,24 @@ function resolveCodecOptions({
|
|
|
77
77
|
function createJSONDeserializer(options) {
|
|
78
78
|
const refs = new Map();
|
|
79
79
|
const resolved = resolveCodecOptions(options);
|
|
80
|
-
|
|
80
|
+
function deserializeJSONChunk(node) {
|
|
81
81
|
return fromCrossJSON(node, {
|
|
82
82
|
refs,
|
|
83
83
|
...resolved
|
|
84
84
|
});
|
|
85
|
+
}
|
|
86
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
87
|
+
for (const value of refs.values()) {
|
|
88
|
+
if (value === null || typeof value !== "object") continue;
|
|
89
|
+
if (value.__SEROVAL_STREAM__) {
|
|
90
|
+
value.throw(error);
|
|
91
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
92
|
+
value.p.then(undefined, () => {});
|
|
93
|
+
value.f(error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
85
96
|
};
|
|
97
|
+
return deserializeJSONChunk;
|
|
86
98
|
}
|
|
87
99
|
function createJSONDataTable(options) {
|
|
88
100
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -79,12 +79,24 @@ function resolveCodecOptions({
|
|
|
79
79
|
function createJSONDeserializer(options) {
|
|
80
80
|
const refs = new Map();
|
|
81
81
|
const resolved = resolveCodecOptions(options);
|
|
82
|
-
|
|
82
|
+
function deserializeJSONChunk(node) {
|
|
83
83
|
return seroval.fromCrossJSON(node, {
|
|
84
84
|
refs,
|
|
85
85
|
...resolved
|
|
86
86
|
});
|
|
87
|
+
}
|
|
88
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
89
|
+
for (const value of refs.values()) {
|
|
90
|
+
if (value === null || typeof value !== "object") continue;
|
|
91
|
+
if (value.__SEROVAL_STREAM__) {
|
|
92
|
+
value.throw(error);
|
|
93
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
94
|
+
value.p.then(undefined, () => {});
|
|
95
|
+
value.f(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
87
98
|
};
|
|
99
|
+
return deserializeJSONChunk;
|
|
88
100
|
}
|
|
89
101
|
function createJSONDataTable(options) {
|
|
90
102
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -78,12 +78,24 @@ function resolveCodecOptions({
|
|
|
78
78
|
function createJSONDeserializer(options) {
|
|
79
79
|
const refs = new Map();
|
|
80
80
|
const resolved = resolveCodecOptions(options);
|
|
81
|
-
|
|
81
|
+
function deserializeJSONChunk(node) {
|
|
82
82
|
return fromCrossJSON(node, {
|
|
83
83
|
refs,
|
|
84
84
|
...resolved
|
|
85
85
|
});
|
|
86
|
+
}
|
|
87
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
88
|
+
for (const value of refs.values()) {
|
|
89
|
+
if (value === null || typeof value !== "object") continue;
|
|
90
|
+
if (value.__SEROVAL_STREAM__) {
|
|
91
|
+
value.throw(error);
|
|
92
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
93
|
+
value.p.then(undefined, () => {});
|
|
94
|
+
value.f(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
86
97
|
};
|
|
98
|
+
return deserializeJSONChunk;
|
|
87
99
|
}
|
|
88
100
|
function createJSONDataTable(options) {
|
|
89
101
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -18,6 +18,7 @@ function withMeta(fn, meta) {
|
|
|
18
18
|
Object.assign(metadata, meta);
|
|
19
19
|
return fn;
|
|
20
20
|
}
|
|
21
|
+
const LIVE_SOURCE = Symbol.for("solid.LiveSource");
|
|
21
22
|
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
22
23
|
function provideServerFunctionRPC(rpc) {
|
|
23
24
|
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
@@ -138,7 +139,7 @@ const BodyFormat = {
|
|
|
138
139
|
Uint8Array: "7",
|
|
139
140
|
Json: "8"
|
|
140
141
|
};
|
|
141
|
-
const JSON_SAFE_DEPTH_LIMIT =
|
|
142
|
+
const JSON_SAFE_DEPTH_LIMIT = 4096;
|
|
142
143
|
const EXIT = {};
|
|
143
144
|
function isJSONSafe(value) {
|
|
144
145
|
const stack = [value];
|
|
@@ -165,6 +166,7 @@ function isJSONSafe(value) {
|
|
|
165
166
|
} else {
|
|
166
167
|
const proto = Object.getPrototypeOf(v);
|
|
167
168
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
169
|
+
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
168
170
|
for (const k in v) stack.push(v[k]);
|
|
169
171
|
}
|
|
170
172
|
}
|
|
@@ -366,7 +368,7 @@ async function deserializeStream(source, codecOptions) {
|
|
|
366
368
|
function interpretChunk(chunk) {
|
|
367
369
|
return deserializeChunk(JSON.parse(chunk));
|
|
368
370
|
}
|
|
369
|
-
|
|
371
|
+
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
370
372
|
return interpretChunk(result.value);
|
|
371
373
|
}
|
|
372
374
|
return undefined;
|
|
@@ -429,7 +431,7 @@ async function createRequest(base, id, instance, options, meta) {
|
|
|
429
431
|
[FUNCTION_HEADER]: id,
|
|
430
432
|
[INSTANCE_HEADER]: instance
|
|
431
433
|
};
|
|
432
|
-
if (getFlightDataConsumer() && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
434
|
+
if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
433
435
|
headers[SINGLE_FLIGHT_HEADER] = "true";
|
|
434
436
|
}
|
|
435
437
|
let init = {
|
|
@@ -511,6 +513,11 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
511
513
|
id,
|
|
512
514
|
meta
|
|
513
515
|
}) : undefined;
|
|
516
|
+
const controller = options.signal ? undefined : new AbortController();
|
|
517
|
+
if (controller) options = {
|
|
518
|
+
...options,
|
|
519
|
+
signal: controller.signal
|
|
520
|
+
};
|
|
514
521
|
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
515
522
|
if (handler) {
|
|
516
523
|
const handled = handler.handle(response, {
|
|
@@ -541,6 +548,20 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
541
548
|
if (response.headers.has(ERROR_HEADER)) {
|
|
542
549
|
throw result;
|
|
543
550
|
}
|
|
551
|
+
if (controller && result?.[Symbol.asyncIterator]) {
|
|
552
|
+
return {
|
|
553
|
+
[Symbol.asyncIterator]() {
|
|
554
|
+
const it = result[Symbol.asyncIterator]();
|
|
555
|
+
return {
|
|
556
|
+
next: () => it.next(),
|
|
557
|
+
return: value => (controller.abort(), Promise.resolve({
|
|
558
|
+
done: true,
|
|
559
|
+
value
|
|
560
|
+
}))
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
}
|
|
544
565
|
return result;
|
|
545
566
|
}
|
|
546
567
|
function createServerReference(id, name, base) {
|
|
@@ -609,6 +630,131 @@ function GET(fn) {
|
|
|
609
630
|
method: "GET"
|
|
610
631
|
});
|
|
611
632
|
}
|
|
633
|
+
function live(fn) {
|
|
634
|
+
if (!isServerFunction(fn)) {
|
|
635
|
+
throw new Error("live expects a server function reference");
|
|
636
|
+
}
|
|
637
|
+
const id = fn.id;
|
|
638
|
+
const metadata = {
|
|
639
|
+
...getServerFunctionMetadata(fn),
|
|
640
|
+
live: true
|
|
641
|
+
};
|
|
642
|
+
const wrapped = (...args) => {
|
|
643
|
+
const iterable = {
|
|
644
|
+
[LIVE_SOURCE]: true,
|
|
645
|
+
[Symbol.asyncIterator]() {
|
|
646
|
+
let it;
|
|
647
|
+
let connected = false;
|
|
648
|
+
let attempts = 0;
|
|
649
|
+
let stopped = false;
|
|
650
|
+
let ended = false;
|
|
651
|
+
let timer, wake;
|
|
652
|
+
const DONE = {
|
|
653
|
+
done: true,
|
|
654
|
+
value: undefined
|
|
655
|
+
};
|
|
656
|
+
const emit = (state, error) => {
|
|
657
|
+
try {
|
|
658
|
+
iterable.onstatus && iterable.onstatus(state, error);
|
|
659
|
+
} catch {}
|
|
660
|
+
};
|
|
661
|
+
const emitClosed = () => {
|
|
662
|
+
if (ended) return;
|
|
663
|
+
ended = true;
|
|
664
|
+
emit("closed");
|
|
665
|
+
};
|
|
666
|
+
const closeIt = value => {
|
|
667
|
+
const current = it;
|
|
668
|
+
it = undefined;
|
|
669
|
+
if (current) {
|
|
670
|
+
try {
|
|
671
|
+
const r = current.return && current.return(value);
|
|
672
|
+
if (r && typeof r.then === "function") r.then(undefined, () => {});
|
|
673
|
+
} catch {}
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
const callOnce = () => {
|
|
677
|
+
if (metadata.method === "GET") return fn(...args);
|
|
678
|
+
const handler = config.responseHandler;
|
|
679
|
+
if (handler && handler.intercept) {
|
|
680
|
+
const hit = handler.intercept({
|
|
681
|
+
id,
|
|
682
|
+
meta: metadata,
|
|
683
|
+
args
|
|
684
|
+
});
|
|
685
|
+
if (hit !== undefined) return hit;
|
|
686
|
+
}
|
|
687
|
+
return fetchServerFunction(fn.url, id, {
|
|
688
|
+
read: true
|
|
689
|
+
}, args, metadata, args);
|
|
690
|
+
};
|
|
691
|
+
const pull = async () => {
|
|
692
|
+
while (!stopped) {
|
|
693
|
+
try {
|
|
694
|
+
if (!it) {
|
|
695
|
+
const result = await callOnce();
|
|
696
|
+
connected = true;
|
|
697
|
+
it = result !== null && typeof result === "object" && result[Symbol.asyncIterator] ? result[Symbol.asyncIterator]() : async function* () {
|
|
698
|
+
yield result;
|
|
699
|
+
}();
|
|
700
|
+
if (stopped) {
|
|
701
|
+
closeIt();
|
|
702
|
+
return DONE;
|
|
703
|
+
}
|
|
704
|
+
emit("connected");
|
|
705
|
+
}
|
|
706
|
+
const r = await it.next();
|
|
707
|
+
if (r.done) {
|
|
708
|
+
emitClosed();
|
|
709
|
+
return DONE;
|
|
710
|
+
}
|
|
711
|
+
if (stopped) return DONE;
|
|
712
|
+
attempts = 0;
|
|
713
|
+
return r;
|
|
714
|
+
} catch (error) {
|
|
715
|
+
if (!connected) throw error;
|
|
716
|
+
it = undefined;
|
|
717
|
+
emit("reconnecting", error);
|
|
718
|
+
await new Promise(resolve => {
|
|
719
|
+
wake = resolve;
|
|
720
|
+
timer = setTimeout(resolve, Math.min(500 * 2 ** attempts++, 10000));
|
|
721
|
+
if (typeof addEventListener === "function") addEventListener("online", resolve, {
|
|
722
|
+
once: true
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
clearTimeout(timer);
|
|
726
|
+
if (typeof removeEventListener === "function") removeEventListener("online", wake);
|
|
727
|
+
timer = wake = undefined;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return DONE;
|
|
731
|
+
};
|
|
732
|
+
return {
|
|
733
|
+
next: () => pull(),
|
|
734
|
+
return(value) {
|
|
735
|
+
stopped = true;
|
|
736
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
737
|
+
if (wake) wake();
|
|
738
|
+
closeIt(value);
|
|
739
|
+
emitClosed();
|
|
740
|
+
return Promise.resolve({
|
|
741
|
+
done: true,
|
|
742
|
+
value
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
return iterable;
|
|
749
|
+
};
|
|
750
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
751
|
+
wrapped.id = id;
|
|
752
|
+
Object.defineProperty(wrapped, "url", {
|
|
753
|
+
get: () => fn.url,
|
|
754
|
+
configurable: true
|
|
755
|
+
});
|
|
756
|
+
return wrapped;
|
|
757
|
+
}
|
|
612
758
|
function registerServerReference() {
|
|
613
759
|
throw new Error("registerServerReference must not be called in the client build");
|
|
614
760
|
}
|
|
@@ -640,6 +786,7 @@ exports.getServerFunctionMetadata = getServerFunctionMetadata;
|
|
|
640
786
|
exports.getServerFunctionsCodec = getServerFunctionsCodec;
|
|
641
787
|
exports.hasFlashCookie = hasFlashCookie;
|
|
642
788
|
exports.isServerFunction = isServerFunction;
|
|
789
|
+
exports.live = live;
|
|
643
790
|
exports.registerServerReference = registerServerReference;
|
|
644
791
|
exports.serializeString = serializeString;
|
|
645
792
|
exports.subscribeFlightData = subscribeFlightData;
|
|
@@ -16,6 +16,7 @@ function withMeta(fn, meta) {
|
|
|
16
16
|
Object.assign(metadata, meta);
|
|
17
17
|
return fn;
|
|
18
18
|
}
|
|
19
|
+
const LIVE_SOURCE = Symbol.for("solid.LiveSource");
|
|
19
20
|
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
20
21
|
function provideServerFunctionRPC(rpc) {
|
|
21
22
|
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
@@ -136,7 +137,7 @@ const BodyFormat = {
|
|
|
136
137
|
Uint8Array: "7",
|
|
137
138
|
Json: "8"
|
|
138
139
|
};
|
|
139
|
-
const JSON_SAFE_DEPTH_LIMIT =
|
|
140
|
+
const JSON_SAFE_DEPTH_LIMIT = 4096;
|
|
140
141
|
const EXIT = {};
|
|
141
142
|
function isJSONSafe(value) {
|
|
142
143
|
const stack = [value];
|
|
@@ -163,6 +164,7 @@ function isJSONSafe(value) {
|
|
|
163
164
|
} else {
|
|
164
165
|
const proto = Object.getPrototypeOf(v);
|
|
165
166
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
167
|
+
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
166
168
|
for (const k in v) stack.push(v[k]);
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -364,7 +366,7 @@ async function deserializeStream(source, codecOptions) {
|
|
|
364
366
|
function interpretChunk(chunk) {
|
|
365
367
|
return deserializeChunk(JSON.parse(chunk));
|
|
366
368
|
}
|
|
367
|
-
|
|
369
|
+
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
368
370
|
return interpretChunk(result.value);
|
|
369
371
|
}
|
|
370
372
|
return undefined;
|
|
@@ -427,7 +429,7 @@ async function createRequest(base, id, instance, options, meta) {
|
|
|
427
429
|
[FUNCTION_HEADER]: id,
|
|
428
430
|
[INSTANCE_HEADER]: instance
|
|
429
431
|
};
|
|
430
|
-
if (getFlightDataConsumer() && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
432
|
+
if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
431
433
|
headers[SINGLE_FLIGHT_HEADER] = "true";
|
|
432
434
|
}
|
|
433
435
|
let init = {
|
|
@@ -509,6 +511,11 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
509
511
|
id,
|
|
510
512
|
meta
|
|
511
513
|
}) : undefined;
|
|
514
|
+
const controller = options.signal ? undefined : new AbortController();
|
|
515
|
+
if (controller) options = {
|
|
516
|
+
...options,
|
|
517
|
+
signal: controller.signal
|
|
518
|
+
};
|
|
512
519
|
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
513
520
|
if (handler) {
|
|
514
521
|
const handled = handler.handle(response, {
|
|
@@ -539,6 +546,20 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
539
546
|
if (response.headers.has(ERROR_HEADER)) {
|
|
540
547
|
throw result;
|
|
541
548
|
}
|
|
549
|
+
if (controller && result?.[Symbol.asyncIterator]) {
|
|
550
|
+
return {
|
|
551
|
+
[Symbol.asyncIterator]() {
|
|
552
|
+
const it = result[Symbol.asyncIterator]();
|
|
553
|
+
return {
|
|
554
|
+
next: () => it.next(),
|
|
555
|
+
return: value => (controller.abort(), Promise.resolve({
|
|
556
|
+
done: true,
|
|
557
|
+
value
|
|
558
|
+
}))
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
542
563
|
return result;
|
|
543
564
|
}
|
|
544
565
|
function createServerReference(id, name, base) {
|
|
@@ -607,6 +628,131 @@ function GET(fn) {
|
|
|
607
628
|
method: "GET"
|
|
608
629
|
});
|
|
609
630
|
}
|
|
631
|
+
function live(fn) {
|
|
632
|
+
if (!isServerFunction(fn)) {
|
|
633
|
+
throw new Error("live expects a server function reference");
|
|
634
|
+
}
|
|
635
|
+
const id = fn.id;
|
|
636
|
+
const metadata = {
|
|
637
|
+
...getServerFunctionMetadata(fn),
|
|
638
|
+
live: true
|
|
639
|
+
};
|
|
640
|
+
const wrapped = (...args) => {
|
|
641
|
+
const iterable = {
|
|
642
|
+
[LIVE_SOURCE]: true,
|
|
643
|
+
[Symbol.asyncIterator]() {
|
|
644
|
+
let it;
|
|
645
|
+
let connected = false;
|
|
646
|
+
let attempts = 0;
|
|
647
|
+
let stopped = false;
|
|
648
|
+
let ended = false;
|
|
649
|
+
let timer, wake;
|
|
650
|
+
const DONE = {
|
|
651
|
+
done: true,
|
|
652
|
+
value: undefined
|
|
653
|
+
};
|
|
654
|
+
const emit = (state, error) => {
|
|
655
|
+
try {
|
|
656
|
+
iterable.onstatus && iterable.onstatus(state, error);
|
|
657
|
+
} catch {}
|
|
658
|
+
};
|
|
659
|
+
const emitClosed = () => {
|
|
660
|
+
if (ended) return;
|
|
661
|
+
ended = true;
|
|
662
|
+
emit("closed");
|
|
663
|
+
};
|
|
664
|
+
const closeIt = value => {
|
|
665
|
+
const current = it;
|
|
666
|
+
it = undefined;
|
|
667
|
+
if (current) {
|
|
668
|
+
try {
|
|
669
|
+
const r = current.return && current.return(value);
|
|
670
|
+
if (r && typeof r.then === "function") r.then(undefined, () => {});
|
|
671
|
+
} catch {}
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
const callOnce = () => {
|
|
675
|
+
if (metadata.method === "GET") return fn(...args);
|
|
676
|
+
const handler = config.responseHandler;
|
|
677
|
+
if (handler && handler.intercept) {
|
|
678
|
+
const hit = handler.intercept({
|
|
679
|
+
id,
|
|
680
|
+
meta: metadata,
|
|
681
|
+
args
|
|
682
|
+
});
|
|
683
|
+
if (hit !== undefined) return hit;
|
|
684
|
+
}
|
|
685
|
+
return fetchServerFunction(fn.url, id, {
|
|
686
|
+
read: true
|
|
687
|
+
}, args, metadata, args);
|
|
688
|
+
};
|
|
689
|
+
const pull = async () => {
|
|
690
|
+
while (!stopped) {
|
|
691
|
+
try {
|
|
692
|
+
if (!it) {
|
|
693
|
+
const result = await callOnce();
|
|
694
|
+
connected = true;
|
|
695
|
+
it = result !== null && typeof result === "object" && result[Symbol.asyncIterator] ? result[Symbol.asyncIterator]() : async function* () {
|
|
696
|
+
yield result;
|
|
697
|
+
}();
|
|
698
|
+
if (stopped) {
|
|
699
|
+
closeIt();
|
|
700
|
+
return DONE;
|
|
701
|
+
}
|
|
702
|
+
emit("connected");
|
|
703
|
+
}
|
|
704
|
+
const r = await it.next();
|
|
705
|
+
if (r.done) {
|
|
706
|
+
emitClosed();
|
|
707
|
+
return DONE;
|
|
708
|
+
}
|
|
709
|
+
if (stopped) return DONE;
|
|
710
|
+
attempts = 0;
|
|
711
|
+
return r;
|
|
712
|
+
} catch (error) {
|
|
713
|
+
if (!connected) throw error;
|
|
714
|
+
it = undefined;
|
|
715
|
+
emit("reconnecting", error);
|
|
716
|
+
await new Promise(resolve => {
|
|
717
|
+
wake = resolve;
|
|
718
|
+
timer = setTimeout(resolve, Math.min(500 * 2 ** attempts++, 10000));
|
|
719
|
+
if (typeof addEventListener === "function") addEventListener("online", resolve, {
|
|
720
|
+
once: true
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
clearTimeout(timer);
|
|
724
|
+
if (typeof removeEventListener === "function") removeEventListener("online", wake);
|
|
725
|
+
timer = wake = undefined;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return DONE;
|
|
729
|
+
};
|
|
730
|
+
return {
|
|
731
|
+
next: () => pull(),
|
|
732
|
+
return(value) {
|
|
733
|
+
stopped = true;
|
|
734
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
735
|
+
if (wake) wake();
|
|
736
|
+
closeIt(value);
|
|
737
|
+
emitClosed();
|
|
738
|
+
return Promise.resolve({
|
|
739
|
+
done: true,
|
|
740
|
+
value
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
return iterable;
|
|
747
|
+
};
|
|
748
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
749
|
+
wrapped.id = id;
|
|
750
|
+
Object.defineProperty(wrapped, "url", {
|
|
751
|
+
get: () => fn.url,
|
|
752
|
+
configurable: true
|
|
753
|
+
});
|
|
754
|
+
return wrapped;
|
|
755
|
+
}
|
|
610
756
|
function registerServerReference() {
|
|
611
757
|
throw new Error("registerServerReference must not be called in the client build");
|
|
612
758
|
}
|
|
@@ -614,4 +760,4 @@ function getServerFunctionInvocation() {
|
|
|
614
760
|
return undefined;
|
|
615
761
|
}
|
|
616
762
|
|
|
617
|
-
export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, serializeString, subscribeFlightData, withMeta };
|
|
763
|
+
export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, live, registerServerReference, serializeString, subscribeFlightData, withMeta };
|