itty-router 3.0.2 → 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 +71 -101
- 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
|
|
|
@@ -13,17 +13,41 @@
|
|
|
13
13
|
|
|
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
|
+
# Major Announcement: v3.x is Live!
|
|
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.
|
|
18
|
+
|
|
19
|
+
### Increase in bundle size (~250 bytes)
|
|
20
|
+
This was sadly overdue (and hopefully can be golfed down a bit), but as a result addressed the following issues from v3.x:
|
|
21
|
+
|
|
22
|
+
1 . Routes can now capture complex/unknown paths using the trailing `+` modifier. As a result, this is now possible:
|
|
23
|
+
```js
|
|
24
|
+
router.handle('/get-file/:path+', ({ params }) => params)
|
|
25
|
+
|
|
26
|
+
// GET /get-file/with/a/long/path.png => { path: "with/a/long/path.png" }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Query params with multiple same-name values now operate as you would expect (previously, they overwrote each other)
|
|
30
|
+
```js
|
|
31
|
+
router.handle('/foo', ({ query }) => query)
|
|
32
|
+
|
|
33
|
+
// GET /foo?pets=mittens&pets=fluffy&pets=rex&bar=baz => { bar: "baz", pets: ["mittens", "fluffy", "rex"] }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Breaking TS changes
|
|
37
|
+
I've been forced to rewrite the TS types. This will need a bit of documentation...
|
|
38
|
+
|
|
16
39
|
### Addons & Related Libraries
|
|
17
40
|
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)!
|
|
18
41
|
2. [itty-cors](https://www.npmjs.com/package/itty-cors) (early access/alpha) - Easy CORS handling for itty APIs.
|
|
19
42
|
2. [itty-durable](https://www.npmjs.com/package/itty-durable) - creates a more direct object-like API for interacting with [Cloudflare Durable Objects](https://developers.cloudflare.com/workers/learning/using-durable-objects).
|
|
20
43
|
|
|
21
44
|
## Features
|
|
22
|
-
- [x] Tiny ([~
|
|
45
|
+
- [x] Tiny ([~780 bytes](https://bundlephobia.com/package/itty-router) compressed), with zero dependencies.
|
|
23
46
|
- [x] [Fully typed/TypeScript support](#typescript)
|
|
24
47
|
- [x] Supports sync/async handlers/middleware.
|
|
25
48
|
- [x] Parses route params, with wildcards and optionals (e.g. `/api/:collection/:id?`)
|
|
26
|
-
- [x]
|
|
49
|
+
- [x] ["Greedy" route captures](#greedy) (e.g. `/api/:path+`)
|
|
50
|
+
- [x] Query parsing (e.g. `?page=3&foo=bar&foo=baz`)
|
|
27
51
|
- [x] [Middleware support](#middleware). Any number of sync/async handlers may be passed to a route.
|
|
28
52
|
- [x] [Nestable](#nested-routers-with-404-handling). Supports nesting routers for API branching.
|
|
29
53
|
- [x] [Base path](#nested-routers-with-404-handling) for prefixing all routes.
|
|
@@ -128,14 +152,15 @@ GET /todos/jane
|
|
|
128
152
|
query: {}
|
|
129
153
|
}
|
|
130
154
|
|
|
131
|
-
GET /todos/jane?limit=2&page=1
|
|
155
|
+
GET /todos/jane?limit=2&page=1&foo=bar&foo=baz
|
|
132
156
|
{
|
|
133
157
|
params: {
|
|
134
158
|
user: 'jane'
|
|
135
159
|
},
|
|
136
160
|
query: {
|
|
137
161
|
limit: '2',
|
|
138
|
-
page: '2'
|
|
162
|
+
page: '2',
|
|
163
|
+
foo: ['bar', 'baz],
|
|
139
164
|
}
|
|
140
165
|
}
|
|
141
166
|
*/
|
|
@@ -292,16 +317,16 @@ export default {
|
|
|
292
317
|
|
|
293
318
|
// alternative advanced/manual approach for downstream control
|
|
294
319
|
export default {
|
|
295
|
-
fetch: (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
+
})
|
|
305
330
|
}
|
|
306
331
|
```
|
|
307
332
|
|
|
@@ -390,104 +415,49 @@ await router.handle({ method: 'GET', url: 'https:nowhere.com/custom-a123' })
|
|
|
390
415
|
|
|
391
416
|
### Typescript
|
|
392
417
|
|
|
393
|
-
|
|
418
|
+
As of version `3.x`, itty-router is TypeScript-first, meaning it has full hinting out of the box.
|
|
394
419
|
|
|
395
420
|
```ts
|
|
396
|
-
import {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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,
|
|
404
433
|
}
|
|
405
434
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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']
|
|
410
443
|
}
|
|
411
444
|
|
|
412
|
-
const router =
|
|
445
|
+
const router = <CustomRouter>Router({ base: '/' })
|
|
413
446
|
|
|
414
|
-
router
|
|
415
|
-
|
|
416
|
-
|
|
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
|
+
})
|
|
417
455
|
|
|
418
456
|
addEventListener('fetch', (event: FetchEvent) => {
|
|
419
457
|
event.respondWith(router.handle(event.request))
|
|
420
458
|
})
|
|
421
459
|
```
|
|
422
460
|
|
|
423
|
-
Both generics are optional. `TRequest` defaults to `Request` and `TMethods` defaults to `{}`.
|
|
424
|
-
|
|
425
|
-
```ts
|
|
426
|
-
import { Router, Route } from 'itty-router'
|
|
427
|
-
|
|
428
|
-
type MethodType = 'GET' | 'POST' | 'PUPPY'
|
|
429
|
-
|
|
430
|
-
interface IRequest extends Request {
|
|
431
|
-
method: MethodType
|
|
432
|
-
url: string
|
|
433
|
-
optional?: string
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
interface IMethods {
|
|
437
|
-
get: Route
|
|
438
|
-
post: Route
|
|
439
|
-
puppy: Route
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const router = Router() // Valid
|
|
443
|
-
const router = Router<IRequest>() // Valid
|
|
444
|
-
const router = Router<Request, IMethods>() // Valid
|
|
445
|
-
const router = Router<void, IMethods>() // Valid
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
The router will also accept any string as a method, not just those provided on the `TMethods` type.
|
|
449
|
-
|
|
450
|
-
```ts
|
|
451
|
-
import { Router, Route } from 'itty-router'
|
|
452
|
-
|
|
453
|
-
interface IMethods {
|
|
454
|
-
get: Route
|
|
455
|
-
post: Route
|
|
456
|
-
puppy: Route
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const router = Router<void, IMethods>()
|
|
460
|
-
|
|
461
|
-
router.puppy('/', request => {}) // Valid
|
|
462
|
-
router.kitten('/', request => {}) // Also Valid
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
The `itty-router` package also exports an interface containing all of the HTTP methods.
|
|
466
|
-
|
|
467
|
-
```ts
|
|
468
|
-
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
469
|
-
|
|
470
|
-
const router = Router<void, IHTTPMethods>()
|
|
471
|
-
|
|
472
|
-
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
473
|
-
router.puppy('/', request => {}) // Valid but not strongly typed
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
You can also extend `IHTTPMethods` with your own custom methods so they will be strongly typed.
|
|
477
|
-
|
|
478
|
-
```ts
|
|
479
|
-
import { Router, Route, IHTTPMethods } from 'itty-router'
|
|
480
|
-
|
|
481
|
-
interface IMethods extends IHTTPMethods {
|
|
482
|
-
puppy: Route
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const router = Router<void, IMethods>()
|
|
486
|
-
|
|
487
|
-
router.get('/', request => {}) // Exposed via IHTTPMethods
|
|
488
|
-
router.puppy('/', request => {}) // Strongly typed
|
|
489
|
-
```
|
|
490
|
-
|
|
491
461
|
## Testing and Contributing
|
|
492
462
|
1. Fork repo
|
|
493
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};
|