@server/next 0.42.0 → 0.43.0

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 (3) hide show
  1. package/index.d.ts +4 -1
  2. package/index.js +52 -41
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -103,10 +103,13 @@ type RouteOptions = {
103
103
  params?: StandardSchemaV1<any, any>;
104
104
  response?: StandardSchemaV1<any, any>;
105
105
  cache?: CacheOption;
106
+ uploads?: string | Bucket | UploadOptions | false;
106
107
  };
107
108
  type Route = {
108
109
  path: string;
109
- options: RouteOptions;
110
+ options: Omit<RouteOptions, "uploads"> & {
111
+ uploads?: Settings["uploads"];
112
+ };
110
113
  fns: Middleware[];
111
114
  };
112
115
  type Cookie = {
package/index.js CHANGED
@@ -50,6 +50,7 @@ ServerError_default.extend({
50
50
  message: "Missing the OAuth 'code' in the request body"
51
51
  },
52
52
  SESSION_JWT: "The `jwt` strategy is stateless, so there is no `ctx.session` (tried '{key}'). Use the `token` strategy for server-side sessions, or `cookie` for browsers",
53
+ SESSION_GUEST: "No `ctx.session` for this request (tried '{key}'): the `token` strategy carries the session in the Authorization header, and this request has none. Sign in first, or use the `cookie` strategy for guest sessions",
53
54
  AUTH_INVALID_HEADER: {
54
55
  status: 401,
55
56
  message: "Invalid authorization header {type}, must send 'Bearer {TOKEN}' (with space)"
@@ -104,6 +105,17 @@ var StatusError = class extends Error {
104
105
  }
105
106
  };
106
107
 
108
+ // src/helpers/bucket.ts
109
+ import FileSystem from "bucket/fs";
110
+ function bucket(root) {
111
+ if (!root) return null;
112
+ if (typeof root === "string") return FileSystem(root);
113
+ if (typeof root.file === "function") return root;
114
+ throw new Error(
115
+ "Invalid bucket: pass a directory path or a `bucket` instance (with .file())"
116
+ );
117
+ }
118
+
107
119
  // src/helpers/createId.ts
108
120
  var alphabet = "useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
109
121
  var random = (bytes) => crypto.getRandomValues(new Uint8Array(bytes));
@@ -146,6 +158,16 @@ function createId(source, size = 16) {
146
158
  }
147
159
 
148
160
  // src/helpers/upload.ts
161
+ function resolveUploads(up) {
162
+ if (!up) return null;
163
+ if (typeof up === "object" && "bucket" in up) {
164
+ const { bucket: bucket2, maxSize, minSize, fileType: fileType2 } = up;
165
+ if (maxSize != null) parseBytes(maxSize);
166
+ if (minSize != null) parseBytes(minSize);
167
+ return { bucket: bucket(bucket2), maxSize, minSize, fileType: fileType2 };
168
+ }
169
+ return { bucket: bucket(up) };
170
+ }
149
171
  function parseBytes(value) {
150
172
  if (typeof value === "number") return value;
151
173
  const units = {
@@ -1010,8 +1032,8 @@ var validateToken = (authorization) => {
1010
1032
  return id;
1011
1033
  };
1012
1034
  function findSessionId(ctx) {
1013
- const strategy = ctx.options.auth?.strategy;
1014
- if (strategy?.includes("token") && ctx.headers.authorization) {
1035
+ if (ctx.options.auth?.strategy.includes("token")) {
1036
+ if (!ctx.headers.authorization) return;
1015
1037
  return validateToken(ctx.headers.authorization);
1016
1038
  }
1017
1039
  return ctx.cookies.session || void 0;
@@ -1019,28 +1041,33 @@ function findSessionId(ctx) {
1019
1041
 
1020
1042
  // src/middle/session.ts
1021
1043
  var loaded = /* @__PURE__ */ new WeakMap();
1022
- function jwtSession() {
1044
+ function noSession(error) {
1023
1045
  const target = {};
1024
1046
  return new Proxy(target, {
1025
1047
  get(target2, key) {
1026
1048
  if (typeof key === "symbol" || key === "then") return target2[key];
1027
- throw ServerError_default.SESSION_JWT({ key: String(key) });
1049
+ throw error(String(key));
1028
1050
  },
1029
1051
  set(target2, key, value) {
1030
1052
  if (typeof key === "symbol") {
1031
1053
  target2[key] = value;
1032
1054
  return true;
1033
1055
  }
1034
- throw ServerError_default.SESSION_JWT({ key: String(key) });
1056
+ throw error(String(key));
1035
1057
  }
1036
1058
  });
1037
1059
  }
1038
1060
  async function session(ctx) {
1039
- if (ctx.options.auth?.strategy.includes("jwt")) {
1040
- ctx.session = jwtSession();
1061
+ const strategy = ctx.options.auth?.strategy;
1062
+ if (strategy?.includes("jwt")) {
1063
+ ctx.session = noSession((key) => ServerError_default.SESSION_JWT({ key }));
1041
1064
  return;
1042
1065
  }
1043
1066
  const id = findSessionId(ctx);
1067
+ if (!id && strategy?.includes("token")) {
1068
+ ctx.session = noSession((key) => ServerError_default.SESSION_GUEST({ key }));
1069
+ return;
1070
+ }
1044
1071
  ctx.session = id && await ctx.options.sessions.get(id) || {};
1045
1072
  loaded.set(ctx, { id, data: JSON.stringify(ctx.session) });
1046
1073
  }
@@ -1050,6 +1077,10 @@ async function finishLogin(ctx, input, opts = {}) {
1050
1077
  const settings = ctx.options.auth;
1051
1078
  const { strategy, onLogin, onUser, onToken } = settings;
1052
1079
  const key = String(input.key);
1080
+ if (!strategy.includes("jwt") && !loaded.has(ctx)) {
1081
+ ctx.session = {};
1082
+ loaded.set(ctx, { id: void 0, data: "{}" });
1083
+ }
1053
1084
  const auth2 = {
1054
1085
  user: key,
1055
1086
  provider: input.provider,
@@ -1613,17 +1644,6 @@ function parseAuthOptions(auth2) {
1613
1644
  };
1614
1645
  }
1615
1646
 
1616
- // src/helpers/bucket.ts
1617
- import FileSystem from "bucket/fs";
1618
- function bucket(root) {
1619
- if (!root) return null;
1620
- if (typeof root === "string") return FileSystem(root);
1621
- if (typeof root.file === "function") return root;
1622
- throw new Error(
1623
- "Invalid bucket: pass a directory path or a `bucket` instance (with .file())"
1624
- );
1625
- }
1626
-
1627
1647
  // src/helpers/color.ts
1628
1648
  var map = {
1629
1649
  reset: 0,
@@ -1863,17 +1883,7 @@ function config(options = {}) {
1863
1883
  }
1864
1884
  const publicDir = options.public || env2.PUBLIC;
1865
1885
  settings.public = publicDir ? bucket(publicDir) : null;
1866
- const up = options.uploads;
1867
- if (!up) {
1868
- settings.uploads = null;
1869
- } else if (typeof up === "object" && "bucket" in up) {
1870
- const { bucket: bucket2, maxSize, minSize, fileType: fileType2 } = up;
1871
- if (maxSize != null) parseBytes(maxSize);
1872
- if (minSize != null) parseBytes(minSize);
1873
- settings.uploads = { bucket: bucket(bucket2), maxSize, minSize, fileType: fileType2 };
1874
- } else {
1875
- settings.uploads = { bucket: bucket(up) };
1876
- }
1886
+ settings.uploads = resolveUploads(options.uploads);
1877
1887
  const favicon2 = options.favicon || env2.FAVICON;
1878
1888
  if (favicon2) settings.favicon = favicon2;
1879
1889
  const production = env2.NODE_ENV === "production";
@@ -1886,7 +1896,7 @@ function config(options = {}) {
1886
1896
  if (!settings.auth.users) {
1887
1897
  if (production) {
1888
1898
  throw new Error(
1889
- "Auth in production needs a persistent `users` store, like auth: { ..., users: kv(redis).prefix('users:') }."
1899
+ "Auth in production needs a persistent `users` store, like auth: { ..., users: kv(redis).prefix('user:') }."
1890
1900
  );
1891
1901
  }
1892
1902
  settings.auth.users = toStore(/* @__PURE__ */ new Map());
@@ -2106,13 +2116,11 @@ async function parseResponse(out, ctx) {
2106
2116
  out.headers.set("Server-Timing", ctx.time.headers());
2107
2117
  }
2108
2118
  const prev = loaded.get(ctx);
2109
- const jwt = ctx.options.auth?.strategy.includes("jwt");
2110
- const data = jwt ? "{}" : JSON.stringify(ctx.session ?? {});
2111
- if (!jwt && data !== (prev?.data ?? "{}")) {
2119
+ if (prev && JSON.stringify(ctx.session ?? {}) !== prev.data) {
2112
2120
  if (ctx.options.sessionsDefault && ctx.platform.production) {
2113
2121
  warnDefault();
2114
2122
  }
2115
- let id = prev?.id;
2123
+ let id = prev.id;
2116
2124
  if (!id) {
2117
2125
  id = createId();
2118
2126
  out.headers.append(
@@ -2465,14 +2473,14 @@ async function getJwtUser(ctx) {
2465
2473
  return exposed;
2466
2474
  }
2467
2475
  async function getAuthSession(ctx) {
2468
- let session2 = ctx.session;
2469
- if (!session2) {
2470
- const id = findSessionId(ctx);
2471
- if (!id) return;
2472
- session2 = await ctx.options.sessions.get(id) ?? void 0;
2476
+ if (loaded.has(ctx)) {
2477
+ const session3 = ctx.session;
2478
+ return session3?.user ? session3 : void 0;
2473
2479
  }
2474
- if (!session2?.user) return;
2475
- return session2;
2480
+ const id = findSessionId(ctx);
2481
+ if (!id) return;
2482
+ const session2 = await ctx.options.sessions.get(id);
2483
+ return session2?.user ? session2 : void 0;
2476
2484
  }
2477
2485
  async function getUser(ctx) {
2478
2486
  if (!ctx.options.auth) return;
@@ -3231,6 +3239,9 @@ var Router = class _Router {
3231
3239
  options = rest.shift();
3232
3240
  }
3233
3241
  checkParserConflict(options, this.settings?.parser);
3242
+ if (options.uploads !== void 0) {
3243
+ options.uploads = resolveUploads(options.uploads);
3244
+ }
3234
3245
  const base = method === "socket" ? [] : this.middleware;
3235
3246
  const fns = [...base, ...rest].filter((fn) => fn != null);
3236
3247
  this.handlers[method].push({ path, options, fns });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
5
5
  "homepage": "https://server-js.com/",
6
6
  "repository": "github:franciscop/server-next",