itty-router 3.0.3 → 3.0.4
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 +49 -104
- package/dist/itty-router.d.ts +11 -9
- package/dist/itty-router.js +1 -1
- package/dist/itty-router.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.com/package/itty-router)
|
|
4
4
|
[](https://bundlephobia.com/result?p=itty-router)
|
|
5
|
-
[](https://github.com/kwhitley/itty-router/actions/workflows/verify.yml)
|
|
6
|
+
[](https://coveralls.io/github/kwhitley/itty-router?branch=v3.x)
|
|
7
7
|
[](https://npmjs.com/package/itty-router)
|
|
8
8
|
[](https://github.com/kwhitley/itty-router/issues)
|
|
9
9
|
|
|
@@ -14,12 +14,10 @@
|
|
|
14
14
|
Tiny, zero-dependency router with route param and query parsing - built for [Cloudflare Workers](https://developers.cloudflare.com/workers/), but works everywhere!
|
|
15
15
|
|
|
16
16
|
# Major Announcement: v3.x is Live!
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
This comes with a couple major changes, and is now a TS-first lib.
|
|
17
|
+
Version 3 introduces itty as a TypeScript-first library. This version should break no existing JS users, but TS users may have to update their types, as shown below. Please [join the discussion on Discord](https://discord.gg/53vyrZAu9u) to assist in this rollout! In the meantime, thanks everyone for your patience! Here are the major changes in version 3, with `itty-router-extras` (certainly) and likely `itty-cors` to be added into core as upcoming minor releases.
|
|
20
18
|
|
|
21
19
|
### Increase in bundle size (~250 bytes)
|
|
22
|
-
This was sadly overdue (and hopefully can be golfed down a bit), but as a result addressed the following issues from
|
|
20
|
+
This was sadly overdue (and hopefully can be golfed down a bit), but as a result addressed the following issues from v3.x:
|
|
23
21
|
|
|
24
22
|
1 . Routes can now capture complex/unknown paths using the trailing `+` modifier. As a result, this is now possible:
|
|
25
23
|
```js
|
|
@@ -48,7 +46,8 @@ I've been forced to rewrite the TS types. This will need a bit of documentation
|
|
|
48
46
|
- [x] [Fully typed/TypeScript support](#typescript)
|
|
49
47
|
- [x] Supports sync/async handlers/middleware.
|
|
50
48
|
- [x] Parses route params, with wildcards and optionals (e.g. `/api/:collection/:id?`)
|
|
51
|
-
- [x]
|
|
49
|
+
- [x] ["Greedy" route captures](#greedy) (e.g. `/api/:path+`)
|
|
50
|
+
- [x] Query parsing (e.g. `?page=3&foo=bar&foo=baz`)
|
|
52
51
|
- [x] [Middleware support](#middleware). Any number of sync/async handlers may be passed to a route.
|
|
53
52
|
- [x] [Nestable](#nested-routers-with-404-handling). Supports nesting routers for API branching.
|
|
54
53
|
- [x] [Base path](#nested-routers-with-404-handling) for prefixing all routes.
|
|
@@ -153,14 +152,15 @@ GET /todos/jane
|
|
|
153
152
|
query: {}
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
GET /todos/jane?limit=2&page=1
|
|
155
|
+
GET /todos/jane?limit=2&page=1&foo=bar&foo=baz
|
|
157
156
|
{
|
|
158
157
|
params: {
|
|
159
158
|
user: 'jane'
|
|
160
159
|
},
|
|
161
160
|
query: {
|
|
162
161
|
limit: '2',
|
|
163
|
-
page: '2'
|
|
162
|
+
page: '2',
|
|
163
|
+
foo: ['bar', 'baz],
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
*/
|
|
@@ -317,16 +317,16 @@ export default {
|
|
|
317
317
|
|
|
318
318
|
// alternative advanced/manual approach for downstream control
|
|
319
319
|
export default {
|
|
320
|
-
fetch: (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
320
|
+
fetch: (request, env, context) => router
|
|
321
|
+
.handle(request, env, context)
|
|
322
|
+
.then(response => {
|
|
323
|
+
// can modify response here before final return, e.g. CORS headers
|
|
324
|
+
|
|
325
|
+
return response
|
|
326
|
+
})
|
|
327
|
+
.catch(err => {
|
|
328
|
+
// and do something with the errors here, like logging, error status, etc
|
|
329
|
+
})
|
|
330
330
|
}
|
|
331
331
|
```
|
|
332
332
|
|
|
@@ -415,104 +415,49 @@ await router.handle({ method: 'GET', url: 'https:nowhere.com/custom-a123' })
|
|
|
415
415
|
|
|
416
416
|
### Typescript
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
As of version `3.x`, itty-router is TypeScript-first, meaning it has full hinting out of the box.
|
|
419
419
|
|
|
420
420
|
```ts
|
|
421
|
-
import {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
421
|
+
import {
|
|
422
|
+
Router, // the router itself
|
|
423
|
+
IRequest, // lightweight/generic Request type
|
|
424
|
+
RouterType, // generic Router type
|
|
425
|
+
Route, // generic Route type
|
|
426
|
+
} from './itty-router'
|
|
427
|
+
|
|
428
|
+
// declare a custom Router type with used methods
|
|
429
|
+
interface CustomRouter extends RouterType {
|
|
430
|
+
all: Route,
|
|
431
|
+
get: Route,
|
|
432
|
+
puppy: Route,
|
|
429
433
|
}
|
|
430
434
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
+
// declare a custom Request type to allow request injection from middleware
|
|
436
|
+
type RequestWithAuthors = {
|
|
437
|
+
authors?: string[]
|
|
438
|
+
} & IRequest
|
|
439
|
+
|
|
440
|
+
// middleware that modifies the request
|
|
441
|
+
const withAuthors = (request: IRequest) => {
|
|
442
|
+
request.authors = ['foo', 'bar']
|
|
435
443
|
}
|
|
436
444
|
|
|
437
|
-
const router =
|
|
445
|
+
const router = <CustomRouter>Router({ base: '/' })
|
|
438
446
|
|
|
439
|
-
router
|
|
440
|
-
|
|
441
|
-
|
|
447
|
+
router
|
|
448
|
+
.all<CustomRouter>('*', () => {})
|
|
449
|
+
.get<CustomRouter>('/authors', withAuthors, (request: RequestWithAuthors) => {
|
|
450
|
+
return request.authors?.[0]
|
|
451
|
+
})
|
|
452
|
+
.puppy('*', (request) => {
|
|
453
|
+
const foo = request.query.foo
|
|
454
|
+
})
|
|
442
455
|
|
|
443
456
|
addEventListener('fetch', (event: FetchEvent) => {
|
|
444
457
|
event.respondWith(router.handle(event.request))
|
|
445
458
|
})
|
|
446
459
|
```
|
|
447
460
|
|
|
448
|
-
Both generics are optional. `TRequest` defaults to `Request` and `TMethods` defaults to `{}`.
|
|
449
|
-
|
|
450
|
-
```ts
|
|
451
|
-
import { Router, Route } from 'itty-router'
|
|
452
|
-
|
|
453
|
-
type MethodType = 'GET' | 'POST' | 'PUPPY'
|
|
454
|
-
|
|
455
|
-
interface IRequest extends Request {
|
|
456
|
-
method: MethodType
|
|
457
|
-
url: string
|
|
458
|
-
optional?: string
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
interface IMethods {
|
|
462
|
-
get: Route
|
|
463
|
-
post: Route
|
|
464
|
-
puppy: Route
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const router = Router() // Valid
|
|
468
|
-
const router = Router<IRequest>() // Valid
|
|
469
|
-
const router = Router<Request, IMethods>() // Valid
|
|
470
|
-
const router = Router<void, IMethods>() // Valid
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
The router will also accept any string as a method, not just those provided on the `TMethods` type.
|
|
474
|
-
|
|
475
|
-
```ts
|
|
476
|
-
import { Router, Route } from 'itty-router'
|
|
477
|
-
|
|
478
|
-
interface IMethods {
|
|
479
|
-
get: Route
|
|
480
|
-
post: Route
|
|
481
|
-
puppy: Route
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const router = Router<void, IMethods>()
|
|
485
|
-
|
|
486
|
-
router.puppy('/', request => {}) // Valid
|
|
487
|
-
router.kitten('/', request => {}) // Also Valid
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
The `itty-router` package also exports an interface containing all of the HTTP methods.
|
|
491
|
-
|
|
492
|
-
```ts
|
|
493
|
-
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
494
|
-
|
|
495
|
-
const router = Router<void, IHTTPMethods>()
|
|
496
|
-
|
|
497
|
-
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
498
|
-
router.puppy('/', request => {}) // Valid but not strongly typed
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
You can also extend `IHTTPMethods` with your own custom methods so they will be strongly typed.
|
|
502
|
-
|
|
503
|
-
```ts
|
|
504
|
-
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
505
|
-
|
|
506
|
-
interface IMethods extends IHTTPMethods {
|
|
507
|
-
puppy: Route
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const router = Router<void, IMethods>()
|
|
511
|
-
|
|
512
|
-
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
513
|
-
router.puppy('/', request => {}) // Strongly typed
|
|
514
|
-
```
|
|
515
|
-
|
|
516
461
|
## Testing and Contributing
|
|
517
462
|
1. Fork repo
|
|
518
463
|
1. Install dev dependencies via `yarn`
|
package/dist/itty-router.d.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
declare type
|
|
1
|
+
declare type GenericTraps = {
|
|
2
2
|
[key: string]: any;
|
|
3
3
|
};
|
|
4
|
-
declare type
|
|
4
|
+
declare type IRequest = {
|
|
5
5
|
method: string;
|
|
6
6
|
url: string;
|
|
7
|
-
|
|
7
|
+
params: GenericTraps;
|
|
8
|
+
query: GenericTraps;
|
|
9
|
+
} & GenericTraps;
|
|
8
10
|
interface RouterOptions {
|
|
9
11
|
base?: string;
|
|
10
12
|
routes?: RouteEntry[];
|
|
11
13
|
}
|
|
12
14
|
interface RouteHandler {
|
|
13
|
-
(request:
|
|
15
|
+
(request: IRequest, ...args: any): any;
|
|
14
16
|
}
|
|
15
17
|
declare type RouteEntry = [string, RegExp, RouteHandler[]];
|
|
16
|
-
declare type Route = (path: string, ...handlers: RouteHandler[]) =>
|
|
17
|
-
declare type
|
|
18
|
+
declare type Route = <T extends RouterType>(path: string, ...handlers: RouteHandler[]) => T;
|
|
19
|
+
declare type RouterHints = {
|
|
18
20
|
all?: Route;
|
|
19
21
|
delete?: Route;
|
|
20
22
|
get?: Route;
|
|
@@ -26,8 +28,8 @@ declare type RouterTraps = {
|
|
|
26
28
|
declare type RouterType = {
|
|
27
29
|
__proto__: RouterType;
|
|
28
30
|
routes: RouteEntry[];
|
|
29
|
-
handle: (request:
|
|
30
|
-
} &
|
|
31
|
+
handle: (request: IRequest, ...extra: any) => Promise<any>;
|
|
32
|
+
} & RouterHints;
|
|
31
33
|
declare const Router: ({ base, routes }?: RouterOptions) => RouterType;
|
|
32
34
|
|
|
33
|
-
export {
|
|
35
|
+
export { GenericTraps, IRequest, Route, RouteEntry, RouteHandler, Router, RouterHints, RouterOptions, RouterType };
|
package/dist/itty-router.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var
|
|
1
|
+
"use strict";var u=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var h=(r,e)=>{for(var t in e)u(r,t,{get:e[t],enumerable:!0})},x=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of d(e))!g.call(r,o)&&o!==t&&u(r,o,{get:()=>e[o],enumerable:!(n=l(e,o))||n.enumerable});return r};var f=r=>x(u({},"__esModule",{value:!0}),r);var m={};h(m,{Router:()=>c});module.exports=f(m);var T=r=>[...r.entries()].reduce((e,[t,n])=>(e[t]===void 0?e[t]=n:e[t]=[e[t],n].flat())&&e||e,{}),c=({base:r="",routes:e=[]}={})=>({__proto__:new Proxy({},{get:(t,n,o)=>(p,...a)=>e.push([n.toUpperCase(),RegExp(`^${(r+p).replace(/(\/?)\*/g,"($1.*)?").replace(/(\/$)|((?<=\/)\/)/,"").replace(/(:(\w+)\+)/,"(?<$2>.*)").replace(/:(\w+)(\?)?(\.)?/g,"$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/,"\\.").replace(/\)\.\?\(([^\[]+)\[\^/g,"?)\\.?($1(?<=\\.)[^\\.")}/*$`),a])&&o}),routes:e,async handle(t,...n){let o,p,a=new URL(t.url);t.query=T(a.searchParams);for(let[s,R,y]of e)if((s===t.method||s==="ALL")&&(p=a.pathname.match(R))){t.params=p.groups||{};for(let i of y)if((o=await i(t.proxy||t,...n))!==void 0)return o}}});0&&(module.exports={Router});
|
package/dist/itty-router.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var i=a=>[...a.entries()].reduce((t,[e,r])=>(t[e]===void 0?t[e]=r:t[e]=[t[e],r].flat())&&t||t,{}),l=({base:a="",routes:t=[]}={})=>({__proto__:new Proxy({},{get:(e,r,o)=>(n,...p)=>t.push([r.toUpperCase(),RegExp(`^${(a+n).replace(/(\/?)\*/g,"($1.*)?").replace(/(\/$)|((?<=\/)\/)/,"").replace(/(:(\w+)\+)/,"(?<$2>.*)").replace(/:(\w+)(\?)?(\.)?/g,"$2(?<$1>[^/]+)$2$3").replace(/\.(?=[\w(])/,"\\.").replace(/\)\.\?\(([^\[]+)\[\^/g,"?)\\.?($1(?<=\\.)[^\\.")}/*$`),p])&&o}),routes:t,async handle(e,...r){let o,n,p=new URL(e.url);e.query=i(p.searchParams);for(let[u,s,R]of t)if((u===e.method||u==="ALL")&&(n=p.pathname.match(s))){e.params=n.groups||{};for(let y of R)if((o=await y(e.proxy||e,...r))!==void 0)return o}}});export{l as Router};
|