@sourceregistry/node-webserver 1.7.3 → 1.7.5

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/README.md CHANGED
@@ -413,6 +413,45 @@ app.GET("/admin", enhance(
413
413
  ));
414
414
  ```
415
415
 
416
+ ### Typed enhancers
417
+
418
+ You can define reusable enhancers with the `EventEnhancer` type. The fifth type parameter (`TExtra`) controls whether the enhancer receives extra event properties like `websocket`.
419
+
420
+ Without WebSocket — works in any HTTP route:
421
+
422
+ ```ts
423
+ import type { EventEnhancer } from "@sourceregistry/node-webserver";
424
+
425
+ const withAuth: EventEnhancer<any, any, App.Locals, { user: { id: string; role: string } }> = async (event) => {
426
+ const token = event.request.headers.get("authorization");
427
+ if (!token) error(401, { message: "Unauthorized" });
428
+ return { user: await verifyToken(token) };
429
+ };
430
+ ```
431
+
432
+ With WebSocket — only usable in `router.WS()` routes:
433
+
434
+ ```ts
435
+ import type { EventEnhancer } from "@sourceregistry/node-webserver";
436
+ import type { WebSocket } from "ws";
437
+
438
+ const withWsAuth: EventEnhancer<any, any, App.Locals, { user: { id: string } }, { websocket: WebSocket }> = async (event) => {
439
+ // event.websocket is available here
440
+ const token = event.request.headers.get("authorization");
441
+ if (!token) error(401, { message: "Unauthorized" });
442
+ return { user: await verifyToken(token) };
443
+ };
444
+
445
+ router.WS("/ws/chat", enhance(
446
+ async ({ context, websocket }) => {
447
+ websocket.send(`hello ${context.user.id}`);
448
+ },
449
+ withWsAuth
450
+ ));
451
+ ```
452
+
453
+ Plain HTTP enhancers (`TExtra = {}`) can still be passed to a WS `enhance()` call — they just won't have access to `websocket`.
454
+
416
455
  ## Router Lifecycle Hooks
417
456
 
418
457
  Use `pre()` for logic that should run before route resolution, and `post()` for logic that should run after a response has been produced.
@@ -486,6 +525,32 @@ app.WS("/ws/chat/[room]", async (event) => {
486
525
  });
487
526
  ```
488
527
 
528
+ `enhance()` works with WebSocket handlers too. When your handler includes `websocket` in the event type, the returned function requires it automatically:
529
+
530
+ ```ts
531
+ import { enhance, error } from "@sourceregistry/node-webserver";
532
+
533
+ app.WS("/ws/chat/[room]", enhance(
534
+ async ({ context, params, websocket }) => {
535
+ websocket.send(`joined:${params.room} as ${context.user.id}`);
536
+
537
+ websocket.on("message", (message) => {
538
+ websocket.send(`echo:${message.toString()}`);
539
+ });
540
+ },
541
+ async (event) => {
542
+ const token = event.request.headers.get("authorization");
543
+ if (!token) {
544
+ error(401, { message: "Unauthorized" });
545
+ }
546
+
547
+ return { user: { id: "u_1", role: "member" } };
548
+ }
549
+ ));
550
+ ```
551
+
552
+ If an enhancer returns a `Response` (or throws via `error()`), the WebSocket handler is skipped and the connection closes.
553
+
489
554
  ## Static Files
490
555
 
491
556
  Use `dir()` to expose a directory through a route, or `serveStatic()` directly if you want manual control.
package/dist/enhance.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import { RequestEvent } from './types';
2
2
  import { MaybePromise } from './types/MaybePromise';
3
+ import { WebSocket } from 'ws';
3
4
  type AnyFn = (...args: any[]) => any;
4
5
  type ConcatReturnTypes<T extends AnyFn[]> = T extends [] ? {} : T extends [infer First, ...infer Rest] ? First extends AnyFn ? Awaited<ReturnType<First>> & ConcatReturnTypes<Rest extends AnyFn[] ? Rest : []> : {} : {};
5
- export type EventEnhancer<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = (event: RequestEvent<Params, RouteId, Locals>) => MaybePromise<Context | void | undefined | Response>;
6
+ export type EventEnhancer<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>, TExtra extends object = {}> = (event: RequestEvent<Params, RouteId, Locals> & TExtra) => MaybePromise<Context | void | undefined | Response>;
6
7
  export type EnhancedRequestEvent<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = RequestEvent<Params, RouteId, Locals> & {
7
8
  context: Context;
8
9
  };
9
10
  export type EnhancedRouteHandler<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = (event: EnhancedRequestEvent<Params, RouteId, Locals, Context>) => MaybePromise<Response>;
10
- export declare const enhance: <Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Enhancers extends EventEnhancer<Params, RouteId, Locals, any>[] = EventEnhancer<Params, RouteId, Locals, any>[], Context extends Awaited<ConcatReturnTypes<Enhancers>> = Awaited<ConcatReturnTypes<Enhancers>>>(handler: EnhancedRouteHandler<Params, RouteId, Locals, Context>, ...enhancers: Enhancers) => (event: RequestEvent<Params, RouteId, Locals>) => Promise<Response>;
11
+ export type EnhancedWebSocketHandler<Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, Context extends Record<string, any> = Record<string, any>> = (event: EnhancedRequestEvent<Params, RouteId, Locals, Context> & {
12
+ websocket: WebSocket;
13
+ }) => MaybePromise<any>;
14
+ export declare const enhance: <Params extends Partial<Record<string, string>> = Partial<Record<string, string>>, RouteId extends string | null = string | null, Locals extends App.Locals = App.Locals, TExtra extends object = {}, TReturn = unknown, Enhancers extends EventEnhancer<Params, RouteId, Locals, any, TExtra>[] = EventEnhancer<Params, RouteId, Locals, any, TExtra>[], Context extends Awaited<ConcatReturnTypes<Enhancers>> = Awaited<ConcatReturnTypes<Enhancers>>>(handler: (event: EnhancedRequestEvent<Params, RouteId, Locals, Context> & TExtra) => MaybePromise<TReturn>, ...enhancers: Enhancers) => (event: RequestEvent<Params, RouteId, Locals> & TExtra) => Promise<TReturn | Response>;
11
15
  export {};