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

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.
Files changed (86) hide show
  1. package/dist/dev.cjs +433 -72
  2. package/dist/dev.js +426 -71
  3. package/dist/server.cjs +39 -21
  4. package/dist/server.js +40 -23
  5. package/dist/web.cjs +421 -70
  6. package/dist/web.js +414 -69
  7. package/frames/dist/client.cjs +8 -8
  8. package/frames/dist/client.dev.cjs +8 -8
  9. package/frames/dist/client.dev.js +8 -8
  10. package/frames/dist/client.js +8 -8
  11. package/frames/dist/server.cjs +28 -18
  12. package/frames/dist/server.js +29 -19
  13. package/package.json +20 -22
  14. package/serialization/dist/decode.cjs +3 -3
  15. package/serialization/dist/decode.js +3 -3
  16. package/serialization/dist/serialization.cjs +7 -11
  17. package/serialization/dist/serialization.js +7 -6
  18. package/serialization/types/index.d.ts +69 -157
  19. package/serialization/types/serializer-decode.d.ts +89 -112
  20. package/serialization/types/serializer.d.ts +94 -0
  21. package/serialization/types-cjs/index.d.cts +69 -157
  22. package/serialization/types-cjs/serializer-decode.d.cts +89 -112
  23. package/serialization/types-cjs/serializer.d.cts +94 -0
  24. package/server-functions/dist/client.cjs +126 -19
  25. package/server-functions/dist/client.js +123 -19
  26. package/server-functions/dist/server.cjs +156 -28
  27. package/server-functions/dist/server.dev.cjs +156 -28
  28. package/server-functions/dist/server.dev.js +153 -28
  29. package/server-functions/dist/server.js +153 -28
  30. package/types/client.d.ts +152 -290
  31. package/types/constants.d.ts +18 -0
  32. package/types/cookies.d.ts +31 -75
  33. package/types/frames/frame-client.d.ts +287 -277
  34. package/types/frames/frame-container-plugin.d.ts +71 -0
  35. package/types/frames/frame-sink.d.ts +58 -160
  36. package/types/frames/frame-transport.d.ts +161 -194
  37. package/types/frames/serializer-decode.d.ts +159 -0
  38. package/types/frames/serializer.d.ts +69 -157
  39. package/types/head.d.ts +16 -0
  40. package/types/index.d.ts +1 -65
  41. package/types/index.server.d.ts +125 -0
  42. package/types/jsx.d.ts +5 -13
  43. package/types/patch-driver.d.ts +3 -0
  44. package/types/reconcile.d.ts +1 -0
  45. package/types/render.d.ts +4 -0
  46. package/types/response.d.ts +48 -152
  47. package/types/serializer-decode.d.ts +89 -112
  48. package/types/serializer.d.ts +69 -157
  49. package/types/server-functions/client.d.ts +124 -222
  50. package/types/server-functions/flash.d.ts +10 -30
  51. package/types/server-functions/registry.d.ts +100 -0
  52. package/types/server-functions/rich-args.d.ts +1 -10
  53. package/types/server-functions/server.d.ts +295 -574
  54. package/types/server-functions/shared.d.ts +155 -458
  55. package/types/server-mock.d.ts +2 -2
  56. package/types/server.d.ts +242 -434
  57. package/types-cjs/client.d.cts +152 -290
  58. package/types-cjs/constants.d.cts +18 -0
  59. package/types-cjs/cookies.d.cts +31 -75
  60. package/types-cjs/frames/frame-client.d.cts +287 -277
  61. package/types-cjs/frames/frame-container-plugin.d.cts +71 -0
  62. package/types-cjs/frames/frame-sink.d.cts +58 -160
  63. package/types-cjs/frames/frame-transport.d.cts +161 -194
  64. package/types-cjs/frames/serializer-decode.d.cts +159 -0
  65. package/types-cjs/frames/serializer.d.cts +69 -157
  66. package/types-cjs/head.d.cts +16 -0
  67. package/types-cjs/index.d.cts +1 -65
  68. package/types-cjs/index.server.d.cts +125 -0
  69. package/types-cjs/jsx.d.cts +5 -13
  70. package/types-cjs/patch-driver.d.cts +3 -0
  71. package/types-cjs/reconcile.d.cts +1 -0
  72. package/types-cjs/render.d.cts +4 -0
  73. package/types-cjs/response.d.cts +48 -152
  74. package/types-cjs/serializer-decode.d.cts +89 -112
  75. package/types-cjs/serializer.d.cts +69 -157
  76. package/types-cjs/server-functions/client.d.cts +124 -222
  77. package/types-cjs/server-functions/flash.d.cts +10 -30
  78. package/types-cjs/server-functions/registry.d.cts +100 -0
  79. package/types-cjs/server-functions/rich-args.d.cts +1 -10
  80. package/types-cjs/server-functions/server.d.cts +295 -574
  81. package/types-cjs/server-functions/shared.d.cts +155 -458
  82. package/types-cjs/server-mock.d.cts +2 -2
  83. package/types-cjs/server.d.cts +242 -434
  84. package/LICENSE +0 -21
  85. package/types/core.d.ts +0 -9
  86. package/types-cjs/core.d.cts +0 -9
