itty-router 2.4.10 → 2.5.2
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/CHANGELOG.md +5 -0
- package/README.md +156 -33
- package/dist/itty-router.d.ts +18 -5
- package/dist/itty-router.min.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
Until this library makes it to a production release of v1.x, **minor versions may contain breaking changes to the API**. After v1.x, semantic versioning will be honored, and breaking changes will only occur under the umbrella of a major version bump.
|
|
3
3
|
|
|
4
|
+
[@SupremeTechnopriest)(https://github.com/SupremeTechnopriest) - improved TypeScript support and documentation! :D\
|
|
5
|
+
|
|
6
|
+
- **v2.5.2** - fixes issue with arrow functions in CommonJS named exports (rare issue)
|
|
7
|
+
- **v2.5.1** - added context to Cloudflare ES module syntax example (credit [@jcapogna](https://github.com/jcapogna))
|
|
8
|
+
- **v2.5.0** - improved TypeScript docs/types (thanks [@SupremeTechnopriest](https://github.com/SupremeTechnopriest)!)
|
|
4
9
|
- **v2.4.9** - fixed the cursed "optional" file format capturing bug - RIP all the bytes lost
|
|
5
10
|
- **v2.4.6** - fixed README issues
|
|
6
11
|
- **v2.4.1** - fixed type errors introduced with 2.4.0
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[![npm package][npm-image]][npm-url]
|
|
4
4
|
[![minified + gzipped size][gzip-image]][gzip-url]
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
[![Coverage Status][coveralls-image]][coveralls-url]
|
|
7
7
|
[![Open Issues][issues-image]][issues-url]
|
|
8
8
|
<a href="https://npmjs.com/package/itty-router" target="\_parent">
|
|
@@ -19,14 +19,15 @@
|
|
|
19
19
|
<img alt="Join the discussion on Github" src="https://img.shields.io/badge/Github%20Discussions%20%26%20Support-Chat%20now!-blue" />
|
|
20
20
|
</a>-->
|
|
21
21
|
|
|
22
|
-
It's an itty bitty router, designed for express-like routing within [Cloudflare Workers](https://developers.cloudflare.com/workers/) (or anywhere else). Like... it's super tiny ([~
|
|
22
|
+
It's an itty bitty router, designed for express-like routing within [Cloudflare Workers](https://developers.cloudflare.com/workers/) (or anywhere else). Like... it's super tiny ([~500 bytes](https://bundlephobia.com/package/itty-router)), with zero dependencies. For reals.
|
|
23
23
|
|
|
24
24
|
### Addons & Related Libraries
|
|
25
25
|
1. [itty-router-extras](https://www.npmjs.com/package/itty-router-extras) - adds quality-of-life improvements and utility functions for simplifying request/response code (e.g. middleware, cookies, body parsing, json handling, errors, and an itty version with automatic exception handling)!
|
|
26
26
|
2. [itty-durable](https://github.com/kwhitley/itty-durable) - (EXPERIMENTAL/alpha) creates a more direct object-like API for interacting with [Cloudflare Durable Objects](https://developers.cloudflare.com/workers/learning/using-durable-objects).
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
|
-
- [x] Tiny ([~
|
|
29
|
+
- [x] Tiny ([~500 bytes](https://bundlephobia.com/package/itty-router) compressed), with zero dependencies.
|
|
30
|
+
- [x] [TypeScript support](#typescript)!
|
|
30
31
|
- [x] Full sync/async support. Use it when you need it!
|
|
31
32
|
- [x] Route params, with wildcards and optionals (e.g. `/api/:collection/:id?`)
|
|
32
33
|
- [x] Query parsing (e.g. `?page=3&foo=bar`)
|
|
@@ -282,16 +283,33 @@ router.get('/todos/:id.:format?', request => {
|
|
|
282
283
|
```
|
|
283
284
|
|
|
284
285
|
### Cloudflare ES6 Module Syntax (required for Durable Objects) <a id="cf-es6-module-syntax"></a>
|
|
286
|
+
See https://developers.cloudflare.com/workers/runtime-apis/fetch-event#syntax-module-worker
|
|
285
287
|
```js
|
|
288
|
+
import { Router } from 'itty-router'
|
|
289
|
+
|
|
286
290
|
const router = Router()
|
|
287
291
|
|
|
288
|
-
router.get('/', (request, env) => {
|
|
292
|
+
router.get('/', (request, env, context) => {
|
|
289
293
|
// now have access to the env (where CF bindings like durables, KV, etc now are)
|
|
290
294
|
})
|
|
291
295
|
|
|
292
296
|
export default {
|
|
293
297
|
fetch: router.handle // yep, it's this easy.
|
|
294
298
|
}
|
|
299
|
+
|
|
300
|
+
// alternative advanced/manual approach for downstream control
|
|
301
|
+
export default {
|
|
302
|
+
fetch: (...args) => router
|
|
303
|
+
.handle(...args)
|
|
304
|
+
.then(response =>
|
|
305
|
+
// can modify response here before final return, e.g. CORS headers
|
|
306
|
+
|
|
307
|
+
return response
|
|
308
|
+
})
|
|
309
|
+
.catch(err => {
|
|
310
|
+
// and do something with the errors here, like logging, error status, etc
|
|
311
|
+
})
|
|
312
|
+
}
|
|
295
313
|
```
|
|
296
314
|
|
|
297
315
|
### Extending itty router
|
|
@@ -377,6 +395,106 @@ router.get('/test', () => new Response('You can still define routes normally as
|
|
|
377
395
|
await router.handle({ method: 'GET', url: 'https:nowhere.com/custom-a123' }) // { id: "a123" }
|
|
378
396
|
```
|
|
379
397
|
|
|
398
|
+
### Typescript
|
|
399
|
+
|
|
400
|
+
For Typescript projects, the Router can be adorned with two generics: A custom request interface and a custom methods interface.
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import { Router, Route, Request } from 'itty-router'
|
|
404
|
+
|
|
405
|
+
type MethodType = 'GET' | 'POST' | 'PUPPY'
|
|
406
|
+
|
|
407
|
+
interface IRequest extends Request {
|
|
408
|
+
method: MethodType // method is required to be on the interface
|
|
409
|
+
url: string // url is required to be on the interface
|
|
410
|
+
optional?: string
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
interface IMethods {
|
|
414
|
+
get: Route
|
|
415
|
+
post: Route
|
|
416
|
+
puppy: Route
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const router = Router<IRequest, IMethods>()
|
|
420
|
+
|
|
421
|
+
router.get('/', (request: IRequest) => {})
|
|
422
|
+
router.post('/', (request: IRequest) => {})
|
|
423
|
+
router.puppy('/', (request: IRequest) => {})
|
|
424
|
+
|
|
425
|
+
addEventListener('fetch', (event: FetchEvent) => {
|
|
426
|
+
event.respondWith(router.handle(event.request))
|
|
427
|
+
})
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Both generics are optional. `TRequest` defaults to `Request` and `TMethods` defaults to `{}`.
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import { Router, Route } from 'itty-router'
|
|
434
|
+
|
|
435
|
+
type MethodType = 'GET' | 'POST' | 'PUPPY'
|
|
436
|
+
|
|
437
|
+
interface IRequest extends Request {
|
|
438
|
+
method: MethodType
|
|
439
|
+
url: string
|
|
440
|
+
optional?: string
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
interface IMethods {
|
|
444
|
+
get: Route
|
|
445
|
+
post: Route
|
|
446
|
+
puppy: Route
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const router = Router() // Valid
|
|
450
|
+
const router = Router<IRequest>() // Valid
|
|
451
|
+
const router = Router<Request, IMethods>() // Valid
|
|
452
|
+
const router = Router<void, IMethods>() // Valid
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
The router will also accept any string as a method, not just those provided on the `TMethods` type.
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
import { Router, Route } from 'itty-router'
|
|
459
|
+
|
|
460
|
+
interface IMethods {
|
|
461
|
+
get: Route
|
|
462
|
+
post: Route
|
|
463
|
+
puppy: Route
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const router = Router<void, IMethods>()
|
|
467
|
+
|
|
468
|
+
router.puppy('/', request => {}) // Valid
|
|
469
|
+
router.kitten('/', request => {}) // Also Valid
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
The `itty-router` package also exports an interface containing all of the HTTP methods.
|
|
473
|
+
|
|
474
|
+
```ts
|
|
475
|
+
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
476
|
+
|
|
477
|
+
const router = Router<void, IHTTPMethods>()
|
|
478
|
+
|
|
479
|
+
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
480
|
+
router.puppy('/', request => {}) // Valid but not strongly typed
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
You can also extend `IHTTPMethods` with your own custom methods so they will be strongly typed.
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
487
|
+
|
|
488
|
+
interface IMethods extends IHTTPMethods {
|
|
489
|
+
puppy: Route
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const router = Router<void, IMethods>()
|
|
493
|
+
|
|
494
|
+
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
495
|
+
router.puppy('/', request => {}) // Strongly typed
|
|
496
|
+
```
|
|
497
|
+
|
|
380
498
|
## Testing and Contributing
|
|
381
499
|
1. Fork repo
|
|
382
500
|
1. Install dev dependencies via `yarn`
|
|
@@ -389,36 +507,38 @@ await router.handle({ method: 'GET', url: 'https:nowhere.com/custom-a123' })
|
|
|
389
507
|
|
|
390
508
|
### The Entire Code (for more legibility, [see src on GitHub](https://github.com/kwhitley/itty-router/blob/v2.x/src/itty-router.js))
|
|
391
509
|
```js
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
q.
|
|
415
|
-
|
|
416
|
-
|
|
510
|
+
function Router({ base = '', routes = [] } = {}) {
|
|
511
|
+
return {
|
|
512
|
+
__proto__: new Proxy({}, {
|
|
513
|
+
get: (t, k, c) => (p, ...H) =>
|
|
514
|
+
routes.push([
|
|
515
|
+
k.toUpperCase(),
|
|
516
|
+
RegExp(`^${(base + p)
|
|
517
|
+
.replace(/(\/?)\*/g, '($1.*)?')
|
|
518
|
+
.replace(/\/$/, '')
|
|
519
|
+
.replace(/:(\w+)(\?)?(\.)?/g, '$2(?<$1>[^/]+)$2$3')
|
|
520
|
+
.replace(/\.(?=[\w(])/, '\\.')
|
|
521
|
+
.replace(/\)\.\?\(([^\[]+)\[\^/g, '?)\\.?($1(?<=\\.)[^\\.') // RIP all the bytes lost :'(
|
|
522
|
+
}/*$`),
|
|
523
|
+
H,
|
|
524
|
+
]) && c
|
|
525
|
+
}),
|
|
526
|
+
routes,
|
|
527
|
+
async handle (q, ...a) {
|
|
528
|
+
let s, m,
|
|
529
|
+
u = new URL(q.url)
|
|
530
|
+
q.query = Object.fromEntries(u.searchParams)
|
|
531
|
+
for (let [M, p, H] of routes) {
|
|
532
|
+
if ((M === q.method || M === 'ALL') && (m = u.pathname.match(p))) {
|
|
533
|
+
q.params = m.groups
|
|
534
|
+
for (let h of H) {
|
|
535
|
+
if ((s = await h(q.proxy || q, ...a)) !== undefined) return s
|
|
536
|
+
}
|
|
417
537
|
}
|
|
418
538
|
}
|
|
419
539
|
}
|
|
420
540
|
}
|
|
421
|
-
}
|
|
541
|
+
}
|
|
422
542
|
```
|
|
423
543
|
|
|
424
544
|
## Special Thanks
|
|
@@ -436,8 +556,6 @@ This trick allows methods (e.g. "get", "post") to by defined dynamically by the
|
|
|
436
556
|
[issues-url]:https://github.com/kwhitley/itty-router/issues
|
|
437
557
|
[npm-image]:https://img.shields.io/npm/v/itty-router.svg
|
|
438
558
|
[npm-url]:http://npmjs.org/package/itty-router
|
|
439
|
-
[travis-image]:https://app.travis-ci.com/kwhitley/itty-router.svg?branch=v2.x
|
|
440
|
-
[travis-url]:https://travis-ci.org/kwhitley/itty-router
|
|
441
559
|
[david-image]:https://david-dm.org/kwhitley/itty-router/status.svg
|
|
442
560
|
[david-url]:https://david-dm.org/kwhitley/itty-router
|
|
443
561
|
[coveralls-image]:https://coveralls.io/repos/github/kwhitley/itty-router/badge.svg?branch=v2.x
|
|
@@ -453,9 +571,14 @@ These folks are the real heroes, making open source the powerhouse that it is!
|
|
|
453
571
|
- [@mvasigh](https://github.com/mvasigh) - proxy hack wizard behind itty, coding partner in crime, maker of the entire doc site, etc, etc.
|
|
454
572
|
- [@taralx](https://github.com/taralx) - router internal code-golfing refactor for performance and character savings
|
|
455
573
|
- [@hunterloftis](https://github.com/hunterloftis) - router.handle() method now accepts extra arguments and passed them to route functions
|
|
574
|
+
- [@SupremeTechnopriest](https://github.com/SupremeTechnopriest) - improved TypeScript support and documentation! :D
|
|
456
575
|
#### Fixes
|
|
457
576
|
- [@taralx](https://github.com/taralx) - QOL fixes for contributing (dev dep fix and test file consistency) <3
|
|
458
577
|
- [@technoyes](https://github.com/technoyes) - three kind-of-a-big-deal errors fixed. Imagine the look on my face... thanks man!! :)
|
|
459
578
|
- [@roojay520](https://github.com/roojay520) - TS interface fixes
|
|
460
579
|
#### Documentation
|
|
461
|
-
- [@arunsathiya](https://github.com/arunsathiya),
|
|
580
|
+
- [@arunsathiya](https://github.com/arunsathiya),
|
|
581
|
+
[@poacher2k](https://github.com/poacher2k),
|
|
582
|
+
[@ddarkr](https://github.com/ddarkr),
|
|
583
|
+
[@kclauson](https://github.com/kclauson),
|
|
584
|
+
[@jcapogna](https://github.com/jcapogna)
|
package/dist/itty-router.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Obj = {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
export interface RouteHandler<TRequest> {
|
|
6
|
-
(request: TRequest
|
|
6
|
+
(request: TRequest, ...args: any): any
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface Route {
|
|
@@ -29,10 +29,23 @@ export interface Request {
|
|
|
29
29
|
text?(): Promise<any>
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export
|
|
33
|
-
|
|
32
|
+
export interface IHTTPMethods {
|
|
33
|
+
get: Route
|
|
34
|
+
head: Route
|
|
35
|
+
post: Route
|
|
36
|
+
put: Route
|
|
37
|
+
delete: Route
|
|
38
|
+
connect: Route
|
|
39
|
+
options: Route
|
|
40
|
+
trace: Route
|
|
41
|
+
patch: Route
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Router<TRequest = Request, TMethods = {}> = {
|
|
45
|
+
handle: (request: TRequest, ...extra: any) => any
|
|
34
46
|
routes: RouteEntry<TRequest>[]
|
|
35
|
-
|
|
47
|
+
all: Route
|
|
48
|
+
} & TMethods & {
|
|
36
49
|
[any:string]: Route
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -41,4 +54,4 @@ export interface RouterOptions<TRequest> {
|
|
|
41
54
|
routes?: RouteEntry<TRequest>[]
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
export function Router<TRequest>(options?:RouterOptions<TRequest>): Router<TRequest>
|
|
57
|
+
export function Router<TRequest = Request, TMethods = {}>(options?:RouterOptions<TRequest>): Router<TRequest, TMethods>
|
package/dist/itty-router.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports={Router:({base:
|
|
1
|
+
module.exports={Router:function({base:t="",routes:n=[]}={}){return{__proto__:new Proxy({},{get:(e,a,o)=>(e,...r)=>n.push([a.toUpperCase(),RegExp(`^${(t+e).replace(/(\/?)\*/g,"($1.*)?").replace(/\/$/,"").replace(/:(\w+)(\?)?(\.)?/g,"$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/,"\\.").replace(/\)\.\?\(([^\[]+)\[\^/g,"?)\\.?($1(?<=\\.)[^\\.")}/*$`),r])&&o}),routes:n,async handle(e,...r){let a,o,t=new URL(e.url);e.query=Object.fromEntries(t.searchParams);for(var[p,s,u]of n)if((p===e.method||"ALL"===p)&&(o=t.pathname.match(s))){e.params=o.groups;for(var c of u)if(void 0!==(a=await c(e.proxy||e,...r)))return a}}}}};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "itty-router",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "Tiny, zero-dependency router with route param and query parsing - built for Cloudflare Workers, but works everywhere!",
|
|
5
5
|
"main": "./dist/itty-router.min.js",
|
|
6
6
|
"types": "./dist/itty-router.d.ts",
|