@server/next 0.30.0 → 0.31.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 +9 -7
  2. package/index.js +84 -85
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -32,6 +32,11 @@ type RouteOptions = {
32
32
  title?: string;
33
33
  description?: string;
34
34
  };
35
+ type Route = {
36
+ path: string;
37
+ options: RouteOptions;
38
+ fns: Middleware[];
39
+ };
35
40
  type Cookie = {
36
41
  value?: string | null;
37
42
  path?: string;
@@ -251,12 +256,11 @@ declare global {
251
256
  }
252
257
 
253
258
  type Mids<O extends ServerConfig, Path extends string> = Middleware<O, PathToParams<Path>>[];
254
- type PathOrMiddle<O extends ServerConfig = object> = string | Middleware<O>;
255
- type FullRoute = [RouterMethod, string, ...Middleware[]][];
256
259
  declare class Router<O extends ServerConfig = object> {
257
- handlers: Record<Method, FullRoute>;
260
+ middleware: Middleware[];
261
+ handlers: Record<Method, Route[]>;
258
262
  self(): this;
259
- handle(method: RouterMethod, path: PathOrMiddle<O>, ...middleware: Middleware<O>[]): this;
263
+ handle(method: Method, pathOrFn?: any, ...rest: any[]): this;
260
264
  socket<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
261
265
  socket<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
262
266
  socket(...middleware: Middleware<O>[]): this;
@@ -290,9 +294,7 @@ declare class Router<O extends ServerConfig = object> {
290
294
  options(...middleware: Middleware<O>[]): this;
291
295
  options(options: RouteOptions, ...middleware: Middleware<O>[]): this;
292
296
  use(...middleware: Middleware[]): this;
293
- use(path: string, ...middleware: Middleware[]): this;
294
297
  use(router: Router): this;
295
- use(path: string, router: Router): this;
296
298
  }
297
299
  declare function router(): Router;
298
300
 
@@ -452,4 +454,4 @@ declare class Server<O extends ServerConfig = {}> extends Router<O> {
452
454
  }
453
455
  declare function server<Session extends Record<string, any> = {}, User extends Record<string, any> = {}>(options?: Options): Server<ServerConfig<Session, User>>;
454
456
 
455
- export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type Bucket, type BunEnv, type Context, type Cookie, type CorsSettings, type EventCallback, type ExtractPathParams, type InferParamType, type InlineReply, type KVStore, type LimitOptions, type LogLevel, type Logger, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type RouteOptions, type RouterMethod, type SecurityOptions, type SecuritySettings, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, UploadPipeline, type UploadedFile, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
457
+ export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type Bucket, type BunEnv, type Context, type Cookie, type CorsSettings, type EventCallback, type ExtractPathParams, type InferParamType, type InlineReply, type KVStore, type LimitOptions, type LogLevel, type Logger, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type Route, type RouteOptions, type RouterMethod, type SecurityOptions, type SecuritySettings, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, UploadPipeline, type UploadedFile, cookies, server as default, download, file, headers, json, redirect, router, send, status, type, upload };
package/index.js CHANGED
@@ -1203,17 +1203,23 @@ function applyCors(res, ctx) {
1203
1203
 
1204
1204
  // src/helpers/createWebsocket.ts
1205
1205
  function createWebsocket(sockets, handlers) {
1206
+ const run = (event, socket, body) => {
1207
+ const routes = handlers.socket?.filter((r2) => r2.path === event) ?? [];
1208
+ for (const route of routes) {
1209
+ for (const fn of route.fns) {
1210
+ fn({ socket, sockets, body });
1211
+ }
1212
+ }
1213
+ };
1206
1214
  return {
1207
- message: async (socket, body) => {
1208
- handlers.socket?.filter((s) => s[1] === "message")?.map((s) => s[2]({ socket, sockets, body }));
1209
- },
1215
+ message: (socket, body) => run("message", socket, body),
1210
1216
  open: (socket) => {
1211
1217
  sockets.push(socket);
1212
- handlers.socket?.filter((s) => s[1] === "open")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
1218
+ run("open", socket);
1213
1219
  },
1214
1220
  close: (socket) => {
1215
1221
  sockets.splice(sockets.indexOf(socket), 1);
1216
- handlers.socket?.filter((s) => s[1] === "close")?.map((s) => s[2]({ socket, sockets, body: void 0 }));
1222
+ run("close", socket);
1217
1223
  }
1218
1224
  };
1219
1225
  }
@@ -1420,18 +1426,23 @@ function validate(ctx, schema) {
1420
1426
  }
1421
1427
 
1422
1428
  // src/helpers/handleRequest.ts
1423
- async function handleRequest(handlers, ctx) {
1424
- const res = await getResponse(handlers, ctx);
1429
+ async function handleRequest(app, ctx) {
1430
+ const res = await getResponse(app, ctx);
1425
1431
  if (res) ctx.options.log.request(ctx, res);
1426
1432
  return res;
1427
1433
  }
1428
- async function getResponse(handlers, ctx) {
1434
+ async function getResponse(app, ctx) {
1429
1435
  try {
1430
- for (const [method, matcher, ...cbs] of handlers[ctx.method]) {
1431
- const match = pathPattern(matcher, ctx.url.pathname || "/");
1432
- if (!match) continue;
1433
- define(ctx.url, "params", () => match);
1434
- for (const cb of cbs) {
1436
+ let matched = false;
1437
+ for (const route of app.handlers[ctx.method]) {
1438
+ const params = pathPattern(route.path, ctx.url.pathname || "/");
1439
+ if (!params) continue;
1440
+ matched = true;
1441
+ define(ctx.url, "params", () => params);
1442
+ if (Object.keys(route.options).length) {
1443
+ ctx.options = { ...app.settings, ...route.options };
1444
+ }
1445
+ for (const cb of route.fns) {
1435
1446
  if (typeof cb === "function") {
1436
1447
  const res = await cb(ctx);
1437
1448
  const out = await parseResponse(res, ctx);
@@ -1440,7 +1451,13 @@ async function getResponse(handlers, ctx) {
1440
1451
  validate(ctx, cb);
1441
1452
  }
1442
1453
  }
1443
- if (method !== "*") break;
1454
+ break;
1455
+ }
1456
+ if (!matched) {
1457
+ for (const mw of app.middleware) {
1458
+ const out = await parseResponse(await mw(ctx), ctx);
1459
+ if (out) return out;
1460
+ }
1444
1461
  }
1445
1462
  if (ctx.platform.provider === "netlify") return;
1446
1463
  throw new ServerError_default("NOT_FOUND", 404, "Not Found");
@@ -1934,7 +1951,7 @@ async function favicon(ctx) {
1934
1951
  return icon ? type("ico").send(icon) : 204;
1935
1952
  }
1936
1953
  const handled = ctx.app.handlers.get.some(
1937
- ([method, matcher]) => method !== "*" && pathPattern(matcher, "/favicon.ico")
1954
+ (route) => pathPattern(route.path, "/favicon.ico")
1938
1955
  );
1939
1956
  if (handled) return;
1940
1957
  return 204;
@@ -1953,11 +1970,8 @@ var encode = (str = "") => {
1953
1970
  if (typeof str !== "string") return "";
1954
1971
  return str.replace(/[&<>"]/g, (tag) => entities[tag]);
1955
1972
  };
1956
- var getConfig = (routes) => {
1957
- const config2 = routes.find(
1958
- (r2) => typeof r2 !== "string" && typeof r2 !== "function" && typeof r2 === "object"
1959
- );
1960
- if (!config2) return {};
1973
+ var getConfig = (options = {}) => {
1974
+ const config2 = { ...options };
1961
1975
  if (config2.tags) {
1962
1976
  if (typeof config2.tags === "string") {
1963
1977
  config2.tags = config2.tags.split(/\s*,\s*/g);
@@ -2002,13 +2016,10 @@ var generateOpenApiPaths = (handlers) => {
2002
2016
  const paths = {};
2003
2017
  for (const [method, routes] of Object.entries(handlers)) {
2004
2018
  for (const route of routes) {
2005
- const [_, path2, fn, meta] = [
2006
- route[0],
2007
- route[1],
2008
- route.find((p) => typeof p === "function"),
2009
- route.find((p) => typeof p === "object")
2010
- ];
2011
- const config2 = getConfig(route);
2019
+ const path2 = route.path;
2020
+ const fn = route.fns.find((p) => typeof p === "function");
2021
+ const meta = route.fns.find((p) => typeof p === "object");
2022
+ const config2 = getConfig(route.options);
2012
2023
  if (typeof path2 !== "string" || path2 === "*" || path2 === "/docs" || !fn) {
2013
2024
  continue;
2014
2025
  }
@@ -2107,7 +2118,7 @@ function preflight(ctx) {
2107
2118
  if (ctx.method !== "options") return;
2108
2119
  if (!ctx.headers["access-control-request-method"]) return;
2109
2120
  const handled = ctx.app.handlers.options.some(
2110
- ([method, matcher]) => method !== "*" && pathPattern(matcher, ctx.url.pathname)
2121
+ (route) => pathPattern(route.path, ctx.url.pathname)
2111
2122
  );
2112
2123
  if (handled) return;
2113
2124
  return 204;
@@ -2292,7 +2303,7 @@ var Winter = async (app, request, env2) => {
2292
2303
  if (env2?.upgrade(request)) return;
2293
2304
  Object.assign(globalThis.env, env2);
2294
2305
  const ctx = await createWinter(request, app, env2);
2295
- const res = await handleRequest(app.handlers, ctx);
2306
+ const res = await handleRequest(app, ctx);
2296
2307
  ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
2297
2308
  return res;
2298
2309
  };
@@ -2301,7 +2312,7 @@ var Node = async (app) => {
2301
2312
  http.createServer(async (request, response) => {
2302
2313
  const ctx = await createNode(request, app);
2303
2314
  if ("error" in ctx) throw ctx.error;
2304
- const out = await handleRequest(app.handlers, ctx);
2315
+ const out = await handleRequest(app, ctx);
2305
2316
  response.writeHead(out.status || 200, parseHeaders_default(out.headers));
2306
2317
  if (out.body instanceof ReadableStream) {
2307
2318
  await iterate(out.body, (chunk) => response.write(chunk));
@@ -2319,16 +2330,16 @@ var Netlify = async (app, request, context) => {
2319
2330
  throw new Error("Netlify doesn't exist");
2320
2331
  }
2321
2332
  const ctx = await createWinter(request, app);
2322
- const res = await handleRequest(app.handlers, ctx);
2333
+ const res = await handleRequest(app, ctx);
2323
2334
  ctx.events.trigger("finish", { ...ctx, res, end: performance.now() });
2324
2335
  return res;
2325
2336
  };
2326
2337
 
2327
2338
  // src/router.ts
2328
- function isMiddleware(x) {
2329
- return typeof x === "function";
2330
- }
2331
2339
  var Router = class _Router {
2340
+ // Cross-cutting middleware added with .use(); they run on every request
2341
+ middleware = [];
2342
+ // Routes per method, each carrying its own (already-flattened) chain of fns
2332
2343
  handlers = {
2333
2344
  socket: [],
2334
2345
  get: [],
@@ -2344,79 +2355,67 @@ var Router = class _Router {
2344
2355
  self() {
2345
2356
  return this;
2346
2357
  }
2347
- handle(method, path2, ...middleware) {
2348
- if (typeof path2 !== "string") {
2349
- middleware.unshift(path2);
2350
- path2 = "*";
2351
- }
2352
- const methods2 = method === "*" ? Object.keys(this.handlers) : [method];
2353
- for (const m of methods2) {
2354
- this.handlers[m].push([method, path2, ...middleware]);
2355
- }
2358
+ // Registers one route: bakes the current middleware + the route's own
2359
+ // functions into a single flat `fns` list. A plain options object may sit
2360
+ // between the path and the handlers, and it's pulled out here.
2361
+ handle(method, pathOrFn, ...rest) {
2362
+ let path2 = "*";
2363
+ if (typeof pathOrFn === "string") {
2364
+ path2 = pathOrFn;
2365
+ } else if (pathOrFn != null) {
2366
+ rest.unshift(pathOrFn);
2367
+ }
2368
+ let options = {};
2369
+ if (rest[0] != null && typeof rest[0] !== "function") {
2370
+ options = rest.shift();
2371
+ }
2372
+ const base = method === "socket" ? [] : this.middleware;
2373
+ const fns = [...base, ...rest].filter((fn) => fn != null);
2374
+ this.handlers[method].push({ path: path2, options, fns });
2356
2375
  return this.self();
2357
2376
  }
2358
2377
  socket(pathOrMid, optionsOrMid, ...middleware) {
2359
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2360
- return this.handle("socket", pathOrMid, optionsOrMid, ...middleware);
2361
- }
2362
- return this.handle("socket", pathOrMid, ...middleware);
2378
+ return this.handle("socket", pathOrMid, optionsOrMid, ...middleware);
2363
2379
  }
2364
2380
  get(pathOrMid, optionsOrMid, ...middleware) {
2365
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2366
- return this.handle("get", pathOrMid, optionsOrMid, ...middleware);
2367
- }
2368
- return this.handle("get", pathOrMid, ...middleware);
2381
+ return this.handle("get", pathOrMid, optionsOrMid, ...middleware);
2369
2382
  }
2370
2383
  head(pathOrMid, optionsOrMid, ...middleware) {
2371
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2372
- return this.handle("head", pathOrMid, optionsOrMid, ...middleware);
2373
- }
2374
- return this.handle("head", pathOrMid, ...middleware);
2384
+ return this.handle("head", pathOrMid, optionsOrMid, ...middleware);
2375
2385
  }
2376
2386
  post(pathOrMid, optionsOrMid, ...middleware) {
2377
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2378
- return this.handle("post", pathOrMid, optionsOrMid, ...middleware);
2379
- }
2380
- return this.handle("post", pathOrMid, ...middleware);
2387
+ return this.handle("post", pathOrMid, optionsOrMid, ...middleware);
2381
2388
  }
2382
2389
  put(pathOrMid, optionsOrMid, ...middleware) {
2383
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2384
- return this.handle("put", pathOrMid, optionsOrMid, ...middleware);
2385
- }
2386
- return this.handle("put", pathOrMid, ...middleware);
2390
+ return this.handle("put", pathOrMid, optionsOrMid, ...middleware);
2387
2391
  }
2388
2392
  patch(pathOrMid, optionsOrMid, ...middleware) {
2389
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2390
- return this.handle("patch", pathOrMid, optionsOrMid, ...middleware);
2391
- }
2392
- return this.handle("patch", pathOrMid, ...middleware);
2393
+ return this.handle("patch", pathOrMid, optionsOrMid, ...middleware);
2393
2394
  }
2394
2395
  delete(pathOrMid, optionsOrMid, ...middleware) {
2395
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2396
- return this.handle("delete", pathOrMid, optionsOrMid, ...middleware);
2397
- }
2398
- return this.handle("delete", pathOrMid, ...middleware);
2396
+ return this.handle("delete", pathOrMid, optionsOrMid, ...middleware);
2399
2397
  }
2400
2398
  options(pathOrMid, optionsOrMid, ...middleware) {
2401
- if (typeof pathOrMid === "string" && isMiddleware(optionsOrMid)) {
2402
- return this.handle("options", pathOrMid, optionsOrMid, ...middleware);
2403
- }
2404
- return this.handle("options", pathOrMid, ...middleware);
2399
+ return this.handle("options", pathOrMid, optionsOrMid, ...middleware);
2405
2400
  }
2406
2401
  use(...args) {
2407
- const path2 = typeof args[0] === "string" ? args.shift() : "*";
2408
- if (args[0] instanceof _Router) {
2409
- const basePath = `/${path2.replace(/\*$/, "")}/`.replace(/^\/+/, "/").replace(/\/+$/, "/");
2410
- const handlers = args[0].handlers;
2411
- for (const m in handlers) {
2412
- for (const [method, path3, ...middleware] of handlers[m]) {
2413
- const fullPath = basePath + path3.replace(/^\//, "");
2414
- this.handlers[m].push([method, fullPath, ...middleware]);
2402
+ for (const arg of args) {
2403
+ if (arg instanceof _Router) {
2404
+ for (const m of Object.keys(arg.handlers)) {
2405
+ for (const route of arg.handlers[m]) {
2406
+ const base = m === "socket" ? [] : this.middleware;
2407
+ this.handlers[m].push({
2408
+ path: route.path,
2409
+ options: route.options,
2410
+ fns: [...base, ...route.fns]
2411
+ });
2412
+ }
2415
2413
  }
2414
+ } else {
2415
+ this.middleware.push(arg);
2416
2416
  }
2417
- return this.self();
2418
2417
  }
2419
- return this.handle("*", path2, ...args);
2418
+ return this.self();
2420
2419
  }
2421
2420
  };
2422
2421
  function router() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.30.0",
3
+ "version": "0.31.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",