@bepalo/router 1.1.12 → 1.2.14
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 +288 -160
- package/dist/cjs/helpers.d.ts +26 -6
- package/dist/cjs/helpers.d.ts.map +1 -1
- package/dist/cjs/helpers.js +63 -32
- package/dist/cjs/helpers.js.map +1 -1
- package/dist/cjs/middlewares.d.ts +17 -12
- package/dist/cjs/middlewares.d.ts.map +1 -1
- package/dist/cjs/middlewares.js +20 -8
- package/dist/cjs/middlewares.js.map +1 -1
- package/dist/cjs/router.d.ts +16 -24
- package/dist/cjs/router.d.ts.map +1 -1
- package/dist/cjs/router.js +18 -17
- package/dist/cjs/router.js.map +1 -1
- package/dist/cjs/types.d.ts +9 -2
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/helpers.d.ts +26 -6
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +63 -32
- package/dist/helpers.js.map +1 -1
- package/dist/middlewares.d.ts +17 -12
- package/dist/middlewares.d.ts.map +1 -1
- package/dist/middlewares.js +20 -8
- package/dist/middlewares.js.map +1 -1
- package/dist/router.d.ts +16 -24
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +18 -17
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +8,43 @@
|
|
|
8
8
|
|
|
9
9
|
[](test-result.md)
|
|
10
10
|
|
|
11
|
-
**A fast, feature-rich HTTP router for modern JavaScript runtimes.**
|
|
11
|
+
**A fast, feature-rich HTTP router for modern JavaScript runtimes.** [jump to example](#example)
|
|
12
|
+
|
|
13
|
+
## 📑 Table of Contents
|
|
14
|
+
|
|
15
|
+
1. [🏆 @bepalo/router](#-bepalorouter)
|
|
16
|
+
2. [✨ Features](#-features)
|
|
17
|
+
3. [🚀 Get Started](#-get-started)
|
|
18
|
+
- [📥 Installation](#-installation)
|
|
19
|
+
- [📦 Basic Usage](#-basic-usage)
|
|
20
|
+
- [Example](#example)
|
|
21
|
+
- [Serve with client address](#serve-with-client-address)
|
|
22
|
+
- [Bun](#bun)
|
|
23
|
+
- [Deno](#deno)
|
|
24
|
+
- [Nodejs](#nodejs)
|
|
25
|
+
4. [📚 Core Concepts](#-core-concepts)
|
|
26
|
+
- [Handler Types & Execution Order](#handler-types--execution-order)
|
|
27
|
+
- [Router Context](#router-context)
|
|
28
|
+
5. [📖 API Reference](#-api-reference)
|
|
29
|
+
- [Router Class](#router-class)
|
|
30
|
+
- [Constructor](#constructor)
|
|
31
|
+
- [Configuration Options](#configuration-options)
|
|
32
|
+
- [Handler Registration Methods](#handler-registration-methods)
|
|
33
|
+
- [Handler Options](#handler-options)
|
|
34
|
+
- [Router Composition](#router-composition)
|
|
35
|
+
- [Request Processing](#request-processing)
|
|
36
|
+
- [Helper Functions](#helper-functions)
|
|
37
|
+
- [Provided Middleware](#provided-middleware)
|
|
38
|
+
- [🔧 Advanced Usage](#-advanced-usage)
|
|
39
|
+
- [Pipeline (Multiple Handlers)](#pipeline-multiple-handlers)
|
|
40
|
+
- [Path Patterns](#path-patterns)
|
|
41
|
+
- [Route Priority](#route-priority)
|
|
42
|
+
- [Router Composition Example](#router-composition-example)
|
|
43
|
+
6. [🎯 Performance](#-performance)
|
|
44
|
+
- [Comparison with Other Routers](#comparison-with-other-routers)
|
|
45
|
+
7. [📄 License](#-license)
|
|
46
|
+
8. [🕊️ Thanks and Enjoy](#-thanks-and-enjoy)
|
|
47
|
+
9. [💖 Be a Sponsor](#-be-a-sponsor)
|
|
12
48
|
|
|
13
49
|
## ✨ Features
|
|
14
50
|
|
|
@@ -107,8 +143,178 @@ Deno.serve(
|
|
|
107
143
|
console.log("Server running at http://localhost:3000");
|
|
108
144
|
```
|
|
109
145
|
|
|
146
|
+
### Example
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
import {
|
|
150
|
+
Router,
|
|
151
|
+
status,
|
|
152
|
+
html,
|
|
153
|
+
json,
|
|
154
|
+
cors,
|
|
155
|
+
limitRate,
|
|
156
|
+
parseBody,
|
|
157
|
+
type CTXBody,
|
|
158
|
+
type CTXAddress,
|
|
159
|
+
type SocketAddress,
|
|
160
|
+
} from "@bepalo/router";
|
|
161
|
+
import { z } from "zod";
|
|
162
|
+
|
|
163
|
+
const router = new Router<CTXAddress>({
|
|
164
|
+
// Default headers can accept static headers or dynamic headers
|
|
165
|
+
// as a function like this
|
|
166
|
+
defaultHeaders: () => [
|
|
167
|
+
["X-Powered-By", "@bepalo/router"],
|
|
168
|
+
["Date", new Date().toUTCString()],
|
|
169
|
+
],
|
|
170
|
+
// Errors are caught by defualt but not logged
|
|
171
|
+
defaultCatcher: (req, ctx) => {
|
|
172
|
+
console.error("Error:", ctx.error);
|
|
173
|
+
return json({ error: "Something went wrong" }, { status: 500 });
|
|
174
|
+
},
|
|
175
|
+
// For crude optimizations
|
|
176
|
+
enable: {
|
|
177
|
+
hooks: false,
|
|
178
|
+
afters: false,
|
|
179
|
+
filters: true,
|
|
180
|
+
fallbacks: true,
|
|
181
|
+
catchers: true,
|
|
182
|
+
},
|
|
183
|
+
///...
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Global CORS
|
|
187
|
+
router.filter("GET /.**", [
|
|
188
|
+
limitRate({
|
|
189
|
+
key: (req, ctx) => ctx.address.address, // used to identify client
|
|
190
|
+
maxTokens: 30,
|
|
191
|
+
refillRate: 3, // 3 tokens every second
|
|
192
|
+
setXRateLimitHeaders: true,
|
|
193
|
+
}),
|
|
194
|
+
cors({
|
|
195
|
+
origins: "*",
|
|
196
|
+
methods: ["GET"],
|
|
197
|
+
}),
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
// Rate limiting for API
|
|
201
|
+
router.filter(
|
|
202
|
+
[
|
|
203
|
+
"GET /api/.**",
|
|
204
|
+
"POST /api/.**",
|
|
205
|
+
"PUT /api/.**",
|
|
206
|
+
"PATCH /api/.**",
|
|
207
|
+
"DELETE /api/.**",
|
|
208
|
+
],
|
|
209
|
+
[
|
|
210
|
+
limitRate({
|
|
211
|
+
key: (req, ctx) => ctx.address.address, // used to identify client
|
|
212
|
+
maxTokens: 100,
|
|
213
|
+
refillInterval: 30_000, // every 30 seconds
|
|
214
|
+
refillRate: 50, // 50 tokens every refillInterval
|
|
215
|
+
setXRateLimitHeaders: true,
|
|
216
|
+
}),
|
|
217
|
+
cors({
|
|
218
|
+
origins: ["http://localhost:3000", "https://example.com"],
|
|
219
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
220
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
221
|
+
credentials: true,
|
|
222
|
+
endHere: true,
|
|
223
|
+
}),
|
|
224
|
+
],
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Main route
|
|
228
|
+
router.handle("GET /", () =>
|
|
229
|
+
html("<h1>Welcome! Enjoy using @beplao/router</h1>"),
|
|
230
|
+
);
|
|
231
|
+
router.handle("GET /status", () => status(200));
|
|
232
|
+
|
|
233
|
+
// Sample sub-route `/api/user`
|
|
234
|
+
////////////////////////////////////////
|
|
235
|
+
// eg. inside routes/api/user.ts
|
|
236
|
+
const userRepo = new Map();
|
|
237
|
+
const userAPI = new Router();
|
|
238
|
+
let topId = 1000;
|
|
239
|
+
|
|
240
|
+
const postUserBodySchema = z.object({
|
|
241
|
+
name: z.string(),
|
|
242
|
+
password: z.string().min(4),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
userAPI.filter<CTXBody>("POST /", [
|
|
246
|
+
parseBody(),
|
|
247
|
+
(req, { body }) => {
|
|
248
|
+
const { success, error } = postUserBodySchema.safeParse(body);
|
|
249
|
+
const errors = error?.issues ?? [];
|
|
250
|
+
if (!success) return json({ errors }, { status: 400 });
|
|
251
|
+
},
|
|
252
|
+
]);
|
|
253
|
+
userAPI.handle<CTXBody>("POST /", [
|
|
254
|
+
(req, { body }) => {
|
|
255
|
+
const id = topId++;
|
|
256
|
+
const { name, password } = body;
|
|
257
|
+
const user = { id, name, password };
|
|
258
|
+
userRepo.set(id, user);
|
|
259
|
+
return json({ success: true, id }, { status: 201 });
|
|
260
|
+
},
|
|
261
|
+
]);
|
|
262
|
+
|
|
263
|
+
userAPI.handle("GET /", () =>
|
|
264
|
+
json({ users: Object.fromEntries(userRepo.entries()) }),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
userAPI.handle("GET /:userId", (req, { params }) => {
|
|
268
|
+
const { userId } = params;
|
|
269
|
+
const user = userRepo.get(parseInt(userId));
|
|
270
|
+
if (!user) return json({ error: "User not found" }, { status: 404 });
|
|
271
|
+
return json({ user });
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
////////////////////////////////////////
|
|
275
|
+
|
|
276
|
+
router.append("/api/user", userAPI);
|
|
277
|
+
|
|
278
|
+
// fallback handling
|
|
279
|
+
router.fallback("GET /api/.**", () =>
|
|
280
|
+
json({ error: "Not found" }, { status: 404 }),
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// Error handling
|
|
284
|
+
router.catch(
|
|
285
|
+
[
|
|
286
|
+
"GET /api/.**",
|
|
287
|
+
"POST /api/.**",
|
|
288
|
+
"PUT /api/.**",
|
|
289
|
+
"PATCH /api/.**",
|
|
290
|
+
"DELETE /api/.**",
|
|
291
|
+
],
|
|
292
|
+
[
|
|
293
|
+
(req, ctx) => {
|
|
294
|
+
console.error("APIError:", ctx.error);
|
|
295
|
+
return json({ error: "Something went wrong" }, { status: 500 });
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Start server
|
|
301
|
+
Bun.serve({
|
|
302
|
+
port: 3000,
|
|
303
|
+
async fetch(req, server) {
|
|
304
|
+
const address = server.requestIP(req) as SocketAddress | null;
|
|
305
|
+
if (!address) throw new Error("null client address");
|
|
306
|
+
/// best to log request and response here...
|
|
307
|
+
return await router.respond(req, { address });
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
console.log("Server listening on http://localhost:3000");
|
|
312
|
+
```
|
|
313
|
+
|
|
110
314
|
### Serve with client address
|
|
111
315
|
|
|
316
|
+
#### Bun
|
|
317
|
+
|
|
112
318
|
```js
|
|
113
319
|
// Bun example
|
|
114
320
|
Bun.serve({
|
|
@@ -119,7 +325,11 @@ Bun.serve({
|
|
|
119
325
|
},
|
|
120
326
|
});
|
|
121
327
|
console.log("Server running at http://localhost:3000");
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Deno
|
|
122
331
|
|
|
332
|
+
```js
|
|
123
333
|
// Deno example
|
|
124
334
|
Deno.serve(
|
|
125
335
|
{
|
|
@@ -136,52 +346,57 @@ Deno.serve(
|
|
|
136
346
|
},
|
|
137
347
|
// router.respond.bind(router),
|
|
138
348
|
);
|
|
349
|
+
```
|
|
139
350
|
|
|
140
|
-
|
|
141
|
-
http.createServer(async (req, res) => {
|
|
142
|
-
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
143
|
-
|
|
144
|
-
// Build fetch request
|
|
145
|
-
const headers = new Headers();
|
|
146
|
-
Object.entries(req.headers).forEach(
|
|
147
|
-
([k, v]) => v && headers.set(k, v.toString()),
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const request = new Request(url, {
|
|
151
|
-
method: req.method,
|
|
152
|
-
headers,
|
|
153
|
-
body: ["GET", "HEAD"].includes(req.method) ? undefined : req,
|
|
154
|
-
duplex: "half",
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const address = {
|
|
158
|
-
family: req.socket.remoteFamily,
|
|
159
|
-
address: req.socket.remoteAddress,
|
|
160
|
-
port: req.socket.remotePort,
|
|
161
|
-
};
|
|
162
|
-
try {
|
|
163
|
-
const response = await router.respond(request, { address });
|
|
351
|
+
#### Nodejs
|
|
164
352
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
353
|
+
```js
|
|
354
|
+
// Nodejs example. very slow
|
|
355
|
+
http
|
|
356
|
+
.createServer(async (req, res) => {
|
|
357
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
358
|
+
|
|
359
|
+
// Build fetch request
|
|
360
|
+
const headers = new Headers();
|
|
361
|
+
Object.entries(req.headers).forEach(
|
|
362
|
+
([k, v]) => v && headers.set(k, v.toString()),
|
|
169
363
|
);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
364
|
+
|
|
365
|
+
const request = new Request(url, {
|
|
366
|
+
method: req.method,
|
|
367
|
+
headers,
|
|
368
|
+
body: ["GET", "HEAD"].includes(req.method) ? undefined : req,
|
|
369
|
+
duplex: "half",
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const address = {
|
|
373
|
+
family: req.socket.remoteFamily,
|
|
374
|
+
address: req.socket.remoteAddress,
|
|
375
|
+
port: req.socket.remotePort,
|
|
376
|
+
};
|
|
377
|
+
try {
|
|
378
|
+
const response = await router.respond(request, { address });
|
|
379
|
+
|
|
380
|
+
res.writeHead(
|
|
381
|
+
response.status,
|
|
382
|
+
response.statusText,
|
|
383
|
+
Object.fromEntries(response.headers.entries()),
|
|
384
|
+
);
|
|
385
|
+
if (response.body) {
|
|
386
|
+
const reader = response.body.getReader();
|
|
387
|
+
while (true) {
|
|
388
|
+
const { done, value } = await reader.read();
|
|
389
|
+
if (done) break;
|
|
390
|
+
res.write(value);
|
|
391
|
+
}
|
|
176
392
|
}
|
|
393
|
+
res.end();
|
|
394
|
+
} catch {
|
|
395
|
+
res.writeHead(500).end();
|
|
177
396
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
res.writeHead(500).end();
|
|
181
|
-
}
|
|
182
|
-
}).on("connection", (socket) => socket.setNoDelay(true))
|
|
397
|
+
})
|
|
398
|
+
.on("connection", (socket) => socket.setNoDelay(true))
|
|
183
399
|
.listen(3000, () => console.log("Server running on port 3000"));
|
|
184
|
-
|
|
185
400
|
```
|
|
186
401
|
|
|
187
402
|
## 📚 Core Concepts
|
|
@@ -327,6 +542,8 @@ router.respond(
|
|
|
327
542
|
```ts
|
|
328
543
|
import {
|
|
329
544
|
status, // HTTP status response
|
|
545
|
+
redirect, // Redirect response with location header set
|
|
546
|
+
forward, // forward to other path within the router.
|
|
330
547
|
text, // Plain text response
|
|
331
548
|
html, // HTML response
|
|
332
549
|
json, // JSON response
|
|
@@ -498,129 +715,40 @@ Routes are matched in this order of priority:
|
|
|
498
715
|
### Router Composition Example
|
|
499
716
|
|
|
500
717
|
```js
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
return json({ created: true, data: body }, { status: 201 });
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
// Nested router
|
|
511
|
-
const v1Router = new Router();
|
|
512
|
-
v1Router.handle("GET /status", () => json({ version: "1.0", status: "ok" }));
|
|
513
|
-
apiRouter.append("/v1", v1Router);
|
|
514
|
-
|
|
515
|
-
// Mount API router under /api
|
|
516
|
-
const mainRouter = new Router();
|
|
517
|
-
mainRouter.append("/api", apiRouter);
|
|
518
|
-
|
|
519
|
-
// Add some frontend routes
|
|
520
|
-
mainRouter.handle("GET /", () => html("<h1>Home</h1>"));
|
|
521
|
-
mainRouter.handle("GET /about", () => html("<h1>About</h1>"));
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Complete Example with Middleware
|
|
525
|
-
|
|
526
|
-
```js
|
|
527
|
-
import {
|
|
528
|
-
Router,
|
|
529
|
-
text,
|
|
530
|
-
json,
|
|
531
|
-
cors,
|
|
532
|
-
limitRate,
|
|
533
|
-
type CTXAddress,
|
|
534
|
-
type SocketAddress,
|
|
535
|
-
type RouterContext,
|
|
536
|
-
} from "@bepalo/router";
|
|
537
|
-
|
|
538
|
-
const router = new Router<RouterContext & CTXAddress>({
|
|
539
|
-
defaultHeaders: () => [
|
|
540
|
-
["X-Powered-By", "@bepalo/router"],
|
|
541
|
-
["Date", new Date().toUTCString()]
|
|
542
|
-
],
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
// Global CORS
|
|
546
|
-
router.filter("*", [
|
|
547
|
-
cors({
|
|
548
|
-
origins: ["http://localhost:3000", "https://example.com"],
|
|
549
|
-
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
550
|
-
allowedHeaders: ["Content-Type", "Authorization"],
|
|
551
|
-
credentials: true,
|
|
552
|
-
}),
|
|
553
|
-
]);
|
|
554
|
-
|
|
555
|
-
// Rate limiting for API
|
|
556
|
-
router.filter(
|
|
557
|
-
[
|
|
558
|
-
"GET /api/.**",
|
|
559
|
-
"POST /api/.**",
|
|
560
|
-
"PUT /api/.**",
|
|
561
|
-
"PATCH /api/.**",
|
|
562
|
-
"DELETE /api/.**",
|
|
563
|
-
],
|
|
718
|
+
// User API routes
|
|
719
|
+
const userAPIRouter = new Router();
|
|
720
|
+
userAPIRouter.handle("GET /", () => json({ user: {} }));
|
|
721
|
+
userAPIRouter.handle <
|
|
722
|
+
CTXBody >
|
|
723
|
+
("POST /",
|
|
564
724
|
[
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
725
|
+
parseBody(),
|
|
726
|
+
async (req, { body }) => {
|
|
727
|
+
return json({ success: true, data: body }, { status: 201 });
|
|
728
|
+
},
|
|
729
|
+
]);
|
|
730
|
+
|
|
731
|
+
// Session API routes
|
|
732
|
+
const sessionAPIRouter = new Router();
|
|
733
|
+
sessionAPIRouter.handle("GET /", () => json({ session: {} }));
|
|
734
|
+
sessionAPIRouter.handle("POST /", [
|
|
735
|
+
parseBody(),
|
|
736
|
+
async (req, { body }) => {
|
|
737
|
+
return json({ success: true, data: body }, { status: 201 });
|
|
738
|
+
},
|
|
571
739
|
]);
|
|
572
740
|
|
|
573
|
-
//
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// users API `/api/users` router
|
|
577
|
-
{
|
|
578
|
-
const usersAPI = new Router();
|
|
741
|
+
// API v1 router
|
|
742
|
+
const v1APIRouter = new Router();
|
|
743
|
+
v1APIRouter.handle("GET /status", () => json({ version: "1.0", status: "ok" }));
|
|
579
744
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
usersAPI.handle("GET /", () =>
|
|
586
|
-
json({
|
|
587
|
-
users: [
|
|
588
|
-
{ id: 1, name: "Abebe" },
|
|
589
|
-
{ id: 2, name: "Derartu" },
|
|
590
|
-
],
|
|
591
|
-
}),
|
|
592
|
-
);
|
|
593
|
-
|
|
594
|
-
usersAPI.handle("GET /:id", (req, { params }) =>
|
|
595
|
-
json({ user: { id: params.id, name: "User " + params.id } }),
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
router.append("/api/users", usersAPI);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Custom Error handling
|
|
602
|
-
router.catch("*", (req, ctx) => {
|
|
603
|
-
console.error("Error:", ctx.error);
|
|
604
|
-
return json({ error: "Internal server error" }, { status: 500 });
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
// Custom fallback handler
|
|
608
|
-
router.fallback("*", () => json({ error: "Not found" }, { status: 404 }));
|
|
609
|
-
|
|
610
|
-
// Start server
|
|
611
|
-
Bun.serve({
|
|
612
|
-
port: 3000,
|
|
613
|
-
async fetch(req, server) {
|
|
614
|
-
const address = server.requestIP(req) as SocketAddress | null;
|
|
615
|
-
if(!address) {
|
|
616
|
-
throw new Error("null client address");
|
|
617
|
-
}
|
|
618
|
-
return await router.respond(req, { address });
|
|
619
|
-
},
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
console.log("Server listening on http://localhost:3000");
|
|
745
|
+
// Composition is useful for defining routes in multiple files
|
|
746
|
+
// and appending them in other routes.
|
|
747
|
+
v1APIRouter.append("/user", userAPIRouter);
|
|
748
|
+
v1APIRouter.append("/session", sessionAPIRouter);
|
|
623
749
|
|
|
750
|
+
const mainRouter = new Router();
|
|
751
|
+
mainRouter.append("/api/v1", v1APIRouter);
|
|
624
752
|
```
|
|
625
753
|
|
|
626
754
|
## 🎯 Performance
|
|
@@ -640,7 +768,7 @@ The router uses a radix tree (trie) data structure for route matching, providing
|
|
|
640
768
|
| Feature | @bepalo/router | Express | Hono | Fastify |
|
|
641
769
|
| ------------------------------- | -------------- | ------- | ---- | ------- |
|
|
642
770
|
| Radix Tree Routing | ✅ | ❌ | ✅ | ✅ |
|
|
643
|
-
|
|
|
771
|
+
| Few Dependencies | ✅ | ❌ | ❌ | ❌ |
|
|
644
772
|
| TypeScript Native | ✅ | ❌ | ✅ | ✅ |
|
|
645
773
|
| Extended Handler Phases | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
646
774
|
| Built-in Middleware | ✅ | ❌ | ✅ | ✅ |
|
package/dist/cjs/helpers.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { RouterContext } from "./router";
|
|
1
2
|
import { Handler } from "./types";
|
|
2
3
|
export * from "./upload-stream";
|
|
3
|
-
type SURecord = Record<string, unknown>;
|
|
4
|
-
type SSRecord = Record<string, string>;
|
|
5
4
|
export interface SocketAddress {
|
|
6
5
|
address: string;
|
|
7
6
|
family: string;
|
|
8
7
|
port: number;
|
|
9
8
|
}
|
|
9
|
+
export type CTXError = {
|
|
10
|
+
error: Error;
|
|
11
|
+
};
|
|
10
12
|
export type CTXAddress = {
|
|
11
13
|
address: SocketAddress;
|
|
12
14
|
};
|
|
@@ -24,6 +26,24 @@ export declare function getHttpStatusText(code: number): string;
|
|
|
24
26
|
* status(204, null); // No content response
|
|
25
27
|
*/
|
|
26
28
|
export declare const status: (status: number, content?: string | null, init?: ResponseInit) => Response;
|
|
29
|
+
/**
|
|
30
|
+
* Creates a redirect Response.
|
|
31
|
+
* Defaults to 302 Found unless another status is provided.
|
|
32
|
+
* @param {string} location - The URL to redirect to
|
|
33
|
+
* @param {number} [code=302] - The HTTP status code (301, 302, 303, 307, 308)
|
|
34
|
+
* @param {ResponseInit} [init] - Additional response initialization options
|
|
35
|
+
* @returns {Response} A Response object with Location header
|
|
36
|
+
*/
|
|
37
|
+
export declare const redirect: (location: string, init?: ResponseInit) => Response;
|
|
38
|
+
/**
|
|
39
|
+
* Forwards the request to another handler internally.
|
|
40
|
+
* Does not change the URL or send a redirect to the client.
|
|
41
|
+
* @param {string} path - The new path to forward to
|
|
42
|
+
* @returns {Response} A Response object with the forwarded request's response
|
|
43
|
+
*/
|
|
44
|
+
export declare const forward: <XContext = {}>(path: string, options?: {
|
|
45
|
+
method: string;
|
|
46
|
+
}) => Handler<RouterContext<XContext>>;
|
|
27
47
|
/**
|
|
28
48
|
* Creates a text/plain Response.
|
|
29
49
|
* Defaults to status 200 and text/plain content-type if not specified.
|
|
@@ -177,10 +197,10 @@ export declare const parseCookieFromRequest: <Expected extends Record<string, st
|
|
|
177
197
|
/**
|
|
178
198
|
* Context object containing parsed cookies.
|
|
179
199
|
* @typedef {Object} CTXCookie
|
|
180
|
-
* @property {
|
|
200
|
+
* @property {Record<string, string>} cookie - Parsed cookies from the request
|
|
181
201
|
*/
|
|
182
202
|
export type CTXCookie = {
|
|
183
|
-
cookie:
|
|
203
|
+
cookie: Record<string, string>;
|
|
184
204
|
};
|
|
185
205
|
/**
|
|
186
206
|
* Creates middleware that parses cookies from the request and adds them to the context.
|
|
@@ -193,10 +213,10 @@ export declare const parseCookie: <Context extends CTXCookie>() => Handler<Conte
|
|
|
193
213
|
/**
|
|
194
214
|
* Context object containing parsed request body.
|
|
195
215
|
* @typedef {Object} CTXBody
|
|
196
|
-
* @property {
|
|
216
|
+
* @property {Record<string, unknown>} body - Parsed request body data
|
|
197
217
|
*/
|
|
198
218
|
export type CTXBody = {
|
|
199
|
-
body:
|
|
219
|
+
body: Record<string, unknown>;
|
|
200
220
|
};
|
|
201
221
|
/**
|
|
202
222
|
* Supported media types for request body parsing.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"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"}
|