@devbro/neko-router 0.1.16 → 0.1.18
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 +703 -32
- package/dist/CompiledRoute.d.mts +0 -1
- package/dist/CompiledRoute.mjs +2 -4
- package/dist/CompiledRoute.mjs.map +1 -1
- package/dist/Route.mjs +1 -1
- package/dist/Route.mjs.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,47 +1,718 @@
|
|
|
1
1
|
# @devbro/neko-router
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A powerful, flexible routing solution for Node.js and TypeScript applications. Build RESTful APIs and web applications with an intuitive, Express-like API.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @devbro/neko-router
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🎯 **Flexible Routing** - Support for functions and controllers
|
|
14
|
+
- 🛣️ **Path Parameters** - Dynamic route segments with type safety
|
|
15
|
+
- 🔒 **Middleware Support** - Function and class-based middleware
|
|
16
|
+
- 🔄 **Singleton & Per-Request** - Choose middleware lifecycle
|
|
17
|
+
- 📝 **Multiple HTTP Methods** - GET, POST, PUT, DELETE, PATCH, etc.
|
|
18
|
+
- 🎨 **Fluent API** - Chainable, intuitive interface
|
|
19
|
+
- 🛡️ **Type-Safe** - Full TypeScript support
|
|
20
|
+
- ⚡ **High Performance** - Fast route matching and resolution
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { Router, Request, Response } from '@devbro/neko-router';
|
|
26
|
+
|
|
27
|
+
// Create router instance
|
|
28
|
+
const router = new Router();
|
|
29
|
+
|
|
30
|
+
// Add a simple route
|
|
31
|
+
router.addRoute(['GET'], '/api/users', async (req: Request, res: Response) => {
|
|
32
|
+
res.statusCode = 200;
|
|
33
|
+
return { users: ['Alice', 'Bob', 'Charlie'] };
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Resolve and execute
|
|
37
|
+
const req = { url: '/api/users', method: 'GET' } as Request;
|
|
38
|
+
const res = {} as Response;
|
|
39
|
+
|
|
40
|
+
const compiledRoute = router.getCompiledRoute(req, res);
|
|
41
|
+
const result = await compiledRoute.run();
|
|
42
|
+
|
|
43
|
+
console.log(result); // { users: ['Alice', 'Bob', 'Charlie'] }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Core Concepts
|
|
47
|
+
|
|
48
|
+
### Basic Routing
|
|
49
|
+
|
|
50
|
+
Define routes for different HTTP methods:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { Router, Request, Response } from '@devbro/neko-router';
|
|
54
|
+
|
|
55
|
+
const router = new Router();
|
|
56
|
+
|
|
57
|
+
// GET request
|
|
58
|
+
router.addRoute(['GET'], '/api/posts', async (req: Request, res: Response) => {
|
|
59
|
+
res.statusCode = 200;
|
|
60
|
+
return { posts: [] };
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// POST request
|
|
64
|
+
router.addRoute(['POST'], '/api/posts', async (req: Request, res: Response) => {
|
|
65
|
+
res.statusCode = 201;
|
|
66
|
+
return { message: 'Post created' };
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// PUT request
|
|
70
|
+
router.addRoute(['PUT'], '/api/posts/:id', async (req: Request, res: Response) => {
|
|
71
|
+
res.statusCode = 200;
|
|
72
|
+
return { message: `Post ${req.params.id} updated` };
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// DELETE request
|
|
76
|
+
router.addRoute(['DELETE'], '/api/posts/:id', async (req: Request, res: Response) => {
|
|
77
|
+
res.statusCode = 204;
|
|
78
|
+
return null;
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Multiple HTTP Methods
|
|
83
|
+
|
|
84
|
+
Handle multiple methods on the same route:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Support both GET and HEAD
|
|
88
|
+
router.addRoute(['GET', 'HEAD'], '/api/health', async (req: Request, res: Response) => {
|
|
89
|
+
res.statusCode = 200;
|
|
90
|
+
return { status: 'healthy' };
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Common pattern for resources
|
|
94
|
+
router.addRoute(['GET', 'POST'], '/api/comments', async (req: Request, res: Response) => {
|
|
95
|
+
if (req.method === 'GET') {
|
|
96
|
+
return { comments: [] };
|
|
97
|
+
} else {
|
|
98
|
+
return { message: 'Comment created' };
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Path Parameters
|
|
104
|
+
|
|
105
|
+
Capture dynamic segments from the URL:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// Single parameter
|
|
109
|
+
router.addRoute(['GET'], '/api/users/:userId', async (req: Request, res: Response) => {
|
|
110
|
+
const userId = req.params.userId;
|
|
111
|
+
res.statusCode = 200;
|
|
112
|
+
return { userId, name: 'John Doe' };
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Multiple parameters
|
|
116
|
+
router.addRoute(
|
|
117
|
+
['GET'],
|
|
118
|
+
'/api/posts/:postId/comments/:commentId',
|
|
119
|
+
async (req: Request, res: Response) => {
|
|
120
|
+
const { postId, commentId } = req.params;
|
|
121
|
+
res.statusCode = 200;
|
|
122
|
+
return { postId, commentId };
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Optional parameters with query strings
|
|
127
|
+
router.addRoute(['GET'], '/api/search/:category', async (req: Request, res: Response) => {
|
|
128
|
+
const category = req.params.category;
|
|
129
|
+
const query = req.query?.q || '';
|
|
130
|
+
res.statusCode = 200;
|
|
131
|
+
return { category, query };
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Middleware
|
|
136
|
+
|
|
137
|
+
### Function Middleware
|
|
138
|
+
|
|
139
|
+
Use simple functions as middleware:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// Logging middleware
|
|
143
|
+
const loggerMiddleware = async (req: Request, res: Response, next: Function) => {
|
|
144
|
+
console.log(`[${req.method}] ${req.url}`);
|
|
145
|
+
await next();
|
|
146
|
+
console.log(`Response: ${res.statusCode}`);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Authentication middleware
|
|
150
|
+
const authMiddleware = async (req: Request, res: Response, next: Function) => {
|
|
151
|
+
const token = req.headers?.authorization;
|
|
152
|
+
|
|
153
|
+
if (!token) {
|
|
154
|
+
res.statusCode = 401;
|
|
155
|
+
return { error: 'Unauthorized' };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Verify token...
|
|
159
|
+
req.user = { id: 1, name: 'User' };
|
|
160
|
+
await next();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Apply middleware to route
|
|
164
|
+
router
|
|
165
|
+
.addRoute(['GET'], '/api/protected', async (req: Request, res: Response) => {
|
|
166
|
+
return { user: req.user };
|
|
167
|
+
})
|
|
168
|
+
.addMiddleware([loggerMiddleware, authMiddleware]);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Multiple Middleware
|
|
11
172
|
|
|
12
|
-
|
|
173
|
+
Chain multiple middleware functions:
|
|
13
174
|
|
|
14
175
|
```ts
|
|
15
|
-
|
|
16
|
-
|
|
176
|
+
const middleware1 = async (req: Request, res: Response, next: Function) => {
|
|
177
|
+
console.log('Middleware 1 - Before');
|
|
178
|
+
await next();
|
|
179
|
+
console.log('Middleware 1 - After');
|
|
180
|
+
};
|
|
17
181
|
|
|
18
|
-
|
|
19
|
-
|
|
182
|
+
const middleware2 = async (req: Request, res: Response, next: Function) => {
|
|
183
|
+
console.log('Middleware 2 - Before');
|
|
184
|
+
await next();
|
|
185
|
+
console.log('Middleware 2 - After');
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
router
|
|
189
|
+
.addRoute(['POST'], '/api/data', async (req: Request, res: Response) => {
|
|
190
|
+
return { message: 'Data processed' };
|
|
191
|
+
})
|
|
192
|
+
.addMiddleware([middleware1, middleware2]);
|
|
193
|
+
|
|
194
|
+
// Execution order:
|
|
195
|
+
// Middleware 1 - Before
|
|
196
|
+
// Middleware 2 - Before
|
|
197
|
+
// Route handler
|
|
198
|
+
// Middleware 2 - After
|
|
199
|
+
// Middleware 1 - After
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Inline Middleware
|
|
203
|
+
|
|
204
|
+
Add middleware directly in the chain:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
router
|
|
208
|
+
.addRoute(['GET'], '/api/users/:id', async (req: Request, res: Response) => {
|
|
209
|
+
return { userId: req.params.id };
|
|
210
|
+
})
|
|
211
|
+
.addMiddleware(async (req, res, next) => {
|
|
212
|
+
console.log('Accessing user:', req.params.id);
|
|
213
|
+
await next();
|
|
214
|
+
console.log('Request completed with status:', res.statusCode);
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Class-Based Middleware
|
|
219
|
+
|
|
220
|
+
Use classes for more complex middleware:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
class AuthenticationMiddleware {
|
|
224
|
+
async handle(req: Request, res: Response, next: Function) {
|
|
225
|
+
// Authentication logic
|
|
226
|
+
const token = req.headers?.authorization;
|
|
227
|
+
|
|
228
|
+
if (!token) {
|
|
229
|
+
res.statusCode = 401;
|
|
230
|
+
throw new Error('No token provided');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Verify and attach user
|
|
234
|
+
req.user = await this.verifyToken(token);
|
|
235
|
+
await next();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async verifyToken(token: string) {
|
|
239
|
+
// Token verification logic
|
|
240
|
+
return { id: 1, email: 'user@example.com' };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Use the class middleware
|
|
245
|
+
const authMiddleware = new AuthenticationMiddleware();
|
|
246
|
+
|
|
247
|
+
router
|
|
248
|
+
.addRoute(['GET'], '/api/profile', async (req: Request, res: Response) => {
|
|
249
|
+
return { profile: req.user };
|
|
250
|
+
})
|
|
251
|
+
.addMiddleware(authMiddleware.handle.bind(authMiddleware));
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Singleton vs Per-Request Middleware
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
// Singleton middleware - shared instance across requests
|
|
258
|
+
class CacheMiddleware {
|
|
259
|
+
private cache = new Map();
|
|
260
|
+
|
|
261
|
+
async handle(req: Request, res: Response, next: Function) {
|
|
262
|
+
const key = req.url;
|
|
263
|
+
|
|
264
|
+
if (this.cache.has(key)) {
|
|
265
|
+
return this.cache.get(key);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await next();
|
|
269
|
+
this.cache.set(key, res.body);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const cacheMiddleware = new CacheMiddleware(); // Single instance
|
|
274
|
+
|
|
275
|
+
// Per-request middleware - new instance for each request
|
|
276
|
+
router
|
|
277
|
+
.addRoute(['GET'], '/api/data', async (req: Request, res: Response) => {
|
|
278
|
+
return { data: 'expensive computation' };
|
|
279
|
+
})
|
|
280
|
+
.addMiddleware(cacheMiddleware.handle.bind(cacheMiddleware));
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Route Resolution
|
|
284
|
+
|
|
285
|
+
### Resolving Routes
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
const router = new Router();
|
|
289
|
+
|
|
290
|
+
router.addRoute(['GET'], '/api/users', async (req: Request, res: Response) => {
|
|
291
|
+
return { users: [] };
|
|
20
292
|
});
|
|
21
293
|
|
|
294
|
+
// Create request object
|
|
295
|
+
const req = {
|
|
296
|
+
url: '/api/users',
|
|
297
|
+
method: 'GET',
|
|
298
|
+
headers: {},
|
|
299
|
+
query: {},
|
|
300
|
+
} as Request;
|
|
301
|
+
|
|
302
|
+
const res = {} as Response;
|
|
303
|
+
|
|
304
|
+
// Resolve the route
|
|
305
|
+
const resolved = router.resolve(req);
|
|
306
|
+
console.log(resolved); // Route information
|
|
307
|
+
|
|
308
|
+
// Get compiled route and execute
|
|
309
|
+
const compiledRoute = router.getCompiledRoute(req, res);
|
|
310
|
+
const result = await compiledRoute.run();
|
|
311
|
+
|
|
312
|
+
console.log(res.statusCode); // 200
|
|
313
|
+
console.log(result); // { users: [] }
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Handling 404
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
const req = { url: '/api/nonexistent', method: 'GET' } as Request;
|
|
320
|
+
const res = {} as Response;
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
const compiledRoute = router.getCompiledRoute(req, res);
|
|
324
|
+
await compiledRoute.run();
|
|
325
|
+
} catch (error) {
|
|
326
|
+
// Handle route not found
|
|
327
|
+
res.statusCode = 404;
|
|
328
|
+
return { error: 'Route not found' };
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Real-World Examples
|
|
333
|
+
|
|
334
|
+
### RESTful API
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { Router, Request, Response } from '@devbro/neko-router';
|
|
338
|
+
|
|
339
|
+
const router = new Router();
|
|
340
|
+
|
|
341
|
+
// Logging middleware
|
|
342
|
+
const logger = async (req: Request, res: Response, next: Function) => {
|
|
343
|
+
const start = Date.now();
|
|
344
|
+
await next();
|
|
345
|
+
const duration = Date.now() - start;
|
|
346
|
+
console.log(`[${req.method}] ${req.url} - ${res.statusCode} (${duration}ms)`);
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// List all posts
|
|
350
|
+
router
|
|
351
|
+
.addRoute(['GET'], '/api/posts', async (req: Request, res: Response) => {
|
|
352
|
+
const posts = await database.getPosts();
|
|
353
|
+
res.statusCode = 200;
|
|
354
|
+
return { posts };
|
|
355
|
+
})
|
|
356
|
+
.addMiddleware(logger);
|
|
357
|
+
|
|
358
|
+
// Get single post
|
|
359
|
+
router
|
|
360
|
+
.addRoute(['GET'], '/api/posts/:id', async (req: Request, res: Response) => {
|
|
361
|
+
const post = await database.getPost(req.params.id);
|
|
362
|
+
res.statusCode = 200;
|
|
363
|
+
return { post };
|
|
364
|
+
})
|
|
365
|
+
.addMiddleware(logger);
|
|
366
|
+
|
|
367
|
+
// Create post
|
|
22
368
|
router
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
369
|
+
.addRoute(['POST'], '/api/posts', async (req: Request, res: Response) => {
|
|
370
|
+
const newPost = await database.createPost(req.body);
|
|
371
|
+
res.statusCode = 201;
|
|
372
|
+
return { post: newPost };
|
|
373
|
+
})
|
|
374
|
+
.addMiddleware([logger, authMiddleware]);
|
|
27
375
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
376
|
+
// Update post
|
|
377
|
+
router
|
|
378
|
+
.addRoute(['PUT'], '/api/posts/:id', async (req: Request, res: Response) => {
|
|
379
|
+
const updated = await database.updatePost(req.params.id, req.body);
|
|
380
|
+
res.statusCode = 200;
|
|
381
|
+
return { post: updated };
|
|
382
|
+
})
|
|
383
|
+
.addMiddleware([logger, authMiddleware]);
|
|
384
|
+
|
|
385
|
+
// Delete post
|
|
386
|
+
router
|
|
387
|
+
.addRoute(['DELETE'], '/api/posts/:id', async (req: Request, res: Response) => {
|
|
388
|
+
await database.deletePost(req.params.id);
|
|
389
|
+
res.statusCode = 204;
|
|
390
|
+
return null;
|
|
391
|
+
})
|
|
392
|
+
.addMiddleware([logger, authMiddleware]);
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Error Handling Middleware
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
const errorHandler = async (req: Request, res: Response, next: Function) => {
|
|
399
|
+
try {
|
|
400
|
+
await next();
|
|
401
|
+
} catch (error) {
|
|
402
|
+
console.error('Error:', error);
|
|
403
|
+
res.statusCode = 500;
|
|
404
|
+
return {
|
|
405
|
+
error: 'Internal Server Error',
|
|
406
|
+
message: error.message,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
router
|
|
412
|
+
.addRoute(['GET'], '/api/risky', async (req: Request, res: Response) => {
|
|
413
|
+
throw new Error('Something went wrong!');
|
|
414
|
+
})
|
|
415
|
+
.addMiddleware(errorHandler);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Request Validation
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
const validateCreateUser = async (req: Request, res: Response, next: Function) => {
|
|
422
|
+
const { email, name } = req.body;
|
|
423
|
+
|
|
424
|
+
if (!email || !name) {
|
|
425
|
+
res.statusCode = 400;
|
|
426
|
+
return { error: 'Email and name are required' };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!email.includes('@')) {
|
|
430
|
+
res.statusCode = 400;
|
|
431
|
+
return { error: 'Invalid email format' };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
await next();
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
router
|
|
438
|
+
.addRoute(['POST'], '/api/users', async (req: Request, res: Response) => {
|
|
439
|
+
const user = await createUser(req.body);
|
|
440
|
+
res.statusCode = 201;
|
|
441
|
+
return { user };
|
|
442
|
+
})
|
|
443
|
+
.addMiddleware(validateCreateUser);
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### CORS Middleware
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
const corsMiddleware = async (req: Request, res: Response, next: Function) => {
|
|
450
|
+
res.headers = {
|
|
451
|
+
...res.headers,
|
|
452
|
+
'Access-Control-Allow-Origin': '*',
|
|
453
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
454
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
if (req.method === 'OPTIONS') {
|
|
458
|
+
res.statusCode = 204;
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
await next();
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// Apply to all routes
|
|
466
|
+
router
|
|
467
|
+
.addRoute(
|
|
468
|
+
['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
469
|
+
'/api/*',
|
|
470
|
+
async (req: Request, res: Response) => {
|
|
471
|
+
// Route handler
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
.addMiddleware(corsMiddleware);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Rate Limiting
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
class RateLimiter {
|
|
481
|
+
private requests = new Map<string, number[]>();
|
|
482
|
+
private limit = 100;
|
|
483
|
+
private window = 60000; // 1 minute
|
|
484
|
+
|
|
485
|
+
async handle(req: Request, res: Response, next: Function) {
|
|
486
|
+
const ip = req.headers['x-forwarded-for'] || req.connection?.remoteAddress;
|
|
487
|
+
const now = Date.now();
|
|
488
|
+
|
|
489
|
+
if (!this.requests.has(ip)) {
|
|
490
|
+
this.requests.set(ip, []);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const userRequests = this.requests.get(ip)!;
|
|
494
|
+
const recentRequests = userRequests.filter((time) => now - time < this.window);
|
|
495
|
+
|
|
496
|
+
if (recentRequests.length >= this.limit) {
|
|
497
|
+
res.statusCode = 429;
|
|
498
|
+
return { error: 'Too many requests' };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
recentRequests.push(now);
|
|
502
|
+
this.requests.set(ip, recentRequests);
|
|
503
|
+
|
|
504
|
+
await next();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const rateLimiter = new RateLimiter();
|
|
509
|
+
|
|
510
|
+
router
|
|
511
|
+
.addRoute(['GET'], '/api/data', async (req: Request, res: Response) => {
|
|
512
|
+
return { data: 'sensitive information' };
|
|
513
|
+
})
|
|
514
|
+
.addMiddleware(rateLimiter.handle.bind(rateLimiter));
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
## Integration with HTTP Servers
|
|
518
|
+
|
|
519
|
+
### With Node.js HTTP
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import http from 'http';
|
|
523
|
+
import { Router, Request, Response } from '@devbro/neko-router';
|
|
524
|
+
|
|
525
|
+
const router = new Router();
|
|
526
|
+
|
|
527
|
+
// Define routes
|
|
528
|
+
router.addRoute(['GET'], '/api/hello', async (req: Request, res: Response) => {
|
|
529
|
+
res.statusCode = 200;
|
|
530
|
+
return { message: 'Hello World' };
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// Create HTTP server
|
|
534
|
+
const server = http.createServer(async (req, res) => {
|
|
535
|
+
try {
|
|
536
|
+
const request = req as Request;
|
|
537
|
+
const response = res as Response;
|
|
538
|
+
|
|
539
|
+
const compiledRoute = router.getCompiledRoute(request, response);
|
|
540
|
+
const result = await compiledRoute.run();
|
|
541
|
+
|
|
542
|
+
res.writeHead(response.statusCode || 200, { 'Content-Type': 'application/json' });
|
|
543
|
+
res.end(JSON.stringify(result));
|
|
544
|
+
} catch (error) {
|
|
545
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
546
|
+
res.end(JSON.stringify({ error: 'Not Found' }));
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
server.listen(3000, () => {
|
|
551
|
+
console.log('Server running on port 3000');
|
|
552
|
+
});
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### With Express
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
import express from 'express';
|
|
559
|
+
import { Router as NekoRouter, Request, Response } from '@devbro/neko-router';
|
|
39
560
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
let resolved = router.resolve(req);
|
|
561
|
+
const app = express();
|
|
562
|
+
const router = new NekoRouter();
|
|
43
563
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
564
|
+
// Define routes in neko-router
|
|
565
|
+
router.addRoute(['GET'], '/api/users', async (req: Request, res: Response) => {
|
|
566
|
+
res.statusCode = 200;
|
|
567
|
+
return { users: [] };
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
// Use as Express middleware
|
|
571
|
+
app.use(async (req, res, next) => {
|
|
572
|
+
try {
|
|
573
|
+
const request = req as Request;
|
|
574
|
+
const response = res as Response;
|
|
575
|
+
|
|
576
|
+
const compiledRoute = router.getCompiledRoute(request, response);
|
|
577
|
+
const result = await compiledRoute.run();
|
|
578
|
+
|
|
579
|
+
res.status(response.statusCode || 200).json(result);
|
|
580
|
+
} catch (error) {
|
|
581
|
+
next();
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
app.listen(3000);
|
|
47
586
|
```
|
|
587
|
+
|
|
588
|
+
## Best Practices
|
|
589
|
+
|
|
590
|
+
1. **Organize Routes** - Group related routes together
|
|
591
|
+
2. **Use Middleware** - Extract common logic into reusable middleware
|
|
592
|
+
3. **Error Handling** - Always wrap route handlers in error handling middleware
|
|
593
|
+
4. **Validation** - Validate input data before processing
|
|
594
|
+
5. **Type Safety** - Use TypeScript interfaces for request/response types
|
|
595
|
+
6. **Status Codes** - Set appropriate HTTP status codes
|
|
596
|
+
7. **Async/Await** - Use async/await for cleaner asynchronous code
|
|
597
|
+
8. **Naming** - Use descriptive names for routes and middleware
|
|
598
|
+
|
|
599
|
+
## TypeScript Support
|
|
600
|
+
|
|
601
|
+
Full TypeScript definitions included:
|
|
602
|
+
|
|
603
|
+
```ts
|
|
604
|
+
import { Router, Request, Response, Middleware } from '@devbro/neko-router';
|
|
605
|
+
|
|
606
|
+
// Extend Request type
|
|
607
|
+
interface CustomRequest extends Request {
|
|
608
|
+
user?: {
|
|
609
|
+
id: number;
|
|
610
|
+
email: string;
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Type-safe route handler
|
|
615
|
+
router.addRoute(['GET'], '/api/profile', async (req: CustomRequest, res: Response) => {
|
|
616
|
+
if (!req.user) {
|
|
617
|
+
res.statusCode = 401;
|
|
618
|
+
return { error: 'Unauthorized' };
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return { profile: req.user };
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// Type-safe middleware
|
|
625
|
+
const authMiddleware: Middleware = async (req: CustomRequest, res: Response, next: Function) => {
|
|
626
|
+
// Authentication logic
|
|
627
|
+
await next();
|
|
628
|
+
};
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
## API Reference
|
|
632
|
+
|
|
633
|
+
### `Router`
|
|
634
|
+
|
|
635
|
+
#### Methods
|
|
636
|
+
|
|
637
|
+
##### `addRoute(methods: string[], path: string, handler: Function): Route`
|
|
638
|
+
|
|
639
|
+
Add a new route to the router.
|
|
640
|
+
|
|
641
|
+
```ts
|
|
642
|
+
router.addRoute(['GET', 'POST'], '/api/users', handler);
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
##### `resolve(req: Request): RouteInfo | null`
|
|
646
|
+
|
|
647
|
+
Resolve a request to route information.
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
const routeInfo = router.resolve(req);
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
##### `getCompiledRoute(req: Request, res: Response): CompiledRoute`
|
|
654
|
+
|
|
655
|
+
Get a compiled route ready for execution.
|
|
656
|
+
|
|
657
|
+
```ts
|
|
658
|
+
const compiled = router.getCompiledRoute(req, res);
|
|
659
|
+
await compiled.run();
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### `Route`
|
|
663
|
+
|
|
664
|
+
#### Methods
|
|
665
|
+
|
|
666
|
+
##### `addMiddleware(middleware: Function | Function[]): Route`
|
|
667
|
+
|
|
668
|
+
Add middleware to the route.
|
|
669
|
+
|
|
670
|
+
```ts
|
|
671
|
+
route.addMiddleware([middleware1, middleware2]);
|
|
672
|
+
route.addMiddleware(singleMiddleware);
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Types
|
|
676
|
+
|
|
677
|
+
```ts
|
|
678
|
+
interface Request {
|
|
679
|
+
url: string;
|
|
680
|
+
method: string;
|
|
681
|
+
headers?: Record<string, string>;
|
|
682
|
+
params?: Record<string, string>;
|
|
683
|
+
query?: Record<string, any>;
|
|
684
|
+
body?: any;
|
|
685
|
+
[key: string]: any;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
interface Response {
|
|
689
|
+
statusCode?: number;
|
|
690
|
+
headers?: Record<string, string>;
|
|
691
|
+
body?: any;
|
|
692
|
+
[key: string]: any;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
type Middleware = (req: Request, res: Response, next: Function) => Promise<any>;
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
## Performance Tips
|
|
699
|
+
|
|
700
|
+
1. **Route Order** - Place specific routes before generic ones
|
|
701
|
+
2. **Middleware Order** - Put fast middleware before slow ones
|
|
702
|
+
3. **Caching** - Cache route resolution results when possible
|
|
703
|
+
4. **Async Operations** - Use async/await efficiently
|
|
704
|
+
5. **Memory Management** - Clean up middleware state appropriately
|
|
705
|
+
|
|
706
|
+
## Contributing
|
|
707
|
+
|
|
708
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
709
|
+
|
|
710
|
+
## License
|
|
711
|
+
|
|
712
|
+
MIT
|
|
713
|
+
|
|
714
|
+
## Related Packages
|
|
715
|
+
|
|
716
|
+
- [@devbro/neko-http](https://www.npmjs.com/package/@devbro/neko-http) - HTTP client utilities
|
|
717
|
+
- [@devbro/neko-context](https://www.npmjs.com/package/@devbro/neko-context) - Context management
|
|
718
|
+
- [@devbro/pashmak](https://www.npmjs.com/package/@devbro/pashmak) - Full-stack TypeScript framework
|
package/dist/CompiledRoute.d.mts
CHANGED
|
@@ -11,7 +11,6 @@ declare class CompiledRoute {
|
|
|
11
11
|
private middlewares;
|
|
12
12
|
getMiddlewares(): Middleware[];
|
|
13
13
|
private prepareMiddlewares;
|
|
14
|
-
isClass(func: any): boolean;
|
|
15
14
|
run(): Promise<void>;
|
|
16
15
|
prepareOutputJsonFormat<T>(obj: object | Array<any>): T;
|
|
17
16
|
convertToString(obj: any): string;
|
package/dist/CompiledRoute.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Stream } from "stream";
|
|
2
2
|
import { Middleware } from "./Middleware.mjs";
|
|
3
3
|
import { MiddlewareFactory } from "./MiddlewareFactory.mjs";
|
|
4
|
+
import { isClass } from "@devbro/neko-helper";
|
|
4
5
|
class CompiledRoute {
|
|
5
6
|
constructor(route, request, response, globalMiddlewares = []) {
|
|
6
7
|
this.route = route;
|
|
@@ -18,7 +19,7 @@ class CompiledRoute {
|
|
|
18
19
|
for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {
|
|
19
20
|
if (middleware instanceof Middleware) {
|
|
20
21
|
this.middlewares.push(middleware);
|
|
21
|
-
} else if (
|
|
22
|
+
} else if (isClass(middleware)) {
|
|
22
23
|
this.middlewares.push(middleware.getInstance({}));
|
|
23
24
|
} else if (typeof middleware === "function") {
|
|
24
25
|
this.middlewares.push(MiddlewareFactory.create(middleware));
|
|
@@ -27,9 +28,6 @@ class CompiledRoute {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
isClass(func) {
|
|
31
|
-
return typeof func === "function" && /^class\s/.test(Function.prototype.toString.call(func));
|
|
32
|
-
}
|
|
33
31
|
async run() {
|
|
34
32
|
return await this.runMiddlewares(this.middlewares, this.request, this.response);
|
|
35
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/CompiledRoute.mts"],"sourcesContent":["import { Stream } from 'stream';\nimport { Middleware } from './Middleware.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\n\nexport class CompiledRoute {\n constructor(\n public route: Route,\n public request: Request,\n public response: Response,\n public globalMiddlewares: MiddlewareProvider[] = []\n ) {\n this.prepareMiddlewares();\n }\n\n private middlewares: Middleware[] = [];\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n private prepareMiddlewares() {\n this.middlewares = [];\n for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {\n if (middleware instanceof Middleware) {\n this.middlewares.push(middleware);\n } else if (
|
|
1
|
+
{"version":3,"sources":["../src/CompiledRoute.mts"],"sourcesContent":["import { Stream } from 'stream';\nimport { Middleware } from './Middleware.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\nimport { isClass } from '@devbro/neko-helper';\n\nexport class CompiledRoute {\n constructor(\n public route: Route,\n public request: Request,\n public response: Response,\n public globalMiddlewares: MiddlewareProvider[] = []\n ) {\n this.prepareMiddlewares();\n }\n\n private middlewares: Middleware[] = [];\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n private prepareMiddlewares() {\n this.middlewares = [];\n for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {\n if (middleware instanceof Middleware) {\n this.middlewares.push(middleware);\n } else if (isClass(middleware)) {\n this.middlewares.push((middleware as any).getInstance({}));\n } else if (typeof middleware === 'function') {\n this.middlewares.push(MiddlewareFactory.create(middleware as HandlerType));\n } else {\n throw new Error('Invalid middleware type');\n }\n }\n }\n\n async run() {\n return await this.runMiddlewares(this.middlewares, this.request, this.response);\n }\n\n prepareOutputJsonFormat<T>(obj: object | Array<any>): T {\n function traverse(value: any): any {\n if (value === undefined || value === null) {\n return null;\n }\n\n if (!value || typeof value !== 'object') {\n return value;\n }\n\n if (typeof value.toJson === 'function') {\n return traverse(value.toJson());\n }\n\n // to handle Date and other built-in types\n if (typeof value.toJSON === 'function') {\n return traverse(value.toJSON());\n }\n\n if (Array.isArray(value)) {\n return value.map(traverse);\n }\n\n const result: Record<string, any> = {};\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n result[key] = traverse(value[key]);\n }\n }\n return result;\n }\n\n return traverse(obj);\n }\n\n convertToString(obj: any) {\n if (typeof obj === 'string') {\n return obj;\n } else if (obj instanceof Buffer) {\n return obj.toString();\n } else if (typeof obj === 'object') {\n return JSON.stringify(this.prepareOutputJsonFormat(obj));\n }\n return String(obj);\n }\n\n async processResponseBody(res: Response, controller_rc: any) {\n if (controller_rc && res.writableEnded) {\n throw new Error('cannot write to response, response has already ended');\n }\n\n if (res.writableEnded) {\n return;\n }\n\n if (controller_rc) {\n const header_content_type = res.getHeader('Content-Type');\n if (controller_rc instanceof Stream || Buffer.isBuffer(controller_rc)) {\n await this.writeAsync(res, controller_rc);\n } else if (!header_content_type && typeof controller_rc === 'object') {\n res.setHeader('Content-Type', 'application/json');\n res.write(this.convertToString(controller_rc));\n } else if (!header_content_type) {\n res.setHeader('Content-Type', 'text/plain');\n res.write(this.convertToString(controller_rc));\n } else {\n res.write(this.convertToString(controller_rc));\n }\n return;\n } else {\n res.statusCode = [200].includes(res.statusCode) ? 204 : res.statusCode;\n }\n }\n\n async writeAsync(res: any, chunk: any) {\n return new Promise((resolve, reject) => {\n const ok = res.write(chunk, (err: any) => {\n if (err) reject(err);\n });\n if (ok) {\n // write went through immediately\n resolve(0);\n } else {\n // wait for 'drain' when buffer is flushed\n res.once('drain', resolve);\n }\n });\n }\n\n async runMiddlewares(middlewares: Middleware[], req: Request, res: Response) {\n let index = 0;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const me = this;\n\n async function next() {\n if (index >= middlewares.length) {\n const controller_rc = await me.route.callHanlder(req, res);\n await me.processResponseBody(res, controller_rc);\n return;\n }\n\n const middleware: Middleware | any = middlewares[index++];\n\n if (middleware instanceof Middleware) {\n await middleware.call(req, res, next);\n } else if (typeof middleware === 'function') {\n await middleware(req, res, next);\n } else {\n throw new Error('does not know how to run middleware');\n }\n }\n\n await next();\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/Route.mjs
CHANGED
|
@@ -28,7 +28,7 @@ class Route {
|
|
|
28
28
|
i = start;
|
|
29
29
|
} else if (char === "*") {
|
|
30
30
|
let start = i + 1;
|
|
31
|
-
while (start < path.length && /[a-zA-Z0-9_
|
|
31
|
+
while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {
|
|
32
32
|
start++;
|
|
33
33
|
}
|
|
34
34
|
tokens.push({ type: "WILDCARD", value: path.slice(i + 1, start) });
|
package/dist/Route.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Route.mts"],"sourcesContent":["import { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { LexerToken, Request, Response } from './types.mjs';\n\nexport class Route {\n private middlewares: MiddlewareProvider[] = [];\n private urlRegex: RegExp;\n constructor(\n public methods: HttpMethod[],\n public path: string,\n public handler: HandlerType\n ) {\n this.urlRegex = this.pathToRegex(path);\n }\n pathToRegex(path: string): RegExp {\n const lex = this.lexUrlPath(path);\n return this.tokensToRegex(lex);\n }\n\n lexUrlPath(path: string) {\n const tokens = [];\n let i = 0;\n\n while (i < path.length) {\n const char = path[i];\n\n if (char === '/') {\n tokens.push({ type: 'SLASH', value: '/' });\n i++;\n } else if (char === ':') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'PARAM', value: path.slice(i + 1, start) });\n i = start;\n } else if (char === '*') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_
|
|
1
|
+
{"version":3,"sources":["../src/Route.mts"],"sourcesContent":["import { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { LexerToken, Request, Response } from './types.mjs';\n\nexport class Route {\n private middlewares: MiddlewareProvider[] = [];\n private urlRegex: RegExp;\n constructor(\n public methods: HttpMethod[],\n public path: string,\n public handler: HandlerType\n ) {\n this.urlRegex = this.pathToRegex(path);\n }\n pathToRegex(path: string): RegExp {\n const lex = this.lexUrlPath(path);\n return this.tokensToRegex(lex);\n }\n\n lexUrlPath(path: string) {\n const tokens = [];\n let i = 0;\n\n while (i < path.length) {\n const char = path[i];\n\n if (char === '/') {\n tokens.push({ type: 'SLASH', value: '/' });\n i++;\n } else if (char === ':') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'PARAM', value: path.slice(i + 1, start) });\n i = start;\n } else if (char === '*') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'WILDCARD', value: path.slice(i + 1, start) });\n i = start;\n } else {\n let start = i;\n while (start < path.length && !['/', ':', '*'].includes(path[start])) {\n start++;\n }\n tokens.push({ type: 'TEXT', value: path.slice(i, start) });\n i = start;\n }\n }\n\n return tokens;\n }\n tokensToRegex(tokens: LexerToken[]) {\n const regexParts = [];\n\n for (const token of tokens) {\n if (token.type === 'SLASH') {\n regexParts.push('\\\\/');\n } else if (token.type === 'PARAM') {\n regexParts.push(`(?<${token.value}>[^\\\\/]+)`);\n } else if (token.type === 'WILDCARD') {\n regexParts.push('(.+)');\n } else if (token.type === 'TEXT') {\n regexParts.push(token.value.replace(/[-\\/\\\\^$.*+?()[\\]{}|]/g, '\\\\$&'));\n }\n }\n\n if (regexParts.length > 0 && regexParts[regexParts.length - 1] === '\\\\/') {\n regexParts[regexParts.length - 1] = '\\\\/?';\n } else {\n regexParts.push('\\\\/?');\n }\n\n return new RegExp(`^${regexParts.join('')}$`);\n }\n\n /**\n * to evaludate if request is a match for this route\n * @param request http request\n * @returns return true if route is a match for this request\n */\n test(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n const url = new URL(request.url || '/', 'http://localhost');\n return this.testPath(url.pathname);\n }\n\n testPath(pathname: string) {\n return this.urlRegex.test(pathname);\n }\n\n /**\n * returns details of the match, otherwise it returns false\n * @param request the request to match\n * @returns object cotaining details of match including matched params\n */\n match(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n\n const url = new URL(request.url || '/', 'http://localhost');\n\n const r = this.urlRegex.exec(url.pathname);\n if (!r) {\n return false;\n }\n\n return {\n url,\n params: r.groups || {},\n };\n }\n\n prependMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = ([] as MiddlewareProvider[]).concat(middlewares, this.middlewares);\n return this;\n }\n\n addMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = this.middlewares.concat(middlewares);\n return this;\n }\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n callHanlder(request: Request, response: Response) {\n return this.handler(request, response);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -76,6 +76,7 @@ var MiddlewareFactory = class {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
// src/CompiledRoute.mts
|
|
79
|
+
var import_neko_helper = require("@devbro/neko-helper");
|
|
79
80
|
var CompiledRoute = class {
|
|
80
81
|
constructor(route, request, response, globalMiddlewares = []) {
|
|
81
82
|
this.route = route;
|
|
@@ -93,7 +94,7 @@ var CompiledRoute = class {
|
|
|
93
94
|
for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {
|
|
94
95
|
if (middleware instanceof Middleware) {
|
|
95
96
|
this.middlewares.push(middleware);
|
|
96
|
-
} else if (
|
|
97
|
+
} else if ((0, import_neko_helper.isClass)(middleware)) {
|
|
97
98
|
this.middlewares.push(middleware.getInstance({}));
|
|
98
99
|
} else if (typeof middleware === "function") {
|
|
99
100
|
this.middlewares.push(MiddlewareFactory.create(middleware));
|
|
@@ -102,9 +103,6 @@ var CompiledRoute = class {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
|
-
isClass(func) {
|
|
106
|
-
return typeof func === "function" && /^class\s/.test(Function.prototype.toString.call(func));
|
|
107
|
-
}
|
|
108
106
|
async run() {
|
|
109
107
|
return await this.runMiddlewares(this.middlewares, this.request, this.response);
|
|
110
108
|
}
|
|
@@ -326,7 +324,7 @@ var Route = class {
|
|
|
326
324
|
i = start;
|
|
327
325
|
} else if (char === "*") {
|
|
328
326
|
let start = i + 1;
|
|
329
|
-
while (start < path2.length && /[a-zA-Z0-9_
|
|
327
|
+
while (start < path2.length && /[a-zA-Z0-9_]/.test(path2[start])) {
|
|
330
328
|
start++;
|
|
331
329
|
}
|
|
332
330
|
tokens.push({ type: "WILDCARD", value: path2.slice(i + 1, start) });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/CompiledRoute.mts","../src/Middleware.mts","../src/MiddlewareFactory.mts","../src/Controller.mts","../src/Route.mts","../src/Router.mts","../src/helpers.mts"],"sourcesContent":["export * from './types.mjs';\nexport * from './CompiledRoute.mjs';\nexport * from './Controller.mjs';\nexport * from './Route.mjs';\nexport * from './Router.mjs';\nexport * from './Middleware.mjs';\nexport * from './MiddlewareFactory.mjs';\nexport * from './helpers.mjs';\n","import { Stream } from 'stream';\nimport { Middleware } from './Middleware.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\n\nexport class CompiledRoute {\n constructor(\n public route: Route,\n public request: Request,\n public response: Response,\n public globalMiddlewares: MiddlewareProvider[] = []\n ) {\n this.prepareMiddlewares();\n }\n\n private middlewares: Middleware[] = [];\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n private prepareMiddlewares() {\n this.middlewares = [];\n for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {\n if (middleware instanceof Middleware) {\n this.middlewares.push(middleware);\n } else if (this.isClass(middleware)) {\n this.middlewares.push((middleware as any).getInstance({}));\n } else if (typeof middleware === 'function') {\n this.middlewares.push(MiddlewareFactory.create(middleware as HandlerType));\n } else {\n throw new Error('Invalid middleware type');\n }\n }\n }\n\n isClass(func: any) {\n return typeof func === 'function' && /^class\\s/.test(Function.prototype.toString.call(func));\n }\n\n async run() {\n return await this.runMiddlewares(this.middlewares, this.request, this.response);\n }\n\n prepareOutputJsonFormat<T>(obj: object | Array<any>): T {\n function traverse(value: any): any {\n if (value === undefined || value === null) {\n return null;\n }\n\n if (!value || typeof value !== 'object') {\n return value;\n }\n\n if (typeof value.toJson === 'function') {\n return traverse(value.toJson());\n }\n\n // to handle Date and other built-in types\n if (typeof value.toJSON === 'function') {\n return traverse(value.toJSON());\n }\n\n if (Array.isArray(value)) {\n return value.map(traverse);\n }\n\n const result: Record<string, any> = {};\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n result[key] = traverse(value[key]);\n }\n }\n return result;\n }\n\n return traverse(obj);\n }\n\n convertToString(obj: any) {\n if (typeof obj === 'string') {\n return obj;\n } else if (obj instanceof Buffer) {\n return obj.toString();\n } else if (typeof obj === 'object') {\n return JSON.stringify(this.prepareOutputJsonFormat(obj));\n }\n return String(obj);\n }\n\n async processResponseBody(res: Response, controller_rc: any) {\n if (controller_rc && res.writableEnded) {\n throw new Error('cannot write to response, response has already ended');\n }\n\n if (res.writableEnded) {\n return;\n }\n\n if (controller_rc) {\n const header_content_type = res.getHeader('Content-Type');\n if (controller_rc instanceof Stream || Buffer.isBuffer(controller_rc)) {\n await this.writeAsync(res, controller_rc);\n } else if (!header_content_type && typeof controller_rc === 'object') {\n res.setHeader('Content-Type', 'application/json');\n res.write(this.convertToString(controller_rc));\n } else if (!header_content_type) {\n res.setHeader('Content-Type', 'text/plain');\n res.write(this.convertToString(controller_rc));\n } else {\n res.write(this.convertToString(controller_rc));\n }\n return;\n } else {\n res.statusCode = [200].includes(res.statusCode) ? 204 : res.statusCode;\n }\n }\n\n async writeAsync(res: any, chunk: any) {\n return new Promise((resolve, reject) => {\n const ok = res.write(chunk, (err: any) => {\n if (err) reject(err);\n });\n if (ok) {\n // write went through immediately\n resolve(0);\n } else {\n // wait for 'drain' when buffer is flushed\n res.once('drain', resolve);\n }\n });\n }\n\n async runMiddlewares(middlewares: Middleware[], req: Request, res: Response) {\n let index = 0;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const me = this;\n\n async function next() {\n if (index >= middlewares.length) {\n const controller_rc = await me.route.callHanlder(req, res);\n await me.processResponseBody(res, controller_rc);\n return;\n }\n\n const middleware: Middleware | any = middlewares[index++];\n\n if (middleware instanceof Middleware) {\n await middleware.call(req, res, next);\n } else if (typeof middleware === 'function') {\n await middleware(req, res, next);\n } else {\n throw new Error('does not know how to run middleware');\n }\n }\n\n await next();\n }\n}\n","import { Request, Response } from './types.mjs';\n\nexport abstract class Middleware {\n protected constructor(params: any = {}) {}\n static getInstance(params: any): Middleware {\n throw new Error('Method not implemented. Please implement a static getInstance method.');\n }\n\n abstract call(req: Request, res: Response, next: () => Promise<void>): Promise<void>;\n}\n","import { Middleware } from './Middleware.mjs';\nimport { HandlerType, Request, Response } from './types.mjs';\n\nexport class MiddlewareFactory {\n public static create(func: HandlerType): Middleware {\n const cls = class extends Middleware {\n call(req: Request, res: Response, next: () => Promise<void>): Promise<void> {\n return func(req, res, next);\n }\n constructor(params: any = {}) {\n super(params);\n }\n };\n\n return new cls();\n }\n}\n","import { ControllerDecoratorOptions, HttpMethod, MiddlewareProvider } from './types.mjs';\n\nexport class BaseController {\n declare static routes: {\n methods: HttpMethod[];\n path: string;\n handler: string;\n middlewares: MiddlewareProvider[];\n }[];\n static basePath: string = '';\n static baseMiddlewares: MiddlewareProvider[];\n\n static getInstance() {\n return new this();\n }\n}\n\nexport function Controller(path: string, options: ControllerDecoratorOptions = {}): ClassDecorator {\n return function (target: any) {\n (target as any).routes = (target as any).routes || [];\n (target as any).basePath = path;\n (target as any).baseMiddlewares = options.middlewares || [];\n };\n}\n\nfunction createHttpDecorator(data: {\n methods: HttpMethod[];\n path: string;\n middlewares: MiddlewareProvider[];\n}): MethodDecorator {\n return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {\n const ctor = target.constructor;\n if (!ctor.routes) ctor.routes = [];\n ctor.routes.push({\n methods: data.methods,\n path: data.path,\n handler: propertyKey,\n middlewares: data.middlewares || [],\n });\n\n const originalMethod = descriptor.value!;\n const paramKeys = Reflect.ownKeys(target.constructor);\n const methodName = propertyKey.toString();\n\n descriptor.value = async function (...args: any[]) {\n const paramCustomKeys = paramKeys.filter(\n (key) =>\n typeof key === 'string' && key.startsWith(`${methodName}:`) && key.endsWith(':custom')\n );\n for (const paramKey of paramCustomKeys) {\n const paramIndex = parseInt((paramKey as string).split(':')[1]);\n let method = Reflect.get(target.constructor, paramKey.toString());\n if (typeof paramIndex === 'number' && typeof method === 'function') {\n args[paramIndex] = await method();\n }\n }\n\n return originalMethod.apply(this, args);\n };\n };\n}\n\nexport function Get(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['GET', 'HEAD'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Post(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['POST'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Put(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['PUT'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Patch(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['PATCH'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Delete(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['DELETE'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Options(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['OPTIONS'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function createParamDecorator(func: () => Promise<any> | (() => any)): ParameterDecorator {\n return function MyParamDecorator(\n target: Object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number\n ) {\n Reflect.set(target.constructor, `${propertyKey?.toString()}:${parameterIndex}:custom`, func);\n };\n}\n","import { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { LexerToken, Request, Response } from './types.mjs';\n\nexport class Route {\n private middlewares: MiddlewareProvider[] = [];\n private urlRegex: RegExp;\n constructor(\n public methods: HttpMethod[],\n public path: string,\n public handler: HandlerType\n ) {\n this.urlRegex = this.pathToRegex(path);\n }\n pathToRegex(path: string): RegExp {\n const lex = this.lexUrlPath(path);\n return this.tokensToRegex(lex);\n }\n\n lexUrlPath(path: string) {\n const tokens = [];\n let i = 0;\n\n while (i < path.length) {\n const char = path[i];\n\n if (char === '/') {\n tokens.push({ type: 'SLASH', value: '/' });\n i++;\n } else if (char === ':') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'PARAM', value: path.slice(i + 1, start) });\n i = start;\n } else if (char === '*') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_\\.]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'WILDCARD', value: path.slice(i + 1, start) });\n i = start;\n } else {\n let start = i;\n while (start < path.length && !['/', ':', '*'].includes(path[start])) {\n start++;\n }\n tokens.push({ type: 'TEXT', value: path.slice(i, start) });\n i = start;\n }\n }\n\n return tokens;\n }\n tokensToRegex(tokens: LexerToken[]) {\n const regexParts = [];\n\n for (const token of tokens) {\n if (token.type === 'SLASH') {\n regexParts.push('\\\\/');\n } else if (token.type === 'PARAM') {\n regexParts.push(`(?<${token.value}>[^\\\\/]+)`);\n } else if (token.type === 'WILDCARD') {\n regexParts.push('(.+)');\n } else if (token.type === 'TEXT') {\n regexParts.push(token.value.replace(/[-\\/\\\\^$.*+?()[\\]{}|]/g, '\\\\$&'));\n }\n }\n\n if (regexParts.length > 0 && regexParts[regexParts.length - 1] === '\\\\/') {\n regexParts[regexParts.length - 1] = '\\\\/?';\n } else {\n regexParts.push('\\\\/?');\n }\n\n return new RegExp(`^${regexParts.join('')}$`);\n }\n\n /**\n * to evaludate if request is a match for this route\n * @param request http request\n * @returns return true if route is a match for this request\n */\n test(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n const url = new URL(request.url || '/', 'http://localhost');\n return this.testPath(url.pathname);\n }\n\n testPath(pathname: string) {\n return this.urlRegex.test(pathname);\n }\n\n /**\n * returns details of the match, otherwise it returns false\n * @param request the request to match\n * @returns object cotaining details of match including matched params\n */\n match(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n\n const url = new URL(request.url || '/', 'http://localhost');\n\n const r = this.urlRegex.exec(url.pathname);\n if (!r) {\n return false;\n }\n\n return {\n url,\n params: r.groups || {},\n };\n }\n\n prependMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = ([] as MiddlewareProvider[]).concat(middlewares, this.middlewares);\n return this;\n }\n\n addMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = this.middlewares.concat(middlewares);\n return this;\n }\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n callHanlder(request: Request, response: Response) {\n return this.handler(request, response);\n }\n}\n","import { CompiledRoute } from './CompiledRoute.mjs';\nimport { BaseController } from './Controller.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\nimport path from 'path';\nimport urlJoin from 'url-join';\n\nexport class Router {\n private middlewares: MiddlewareProvider[] = [];\n routes: Route[] = [];\n addRoute(methods: HttpMethod[], path: string, handler: HandlerType) {\n const route: Route = new Route(methods, path, handler);\n this.routes.push(route);\n return route;\n }\n\n getMiddlewares() {\n return [...this.middlewares];\n }\n\n addController(controller: typeof BaseController) {\n const basePath = controller.basePath || '';\n for (const route of controller.routes) {\n const urlPath = path.join(basePath, route.path);\n this.addRoute(route.methods, urlPath, async (req: Request, res: Response) => {\n const controllerInstance = controller.getInstance();\n // @ts-ignore\n return await controllerInstance[route.handler]();\n }).addMiddleware([...controller.baseMiddlewares, ...route.middlewares]);\n }\n }\n\n addRouter(path: string, router: Router) {\n for (const route of router.routes) {\n let path2 = urlJoin('/', path, route.path);\n this.addRoute(route.methods, path2, route.handler)\n .addMiddleware(router.getMiddlewares())\n .addMiddleware(route.getMiddlewares());\n }\n }\n\n addGlobalMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = this.middlewares.concat(middlewares);\n }\n\n resolve(request: Request): Route | undefined {\n for (const route of this.routes) {\n if (route.test(request)) {\n return route;\n }\n }\n return undefined;\n }\n\n resolveMultiple(request: Request): Route[] {\n const rc: Route[] = [];\n const url = new URL(request.url || '/', 'http://localhost');\n for (const route of this.routes) {\n if (route.testPath(url.pathname)) {\n rc.push(route);\n }\n }\n return rc;\n }\n\n getCompiledRoute(request: Request, response: Response) {\n const route = this.resolve(request);\n if (!route) {\n return undefined;\n }\n const match = route.match(request);\n if (!match) {\n return undefined;\n }\n\n request.params = match.params;\n return new CompiledRoute(route, request, response, this.middlewares);\n }\n}\n","import { Middleware } from './Middleware.mjs';\nimport { Request, Response } from './types.mjs';\n\nexport async function runNext(\n middlewares: Middleware[],\n req: Request,\n res: Response,\n final: (request: Request, response: Response) => Promise<void>\n) {\n let index = 0;\n\n async function next() {\n if (index >= middlewares.length) {\n return await final(req, res);\n }\n\n const middleware: Middleware | any = middlewares[index++];\n\n if (middleware instanceof Middleware) {\n await middleware.call(req, res, next);\n } else if (typeof middleware === 'function') {\n await middleware(req, res, next);\n } else {\n throw new Error('does not know how to run middleware');\n }\n }\n\n await next();\n}\n"],"mappings":"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","names":["path","path","path","path2","urlJoin"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/CompiledRoute.mts","../src/Middleware.mts","../src/MiddlewareFactory.mts","../src/Controller.mts","../src/Route.mts","../src/Router.mts","../src/helpers.mts"],"sourcesContent":["export * from './types.mjs';\nexport * from './CompiledRoute.mjs';\nexport * from './Controller.mjs';\nexport * from './Route.mjs';\nexport * from './Router.mjs';\nexport * from './Middleware.mjs';\nexport * from './MiddlewareFactory.mjs';\nexport * from './helpers.mjs';\n","import { Stream } from 'stream';\nimport { Middleware } from './Middleware.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\nimport { isClass } from '@devbro/neko-helper';\n\nexport class CompiledRoute {\n constructor(\n public route: Route,\n public request: Request,\n public response: Response,\n public globalMiddlewares: MiddlewareProvider[] = []\n ) {\n this.prepareMiddlewares();\n }\n\n private middlewares: Middleware[] = [];\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n private prepareMiddlewares() {\n this.middlewares = [];\n for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {\n if (middleware instanceof Middleware) {\n this.middlewares.push(middleware);\n } else if (isClass(middleware)) {\n this.middlewares.push((middleware as any).getInstance({}));\n } else if (typeof middleware === 'function') {\n this.middlewares.push(MiddlewareFactory.create(middleware as HandlerType));\n } else {\n throw new Error('Invalid middleware type');\n }\n }\n }\n\n async run() {\n return await this.runMiddlewares(this.middlewares, this.request, this.response);\n }\n\n prepareOutputJsonFormat<T>(obj: object | Array<any>): T {\n function traverse(value: any): any {\n if (value === undefined || value === null) {\n return null;\n }\n\n if (!value || typeof value !== 'object') {\n return value;\n }\n\n if (typeof value.toJson === 'function') {\n return traverse(value.toJson());\n }\n\n // to handle Date and other built-in types\n if (typeof value.toJSON === 'function') {\n return traverse(value.toJSON());\n }\n\n if (Array.isArray(value)) {\n return value.map(traverse);\n }\n\n const result: Record<string, any> = {};\n for (const key in value) {\n if (Object.prototype.hasOwnProperty.call(value, key)) {\n result[key] = traverse(value[key]);\n }\n }\n return result;\n }\n\n return traverse(obj);\n }\n\n convertToString(obj: any) {\n if (typeof obj === 'string') {\n return obj;\n } else if (obj instanceof Buffer) {\n return obj.toString();\n } else if (typeof obj === 'object') {\n return JSON.stringify(this.prepareOutputJsonFormat(obj));\n }\n return String(obj);\n }\n\n async processResponseBody(res: Response, controller_rc: any) {\n if (controller_rc && res.writableEnded) {\n throw new Error('cannot write to response, response has already ended');\n }\n\n if (res.writableEnded) {\n return;\n }\n\n if (controller_rc) {\n const header_content_type = res.getHeader('Content-Type');\n if (controller_rc instanceof Stream || Buffer.isBuffer(controller_rc)) {\n await this.writeAsync(res, controller_rc);\n } else if (!header_content_type && typeof controller_rc === 'object') {\n res.setHeader('Content-Type', 'application/json');\n res.write(this.convertToString(controller_rc));\n } else if (!header_content_type) {\n res.setHeader('Content-Type', 'text/plain');\n res.write(this.convertToString(controller_rc));\n } else {\n res.write(this.convertToString(controller_rc));\n }\n return;\n } else {\n res.statusCode = [200].includes(res.statusCode) ? 204 : res.statusCode;\n }\n }\n\n async writeAsync(res: any, chunk: any) {\n return new Promise((resolve, reject) => {\n const ok = res.write(chunk, (err: any) => {\n if (err) reject(err);\n });\n if (ok) {\n // write went through immediately\n resolve(0);\n } else {\n // wait for 'drain' when buffer is flushed\n res.once('drain', resolve);\n }\n });\n }\n\n async runMiddlewares(middlewares: Middleware[], req: Request, res: Response) {\n let index = 0;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const me = this;\n\n async function next() {\n if (index >= middlewares.length) {\n const controller_rc = await me.route.callHanlder(req, res);\n await me.processResponseBody(res, controller_rc);\n return;\n }\n\n const middleware: Middleware | any = middlewares[index++];\n\n if (middleware instanceof Middleware) {\n await middleware.call(req, res, next);\n } else if (typeof middleware === 'function') {\n await middleware(req, res, next);\n } else {\n throw new Error('does not know how to run middleware');\n }\n }\n\n await next();\n }\n}\n","import { Request, Response } from './types.mjs';\n\nexport abstract class Middleware {\n protected constructor(params: any = {}) {}\n static getInstance(params: any): Middleware {\n throw new Error('Method not implemented. Please implement a static getInstance method.');\n }\n\n abstract call(req: Request, res: Response, next: () => Promise<void>): Promise<void>;\n}\n","import { Middleware } from './Middleware.mjs';\nimport { HandlerType, Request, Response } from './types.mjs';\n\nexport class MiddlewareFactory {\n public static create(func: HandlerType): Middleware {\n const cls = class extends Middleware {\n call(req: Request, res: Response, next: () => Promise<void>): Promise<void> {\n return func(req, res, next);\n }\n constructor(params: any = {}) {\n super(params);\n }\n };\n\n return new cls();\n }\n}\n","import { ControllerDecoratorOptions, HttpMethod, MiddlewareProvider } from './types.mjs';\n\nexport class BaseController {\n declare static routes: {\n methods: HttpMethod[];\n path: string;\n handler: string;\n middlewares: MiddlewareProvider[];\n }[];\n static basePath: string = '';\n static baseMiddlewares: MiddlewareProvider[];\n\n static getInstance() {\n return new this();\n }\n}\n\nexport function Controller(path: string, options: ControllerDecoratorOptions = {}): ClassDecorator {\n return function (target: any) {\n (target as any).routes = (target as any).routes || [];\n (target as any).basePath = path;\n (target as any).baseMiddlewares = options.middlewares || [];\n };\n}\n\nfunction createHttpDecorator(data: {\n methods: HttpMethod[];\n path: string;\n middlewares: MiddlewareProvider[];\n}): MethodDecorator {\n return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {\n const ctor = target.constructor;\n if (!ctor.routes) ctor.routes = [];\n ctor.routes.push({\n methods: data.methods,\n path: data.path,\n handler: propertyKey,\n middlewares: data.middlewares || [],\n });\n\n const originalMethod = descriptor.value!;\n const paramKeys = Reflect.ownKeys(target.constructor);\n const methodName = propertyKey.toString();\n\n descriptor.value = async function (...args: any[]) {\n const paramCustomKeys = paramKeys.filter(\n (key) =>\n typeof key === 'string' && key.startsWith(`${methodName}:`) && key.endsWith(':custom')\n );\n for (const paramKey of paramCustomKeys) {\n const paramIndex = parseInt((paramKey as string).split(':')[1]);\n let method = Reflect.get(target.constructor, paramKey.toString());\n if (typeof paramIndex === 'number' && typeof method === 'function') {\n args[paramIndex] = await method();\n }\n }\n\n return originalMethod.apply(this, args);\n };\n };\n}\n\nexport function Get(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['GET', 'HEAD'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Post(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['POST'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Put(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['PUT'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Patch(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['PATCH'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Delete(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['DELETE'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function Options(\n data: { path?: string; middlewares?: MiddlewareProvider[] } = {}\n): MethodDecorator {\n return createHttpDecorator({\n methods: ['OPTIONS'],\n path: data.path || '/',\n middlewares: data.middlewares || [],\n });\n}\n\nexport function createParamDecorator(func: () => Promise<any> | (() => any)): ParameterDecorator {\n return function MyParamDecorator(\n target: Object,\n propertyKey: string | symbol | undefined,\n parameterIndex: number\n ) {\n Reflect.set(target.constructor, `${propertyKey?.toString()}:${parameterIndex}:custom`, func);\n };\n}\n","import { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { LexerToken, Request, Response } from './types.mjs';\n\nexport class Route {\n private middlewares: MiddlewareProvider[] = [];\n private urlRegex: RegExp;\n constructor(\n public methods: HttpMethod[],\n public path: string,\n public handler: HandlerType\n ) {\n this.urlRegex = this.pathToRegex(path);\n }\n pathToRegex(path: string): RegExp {\n const lex = this.lexUrlPath(path);\n return this.tokensToRegex(lex);\n }\n\n lexUrlPath(path: string) {\n const tokens = [];\n let i = 0;\n\n while (i < path.length) {\n const char = path[i];\n\n if (char === '/') {\n tokens.push({ type: 'SLASH', value: '/' });\n i++;\n } else if (char === ':') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'PARAM', value: path.slice(i + 1, start) });\n i = start;\n } else if (char === '*') {\n let start = i + 1;\n while (start < path.length && /[a-zA-Z0-9_]/.test(path[start])) {\n start++;\n }\n tokens.push({ type: 'WILDCARD', value: path.slice(i + 1, start) });\n i = start;\n } else {\n let start = i;\n while (start < path.length && !['/', ':', '*'].includes(path[start])) {\n start++;\n }\n tokens.push({ type: 'TEXT', value: path.slice(i, start) });\n i = start;\n }\n }\n\n return tokens;\n }\n tokensToRegex(tokens: LexerToken[]) {\n const regexParts = [];\n\n for (const token of tokens) {\n if (token.type === 'SLASH') {\n regexParts.push('\\\\/');\n } else if (token.type === 'PARAM') {\n regexParts.push(`(?<${token.value}>[^\\\\/]+)`);\n } else if (token.type === 'WILDCARD') {\n regexParts.push('(.+)');\n } else if (token.type === 'TEXT') {\n regexParts.push(token.value.replace(/[-\\/\\\\^$.*+?()[\\]{}|]/g, '\\\\$&'));\n }\n }\n\n if (regexParts.length > 0 && regexParts[regexParts.length - 1] === '\\\\/') {\n regexParts[regexParts.length - 1] = '\\\\/?';\n } else {\n regexParts.push('\\\\/?');\n }\n\n return new RegExp(`^${regexParts.join('')}$`);\n }\n\n /**\n * to evaludate if request is a match for this route\n * @param request http request\n * @returns return true if route is a match for this request\n */\n test(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n const url = new URL(request.url || '/', 'http://localhost');\n return this.testPath(url.pathname);\n }\n\n testPath(pathname: string) {\n return this.urlRegex.test(pathname);\n }\n\n /**\n * returns details of the match, otherwise it returns false\n * @param request the request to match\n * @returns object cotaining details of match including matched params\n */\n match(request: Request) {\n if (this.methods.indexOf(request.method as HttpMethod) === -1) {\n return false;\n }\n\n const url = new URL(request.url || '/', 'http://localhost');\n\n const r = this.urlRegex.exec(url.pathname);\n if (!r) {\n return false;\n }\n\n return {\n url,\n params: r.groups || {},\n };\n }\n\n prependMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = ([] as MiddlewareProvider[]).concat(middlewares, this.middlewares);\n return this;\n }\n\n addMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = this.middlewares.concat(middlewares);\n return this;\n }\n\n getMiddlewares() {\n return this.middlewares;\n }\n\n callHanlder(request: Request, response: Response) {\n return this.handler(request, response);\n }\n}\n","import { CompiledRoute } from './CompiledRoute.mjs';\nimport { BaseController } from './Controller.mjs';\nimport { MiddlewareFactory } from './MiddlewareFactory.mjs';\nimport { Route } from './Route.mjs';\nimport { HandlerType, HttpMethod, MiddlewareProvider } from './types.mjs';\nimport { Request, Response } from './types.mjs';\nimport path from 'path';\nimport urlJoin from 'url-join';\n\nexport class Router {\n private middlewares: MiddlewareProvider[] = [];\n routes: Route[] = [];\n addRoute(methods: HttpMethod[], path: string, handler: HandlerType) {\n const route: Route = new Route(methods, path, handler);\n this.routes.push(route);\n return route;\n }\n\n getMiddlewares() {\n return [...this.middlewares];\n }\n\n addController(controller: typeof BaseController) {\n const basePath = controller.basePath || '';\n for (const route of controller.routes) {\n const urlPath = path.join(basePath, route.path);\n this.addRoute(route.methods, urlPath, async (req: Request, res: Response) => {\n const controllerInstance = controller.getInstance();\n // @ts-ignore\n return await controllerInstance[route.handler]();\n }).addMiddleware([...controller.baseMiddlewares, ...route.middlewares]);\n }\n }\n\n addRouter(path: string, router: Router) {\n for (const route of router.routes) {\n let path2 = urlJoin('/', path, route.path);\n this.addRoute(route.methods, path2, route.handler)\n .addMiddleware(router.getMiddlewares())\n .addMiddleware(route.getMiddlewares());\n }\n }\n\n addGlobalMiddleware(middlewares: MiddlewareProvider | MiddlewareProvider[]) {\n this.middlewares = this.middlewares.concat(middlewares);\n }\n\n resolve(request: Request): Route | undefined {\n for (const route of this.routes) {\n if (route.test(request)) {\n return route;\n }\n }\n return undefined;\n }\n\n resolveMultiple(request: Request): Route[] {\n const rc: Route[] = [];\n const url = new URL(request.url || '/', 'http://localhost');\n for (const route of this.routes) {\n if (route.testPath(url.pathname)) {\n rc.push(route);\n }\n }\n return rc;\n }\n\n getCompiledRoute(request: Request, response: Response) {\n const route = this.resolve(request);\n if (!route) {\n return undefined;\n }\n const match = route.match(request);\n if (!match) {\n return undefined;\n }\n\n request.params = match.params;\n return new CompiledRoute(route, request, response, this.middlewares);\n }\n}\n","import { Middleware } from './Middleware.mjs';\nimport { Request, Response } from './types.mjs';\n\nexport async function runNext(\n middlewares: Middleware[],\n req: Request,\n res: Response,\n final: (request: Request, response: Response) => Promise<void>\n) {\n let index = 0;\n\n async function next() {\n if (index >= middlewares.length) {\n return await final(req, res);\n }\n\n const middleware: Middleware | any = middlewares[index++];\n\n if (middleware instanceof Middleware) {\n await middleware.call(req, res, next);\n } else if (typeof middleware === 'function') {\n await middleware(req, res, next);\n } else {\n throw new Error('does not know how to run middleware');\n }\n }\n\n await next();\n}\n"],"mappings":"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","names":["path","path","path","path2","urlJoin"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devbro/neko-router",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "general purpose router for URI to controller selection",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsup",
|
|
20
20
|
"test": "vitest run",
|
|
21
|
+
"bench": "vitest bench",
|
|
21
22
|
"test:watch": "vitest watch",
|
|
22
23
|
"test:coverage": "vitest run --coverage",
|
|
23
24
|
"format": "eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0 ",
|