@@ -26,6 +26,32 @@ function withMeta(fn, meta) {
26
26
  Object.assign(metadata, meta);
27
27
  return fn;
28
28
  }
29
+ const SERVER_FUNCTION_INVOKE = Symbol.for("solid.ServerFunctionInvoke");
30
+ const INVOKE_OPTION_REDIRECTS = {
31
+ headers: "Session-dynamic headers belong to the prepareRequest hook, declaration metadata to " + "withMeta(fn, meta), and data belongs in the arguments, where it is serialized, typed, " + "and part of the cache key.",
32
+ method: "The method is declaration-scoped: declare the function with GET(fn).",
33
+ body: "The arguments are the body: pass them in the args array.",
34
+ timeout: "Compose timeouts through `signal` with AbortSignal.timeout(ms) (and AbortSignal.any to " + "combine it with a caller signal)."
35
+ };
36
+ function invoke(fn, options, ...args) {
37
+ const channel = typeof fn === "function" && fn[SERVER_FUNCTION_INVOKE];
38
+ if (!channel) {
39
+ throw new Error(isServerFunction(fn) ? "invoke: this wrapper does not forward the invocation channel " + "(SERVER_FUNCTION_INVOKE). Wrappers that share calls (caches, channels) opt in " + "deliberately — a caller's signal cannot own a wire other callers share. Invoke " + "the underlying reference directly, or use the wrapper's own per-call idioms." : "invoke expects a server function reference (or a wrapper that forwards its " + "invocation channel). Per-call options apply at the transport; for a data " + "layer's calls, use its per-call options instead.");
40
+ }
41
+ if (options === null || typeof options !== "object") {
42
+ throw new Error("invoke's second argument is the invocation options bag: invoke(fn, { signal }, ...args)");
43
+ }
44
+ const picked = {};
45
+ for (const key in options) {
46
+ if (key !== "signal" && key !== "keepalive" && key !== "priority") {
47
+ throw new Error(`\`${key}\` is not an invocation option. ` + (INVOKE_OPTION_REDIRECTS[key] || "Options here are strictly invocation-scoped (they vary between calls of the " + "same function): signal, keepalive, priority."));
48
+ }
49
+ }
50
+ if (options.signal !== undefined) picked.signal = options.signal;
51
+ if (options.keepalive !== undefined) picked.keepalive = options.keepalive;
52
+ if (options.priority !== undefined) picked.priority = options.priority;
53
+ return channel(args, picked);
54
+ }
29
55
  const LIVE_SOURCE = Symbol.for("solid.LiveSource");
30
56
  const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
