@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,65 +1,81 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var solidJs = require('solid-js');
|
|
4
|
-
var seroval = require('seroval');
|
|
5
|
-
var web = require('seroval-plugins/web');
|
|
6
4
|
|
|
7
5
|
const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
|
|
8
6
|
function isResponseEnvelope(value) {
|
|
9
7
|
return !!(value && typeof value === "object" && value[ENVELOPE]);
|
|
10
8
|
}
|
|
9
|
+
const SAFE_ERROR = Symbol.for("solid.SafeError");
|
|
10
|
+
function isSafeError(value) {
|
|
11
|
+
return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
|
|
12
|
+
}
|
|
11
13
|
const REVALIDATE_HEADER = "X-Revalidate";
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
|
|
18
|
-
function resolveSerializerPlugins(customPlugins) {
|
|
19
|
-
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
|
|
20
|
-
}
|
|
21
|
-
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
|
|
22
|
-
const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
23
|
-
function resolveCodecOptions({
|
|
24
|
-
plugins,
|
|
25
|
-
disabledFeatures,
|
|
26
|
-
depthLimit
|
|
27
|
-
} = {}) {
|
|
28
|
-
return {
|
|
29
|
-
plugins: resolveSerializerPlugins(plugins),
|
|
30
|
-
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
31
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
32
|
-
};
|
|
15
|
+
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
16
|
+
function getServerFunctionMetadata(fn) {
|
|
17
|
+
if (typeof fn !== "function") return undefined;
|
|
18
|
+
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
33
19
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
onDone,
|
|
37
|
-
onError,
|
|
38
|
-
...codecOptions
|
|
39
|
-
}) {
|
|
40
|
-
const resolved = resolveCodecOptions(codecOptions);
|
|
41
|
-
return seroval.toCrossJSONStream(value, {
|
|
42
|
-
onParse,
|
|
43
|
-
onDone,
|
|
44
|
-
onError,
|
|
45
|
-
...resolved,
|
|
46
|
-
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
|
|
47
|
-
});
|
|
20
|
+
function isServerFunction(fn) {
|
|
21
|
+
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
48
22
|
}
|
|
49
|
-
function
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
23
|
+
function withMeta(fn, meta) {
|
|
24
|
+
const metadata = getServerFunctionMetadata(fn);
|
|
25
|
+
if (!metadata) {
|
|
26
|
+
throw new Error("withMeta expects a server function reference");
|
|
27
|
+
}
|
|
28
|
+
Object.assign(metadata, meta);
|
|
29
|
+
return fn;
|
|
30
|
+
}
|
|
31
|
+
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
32
|
+
function provideServerFunctionRPC(rpc) {
|
|
33
|
+
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
58
34
|
}
|
|
59
35
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
36
|
+
function parseCookieHeader(header) {
|
|
37
|
+
const cookies = {};
|
|
38
|
+
if (!header) return cookies;
|
|
39
|
+
for (const part of header.split(";")) {
|
|
40
|
+
const eq = part.indexOf("=");
|
|
41
|
+
if (eq < 0) continue;
|
|
42
|
+
const name = decodeSafe(part.slice(0, eq).trim());
|
|
43
|
+
let value = part.slice(eq + 1).trim();
|
|
44
|
+
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
|
|
45
|
+
value = value.slice(1, -1);
|
|
46
|
+
}
|
|
47
|
+
cookies[name] = decodeSafe(value);
|
|
48
|
+
}
|
|
49
|
+
return cookies;
|
|
50
|
+
}
|
|
51
|
+
function decodeSafe(text) {
|
|
52
|
+
try {
|
|
53
|
+
return decodeURIComponent(text);
|
|
54
|
+
} catch {
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function serializeCookie(name, value, options = {}) {
|
|
59
|
+
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
|
60
|
+
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
|
|
61
|
+
if (options.domain) cookie += `; Domain=${options.domain}`;
|
|
62
|
+
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
|
|
63
|
+
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
|
|
64
|
+
if (options.httpOnly) cookie += "; HttpOnly";
|
|
65
|
+
if (options.secure) cookie += "; Secure";
|
|
66
|
+
if (options.sameSite) {
|
|
67
|
+
const sameSite = options.sameSite.toLowerCase();
|
|
68
|
+
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
|
|
69
|
+
}
|
|
70
|
+
return cookie;
|
|
71
|
+
}
|
|
72
|
+
const FLASH_COOKIE = "flash";
|
|
73
|
+
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
74
|
+
function hasFlashCookie(cookieHeader) {
|
|
75
|
+
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
76
|
+
}
|
|
77
|
+
function clearFlashCookie() {
|
|
78
|
+
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
const codecConfig = {
|
|
@@ -75,22 +91,6 @@ function subscribeFlightData(consumer) {
|
|
|
75
91
|
return () => {
|
|
76
92
|
};
|
|
77
93
|
}
|
|
78
|
-
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
79
|
-
function getServerFunctionMetadata(fn) {
|
|
80
|
-
if (typeof fn !== "function") return undefined;
|
|
81
|
-
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
82
|
-
}
|
|
83
|
-
function isServerFunction(fn) {
|
|
84
|
-
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
85
|
-
}
|
|
86
|
-
function withMeta(fn, meta) {
|
|
87
|
-
const metadata = getServerFunctionMetadata(fn);
|
|
88
|
-
if (!metadata) {
|
|
89
|
-
throw new Error("withMeta expects a server function reference");
|
|
90
|
-
}
|
|
91
|
-
Object.assign(metadata, meta);
|
|
92
|
-
return fn;
|
|
93
|
-
}
|
|
94
94
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
95
95
|
const ERROR_HEADER = "X-Server-Function-Error";
|
|
96
96
|
const ERROR_HEADER_MARKER = "=?1?";
|
|
@@ -121,18 +121,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
|
121
121
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
122
122
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
123
123
|
const FILE_FORM_KEY = "__server_function_file__";
|
|
124
|
-
const FLASH_COOKIE = "flash";
|
|
125
|
-
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
126
|
-
function hasFlashCookie(cookieHeader) {
|
|
127
|
-
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
128
|
-
}
|
|
129
|
-
function matchFlashCookie(cookieHeader) {
|
|
130
|
-
const match = cookieHeader && cookieHeader.match(FLASH_MATCHER);
|
|
131
|
-
return match ? match[1] : undefined;
|
|
132
|
-
}
|
|
133
|
-
function clearFlashCookie() {
|
|
134
|
-
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
135
|
-
}
|
|
136
124
|
const BodyFormat = {
|
|
137
125
|
Serialized: "0",
|
|
138
126
|
String: "1",
|
|
@@ -144,6 +132,38 @@ const BodyFormat = {
|
|
|
144
132
|
Uint8Array: "7",
|
|
145
133
|
Json: "8"
|
|
146
134
|
};
|
|
135
|
+
const JSON_SAFE_DEPTH_LIMIT = 10000;
|
|
136
|
+
const EXIT = {};
|
|
137
|
+
function isJSONSafe(value) {
|
|
138
|
+
const stack = [value];
|
|
139
|
+
const ancestors = new Set();
|
|
140
|
+
while (stack.length) {
|
|
141
|
+
const v = stack.pop();
|
|
142
|
+
if (v === EXIT) {
|
|
143
|
+
ancestors.delete(stack.pop());
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (v === null) continue;
|
|
147
|
+
const t = typeof v;
|
|
148
|
+
if (t === "string" || t === "boolean") continue;
|
|
149
|
+
if (t === "number") {
|
|
150
|
+
if (!Number.isFinite(v)) return false;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (t !== "object") return false;
|
|
154
|
+
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
|
|
155
|
+
ancestors.add(v);
|
|
156
|
+
stack.push(v, EXIT);
|
|
157
|
+
if (Array.isArray(v)) {
|
|
158
|
+
for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
159
|
+
} else {
|
|
160
|
+
const proto = Object.getPrototypeOf(v);
|
|
161
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
162
|
+
for (const k in v) stack.push(v[k]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
147
167
|
function getHeadersAndBody(body) {
|
|
148
168
|
switch (true) {
|
|
149
169
|
case typeof body === "string":
|
|
@@ -237,13 +257,11 @@ async function extractBody(source, codecOptions) {
|
|
|
237
257
|
return undefined;
|
|
238
258
|
}
|
|
239
259
|
function createChunk(data) {
|
|
240
|
-
const
|
|
260
|
+
const encoder = new TextEncoder();
|
|
261
|
+
const encodeData = encoder.encode(data);
|
|
241
262
|
const bytes = encodeData.length;
|
|
242
|
-
const baseHex = bytes.toString(16);
|
|
243
|
-
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
244
|
-
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
245
263
|
const chunk = new Uint8Array(12 + bytes);
|
|
246
|
-
chunk.set(
|
|
264
|
+
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
|
|
247
265
|
chunk.set(encodeData, 12);
|
|
248
266
|
return chunk;
|
|
249
267
|
}
|
|
@@ -275,8 +293,8 @@ class ChunkReader {
|
|
|
275
293
|
}
|
|
276
294
|
await this.readChunk();
|
|
277
295
|
}
|
|
278
|
-
const
|
|
279
|
-
const bytes = Number.parseInt(
|
|
296
|
+
const decoder = new TextDecoder();
|
|
297
|
+
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
|
|
280
298
|
if (Number.isNaN(bytes)) {
|
|
281
299
|
throw new Error("Malformed server function stream.");
|
|
282
300
|
}
|
|
@@ -286,7 +304,7 @@ class ChunkReader {
|
|
|
286
304
|
}
|
|
287
305
|
await this.readChunk();
|
|
288
306
|
}
|
|
289
|
-
const partial =
|
|
307
|
+
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
|
|
290
308
|
this.buffer = this.buffer.subarray(12 + bytes);
|
|
291
309
|
return {
|
|
292
310
|
done: false,
|
|
@@ -305,7 +323,10 @@ class ChunkReader {
|
|
|
305
323
|
}
|
|
306
324
|
function serializeStream(value, codecOptions) {
|
|
307
325
|
return new ReadableStream({
|
|
308
|
-
start(controller) {
|
|
326
|
+
async start(controller) {
|
|
327
|
+
const {
|
|
328
|
+
serializeJSON
|
|
329
|
+
} = await import('@solidjs/web/serialization');
|
|
309
330
|
serializeJSON(value, {
|
|
310
331
|
...codecOptions,
|
|
311
332
|
onParse(node) {
|
|
@@ -328,6 +349,9 @@ async function deserializeStream(source, codecOptions) {
|
|
|
328
349
|
const reader = new ChunkReader(source.body);
|
|
329
350
|
const result = await reader.next();
|
|
330
351
|
if (!result.done) {
|
|
352
|
+
const {
|
|
353
|
+
createJSONDeserializer
|
|
354
|
+
} = await import('@solidjs/web/serialization/decode');
|
|
331
355
|
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
332
356
|
function interpretChunk(chunk) {
|
|
333
357
|
return deserializeChunk(JSON.parse(chunk));
|
|
@@ -357,6 +381,77 @@ async function decodeResponsePayload(response, codecOptions) {
|
|
|
357
381
|
};
|
|
358
382
|
}
|
|
359
383
|
|
|
384
|
+
const RequestContext = Symbol.for("solid.RequestContext");
|
|
385
|
+
function getRequestEvent() {
|
|
386
|
+
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
|
|
387
|
+
}
|
|
388
|
+
function reportLostHeaderWrite(method, name) {
|
|
389
|
+
const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
|
|
390
|
+
console.error(message);
|
|
391
|
+
}
|
|
392
|
+
function commitResponseStub(stub, {
|
|
393
|
+
allowLateLocation = false
|
|
394
|
+
} = {}) {
|
|
395
|
+
if (!stub || stub.committed) return stub;
|
|
396
|
+
stub.committed = true;
|
|
397
|
+
const headers = stub.headers;
|
|
398
|
+
if (!headers || typeof headers.set !== "function") return stub;
|
|
399
|
+
for (const method of ["set", "append", "delete"]) {
|
|
400
|
+
const original = headers[method].bind(headers);
|
|
401
|
+
headers[method] = function (name, ...rest) {
|
|
402
|
+
if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
|
|
403
|
+
return original(name, ...rest);
|
|
404
|
+
}
|
|
405
|
+
reportLostHeaderWrite(method, name);
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
return stub;
|
|
409
|
+
}
|
|
410
|
+
function copyInitHeaders(init) {
|
|
411
|
+
if (!init || !init.getSetCookie) return new Headers(init);
|
|
412
|
+
const headers = new Headers();
|
|
413
|
+
init.forEach((value, key) => {
|
|
414
|
+
if (key !== "set-cookie") headers.append(key, value);
|
|
415
|
+
});
|
|
416
|
+
for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
|
|
417
|
+
return headers;
|
|
418
|
+
}
|
|
419
|
+
const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
|
|
420
|
+
function fillsStubGap(key, headers, response) {
|
|
421
|
+
if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
|
|
422
|
+
if (response.body === null && (key === "content-type" || key === "content-length")) return false;
|
|
423
|
+
return !headers.has(key);
|
|
424
|
+
}
|
|
425
|
+
function commitEventResponse(response, event = getRequestEvent()) {
|
|
426
|
+
const stub = event && event.response;
|
|
427
|
+
if (!stub || !stub.headers || stub.committed) return response;
|
|
428
|
+
const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
|
|
429
|
+
commitResponseStub(stub);
|
|
430
|
+
let hasGaps = false;
|
|
431
|
+
stub.headers.forEach((value, key) => {
|
|
432
|
+
if (fillsStubGap(key, response.headers, response)) hasGaps = true;
|
|
433
|
+
});
|
|
434
|
+
if (!cookies.length && !hasGaps) return response;
|
|
435
|
+
try {
|
|
436
|
+
for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
|
|
437
|
+
stub.headers.forEach((value, key) => {
|
|
438
|
+
if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
|
|
439
|
+
});
|
|
440
|
+
return response;
|
|
441
|
+
} catch {
|
|
442
|
+
const headers = copyInitHeaders(response.headers);
|
|
443
|
+
for (const cookie of cookies) headers.append("Set-Cookie", cookie);
|
|
444
|
+
stub.headers.forEach((value, key) => {
|
|
445
|
+
if (fillsStubGap(key, headers, response)) headers.set(key, value);
|
|
446
|
+
});
|
|
447
|
+
return new Response(response.body, {
|
|
448
|
+
status: response.status,
|
|
449
|
+
statusText: response.statusText,
|
|
450
|
+
headers
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
360
455
|
function encodeInputValue(value) {
|
|
361
456
|
if (value instanceof FormData) return {
|
|
362
457
|
$f: [...value.entries()].filter(([, v]) => typeof v === "string")
|
|
@@ -386,13 +481,16 @@ function encodeFlashCookie(url, result, input, thrown) {
|
|
|
386
481
|
thrown: !!thrown,
|
|
387
482
|
input: input.map(encodeInputValue)
|
|
388
483
|
};
|
|
389
|
-
return
|
|
484
|
+
return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
|
|
485
|
+
secure: true,
|
|
486
|
+
httpOnly: true
|
|
487
|
+
});
|
|
390
488
|
}
|
|
391
489
|
function decodeFlashCookie(cookieHeader) {
|
|
392
|
-
const match =
|
|
490
|
+
const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
|
|
393
491
|
if (!match) return;
|
|
394
492
|
try {
|
|
395
|
-
const payload = JSON.parse(
|
|
493
|
+
const payload = JSON.parse(match);
|
|
396
494
|
if (!payload || !payload.result) return;
|
|
397
495
|
const result = payload.error ? new Error(payload.result) : payload.result;
|
|
398
496
|
return {
|
|
@@ -408,6 +506,7 @@ function decodeFlashCookie(cookieHeader) {
|
|
|
408
506
|
|
|
409
507
|
const config = {
|
|
410
508
|
provideEvent: undefined,
|
|
509
|
+
wrapInvocation: undefined,
|
|
411
510
|
collectFlightData: undefined,
|
|
412
511
|
transformResult: undefined,
|
|
413
512
|
transformFlightResult: undefined,
|
|
@@ -417,6 +516,7 @@ const config = {
|
|
|
417
516
|
};
|
|
418
517
|
function configureServerFunctionsServer({
|
|
419
518
|
provideEvent,
|
|
519
|
+
wrapInvocation,
|
|
420
520
|
collectFlightData,
|
|
421
521
|
transformResult,
|
|
422
522
|
transformFlightResult,
|
|
@@ -426,6 +526,7 @@ function configureServerFunctionsServer({
|
|
|
426
526
|
codec
|
|
427
527
|
} = {}) {
|
|
428
528
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
529
|
+
if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
|
|
429
530
|
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
|
|
430
531
|
if (transformResult !== undefined) config.transformResult = transformResult;
|
|
431
532
|
if (transformFlightResult !== undefined) config.transformFlightResult = transformFlightResult;
|
|
@@ -443,7 +544,17 @@ function provideEvent(event, fn) {
|
|
|
443
544
|
const REGISTRATIONS = new Map();
|
|
444
545
|
const METHODS = new Map();
|
|
445
546
|
const INVOCATIONS = new WeakMap();
|
|
547
|
+
let rpcProvided = false;
|
|
548
|
+
function provideRPC() {
|
|
549
|
+
if (rpcProvided) return;
|
|
550
|
+
rpcProvided = true;
|
|
551
|
+
provideServerFunctionRPC({
|
|
552
|
+
GET,
|
|
553
|
+
decodeResponse
|
|
554
|
+
});
|
|
555
|
+
}
|
|
446
556
|
function registerServerFunction(id, callback) {
|
|
557
|
+
provideRPC();
|
|
447
558
|
REGISTRATIONS.set(id, callback);
|
|
448
559
|
return callback;
|
|
449
560
|
}
|
|
@@ -468,6 +579,7 @@ function createServerReference({
|
|
|
468
579
|
name
|
|
469
580
|
}) {
|
|
470
581
|
if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
|
|
582
|
+
provideRPC();
|
|
471
583
|
const metadata = name === undefined ? {} : {
|
|
472
584
|
name
|
|
473
585
|
};
|
|
@@ -491,7 +603,13 @@ function createServerReference({
|
|
|
491
603
|
});
|
|
492
604
|
evt.serverOnly = true;
|
|
493
605
|
const result = provideEvent(evt, () => {
|
|
494
|
-
|
|
606
|
+
const run = () => fn.apply(thisArg, args);
|
|
607
|
+
return config.wrapInvocation ? config.wrapInvocation(run, {
|
|
608
|
+
id,
|
|
609
|
+
args,
|
|
610
|
+
event: evt,
|
|
611
|
+
direct: true
|
|
612
|
+
}) : run();
|
|
495
613
|
});
|
|
496
614
|
const transform = config.transformDirectResult;
|
|
497
615
|
if (transform && result && typeof result.then === "function") {
|
|
@@ -573,7 +691,9 @@ async function foldFlightData(hook, event, headers, outcome, context = {}) {
|
|
|
573
691
|
return transformed;
|
|
574
692
|
}
|
|
575
693
|
}
|
|
576
|
-
return {
|
|
694
|
+
return outcome.value === undefined ? {
|
|
695
|
+
data
|
|
696
|
+
} : {
|
|
577
697
|
value: outcome.value,
|
|
578
698
|
data
|
|
579
699
|
};
|
|
@@ -633,6 +753,16 @@ function foldSetCookies(headers, setCookies) {
|
|
|
633
753
|
if (serialized) folded.set("cookie", serialized);
|
|
634
754
|
return folded;
|
|
635
755
|
}
|
|
756
|
+
function mergeResponseHeaders(target, source) {
|
|
757
|
+
source.forEach((value, key) => {
|
|
758
|
+
if (key !== "set-cookie") target.append(key, value);
|
|
759
|
+
});
|
|
760
|
+
if (source.getSetCookie) {
|
|
761
|
+
for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
|
|
762
|
+
} else if (source.has("set-cookie")) {
|
|
763
|
+
target.append("Set-Cookie", source.get("set-cookie"));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
636
766
|
const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
|
|
637
767
|
function createNoJSHandler({
|
|
638
768
|
base = ""
|
|
@@ -647,7 +777,8 @@ function createNoJSHandler({
|
|
|
647
777
|
let status = 303;
|
|
648
778
|
let headers;
|
|
649
779
|
if (result instanceof Response) {
|
|
650
|
-
headers = new Headers(
|
|
780
|
+
headers = new Headers();
|
|
781
|
+
mergeResponseHeaders(headers, result.headers);
|
|
651
782
|
if (result.headers.has("Location")) {
|
|
652
783
|
headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());
|
|
653
784
|
if (validRedirectStatuses.has(result.status)) status = result.status;
|
|
@@ -694,19 +825,46 @@ function encodeResult(value, headers, status, codec) {
|
|
|
694
825
|
headers
|
|
695
826
|
});
|
|
696
827
|
}
|
|
828
|
+
if (value === undefined) {
|
|
829
|
+
return new Response(null, {
|
|
830
|
+
status,
|
|
831
|
+
headers
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
try {
|
|
835
|
+
if (isJSONSafe(value)) {
|
|
836
|
+
headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
|
|
837
|
+
headers.set("Content-Type", "application/json");
|
|
838
|
+
return new Response(JSON.stringify(value), {
|
|
839
|
+
status,
|
|
840
|
+
headers
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
} catch {
|
|
844
|
+
}
|
|
697
845
|
const response = serializedResponse(value, headers, codec);
|
|
698
846
|
return status === 200 ? response : new Response(response.body, {
|
|
699
847
|
status,
|
|
700
848
|
headers
|
|
701
849
|
});
|
|
702
850
|
}
|
|
851
|
+
const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
|
|
852
|
+
let DEV = false === true;
|
|
853
|
+
function setServerFunctionsDev(dev) {
|
|
854
|
+
DEV = !!dev;
|
|
855
|
+
}
|
|
856
|
+
function sanitizeServerError(value) {
|
|
857
|
+
if (DEV) return value;
|
|
858
|
+
if (isSafeError(value)) return value;
|
|
859
|
+
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
|
|
860
|
+
}
|
|
703
861
|
async function handleServerFunctionRequest(request, options = {}) {
|
|
704
862
|
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
705
863
|
const url = new URL(request.url);
|
|
706
864
|
const instance = request.headers.get(INSTANCE_HEADER);
|
|
707
865
|
const functionId = resolveFunctionId(request, url);
|
|
708
866
|
if (!functionId) {
|
|
709
|
-
return new Response(
|
|
867
|
+
return new Response(DEV ? "Server function not found" : null, {
|
|
710
868
|
status: 404
|
|
711
869
|
});
|
|
712
870
|
}
|
|
@@ -714,12 +872,12 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
714
872
|
try {
|
|
715
873
|
serverFunction = getServerFunction(functionId);
|
|
716
874
|
} catch {
|
|
717
|
-
return new Response(
|
|
875
|
+
return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
|
|
718
876
|
status: 404
|
|
719
877
|
});
|
|
720
878
|
}
|
|
721
879
|
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
722
|
-
return new Response(
|
|
880
|
+
return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
|
|
723
881
|
status: 405,
|
|
724
882
|
headers: {
|
|
725
883
|
Allow: "POST"
|
|
@@ -733,6 +891,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
733
891
|
const provide = options.provideEvent || provideEvent;
|
|
734
892
|
const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
|
|
735
893
|
const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
|
|
894
|
+
const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
|
|
736
895
|
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
|
|
737
896
|
const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
|
|
738
897
|
const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
|
|
@@ -747,136 +906,148 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
747
906
|
transformFlightResult
|
|
748
907
|
};
|
|
749
908
|
const headers = new Headers();
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
909
|
+
const dispatch = async () => {
|
|
910
|
+
try {
|
|
911
|
+
let result = await provide(event, async () => {
|
|
912
|
+
INVOCATIONS.set(event, {
|
|
913
|
+
id: functionId
|
|
914
|
+
});
|
|
915
|
+
const run = () => serverFunction(...parsed);
|
|
916
|
+
return wrapInvocation ? wrapInvocation(run, {
|
|
917
|
+
id: functionId,
|
|
918
|
+
args: parsed,
|
|
919
|
+
event,
|
|
920
|
+
request,
|
|
921
|
+
direct: false
|
|
922
|
+
}) : run();
|
|
754
923
|
});
|
|
755
|
-
return serverFunction(...parsed);
|
|
756
|
-
});
|
|
757
|
-
if (transformResult) {
|
|
758
|
-
result = await transformResult(event, result, flightContext);
|
|
759
|
-
}
|
|
760
|
-
let status = 200;
|
|
761
|
-
let metadata;
|
|
762
|
-
if (isResponseEnvelope(result)) {
|
|
763
|
-
const {
|
|
764
|
-
response,
|
|
765
|
-
value
|
|
766
|
-
} = result;
|
|
767
|
-
if (!instance && !handleNoJS && response && response.body) {
|
|
768
|
-
return response;
|
|
769
|
-
}
|
|
770
|
-
if (response && response.headers) {
|
|
771
|
-
response.headers.forEach((val, key) => headers.append(key, val));
|
|
772
|
-
}
|
|
773
|
-
if (response && response.status && (response.status < 300 || response.status >= 400)) {
|
|
774
|
-
status = response.status;
|
|
775
|
-
}
|
|
776
|
-
metadata = response;
|
|
777
|
-
result = value;
|
|
778
|
-
} else if (result instanceof Response) {
|
|
779
|
-
if (result.headers && result.headers.has("X-Content-Raw")) return result;
|
|
780
|
-
if (instance) {
|
|
781
|
-
if (result.headers) {
|
|
782
|
-
result.headers.forEach((value, key) => headers.append(key, value));
|
|
783
|
-
}
|
|
784
|
-
if (result.status && (result.status < 300 || result.status >= 400)) {
|
|
785
|
-
status = result.status;
|
|
786
|
-
}
|
|
787
|
-
metadata = result;
|
|
788
|
-
if (result.body == null) {
|
|
789
|
-
result = null;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
if (collectsFlight) {
|
|
794
|
-
result = await foldFlightData(flightHook, event, headers, {
|
|
795
|
-
id: functionId,
|
|
796
|
-
value: result,
|
|
797
|
-
response: metadata,
|
|
798
|
-
request,
|
|
799
|
-
thrown: false
|
|
800
|
-
}, flightContext);
|
|
801
|
-
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
|
|
802
|
-
}
|
|
803
|
-
if (!instance) {
|
|
804
|
-
if (handleNoJS) return handleNoJS(result, request, parsed);
|
|
805
|
-
if (result instanceof Response) return result;
|
|
806
|
-
return encodeResult(result, headers, 200, codec);
|
|
807
|
-
}
|
|
808
|
-
return encodeResult(result, headers, status, codec);
|
|
809
|
-
} catch (x) {
|
|
810
|
-
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
811
924
|
if (transformResult) {
|
|
812
|
-
|
|
813
|
-
...flightContext,
|
|
814
|
-
thrown: true
|
|
815
|
-
});
|
|
925
|
+
result = await transformResult(event, result, flightContext);
|
|
816
926
|
}
|
|
817
927
|
let status = 200;
|
|
818
928
|
let metadata;
|
|
819
|
-
if (isResponseEnvelope(
|
|
929
|
+
if (isResponseEnvelope(result)) {
|
|
820
930
|
const {
|
|
821
931
|
response,
|
|
822
932
|
value
|
|
823
|
-
} =
|
|
933
|
+
} = result;
|
|
934
|
+
if (!instance && !handleNoJS && response && response.body) {
|
|
935
|
+
return response;
|
|
936
|
+
}
|
|
824
937
|
if (response && response.headers) {
|
|
825
|
-
|
|
938
|
+
mergeResponseHeaders(headers, response.headers);
|
|
826
939
|
}
|
|
827
|
-
if (response && response.status && (
|
|
940
|
+
if (response && response.status && (response.status < 300 || response.status >= 400)) {
|
|
828
941
|
status = response.status;
|
|
829
942
|
}
|
|
830
943
|
metadata = response;
|
|
831
|
-
|
|
832
|
-
} else if (
|
|
833
|
-
if (
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
944
|
+
result = value;
|
|
945
|
+
} else if (result instanceof Response) {
|
|
946
|
+
if (result.headers && result.headers.has("X-Content-Raw")) return result;
|
|
947
|
+
if (instance) {
|
|
948
|
+
if (result.headers) {
|
|
949
|
+
mergeResponseHeaders(headers, result.headers);
|
|
950
|
+
}
|
|
951
|
+
if (result.status && (result.status < 300 || result.status >= 400)) {
|
|
952
|
+
status = result.status;
|
|
953
|
+
}
|
|
954
|
+
metadata = result;
|
|
955
|
+
if (result.body == null) {
|
|
956
|
+
result = null;
|
|
957
|
+
}
|
|
842
958
|
}
|
|
843
959
|
}
|
|
844
960
|
if (collectsFlight) {
|
|
845
|
-
|
|
961
|
+
result = await foldFlightData(flightHook, event, headers, {
|
|
846
962
|
id: functionId,
|
|
847
|
-
value:
|
|
963
|
+
value: result,
|
|
848
964
|
response: metadata,
|
|
849
965
|
request,
|
|
850
|
-
thrown:
|
|
966
|
+
thrown: false
|
|
851
967
|
}, flightContext);
|
|
852
|
-
if (
|
|
853
|
-
|
|
854
|
-
|
|
968
|
+
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
|
|
969
|
+
}
|
|
970
|
+
if (!instance) {
|
|
971
|
+
if (handleNoJS) return handleNoJS(result, request, parsed);
|
|
972
|
+
if (result instanceof Response) return result;
|
|
973
|
+
return encodeResult(result, headers, 200, codec);
|
|
974
|
+
}
|
|
975
|
+
return encodeResult(result, headers, status, codec);
|
|
976
|
+
} catch (x) {
|
|
977
|
+
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
978
|
+
if (transformResult) {
|
|
979
|
+
x = await transformResult(event, x, {
|
|
980
|
+
...flightContext,
|
|
981
|
+
thrown: true
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
let status = 200;
|
|
985
|
+
let metadata;
|
|
986
|
+
if (isResponseEnvelope(x)) {
|
|
987
|
+
const {
|
|
988
|
+
response,
|
|
989
|
+
value
|
|
990
|
+
} = x;
|
|
991
|
+
if (response && response.headers) {
|
|
992
|
+
mergeResponseHeaders(headers, response.headers);
|
|
993
|
+
}
|
|
994
|
+
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
|
|
995
|
+
status = response.status;
|
|
996
|
+
}
|
|
997
|
+
metadata = response;
|
|
998
|
+
x = value;
|
|
999
|
+
} else if (x instanceof Response) {
|
|
1000
|
+
if (x.headers) {
|
|
1001
|
+
mergeResponseHeaders(headers, x.headers);
|
|
1002
|
+
}
|
|
1003
|
+
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
|
|
1004
|
+
status = x.status;
|
|
1005
|
+
}
|
|
1006
|
+
metadata = x;
|
|
1007
|
+
if (x.body == null) {
|
|
1008
|
+
x = null;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (collectsFlight) {
|
|
1012
|
+
x = await foldFlightData(flightHook, event, headers, {
|
|
1013
|
+
id: functionId,
|
|
1014
|
+
value: x,
|
|
1015
|
+
response: metadata,
|
|
1016
|
+
request,
|
|
1017
|
+
thrown: true
|
|
1018
|
+
}, flightContext);
|
|
1019
|
+
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
|
|
1020
|
+
x.headers.set(ERROR_HEADER, "true");
|
|
1021
|
+
return x;
|
|
1022
|
+
}
|
|
855
1023
|
}
|
|
1024
|
+
headers.set(ERROR_HEADER, "true");
|
|
1025
|
+
if (!instance) {
|
|
1026
|
+
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
|
|
1027
|
+
if (x instanceof Response) return x;
|
|
1028
|
+
}
|
|
1029
|
+
return encodeResult(x, headers, status, codec);
|
|
856
1030
|
}
|
|
857
|
-
|
|
1031
|
+
const safe = sanitizeServerError(x);
|
|
858
1032
|
if (!instance) {
|
|
859
|
-
if (handleNoJS) return handleNoJS(
|
|
860
|
-
|
|
1033
|
+
if (handleNoJS) return handleNoJS(safe, request, parsed, true);
|
|
1034
|
+
const message = safe instanceof Error ? safe.message : String(safe);
|
|
1035
|
+
return new Response(DEV ? message : null, {
|
|
1036
|
+
status: 500
|
|
1037
|
+
});
|
|
861
1038
|
}
|
|
862
|
-
|
|
1039
|
+
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
|
|
1040
|
+
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
1041
|
+
return encodeResult(safe, headers, 200, codec);
|
|
863
1042
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
const message = x instanceof Error ? x.message : String(x);
|
|
867
|
-
return new Response(process.env.NODE_ENV === "development" ? message : null, {
|
|
868
|
-
status: 500
|
|
869
|
-
});
|
|
870
|
-
}
|
|
871
|
-
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
|
|
872
|
-
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
873
|
-
return encodeResult(x, headers, 200, codec);
|
|
874
|
-
}
|
|
1043
|
+
};
|
|
1044
|
+
return commitEventResponse(await dispatch(), event);
|
|
875
1045
|
}
|
|
876
1046
|
|
|
877
1047
|
exports.ERROR_HEADER = ERROR_HEADER;
|
|
878
1048
|
exports.FLASH_COOKIE = FLASH_COOKIE;
|
|
879
1049
|
exports.FUNCTION_HEADER = FUNCTION_HEADER;
|
|
1050
|
+
exports.GENERIC_SERVER_ERROR_MESSAGE = GENERIC_SERVER_ERROR_MESSAGE;
|
|
880
1051
|
exports.GET = GET;
|
|
881
1052
|
exports.INSTANCE_HEADER = INSTANCE_HEADER;
|
|
882
1053
|
exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
|
|
@@ -900,5 +1071,7 @@ exports.hasFlashCookie = hasFlashCookie;
|
|
|
900
1071
|
exports.isServerFunction = isServerFunction;
|
|
901
1072
|
exports.registerServerFunction = registerServerFunction;
|
|
902
1073
|
exports.registerServerReference = registerServerReference;
|
|
1074
|
+
exports.sanitizeServerError = sanitizeServerError;
|
|
1075
|
+
exports.setServerFunctionsDev = setServerFunctionsDev;
|
|
903
1076
|
exports.subscribeFlightData = subscribeFlightData;
|
|
904
1077
|
exports.withMeta = withMeta;
|