@ditojs/router 2.0.5 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/Result.js +13 -9
- package/src/Router.test.js +163 -139
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/router",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Router – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/master/packages/router",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"node >= 18"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ditojs/utils": "^2.0
|
|
24
|
+
"@ditojs/utils": "^2.1.0"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"route",
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"koa",
|
|
31
31
|
"dito"
|
|
32
32
|
],
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "5c5048b661395268e720b9ec402d4301bb4a34aa",
|
|
34
34
|
"readme": "# Dito.js Router\n\nDito.js is a declarative and modern web framework with a focus on API driven\ndevelopment, based on Koa.js, Objection.js and Vue.js\n\nReleased in 2018 under the MIT license, with support by https://lineto.com/\n\nDito.js Router is a high performance, tree-based, framework agnostic HTTP\nrouter, based on [trek-router](https://github.com/trekjs/router), which in turn\nis inspired by [Echo](https://github.com/labstack/echo)'s Router.\n\n## How does it work?\n\nThe router relies on a tree structure which makes heavy use of common\nprefixes, essentially a [prefix tree](https://en.wikipedia.org/wiki/Trie).\n\n## Usage\n\n```js\nimport Koa from 'koa'\nimport Router from '@ditojs/router'\n\nconst app = new Koa()\nconst router = new Router()\n\n// static route\nrouter.get('/folders/files/bolt.gif', ctx => {\n ctx.body = `this ain't no GIF!`\n})\n\n// param route\nrouter.get('/users/:id', ctx => {\n ctx.body = `requesting user ${ctx.params.id}`\n})\n\n// match-any route\nrouter.get('/books/*', ctx => {\n ctx.body = `sub-route: ${ctx.params['*']}`\n})\n\n// Handler found\nlet { handler, params } = router.find('get', '/users/233')\nconsole.log(handler) // ctx => { ... }\n\n// Entry not Found\nlet { handler, params } = router.find('get', '/photos/233')\nconsole.log(handler) // null\n\n// Install router middleware\napp.use(async (ctx, next) => {\n const { method, path } = ctx\n const result = router.find(method, path)\n const { handler, params } = result\n if (handler) {\n ctx.params = params || {}\n return handler(ctx, next)\n } else {\n try {\n await next()\n } finally {\n if (ctx.body === undefined && ctx.status === 404) {\n ctx.status = result.status || 404\n if (ctx.status !== 404 && result.allowed) {\n ctx.set('Allow', result.allowed.join(', '))\n }\n }\n }\n }\n})\n\napp.listen(4040, () => console.log('Koa app listening on 4040'))\n```\n"
|
|
35
35
|
}
|
package/src/Result.js
CHANGED
|
@@ -5,16 +5,20 @@ export default class Result {
|
|
|
5
5
|
// Send 405 / 501 errors, except for 'GET' (404) and 'OPTIONS' (200).
|
|
6
6
|
// 405: 'Method Not Allowed' (tree was found, but node was not)
|
|
7
7
|
// 501: 'Method Not Implemented' (there is no tree for that method)
|
|
8
|
-
this.status =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
this.status = (
|
|
9
|
+
{
|
|
10
|
+
GET: 404,
|
|
11
|
+
get: 404,
|
|
12
|
+
options: 200,
|
|
13
|
+
OPTIONS: 200
|
|
14
|
+
}[method] ||
|
|
15
|
+
(tree ? 405 : 501)
|
|
16
|
+
)
|
|
14
17
|
// Getter for the `allowed` property, called once, then cached:
|
|
15
|
-
this[SYMBOL_ALLOWED] = () =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
this[SYMBOL_ALLOWED] = () =>
|
|
19
|
+
tree
|
|
20
|
+
? router.getAllowedMethods(path, method)
|
|
21
|
+
: router.getAllowedMethods()
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
get allowed() {
|
package/src/Router.test.js
CHANGED
|
@@ -88,10 +88,12 @@ describe('Router', () => {
|
|
|
88
88
|
router.add('GET', '/folders/files/bolt.gif', getHandler)
|
|
89
89
|
router.add('PUT', '/folders/files/bolt.gif', putHandler)
|
|
90
90
|
|
|
91
|
-
expect(router.toString()).toBe(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
expect(router.toString()).toBe(
|
|
92
|
+
deindent`
|
|
93
|
+
/ children=1
|
|
94
|
+
└── folders/files/bolt.gif getHandler() children=0
|
|
95
|
+
`.trim()
|
|
96
|
+
)
|
|
95
97
|
|
|
96
98
|
result = router.find('GET', '/folders/files/bolt.gif')
|
|
97
99
|
expect(result.handler).toBe(getHandler)
|
|
@@ -124,11 +126,13 @@ describe('Router', () => {
|
|
|
124
126
|
it('handles match-any nodes (catch-all)', () => {
|
|
125
127
|
router.add('GET', '/static/**', handler)
|
|
126
128
|
|
|
127
|
-
expect(router.toString()).toBe(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
expect(router.toString()).toBe(
|
|
130
|
+
deindent`
|
|
131
|
+
/ children=1
|
|
132
|
+
└── static/ children=1
|
|
133
|
+
└── ** handler() children=0
|
|
134
|
+
`.trim()
|
|
135
|
+
)
|
|
132
136
|
|
|
133
137
|
result = router.find('GET', '/static')
|
|
134
138
|
expect(result.handler).toBeUndefined()
|
|
@@ -152,12 +156,14 @@ describe('Router', () => {
|
|
|
152
156
|
it('handles match-any nodes (wildcard)', () => {
|
|
153
157
|
router.add('GET', '/prefix/*/suffix', handler)
|
|
154
158
|
|
|
155
|
-
expect(router.toString()).toBe(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
expect(router.toString()).toBe(
|
|
160
|
+
deindent`
|
|
161
|
+
/ children=1
|
|
162
|
+
└── prefix/ children=1
|
|
163
|
+
└── * children=1
|
|
164
|
+
└── /suffix handler() children=0
|
|
165
|
+
`.trim()
|
|
166
|
+
)
|
|
161
167
|
|
|
162
168
|
result = router.find('GET', '/prefix')
|
|
163
169
|
expect(result.handler).toBeUndefined()
|
|
@@ -181,14 +187,16 @@ describe('Router', () => {
|
|
|
181
187
|
router.add('GET', '/static/**/suffix/*/1/:param/2', handler)
|
|
182
188
|
router.add('GET', '/static/**/suffix/*/1/:param/2/**/end', handler)
|
|
183
189
|
|
|
184
|
-
expect(router.toString()).toBe(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
expect(router.toString()).toBe(
|
|
191
|
+
deindent`
|
|
192
|
+
/ children=1
|
|
193
|
+
└── static/ children=4
|
|
194
|
+
├── **/suffix handler() children=0
|
|
195
|
+
├── **/suffix/**/more handler() children=0
|
|
196
|
+
├── **/suffix/*/1/:param/2 handler() children=0
|
|
197
|
+
└── **/suffix/*/1/:param/2/**/end handler() children=0
|
|
198
|
+
`.trim()
|
|
199
|
+
)
|
|
192
200
|
|
|
193
201
|
result = router.find('GET', '/static')
|
|
194
202
|
expect(result.handler).toBeUndefined()
|
|
@@ -235,7 +243,10 @@ describe('Router', () => {
|
|
|
235
243
|
result = router.find('GET', '/static/one/two/suffix/three/1/bla/3')
|
|
236
244
|
expect(result.handler).toBeUndefined()
|
|
237
245
|
|
|
238
|
-
result = router.find(
|
|
246
|
+
result = router.find(
|
|
247
|
+
'GET',
|
|
248
|
+
`/static/one/two/suffix/three/1/bla/2/what/ever/end`
|
|
249
|
+
)
|
|
239
250
|
expect(result.handler).toBe(handler)
|
|
240
251
|
expect(result.params).toEqual({
|
|
241
252
|
$: 'three',
|
|
@@ -263,26 +274,28 @@ describe('Router', () => {
|
|
|
263
274
|
['/geocoder/**', 'anyGeocoder']
|
|
264
275
|
])
|
|
265
276
|
|
|
266
|
-
expect(router.toString()).toBe(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
277
|
+
expect(router.toString()).toBe(
|
|
278
|
+
deindent`
|
|
279
|
+
/ root() children=1
|
|
280
|
+
└── geocoder geocoder() children=1
|
|
281
|
+
└── / children=4
|
|
282
|
+
├── n children=2
|
|
283
|
+
│ ├── ew newGeocoder() children=0
|
|
284
|
+
│ └── otify notifyGeocoder() children=0
|
|
285
|
+
├── e children=2
|
|
286
|
+
│ ├── dit editGeocoder() children=1
|
|
287
|
+
│ │ └── / children=2
|
|
288
|
+
│ │ ├── email editEmailGeocoder() children=0
|
|
289
|
+
│ │ └── :item editItemGeocoder() children=0
|
|
290
|
+
│ └── xchange exchangeGeocoder() children=1
|
|
291
|
+
│ └── / children=2
|
|
292
|
+
│ ├── email exchangeEmailGeocoder() children=0
|
|
293
|
+
│ └── :item exchangeItemGeocoder() children=0
|
|
294
|
+
├── :action actionGeocoder() children=1
|
|
295
|
+
│ └── /echo echoGeocoder() children=0
|
|
296
|
+
└── ** anyGeocoder() children=0
|
|
297
|
+
`.trim()
|
|
298
|
+
)
|
|
286
299
|
result = router.find('GET', '')
|
|
287
300
|
expect(result.handler).not.toBeUndefined()
|
|
288
301
|
expect(result.params).toEqual({})
|
|
@@ -377,30 +390,32 @@ describe('Router', () => {
|
|
|
377
390
|
['/users/**', 'anyUser']
|
|
378
391
|
])
|
|
379
392
|
|
|
380
|
-
expect(router.toString()).toBe(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
393
|
+
expect(router.toString()).toBe(
|
|
394
|
+
deindent`
|
|
395
|
+
/ children=1
|
|
396
|
+
└── users users() children=1
|
|
397
|
+
└── / children=3
|
|
398
|
+
├── n children=2
|
|
399
|
+
│ ├── e children=2
|
|
400
|
+
│ │ ├── w newUser() children=0
|
|
401
|
+
│ │ └── i newUser() children=0
|
|
402
|
+
│ └── oi newUser() children=0
|
|
403
|
+
├── :userId user() children=1
|
|
404
|
+
│ └── / children=4
|
|
405
|
+
│ ├── edit editUser() children=0
|
|
406
|
+
│ ├── :action actionUser() children=1
|
|
407
|
+
│ │ └── / children=1
|
|
408
|
+
│ │ └── :good children=1
|
|
409
|
+
│ │ └── / children=1
|
|
410
|
+
│ │ └── :bad children=1
|
|
411
|
+
│ │ └── /ddd editUser() children=0
|
|
412
|
+
│ ├── photos/ children=1
|
|
413
|
+
│ │ └── :id photo() children=0
|
|
414
|
+
│ └── books/ children=1
|
|
415
|
+
│ └── :id book() children=0
|
|
416
|
+
└── ** anyUser() children=0
|
|
417
|
+
`.trim()
|
|
418
|
+
)
|
|
404
419
|
|
|
405
420
|
result = router.find('GET', '/users/610/books/987/edit')
|
|
406
421
|
expect(result.handler).not.toBeUndefined()
|
|
@@ -489,39 +504,41 @@ describe('Router', () => {
|
|
|
489
504
|
|
|
490
505
|
it('parses routes into the correct tree', () => {
|
|
491
506
|
createRoutes(router, routes)
|
|
492
|
-
expect(router.toString()).toBe(
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
507
|
+
expect(router.toString()).toBe(
|
|
508
|
+
deindent`
|
|
509
|
+
/ children=3
|
|
510
|
+
├── users users() children=1
|
|
511
|
+
│ └── / children=2
|
|
512
|
+
│ ├── new newUser() children=0
|
|
513
|
+
│ └── :id user() children=1
|
|
514
|
+
│ └── / children=3
|
|
515
|
+
│ ├── :action actionUser() children=0
|
|
516
|
+
│ ├── e children=2
|
|
517
|
+
│ │ ├── dit editUser() children=0
|
|
518
|
+
│ │ └── vent eventUser() children=0
|
|
519
|
+
│ └── change changeUser() children=0
|
|
520
|
+
├── photos photos() children=1
|
|
521
|
+
│ └── / children=2
|
|
522
|
+
│ ├── new newPhoto() children=0
|
|
523
|
+
│ └── :id photo() children=1
|
|
524
|
+
│ └── / children=3
|
|
525
|
+
│ ├── :action actionPhoto() children=0
|
|
526
|
+
│ ├── e children=2
|
|
527
|
+
│ │ ├── dit editPhoto() children=0
|
|
528
|
+
│ │ └── vent eventPhoto() children=0
|
|
529
|
+
│ └── change changePhoto() children=0
|
|
530
|
+
└── books books() children=1
|
|
531
|
+
└── / children=2
|
|
532
|
+
├── new newBook() children=0
|
|
533
|
+
└── :id book() children=1
|
|
534
|
+
└── / children=3
|
|
535
|
+
├── :action actionBook() children=0
|
|
536
|
+
├── e children=2
|
|
537
|
+
│ ├── dit editBook() children=0
|
|
538
|
+
│ └── vent eventBook() children=0
|
|
539
|
+
└── change changeBook() children=0
|
|
540
|
+
`.trim()
|
|
541
|
+
)
|
|
525
542
|
})
|
|
526
543
|
|
|
527
544
|
it('results in expected parameters', () => {
|
|
@@ -623,14 +640,16 @@ describe('Router', () => {
|
|
|
623
640
|
|
|
624
641
|
it('parses routes into the correct tree', () => {
|
|
625
642
|
createRoutes(router, routes)
|
|
626
|
-
expect(router.toString()).toBe(
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
643
|
+
expect(router.toString()).toBe(
|
|
644
|
+
deindent`
|
|
645
|
+
/ children=1
|
|
646
|
+
└── admin/articles articles() children=1
|
|
647
|
+
└── / children=2
|
|
648
|
+
├── new newArticle() children=0
|
|
649
|
+
└── :id article() children=1
|
|
650
|
+
└── /edit editArticle() children=0
|
|
651
|
+
`.trim()
|
|
652
|
+
)
|
|
634
653
|
})
|
|
635
654
|
|
|
636
655
|
it('results in expected parameters', () => {
|
|
@@ -670,22 +689,24 @@ describe('Router', () => {
|
|
|
670
689
|
|
|
671
690
|
it('parses routes into the correct tree', () => {
|
|
672
691
|
createRoutes(router, routes)
|
|
673
|
-
expect(router.toString()).toBe(
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
692
|
+
expect(router.toString()).toBe(
|
|
693
|
+
deindent`
|
|
694
|
+
/ children=1
|
|
695
|
+
└── magazines/ children=1
|
|
696
|
+
└── :m_id children=1
|
|
697
|
+
└── / children=2
|
|
698
|
+
├── articles articles() children=1
|
|
699
|
+
│ └── / children=2
|
|
700
|
+
│ ├── new newArticle() children=0
|
|
701
|
+
│ └── :id article() children=1
|
|
702
|
+
│ └── /edit editArticle() children=0
|
|
703
|
+
└── photos photos() children=1
|
|
704
|
+
└── / children=2
|
|
705
|
+
├── new newPhoto() children=0
|
|
706
|
+
└── :id photo() children=1
|
|
707
|
+
└── /edit editPhoto() children=0
|
|
708
|
+
`.trim()
|
|
709
|
+
)
|
|
689
710
|
})
|
|
690
711
|
|
|
691
712
|
it('results in expected parameters', () => {
|
|
@@ -734,30 +755,33 @@ describe('Router', () => {
|
|
|
734
755
|
})
|
|
735
756
|
|
|
736
757
|
it('identifies unnamed handlers', () => {
|
|
737
|
-
router.add('GET', '/function', function() {})
|
|
758
|
+
router.add('GET', '/function', function () {})
|
|
738
759
|
router.add('GET', '/closure', () => {})
|
|
739
|
-
expect(router.toString()).toBe(
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
760
|
+
expect(router.toString()).toBe(
|
|
761
|
+
deindent`
|
|
762
|
+
/ children=2
|
|
763
|
+
├── function ƒ() children=0
|
|
764
|
+
└── closure ƒ() children=0
|
|
765
|
+
`.trim()
|
|
766
|
+
)
|
|
744
767
|
})
|
|
745
768
|
|
|
746
769
|
it(`deals with paths not starting with '/'`, () => {
|
|
747
770
|
router.add('GET', '/users', () => {})
|
|
748
771
|
router.add('GET', '/wacky-path', () => {})
|
|
749
|
-
expect(router.toString()).toBe(
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
772
|
+
expect(router.toString()).toBe(
|
|
773
|
+
deindent`
|
|
774
|
+
/ children=2
|
|
775
|
+
├── users ƒ() children=0
|
|
776
|
+
└── wacky-path ƒ() children=0
|
|
777
|
+
`.trim()
|
|
778
|
+
)
|
|
754
779
|
})
|
|
755
780
|
})
|
|
756
781
|
|
|
757
782
|
function createFunc(name) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
))()
|
|
783
|
+
// eslint-disable-next-line no-new-func
|
|
784
|
+
return new Function(`return function ${name}(){}`)()
|
|
761
785
|
}
|
|
762
786
|
|
|
763
787
|
function createRoutes(router, routes) {
|