@b9g/router 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -279,7 +279,10 @@ type TrailingSlashMode = "strip" | "add"
279
279
  Standard middleware is available from `@b9g/router/middleware`:
280
280
 
281
281
  ```typescript
282
- import {cors, trailingSlash} from '@b9g/router/middleware';
282
+ import {cors, logger, trailingSlash} from '@b9g/router/middleware';
283
+
284
+ // Request logging
285
+ router.use(logger());
283
286
 
284
287
  // CORS middleware
285
288
  router.use(cors({
@@ -293,6 +296,21 @@ router.use(trailingSlash("strip")); // /path/ → /path
293
296
 
294
297
  ### Available Middleware
295
298
 
299
+ #### `logger(options?: LoggerOptions)`
300
+
301
+ Logs incoming requests and outgoing responses with timing information via [LogTape](https://github.com/dahlia/logtape).
302
+
303
+ ```
304
+ → GET /
305
+ ← 200 GET / (3ms)
306
+ ```
307
+
308
+ ```typescript
309
+ interface LoggerOptions {
310
+ category?: string[]; // Default: ["app", "router"]
311
+ }
312
+ ```
313
+
296
314
  #### `cors(options?: CORSOptions)`
297
315
 
298
316
  Handles Cross-Origin Resource Sharing headers and preflight requests.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/router",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Universal request router built on web standards with generator-based middleware.",
5
5
  "keywords": [
6
6
  "router",
@@ -12,9 +12,15 @@
12
12
  "generator",
13
13
  "shovel"
14
14
  ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/bikeshaving/shovel.git",
18
+ "directory": "packages/router"
19
+ },
15
20
  "dependencies": {
16
- "@b9g/http-errors": "^0.2.0",
17
- "@b9g/match-pattern": "^0.2.0"
21
+ "@b9g/http-errors": "^0.2.1",
22
+ "@b9g/match-pattern": "^0.2.1",
23
+ "@logtape/logtape": "^1.2.0"
18
24
  },
19
25
  "devDependencies": {
20
26
  "@b9g/libuild": "^0.1.18"
package/src/index.js CHANGED
@@ -135,7 +135,8 @@ var RadixTreeExecutor = class {
135
135
  */
136
136
  #matchTree(pathname, method) {
137
137
  const result = this.#matchTreeByPath(pathname);
138
- if (!result) return null;
138
+ if (!result)
139
+ return null;
139
140
  const { node, params } = result;
140
141
  let entry = node.routes.get(method);
141
142
  if (!entry && method === "HEAD") {
@@ -617,7 +618,8 @@ var Router = class {
617
618
  context,
618
619
  runningGenerators
619
620
  );
620
- if (currentResponse) break;
621
+ if (currentResponse)
622
+ break;
621
623
  }
622
624
  if (!currentResponse) {
623
625
  for (const middleware of routeMiddlewares) {
@@ -627,7 +629,8 @@ var Router = class {
627
629
  context,
628
630
  runningGenerators
629
631
  );
630
- if (currentResponse) break;
632
+ if (currentResponse)
633
+ break;
631
634
  }
632
635
  }
633
636
  if (!currentResponse) {
@@ -98,4 +98,39 @@ export interface CORSOptions {
98
98
  * }));
99
99
  * ```
100
100
  */
101
+ /**
102
+ * Logger configuration options
103
+ */
104
+ export interface LoggerOptions {
105
+ /**
106
+ * LogTape category for the logger
107
+ * @default ["app", "router"]
108
+ */
109
+ category?: string[];
110
+ }
111
+ /**
112
+ * Request logging middleware
113
+ *
114
+ * Logs incoming requests and outgoing responses with timing information.
115
+ * Uses LogTape via the specified category (default: ["app", "router"]).
116
+ *
117
+ * Output format:
118
+ * ```
119
+ * → GET /
120
+ * ← 200 GET / (3ms)
121
+ * ```
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * import {Router} from "@b9g/router";
126
+ * import {logger} from "@b9g/router/middleware";
127
+ *
128
+ * const router = new Router();
129
+ * router.use(logger());
130
+ *
131
+ * // Custom category
132
+ * router.use(logger({ category: ["app", "http"] }));
133
+ * ```
134
+ */
135
+ export declare function logger(options?: LoggerOptions): (request: Request) => AsyncGenerator<Request, Response | undefined, Response>;
101
136
  export declare function cors(options?: CORSOptions): (request: Request, _context: any) => AsyncGenerator<Request, Response | undefined, Response>;
package/src/middleware.js CHANGED
@@ -1,10 +1,12 @@
1
1
  /// <reference types="./middleware.d.ts" />
2
2
  // src/middleware.ts
3
+ import { getLogger } from "@logtape/logtape";
3
4
  function trailingSlash(mode) {
4
5
  return (req) => {
5
6
  const url = new URL(req.url);
6
7
  const pathname = url.pathname;
7
- if (pathname === "/") return;
8
+ if (pathname === "/")
9
+ return;
8
10
  let newPathname = null;
9
11
  if (mode === "strip" && pathname.endsWith("/")) {
10
12
  newPathname = pathname.slice(0, -1);
@@ -38,6 +40,21 @@ function getAllowedOrigin(config, requestOrigin) {
38
40
  }
39
41
  return null;
40
42
  }
43
+ function logger(options = {}) {
44
+ const { category = ["app", "router"] } = options;
45
+ const log = getLogger(category);
46
+ return async function* (request) {
47
+ const url = new URL(request.url);
48
+ const method = request.method;
49
+ const pathname = url.pathname;
50
+ log.info`→ ${method} ${pathname}`;
51
+ const start = Date.now();
52
+ const response = yield request;
53
+ const duration = Date.now() - start;
54
+ log.info`← ${response.status} ${method} ${pathname} (${duration}ms)`;
55
+ return response;
56
+ };
57
+ }
41
58
  function cors(options = {}) {
42
59
  const {
43
60
  origin = "*",
@@ -103,5 +120,6 @@ function cors(options = {}) {
103
120
  }
104
121
  export {
105
122
  cors,
123
+ logger,
106
124
  trailingSlash
107
125
  };