@solidjs/web 2.0.0-beta.31 → 2.0.0-beta.33
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 -6
- package/dist/dev.cjs +285 -60
- package/dist/dev.js +267 -57
- package/dist/server.cjs +1148 -176
- package/dist/server.js +1133 -175
- package/dist/web.cjs +285 -60
- package/dist/web.js +267 -57
- package/frames/dist/client.cjs +397 -175
- package/frames/dist/client.dev.cjs +401 -175
- package/frames/dist/client.dev.js +399 -173
- package/frames/dist/client.js +395 -173
- package/frames/dist/server.cjs +1424 -277
- package/frames/dist/server.js +1426 -280
- package/package.json +69 -7
- package/serialization/decode/package.json +20 -0
- package/serialization/dist/decode.cjs +110 -0
- package/serialization/dist/decode.js +104 -0
- package/serialization/dist/serialization.cjs +106 -43
- package/serialization/dist/serialization.js +100 -44
- package/serialization/types/index.d.ts +85 -60
- package/serialization/types/serializer-decode.d.ts +182 -0
- package/serialization/types-cjs/index.d.cts +85 -60
- package/serialization/types-cjs/serializer-decode.d.cts +182 -0
- package/server-functions/dist/client.cjs +131 -98
- package/server-functions/dist/client.js +131 -99
- package/server-functions/dist/rich-args.cjs +11 -0
- package/server-functions/dist/rich-args.js +9 -0
- package/server-functions/dist/server.cjs +367 -194
- package/server-functions/dist/server.dev.cjs +1077 -0
- package/server-functions/dist/server.dev.js +1045 -0
- package/server-functions/dist/server.js +365 -195
- package/server-functions/package.json +10 -0
- package/server-functions/rich-args/package.json +20 -0
- package/storage/types/index.d.ts +1 -1
- package/storage/types-cjs/index.d.cts +1 -1
- package/types/client.d.ts +149 -6
- package/types/cookies.d.ts +93 -0
- package/types/core.d.ts +4 -2
- package/types/frames/client.d.ts +15 -1
- package/types/frames/frame-client.d.ts +63 -7
- package/types/frames/frame-sink.d.ts +26 -3
- package/types/frames/frame-transport.d.ts +40 -8
- package/types/frames/serializer.d.ts +85 -60
- package/types/frames/server.d.ts +22 -0
- package/types/index.d.ts +2 -3
- package/types/response.d.ts +45 -0
- package/types/serializer-decode.d.ts +182 -0
- package/types/serializer.d.ts +85 -60
- package/types/server-functions/client.d.ts +2 -1
- package/types/server-functions/rich-args.d.ts +10 -0
- package/types/server-functions/server.d.ts +99 -1
- package/types/server-functions/shared.d.ts +79 -1
- package/types/server-mock.d.ts +171 -59
- package/types/server.d.ts +209 -37
- package/types-cjs/client.d.cts +149 -6
- package/types-cjs/cookies.d.cts +93 -0
- package/types-cjs/core.d.cts +4 -2
- package/types-cjs/frames/client.d.cts +15 -1
- package/types-cjs/frames/frame-client.d.cts +63 -7
- package/types-cjs/frames/frame-sink.d.cts +26 -3
- package/types-cjs/frames/frame-transport.d.cts +40 -8
- package/types-cjs/frames/serializer.d.cts +85 -60
- package/types-cjs/frames/server.d.cts +22 -0
- package/types-cjs/index.d.cts +2 -3
- package/types-cjs/response.d.cts +45 -0
- package/types-cjs/serializer-decode.d.cts +182 -0
- package/types-cjs/serializer.d.cts +85 -60
- package/types-cjs/server-functions/client.d.cts +2 -1
- package/types-cjs/server-functions/rich-args.d.cts +10 -0
- package/types-cjs/server-functions/server.d.cts +99 -1
- package/types-cjs/server-functions/shared.d.cts +79 -1
- package/types-cjs/server-mock.d.cts +171 -59
- package/types-cjs/server.d.cts +209 -37
|
@@ -1,39 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var seroval = require('seroval');
|
|
4
|
-
var web = require('seroval-plugins/web');
|
|
5
|
-
|
|
6
3
|
const REVALIDATE_HEADER = "X-Revalidate";
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function resolveSerializerPlugins(customPlugins) {
|
|
13
|
-
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
|
|
14
|
-
}
|
|
15
|
-
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
|
|
16
|
-
const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
17
|
-
function resolveCodecOptions({
|
|
18
|
-
plugins,
|
|
19
|
-
disabledFeatures,
|
|
20
|
-
depthLimit
|
|
21
|
-
} = {}) {
|
|
22
|
-
return {
|
|
23
|
-
plugins: resolveSerializerPlugins(plugins),
|
|
24
|
-
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
25
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
26
|
-
};
|
|
5
|
+
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
6
|
+
function getServerFunctionMetadata(fn) {
|
|
7
|
+
if (typeof fn !== "function") return undefined;
|
|
8
|
+
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
27
9
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
function isServerFunction(fn) {
|
|
11
|
+
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
12
|
+
}
|
|
13
|
+
function withMeta(fn, meta) {
|
|
14
|
+
const metadata = getServerFunctionMetadata(fn);
|
|
15
|
+
if (!metadata) {
|
|
16
|
+
throw new Error("withMeta expects a server function reference");
|
|
17
|
+
}
|
|
18
|
+
Object.assign(metadata, meta);
|
|
19
|
+
return fn;
|
|
20
|
+
}
|
|
21
|
+
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
22
|
+
function provideServerFunctionRPC(rpc) {
|
|
23
|
+
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const FLASH_COOKIE = "flash";
|
|
27
|
+
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
28
|
+
function hasFlashCookie(cookieHeader) {
|
|
29
|
+
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
30
|
+
}
|
|
31
|
+
function clearFlashCookie() {
|
|
32
|
+
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
const codecConfig = {
|
|
@@ -101,22 +97,6 @@ function stableString(value, seen) {
|
|
|
101
97
|
}
|
|
102
98
|
return out + "}";
|
|
103
99
|
}
|
|
104
|
-
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
105
|
-
function getServerFunctionMetadata(fn) {
|
|
106
|
-
if (typeof fn !== "function") return undefined;
|
|
107
|
-
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
108
|
-
}
|
|
109
|
-
function isServerFunction(fn) {
|
|
110
|
-
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
111
|
-
}
|
|
112
|
-
function withMeta(fn, meta) {
|
|
113
|
-
const metadata = getServerFunctionMetadata(fn);
|
|
114
|
-
if (!metadata) {
|
|
115
|
-
throw new Error("withMeta expects a server function reference");
|
|
116
|
-
}
|
|
117
|
-
Object.assign(metadata, meta);
|
|
118
|
-
return fn;
|
|
119
|
-
}
|
|
120
100
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
121
101
|
const ERROR_HEADER = "X-Server-Function-Error";
|
|
122
102
|
const ERROR_HEADER_MARKER = "=?1?";
|
|
@@ -147,14 +127,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
|
147
127
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
148
128
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
149
129
|
const FILE_FORM_KEY = "__server_function_file__";
|
|
150
|
-
const FLASH_COOKIE = "flash";
|
|
151
|
-
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
152
|
-
function hasFlashCookie(cookieHeader) {
|
|
153
|
-
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
154
|
-
}
|
|
155
|
-
function clearFlashCookie() {
|
|
156
|
-
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
157
|
-
}
|
|
158
130
|
const BodyFormat = {
|
|
159
131
|
Serialized: "0",
|
|
160
132
|
String: "1",
|
|
@@ -166,6 +138,38 @@ const BodyFormat = {
|
|
|
166
138
|
Uint8Array: "7",
|
|
167
139
|
Json: "8"
|
|
168
140
|
};
|
|
141
|
+
const JSON_SAFE_DEPTH_LIMIT = 10000;
|
|
142
|
+
const EXIT = {};
|
|
143
|
+
function isJSONSafe(value) {
|
|
144
|
+
const stack = [value];
|
|
145
|
+
const ancestors = new Set();
|
|
146
|
+
while (stack.length) {
|
|
147
|
+
const v = stack.pop();
|
|
148
|
+
if (v === EXIT) {
|
|
149
|
+
ancestors.delete(stack.pop());
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (v === null) continue;
|
|
153
|
+
const t = typeof v;
|
|
154
|
+
if (t === "string" || t === "boolean") continue;
|
|
155
|
+
if (t === "number") {
|
|
156
|
+
if (!Number.isFinite(v)) return false;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (t !== "object") return false;
|
|
160
|
+
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
|
|
161
|
+
ancestors.add(v);
|
|
162
|
+
stack.push(v, EXIT);
|
|
163
|
+
if (Array.isArray(v)) {
|
|
164
|
+
for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
165
|
+
} else {
|
|
166
|
+
const proto = Object.getPrototypeOf(v);
|
|
167
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
168
|
+
for (const k in v) stack.push(v[k]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
169
173
|
function getHeadersAndBody(body) {
|
|
170
174
|
switch (true) {
|
|
171
175
|
case typeof body === "string":
|
|
@@ -259,13 +263,11 @@ async function extractBody(source, codecOptions) {
|
|
|
259
263
|
return undefined;
|
|
260
264
|
}
|
|
261
265
|
function createChunk(data) {
|
|
262
|
-
const
|
|
266
|
+
const encoder = new TextEncoder();
|
|
267
|
+
const encodeData = encoder.encode(data);
|
|
263
268
|
const bytes = encodeData.length;
|
|
264
|
-
const baseHex = bytes.toString(16);
|
|
265
|
-
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
266
|
-
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
267
269
|
const chunk = new Uint8Array(12 + bytes);
|
|
268
|
-
chunk.set(
|
|
270
|
+
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
|
|
269
271
|
chunk.set(encodeData, 12);
|
|
270
272
|
return chunk;
|
|
271
273
|
}
|
|
@@ -297,8 +299,8 @@ class ChunkReader {
|
|
|
297
299
|
}
|
|
298
300
|
await this.readChunk();
|
|
299
301
|
}
|
|
300
|
-
const
|
|
301
|
-
const bytes = Number.parseInt(
|
|
302
|
+
const decoder = new TextDecoder();
|
|
303
|
+
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
|
|
302
304
|
if (Number.isNaN(bytes)) {
|
|
303
305
|
throw new Error("Malformed server function stream.");
|
|
304
306
|
}
|
|
@@ -308,7 +310,7 @@ class ChunkReader {
|
|
|
308
310
|
}
|
|
309
311
|
await this.readChunk();
|
|
310
312
|
}
|
|
311
|
-
const partial =
|
|
313
|
+
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
|
|
312
314
|
this.buffer = this.buffer.subarray(12 + bytes);
|
|
313
315
|
return {
|
|
314
316
|
done: false,
|
|
@@ -325,6 +327,31 @@ class ChunkReader {
|
|
|
325
327
|
}
|
|
326
328
|
}
|
|
327
329
|
}
|
|
330
|
+
function serializeStream(value, codecOptions) {
|
|
331
|
+
return new ReadableStream({
|
|
332
|
+
async start(controller) {
|
|
333
|
+
const {
|
|
334
|
+
serializeJSON
|
|
335
|
+
} = await import('@solidjs/web/serialization');
|
|
336
|
+
serializeJSON(value, {
|
|
337
|
+
...codecOptions,
|
|
338
|
+
onParse(node) {
|
|
339
|
+
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
340
|
+
},
|
|
341
|
+
onDone() {
|
|
342
|
+
controller.close();
|
|
343
|
+
},
|
|
344
|
+
onError(error) {
|
|
345
|
+
controller.error(error);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async function serializeString(value, codecOptions) {
|
|
352
|
+
const response = new Response(serializeStream(value, codecOptions));
|
|
353
|
+
return await response.text();
|
|
354
|
+
}
|
|
328
355
|
async function deserializeStream(source, codecOptions) {
|
|
329
356
|
if (!source.body) {
|
|
330
357
|
throw new Error("missing body");
|
|
@@ -332,6 +359,9 @@ async function deserializeStream(source, codecOptions) {
|
|
|
332
359
|
const reader = new ChunkReader(source.body);
|
|
333
360
|
const result = await reader.next();
|
|
334
361
|
if (!result.done) {
|
|
362
|
+
const {
|
|
363
|
+
createJSONDeserializer
|
|
364
|
+
} = await import('@solidjs/web/serialization/decode');
|
|
335
365
|
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
336
366
|
function interpretChunk(chunk) {
|
|
337
367
|
return deserializeChunk(JSON.parse(chunk));
|
|
@@ -364,24 +394,9 @@ const config = {
|
|
|
364
394
|
responseHandler: undefined,
|
|
365
395
|
serializeArgs: undefined
|
|
366
396
|
};
|
|
367
|
-
function isJSONSafe(value) {
|
|
368
|
-
if (value === null) return true;
|
|
369
|
-
const t = typeof value;
|
|
370
|
-
if (t === "string" || t === "boolean") return true;
|
|
371
|
-
if (t === "number") return Number.isFinite(value);
|
|
372
|
-
if (t !== "object") return false;
|
|
373
|
-
if (Array.isArray(value)) {
|
|
374
|
-
for (const v of value) if (!isJSONSafe(v)) return false;
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
377
|
-
const proto = Object.getPrototypeOf(value);
|
|
378
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
379
|
-
for (const k in value) if (!isJSONSafe(value[k])) return false;
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
397
|
function serializeArguments(args) {
|
|
383
398
|
if (!config.serializeArgs) {
|
|
384
|
-
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " +
|
|
399
|
+
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") once at startup ' + "to send Dates, Maps, Sets, typed arrays, etc. through the codec — " + "or pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
|
|
385
400
|
}
|
|
386
401
|
return config.serializeArgs(args);
|
|
387
402
|
}
|
|
@@ -399,6 +414,15 @@ function configureServerFunctionsClient({
|
|
|
399
414
|
if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
|
|
400
415
|
}
|
|
401
416
|
let INSTANCE = 0;
|
|
417
|
+
let rpcProvided = false;
|
|
418
|
+
function provideRPC() {
|
|
419
|
+
if (rpcProvided) return;
|
|
420
|
+
rpcProvided = true;
|
|
421
|
+
provideServerFunctionRPC({
|
|
422
|
+
GET,
|
|
423
|
+
decodeResponse
|
|
424
|
+
});
|
|
425
|
+
}
|
|
402
426
|
async function createRequest(base, id, instance, options, meta) {
|
|
403
427
|
const headers = {
|
|
404
428
|
...options.headers,
|
|
@@ -438,31 +462,37 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
438
462
|
}, meta);
|
|
439
463
|
}
|
|
440
464
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
body: JSON.stringify(args),
|
|
445
|
-
headers: {
|
|
446
|
-
...options.headers,
|
|
447
|
-
"Content-Type": "application/json",
|
|
448
|
-
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
449
|
-
}
|
|
450
|
-
}, meta);
|
|
451
|
-
}
|
|
452
|
-
if (args.length > 1) {
|
|
453
|
-
const trailing = getHeadersAndBody(args[args.length - 1]);
|
|
454
|
-
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
|
|
455
|
-
if (trailing && isJSONSafe(leading)) {
|
|
456
|
-
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
|
|
457
|
-
return createRequest(target, id, instance, {
|
|
465
|
+
try {
|
|
466
|
+
if (isJSONSafe(args)) {
|
|
467
|
+
return createRequest(base, id, instance, {
|
|
458
468
|
...options,
|
|
459
|
-
body:
|
|
469
|
+
body: JSON.stringify(args),
|
|
460
470
|
headers: {
|
|
461
471
|
...options.headers,
|
|
462
|
-
|
|
472
|
+
"Content-Type": "application/json",
|
|
473
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
463
474
|
}
|
|
464
475
|
}, meta);
|
|
465
476
|
}
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
if (args.length > 1) {
|
|
480
|
+
try {
|
|
481
|
+
const trailing = getHeadersAndBody(args[args.length - 1]);
|
|
482
|
+
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
|
|
483
|
+
if (trailing && isJSONSafe(leading)) {
|
|
484
|
+
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
|
|
485
|
+
return createRequest(target, id, instance, {
|
|
486
|
+
...options,
|
|
487
|
+
body: trailing.body,
|
|
488
|
+
headers: {
|
|
489
|
+
...options.headers,
|
|
490
|
+
...trailing.headers
|
|
491
|
+
}
|
|
492
|
+
}, meta);
|
|
493
|
+
}
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
466
496
|
}
|
|
467
497
|
return createRequest(base, id, instance, {
|
|
468
498
|
...options,
|
|
@@ -514,6 +544,7 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
514
544
|
return result;
|
|
515
545
|
}
|
|
516
546
|
function createServerReference(id, name, base) {
|
|
547
|
+
provideRPC();
|
|
517
548
|
const metadata = name === undefined ? {} : {
|
|
518
549
|
name
|
|
519
550
|
};
|
|
@@ -544,6 +575,7 @@ function GET(fn) {
|
|
|
544
575
|
if (!isServerFunction(fn)) {
|
|
545
576
|
throw new Error("GET expects a server function reference");
|
|
546
577
|
}
|
|
578
|
+
provideRPC();
|
|
547
579
|
const id = fn.id;
|
|
548
580
|
const metadata = {
|
|
549
581
|
...getServerFunctionMetadata(fn)
|
|
@@ -609,5 +641,6 @@ exports.getServerFunctionsCodec = getServerFunctionsCodec;
|
|
|
609
641
|
exports.hasFlashCookie = hasFlashCookie;
|
|
610
642
|
exports.isServerFunction = isServerFunction;
|
|
611
643
|
exports.registerServerReference = registerServerReference;
|
|
644
|
+
exports.serializeString = serializeString;
|
|
612
645
|
exports.subscribeFlightData = subscribeFlightData;
|
|
613
646
|
exports.withMeta = withMeta;
|
|
@@ -1,37 +1,33 @@
|
|
|
1
|
-
import { fromCrossJSON, Feature } from 'seroval';
|
|
2
|
-
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
|
|
3
|
-
|
|
4
1
|
const REVALIDATE_HEADER = "X-Revalidate";
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function resolveSerializerPlugins(customPlugins) {
|
|
11
|
-
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
|
|
12
|
-
}
|
|
13
|
-
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
|
|
14
|
-
const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
15
|
-
function resolveCodecOptions({
|
|
16
|
-
plugins,
|
|
17
|
-
disabledFeatures,
|
|
18
|
-
depthLimit
|
|
19
|
-
} = {}) {
|
|
20
|
-
return {
|
|
21
|
-
plugins: resolveSerializerPlugins(plugins),
|
|
22
|
-
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
23
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
24
|
-
};
|
|
3
|
+
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
4
|
+
function getServerFunctionMetadata(fn) {
|
|
5
|
+
if (typeof fn !== "function") return undefined;
|
|
6
|
+
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
25
7
|
}
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
8
|
+
function isServerFunction(fn) {
|
|
9
|
+
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
10
|
+
}
|
|
11
|
+
function withMeta(fn, meta) {
|
|
12
|
+
const metadata = getServerFunctionMetadata(fn);
|
|
13
|
+
if (!metadata) {
|
|
14
|
+
throw new Error("withMeta expects a server function reference");
|
|
15
|
+
}
|
|
16
|
+
Object.assign(metadata, meta);
|
|
17
|
+
return fn;
|
|
18
|
+
}
|
|
19
|
+
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
20
|
+
function provideServerFunctionRPC(rpc) {
|
|
21
|
+
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FLASH_COOKIE = "flash";
|
|
25
|
+
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
26
|
+
function hasFlashCookie(cookieHeader) {
|
|
27
|
+
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
28
|
+
}
|
|
29
|
+
function clearFlashCookie() {
|
|
30
|
+
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
const codecConfig = {
|
|
@@ -99,22 +95,6 @@ function stableString(value, seen) {
|
|
|
99
95
|
}
|
|
100
96
|
return out + "}";
|
|
101
97
|
}
|
|
102
|
-
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
103
|
-
function getServerFunctionMetadata(fn) {
|
|
104
|
-
if (typeof fn !== "function") return undefined;
|
|
105
|
-
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
106
|
-
}
|
|
107
|
-
function isServerFunction(fn) {
|
|
108
|
-
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
109
|
-
}
|
|
110
|
-
function withMeta(fn, meta) {
|
|
111
|
-
const metadata = getServerFunctionMetadata(fn);
|
|
112
|
-
if (!metadata) {
|
|
113
|
-
throw new Error("withMeta expects a server function reference");
|
|
114
|
-
}
|
|
115
|
-
Object.assign(metadata, meta);
|
|
116
|
-
return fn;
|
|
117
|
-
}
|
|
118
98
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
119
99
|
const ERROR_HEADER = "X-Server-Function-Error";
|
|
120
100
|
const ERROR_HEADER_MARKER = "=?1?";
|
|
@@ -145,14 +125,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
|
145
125
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
146
126
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
147
127
|
const FILE_FORM_KEY = "__server_function_file__";
|
|
148
|
-
const FLASH_COOKIE = "flash";
|
|
149
|
-
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
150
|
-
function hasFlashCookie(cookieHeader) {
|
|
151
|
-
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
152
|
-
}
|
|
153
|
-
function clearFlashCookie() {
|
|
154
|
-
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
155
|
-
}
|
|
156
128
|
const BodyFormat = {
|
|
157
129
|
Serialized: "0",
|
|
158
130
|
String: "1",
|
|
@@ -164,6 +136,38 @@ const BodyFormat = {
|
|
|
164
136
|
Uint8Array: "7",
|
|
165
137
|
Json: "8"
|
|
166
138
|
};
|
|
139
|
+
const JSON_SAFE_DEPTH_LIMIT = 10000;
|
|
140
|
+
const EXIT = {};
|
|
141
|
+
function isJSONSafe(value) {
|
|
142
|
+
const stack = [value];
|
|
143
|
+
const ancestors = new Set();
|
|
144
|
+
while (stack.length) {
|
|
145
|
+
const v = stack.pop();
|
|
146
|
+
if (v === EXIT) {
|
|
147
|
+
ancestors.delete(stack.pop());
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (v === null) continue;
|
|
151
|
+
const t = typeof v;
|
|
152
|
+
if (t === "string" || t === "boolean") continue;
|
|
153
|
+
if (t === "number") {
|
|
154
|
+
if (!Number.isFinite(v)) return false;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (t !== "object") return false;
|
|
158
|
+
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
|
|
159
|
+
ancestors.add(v);
|
|
160
|
+
stack.push(v, EXIT);
|
|
161
|
+
if (Array.isArray(v)) {
|
|
162
|
+
for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
163
|
+
} else {
|
|
164
|
+
const proto = Object.getPrototypeOf(v);
|
|
165
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
166
|
+
for (const k in v) stack.push(v[k]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
167
171
|
function getHeadersAndBody(body) {
|
|
168
172
|
switch (true) {
|
|
169
173
|
case typeof body === "string":
|
|
@@ -257,13 +261,11 @@ async function extractBody(source, codecOptions) {
|
|
|
257
261
|
return undefined;
|
|
258
262
|
}
|
|
259
263
|
function createChunk(data) {
|
|
260
|
-
const
|
|
264
|
+
const encoder = new TextEncoder();
|
|
265
|
+
const encodeData = encoder.encode(data);
|
|
261
266
|
const bytes = encodeData.length;
|
|
262
|
-
const baseHex = bytes.toString(16);
|
|
263
|
-
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
264
|
-
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
265
267
|
const chunk = new Uint8Array(12 + bytes);
|
|
266
|
-
chunk.set(
|
|
268
|
+
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
|
|
267
269
|
chunk.set(encodeData, 12);
|
|
268
270
|
return chunk;
|
|
269
271
|
}
|
|
@@ -295,8 +297,8 @@ class ChunkReader {
|
|
|
295
297
|
}
|
|
296
298
|
await this.readChunk();
|
|
297
299
|
}
|
|
298
|
-
const
|
|
299
|
-
const bytes = Number.parseInt(
|
|
300
|
+
const decoder = new TextDecoder();
|
|
301
|
+
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
|
|
300
302
|
if (Number.isNaN(bytes)) {
|
|
301
303
|
throw new Error("Malformed server function stream.");
|
|
302
304
|
}
|
|
@@ -306,7 +308,7 @@ class ChunkReader {
|
|
|
306
308
|
}
|
|
307
309
|
await this.readChunk();
|
|
308
310
|
}
|
|
309
|
-
const partial =
|
|
311
|
+
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
|
|
310
312
|
this.buffer = this.buffer.subarray(12 + bytes);
|
|
311
313
|
return {
|
|
312
314
|
done: false,
|
|
@@ -323,6 +325,31 @@ class ChunkReader {
|
|
|
323
325
|
}
|
|
324
326
|
}
|
|
325
327
|
}
|
|
328
|
+
function serializeStream(value, codecOptions) {
|
|
329
|
+
return new ReadableStream({
|
|
330
|
+
async start(controller) {
|
|
331
|
+
const {
|
|
332
|
+
serializeJSON
|
|
333
|
+
} = await import('@solidjs/web/serialization');
|
|
334
|
+
serializeJSON(value, {
|
|
335
|
+
...codecOptions,
|
|
336
|
+
onParse(node) {
|
|
337
|
+
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
338
|
+
},
|
|
339
|
+
onDone() {
|
|
340
|
+
controller.close();
|
|
341
|
+
},
|
|
342
|
+
onError(error) {
|
|
343
|
+
controller.error(error);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
async function serializeString(value, codecOptions) {
|
|
350
|
+
const response = new Response(serializeStream(value, codecOptions));
|
|
351
|
+
return await response.text();
|
|
352
|
+
}
|
|
326
353
|
async function deserializeStream(source, codecOptions) {
|
|
327
354
|
if (!source.body) {
|
|
328
355
|
throw new Error("missing body");
|
|
@@ -330,6 +357,9 @@ async function deserializeStream(source, codecOptions) {
|
|
|
330
357
|
const reader = new ChunkReader(source.body);
|
|
331
358
|
const result = await reader.next();
|
|
332
359
|
if (!result.done) {
|
|
360
|
+
const {
|
|
361
|
+
createJSONDeserializer
|
|
362
|
+
} = await import('@solidjs/web/serialization/decode');
|
|
333
363
|
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
334
364
|
function interpretChunk(chunk) {
|
|
335
365
|
return deserializeChunk(JSON.parse(chunk));
|
|
@@ -362,24 +392,9 @@ const config = {
|
|
|
362
392
|
responseHandler: undefined,
|
|
363
393
|
serializeArgs: undefined
|
|
364
394
|
};
|
|
365
|
-
function isJSONSafe(value) {
|
|
366
|
-
if (value === null) return true;
|
|
367
|
-
const t = typeof value;
|
|
368
|
-
if (t === "string" || t === "boolean") return true;
|
|
369
|
-
if (t === "number") return Number.isFinite(value);
|
|
370
|
-
if (t !== "object") return false;
|
|
371
|
-
if (Array.isArray(value)) {
|
|
372
|
-
for (const v of value) if (!isJSONSafe(v)) return false;
|
|
373
|
-
return true;
|
|
374
|
-
}
|
|
375
|
-
const proto = Object.getPrototypeOf(value);
|
|
376
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
377
|
-
for (const k in value) if (!isJSONSafe(value[k])) return false;
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
395
|
function serializeArguments(args) {
|
|
381
396
|
if (!config.serializeArgs) {
|
|
382
|
-
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " +
|
|
397
|
+
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") once at startup ' + "to send Dates, Maps, Sets, typed arrays, etc. through the codec — " + "or pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
|
|
383
398
|
}
|
|
384
399
|
return config.serializeArgs(args);
|
|
385
400
|
}
|
|
@@ -397,6 +412,15 @@ function configureServerFunctionsClient({
|
|
|
397
412
|
if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
|
|
398
413
|
}
|
|
399
414
|
let INSTANCE = 0;
|
|
415
|
+
let rpcProvided = false;
|
|
416
|
+
function provideRPC() {
|
|
417
|
+
if (rpcProvided) return;
|
|
418
|
+
rpcProvided = true;
|
|
419
|
+
provideServerFunctionRPC({
|
|
420
|
+
GET,
|
|
421
|
+
decodeResponse
|
|
422
|
+
});
|
|
423
|
+
}
|
|
400
424
|
async function createRequest(base, id, instance, options, meta) {
|
|
401
425
|
const headers = {
|
|
402
426
|
...options.headers,
|
|
@@ -436,31 +460,37 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
436
460
|
}, meta);
|
|
437
461
|
}
|
|
438
462
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
body: JSON.stringify(args),
|
|
443
|
-
headers: {
|
|
444
|
-
...options.headers,
|
|
445
|
-
"Content-Type": "application/json",
|
|
446
|
-
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
447
|
-
}
|
|
448
|
-
}, meta);
|
|
449
|
-
}
|
|
450
|
-
if (args.length > 1) {
|
|
451
|
-
const trailing = getHeadersAndBody(args[args.length - 1]);
|
|
452
|
-
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
|
|
453
|
-
if (trailing && isJSONSafe(leading)) {
|
|
454
|
-
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
|
|
455
|
-
return createRequest(target, id, instance, {
|
|
463
|
+
try {
|
|
464
|
+
if (isJSONSafe(args)) {
|
|
465
|
+
return createRequest(base, id, instance, {
|
|
456
466
|
...options,
|
|
457
|
-
body:
|
|
467
|
+
body: JSON.stringify(args),
|
|
458
468
|
headers: {
|
|
459
469
|
...options.headers,
|
|
460
|
-
|
|
470
|
+
"Content-Type": "application/json",
|
|
471
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
461
472
|
}
|
|
462
473
|
}, meta);
|
|
463
474
|
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
if (args.length > 1) {
|
|
478
|
+
try {
|
|
479
|
+
const trailing = getHeadersAndBody(args[args.length - 1]);
|
|
480
|
+
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
|
|
481
|
+
if (trailing && isJSONSafe(leading)) {
|
|
482
|
+
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
|
|
483
|
+
return createRequest(target, id, instance, {
|
|
484
|
+
...options,
|
|
485
|
+
body: trailing.body,
|
|
486
|
+
headers: {
|
|
487
|
+
...options.headers,
|
|
488
|
+
...trailing.headers
|
|
489
|
+
}
|
|
490
|
+
}, meta);
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
464
494
|
}
|
|
465
495
|
return createRequest(base, id, instance, {
|
|
466
496
|
...options,
|
|
@@ -512,6 +542,7 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
512
542
|
return result;
|
|
513
543
|
}
|
|
514
544
|
function createServerReference(id, name, base) {
|
|
545
|
+
provideRPC();
|
|
515
546
|
const metadata = name === undefined ? {} : {
|
|
516
547
|
name
|
|
517
548
|
};
|
|
@@ -542,6 +573,7 @@ function GET(fn) {
|
|
|
542
573
|
if (!isServerFunction(fn)) {
|
|
543
574
|
throw new Error("GET expects a server function reference");
|
|
544
575
|
}
|
|
576
|
+
provideRPC();
|
|
545
577
|
const id = fn.id;
|
|
546
578
|
const metadata = {
|
|
547
579
|
...getServerFunctionMetadata(fn)
|
|
@@ -582,4 +614,4 @@ function getServerFunctionInvocation() {
|
|
|
582
614
|
return undefined;
|
|
583
615
|
}
|
|
584
616
|
|
|
585
|
-
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, subscribeFlightData, withMeta };
|
|
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 };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var client = require('@solidjs/web/server-functions/client');
|
|
4
|
+
|
|
5
|
+
function enableRichArguments() {
|
|
6
|
+
client.configureServerFunctionsClient({
|
|
7
|
+
serializeArgs: args => client.serializeString(args, client.getServerFunctionsCodec())
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
exports.enableRichArguments = enableRichArguments;
|