31
57
  function provideServerFunctionRPC(rpc) {
@@ -90,7 +116,23 @@ function subscribeFlightData(consumer) {
90
116
  return () => {
91
117
  };
92
118
  }
93
- const FUNCTION_HEADER = "X-Server-Function-Id";
119
+ function serverFunctionAddress(endpoint, id) {
120
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
121
+ return `${mount}/${encodeURIComponent(id)}`;
122
+ }
123
+ function parseServerFunctionAddress(pathname, endpoint) {
124
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
125
+ if (!pathname.startsWith(mount)) return null;
126
+ const rest = pathname.slice(mount.length);
127
+ if (!rest.startsWith("/")) return null;
128
+ const segment = rest.slice(1);
129
+ if (!segment || segment.includes("/")) return null;
130
+ try {
131
+ return decodeURIComponent(segment);
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
94
136
  const ERROR_HEADER = "X-Server-Function-Error";
95
137
  const ERROR_HEADER_MARKER = "=?1?";
96
138
  const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
@@ -129,7 +171,8 @@ const BodyFormat = {
129
171
  File: "5",
130
172
  ArrayBuffer: "6",
131
173
  Uint8Array: "7",
132
- Json: "8"
174
+ Json: "8",
175
+ Void: "9"
133
176
  };
134
177
  const JSON_SAFE_DEPTH_LIMIT = 4096;
135
178
  const EXIT = {};
@@ -549,6 +592,9 @@ function getServerFunction(id) {
549
592
  throw new Error("invalid server function: " + id);
550
593
  }
551
594
  function registerServerReference(id, fn, name) {
595
+ if (typeof fn !== "function") {
596
+ throw new Error(`Server function${name ? ` \`${name}\`` : ""} (${id}) is not a function: a module-level ` + `"use server" export must evaluate to a server function (got ${fn === null ? "null" : typeof fn}). Move non-function exports out of the directive module.`);
597
+ }
552
598
  registerServerFunction(id, fn);
553
599
  return {
554
600
  id,
@@ -556,6 +602,33 @@ function registerServerReference(id, fn, name) {
556
602
  name
557
603
  };
558
604
  }
605
+ function inProcessInvoker(call) {
606
+ return (args, options) => {
607
+ const signal = options && options.signal;
608
+ if (!signal) return call(...args);
609
+ if (signal.aborted) return Promise.reject(signal.reason);
610
+ return new Promise((resolve, reject) => {
611
+ const onAbort = () => reject(signal.reason);
612
+ signal.addEventListener("abort", onAbort, {
613
+ once: true
614
+ });
615
+ let result;
616
+ try {
617
+ result = call(...args);
618
+ } catch (error) {
619
+ signal.removeEventListener("abort", onAbort);
620
+ return reject(error);
621
+ }
622
+ Promise.resolve(result).then(value => {
623
+ signal.removeEventListener("abort", onAbort);
624
+ resolve(value);
625
+ }, error => {
626
+ signal.removeEventListener("abort", onAbort);
627
+ reject(error);
628
+ });
629
+ });
630
+ };
631
+ }
559
632
  function createServerReference({
560
633
  id,
561
634
  fn,
@@ -566,13 +639,15 @@ function createServerReference({
566
639
  const metadata = name === undefined ? {} : {
567
640
  name
568
641
  };
569
- return new Proxy(fn, {
642
+ const invokeChannel = inProcessInvoker((...args) => proxy(...args));
643
+ const proxy = new Proxy(fn, {
570
644
  get(target, prop) {
571
645
  if (prop === "id") return id;
572
646
  if (prop === "url") {
573
- return `${config.endpoint}?id=${encodeURIComponent(id)}`;
647
+ return serverFunctionAddress(config.endpoint, id);
574
648
  }
575
649
  if (prop === SERVER_FUNCTION_METADATA) return metadata;
650
+ if (prop === SERVER_FUNCTION_INVOKE) return invokeChannel;
576
651
  return target[prop];
577
652
  },
578
653
  apply(target, thisArg, args) {
@@ -609,6 +684,7 @@ function createServerReference({
609
684
  }) : result;
610
685
  }
611
686
  });
687
+ return proxy;
612
688
  }
613
689
  function GET(fn) {
614
690
  if (!isServerFunction(fn) || typeof fn.id !== "string") {
@@ -635,6 +711,7 @@ function live(fn) {
635
711
  return result;
636
712
  };
637
713
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
714
+ wrapped[SERVER_FUNCTION_INVOKE] = inProcessInvoker(wrapped);
638
715
  wrapped.id = fn.id;
639
716
  Object.defineProperty(wrapped, "url", {
640
717
  get: () => fn.url,
@@ -648,24 +725,23 @@ function getServerFunctionInvocation() {
648
725
  function getEventServerFunctionInvocation(event) {
649
726
  return event && INVOCATIONS.get(event);
650
727
  }
651
- function resolveFunctionId(request, url) {
652
- const reference = request.headers.get(FUNCTION_HEADER);
653
- if (reference) {
654
- return reference.split("#")[0];
655
- }
656
- return url.searchParams.get("id");
728
+ function resolveFunctionId(url) {
729
+ return parseServerFunctionAddress(url.pathname, config.endpoint);
657
730
  }
658
731
  async function parseArguments(request, url, instance, codec) {
659
732
  const parsed = [];
660
733
  const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
661
- if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
662
- const args = url.searchParams.get("args");
663
- if (args) {
664
- const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
665
- for (const arg of result) {
666
- parsed.push(arg);
667
- }
734
+ const args = url.searchParams.get("args");
735
+ if (args && (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized)) {
736
+ const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
737
+ if (!Array.isArray(result)) {
738
+ throw new TypeError("Server function arguments must encode an array");
739
+ }
740
+ for (const arg of result) {
741
+ parsed.push(arg);
668
742
  }
743
+ } else if (!args && url.search && (request.method === "GET" || request.method === "HEAD")) {
744
+ parsed.push(url.searchParams);
669
745
  }
670
746
  if (request.method === "POST" && request.body !== null) {
671
747
  const decoded = await extractBody(request.clone(), codec);
@@ -920,6 +996,7 @@ function encodeResult(value, headers, status, codec, signal) {
920
996
  });
921
997
  }
922
998
  if (value === undefined) {
999
+ headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
923
1000
  return new Response(null, {
924
1001
  status,
925
1002
  headers
@@ -955,6 +1032,17 @@ function sanitizeServerError(value) {
955
1032
  function observeServerFunctionCalls() {
956
1033
  return () => {};
957
1034
  }
1035
+ function serverFunctionUrl(id, boundArgs) {
1036
+ const address = serverFunctionAddress(config.endpoint, id);
1037
+ if (!boundArgs || !boundArgs.length) return address;
1038
+ if (!isJSONSafe(boundArgs)) {
1039
+ throw new Error("Bound arguments in an action url must be JSON-safe: the server reads them the way it " + "reads a form post's, and that convention has no codec. Pass the value through the " + "function's body, or call the reference instead of rendering a url for it.");
1040
+ }
1041
+ return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
1042
+ }
1043
+ function parseServerFunctionUrl(url) {
1044
+ return parseServerFunctionAddress(new URL(url, "http://localhost").pathname, config.endpoint);
1045
+ }
958
1046
  async function matchesOrigin(origin, request, matcher) {
959
1047
  if (matcher === undefined) return origin === new URL(request.url).origin;
960
1048
  if (typeof matcher === "function") return !!(await matcher(origin, request));
@@ -1012,18 +1100,20 @@ function forbiddenResponse() {
1012
1100
  async function handleServerFunctionRequest(request, options = {}) {
1013
1101
  const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1014
1102
  const url = new URL(request.url);
1103
+ const method = request.method;
1104
+ const functionId = resolveFunctionId(url);
1105
+ const declaredRead = (method === "GET" || method === "HEAD") && functionId !== null && METHODS.get(functionId) === "GET";
1015
1106
  const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
1016
- const protectsRequest = csrf !== false;
1107
+ const protectsRequest = csrf !== false && !declaredRead;
1017
1108
  if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
1018
- return forbiddenResponse();
1109
+ return finalizeTransportResponse(forbiddenResponse(), method);
1019
1110
  }
1020
1111
  const instance = request.headers.get(INSTANCE_HEADER);
1021
- const functionId = resolveFunctionId(request, url);
1022
1112
  if (!functionId) {
1023
1113
  const response = new Response(DEV ? "Server function not found" : null, {
1024
1114
  status: 404
1025
1115
  });
1026
- return protectsRequest ? withCSRFVary(response) : response;
1116
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1027
1117
  }
1028
1118
  let serverFunction;
1029
1119
  try {
@@ -1032,16 +1122,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1032
1122
  const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
1033
1123
  status: 404
1034
1124
  });
1035
- return protectsRequest ? withCSRFVary(response) : response;
1125
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1036
1126
  }
1037
- if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
1127
+ if (method !== "POST" && !declaredRead) {
1038
1128
  const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
1039
1129
  status: 405,
1040
1130
  headers: {
1041
- Allow: "POST"
1131
+ Allow: METHODS.get(functionId) === "GET" ? "POST, GET, HEAD" : "POST"
1042
1132
  }
1043
1133
  });
1044
- return protectsRequest ? withCSRFVary(response) : response;
1134
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1045
1135
  }
1046
1136
  const event = options.createEvent ? options.createEvent(request) : {
1047
1137
  request,
@@ -1054,7 +1144,15 @@ async function handleServerFunctionRequest(request, options = {}) {
1054
1144
  const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
1055
1145
  const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
1056
1146
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
1057
- const parsed = await parseArguments(request, url, instance, codec);
1147
+ let parsed;
1148
+ try {
1149
+ parsed = await parseArguments(request, url, instance, codec);
1150
+ } catch {
1151
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1152
+ status: 400
1153
+ });
1154
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1155
+ }
1058
1156
  const flightContext = {
1059
1157
  id: functionId,
1060
1158
  args: parsed,
@@ -1201,7 +1299,34 @@ async function handleServerFunctionRequest(request, options = {}) {
1201
1299
  }
1202
1300
  };
1203
1301
  const response = commitEventResponse(await dispatch(), event);
1204
- return protectsRequest ? withCSRFVary(response) : response;
1302
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1303
+ }
1304
+ function finalizeTransportResponse(response, method) {
1305
+ const stripBody = method === "HEAD" && response.body !== null;
1306
+ if (stripBody || !response.headers.has("Cache-Control")) {
1307
+ try {
1308
+ if (!response.headers.has("Cache-Control")) {
1309
+ response.headers.set("Cache-Control", "no-store");
1310
+ }
1311
+ if (!stripBody) return response;
1312
+ response.body.cancel().catch(() => {});
1313
+ return new Response(null, {
1314
+ status: response.status,
1315
+ statusText: response.statusText,
1316
+ headers: response.headers
1317
+ });
1318
+ } catch {
1319
+ const headers = new Headers(response.headers);
1320
+ if (!headers.has("Cache-Control")) headers.set("Cache-Control", "no-store");
1321
+ if (stripBody) response.body.cancel().catch(() => {});
1322
+ return new Response(stripBody ? null : response.body, {
1323
+ status: response.status,
1324
+ statusText: response.statusText,
1325
+ headers
1326
+ });
1327
+ }
1328
+ }
1329
+ return response;
1205
1330
  }
1206
1331
 
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 };
1332
+ export { ERROR_HEADER, FLASH_COOKIE, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SERVER_FUNCTION_INVOKE, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, invoke, isServerFunction, live, observeServerFunctionCalls, parseServerFunctionUrl, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, serverFunctionUrl, setServerFunctionsDev, subscribeFlightData, withMeta };