@b9g/router 0.1.10 → 0.2.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.
- package/README.md +173 -108
- package/package.json +14 -7
- package/src/index.d.ts +63 -93
- package/src/index.js +281 -212
- package/src/middleware.d.ts +101 -0
- package/src/middleware.js +107 -0
package/README.md
CHANGED
|
@@ -1,55 +1,46 @@
|
|
|
1
1
|
# @b9g/router
|
|
2
2
|
|
|
3
|
-
**Universal request router
|
|
3
|
+
**Universal request router built on web standards with generator-based middleware.**
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **Web Standards**: Built on URLPattern-like syntax, Request, and Response APIs
|
|
8
8
|
- **Generator Middleware**: Uses `yield` for flow control (no Express-style `next()`)
|
|
9
|
-
- **Web Standards**: Built on URLPattern, Request, and Response APIs
|
|
10
9
|
- **Universal**: Same code runs in browsers, Node.js, Bun, and edge platforms
|
|
11
10
|
- **Simple Context**: Route parameters and middleware-extensible context
|
|
11
|
+
- **Router Composition**: Mount subrouters with path prefixes
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm install @b9g/router
|
|
16
|
+
npm install @b9g/router
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
21
|
```javascript
|
|
22
|
-
import {
|
|
22
|
+
import {Router} from '@b9g/router';
|
|
23
23
|
|
|
24
24
|
const router = new Router();
|
|
25
25
|
|
|
26
26
|
// Simple route
|
|
27
|
-
router.
|
|
27
|
+
router.route('/hello').get(() => new Response('Hello World!'));
|
|
28
28
|
|
|
29
29
|
// Route with parameters
|
|
30
|
-
router.
|
|
31
|
-
const {
|
|
32
|
-
return Response.json({
|
|
30
|
+
router.route('/posts/:id').get((request, context) => {
|
|
31
|
+
const {id} = context.params;
|
|
32
|
+
return Response.json({id, title: `Post ${id}`});
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
// Handle request
|
|
36
|
-
const response = await router.
|
|
36
|
+
const response = await router.handle(request);
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## Middleware
|
|
40
40
|
|
|
41
|
-
The router supports generator-based middleware with `yield` for clean flow control:
|
|
42
|
-
|
|
41
|
+
The router supports function and generator-based middleware with `yield` for clean flow control:
|
|
43
42
|
```javascript
|
|
44
|
-
//
|
|
45
|
-
router.use(async function* (request, context) {
|
|
46
|
-
console.log(`${request.method} ${request.url}`);
|
|
47
|
-
const response = yield request;
|
|
48
|
-
console.log(`${response.status}`);
|
|
49
|
-
return response;
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Function middleware (can short-circuit)
|
|
43
|
+
// Function middleware
|
|
53
44
|
router.use(async (request, context) => {
|
|
54
45
|
if (!request.headers.get('Authorization')) {
|
|
55
46
|
return new Response('Unauthorized', { status: 401 });
|
|
@@ -57,69 +48,16 @@ router.use(async (request, context) => {
|
|
|
57
48
|
// Return null/undefined to continue to next middleware
|
|
58
49
|
return null;
|
|
59
50
|
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Caching
|
|
63
|
-
|
|
64
|
-
The router doesn't provide built-in cache integration. For caching, use the global `caches` API directly in your handlers:
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
router.get('/api/posts/:id', async (request, context) => {
|
|
68
|
-
// Use global caches API
|
|
69
|
-
const cache = await caches.open('api-v1');
|
|
70
|
-
|
|
71
|
-
const cached = await cache.match(request);
|
|
72
|
-
if (cached) return cached;
|
|
73
|
-
|
|
74
|
-
const post = await db.posts.get(context.params.id);
|
|
75
|
-
const response = Response.json(post);
|
|
76
|
-
|
|
77
|
-
await cache.put(request, response.clone());
|
|
78
|
-
return response;
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Or implement caching as middleware:
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
// Cache middleware
|
|
86
|
-
async function* cacheMiddleware(request, context) {
|
|
87
|
-
if (request.method !== 'GET') {
|
|
88
|
-
return yield request;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const cache = await caches.open('pages-v1');
|
|
92
|
-
const cached = await cache.match(request);
|
|
93
|
-
if (cached) return cached;
|
|
94
51
|
|
|
52
|
+
// Generator middleware
|
|
53
|
+
router.use(async function* (request, context) {
|
|
54
|
+
console.log(`${request.method} ${request.url}`);
|
|
95
55
|
const response = yield request;
|
|
96
|
-
|
|
97
|
-
if (response.ok) {
|
|
98
|
-
await cache.put(request, response.clone());
|
|
99
|
-
}
|
|
100
|
-
|
|
56
|
+
console.log(`${response.status}`);
|
|
101
57
|
return response;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
router.use(cacheMiddleware);
|
|
58
|
+
});
|
|
105
59
|
```
|
|
106
60
|
|
|
107
|
-
## Exports
|
|
108
|
-
|
|
109
|
-
### Classes
|
|
110
|
-
|
|
111
|
-
- `Router` - Request router with pattern matching and middleware support
|
|
112
|
-
|
|
113
|
-
### Types
|
|
114
|
-
|
|
115
|
-
- `RouteContext` - Context object passed to handlers with params and middleware-added properties
|
|
116
|
-
- `Handler` - Route handler function type `(request, context) => Response | Promise<Response>`
|
|
117
|
-
- `GeneratorMiddleware` - Generator-based middleware type using `yield`
|
|
118
|
-
- `FunctionMiddleware` - Simple function middleware type
|
|
119
|
-
- `Middleware` - Union of GeneratorMiddleware | FunctionMiddleware
|
|
120
|
-
- `HTTPMethod` - HTTP method string literal type
|
|
121
|
-
- `RouteConfig` - Route configuration object
|
|
122
|
-
|
|
123
61
|
## API Reference
|
|
124
62
|
|
|
125
63
|
### Router
|
|
@@ -143,17 +81,6 @@ router.route('/api/posts/:id')
|
|
|
143
81
|
.delete(handler);
|
|
144
82
|
```
|
|
145
83
|
|
|
146
|
-
##### HTTP Method Shortcuts
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
router.get(pattern, handler)
|
|
150
|
-
router.post(pattern, handler)
|
|
151
|
-
router.put(pattern, handler)
|
|
152
|
-
router.delete(pattern, handler)
|
|
153
|
-
router.patch(pattern, handler)
|
|
154
|
-
router.head(pattern, handler)
|
|
155
|
-
router.options(pattern, handler)
|
|
156
|
-
```
|
|
157
84
|
|
|
158
85
|
##### `use(middleware)`
|
|
159
86
|
|
|
@@ -163,12 +90,58 @@ Add global middleware.
|
|
|
163
90
|
router.use(loggingMiddleware);
|
|
164
91
|
```
|
|
165
92
|
|
|
166
|
-
##### `
|
|
93
|
+
##### `handle(request): Promise<Response>`
|
|
94
|
+
|
|
95
|
+
Handle an incoming request and return a response.
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const response = await router.handle(request);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
##### `mount(path, subrouter)`
|
|
102
|
+
|
|
103
|
+
Mount a subrouter at a specific path prefix.
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const apiRouter = new Router();
|
|
107
|
+
apiRouter.route('/users').get(handler);
|
|
108
|
+
|
|
109
|
+
const mainRouter = new Router();
|
|
110
|
+
mainRouter.mount('/api/v1', apiRouter);
|
|
111
|
+
// Routes become: /api/v1/users
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
##### `match(url): RouteMatch | null`
|
|
115
|
+
|
|
116
|
+
Match a URL against registered routes without executing handlers.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const match = router.match(new URL('https://example.com/api/users'));
|
|
120
|
+
if (match) {
|
|
121
|
+
console.log(match.params, match.methods);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Properties
|
|
126
|
+
|
|
127
|
+
##### `routes: RouteEntry[]`
|
|
128
|
+
|
|
129
|
+
Read-only array of registered routes for introspection.
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
router.routes.forEach(route => {
|
|
133
|
+
console.log(route.pattern, route.method);
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
##### `middlewares: MiddlewareEntry[]`
|
|
167
138
|
|
|
168
|
-
|
|
139
|
+
Read-only array of registered middleware for introspection.
|
|
169
140
|
|
|
170
141
|
```javascript
|
|
171
|
-
|
|
142
|
+
router.middlewares.forEach(mw => {
|
|
143
|
+
console.log(mw.pathPrefix);
|
|
144
|
+
});
|
|
172
145
|
```
|
|
173
146
|
|
|
174
147
|
### Context Object
|
|
@@ -198,20 +171,20 @@ router.use(async function* (request, context) {
|
|
|
198
171
|
```javascript
|
|
199
172
|
const router = new Router();
|
|
200
173
|
|
|
201
|
-
router.
|
|
202
|
-
Response.json({
|
|
174
|
+
router.route('/api/health').get(() =>
|
|
175
|
+
Response.json({status: 'ok'})
|
|
203
176
|
);
|
|
204
177
|
|
|
205
|
-
router.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
178
|
+
router.route('/api/posts')
|
|
179
|
+
.get(async () => {
|
|
180
|
+
const posts = await db.posts.findAll();
|
|
181
|
+
return Response.json(posts);
|
|
182
|
+
})
|
|
183
|
+
.post(async (request) => {
|
|
184
|
+
const data = await request.json();
|
|
185
|
+
const post = await db.posts.create(data);
|
|
186
|
+
return Response.json(post, {status: 201});
|
|
187
|
+
});
|
|
215
188
|
```
|
|
216
189
|
|
|
217
190
|
### Authentication Middleware
|
|
@@ -229,7 +202,7 @@ router.use(async function* (request, context) {
|
|
|
229
202
|
});
|
|
230
203
|
|
|
231
204
|
// Protected route
|
|
232
|
-
router.
|
|
205
|
+
router.route('/api/profile').get(async (request, context) => {
|
|
233
206
|
if (!context.user) {
|
|
234
207
|
return new Response('Unauthorized', { status: 401 });
|
|
235
208
|
}
|
|
@@ -242,8 +215,8 @@ router.get('/api/profile', async (request, context) => {
|
|
|
242
215
|
```javascript
|
|
243
216
|
// API subrouter
|
|
244
217
|
const apiRouter = new Router();
|
|
245
|
-
apiRouter.
|
|
246
|
-
apiRouter.
|
|
218
|
+
apiRouter.route('/users').get(getUsersHandler);
|
|
219
|
+
apiRouter.route('/posts').get(getPostsHandler);
|
|
247
220
|
|
|
248
221
|
// Main router
|
|
249
222
|
const mainRouter = new Router();
|
|
@@ -251,6 +224,98 @@ mainRouter.mount('/api/v1', apiRouter);
|
|
|
251
224
|
// Routes become: /api/v1/users, /api/v1/posts
|
|
252
225
|
```
|
|
253
226
|
|
|
227
|
+
## Exports
|
|
228
|
+
|
|
229
|
+
### Classes
|
|
230
|
+
|
|
231
|
+
- `Router` - Main router class
|
|
232
|
+
- `RouteBuilder` - Fluent API for defining routes (returned by `router.route()`)
|
|
233
|
+
|
|
234
|
+
### Types
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// Handler and middleware types
|
|
238
|
+
type Handler = (request: Request, context: RouteContext) => Response | Promise<Response>
|
|
239
|
+
type FunctionMiddleware = (request: Request, context: RouteContext) => Response | null | undefined | Promise<Response | null | undefined>
|
|
240
|
+
type GeneratorMiddleware = (request: Request, context: RouteContext) => Generator<Request, Response | null | undefined, Response> | AsyncGenerator<Request, Response | null | undefined, Response>
|
|
241
|
+
type Middleware = GeneratorMiddleware | FunctionMiddleware
|
|
242
|
+
|
|
243
|
+
// Context and route types
|
|
244
|
+
interface RouteContext {
|
|
245
|
+
params: Record<string, string>
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
interface RouteOptions {
|
|
249
|
+
name?: string
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
interface RouteMatch {
|
|
253
|
+
params: Record<string, string>
|
|
254
|
+
methods: string[]
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
interface RouteEntry {
|
|
258
|
+
pattern: MatchPattern
|
|
259
|
+
method: string
|
|
260
|
+
handler: Handler
|
|
261
|
+
name?: string
|
|
262
|
+
middleware: Middleware[]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
interface MiddlewareEntry {
|
|
266
|
+
middleware: Middleware
|
|
267
|
+
pathPrefix?: string
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// HTTP methods
|
|
271
|
+
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS"
|
|
272
|
+
|
|
273
|
+
// Utility types
|
|
274
|
+
type TrailingSlashMode = "strip" | "add"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Middleware Utilities
|
|
278
|
+
|
|
279
|
+
Standard middleware is available from `@b9g/router/middleware`:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import {cors, trailingSlash} from '@b9g/router/middleware';
|
|
283
|
+
|
|
284
|
+
// CORS middleware
|
|
285
|
+
router.use(cors({
|
|
286
|
+
origin: "https://example.com",
|
|
287
|
+
credentials: true
|
|
288
|
+
}));
|
|
289
|
+
|
|
290
|
+
// Trailing slash normalization
|
|
291
|
+
router.use(trailingSlash("strip")); // /path/ → /path
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Available Middleware
|
|
295
|
+
|
|
296
|
+
#### `cors(options?: CORSOptions)`
|
|
297
|
+
|
|
298
|
+
Handles Cross-Origin Resource Sharing headers and preflight requests.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
interface CORSOptions {
|
|
302
|
+
origin?: string | string[] | ((origin: string) => boolean); // Default: "*"
|
|
303
|
+
methods?: string[]; // Default: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"]
|
|
304
|
+
allowedHeaders?: string[]; // Default: ["Content-Type", "Authorization"]
|
|
305
|
+
exposedHeaders?: string[];
|
|
306
|
+
credentials?: boolean; // Default: false
|
|
307
|
+
maxAge?: number; // Default: 86400 (24 hours)
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
#### `trailingSlash(mode: TrailingSlashMode)`
|
|
312
|
+
|
|
313
|
+
Normalizes URL trailing slashes via 301 redirect.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
type TrailingSlashMode = "strip" | "add";
|
|
317
|
+
```
|
|
318
|
+
|
|
254
319
|
## License
|
|
255
320
|
|
|
256
321
|
MIT
|
package/package.json
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/router",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Universal request router
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Universal request router built on web standards with generator-based middleware.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"router",
|
|
7
7
|
"serviceworker",
|
|
8
8
|
"middleware",
|
|
9
|
-
"cache",
|
|
10
9
|
"universal",
|
|
11
10
|
"web-standards",
|
|
12
11
|
"urlpattern",
|
|
12
|
+
"generator",
|
|
13
13
|
"shovel"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@b9g/http-errors": "^0.
|
|
17
|
-
"@b9g/match-pattern": "^0.
|
|
16
|
+
"@b9g/http-errors": "^0.2.0",
|
|
17
|
+
"@b9g/match-pattern": "^0.2.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@b9g/libuild": "^0.1.18"
|
|
21
|
-
"bun-types": "latest"
|
|
20
|
+
"@b9g/libuild": "^0.1.18"
|
|
22
21
|
},
|
|
23
22
|
"type": "module",
|
|
24
23
|
"types": "src/index.d.ts",
|
|
@@ -36,6 +35,14 @@
|
|
|
36
35
|
"types": "./src/index.d.ts",
|
|
37
36
|
"import": "./src/index.js"
|
|
38
37
|
},
|
|
38
|
+
"./middleware": {
|
|
39
|
+
"types": "./src/middleware.d.ts",
|
|
40
|
+
"import": "./src/middleware.js"
|
|
41
|
+
},
|
|
42
|
+
"./middleware.js": {
|
|
43
|
+
"types": "./src/middleware.d.ts",
|
|
44
|
+
"import": "./src/middleware.js"
|
|
45
|
+
},
|
|
39
46
|
"./package.json": "./package.json"
|
|
40
47
|
}
|
|
41
48
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @b9g/router - Universal request router built on web standards
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - Pure Request/Response routing (works anywhere)
|
|
6
|
-
* - Chainable route builder API
|
|
7
|
-
* - Generator-based middleware with yield continuation
|
|
8
|
-
* - Integration with URLPattern and MatchPattern for enhanced URL matching
|
|
9
|
-
* - Cache-aware routing
|
|
10
|
-
* TODO:
|
|
11
|
-
* - Portable param matching
|
|
12
|
-
* - Typechecking
|
|
13
|
-
*/
|
|
1
|
+
/** @b9g/router - Universal request router built on web standards */
|
|
14
2
|
/**
|
|
15
3
|
* Context object passed to handlers and middleware
|
|
16
4
|
* Contains route parameters extracted from URL pattern matching
|
|
5
|
+
* Augmentable via module declaration for middleware-specific properties
|
|
17
6
|
*/
|
|
18
7
|
export interface RouteContext {
|
|
19
8
|
/** Route parameters extracted from URL pattern matching */
|
|
20
9
|
params: Record<string, string>;
|
|
21
|
-
/**
|
|
22
|
-
[key: string]:
|
|
10
|
+
/** Allow middleware to add arbitrary properties to context */
|
|
11
|
+
[key: string]: unknown;
|
|
23
12
|
}
|
|
24
13
|
/**
|
|
25
14
|
* Handler function signature - terminal response producer
|
|
@@ -27,17 +16,16 @@ export interface RouteContext {
|
|
|
27
16
|
*/
|
|
28
17
|
export type Handler = (request: Request, context: RouteContext) => Response | Promise<Response>;
|
|
29
18
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
19
|
+
* Function middleware signature
|
|
20
|
+
* Can modify request and context, and can return a Response to short-circuit
|
|
32
21
|
*/
|
|
33
|
-
export type
|
|
22
|
+
export type FunctionMiddleware = (request: Request, context: RouteContext) => Response | null | undefined | void | Promise<Response | null | undefined | void>;
|
|
34
23
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* - Return null/undefined: continues to next middleware (fallthrough)
|
|
24
|
+
* Generator middleware signature - uses yield for continuation.
|
|
25
|
+
* Yield to pass control to the next middleware/handler, receive Response back.
|
|
26
|
+
* Optionally yield a modified Request (or yield without value to use original).
|
|
39
27
|
*/
|
|
40
|
-
export type
|
|
28
|
+
export type GeneratorMiddleware = (request: Request, context: RouteContext) => Generator<Request | undefined, Response | null | undefined | void, Response> | AsyncGenerator<Request | undefined, Response | null | undefined | void, Response>;
|
|
41
29
|
/**
|
|
42
30
|
* Union type for all supported middleware types
|
|
43
31
|
* Framework automatically detects type and executes appropriately
|
|
@@ -48,24 +36,40 @@ export type Middleware = GeneratorMiddleware | FunctionMiddleware;
|
|
|
48
36
|
*/
|
|
49
37
|
export type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
|
50
38
|
/**
|
|
51
|
-
* Route
|
|
39
|
+
* Route options for configuring route metadata
|
|
40
|
+
* Augmentable via module declaration for custom metadata
|
|
41
|
+
*/
|
|
42
|
+
export interface RouteOptions {
|
|
43
|
+
/** Optional name for the route, useful for matching/identification */
|
|
44
|
+
name?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Result of matching a URL against registered routes
|
|
52
48
|
*/
|
|
53
|
-
export interface
|
|
54
|
-
/** URL pattern
|
|
49
|
+
export interface RouteMatch {
|
|
50
|
+
/** Route parameters extracted from URL pattern matching */
|
|
51
|
+
params: Record<string, string>;
|
|
52
|
+
/** HTTP methods registered for this pattern */
|
|
53
|
+
methods: string[];
|
|
54
|
+
/** Route name if provided */
|
|
55
|
+
name?: string;
|
|
56
|
+
/** Original pattern string */
|
|
55
57
|
pattern: string;
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
60
|
+
* Route entry stored by the router
|
|
59
61
|
*/
|
|
60
|
-
interface RouteEntry {
|
|
62
|
+
export interface RouteEntry {
|
|
61
63
|
pattern: import("@b9g/match-pattern").MatchPattern;
|
|
62
64
|
method: string;
|
|
63
|
-
handler
|
|
65
|
+
handler?: Handler;
|
|
66
|
+
name?: string;
|
|
67
|
+
middlewares: Middleware[];
|
|
64
68
|
}
|
|
65
69
|
/**
|
|
66
70
|
* Internal middleware entry stored by the router
|
|
67
71
|
*/
|
|
68
|
-
interface MiddlewareEntry {
|
|
72
|
+
export interface MiddlewareEntry {
|
|
69
73
|
middleware: Middleware;
|
|
70
74
|
/** If set, middleware only runs for paths matching this prefix */
|
|
71
75
|
pathPrefix?: string;
|
|
@@ -74,46 +78,51 @@ interface MiddlewareEntry {
|
|
|
74
78
|
* RouteBuilder provides a chainable API for defining routes with multiple HTTP methods
|
|
75
79
|
*
|
|
76
80
|
* Example:
|
|
77
|
-
* router.route('/api/users/:id')
|
|
81
|
+
* router.route('/api/users/:id', { name: 'user' })
|
|
82
|
+
* .use(authMiddleware)
|
|
78
83
|
* .get(getUserHandler)
|
|
79
84
|
* .put(updateUserHandler)
|
|
80
85
|
* .delete(deleteUserHandler);
|
|
81
86
|
*/
|
|
82
|
-
declare class RouteBuilder {
|
|
87
|
+
export declare class RouteBuilder {
|
|
83
88
|
#private;
|
|
84
|
-
constructor(router: Router, pattern: string);
|
|
89
|
+
constructor(router: Router, pattern: string, options?: RouteOptions);
|
|
90
|
+
/**
|
|
91
|
+
* Add route-scoped middleware that only runs when this pattern matches
|
|
92
|
+
*/
|
|
93
|
+
use(middleware: Middleware): RouteBuilder;
|
|
85
94
|
/**
|
|
86
95
|
* Register a GET handler for this route pattern
|
|
87
96
|
*/
|
|
88
|
-
get(handler
|
|
97
|
+
get(handler?: Handler): RouteBuilder;
|
|
89
98
|
/**
|
|
90
99
|
* Register a POST handler for this route pattern
|
|
91
100
|
*/
|
|
92
|
-
post(handler
|
|
101
|
+
post(handler?: Handler): RouteBuilder;
|
|
93
102
|
/**
|
|
94
103
|
* Register a PUT handler for this route pattern
|
|
95
104
|
*/
|
|
96
|
-
put(handler
|
|
105
|
+
put(handler?: Handler): RouteBuilder;
|
|
97
106
|
/**
|
|
98
107
|
* Register a DELETE handler for this route pattern
|
|
99
108
|
*/
|
|
100
|
-
delete(handler
|
|
109
|
+
delete(handler?: Handler): RouteBuilder;
|
|
101
110
|
/**
|
|
102
111
|
* Register a PATCH handler for this route pattern
|
|
103
112
|
*/
|
|
104
|
-
patch(handler
|
|
113
|
+
patch(handler?: Handler): RouteBuilder;
|
|
105
114
|
/**
|
|
106
115
|
* Register a HEAD handler for this route pattern
|
|
107
116
|
*/
|
|
108
|
-
head(handler
|
|
117
|
+
head(handler?: Handler): RouteBuilder;
|
|
109
118
|
/**
|
|
110
119
|
* Register an OPTIONS handler for this route pattern
|
|
111
120
|
*/
|
|
112
|
-
options(handler
|
|
121
|
+
options(handler?: Handler): RouteBuilder;
|
|
113
122
|
/**
|
|
114
123
|
* Register a handler for all HTTP methods on this route pattern
|
|
115
124
|
*/
|
|
116
|
-
all(handler
|
|
125
|
+
all(handler?: Handler): RouteBuilder;
|
|
117
126
|
}
|
|
118
127
|
/**
|
|
119
128
|
* Router provides Request/Response routing with middleware support
|
|
@@ -121,6 +130,8 @@ declare class RouteBuilder {
|
|
|
121
130
|
*/
|
|
122
131
|
export declare class Router {
|
|
123
132
|
#private;
|
|
133
|
+
readonly routes: RouteEntry[];
|
|
134
|
+
readonly middlewares: MiddlewareEntry[];
|
|
124
135
|
constructor();
|
|
125
136
|
/**
|
|
126
137
|
* Register middleware that applies to all routes
|
|
@@ -136,36 +147,28 @@ export declare class Router {
|
|
|
136
147
|
* Returns a chainable interface for registering HTTP method handlers
|
|
137
148
|
*
|
|
138
149
|
* Example:
|
|
139
|
-
* router.route('/api/users/:id')
|
|
150
|
+
* router.route('/api/users/:id', { name: 'user' })
|
|
151
|
+
* .use(authMiddleware)
|
|
140
152
|
* .get(getUserHandler)
|
|
141
153
|
* .put(updateUserHandler);
|
|
142
154
|
*/
|
|
143
|
-
route(pattern: string): RouteBuilder;
|
|
144
|
-
route(config: RouteConfig): RouteBuilder;
|
|
155
|
+
route(pattern: string, options?: RouteOptions): RouteBuilder;
|
|
145
156
|
/**
|
|
146
157
|
* Internal method called by RouteBuilder to register routes
|
|
147
158
|
* Public for RouteBuilder access, but not intended for direct use
|
|
148
159
|
*/
|
|
149
|
-
addRoute(method: HTTPMethod, pattern: string, handler
|
|
150
|
-
/**
|
|
151
|
-
* Handle a request - main entrypoint for ServiceWorker usage
|
|
152
|
-
* Returns a response or throws if no route matches
|
|
153
|
-
*/
|
|
154
|
-
handler: (request: Request) => Promise<Response>;
|
|
155
|
-
/**
|
|
156
|
-
* Match a request against registered routes and execute the handler chain
|
|
157
|
-
* Returns the response from the matched handler, or null if no route matches
|
|
158
|
-
* Note: Global middleware executes even if no route matches
|
|
159
|
-
*/
|
|
160
|
-
match(request: Request): Promise<Response | null>;
|
|
160
|
+
addRoute(method: HTTPMethod, pattern: string, handler?: Handler, name?: string, middlewares?: Middleware[]): void;
|
|
161
161
|
/**
|
|
162
|
-
*
|
|
162
|
+
* Match a URL against registered routes
|
|
163
|
+
* Returns route info (params, methods, name, pattern) or null if no match
|
|
164
|
+
* Does not execute handlers - use handle() for that
|
|
163
165
|
*/
|
|
164
|
-
|
|
166
|
+
match(url: string | URL): RouteMatch | null;
|
|
165
167
|
/**
|
|
166
|
-
*
|
|
168
|
+
* Handle a request - main entrypoint for ServiceWorker usage
|
|
169
|
+
* Executes the matched handler with middleware chain
|
|
167
170
|
*/
|
|
168
|
-
|
|
171
|
+
handle(request: Request): Promise<Response>;
|
|
169
172
|
/**
|
|
170
173
|
* Mount a subrouter at a specific path prefix
|
|
171
174
|
* All routes from the subrouter will be prefixed with the mount path
|
|
@@ -180,37 +183,4 @@ export declare class Router {
|
|
|
180
183
|
* // Routes become: /api/v1/users, /api/v1/users/:id
|
|
181
184
|
*/
|
|
182
185
|
mount(mountPath: string, subrouter: Router): void;
|
|
183
|
-
/**
|
|
184
|
-
* Get route statistics
|
|
185
|
-
*/
|
|
186
|
-
getStats(): {
|
|
187
|
-
routeCount: number;
|
|
188
|
-
middlewareCount: number;
|
|
189
|
-
compiled: boolean;
|
|
190
|
-
};
|
|
191
186
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Mode for trailing slash normalization
|
|
194
|
-
* - "strip": Redirect /path/ → /path (removes trailing slash)
|
|
195
|
-
* - "add": Redirect /path → /path/ (adds trailing slash)
|
|
196
|
-
*/
|
|
197
|
-
export type TrailingSlashMode = "strip" | "add";
|
|
198
|
-
/**
|
|
199
|
-
* Middleware that normalizes trailing slashes via 301 redirect
|
|
200
|
-
*
|
|
201
|
-
* @param mode - "strip" removes trailing slash, "add" adds trailing slash
|
|
202
|
-
* @returns Function middleware that redirects non-canonical URLs
|
|
203
|
-
*
|
|
204
|
-
* @example
|
|
205
|
-
* ```typescript
|
|
206
|
-
* import { Router, trailingSlash } from "@b9g/router";
|
|
207
|
-
*
|
|
208
|
-
* const router = new Router();
|
|
209
|
-
* router.use(trailingSlash("strip")); // Redirect /path/ → /path
|
|
210
|
-
*
|
|
211
|
-
* // Can also be scoped to specific paths
|
|
212
|
-
* router.use("/api", trailingSlash("strip"));
|
|
213
|
-
* ```
|
|
214
|
-
*/
|
|
215
|
-
export declare function trailingSlash(mode: TrailingSlashMode): FunctionMiddleware;
|
|
216
|
-
export {};
|