@solidjs/web 2.0.0-rc.1 → 2.0.0-rc.2

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.
@@ -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
- return new Response(DEV ? "Server function not found" : null, {
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
- return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
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
- return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
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
- return commitEventResponse(await dispatch(), event);
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
- return new Response(DEV ? "Server function not found" : null, {
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
- return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
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
- return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
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
- return commitEventResponse(await dispatch(), event);
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
- return new Response(DEV ? "Server function not found" : null, {
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
- return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
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
- return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
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
- return commitEventResponse(await dispatch(), event);
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 };
package/types/core.d.ts CHANGED
@@ -4,3 +4,6 @@ export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
4
4
  export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
5
5
  export declare const ssrAsyncValue: (value: any) => import("solid-js").SourceAccessor<any>;
6
6
  export declare const waitAsset: (promise: any) => void;
7
+ export declare const driveList: undefined;
8
+ export declare const patchableRaw: undefined;
9
+ export declare const registerPatch: undefined;
@@ -245,6 +245,15 @@ export interface FrameHostOptions {
245
245
  * identity only.
246
246
  */
247
247
  isContainer?(value: unknown): boolean;
248
+ /**
249
+ * Arms event types for behavior claims: the `_bnd` sweep collects the
250
+ * event names it finds and hands them here so delegated dispatch can
251
+ * reach them. Platform glue passes its `delegateEvents` — the option
252
+ * exists (rather than client.js importing the event system) so
253
+ * tree-shaken subsets without events pay nothing. Frames registered
254
+ * with this host inherit it unless they pass their own `delegate`.
255
+ */
256
+ delegate?(eventNames: Iterable<string>): void;
248
257
  }
249
258
 
250
259
  /** @experimental */
@@ -260,6 +269,13 @@ export interface FrameOptions {
260
269
  id?: string;
261
270
  /** Client content keyed by prop name (occurrences resolve by prop). */
262
271
  slots?: Record<string, Slot>;
272
+ /**
273
+ * Raw client props for behavior-claim resolution: server elements carrying
274
+ * `_bnd="pos=prop"` markers (compiled under the `serverComponents` option)
275
+ * resolve ref/event positions by name through this object — read live at
276
+ * dispatch/materialize time, so compiled prop getters stay latest-value.
277
+ */
278
+ props?: Record<string, unknown>;
263
279
  /**
264
280
  * Adopt existing server-rendered DOM: the first apply morphs against it,
265
281
  * and slots sync immediately (hydration attach) — a document-SSR boot
@@ -277,6 +293,8 @@ export interface FrameOptions {
277
293
  * streamed chunks).
278
294
  */
279
295
  ownerScope?<T>(fn: () => T): T;
296
+ /** Per-frame override of the host's `delegate` (see FrameHostOptions). */
297
+ delegate?(eventNames: Iterable<string>): void;
280
298
  /**
281
299
  * Boundary-driven segment reveal. When present, `#revealSegment` hands the
282
300
  * placeholder seam to this hook instead of swapping imperatively: the binding
@@ -127,6 +127,36 @@ export interface ServerFunctionsClientConfig {
127
127
  */
128
128
  export function configureServerFunctionsClient(config?: ServerFunctionsClientConfig): void;
129
129
 
130
+ export interface ServerFunctionRequestCall {
131
+ type: "request";
132
+ id: string;
133
+ instance: string;
134
+ request: Request;
135
+ meta: ServerFunctionMetadata | undefined;
136
+ time: number;
137
+ }
138
+
139
+ export interface ServerFunctionResponseCall {
140
+ type: "response";
141
+ id: string;
142
+ instance: string;
143
+ response: Response;
144
+ meta: ServerFunctionMetadata | undefined;
145
+ time: number;
146
+ }
147
+
148
+ export type ServerFunctionCall = ServerFunctionRequestCall | ServerFunctionResponseCall;
149
+
150
+ /**
151
+ * Observes cloned requests and responses without handling them. Subscribe
152
+ * from devtools; do not use this to replace `prepareRequest` /
153
+ * `responseHandler`. The server entry exports a no-op of the same name so
154
+ * isomorphic `@solidjs/web/server-functions` imports resolve.
155
+ */
156
+ export function observeServerFunctionCalls(
157
+ observer: (call: ServerFunctionCall) => void
158
+ ): () => void;
159
+
130
160
  /**
131
161
  * Declares a server function callable over HTTP GET: calls to the returned
132
162
  * reference go out as GET requests with the arguments codec-encoded in the
@@ -163,8 +193,9 @@ export type LiveSourceStatus = "connected" | "reconnecting" | "closed";
163
193
  * A live call's answer: the source's iterable, plus an optional `onstatus`
164
194
  * side channel for the wire facts the reconnect loop erases from the value
165
195
  * stream — `"connected"` on each successful (re)connect, `"reconnecting"`
166
- * (with the error) on each post-connect death, `"closed"` when the source
167
- * completes or the consumer ends it.
196
+ * (with the error) on each transient post-connect death, `"closed"` when
197
+ * the source completes or the consumer ends it — with the error when the
198
+ * end was a definite rejection (4xx) failing fast instead of retrying.
168
199
  */
169
200
  export type LiveSource<R> = R & {
170
201
  onstatus?: (state: LiveSourceStatus, error?: unknown) => void;
@@ -28,7 +28,7 @@ export type {
28
28
  } from "./shared.js";
29
29
  export { decodeFlashCookie, encodeFlashCookie } from "./flash.js";
30
30
  export type { FlashSubmission } from "./flash.js";
31
- import { ServerFunction } from "./shared.js";
31
+ import { ServerFunction, ServerFunctionMetadata } from "./shared.js";
32
32
 
33
33
  /**
34
34
  * The request event a server function call runs under: the base
@@ -197,6 +197,26 @@ export function createNoJSHandler(
197
197
  options?: NoJSHandlerOptions
198
198
  ): (result: unknown, request: Request, args: unknown[], thrown?: boolean) => Response;
199
199
 
200
+ export type ServerFunctionOriginMatcher =
201
+ | string
202
+ | readonly string[]
203
+ | ((origin: string, request: Request) => boolean | Promise<boolean>);
204
+
205
+ /** Same-origin validation options for server function requests. */
206
+ export interface ServerFunctionCSRFOptions {
207
+ /**
208
+ * Expected public origin. Defaults to the incoming request URL's origin.
209
+ * A function can validate origins dynamically for multi-tenant hosts.
210
+ */
211
+ origin?: ServerFunctionOriginMatcher;
212
+ /**
213
+ * Allows requests without `Sec-Fetch-Site`, `Origin`, or `Referer`.
214
+ * Cross-origin metadata is still rejected.
215
+ * @default false
216
+ */
217
+ allowRequestsWithoutOriginCheck?: boolean;
218
+ }
219
+
200
220
  /** Options for `configureServerFunctionsServer`. */
201
221
  export interface ServerFunctionsServerConfig {
202
222
  /**
@@ -287,6 +307,12 @@ export interface ServerFunctionsServerConfig {
287
307
  * @default "/_server"
288
308
  */
289
309
  endpoint?: string;
310
+ /**
311
+ * Same-origin protection for HTTP server function calls. Enabled by
312
+ * default. Set to `false` only when another trusted layer protects the
313
+ * endpoint.
314
+ */
315
+ csrf?: boolean | ServerFunctionCSRFOptions;
290
316
  /**
291
317
  * Codec options (extra plugins etc.) for decoding arguments and encoding
292
318
  * results — must match the client's. Stored in the shared layer, so
@@ -392,7 +418,9 @@ export function GET<A extends readonly any[], R>(
392
418
  fn: (...args: A) => R
393
419
  ): ServerFunction<A, Awaited<R>>;
394
420
 
395
- /** Wire-state transitions a live call's iterable can report (client side). */
421
+ /** Wire-state transitions a live call's iterable can report (client side).
422
+ * `"closed"` carries the error when a definite rejection (4xx) ended the
423
+ * call instead of the retry loop. */
396
424
  export type LiveSourceStatus = "connected" | "reconnecting" | "closed";
397
425
 
398
426
  /**
@@ -529,6 +557,11 @@ export interface HandleServerFunctionOptions {
529
557
  args: unknown[],
530
558
  thrown?: boolean
531
559
  ): Response | Promise<Response>;
560
+ /**
561
+ * Overrides same-origin protection for this handler. Set to `false` only
562
+ * when another trusted layer protects the endpoint.
563
+ */
564
+ csrf?: boolean | ServerFunctionCSRFOptions;
532
565
  /** Overrides the configured codec options for this handler. */
533
566
  codec?: JSONCodecOptions;
534
567
  }
@@ -543,6 +576,10 @@ export interface HandleServerFunctionOptions {
543
576
  * (default `/_server`); platform adapters (h3, express, ...) convert their
544
577
  * request shape to a web `Request` around it.
545
578
  *
579
+ * Requests are same-origin by default. The handler accepts browser requests
580
+ * proven by `Sec-Fetch-Site`, `Origin`, or `Referer`, and rejects requests
581
+ * without usable metadata unless explicitly configured otherwise.
582
+ *
546
583
  * When the event carries a `response` head stub (`event.response`, see the
547
584
  * server entry's `ResponseStub`), the handler folds it onto every outgoing
548
585
  * response as the head freezes — its `Set-Cookie` values (cookies appended
@@ -606,6 +643,34 @@ export const GENERIC_SERVER_ERROR_MESSAGE: string;
606
643
  */
607
644
  export function sanitizeServerError(value: unknown): unknown;
608
645
 
646
+ export interface ServerFunctionRequestCall {
647
+ type: "request";
648
+ id: string;
649
+ instance: string;
650
+ request: Request;
651
+ meta: ServerFunctionMetadata | undefined;
652
+ time: number;
653
+ }
654
+
655
+ export interface ServerFunctionResponseCall {
656
+ type: "response";
657
+ id: string;
658
+ instance: string;
659
+ response: Response;
660
+ meta: ServerFunctionMetadata | undefined;
661
+ time: number;
662
+ }
663
+
664
+ export type ServerFunctionCall = ServerFunctionRequestCall | ServerFunctionResponseCall;
665
+
666
+ /**
667
+ * Client-only inspection seam. A no-op on this entry so isomorphic
668
+ * `@solidjs/web/server-functions` imports resolve.
669
+ */
670
+ export function observeServerFunctionCalls(
671
+ observer: (call: ServerFunctionCall) => void
672
+ ): () => void;
673
+
609
674
  /**
610
675
  * Overrides the build-variant dev flag for this module instance — the seam
611
676
  * for test harnesses and hand-rolled bundles whose packaging cannot replace