@bepalo/router 1.1.11 → 1.2.13
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 +335 -120
- 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
|
|
|
@@ -63,6 +99,7 @@ import {
|
|
|
63
99
|
type CTXUpload,
|
|
64
100
|
parseUploadStreaming,
|
|
65
101
|
} from "@bepalo/router";
|
|
102
|
+
// } from "jsr:@bepalo/router"; // for deno
|
|
66
103
|
|
|
67
104
|
// Create a router instance
|
|
68
105
|
const router = new Router();
|
|
@@ -94,9 +131,274 @@ Bun.serve({
|
|
|
94
131
|
fetch: (req) => router.respond(req),
|
|
95
132
|
});
|
|
96
133
|
|
|
134
|
+
// Start server (Deno example)
|
|
135
|
+
Deno.serve(
|
|
136
|
+
{
|
|
137
|
+
port: 3000,
|
|
138
|
+
onListen: () => console.log("Server listening on http://localhost:3000"),
|
|
139
|
+
},
|
|
140
|
+
router.respond.bind(router),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
console.log("Server running at http://localhost:3000");
|
|
144
|
+
```
|
|
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
|
+
|
|
314
|
+
### Serve with client address
|
|
315
|
+
|
|
316
|
+
#### Bun
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
// Bun example
|
|
320
|
+
Bun.serve({
|
|
321
|
+
port: 3000,
|
|
322
|
+
async fetch(req, server) {
|
|
323
|
+
const address = server.requestIP(req) as SocketAddress;
|
|
324
|
+
return await router.respond(req, { address });
|
|
325
|
+
},
|
|
326
|
+
});
|
|
97
327
|
console.log("Server running at http://localhost:3000");
|
|
98
328
|
```
|
|
99
329
|
|
|
330
|
+
#### Deno
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
// Deno example
|
|
334
|
+
Deno.serve(
|
|
335
|
+
{
|
|
336
|
+
port: 3000,
|
|
337
|
+
onListen: () => console.log("Server listening on http://localhost:3000"),
|
|
338
|
+
},
|
|
339
|
+
async (req, { remoteAddr }) => {
|
|
340
|
+
const address = {
|
|
341
|
+
family: remoteAddr.transport,
|
|
342
|
+
address: remoteAddr.hostname,
|
|
343
|
+
port: remoteAddr.port,
|
|
344
|
+
} as SocketAddress;
|
|
345
|
+
return router.respond(req, { address });
|
|
346
|
+
},
|
|
347
|
+
// router.respond.bind(router),
|
|
348
|
+
);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
#### Nodejs
|
|
352
|
+
|
|
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()),
|
|
363
|
+
);
|
|
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
|
+
}
|
|
392
|
+
}
|
|
393
|
+
res.end();
|
|
394
|
+
} catch {
|
|
395
|
+
res.writeHead(500).end();
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
.on("connection", (socket) => socket.setNoDelay(true))
|
|
399
|
+
.listen(3000, () => console.log("Server running on port 3000"));
|
|
400
|
+
```
|
|
401
|
+
|
|
100
402
|
## 📚 Core Concepts
|
|
101
403
|
|
|
102
404
|
### Handler Types & Execution Order
|
|
@@ -240,6 +542,8 @@ router.respond(
|
|
|
240
542
|
```ts
|
|
241
543
|
import {
|
|
242
544
|
status, // HTTP status response
|
|
545
|
+
redirect, // Redirect response with location header set
|
|
546
|
+
forward, // forward to other path within the router.
|
|
243
547
|
text, // Plain text response
|
|
244
548
|
html, // HTML response
|
|
245
549
|
json, // JSON response
|
|
@@ -411,129 +715,40 @@ Routes are matched in this order of priority:
|
|
|
411
715
|
### Router Composition Example
|
|
412
716
|
|
|
413
717
|
```js
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return json({ created: true, data: body }, { status: 201 });
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Nested router
|
|
424
|
-
const v1Router = new Router();
|
|
425
|
-
v1Router.handle("GET /status", () => json({ version: "1.0", status: "ok" }));
|
|
426
|
-
apiRouter.append("/v1", v1Router);
|
|
427
|
-
|
|
428
|
-
// Mount API router under /api
|
|
429
|
-
const mainRouter = new Router();
|
|
430
|
-
mainRouter.append("/api", apiRouter);
|
|
431
|
-
|
|
432
|
-
// Add some frontend routes
|
|
433
|
-
mainRouter.handle("GET /", () => html("<h1>Home</h1>"));
|
|
434
|
-
mainRouter.handle("GET /about", () => html("<h1>About</h1>"));
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Complete Example with Middleware
|
|
438
|
-
|
|
439
|
-
```js
|
|
440
|
-
import {
|
|
441
|
-
Router,
|
|
442
|
-
text,
|
|
443
|
-
json,
|
|
444
|
-
cors,
|
|
445
|
-
limitRate,
|
|
446
|
-
type CTXAddress,
|
|
447
|
-
type SocketAddress,
|
|
448
|
-
type RouterContext,
|
|
449
|
-
} from "@bepalo/router";
|
|
450
|
-
|
|
451
|
-
const router = new Router<RouterContext & CTXAddress>({
|
|
452
|
-
defaultHeaders: () => [
|
|
453
|
-
["X-Powered-By", "@bepalo/router"],
|
|
454
|
-
["Date", new Date().toUTCString()]
|
|
455
|
-
],
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// Global CORS
|
|
459
|
-
router.filter("*", [
|
|
460
|
-
cors({
|
|
461
|
-
origins: ["http://localhost:3000", "https://example.com"],
|
|
462
|
-
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
463
|
-
allowedHeaders: ["Content-Type", "Authorization"],
|
|
464
|
-
credentials: true,
|
|
465
|
-
}),
|
|
466
|
-
]);
|
|
467
|
-
|
|
468
|
-
// Rate limiting for API
|
|
469
|
-
router.filter(
|
|
718
|
+
// User API routes
|
|
719
|
+
const userAPIRouter = new Router();
|
|
720
|
+
userAPIRouter.handle("GET /", () => json({ user: {} }));
|
|
721
|
+
userAPIRouter.handle <
|
|
722
|
+
CTXBody >
|
|
723
|
+
("POST /",
|
|
470
724
|
[
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
})
|
|
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
|
+
},
|
|
484
739
|
]);
|
|
485
740
|
|
|
486
|
-
//
|
|
487
|
-
|
|
741
|
+
// API v1 router
|
|
742
|
+
const v1APIRouter = new Router();
|
|
743
|
+
v1APIRouter.handle("GET /status", () => json({ version: "1.0", status: "ok" }));
|
|
488
744
|
|
|
489
|
-
//
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
usersAPI.handle("POST /", async (req) => {
|
|
494
|
-
const body = await req.json();
|
|
495
|
-
return json({ id: Date.now(), ...body }, { status: 201 });
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
usersAPI.handle("GET /", () =>
|
|
499
|
-
json({
|
|
500
|
-
users: [
|
|
501
|
-
{ id: 1, name: "Abebe" },
|
|
502
|
-
{ id: 2, name: "Derartu" },
|
|
503
|
-
],
|
|
504
|
-
}),
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
usersAPI.handle("GET /:id", (req, { params }) =>
|
|
508
|
-
json({ user: { id: params.id, name: "User " + params.id } }),
|
|
509
|
-
);
|
|
510
|
-
|
|
511
|
-
router.append("/api/users", usersAPI);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// Custom Error handling
|
|
515
|
-
router.catch("*", (req, ctx) => {
|
|
516
|
-
console.error("Error:", ctx.error);
|
|
517
|
-
return json({ error: "Internal server error" }, { status: 500 });
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// Custom fallback handler
|
|
521
|
-
router.fallback("*", () => json({ error: "Not found" }, { status: 404 }));
|
|
522
|
-
|
|
523
|
-
// Start server
|
|
524
|
-
Bun.serve({
|
|
525
|
-
port: 3000,
|
|
526
|
-
async fetch(req, server) {
|
|
527
|
-
const address = server.requestIP(req) as SocketAddress | null;
|
|
528
|
-
if(!address) {
|
|
529
|
-
throw new Error("null client address");
|
|
530
|
-
}
|
|
531
|
-
return await router.respond(req, { address });
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
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);
|
|
536
749
|
|
|
750
|
+
const mainRouter = new Router();
|
|
751
|
+
mainRouter.append("/api/v1", v1APIRouter);
|
|
537
752
|
```
|
|
538
753
|
|
|
539
754
|
## 🎯 Performance
|
|
@@ -553,7 +768,7 @@ The router uses a radix tree (trie) data structure for route matching, providing
|
|
|
553
768
|
| Feature | @bepalo/router | Express | Hono | Fastify |
|
|
554
769
|
| ------------------------------- | -------------- | ------- | ---- | ------- |
|
|
555
770
|
| Radix Tree Routing | ✅ | ❌ | ✅ | ✅ |
|
|
556
|
-
|
|
|
771
|
+
| Few Dependencies | ✅ | ❌ | ❌ | ❌ |
|
|
557
772
|
| TypeScript Native | ✅ | ❌ | ✅ | ✅ |
|
|
558
773
|
| Extended Handler Phases | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
559
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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"}
|