itty-router 3.0.3 → 3.0.5
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 -108
- 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,13 +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
|
-
|
|
17
|
+
Version 3 introduces itty as a TypeScript-first library. This version should break no existing JS users, but TS users will likely need to [update their types](#typescript). Please [join the discussion on Discord](https://discord.gg/53vyrZAu9u) to assist in this rollout! In the meantime, thanks everyone for your patience!
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
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
20
|
|
|
21
|
-
### 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 v2.x:
|
|
23
|
-
|
|
24
21
|
1 . Routes can now capture complex/unknown paths using the trailing `+` modifier. As a result, this is now possible:
|
|
25
22
|
```js
|
|
26
23
|
router.handle('/get-file/:path+', ({ params }) => params)
|
|
@@ -35,9 +32,6 @@ This was sadly overdue (and hopefully can be golfed down a bit), but as a result
|
|
|
35
32
|
// GET /foo?pets=mittens&pets=fluffy&pets=rex&bar=baz => { bar: "baz", pets: ["mittens", "fluffy", "rex"] }
|
|
36
33
|
```
|
|
37
34
|
|
|
38
|
-
### Breaking TS changes
|
|
39
|
-
I've been forced to rewrite the TS types. This will need a bit of documentation...
|
|
40
|
-
|
|
41
35
|
### Addons & Related Libraries
|
|
42
36
|
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)!
|
|
43
37
|
2. [itty-cors](https://www.npmjs.com/package/itty-cors) (early access/alpha) - Easy CORS handling for itty APIs.
|
|
@@ -48,7 +42,8 @@ I've been forced to rewrite the TS types. This will need a bit of documentation
|
|
|
48
42
|
- [x] [Fully typed/TypeScript support](#typescript)
|
|
49
43
|
- [x] Supports sync/async handlers/middleware.
|
|
50
44
|
- [x] Parses route params, with wildcards and optionals (e.g. `/api/:collection/:id?`)
|
|
51
|
-
- [x]
|
|
45
|
+
- [x] ["Greedy" route captures](#greedy) (e.g. `/api/:path+`)
|
|
46
|
+
- [x] Query parsing (e.g. `?page=3&foo=bar&foo=baz`)
|
|
52
47
|
- [x] [Middleware support](#middleware). Any number of sync/async handlers may be passed to a route.
|
|
53
48
|
- [x] [Nestable](#nested-routers-with-404-handling). Supports nesting routers for API branching.
|
|
54
49
|
- [x] [Base path](#nested-routers-with-404-handling) for prefixing all routes.
|
|
@@ -153,14 +148,15 @@ GET /todos/jane
|
|
|
153
148
|
query: {}
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
GET /todos/jane?limit=2&page=1
|
|
151
|
+
GET /todos/jane?limit=2&page=1&foo=bar&foo=baz
|
|
157
152
|
{
|
|
158
153
|
params: {
|
|
159
154
|
user: 'jane'
|
|
160
155
|
},
|
|
161
156
|
query: {
|
|
162
157
|
limit: '2',
|
|
163
|
-
page: '2'
|
|
158
|
+
page: '2',
|
|
159
|
+
foo: ['bar', 'baz],
|
|
164
160
|
}
|
|
165
161
|
}
|
|
166
162
|
*/
|
|
@@ -317,16 +313,16 @@ export default {
|
|
|
317
313
|
|
|
318
314
|
// alternative advanced/manual approach for downstream control
|
|
319
315
|
export default {
|
|
320
|
-
fetch: (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
316
|
+
fetch: (request, env, context) => router
|
|
317
|
+
.handle(request, env, context)
|
|
318
|
+
.then(response => {
|
|
319
|
+
// can modify response here before final return, e.g. CORS headers
|
|
320
|
+
|
|
321
|
+
return response
|
|
322
|
+
})
|
|
323
|
+
.catch(err => {
|
|
324
|
+
// and do something with the errors here, like logging, error status, etc
|
|
325
|
+
})
|
|
330
326
|
}
|
|
331
327
|
```
|
|
332
328
|
|
|
@@ -415,104 +411,49 @@ await router.handle({ method: 'GET', url: 'https:nowhere.com/custom-a123' })
|
|
|
415
411
|
|
|
416
412
|
### Typescript
|
|
417
413
|
|
|
418
|
-
|
|
414
|
+
As of version `3.x`, itty-router is TypeScript-first, meaning it has full hinting out of the box.
|
|
419
415
|
|
|
420
416
|
```ts
|
|
421
|
-
import {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
417
|
+
import {
|
|
418
|
+
Router, // the router itself
|
|
419
|
+
IRequest, // lightweight/generic Request type
|
|
420
|
+
RouterType, // generic Router type
|
|
421
|
+
Route, // generic Route type
|
|
422
|
+
} from './itty-router'
|
|
423
|
+
|
|
424
|
+
// declare a custom Router type with used methods
|
|
425
|
+
interface CustomRouter extends RouterType {
|
|
426
|
+
all: Route,
|
|
427
|
+
get: Route,
|
|
428
|
+
puppy: Route,
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
431
|
+
// declare a custom Request type to allow request injection from middleware
|
|
432
|
+
type RequestWithAuthors = {
|
|
433
|
+
authors?: string[]
|
|
434
|
+
} & IRequest
|
|
435
|
+
|
|
436
|
+
// middleware that modifies the request
|
|
437
|
+
const withAuthors = (request: IRequest) => {
|
|
438
|
+
request.authors = ['foo', 'bar']
|
|
435
439
|
}
|
|
436
440
|
|
|
437
|
-
const router =
|
|
441
|
+
const router = <CustomRouter>Router({ base: '/' })
|
|
438
442
|
|
|
439
|
-
router
|
|
440
|
-
|
|
441
|
-
|
|
443
|
+
router
|
|
444
|
+
.all<CustomRouter>('*', () => {})
|
|
445
|
+
.get<CustomRouter>('/authors', withAuthors, (request: RequestWithAuthors) => {
|
|
446
|
+
return request.authors?.[0]
|
|
447
|
+
})
|
|
448
|
+
.puppy('*', (request) => {
|
|
449
|
+
const foo = request.query.foo
|
|
450
|
+
})
|
|
442
451
|
|
|
443
452
|
addEventListener('fetch', (event: FetchEvent) => {
|
|
444
453
|
event.respondWith(router.handle(event.request))
|
|
445
454
|
})
|
|
446
455
|
```
|
|
447
456
|
|
|
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
457
|
## Testing and Contributing
|
|
517
458
|
1. Fork repo
|
|
518
459
|
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};
|