@devbro/neko-router 0.1.17 → 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.
Files changed (2) hide show
  1. package/README.md +703 -32
  2. package/package.json +2 -1
package/README.md CHANGED
@@ -1,47 +1,718 @@
1
1
  # @devbro/neko-router
2
2
 
3
- cool routing solution for choosing the code that runs for a given URI.
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
- ## supported features
5
+ ## Installation
6
6
 
7
- - support controller and function as main executor
8
- - middleware support both as class and function
9
- - singleton middleware and new instance per request middleware
10
- - support route variables
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
- ## example
173
+ Chain multiple middleware functions:
13
174
 
14
175
  ```ts
15
- import {Router, Request, Response} from '@devbro/neko-router';
16
- const router: Router = new Router();
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
- router.addRoute(['GET', 'HEAD'], '/api/v1/countries', async (req: Request, res: Response) => {
19
- return 'GET countries';
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
- .addRoute(['POST'], '/api/v1/countries', async (req: Request, res: Response) => {
24
- return 'POST countries';
25
- })
26
- .addMiddleware([m1, m2]);
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
- router.addRoute(
29
- ['GET'],
30
- '/api/v1/countries/:countryId',
31
- async (req: Request, res: Response) => {
32
- return 'GET PARAM countries ' + req.params?.countryId;
33
- }
34
- ).addMiddleware((req,res,next) => {
35
- console.log('request.params:', req.params);
36
- await next();
37
- console.log('final result:', res.statusCode);
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
- let req = { url: '/api/v1/countries', method: 'GET' } as Request;
41
- let res = { ??? } as Response;
42
- let resolved = router.resolve(req);
561
+ const app = express();
562
+ const router = new NekoRouter();
43
563
 
44
- let compiled_route = router.getCompiledRoute(req,res);
45
- await compiled_route.run();
46
- console.log(res.statusCode);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devbro/neko-router",
3
- "version": "0.1.17",
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 ",