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 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
- [![Build Status][travis-image]][travis-url]
5
+ ![Build Status](https://github.com/kwhitley/itty-router/actions/workflows/verify.yml/badge.svg)
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 ([~4xx bytes](https://bundlephobia.com/package/itty-router)), with zero dependencies. For reals.
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 ([~4xx bytes](https://bundlephobia.com/package/itty-router) compressed) with zero dependencies.
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
- const Router = ({ base = '', routes = [] } = {}) => ({
393
- __proto__: new Proxy({}, {
394
- get: (t, k, c) => (p, ...H) =>
395
- routes.push([
396
- k.toUpperCase(),
397
- RegExp(`^${(base + p)
398
- .replace(/(\/?)\*/g, '($1.*)?')
399
- .replace(/\/$/, '')
400
- .replace(/:(\w+)(\?)?(\.)?/g, '$2(?<$1>[^/]+)$2$3')
401
- .replace(/\.(?=[\w(])/, '\\.')
402
- .replace(/\)\.\?\(([^\[]+)\[\^/g, '?)\\.?($1(?<=\\.)[^\\.')
403
- }/*$`),
404
- H,
405
- ]) && c
406
- }),
407
- routes,
408
- async handle (q, ...a) {
409
- let s, m,
410
- u = new URL(q.url)
411
- q.query = Object.fromEntries(u.searchParams)
412
- for (let [M, p, H] of routes) {
413
- if ((M === q.method || M === 'ALL') && (m = u.pathname.match(p))) {
414
- q.params = m.groups
415
- for (let h of H) {
416
- if ((s = await h(q.proxy || q, ...a)) !== undefined) return s
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), [@poacher2k](https://github.com/poacher2k), [@ddarkr](https://github.com/ddarkr), [@kclauson](https://github.com/kclauson)
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)
@@ -3,7 +3,7 @@ export type Obj = {
3
3
  }
4
4
 
5
5
  export interface RouteHandler<TRequest> {
6
- (request: TRequest & Request, ...args: any): any
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 type Router<TRequest> = {
33
- handle: (request: Request, ...extra: any) => any
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>
@@ -1 +1 @@
1
- module.exports={Router:({base:p="",routes:u=[]}={})=>({__proto__:new Proxy({},{get:(e,a,o)=>(e,...r)=>u.push([a.toUpperCase(),RegExp(`^${(p+e).replace(/(\/?)\*/g,"($1.*)?").replace(/\/$/,"").replace(/:(\w+)(\?)?(\.)?/g,"$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/,"\\.").replace(/\)\.\?\(([^\[]+)\[\^/g,"?)\\.?($1(?<=\\.)[^\\.")}/*$`),r])&&o}),routes:u,async handle(e,...r){let a,o,p=new URL(e.url);e.query=Object.fromEntries(p.searchParams);for(var[t,s,c]of u)if((t===e.method||"ALL"===t)&&(o=p.pathname.match(s))){e.params=o.groups;for(var l of c)if(void 0!==(a=await l(e.proxy||e,...r)))return a}}})};
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.4.10",
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",