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