@solidjs/web 2.0.0-rc.1 → 2.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev.cjs +116 -66
- package/dist/dev.js +109 -65
- package/dist/server.cjs +386 -109
- package/dist/server.js +378 -113
- package/dist/web.cjs +113 -64
- package/dist/web.js +106 -63
- package/frames/dist/client.cjs +100 -14
- package/frames/dist/client.dev.cjs +103 -14
- package/frames/dist/client.dev.js +104 -15
- package/frames/dist/client.js +101 -15
- package/frames/dist/server.cjs +285 -154
- package/frames/dist/server.js +287 -156
- package/package.json +20 -22
- package/serialization/dist/decode.cjs +22 -5
- package/serialization/dist/decode.js +23 -6
- package/serialization/dist/serialization.cjs +26 -13
- package/serialization/dist/serialization.js +26 -8
- package/serialization/types/index.d.ts +69 -157
- package/serialization/types/serializer-decode.d.ts +89 -112
- package/serialization/types/serializer.d.ts +94 -0
- package/serialization/types-cjs/index.d.cts +69 -157
- package/serialization/types-cjs/serializer-decode.d.cts +89 -112
- package/serialization/types-cjs/serializer.d.cts +94 -0
- package/server-functions/dist/client.cjs +48 -7
- package/server-functions/dist/client.js +48 -8
- package/server-functions/dist/server.cjs +76 -5
- package/server-functions/dist/server.dev.cjs +76 -5
- package/server-functions/dist/server.dev.js +76 -6
- package/server-functions/dist/server.js +76 -6
- package/types/client.d.ts +150 -290
- package/types/constants.d.ts +18 -0
- package/types/cookies.d.ts +31 -75
- package/types/frames/frame-client.d.ts +287 -259
- package/types/frames/frame-container-plugin.d.ts +71 -0
- package/types/frames/frame-sink.d.ts +58 -160
- package/types/frames/frame-transport.d.ts +161 -194
- package/types/frames/serializer-decode.d.ts +159 -0
- package/types/frames/serializer.d.ts +69 -157
- package/types/head.d.ts +16 -0
- package/types/index.d.ts +0 -65
- package/types/index.server.d.ts +125 -0
- package/types/jsx.d.ts +5 -13
- package/types/reconcile.d.ts +1 -0
- package/types/render.d.ts +4 -0
- package/types/response.d.ts +41 -153
- package/types/serializer-decode.d.ts +89 -112
- package/types/serializer.d.ts +69 -157
- package/types/server-functions/client.d.ts +101 -199
- package/types/server-functions/flash.d.ts +10 -30
- package/types/server-functions/registry.d.ts +63 -0
- package/types/server-functions/rich-args.d.ts +1 -10
- package/types/server-functions/server.d.ts +301 -518
- package/types/server-functions/shared.d.ts +112 -458
- package/types/server-mock.d.ts +13 -4
- package/types/server.d.ts +256 -427
- package/types-cjs/client.d.cts +150 -290
- package/types-cjs/constants.d.cts +18 -0
- package/types-cjs/cookies.d.cts +31 -75
- package/types-cjs/frames/frame-client.d.cts +287 -259
- package/types-cjs/frames/frame-container-plugin.d.cts +71 -0
- package/types-cjs/frames/frame-sink.d.cts +58 -160
- package/types-cjs/frames/frame-transport.d.cts +161 -194
- package/types-cjs/frames/serializer-decode.d.cts +159 -0
- package/types-cjs/frames/serializer.d.cts +69 -157
- package/types-cjs/head.d.cts +16 -0
- package/types-cjs/index.d.cts +0 -65
- package/types-cjs/index.server.d.cts +125 -0
- package/types-cjs/jsx.d.cts +5 -13
- package/types-cjs/reconcile.d.cts +1 -0
- package/types-cjs/render.d.cts +4 -0
- package/types-cjs/response.d.cts +41 -153
- package/types-cjs/serializer-decode.d.cts +89 -112
- package/types-cjs/serializer.d.cts +69 -157
- package/types-cjs/server-functions/client.d.cts +101 -199
- package/types-cjs/server-functions/flash.d.cts +10 -30
- package/types-cjs/server-functions/registry.d.cts +63 -0
- package/types-cjs/server-functions/rich-args.d.cts +1 -10
- package/types-cjs/server-functions/server.d.cts +301 -518
- package/types-cjs/server-functions/shared.d.cts +112 -458
- package/types-cjs/server-mock.d.cts +13 -4
- package/types-cjs/server.d.cts +256 -427
- package/LICENSE +0 -21
- package/types/core.d.ts +0 -6
- package/types-cjs/core.d.cts +0 -6
|
@@ -362,6 +362,7 @@ async function decodeResponsePayload(response, codecOptions) {
|
|
|
362
362
|
};
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
|
|
365
366
|
const RequestContext = Symbol.for("solid.RequestContext");
|
|
366
367
|
function getRequestEvent() {
|
|
367
368
|
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;
|
|
@@ -493,7 +494,8 @@ const config = {
|
|
|
493
494
|
transformFlightResult: undefined,
|
|
494
495
|
transformDirectResult: undefined,
|
|
495
496
|
handleNoJS: undefined,
|
|
496
|
-
endpoint: "/_server"
|
|
497
|
+
endpoint: "/_server",
|
|
498
|
+
csrf: true
|
|
497
499
|
};
|
|
498
500
|
function configureServerFunctionsServer({
|
|
499
501
|
provideEvent,
|
|
@@ -504,6 +506,7 @@ function configureServerFunctionsServer({
|
|
|
504
506
|
transformDirectResult,
|
|
505
507
|
handleNoJS,
|
|
506
508
|
endpoint,
|
|
509
|
+
csrf,
|
|
507
510
|
codec
|
|
508
511
|
} = {}) {
|
|
509
512
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
@@ -514,6 +517,7 @@ function configureServerFunctionsServer({
|
|
|
514
517
|
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
515
518
|
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
|
|
516
519
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
520
|
+
if (csrf !== undefined) config.csrf = csrf;
|
|
517
521
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
518
522
|
}
|
|
519
523
|
function provideEvent(event, fn) {
|
|
@@ -950,31 +954,96 @@ function sanitizeServerError(value) {
|
|
|
950
954
|
if (isSafeError(value)) return value;
|
|
951
955
|
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
|
|
952
956
|
}
|
|
957
|
+
function observeServerFunctionCalls() {
|
|
958
|
+
return () => {};
|
|
959
|
+
}
|
|
960
|
+
async function matchesOrigin(origin, request, matcher) {
|
|
961
|
+
if (matcher === undefined) return origin === new URL(request.url).origin;
|
|
962
|
+
if (typeof matcher === "function") return !!(await matcher(origin, request));
|
|
963
|
+
return Array.isArray(matcher) ? matcher.includes(origin) : origin === matcher;
|
|
964
|
+
}
|
|
965
|
+
async function allowsServerFunctionRequest(request, options) {
|
|
966
|
+
const fetchSite = request.headers.get("Sec-Fetch-Site");
|
|
967
|
+
if (fetchSite === "same-origin") return true;
|
|
968
|
+
if (fetchSite === "same-site" || fetchSite === "cross-site" || fetchSite === "none") {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
const origin = request.headers.get("Origin");
|
|
972
|
+
if (origin !== null) return matchesOrigin(origin, request, options.origin);
|
|
973
|
+
const referer = request.headers.get("Referer");
|
|
974
|
+
if (referer !== null) {
|
|
975
|
+
try {
|
|
976
|
+
return matchesOrigin(new URL(referer).origin, request, options.origin);
|
|
977
|
+
} catch {
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return options.allowRequestsWithoutOriginCheck === true;
|
|
982
|
+
}
|
|
983
|
+
const CSRF_VARY = ["Sec-Fetch-Site", "Origin", "Referer"];
|
|
984
|
+
function withCSRFVary(response) {
|
|
985
|
+
const current = response.headers.get("Vary");
|
|
986
|
+
if (current === "*") return response;
|
|
987
|
+
const values = current ? current.split(",").map(value => value.trim()) : [];
|
|
988
|
+
const names = new Set(values.map(value => value.toLowerCase()));
|
|
989
|
+
for (const value of CSRF_VARY) {
|
|
990
|
+
if (!names.has(value.toLowerCase())) values.push(value);
|
|
991
|
+
}
|
|
992
|
+
const vary = values.join(", ");
|
|
993
|
+
try {
|
|
994
|
+
response.headers.set("Vary", vary);
|
|
995
|
+
return response;
|
|
996
|
+
} catch {
|
|
997
|
+
const headers = new Headers(response.headers);
|
|
998
|
+
headers.set("Vary", vary);
|
|
999
|
+
return new Response(response.body, {
|
|
1000
|
+
status: response.status,
|
|
1001
|
+
statusText: response.statusText,
|
|
1002
|
+
headers
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function forbiddenResponse() {
|
|
1007
|
+
return withCSRFVary(new Response(DEV ? "Forbidden" : null, {
|
|
1008
|
+
status: 403,
|
|
1009
|
+
headers: {
|
|
1010
|
+
"Cache-Control": "no-store"
|
|
1011
|
+
}
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
953
1014
|
async function handleServerFunctionRequest(request, options = {}) {
|
|
954
1015
|
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
955
1016
|
const url = new URL(request.url);
|
|
1017
|
+
const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
|
|
1018
|
+
const protectsRequest = csrf !== false;
|
|
1019
|
+
if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
|
|
1020
|
+
return forbiddenResponse();
|
|
1021
|
+
}
|
|
956
1022
|
const instance = request.headers.get(INSTANCE_HEADER);
|
|
957
1023
|
const functionId = resolveFunctionId(request, url);
|
|
958
1024
|
if (!functionId) {
|
|
959
|
-
|
|
1025
|
+
const response = new Response(DEV ? "Server function not found" : null, {
|
|
960
1026
|
status: 404
|
|
961
1027
|
});
|
|
1028
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
962
1029
|
}
|
|
963
1030
|
let serverFunction;
|
|
964
1031
|
try {
|
|
965
1032
|
serverFunction = getServerFunction(functionId);
|
|
966
1033
|
} catch {
|
|
967
|
-
|
|
1034
|
+
const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
|
|
968
1035
|
status: 404
|
|
969
1036
|
});
|
|
1037
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
970
1038
|
}
|
|
971
1039
|
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
972
|
-
|
|
1040
|
+
const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
|
|
973
1041
|
status: 405,
|
|
974
1042
|
headers: {
|
|
975
1043
|
Allow: "POST"
|
|
976
1044
|
}
|
|
977
1045
|
});
|
|
1046
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
978
1047
|
}
|
|
979
1048
|
const event = options.createEvent ? options.createEvent(request) : {
|
|
980
1049
|
request,
|
|
@@ -1133,7 +1202,8 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1133
1202
|
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1134
1203
|
}
|
|
1135
1204
|
};
|
|
1136
|
-
|
|
1205
|
+
const response = commitEventResponse(await dispatch(), event);
|
|
1206
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
1137
1207
|
}
|
|
1138
1208
|
|
|
1139
1209
|
exports.ERROR_HEADER = ERROR_HEADER;
|
|
@@ -1162,6 +1232,7 @@ exports.handleServerFunctionRequest = handleServerFunctionRequest;
|
|
|
1162
1232
|
exports.hasFlashCookie = hasFlashCookie;
|
|
1163
1233
|
exports.isServerFunction = isServerFunction;
|
|
1164
1234
|
exports.live = live;
|
|
1235
|
+
exports.observeServerFunctionCalls = observeServerFunctionCalls;
|
|
1165
1236
|
exports.registerServerFunction = registerServerFunction;
|
|
1166
1237
|
exports.registerServerReference = registerServerReference;
|
|
1167
1238
|
exports.sanitizeServerError = sanitizeServerError;
|
|
@@ -362,6 +362,7 @@ async function decodeResponsePayload(response, codecOptions) {
|
|
|
362
362
|
};
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
|
|
365
366
|
const RequestContext = Symbol.for("solid.RequestContext");
|
|
366
367
|
function getRequestEvent() {
|
|
367
368
|
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;
|
|
@@ -493,7 +494,8 @@ const config = {
|
|
|
493
494
|
transformFlightResult: undefined,
|
|
494
495
|
transformDirectResult: undefined,
|
|
495
496
|
handleNoJS: undefined,
|
|
496
|
-
endpoint: "/_server"
|
|
497
|
+
endpoint: "/_server",
|
|
498
|
+
csrf: true
|
|
497
499
|
};
|
|
498
500
|
function configureServerFunctionsServer({
|
|
499
501
|
provideEvent,
|
|
@@ -504,6 +506,7 @@ function configureServerFunctionsServer({
|
|
|
504
506
|
transformDirectResult,
|
|
505
507
|
handleNoJS,
|
|
506
508
|
endpoint,
|
|
509
|
+
csrf,
|
|
507
510
|
codec
|
|
508
511
|
} = {}) {
|
|
509
512
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
@@ -514,6 +517,7 @@ function configureServerFunctionsServer({
|
|
|
514
517
|
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
515
518
|
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
|
|
516
519
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
520
|
+
if (csrf !== undefined) config.csrf = csrf;
|
|
517
521
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
518
522
|
}
|
|
519
523
|
function provideEvent(event, fn) {
|
|
@@ -950,31 +954,96 @@ function sanitizeServerError(value) {
|
|
|
950
954
|
if (isSafeError(value)) return value;
|
|
951
955
|
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
|
|
952
956
|
}
|
|
957
|
+
function observeServerFunctionCalls() {
|
|
958
|
+
return () => {};
|
|
959
|
+
}
|
|
960
|
+
async function matchesOrigin(origin, request, matcher) {
|
|
961
|
+
if (matcher === undefined) return origin === new URL(request.url).origin;
|
|
962
|
+
if (typeof matcher === "function") return !!(await matcher(origin, request));
|
|
963
|
+
return Array.isArray(matcher) ? matcher.includes(origin) : origin === matcher;
|
|
964
|
+
}
|
|
965
|
+
async function allowsServerFunctionRequest(request, options) {
|
|
966
|
+
const fetchSite = request.headers.get("Sec-Fetch-Site");
|
|
967
|
+
if (fetchSite === "same-origin") return true;
|
|
968
|
+
if (fetchSite === "same-site" || fetchSite === "cross-site" || fetchSite === "none") {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
const origin = request.headers.get("Origin");
|
|
972
|
+
if (origin !== null) return matchesOrigin(origin, request, options.origin);
|
|
973
|
+
const referer = request.headers.get("Referer");
|
|
974
|
+
if (referer !== null) {
|
|
975
|
+
try {
|
|
976
|
+
return matchesOrigin(new URL(referer).origin, request, options.origin);
|
|
977
|
+
} catch {
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return options.allowRequestsWithoutOriginCheck === true;
|
|
982
|
+
}
|
|
983
|
+
const CSRF_VARY = ["Sec-Fetch-Site", "Origin", "Referer"];
|
|
984
|
+
function withCSRFVary(response) {
|
|
985
|
+
const current = response.headers.get("Vary");
|
|
986
|
+
if (current === "*") return response;
|
|
987
|
+
const values = current ? current.split(",").map(value => value.trim()) : [];
|
|
988
|
+
const names = new Set(values.map(value => value.toLowerCase()));
|
|
989
|
+
for (const value of CSRF_VARY) {
|
|
990
|
+
if (!names.has(value.toLowerCase())) values.push(value);
|
|
991
|
+
}
|
|
992
|
+
const vary = values.join(", ");
|
|
993
|
+
try {
|
|
994
|
+
response.headers.set("Vary", vary);
|
|
995
|
+
return response;
|
|
996
|
+
} catch {
|
|
997
|
+
const headers = new Headers(response.headers);
|
|
998
|
+
headers.set("Vary", vary);
|
|
999
|
+
return new Response(response.body, {
|
|
1000
|
+
status: response.status,
|
|
1001
|
+
statusText: response.statusText,
|
|
1002
|
+
headers
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function forbiddenResponse() {
|
|
1007
|
+
return withCSRFVary(new Response(DEV ? "Forbidden" : null, {
|
|
1008
|
+
status: 403,
|
|
1009
|
+
headers: {
|
|
1010
|
+
"Cache-Control": "no-store"
|
|
1011
|
+
}
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
953
1014
|
async function handleServerFunctionRequest(request, options = {}) {
|
|
954
1015
|
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
955
1016
|
const url = new URL(request.url);
|
|
1017
|
+
const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
|
|
1018
|
+
const protectsRequest = csrf !== false;
|
|
1019
|
+
if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
|
|
1020
|
+
return forbiddenResponse();
|
|
1021
|
+
}
|
|
956
1022
|
const instance = request.headers.get(INSTANCE_HEADER);
|
|
957
1023
|
const functionId = resolveFunctionId(request, url);
|
|
958
1024
|
if (!functionId) {
|
|
959
|
-
|
|
1025
|
+
const response = new Response(DEV ? "Server function not found" : null, {
|
|
960
1026
|
status: 404
|
|
961
1027
|
});
|
|
1028
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
962
1029
|
}
|
|
963
1030
|
let serverFunction;
|
|
964
1031
|
try {
|
|
965
1032
|
serverFunction = getServerFunction(functionId);
|
|
966
1033
|
} catch {
|
|
967
|
-
|
|
1034
|
+
const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
|
|
968
1035
|
status: 404
|
|
969
1036
|
});
|
|
1037
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
970
1038
|
}
|
|
971
1039
|
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
972
|
-
|
|
1040
|
+
const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
|
|
973
1041
|
status: 405,
|
|
974
1042
|
headers: {
|
|
975
1043
|
Allow: "POST"
|
|
976
1044
|
}
|
|
977
1045
|
});
|
|
1046
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
978
1047
|
}
|
|
979
1048
|
const event = options.createEvent ? options.createEvent(request) : {
|
|
980
1049
|
request,
|
|
@@ -1133,7 +1202,8 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1133
1202
|
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1134
1203
|
}
|
|
1135
1204
|
};
|
|
1136
|
-
|
|
1205
|
+
const response = commitEventResponse(await dispatch(), event);
|
|
1206
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
1137
1207
|
}
|
|
1138
1208
|
|
|
1139
1209
|
exports.ERROR_HEADER = ERROR_HEADER;
|
|
@@ -1162,6 +1232,7 @@ exports.handleServerFunctionRequest = handleServerFunctionRequest;
|
|
|
1162
1232
|
exports.hasFlashCookie = hasFlashCookie;
|
|
1163
1233
|
exports.isServerFunction = isServerFunction;
|
|
1164
1234
|
exports.live = live;
|
|
1235
|
+
exports.observeServerFunctionCalls = observeServerFunctionCalls;
|
|
1165
1236
|
exports.registerServerFunction = registerServerFunction;
|
|
1166
1237
|
exports.registerServerReference = registerServerReference;
|
|
1167
1238
|
exports.sanitizeServerError = sanitizeServerError;
|
|
@@ -360,6 +360,7 @@ async function decodeResponsePayload(response, codecOptions) {
|
|
|
360
360
|
};
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
|
|
363
364
|
const RequestContext = Symbol.for("solid.RequestContext");
|
|
364
365
|
function getRequestEvent() {
|
|
365
366
|
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;
|
|
@@ -491,7 +492,8 @@ const config = {
|
|
|
491
492
|
transformFlightResult: undefined,
|
|
492
493
|
transformDirectResult: undefined,
|
|
493
494
|
handleNoJS: undefined,
|
|
494
|
-
endpoint: "/_server"
|
|
495
|
+
endpoint: "/_server",
|
|
496
|
+
csrf: true
|
|
495
497
|
};
|
|
496
498
|
function configureServerFunctionsServer({
|
|
497
499
|
provideEvent,
|
|
@@ -502,6 +504,7 @@ function configureServerFunctionsServer({
|
|
|
502
504
|
transformDirectResult,
|
|
503
505
|
handleNoJS,
|
|
504
506
|
endpoint,
|
|
507
|
+
csrf,
|
|
505
508
|
codec
|
|
506
509
|
} = {}) {
|
|
507
510
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
@@ -512,6 +515,7 @@ function configureServerFunctionsServer({
|
|
|
512
515
|
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
513
516
|
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
|
|
514
517
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
518
|
+
if (csrf !== undefined) config.csrf = csrf;
|
|
515
519
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
516
520
|
}
|
|
517
521
|
function provideEvent(event, fn) {
|
|
@@ -948,31 +952,96 @@ function sanitizeServerError(value) {
|
|
|
948
952
|
if (isSafeError(value)) return value;
|
|
949
953
|
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
|
|
950
954
|
}
|
|
955
|
+
function observeServerFunctionCalls() {
|
|
956
|
+
return () => {};
|
|
957
|
+
}
|
|
958
|
+
async function matchesOrigin(origin, request, matcher) {
|
|
959
|
+
if (matcher === undefined) return origin === new URL(request.url).origin;
|
|
960
|
+
if (typeof matcher === "function") return !!(await matcher(origin, request));
|
|
961
|
+
return Array.isArray(matcher) ? matcher.includes(origin) : origin === matcher;
|
|
962
|
+
}
|
|
963
|
+
async function allowsServerFunctionRequest(request, options) {
|
|
964
|
+
const fetchSite = request.headers.get("Sec-Fetch-Site");
|
|
965
|
+
if (fetchSite === "same-origin") return true;
|
|
966
|
+
if (fetchSite === "same-site" || fetchSite === "cross-site" || fetchSite === "none") {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
const origin = request.headers.get("Origin");
|
|
970
|
+
if (origin !== null) return matchesOrigin(origin, request, options.origin);
|
|
971
|
+
const referer = request.headers.get("Referer");
|
|
972
|
+
if (referer !== null) {
|
|
973
|
+
try {
|
|
974
|
+
return matchesOrigin(new URL(referer).origin, request, options.origin);
|
|
975
|
+
} catch {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return options.allowRequestsWithoutOriginCheck === true;
|
|
980
|
+
}
|
|
981
|
+
const CSRF_VARY = ["Sec-Fetch-Site", "Origin", "Referer"];
|
|
982
|
+
function withCSRFVary(response) {
|
|
983
|
+
const current = response.headers.get("Vary");
|
|
984
|
+
if (current === "*") return response;
|
|
985
|
+
const values = current ? current.split(",").map(value => value.trim()) : [];
|
|
986
|
+
const names = new Set(values.map(value => value.toLowerCase()));
|
|
987
|
+
for (const value of CSRF_VARY) {
|
|
988
|
+
if (!names.has(value.toLowerCase())) values.push(value);
|
|
989
|
+
}
|
|
990
|
+
const vary = values.join(", ");
|
|
991
|
+
try {
|
|
992
|
+
response.headers.set("Vary", vary);
|
|
993
|
+
return response;
|
|
994
|
+
} catch {
|
|
995
|
+
const headers = new Headers(response.headers);
|
|
996
|
+
headers.set("Vary", vary);
|
|
997
|
+
return new Response(response.body, {
|
|
998
|
+
status: response.status,
|
|
999
|
+
statusText: response.statusText,
|
|
1000
|
+
headers
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function forbiddenResponse() {
|
|
1005
|
+
return withCSRFVary(new Response(DEV ? "Forbidden" : null, {
|
|
1006
|
+
status: 403,
|
|
1007
|
+
headers: {
|
|
1008
|
+
"Cache-Control": "no-store"
|
|
1009
|
+
}
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
951
1012
|
async function handleServerFunctionRequest(request, options = {}) {
|
|
952
1013
|
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
953
1014
|
const url = new URL(request.url);
|
|
1015
|
+
const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
|
|
1016
|
+
const protectsRequest = csrf !== false;
|
|
1017
|
+
if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
|
|
1018
|
+
return forbiddenResponse();
|
|
1019
|
+
}
|
|
954
1020
|
const instance = request.headers.get(INSTANCE_HEADER);
|
|
955
1021
|
const functionId = resolveFunctionId(request, url);
|
|
956
1022
|
if (!functionId) {
|
|
957
|
-
|
|
1023
|
+
const response = new Response(DEV ? "Server function not found" : null, {
|
|
958
1024
|
status: 404
|
|
959
1025
|
});
|
|
1026
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
960
1027
|
}
|
|
961
1028
|
let serverFunction;
|
|
962
1029
|
try {
|
|
963
1030
|
serverFunction = getServerFunction(functionId);
|
|
964
1031
|
} catch {
|
|
965
|
-
|
|
1032
|
+
const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
|
|
966
1033
|
status: 404
|
|
967
1034
|
});
|
|
1035
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
968
1036
|
}
|
|
969
1037
|
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
970
|
-
|
|
1038
|
+
const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
|
|
971
1039
|
status: 405,
|
|
972
1040
|
headers: {
|
|
973
1041
|
Allow: "POST"
|
|
974
1042
|
}
|
|
975
1043
|
});
|
|
1044
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
976
1045
|
}
|
|
977
1046
|
const event = options.createEvent ? options.createEvent(request) : {
|
|
978
1047
|
request,
|
|
@@ -1131,7 +1200,8 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1131
1200
|
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1132
1201
|
}
|
|
1133
1202
|
};
|
|
1134
|
-
|
|
1203
|
+
const response = commitEventResponse(await dispatch(), event);
|
|
1204
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
1135
1205
|
}
|
|
1136
1206
|
|
|
1137
|
-
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, live, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
1207
|
+
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, live, observeServerFunctionCalls, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
@@ -360,6 +360,7 @@ async function decodeResponsePayload(response, codecOptions) {
|
|
|
360
360
|
};
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
typeof setImmediate === "function" ? setImmediate : fn => setTimeout(fn, 0);
|
|
363
364
|
const RequestContext = Symbol.for("solid.RequestContext");
|
|
364
365
|
function getRequestEvent() {
|
|
365
366
|
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;
|
|
@@ -491,7 +492,8 @@ const config = {
|
|
|
491
492
|
transformFlightResult: undefined,
|
|
492
493
|
transformDirectResult: undefined,
|
|
493
494
|
handleNoJS: undefined,
|
|
494
|
-
endpoint: "/_server"
|
|
495
|
+
endpoint: "/_server",
|
|
496
|
+
csrf: true
|
|
495
497
|
};
|
|
496
498
|
function configureServerFunctionsServer({
|
|
497
499
|
provideEvent,
|
|
@@ -502,6 +504,7 @@ function configureServerFunctionsServer({
|
|
|
502
504
|
transformDirectResult,
|
|
503
505
|
handleNoJS,
|
|
504
506
|
endpoint,
|
|
507
|
+
csrf,
|
|
505
508
|
codec
|
|
506
509
|
} = {}) {
|
|
507
510
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
@@ -512,6 +515,7 @@ function configureServerFunctionsServer({
|
|
|
512
515
|
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
513
516
|
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
|
|
514
517
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
518
|
+
if (csrf !== undefined) config.csrf = csrf;
|
|
515
519
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
516
520
|
}
|
|
517
521
|
function provideEvent(event, fn) {
|
|
@@ -948,31 +952,96 @@ function sanitizeServerError(value) {
|
|
|
948
952
|
if (isSafeError(value)) return value;
|
|
949
953
|
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
|
|
950
954
|
}
|
|
955
|
+
function observeServerFunctionCalls() {
|
|
956
|
+
return () => {};
|
|
957
|
+
}
|
|
958
|
+
async function matchesOrigin(origin, request, matcher) {
|
|
959
|
+
if (matcher === undefined) return origin === new URL(request.url).origin;
|
|
960
|
+
if (typeof matcher === "function") return !!(await matcher(origin, request));
|
|
961
|
+
return Array.isArray(matcher) ? matcher.includes(origin) : origin === matcher;
|
|
962
|
+
}
|
|
963
|
+
async function allowsServerFunctionRequest(request, options) {
|
|
964
|
+
const fetchSite = request.headers.get("Sec-Fetch-Site");
|
|
965
|
+
if (fetchSite === "same-origin") return true;
|
|
966
|
+
if (fetchSite === "same-site" || fetchSite === "cross-site" || fetchSite === "none") {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
const origin = request.headers.get("Origin");
|
|
970
|
+
if (origin !== null) return matchesOrigin(origin, request, options.origin);
|
|
971
|
+
const referer = request.headers.get("Referer");
|
|
972
|
+
if (referer !== null) {
|
|
973
|
+
try {
|
|
974
|
+
return matchesOrigin(new URL(referer).origin, request, options.origin);
|
|
975
|
+
} catch {
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
return options.allowRequestsWithoutOriginCheck === true;
|
|
980
|
+
}
|
|
981
|
+
const CSRF_VARY = ["Sec-Fetch-Site", "Origin", "Referer"];
|
|
982
|
+
function withCSRFVary(response) {
|
|
983
|
+
const current = response.headers.get("Vary");
|
|
984
|
+
if (current === "*") return response;
|
|
985
|
+
const values = current ? current.split(",").map(value => value.trim()) : [];
|
|
986
|
+
const names = new Set(values.map(value => value.toLowerCase()));
|
|
987
|
+
for (const value of CSRF_VARY) {
|
|
988
|
+
if (!names.has(value.toLowerCase())) values.push(value);
|
|
989
|
+
}
|
|
990
|
+
const vary = values.join(", ");
|
|
991
|
+
try {
|
|
992
|
+
response.headers.set("Vary", vary);
|
|
993
|
+
return response;
|
|
994
|
+
} catch {
|
|
995
|
+
const headers = new Headers(response.headers);
|
|
996
|
+
headers.set("Vary", vary);
|
|
997
|
+
return new Response(response.body, {
|
|
998
|
+
status: response.status,
|
|
999
|
+
statusText: response.statusText,
|
|
1000
|
+
headers
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function forbiddenResponse() {
|
|
1005
|
+
return withCSRFVary(new Response(DEV ? "Forbidden" : null, {
|
|
1006
|
+
status: 403,
|
|
1007
|
+
headers: {
|
|
1008
|
+
"Cache-Control": "no-store"
|
|
1009
|
+
}
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
951
1012
|
async function handleServerFunctionRequest(request, options = {}) {
|
|
952
1013
|
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
953
1014
|
const url = new URL(request.url);
|
|
1015
|
+
const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
|
|
1016
|
+
const protectsRequest = csrf !== false;
|
|
1017
|
+
if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
|
|
1018
|
+
return forbiddenResponse();
|
|
1019
|
+
}
|
|
954
1020
|
const instance = request.headers.get(INSTANCE_HEADER);
|
|
955
1021
|
const functionId = resolveFunctionId(request, url);
|
|
956
1022
|
if (!functionId) {
|
|
957
|
-
|
|
1023
|
+
const response = new Response(DEV ? "Server function not found" : null, {
|
|
958
1024
|
status: 404
|
|
959
1025
|
});
|
|
1026
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
960
1027
|
}
|
|
961
1028
|
let serverFunction;
|
|
962
1029
|
try {
|
|
963
1030
|
serverFunction = getServerFunction(functionId);
|
|
964
1031
|
} catch {
|
|
965
|
-
|
|
1032
|
+
const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
|
|
966
1033
|
status: 404
|
|
967
1034
|
});
|
|
1035
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
968
1036
|
}
|
|
969
1037
|
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
970
|
-
|
|
1038
|
+
const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
|
|
971
1039
|
status: 405,
|
|
972
1040
|
headers: {
|
|
973
1041
|
Allow: "POST"
|
|
974
1042
|
}
|
|
975
1043
|
});
|
|
1044
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
976
1045
|
}
|
|
977
1046
|
const event = options.createEvent ? options.createEvent(request) : {
|
|
978
1047
|
request,
|
|
@@ -1131,7 +1200,8 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1131
1200
|
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1132
1201
|
}
|
|
1133
1202
|
};
|
|
1134
|
-
|
|
1203
|
+
const response = commitEventResponse(await dispatch(), event);
|
|
1204
|
+
return protectsRequest ? withCSRFVary(response) : response;
|
|
1135
1205
|
}
|
|
1136
1206
|
|
|
1137
|
-
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, live, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
1207
|
+
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, live, observeServerFunctionCalls, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|