hono 0.0.8 → 0.0.12
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 +225 -40
- package/dist/compose.d.ts +1 -0
- package/dist/compose.js +27 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.js +47 -0
- package/dist/hono.d.ts +42 -0
- package/dist/hono.js +137 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/middleware/basic-auth/basic-auth.d.ts +6 -0
- package/dist/middleware/basic-auth/basic-auth.js +50 -0
- package/dist/middleware/defaultFilter.d.ts +2 -0
- package/dist/middleware/defaultFilter.js +12 -0
- package/dist/middleware/logger/logger.d.ts +6 -0
- package/dist/middleware/logger/logger.js +58 -0
- package/dist/middleware/poweredBy/poweredBy.d.ts +2 -0
- package/dist/middleware/poweredBy/poweredBy.js +11 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.js +14 -0
- package/dist/node.d.ts +24 -0
- package/dist/node.js +104 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +63 -0
- package/package.json +46 -5
- package/.github/workflows/ci.yml +0 -24
- package/src/compose.js +0 -22
- package/src/compose.test.js +0 -42
- package/src/hono.d.ts +0 -47
- package/src/hono.js +0 -142
- package/src/hono.test.js +0 -115
- package/src/methods.js +0 -30
- package/src/middleware/defaultFilter.js +0 -19
- package/src/middleware/poweredBy.js +0 -6
- package/src/middleware/poweredBy.test.js +0 -17
- package/src/middleware.js +0 -7
- package/src/middleware.test.js +0 -17
- package/src/node.js +0 -97
- package/src/node.test.js +0 -135
- package/src/router.test.js +0 -88
- package/src/util.js +0 -26
- package/src/util.test.js +0 -29
package/README.md
CHANGED
|
@@ -6,31 +6,43 @@ Hono [炎] - Tiny web framework for Cloudflare Workers and others.
|
|
|
6
6
|
const { Hono } = require('hono')
|
|
7
7
|
const app = new Hono()
|
|
8
8
|
|
|
9
|
-
app.get('/', () =>
|
|
9
|
+
app.get('/', (c) => c.text('Hono!!'))
|
|
10
10
|
|
|
11
11
|
app.fire()
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Hono[炎] - _**means flame🔥 in Japanese**_ - is small, fast and simple web flamework for a Service Workers API based serverless such as **Cloudflare Workers** and **Fastly Compute@Edge**. Hono does not depend on any npm packages. However, Hono has a router, context object, and middleware including the builtins. It's easy to make a web application.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Fast** - the router is implemented with Trie-Tree structure.
|
|
19
|
+
- **Tiny** - zero dependencies, using Web standard API.
|
|
20
|
+
- **Flexible** - you can make your own middleware.
|
|
21
|
+
- **Easy** - simple API, builtin middleware, and written in TypeScript.
|
|
22
|
+
- **Optimized** - for Cloudflare Workers or Fastly Compute@Edge.
|
|
21
23
|
|
|
22
24
|
## Benchmark
|
|
23
25
|
|
|
26
|
+
**Hono is fastest** compared to other routers for Cloudflare Workers.
|
|
27
|
+
|
|
24
28
|
```
|
|
25
|
-
hono x
|
|
26
|
-
itty-router x
|
|
27
|
-
sunder x
|
|
29
|
+
hono x 758,264 ops/sec ±5.41% (75 runs sampled)
|
|
30
|
+
itty-router x 158,359 ops/sec ±3.21% (89 runs sampled)
|
|
31
|
+
sunder x 297,581 ops/sec ±4.74% (83 runs sampled)
|
|
28
32
|
Fastest is hono
|
|
29
|
-
✨ Done in
|
|
33
|
+
✨ Done in 42.84s.
|
|
30
34
|
```
|
|
31
35
|
|
|
36
|
+
## Hono in 1 minute
|
|
37
|
+
|
|
38
|
+
Below is a demonstration to create an application of Cloudflare Workers with Hono.
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
32
42
|
## Install
|
|
33
43
|
|
|
44
|
+
You can install from npm registry:
|
|
45
|
+
|
|
34
46
|
```
|
|
35
47
|
$ yarn add hono
|
|
36
48
|
```
|
|
@@ -43,10 +55,13 @@ $ npm install hono
|
|
|
43
55
|
|
|
44
56
|
## Methods
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
Instance of `Hono` has these methods:
|
|
59
|
+
|
|
60
|
+
- app.**HTTP_METHOD**(path, handler)
|
|
61
|
+
- app.**all**(path, handler)
|
|
48
62
|
- app.**route**(path)
|
|
49
63
|
- app.**use**(path, middleware)
|
|
64
|
+
- app.**fire**()
|
|
50
65
|
|
|
51
66
|
## Routing
|
|
52
67
|
|
|
@@ -100,6 +115,15 @@ app
|
|
|
100
115
|
.put(() => {...})
|
|
101
116
|
```
|
|
102
117
|
|
|
118
|
+
## async/await
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
app.get('/fetch-url', async () => {
|
|
122
|
+
const response = await fetch('https://example.com/')
|
|
123
|
+
return new Response(`Status is ${response.status}`)
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
103
127
|
## Middleware
|
|
104
128
|
|
|
105
129
|
### Builtin Middleware
|
|
@@ -109,82 +133,243 @@ const { Hono, Middleware } = require('hono')
|
|
|
109
133
|
|
|
110
134
|
...
|
|
111
135
|
|
|
112
|
-
app.use(
|
|
113
|
-
|
|
136
|
+
app.use(
|
|
137
|
+
'/auth/*',
|
|
138
|
+
Middleware.basicAuth({
|
|
139
|
+
username: 'hono',
|
|
140
|
+
password: 'acoolproject',
|
|
141
|
+
})
|
|
142
|
+
)
|
|
114
143
|
```
|
|
115
144
|
|
|
145
|
+
Available builtin middleware are listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
|
|
146
|
+
|
|
116
147
|
### Custom Middleware
|
|
117
148
|
|
|
149
|
+
You can write your own middleware:
|
|
150
|
+
|
|
118
151
|
```js
|
|
119
|
-
|
|
152
|
+
// Custom logger
|
|
153
|
+
app.use('*', async (c, next) => {
|
|
120
154
|
console.log(`[${c.req.method}] ${c.req.url}`)
|
|
121
|
-
next()
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const addHeader = (c, next) => {
|
|
125
|
-
next()
|
|
126
|
-
c.res.headers.add('x-message', 'This is middleware!')
|
|
127
|
-
}
|
|
155
|
+
await next()
|
|
156
|
+
})
|
|
128
157
|
|
|
129
|
-
|
|
130
|
-
app.use('/message/*',
|
|
158
|
+
// Add a custom header
|
|
159
|
+
app.use('/message/*', async (c, next) => {
|
|
160
|
+
await next()
|
|
161
|
+
await c.res.headers.add('x-message', 'This is middleware!')
|
|
162
|
+
})
|
|
131
163
|
|
|
132
164
|
app.get('/message/hello', () => 'Hello Middleware!')
|
|
133
165
|
```
|
|
134
166
|
|
|
167
|
+
### Custom 404 Response
|
|
168
|
+
|
|
169
|
+
You can customize 404 Not Found response:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
app.use('*', async (c, next) => {
|
|
173
|
+
await next()
|
|
174
|
+
if (c.res.status === 404) {
|
|
175
|
+
c.res = new Response('Custom 404 Not Found', { status: 404 })
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Complex Pattern
|
|
181
|
+
|
|
182
|
+
You can also do this:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
// Output response time
|
|
186
|
+
app.use('*', async (c, next) => {
|
|
187
|
+
await next()
|
|
188
|
+
const responseTime = await c.res.headers.get('X-Response-Time')
|
|
189
|
+
console.log(`X-Response-Time: ${responseTime}`)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Add X-Response-Time header
|
|
193
|
+
app.use('*', async (c, next) => {
|
|
194
|
+
const start = Date.now()
|
|
195
|
+
await next()
|
|
196
|
+
const ms = Date.now() - start
|
|
197
|
+
await c.res.headers.append('X-Response-Time', `${ms}ms`)
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
135
201
|
## Context
|
|
136
202
|
|
|
137
|
-
|
|
203
|
+
To handle Request and Reponse easily, you can use Context object:
|
|
204
|
+
|
|
205
|
+
### c.req
|
|
138
206
|
|
|
139
207
|
```js
|
|
208
|
+
|
|
209
|
+
// Get Request object
|
|
140
210
|
app.get('/hello', (c) => {
|
|
141
|
-
const userAgent = c.req.headers('User-Agent')
|
|
211
|
+
const userAgent = c.req.headers.get('User-Agent')
|
|
212
|
+
...
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// Query params
|
|
216
|
+
app.get('/search', (c) => {
|
|
217
|
+
const query = c.req.query('q')
|
|
218
|
+
...
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
// Captured params
|
|
222
|
+
app.get('/entry/:id', (c) => {
|
|
223
|
+
const id = c.req.params('id')
|
|
142
224
|
...
|
|
143
225
|
})
|
|
144
226
|
```
|
|
145
227
|
|
|
146
|
-
### res
|
|
228
|
+
### c.res
|
|
147
229
|
|
|
148
230
|
```js
|
|
231
|
+
// Response object
|
|
149
232
|
app.use('/', (c, next) => {
|
|
150
233
|
next()
|
|
151
234
|
c.res.headers.append('X-Debug', 'Debug message')
|
|
152
235
|
})
|
|
153
236
|
```
|
|
154
237
|
|
|
155
|
-
|
|
238
|
+
### c.text()
|
|
156
239
|
|
|
157
|
-
|
|
240
|
+
Render text as `Content-Type:text/plain`:
|
|
158
241
|
|
|
159
242
|
```js
|
|
160
|
-
app.get('/
|
|
161
|
-
|
|
162
|
-
...
|
|
243
|
+
app.get('/say', (c) => {
|
|
244
|
+
return c.text('Hello!')
|
|
163
245
|
})
|
|
164
246
|
```
|
|
165
247
|
|
|
166
|
-
###
|
|
248
|
+
### c.json()
|
|
249
|
+
|
|
250
|
+
Render JSON as `Content-Type:application/json`:
|
|
167
251
|
|
|
168
252
|
```js
|
|
169
|
-
app.get('/
|
|
170
|
-
|
|
171
|
-
...
|
|
253
|
+
app.get('/api', (c) => {
|
|
254
|
+
return c.json({ message: 'Hello!' })
|
|
172
255
|
})
|
|
173
256
|
```
|
|
174
257
|
|
|
258
|
+
### c.html()
|
|
259
|
+
|
|
260
|
+
Render HTML as `Content-Type:text/html`:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
app.get('/api', (c) => {
|
|
264
|
+
return c.html('<h1>Hello! Hono!</h1>')
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## fire
|
|
269
|
+
|
|
270
|
+
`app.fire()` do:
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
addEventListener('fetch', (event) => {
|
|
274
|
+
event.respondWith(this.handleEvent(event))
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Cloudflare Workers with Hono
|
|
279
|
+
|
|
280
|
+
Using `wrangler` or `miniflare`, you can develop the application locally and publish it with few commands.
|
|
281
|
+
|
|
282
|
+
Let's write your first code for Cloudflare Workers with Hono.
|
|
283
|
+
|
|
284
|
+
### 1. Install Wrangler
|
|
285
|
+
|
|
286
|
+
Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)"
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
$ npm i @cloudflare/wrangler -g
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 2. `npm init`
|
|
293
|
+
|
|
294
|
+
Make npm skeleton directory.
|
|
295
|
+
|
|
296
|
+
```sh
|
|
297
|
+
$ mkdir hono-example
|
|
298
|
+
$ ch hono-example
|
|
299
|
+
$ npm init -y
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 3. `wrangler init`
|
|
303
|
+
|
|
304
|
+
Init as a wrangler project.
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
$ wrangler init
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 4. `npm install hono`
|
|
311
|
+
|
|
312
|
+
Install `hono` from npm registry.
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
$ npm i hono
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### 5. Write your app
|
|
319
|
+
|
|
320
|
+
Only 4 lines!!
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
const { Hono } = require('hono')
|
|
324
|
+
const app = new Hono()
|
|
325
|
+
|
|
326
|
+
app.get('/', (c) => c.text('Hello! Hono!'))
|
|
327
|
+
|
|
328
|
+
app.fire()
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 6. Run
|
|
332
|
+
|
|
333
|
+
Run the development server locally. Then, access like `http://127.0.0.1:8787/` in your Web browser.
|
|
334
|
+
|
|
335
|
+
```sh
|
|
336
|
+
$ wrangler dev
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Publish
|
|
340
|
+
|
|
341
|
+
Deploy to Cloudflare. That's all!
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
$ wrangler publish
|
|
345
|
+
```
|
|
346
|
+
|
|
175
347
|
## Related projects
|
|
176
348
|
|
|
177
|
-
-
|
|
349
|
+
Implementation of the router is inspired by [goblin](https://github.com/bmf-san/goblin). API design is inspired by [express](https://github.com/expressjs/express) and [koa](https://github.com/koajs/koa). [itty-router](https://github.com/kwhitley/itty-router) and [Sunder](https://github.com/SunderJS/sunder) are the other routers or frameworks for Cloudflare Workers.
|
|
350
|
+
|
|
178
351
|
- express <https://github.com/expressjs/express>
|
|
179
|
-
-
|
|
352
|
+
- koa <https://github.com/koajs/koa>
|
|
180
353
|
- itty-router <https://github.com/kwhitley/itty-router>
|
|
181
354
|
- Sunder <https://github.com/SunderJS/sunder>
|
|
182
355
|
- goblin <https://github.com/bmf-san/goblin>
|
|
183
356
|
|
|
357
|
+
## Contributing
|
|
358
|
+
|
|
359
|
+
Contributions Welcome! You can contribute by the following way:
|
|
360
|
+
|
|
361
|
+
- Write or fix documents
|
|
362
|
+
- Write code of middleware
|
|
363
|
+
- Fix bugs
|
|
364
|
+
- Refactor the code
|
|
365
|
+
- etc.
|
|
366
|
+
|
|
367
|
+
If you can, let's make Hono together!
|
|
368
|
+
|
|
184
369
|
## Author
|
|
185
370
|
|
|
186
371
|
Yusuke Wada <https://github.com/yusukebe>
|
|
187
372
|
|
|
188
373
|
## License
|
|
189
374
|
|
|
190
|
-
MIT
|
|
375
|
+
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const compose: (middleware: any) => (context: any, next?: Function) => any;
|
package/dist/compose.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compose = void 0;
|
|
4
|
+
// Based on the code in the MIT licensed `koa-compose` package.
|
|
5
|
+
const compose = (middleware) => {
|
|
6
|
+
return function (context, next) {
|
|
7
|
+
let index = -1;
|
|
8
|
+
return dispatch(0);
|
|
9
|
+
function dispatch(i) {
|
|
10
|
+
if (i <= index)
|
|
11
|
+
return Promise.reject(new Error('next() called multiple times'));
|
|
12
|
+
index = i;
|
|
13
|
+
let fn = middleware[i];
|
|
14
|
+
if (i === middleware.length)
|
|
15
|
+
fn = next;
|
|
16
|
+
if (!fn)
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
try {
|
|
19
|
+
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return Promise.reject(err);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.compose = compose;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class Context {
|
|
2
|
+
req: Request;
|
|
3
|
+
res: Response;
|
|
4
|
+
constructor(req: Request, res: Response);
|
|
5
|
+
newResponse(body?: BodyInit | null | undefined, init?: ResponseInit | undefined): Response;
|
|
6
|
+
text(body: string): Response;
|
|
7
|
+
json(object: object, replacer?: (string | number)[], space?: string | number): Response;
|
|
8
|
+
html(body: string): Response;
|
|
9
|
+
}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Context = void 0;
|
|
4
|
+
class Context {
|
|
5
|
+
constructor(req, res) {
|
|
6
|
+
this.req = req;
|
|
7
|
+
this.res = res;
|
|
8
|
+
}
|
|
9
|
+
newResponse(body, init) {
|
|
10
|
+
return new Response(body, init);
|
|
11
|
+
}
|
|
12
|
+
text(body) {
|
|
13
|
+
if (typeof body !== 'string') {
|
|
14
|
+
throw new TypeError('text method arg must be a string!');
|
|
15
|
+
}
|
|
16
|
+
return this.newResponse(body, {
|
|
17
|
+
status: 200,
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'text/plain',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
json(object, replacer, space) {
|
|
24
|
+
if (typeof object !== 'object') {
|
|
25
|
+
throw new TypeError('json method arg must be a object!');
|
|
26
|
+
}
|
|
27
|
+
const body = JSON.stringify(object, replacer, space);
|
|
28
|
+
return this.newResponse(body, {
|
|
29
|
+
status: 200,
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json; charset=UTF-8',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
html(body) {
|
|
36
|
+
if (typeof body !== 'string') {
|
|
37
|
+
throw new TypeError('html method arg must be a string!');
|
|
38
|
+
}
|
|
39
|
+
return this.newResponse(body, {
|
|
40
|
+
status: 200,
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.Context = Context;
|
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import type { Result } from './node';
|
|
3
|
+
import { Node } from './node';
|
|
4
|
+
import { Middleware } from './middleware';
|
|
5
|
+
import { Context } from './context';
|
|
6
|
+
export { Middleware };
|
|
7
|
+
declare global {
|
|
8
|
+
interface Request {
|
|
9
|
+
params: (key: string) => any;
|
|
10
|
+
query: (key: string) => string | null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
|
|
14
|
+
declare type MiddlwareHandler = (c: Context, next: Function) => Promise<void>;
|
|
15
|
+
export declare class Router<T> {
|
|
16
|
+
node: Node<T>;
|
|
17
|
+
constructor();
|
|
18
|
+
add(method: string, path: string, handler: T): void;
|
|
19
|
+
match(method: string, path: string): Result<T> | null;
|
|
20
|
+
}
|
|
21
|
+
export declare class Hono {
|
|
22
|
+
router: Router<Handler[]>;
|
|
23
|
+
middlewareRouters: Router<MiddlwareHandler>[];
|
|
24
|
+
tempPath: string;
|
|
25
|
+
constructor();
|
|
26
|
+
get(arg: string | Handler, ...args: Handler[]): Hono;
|
|
27
|
+
post(arg: string | Handler, ...args: Handler[]): Hono;
|
|
28
|
+
put(arg: string | Handler, ...args: Handler[]): Hono;
|
|
29
|
+
head(arg: string | Handler, ...args: Handler[]): Hono;
|
|
30
|
+
delete(arg: string | Handler, ...args: Handler[]): Hono;
|
|
31
|
+
options(arg: string | Handler, ...args: Handler[]): Hono;
|
|
32
|
+
patch(arg: string | Handler, ...args: Handler[]): Hono;
|
|
33
|
+
all(arg: string | Handler, ...args: Handler[]): Hono;
|
|
34
|
+
route(path: string): Hono;
|
|
35
|
+
use(path: string, middleware: MiddlwareHandler): void;
|
|
36
|
+
addRoute(method: string, arg: string | Handler, ...args: Handler[]): Hono;
|
|
37
|
+
matchRoute(method: string, path: string): Promise<Result<Handler[]>>;
|
|
38
|
+
dispatch(request: Request, response?: Response): Promise<Response>;
|
|
39
|
+
handleEvent(event: FetchEvent): Promise<Response>;
|
|
40
|
+
fire(): void;
|
|
41
|
+
notFound(): Response;
|
|
42
|
+
}
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Hono = exports.Router = exports.Middleware = void 0;
|
|
4
|
+
const node_1 = require("./node");
|
|
5
|
+
const compose_1 = require("./compose");
|
|
6
|
+
const util_1 = require("./util");
|
|
7
|
+
const middleware_1 = require("./middleware");
|
|
8
|
+
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_1.Middleware; } });
|
|
9
|
+
const context_1 = require("./context");
|
|
10
|
+
const METHOD_NAME_OF_ALL = 'ALL';
|
|
11
|
+
class Router {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.node = new node_1.Node();
|
|
14
|
+
}
|
|
15
|
+
add(method, path, handler) {
|
|
16
|
+
this.node.insert(method, path, handler);
|
|
17
|
+
}
|
|
18
|
+
match(method, path) {
|
|
19
|
+
return this.node.search(method, path);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.Router = Router;
|
|
23
|
+
class Hono {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.router = new Router();
|
|
26
|
+
this.middlewareRouters = [];
|
|
27
|
+
this.tempPath = '/';
|
|
28
|
+
}
|
|
29
|
+
/* HTTP METHODS */
|
|
30
|
+
get(arg, ...args) {
|
|
31
|
+
return this.addRoute('get', arg, ...args);
|
|
32
|
+
}
|
|
33
|
+
post(arg, ...args) {
|
|
34
|
+
return this.addRoute('post', arg, ...args);
|
|
35
|
+
}
|
|
36
|
+
put(arg, ...args) {
|
|
37
|
+
return this.addRoute('put', arg, ...args);
|
|
38
|
+
}
|
|
39
|
+
head(arg, ...args) {
|
|
40
|
+
return this.addRoute('head', arg, ...args);
|
|
41
|
+
}
|
|
42
|
+
delete(arg, ...args) {
|
|
43
|
+
return this.addRoute('delete', arg, ...args);
|
|
44
|
+
}
|
|
45
|
+
options(arg, ...args) {
|
|
46
|
+
return this.addRoute('options', arg, ...args);
|
|
47
|
+
}
|
|
48
|
+
patch(arg, ...args) {
|
|
49
|
+
return this.addRoute('patch', arg, ...args);
|
|
50
|
+
}
|
|
51
|
+
/*
|
|
52
|
+
trace
|
|
53
|
+
copy
|
|
54
|
+
lock
|
|
55
|
+
purge
|
|
56
|
+
unlock
|
|
57
|
+
report
|
|
58
|
+
checkout
|
|
59
|
+
merge
|
|
60
|
+
notify
|
|
61
|
+
subscribe
|
|
62
|
+
unsubscribe
|
|
63
|
+
search
|
|
64
|
+
connect
|
|
65
|
+
*/
|
|
66
|
+
all(arg, ...args) {
|
|
67
|
+
return this.addRoute('all', arg, ...args);
|
|
68
|
+
}
|
|
69
|
+
route(path) {
|
|
70
|
+
this.tempPath = path;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
use(path, middleware) {
|
|
74
|
+
if (middleware.constructor.name !== 'AsyncFunction') {
|
|
75
|
+
throw new TypeError('middleware must be a async function!');
|
|
76
|
+
}
|
|
77
|
+
const router = new Router();
|
|
78
|
+
router.add(METHOD_NAME_OF_ALL, path, middleware);
|
|
79
|
+
this.middlewareRouters.push(router);
|
|
80
|
+
}
|
|
81
|
+
// addRoute('get', '/', handler)
|
|
82
|
+
addRoute(method, arg, ...args) {
|
|
83
|
+
method = method.toUpperCase();
|
|
84
|
+
if (typeof arg === 'string') {
|
|
85
|
+
this.tempPath = arg;
|
|
86
|
+
this.router.add(method, arg, args);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
args.unshift(arg);
|
|
90
|
+
this.router.add(method, this.tempPath, args);
|
|
91
|
+
}
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
async matchRoute(method, path) {
|
|
95
|
+
return this.router.match(method, path);
|
|
96
|
+
}
|
|
97
|
+
async dispatch(request, response) {
|
|
98
|
+
const [method, path] = [request.method, (0, util_1.getPathFromURL)(request.url)];
|
|
99
|
+
const result = await this.matchRoute(method, path);
|
|
100
|
+
request.params = (key) => {
|
|
101
|
+
if (result) {
|
|
102
|
+
return result.params[key];
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
};
|
|
106
|
+
const handler = result ? result.handler[0] : this.notFound; // XXX
|
|
107
|
+
const middleware = [];
|
|
108
|
+
for (const mr of this.middlewareRouters) {
|
|
109
|
+
const mwResult = mr.match(METHOD_NAME_OF_ALL, path);
|
|
110
|
+
if (mwResult) {
|
|
111
|
+
middleware.push(mwResult.handler);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const wrappedHandler = async (context, next) => {
|
|
115
|
+
context.res = await handler(context);
|
|
116
|
+
await next();
|
|
117
|
+
};
|
|
118
|
+
middleware.push(middleware_1.Middleware.defaultFilter);
|
|
119
|
+
middleware.push(wrappedHandler);
|
|
120
|
+
const composed = (0, compose_1.compose)(middleware);
|
|
121
|
+
const c = new context_1.Context(request, response);
|
|
122
|
+
await composed(c);
|
|
123
|
+
return c.res;
|
|
124
|
+
}
|
|
125
|
+
async handleEvent(event) {
|
|
126
|
+
return this.dispatch(event.request);
|
|
127
|
+
}
|
|
128
|
+
fire() {
|
|
129
|
+
addEventListener('fetch', (event) => {
|
|
130
|
+
event.respondWith(this.handleEvent(event));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
notFound() {
|
|
134
|
+
return new Response('Not Found', { status: 404 });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.Hono = Hono;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Context = exports.Middleware = exports.Hono = void 0;
|
|
4
|
+
var hono_1 = require("./hono");
|
|
5
|
+
Object.defineProperty(exports, "Hono", { enumerable: true, get: function () { return hono_1.Hono; } });
|
|
6
|
+
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return hono_1.Middleware; } });
|
|
7
|
+
var context_1 = require("./context");
|
|
8
|
+
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.basicAuth = void 0;
|
|
4
|
+
const util_1 = require("../../util");
|
|
5
|
+
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
|
|
6
|
+
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
|
|
7
|
+
const auth = (req) => {
|
|
8
|
+
if (!req) {
|
|
9
|
+
throw new TypeError('argument req is required');
|
|
10
|
+
}
|
|
11
|
+
if (typeof req !== 'object') {
|
|
12
|
+
throw new TypeError('argument req is required to be an object');
|
|
13
|
+
}
|
|
14
|
+
if (!req.headers || typeof req.headers !== 'object') {
|
|
15
|
+
throw new TypeError('argument req is required to have headers property');
|
|
16
|
+
}
|
|
17
|
+
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization'));
|
|
18
|
+
if (!match) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1]));
|
|
22
|
+
if (!userPass) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return { username: userPass[1], password: userPass[2] };
|
|
26
|
+
};
|
|
27
|
+
function decodeBase64(str) {
|
|
28
|
+
return Buffer.from(str, 'base64').toString();
|
|
29
|
+
}
|
|
30
|
+
const basicAuth = (options) => {
|
|
31
|
+
if (!options.realm) {
|
|
32
|
+
options.realm = 'Secure Area';
|
|
33
|
+
}
|
|
34
|
+
return async (ctx, next) => {
|
|
35
|
+
const user = auth(ctx.req);
|
|
36
|
+
const usernameEqual = user && await (0, util_1.timingSafeEqual)(options.username, user.username);
|
|
37
|
+
const passwordEqual = user && await (0, util_1.timingSafeEqual)(options.password, user.password);
|
|
38
|
+
if (!user || !usernameEqual || !passwordEqual) {
|
|
39
|
+
ctx.res = new Response('Unauthorized', {
|
|
40
|
+
status: 401,
|
|
41
|
+
headers: {
|
|
42
|
+
'WWW-Authenticate': 'Basic realm="' + options.realm.replace(/"/g, '\\"') + '"',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
exports.basicAuth = basicAuth;
|