hono 0.0.12 → 0.0.16
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 +96 -38
- package/dist/context.d.ts +18 -4
- package/dist/context.js +38 -20
- package/dist/hono.d.ts +10 -8
- package/dist/hono.js +16 -6
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -1
- package/dist/middleware/body-parse/body-parse.d.ts +2 -0
- package/dist/middleware/body-parse/body-parse.js +27 -0
- package/dist/middleware/cors/cors.d.ts +11 -0
- package/dist/middleware/cors/cors.js +56 -0
- package/dist/middleware/default.d.ts +2 -0
- package/dist/middleware/default.js +28 -0
- package/dist/middleware/{poweredBy/poweredBy.d.ts → powered-by/powered-by.d.ts} +0 -0
- package/dist/middleware/{poweredBy/poweredBy.js → powered-by/powered-by.js} +0 -0
- package/dist/middleware.d.ts +10 -1
- package/dist/middleware.js +8 -4
- package/dist/node.js +2 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +11 -2
- package/package.json +5 -5
- package/dist/middleware/defaultFilter.d.ts +0 -2
- package/dist/middleware/defaultFilter.js +0 -12
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Hono
|
|
2
2
|
|
|
3
|
-
Hono
|
|
3
|
+
Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web flamework for a Service Workers API based serverless such as Cloudflare Workers and Fastly Compute@Edge.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
|
-
|
|
6
|
+
import { Hono } from 'hono'
|
|
7
7
|
const app = new Hono()
|
|
8
8
|
|
|
9
9
|
app.get('/', (c) => c.text('Hono!!'))
|
|
@@ -11,26 +11,24 @@ app.get('/', (c) => c.text('Hono!!'))
|
|
|
11
11
|
app.fire()
|
|
12
12
|
```
|
|
13
13
|
|
|
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
|
-
|
|
16
14
|
## Features
|
|
17
15
|
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **Optimized** - for Cloudflare Workers or Fastly Compute@Edge.
|
|
16
|
+
- **Ultra fast** - the router is implemented with Trie-Tree structure.
|
|
17
|
+
- **Zero dependencies** - using only Web standard API.
|
|
18
|
+
- **Middleware** - builtin middleware, and you can make your own middleware.
|
|
19
|
+
- **Optimized** - for Cloudflare Workers.
|
|
23
20
|
|
|
24
21
|
## Benchmark
|
|
25
22
|
|
|
26
23
|
**Hono is fastest** compared to other routers for Cloudflare Workers.
|
|
27
24
|
|
|
28
|
-
```
|
|
29
|
-
hono x
|
|
30
|
-
itty-router x 158,
|
|
31
|
-
sunder x
|
|
25
|
+
```plain
|
|
26
|
+
hono x 748,188 ops/sec ±5.40% (77 runs sampled)
|
|
27
|
+
itty-router x 158,817 ops/sec ±3.62% (87 runs sampled)
|
|
28
|
+
sunder x 332,339 ops/sec ±1.11% (95 runs sampled)
|
|
29
|
+
worktop x 205,906 ops/sec ±4.43% (83 runs sampled)
|
|
32
30
|
Fastest is hono
|
|
33
|
-
✨ Done in
|
|
31
|
+
✨ Done in 52.79s.
|
|
34
32
|
```
|
|
35
33
|
|
|
36
34
|
## Hono in 1 minute
|
|
@@ -43,14 +41,14 @@ Below is a demonstration to create an application of Cloudflare Workers with Hon
|
|
|
43
41
|
|
|
44
42
|
You can install from npm registry:
|
|
45
43
|
|
|
46
|
-
```
|
|
47
|
-
|
|
44
|
+
```sh
|
|
45
|
+
yarn add hono
|
|
48
46
|
```
|
|
49
47
|
|
|
50
48
|
or
|
|
51
49
|
|
|
52
50
|
```sh
|
|
53
|
-
|
|
51
|
+
npm install hono
|
|
54
52
|
```
|
|
55
53
|
|
|
56
54
|
## Methods
|
|
@@ -62,6 +60,7 @@ Instance of `Hono` has these methods:
|
|
|
62
60
|
- app.**route**(path)
|
|
63
61
|
- app.**use**(path, middleware)
|
|
64
62
|
- app.**fire**()
|
|
63
|
+
- app.**fetch**(request, env, event)
|
|
65
64
|
|
|
66
65
|
## Routing
|
|
67
66
|
|
|
@@ -71,12 +70,12 @@ Instance of `Hono` has these methods:
|
|
|
71
70
|
|
|
72
71
|
```js
|
|
73
72
|
// HTTP Methods
|
|
74
|
-
app.get('/', () =>
|
|
75
|
-
app.post('/', () =>
|
|
73
|
+
app.get('/', (c) => c.text('GET /'))
|
|
74
|
+
app.post('/', (c) => c.text('POST /'))
|
|
76
75
|
|
|
77
76
|
// Wildcard
|
|
78
|
-
app.get('/wild/*/card', () => {
|
|
79
|
-
return
|
|
77
|
+
app.get('/wild/*/card', (c) => {
|
|
78
|
+
return c.text('GET /wild/*/card')
|
|
80
79
|
})
|
|
81
80
|
```
|
|
82
81
|
|
|
@@ -84,7 +83,7 @@ app.get('/wild/*/card', () => {
|
|
|
84
83
|
|
|
85
84
|
```js
|
|
86
85
|
// Any HTTP methods
|
|
87
|
-
app.all('/hello', () =>
|
|
86
|
+
app.all('/hello', (c) => c.text('Any Method /hello'))
|
|
88
87
|
```
|
|
89
88
|
|
|
90
89
|
### Named Parameter
|
|
@@ -118,9 +117,9 @@ app
|
|
|
118
117
|
## async/await
|
|
119
118
|
|
|
120
119
|
```js
|
|
121
|
-
app.get('/fetch-url', async () => {
|
|
120
|
+
app.get('/fetch-url', async (c) => {
|
|
122
121
|
const response = await fetch('https://example.com/')
|
|
123
|
-
return
|
|
122
|
+
return c.text(`Status is ${response.status}`)
|
|
124
123
|
})
|
|
125
124
|
```
|
|
126
125
|
|
|
@@ -129,10 +128,12 @@ app.get('/fetch-url', async () => {
|
|
|
129
128
|
### Builtin Middleware
|
|
130
129
|
|
|
131
130
|
```js
|
|
132
|
-
|
|
131
|
+
import { Hono, Middleware } from 'hono'
|
|
133
132
|
|
|
134
133
|
...
|
|
135
134
|
|
|
135
|
+
app.use('*', Middleware.poweredBy())
|
|
136
|
+
app.use('*', Middleware.logger())
|
|
136
137
|
app.use(
|
|
137
138
|
'/auth/*',
|
|
138
139
|
Middleware.basicAuth({
|
|
@@ -161,7 +162,7 @@ app.use('/message/*', async (c, next) => {
|
|
|
161
162
|
await c.res.headers.add('x-message', 'This is middleware!')
|
|
162
163
|
})
|
|
163
164
|
|
|
164
|
-
app.get('/message/hello', () => 'Hello Middleware!')
|
|
165
|
+
app.get('/message/hello', (c) => c.text('Hello Middleware!'))
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
### Custom 404 Response
|
|
@@ -177,6 +178,19 @@ app.use('*', async (c, next) => {
|
|
|
177
178
|
})
|
|
178
179
|
```
|
|
179
180
|
|
|
181
|
+
### Handling Error
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
app.use('*', async (c, next) => {
|
|
185
|
+
try {
|
|
186
|
+
await next()
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error(`${err}`)
|
|
189
|
+
c.res = new Response('Custom Error Message', { status: 500 })
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
180
194
|
### Complex Pattern
|
|
181
195
|
|
|
182
196
|
You can also do this:
|
|
@@ -235,6 +249,28 @@ app.use('/', (c, next) => {
|
|
|
235
249
|
})
|
|
236
250
|
```
|
|
237
251
|
|
|
252
|
+
### c.event
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
// FetchEvent object
|
|
256
|
+
app.use('*', async (c, next) => {
|
|
257
|
+
c.event.waitUntil(
|
|
258
|
+
...
|
|
259
|
+
)
|
|
260
|
+
await next()
|
|
261
|
+
})
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### c.env
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
// Environment object for Cloudflare Workers
|
|
268
|
+
app.get('*', async c => {
|
|
269
|
+
const counter = c.env.COUNTER
|
|
270
|
+
...
|
|
271
|
+
})
|
|
272
|
+
```
|
|
273
|
+
|
|
238
274
|
### c.text()
|
|
239
275
|
|
|
240
276
|
Render text as `Content-Type:text/plain`:
|
|
@@ -260,11 +296,20 @@ app.get('/api', (c) => {
|
|
|
260
296
|
Render HTML as `Content-Type:text/html`:
|
|
261
297
|
|
|
262
298
|
```js
|
|
263
|
-
app.get('/
|
|
299
|
+
app.get('/', (c) => {
|
|
264
300
|
return c.html('<h1>Hello! Hono!</h1>')
|
|
265
301
|
})
|
|
266
302
|
```
|
|
267
303
|
|
|
304
|
+
### c.redirect()
|
|
305
|
+
|
|
306
|
+
Redirect, default status code is `302`:
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
app.get('/redirect', (c) => c.redirect('/'))
|
|
310
|
+
app.get('/redirect-permanently', (c) => c.redirect('/', 301))
|
|
311
|
+
```
|
|
312
|
+
|
|
268
313
|
## fire
|
|
269
314
|
|
|
270
315
|
`app.fire()` do:
|
|
@@ -275,6 +320,18 @@ addEventListener('fetch', (event) => {
|
|
|
275
320
|
})
|
|
276
321
|
```
|
|
277
322
|
|
|
323
|
+
## fetch
|
|
324
|
+
|
|
325
|
+
`app.fetch()` is for Cloudflare Module Worker syntax.
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
export default {
|
|
329
|
+
fetch(request: Request, env: Env, event: FetchEvent) {
|
|
330
|
+
return app.fetch(request, env, event)
|
|
331
|
+
},
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
278
335
|
## Cloudflare Workers with Hono
|
|
279
336
|
|
|
280
337
|
Using `wrangler` or `miniflare`, you can develop the application locally and publish it with few commands.
|
|
@@ -286,7 +343,7 @@ Let's write your first code for Cloudflare Workers with Hono.
|
|
|
286
343
|
Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)"
|
|
287
344
|
|
|
288
345
|
```sh
|
|
289
|
-
|
|
346
|
+
npm i @cloudflare/wrangler -g
|
|
290
347
|
```
|
|
291
348
|
|
|
292
349
|
### 2. `npm init`
|
|
@@ -294,9 +351,9 @@ $ npm i @cloudflare/wrangler -g
|
|
|
294
351
|
Make npm skeleton directory.
|
|
295
352
|
|
|
296
353
|
```sh
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
354
|
+
mkdir hono-example
|
|
355
|
+
cd hono-example
|
|
356
|
+
npm init -y
|
|
300
357
|
```
|
|
301
358
|
|
|
302
359
|
### 3. `wrangler init`
|
|
@@ -304,15 +361,15 @@ $ npm init -y
|
|
|
304
361
|
Init as a wrangler project.
|
|
305
362
|
|
|
306
363
|
```sh
|
|
307
|
-
|
|
364
|
+
wrangler init
|
|
308
365
|
```
|
|
309
366
|
|
|
310
367
|
### 4. `npm install hono`
|
|
311
368
|
|
|
312
369
|
Install `hono` from npm registry.
|
|
313
370
|
|
|
314
|
-
```
|
|
315
|
-
|
|
371
|
+
```sh
|
|
372
|
+
npm i hono
|
|
316
373
|
```
|
|
317
374
|
|
|
318
375
|
### 5. Write your app
|
|
@@ -320,7 +377,7 @@ $ npm i hono
|
|
|
320
377
|
Only 4 lines!!
|
|
321
378
|
|
|
322
379
|
```js
|
|
323
|
-
|
|
380
|
+
import { Hono } from 'hono'
|
|
324
381
|
const app = new Hono()
|
|
325
382
|
|
|
326
383
|
app.get('/', (c) => c.text('Hello! Hono!'))
|
|
@@ -333,7 +390,7 @@ app.fire()
|
|
|
333
390
|
Run the development server locally. Then, access like `http://127.0.0.1:8787/` in your Web browser.
|
|
334
391
|
|
|
335
392
|
```sh
|
|
336
|
-
|
|
393
|
+
wrangler dev
|
|
337
394
|
```
|
|
338
395
|
|
|
339
396
|
### Publish
|
|
@@ -341,18 +398,19 @@ $ wrangler dev
|
|
|
341
398
|
Deploy to Cloudflare. That's all!
|
|
342
399
|
|
|
343
400
|
```sh
|
|
344
|
-
|
|
401
|
+
wrangler publish
|
|
345
402
|
```
|
|
346
403
|
|
|
347
404
|
## Related projects
|
|
348
405
|
|
|
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)
|
|
406
|
+
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), [Sunder](https://github.com/SunderJS/sunder), and [worktop](https://github.com/lukeed/worktop) are the other routers or frameworks for Cloudflare Workers.
|
|
350
407
|
|
|
351
408
|
- express <https://github.com/expressjs/express>
|
|
352
409
|
- koa <https://github.com/koajs/koa>
|
|
353
410
|
- itty-router <https://github.com/kwhitley/itty-router>
|
|
354
411
|
- Sunder <https://github.com/SunderJS/sunder>
|
|
355
412
|
- goblin <https://github.com/bmf-san/goblin>
|
|
413
|
+
- worktop <https://github.com/lukeed/worktop>
|
|
356
414
|
|
|
357
415
|
## Contributing
|
|
358
416
|
|
package/dist/context.d.ts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
declare type Headers = {
|
|
3
|
+
[key: string]: string;
|
|
4
|
+
};
|
|
5
|
+
export interface Env {
|
|
6
|
+
}
|
|
1
7
|
export declare class Context {
|
|
2
8
|
req: Request;
|
|
3
9
|
res: Response;
|
|
4
|
-
|
|
10
|
+
env: Env;
|
|
11
|
+
event: FetchEvent;
|
|
12
|
+
constructor(req: Request, opts?: {
|
|
13
|
+
res: Response;
|
|
14
|
+
env: Env;
|
|
15
|
+
event: FetchEvent;
|
|
16
|
+
});
|
|
5
17
|
newResponse(body?: BodyInit | null | undefined, init?: ResponseInit | undefined): Response;
|
|
6
|
-
text(
|
|
7
|
-
json(object: object,
|
|
8
|
-
html(
|
|
18
|
+
text(text: string, status?: number, headers?: Headers): Response;
|
|
19
|
+
json(object: object, status?: number, headers?: Headers): Response;
|
|
20
|
+
html(html: string, status?: number, headers?: Headers): Response;
|
|
21
|
+
redirect(location: string, status?: number): Response;
|
|
9
22
|
}
|
|
23
|
+
export {};
|
package/dist/context.js
CHANGED
|
@@ -1,45 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Context = void 0;
|
|
4
|
+
const util_1 = require("./util");
|
|
4
5
|
class Context {
|
|
5
|
-
constructor(req,
|
|
6
|
+
constructor(req, opts) {
|
|
6
7
|
this.req = req;
|
|
7
|
-
|
|
8
|
+
if (opts) {
|
|
9
|
+
this.res = opts.res;
|
|
10
|
+
this.env = opts.env;
|
|
11
|
+
this.event = opts.event;
|
|
12
|
+
}
|
|
8
13
|
}
|
|
9
14
|
newResponse(body, init) {
|
|
10
15
|
return new Response(body, init);
|
|
11
16
|
}
|
|
12
|
-
text(
|
|
13
|
-
if (typeof
|
|
17
|
+
text(text, status = 200, headers = {}) {
|
|
18
|
+
if (typeof text !== 'string') {
|
|
14
19
|
throw new TypeError('text method arg must be a string!');
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
+
headers['Content-Type'] = 'text/plain';
|
|
22
|
+
return this.newResponse(text, {
|
|
23
|
+
status: status,
|
|
24
|
+
headers: headers,
|
|
21
25
|
});
|
|
22
26
|
}
|
|
23
|
-
json(object,
|
|
27
|
+
json(object, status = 200, headers = {}) {
|
|
24
28
|
if (typeof object !== 'object') {
|
|
25
29
|
throw new TypeError('json method arg must be a object!');
|
|
26
30
|
}
|
|
27
|
-
const body = JSON.stringify(object
|
|
31
|
+
const body = JSON.stringify(object);
|
|
32
|
+
headers['Content-Type'] = 'application/json; charset=UTF-8';
|
|
28
33
|
return this.newResponse(body, {
|
|
29
|
-
status:
|
|
30
|
-
headers:
|
|
31
|
-
'Content-Type': 'application/json; charset=UTF-8',
|
|
32
|
-
},
|
|
34
|
+
status: status,
|
|
35
|
+
headers: headers,
|
|
33
36
|
});
|
|
34
37
|
}
|
|
35
|
-
html(
|
|
36
|
-
if (typeof
|
|
38
|
+
html(html, status = 200, headers = {}) {
|
|
39
|
+
if (typeof html !== 'string') {
|
|
37
40
|
throw new TypeError('html method arg must be a string!');
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
headers['Content-Type'] = 'text/html; charset=UTF-8';
|
|
43
|
+
return this.newResponse(html, {
|
|
44
|
+
status: status,
|
|
45
|
+
headers: headers,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
redirect(location, status = 302) {
|
|
49
|
+
if (typeof location !== 'string') {
|
|
50
|
+
throw new TypeError('location must be a string!');
|
|
51
|
+
}
|
|
52
|
+
if (!(0, util_1.isAbsoluteURL)(location)) {
|
|
53
|
+
const url = new URL(this.req.url);
|
|
54
|
+
url.pathname = location;
|
|
55
|
+
location = url.toString();
|
|
56
|
+
}
|
|
57
|
+
return this.newResponse(null, {
|
|
58
|
+
status: status,
|
|
41
59
|
headers: {
|
|
42
|
-
|
|
60
|
+
Location: location,
|
|
43
61
|
},
|
|
44
62
|
});
|
|
45
63
|
}
|
package/dist/hono.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
2
|
import type { Result } from './node';
|
|
3
3
|
import { Node } from './node';
|
|
4
|
-
import { Middleware } from './middleware';
|
|
5
4
|
import { Context } from './context';
|
|
6
|
-
|
|
5
|
+
import type { Env } from './context';
|
|
7
6
|
declare global {
|
|
8
7
|
interface Request {
|
|
9
|
-
params: (key: string) =>
|
|
8
|
+
params: (key: string) => string;
|
|
10
9
|
query: (key: string) => string | null;
|
|
10
|
+
parsedBody: any;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
|
|
14
|
-
declare type
|
|
13
|
+
export declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
|
|
14
|
+
export declare type MiddlewareHandler = (c: Context, next: Function) => Promise<void>;
|
|
15
15
|
export declare class Router<T> {
|
|
16
16
|
node: Node<T>;
|
|
17
17
|
constructor();
|
|
@@ -20,7 +20,7 @@ export declare class Router<T> {
|
|
|
20
20
|
}
|
|
21
21
|
export declare class Hono {
|
|
22
22
|
router: Router<Handler[]>;
|
|
23
|
-
middlewareRouters: Router<
|
|
23
|
+
middlewareRouters: Router<MiddlewareHandler>[];
|
|
24
24
|
tempPath: string;
|
|
25
25
|
constructor();
|
|
26
26
|
get(arg: string | Handler, ...args: Handler[]): Hono;
|
|
@@ -32,11 +32,13 @@ export declare class Hono {
|
|
|
32
32
|
patch(arg: string | Handler, ...args: Handler[]): Hono;
|
|
33
33
|
all(arg: string | Handler, ...args: Handler[]): Hono;
|
|
34
34
|
route(path: string): Hono;
|
|
35
|
-
use(path: string, middleware:
|
|
35
|
+
use(path: string, middleware: MiddlewareHandler): void;
|
|
36
36
|
addRoute(method: string, arg: string | Handler, ...args: Handler[]): Hono;
|
|
37
37
|
matchRoute(method: string, path: string): Promise<Result<Handler[]>>;
|
|
38
|
-
dispatch(request: Request,
|
|
38
|
+
dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
|
|
39
39
|
handleEvent(event: FetchEvent): Promise<Response>;
|
|
40
|
+
fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
|
|
40
41
|
fire(): void;
|
|
42
|
+
onError(err: any): Response;
|
|
41
43
|
notFound(): Response;
|
|
42
44
|
}
|
package/dist/hono.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Hono = exports.Router =
|
|
3
|
+
exports.Hono = exports.Router = void 0;
|
|
4
4
|
const node_1 = require("./node");
|
|
5
5
|
const compose_1 = require("./compose");
|
|
6
6
|
const util_1 = require("./util");
|
|
7
7
|
const middleware_1 = require("./middleware");
|
|
8
|
-
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_1.Middleware; } });
|
|
9
8
|
const context_1 = require("./context");
|
|
10
9
|
const METHOD_NAME_OF_ALL = 'ALL';
|
|
11
10
|
class Router {
|
|
@@ -94,7 +93,7 @@ class Hono {
|
|
|
94
93
|
async matchRoute(method, path) {
|
|
95
94
|
return this.router.match(method, path);
|
|
96
95
|
}
|
|
97
|
-
async dispatch(request,
|
|
96
|
+
async dispatch(request, env, event) {
|
|
98
97
|
const [method, path] = [request.method, (0, util_1.getPathFromURL)(request.url)];
|
|
99
98
|
const result = await this.matchRoute(method, path);
|
|
100
99
|
request.params = (key) => {
|
|
@@ -115,21 +114,32 @@ class Hono {
|
|
|
115
114
|
context.res = await handler(context);
|
|
116
115
|
await next();
|
|
117
116
|
};
|
|
118
|
-
middleware.push(middleware_1.Middleware.
|
|
117
|
+
middleware.push(middleware_1.Middleware.default);
|
|
119
118
|
middleware.push(wrappedHandler);
|
|
120
119
|
const composed = (0, compose_1.compose)(middleware);
|
|
121
|
-
const c = new context_1.Context(request,
|
|
120
|
+
const c = new context_1.Context(request, { env: env, event: event, res: null });
|
|
122
121
|
await composed(c);
|
|
123
122
|
return c.res;
|
|
124
123
|
}
|
|
125
124
|
async handleEvent(event) {
|
|
126
|
-
return this.dispatch(event.request)
|
|
125
|
+
return this.dispatch(event.request, {}, event).catch((err) => {
|
|
126
|
+
return this.onError(err);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async fetch(request, env, event) {
|
|
130
|
+
return this.dispatch(request, env, event).catch((err) => {
|
|
131
|
+
return this.onError(err);
|
|
132
|
+
});
|
|
127
133
|
}
|
|
128
134
|
fire() {
|
|
129
135
|
addEventListener('fetch', (event) => {
|
|
130
136
|
event.respondWith(this.handleEvent(event));
|
|
131
137
|
});
|
|
132
138
|
}
|
|
139
|
+
onError(err) {
|
|
140
|
+
console.error(err);
|
|
141
|
+
return new Response('Internal Server Error', { status: 500 });
|
|
142
|
+
}
|
|
133
143
|
notFound() {
|
|
134
144
|
return new Response('Not Found', { status: 404 });
|
|
135
145
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Context = exports.Middleware = exports.Hono = void 0;
|
|
4
4
|
var hono_1 = require("./hono");
|
|
5
5
|
Object.defineProperty(exports, "Hono", { enumerable: true, get: function () { return hono_1.Hono; } });
|
|
6
|
-
|
|
6
|
+
var middleware_1 = require("./middleware");
|
|
7
|
+
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_1.Middleware; } });
|
|
7
8
|
var context_1 = require("./context");
|
|
8
9
|
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bodyParse = void 0;
|
|
4
|
+
const bodyParse = () => {
|
|
5
|
+
return async (ctx, next) => {
|
|
6
|
+
const contentType = ctx.req.headers.get('Content-Type') || '';
|
|
7
|
+
if (contentType.includes('application/json')) {
|
|
8
|
+
ctx.req.parsedBody = await ctx.req.json();
|
|
9
|
+
}
|
|
10
|
+
else if (contentType.includes('application/text')) {
|
|
11
|
+
ctx.req.parsedBody = await ctx.req.text();
|
|
12
|
+
}
|
|
13
|
+
else if (contentType.includes('text/html')) {
|
|
14
|
+
ctx.req.parsedBody = await ctx.req.text();
|
|
15
|
+
}
|
|
16
|
+
else if (contentType.includes('form')) {
|
|
17
|
+
const form = {};
|
|
18
|
+
const data = [...(await ctx.req.formData())].reduce((acc, cur) => {
|
|
19
|
+
acc[cur[0]] = cur[1];
|
|
20
|
+
return acc;
|
|
21
|
+
}, form);
|
|
22
|
+
ctx.req.parsedBody = data;
|
|
23
|
+
}
|
|
24
|
+
await next();
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.bodyParse = bodyParse;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '../../context';
|
|
2
|
+
declare type CORSOptions = {
|
|
3
|
+
origin: string;
|
|
4
|
+
allowMethods?: string[];
|
|
5
|
+
allowHeaders?: string[];
|
|
6
|
+
maxAge?: number;
|
|
7
|
+
credentials?: boolean;
|
|
8
|
+
exposeHeaders?: string[];
|
|
9
|
+
};
|
|
10
|
+
export declare const cors: (options?: CORSOptions) => (c: Context, next: Function) => Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cors = void 0;
|
|
4
|
+
const cors = (options) => {
|
|
5
|
+
const defaults = {
|
|
6
|
+
origin: '*',
|
|
7
|
+
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
|
|
8
|
+
allowHeaders: [],
|
|
9
|
+
exposeHeaders: [],
|
|
10
|
+
};
|
|
11
|
+
const opts = Object.assign(Object.assign({}, defaults), options);
|
|
12
|
+
return async (c, next) => {
|
|
13
|
+
await next();
|
|
14
|
+
function set(key, value) {
|
|
15
|
+
c.res.headers.append(key, value);
|
|
16
|
+
}
|
|
17
|
+
set('Access-Control-Allow-Origin', opts.origin);
|
|
18
|
+
// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
|
|
19
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
|
20
|
+
if (opts.origin !== '*') {
|
|
21
|
+
set('Vary', 'Origin');
|
|
22
|
+
}
|
|
23
|
+
if (opts.credentials) {
|
|
24
|
+
set('Access-Control-Allow-Credentials', 'true');
|
|
25
|
+
}
|
|
26
|
+
if (opts.exposeHeaders.length) {
|
|
27
|
+
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','));
|
|
28
|
+
}
|
|
29
|
+
if (c.req.method === 'OPTIONS') {
|
|
30
|
+
// Preflight
|
|
31
|
+
if (opts.maxAge != null) {
|
|
32
|
+
set('Access-Control-Max-Age', opts.maxAge.toString());
|
|
33
|
+
}
|
|
34
|
+
if (opts.allowMethods.length) {
|
|
35
|
+
set('Access-Control-Allow-Methods', opts.allowMethods.join(','));
|
|
36
|
+
}
|
|
37
|
+
let headers = opts.allowHeaders;
|
|
38
|
+
if (!headers.length) {
|
|
39
|
+
const requestHeaders = c.req.headers.get('Access-Control-Request-Headers');
|
|
40
|
+
if (requestHeaders) {
|
|
41
|
+
headers = requestHeaders.split(/\s*,\s*/);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (headers.length) {
|
|
45
|
+
set('Access-Control-Allow-Headers', headers.join(','));
|
|
46
|
+
set('Vary', 'Access-Control-Request-Headers');
|
|
47
|
+
}
|
|
48
|
+
c.res = new Response(null, {
|
|
49
|
+
headers: c.res.headers,
|
|
50
|
+
status: 204,
|
|
51
|
+
statusText: c.res.statusText,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
exports.cors = cors;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultMiddleware = void 0;
|
|
4
|
+
const defaultMiddleware = async (c, next) => {
|
|
5
|
+
c.req.query = (key) => {
|
|
6
|
+
// eslint-disable-next-line
|
|
7
|
+
const url = new URL(c.req.url);
|
|
8
|
+
return url.searchParams.get(key);
|
|
9
|
+
};
|
|
10
|
+
await next();
|
|
11
|
+
/*
|
|
12
|
+
TODO:
|
|
13
|
+
Adding Content-Length header make it more slower.
|
|
14
|
+
This should not be default middleware...
|
|
15
|
+
if (c.res.body) {
|
|
16
|
+
// Do not clone Response, ex: c.res.clone().arrayBuffer()
|
|
17
|
+
const response = new Response(c.res.body, {
|
|
18
|
+
status: c.res.status,
|
|
19
|
+
statusText: c.res.statusText,
|
|
20
|
+
headers: c.res.headers,
|
|
21
|
+
})
|
|
22
|
+
c.res = response
|
|
23
|
+
const buff = await c.res.clone().arrayBuffer()
|
|
24
|
+
c.res.headers.append('Content-Length', buff.byteLength.toString())
|
|
25
|
+
}
|
|
26
|
+
*/
|
|
27
|
+
};
|
|
28
|
+
exports.defaultMiddleware = defaultMiddleware;
|
|
File without changes
|
|
File without changes
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare class Middleware {
|
|
2
|
-
static
|
|
2
|
+
static default: (c: import("./context").Context, next: Function) => Promise<void>;
|
|
3
3
|
static poweredBy: () => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
4
4
|
static logger: (fn?: {
|
|
5
5
|
(...data: any[]): void;
|
|
@@ -11,4 +11,13 @@ export declare class Middleware {
|
|
|
11
11
|
password: string;
|
|
12
12
|
realm?: string;
|
|
13
13
|
}) => (ctx: import("./context").Context, next: Function) => Promise<any>;
|
|
14
|
+
static bodyParse: () => (ctx: import("./context").Context, next: Function) => Promise<void>;
|
|
15
|
+
static cors: (options?: {
|
|
16
|
+
origin: string;
|
|
17
|
+
allowMethods?: string[];
|
|
18
|
+
allowHeaders?: string[];
|
|
19
|
+
maxAge?: number;
|
|
20
|
+
credentials?: boolean;
|
|
21
|
+
exposeHeaders?: string[];
|
|
22
|
+
}) => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
14
23
|
}
|
package/dist/middleware.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Middleware = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const default_1 = require("./middleware/default");
|
|
5
|
+
const powered_by_1 = require("./middleware/powered-by/powered-by");
|
|
6
6
|
const logger_1 = require("./middleware/logger/logger");
|
|
7
7
|
const basic_auth_1 = require("./middleware/basic-auth/basic-auth");
|
|
8
|
+
const body_parse_1 = require("./middleware/body-parse/body-parse");
|
|
9
|
+
const cors_1 = require("./middleware/cors/cors");
|
|
8
10
|
class Middleware {
|
|
9
11
|
}
|
|
10
12
|
exports.Middleware = Middleware;
|
|
11
|
-
Middleware.
|
|
12
|
-
Middleware.poweredBy =
|
|
13
|
+
Middleware.default = default_1.defaultMiddleware;
|
|
14
|
+
Middleware.poweredBy = powered_by_1.poweredBy;
|
|
13
15
|
Middleware.logger = logger_1.logger;
|
|
14
16
|
Middleware.basicAuth = basic_auth_1.basicAuth;
|
|
17
|
+
Middleware.bodyParse = body_parse_1.bodyParse;
|
|
18
|
+
Middleware.cors = cors_1.cors;
|
package/dist/node.js
CHANGED
|
@@ -46,7 +46,8 @@ class Node {
|
|
|
46
46
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
47
47
|
const p = parts[i];
|
|
48
48
|
// '*' => match any path
|
|
49
|
-
|
|
49
|
+
// /api/* => default wildcard match
|
|
50
|
+
if (curNode.children['*'] && !curNode.children[p]) {
|
|
50
51
|
const astNode = curNode.children['*'];
|
|
51
52
|
if (Object.keys(astNode.children).length === 0) {
|
|
52
53
|
curNode = astNode;
|
package/dist/util.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const splitPath: (path: string) => string[];
|
|
2
2
|
export declare const getPattern: (label: string) => string[] | null;
|
|
3
3
|
export declare const getPathFromURL: (url: string) => string;
|
|
4
|
+
export declare const isAbsoluteURL: (url: string) => boolean;
|
|
4
5
|
export declare const timingSafeEqual: (a: any, b: any) => Promise<boolean>;
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.timingSafeEqual = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
|
|
3
|
+
exports.timingSafeEqual = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
|
|
4
|
+
const URL_REGEXP = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|
4
5
|
const splitPath = (path) => {
|
|
5
6
|
const paths = path.split(/\//); // faster than path.split('/')
|
|
6
7
|
if (paths[0] === '') {
|
|
@@ -27,13 +28,21 @@ const getPattern = (label) => {
|
|
|
27
28
|
exports.getPattern = getPattern;
|
|
28
29
|
const getPathFromURL = (url) => {
|
|
29
30
|
// XXX
|
|
30
|
-
const match = url.match(
|
|
31
|
+
const match = url.match(URL_REGEXP);
|
|
31
32
|
if (match) {
|
|
32
33
|
return match[5];
|
|
33
34
|
}
|
|
34
35
|
return '';
|
|
35
36
|
};
|
|
36
37
|
exports.getPathFromURL = getPathFromURL;
|
|
38
|
+
const isAbsoluteURL = (url) => {
|
|
39
|
+
const match = url.match(URL_REGEXP);
|
|
40
|
+
if (match && match[1]) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
};
|
|
45
|
+
exports.isAbsoluteURL = isAbsoluteURL;
|
|
37
46
|
const bufferEqual = (a, b) => {
|
|
38
47
|
if (a === b) {
|
|
39
48
|
return true;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.16",
|
|
4
|
+
"description": "[炎] Ultrafast web framework for Cloudflare Workers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"@cloudflare/workers-types": "^3.3.0",
|
|
38
38
|
"@types/jest": "^27.4.0",
|
|
39
39
|
"@types/node": "^17.0.8",
|
|
40
|
-
"@types/service-worker-mock": "^2.0.1",
|
|
41
40
|
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
41
|
+
"@typescript-eslint/parser": "^5.9.0",
|
|
42
42
|
"eslint": "^7.26.0",
|
|
43
43
|
"eslint-config-prettier": "^8.1.0",
|
|
44
44
|
"eslint-define-config": "^1.2.1",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"eslint-plugin-import": "^2.20.2",
|
|
49
49
|
"eslint-plugin-node": "^11.1.0",
|
|
50
50
|
"eslint-plugin-prettier": "^4.0.0",
|
|
51
|
+
"form-data": "^4.0.0",
|
|
51
52
|
"jest": "^27.4.5",
|
|
53
|
+
"jest-environment-miniflare": "^2.0.0",
|
|
52
54
|
"rimraf": "^3.0.2",
|
|
53
|
-
"service-worker-mock": "^2.0.5",
|
|
54
55
|
"ts-jest": "^27.1.2",
|
|
55
|
-
"@typescript-eslint/parser": "^5.9.0",
|
|
56
56
|
"typescript": "^4.5.4"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defaultFilter = void 0;
|
|
4
|
-
const defaultFilter = async (c, next) => {
|
|
5
|
-
c.req.query = (key) => {
|
|
6
|
-
// eslint-disable-next-line
|
|
7
|
-
const url = new URL(c.req.url);
|
|
8
|
-
return url.searchParams.get(key);
|
|
9
|
-
};
|
|
10
|
-
await next();
|
|
11
|
-
};
|
|
12
|
-
exports.defaultFilter = defaultFilter;
|