hono 0.5.0 → 0.5.3
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 +78 -48
- package/dist/compose.d.ts +1 -1
- package/dist/compose.js +18 -23
- package/dist/context.js +2 -2
- package/dist/hono.d.ts +6 -5
- package/dist/hono.js +4 -0
- package/dist/middleware/basic-auth/basic-auth.d.ts +1 -0
- package/dist/middleware/basic-auth/basic-auth.js +4 -3
- package/dist/middleware/body-parse/body-parse.js +2 -18
- package/dist/middleware/etag/etag.d.ts +6 -0
- package/dist/middleware/etag/etag.js +28 -0
- package/dist/utils/body.d.ts +1 -0
- package/dist/utils/body.js +24 -0
- package/dist/utils/buffer.d.ts +1 -3
- package/dist/utils/buffer.js +7 -46
- package/dist/utils/crypto.d.ts +10 -0
- package/dist/utils/crypto.js +58 -0
- package/dist/utils/mime.js +3 -3
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
# Hono
|
|
1
|
+
# Hono\[炎\]
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
<a href="https://github.com/yusukebe/hono/blob/master/README.md">English</a>
|
|
5
|
+
·
|
|
6
|
+
<a href="https://github.com/yusukebe/hono/blob/master/docs/README.ja.md">日本語</a>
|
|
7
|
+
</p>
|
|
2
8
|
|
|
3
9
|
[](https://github.com/yusukebe/hono/actions)
|
|
4
10
|
[](https://github.com/yusukebe/hono/blob/master/LICENSE)
|
|
@@ -8,7 +14,7 @@
|
|
|
8
14
|
[](https://github.com/yusukebe/hono/pulse)
|
|
9
15
|
[](https://github.com/yusukebe/hono/commits/master)
|
|
10
16
|
|
|
11
|
-
Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web framework for
|
|
17
|
+
Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web framework for Cloudflare Workers and Fastly Compute@Edge.
|
|
12
18
|
|
|
13
19
|
```js
|
|
14
20
|
import { Hono } from 'hono'
|
|
@@ -22,26 +28,26 @@ app.fire()
|
|
|
22
28
|
## Features
|
|
23
29
|
|
|
24
30
|
- **Ultrafast** - the router does not use linear loops.
|
|
25
|
-
- **Zero-dependencies** - using only Web standard API.
|
|
26
|
-
- **Middleware** -
|
|
31
|
+
- **Zero-dependencies** - using only Service Worker and Web standard API.
|
|
32
|
+
- **Middleware** - built-in middleware and ability to extend with your own middleware.
|
|
27
33
|
- **Optimized** - for Cloudflare Workers.
|
|
28
34
|
|
|
29
35
|
## Benchmark
|
|
30
36
|
|
|
31
|
-
**Hono is fastest
|
|
37
|
+
**Hono is fastest**, compared to other routers for Cloudflare Workers.
|
|
32
38
|
|
|
33
39
|
```plain
|
|
34
|
-
hono x
|
|
35
|
-
itty-router x
|
|
36
|
-
sunder x
|
|
37
|
-
worktop x
|
|
40
|
+
hono x 809,503 ops/sec ±6.94% (73 runs sampled)
|
|
41
|
+
itty-router x 157,310 ops/sec ±4.31% (87 runs sampled)
|
|
42
|
+
sunder x 328,350 ops/sec ±2.30% (95 runs sampled)
|
|
43
|
+
worktop x 209,758 ops/sec ±4.28% (83 runs sampled)
|
|
38
44
|
Fastest is hono
|
|
39
|
-
✨ Done in
|
|
45
|
+
✨ Done in 60.66s.
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
## Hono in 1 minute
|
|
43
49
|
|
|
44
|
-
A demonstration to create an application
|
|
50
|
+
A demonstration to create an application for Cloudflare Workers with Hono.
|
|
45
51
|
|
|
46
52
|

|
|
47
53
|
|
|
@@ -53,13 +59,13 @@ Now, the named path parameter has types.
|
|
|
53
59
|
|
|
54
60
|
You can install Hono from the npm registry.
|
|
55
61
|
|
|
56
|
-
```
|
|
62
|
+
```
|
|
57
63
|
$ yarn add hono
|
|
58
64
|
```
|
|
59
65
|
|
|
60
66
|
or
|
|
61
67
|
|
|
62
|
-
```
|
|
68
|
+
```
|
|
63
69
|
$ npm install hono
|
|
64
70
|
```
|
|
65
71
|
|
|
@@ -75,6 +81,7 @@ An instance of `Hono` has these methods.
|
|
|
75
81
|
- app.**onError**(err, handler)
|
|
76
82
|
- app.**fire**()
|
|
77
83
|
- app.**fetch**(request, env, event)
|
|
84
|
+
- app.**request**(path, option)
|
|
78
85
|
|
|
79
86
|
## Routing
|
|
80
87
|
|
|
@@ -147,7 +154,9 @@ app.get('/fetch-url', async (c) => {
|
|
|
147
154
|
|
|
148
155
|
## Middleware
|
|
149
156
|
|
|
150
|
-
###
|
|
157
|
+
### Built-in Middleware
|
|
158
|
+
|
|
159
|
+
Hono has built-in middleware.
|
|
151
160
|
|
|
152
161
|
```js
|
|
153
162
|
import { Hono } from 'hono'
|
|
@@ -168,7 +177,7 @@ app.use(
|
|
|
168
177
|
)
|
|
169
178
|
```
|
|
170
179
|
|
|
171
|
-
Available
|
|
180
|
+
Available built-in middleware is listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
|
|
172
181
|
|
|
173
182
|
### Custom Middleware
|
|
174
183
|
|
|
@@ -184,15 +193,13 @@ app.use('*', async (c, next) => {
|
|
|
184
193
|
// Add a custom header
|
|
185
194
|
app.use('/message/*', async (c, next) => {
|
|
186
195
|
await next()
|
|
187
|
-
|
|
196
|
+
c.header('x-message', 'This is middleware!')
|
|
188
197
|
})
|
|
189
198
|
|
|
190
199
|
app.get('/message/hello', (c) => c.text('Hello Middleware!'))
|
|
191
200
|
```
|
|
192
201
|
|
|
193
|
-
##
|
|
194
|
-
|
|
195
|
-
### Not Found
|
|
202
|
+
## Not Found
|
|
196
203
|
|
|
197
204
|
`app.notFound` for customizing Not Found Response.
|
|
198
205
|
|
|
@@ -202,7 +209,7 @@ app.notFound((c) => {
|
|
|
202
209
|
})
|
|
203
210
|
```
|
|
204
211
|
|
|
205
|
-
|
|
212
|
+
## Error Handling
|
|
206
213
|
|
|
207
214
|
`app.onError` handle the error and return the customized Response.
|
|
208
215
|
|
|
@@ -215,7 +222,7 @@ app.onError((err, c) => {
|
|
|
215
222
|
|
|
216
223
|
## Context
|
|
217
224
|
|
|
218
|
-
To handle Request and Reponse, you can use Context object.
|
|
225
|
+
To handle Request and Reponse, you can use `Context` object.
|
|
219
226
|
|
|
220
227
|
### c.req
|
|
221
228
|
|
|
@@ -275,7 +282,7 @@ new Response('Thank you for comming', {
|
|
|
275
282
|
|
|
276
283
|
### c.text()
|
|
277
284
|
|
|
278
|
-
Render
|
|
285
|
+
Render text as `Content-Type:text/plain`.
|
|
279
286
|
|
|
280
287
|
```js
|
|
281
288
|
app.get('/say', (c) => {
|
|
@@ -305,7 +312,7 @@ app.get('/', (c) => {
|
|
|
305
312
|
|
|
306
313
|
### c.notFound()
|
|
307
314
|
|
|
308
|
-
Return the `
|
|
315
|
+
Return the `Not Found` Response.
|
|
309
316
|
|
|
310
317
|
```js
|
|
311
318
|
app.get('/notfound', (c) => {
|
|
@@ -381,51 +388,74 @@ export default app
|
|
|
381
388
|
*/
|
|
382
389
|
```
|
|
383
390
|
|
|
391
|
+
## request
|
|
392
|
+
|
|
393
|
+
`request` is a useful method for testing.
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
test('GET /hello is ok', async () => {
|
|
397
|
+
const res = await app.request('http://localhost/hello')
|
|
398
|
+
expect(res.status).toBe(200)
|
|
399
|
+
})
|
|
400
|
+
```
|
|
401
|
+
|
|
384
402
|
## Cloudflare Workers with Hono
|
|
385
403
|
|
|
386
|
-
Using
|
|
404
|
+
Using [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/) or [Miniflare](https://miniflare.dev), you can develop the application locally and publish it with few commands.
|
|
387
405
|
|
|
388
406
|
Let's write your first code for Cloudflare Workers with Hono.
|
|
389
407
|
|
|
390
|
-
|
|
408
|
+
---
|
|
391
409
|
|
|
392
|
-
|
|
410
|
+
### Caution
|
|
393
411
|
|
|
394
|
-
|
|
395
|
-
$ npm i @cloudflare/wrangler -g
|
|
396
|
-
```
|
|
412
|
+
**Wrangler 1.x** does not support importing middleware. We recommend two ways:
|
|
397
413
|
|
|
398
|
-
|
|
414
|
+
1. Use [Wragler 2.0 Beta](https://github.com/cloudflare/wrangler2).
|
|
415
|
+
2. Build without webpack 4.x. For example, you can use esbuild. See [the starter template](https://github.com/yusukebe/hono-minimal).
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### 1. `npm init`
|
|
399
420
|
|
|
400
421
|
Make a npm skeleton directory.
|
|
401
422
|
|
|
402
|
-
```
|
|
403
|
-
mkdir hono-example
|
|
404
|
-
cd hono-example
|
|
405
|
-
npm init -y
|
|
423
|
+
```
|
|
424
|
+
$ mkdir hono-example
|
|
425
|
+
$ cd hono-example
|
|
426
|
+
$ npm init -y
|
|
406
427
|
```
|
|
407
428
|
|
|
408
|
-
###
|
|
429
|
+
### 2. `wrangler init`
|
|
409
430
|
|
|
410
|
-
|
|
431
|
+
Initialize as a wrangler project.
|
|
411
432
|
|
|
412
|
-
```
|
|
413
|
-
$ wrangler init
|
|
433
|
+
```
|
|
434
|
+
$ npx wrangler@beta init
|
|
414
435
|
```
|
|
415
436
|
|
|
416
|
-
|
|
437
|
+
Answer the questions. If you want, you can answer `y`.
|
|
438
|
+
|
|
439
|
+
```
|
|
440
|
+
Would you like to install wrangler into your package.json? (y/n) <--- n
|
|
441
|
+
Would you like to use TypeScript? (y/n) <--- n
|
|
442
|
+
Would you like to create a Worker at src/index.js? (y/n) <--- n
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### 3. `npm install hono`
|
|
417
446
|
|
|
418
447
|
Install `hono` from the npm registry.
|
|
419
448
|
|
|
420
|
-
```
|
|
449
|
+
```
|
|
421
450
|
$ npm i hono
|
|
422
451
|
```
|
|
423
452
|
|
|
424
|
-
###
|
|
453
|
+
### 4. Write your app
|
|
425
454
|
|
|
426
455
|
Only 4 lines!!
|
|
427
456
|
|
|
428
457
|
```js
|
|
458
|
+
// index.js
|
|
429
459
|
import { Hono } from 'hono'
|
|
430
460
|
const app = new Hono()
|
|
431
461
|
|
|
@@ -434,25 +464,25 @@ app.get('/', (c) => c.text('Hello! Hono!'))
|
|
|
434
464
|
app.fire()
|
|
435
465
|
```
|
|
436
466
|
|
|
437
|
-
###
|
|
467
|
+
### 5. Run
|
|
438
468
|
|
|
439
469
|
Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
|
|
440
470
|
|
|
441
|
-
```
|
|
442
|
-
$ wrangler dev
|
|
471
|
+
```
|
|
472
|
+
$ npx wrangler@beta dev index.js
|
|
443
473
|
```
|
|
444
474
|
|
|
445
|
-
###
|
|
475
|
+
### 6. Publish
|
|
446
476
|
|
|
447
477
|
Deploy to Cloudflare. That's all!
|
|
448
478
|
|
|
449
|
-
```
|
|
450
|
-
$ wrangler publish
|
|
479
|
+
```
|
|
480
|
+
$ npx wrangler@beta publish index.js
|
|
451
481
|
```
|
|
452
482
|
|
|
453
483
|
## Starter template
|
|
454
484
|
|
|
455
|
-
You can start making your
|
|
485
|
+
You can start making your Cloudflare Workers application with [the starter template](https://github.com/yusukebe/hono-minimal). It is really minimal using TypeScript, esbuild, and Miniflare.
|
|
456
486
|
|
|
457
487
|
To generate a project skelton, run this command.
|
|
458
488
|
|
package/dist/compose.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ErrorHandler } from './hono';
|
|
2
|
-
export declare const compose: <
|
|
2
|
+
export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler) => (context: C) => Promise<C>;
|
package/dist/compose.js
CHANGED
|
@@ -4,36 +4,31 @@ exports.compose = void 0;
|
|
|
4
4
|
const context_1 = require("./context");
|
|
5
5
|
// Based on the code in the MIT licensed `koa-compose` package.
|
|
6
6
|
const compose = (middleware, onError) => {
|
|
7
|
-
return function (context
|
|
7
|
+
return function (context) {
|
|
8
8
|
let index = -1;
|
|
9
9
|
return dispatch(0);
|
|
10
10
|
async function dispatch(i) {
|
|
11
|
-
if (i
|
|
11
|
+
if (i === middleware.length) {
|
|
12
|
+
return context;
|
|
13
|
+
}
|
|
14
|
+
if (i <= index) {
|
|
12
15
|
return Promise.reject(new Error('next() called multiple times'));
|
|
16
|
+
}
|
|
17
|
+
const handler = middleware[i];
|
|
13
18
|
index = i;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
fn = next;
|
|
17
|
-
if (!fn)
|
|
19
|
+
return Promise.resolve(handler(context, dispatch.bind(null, i + 1)))
|
|
20
|
+
.then(() => {
|
|
18
21
|
return context;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
})
|
|
23
|
+
.catch((err) => {
|
|
24
|
+
if (onError && context instanceof context_1.Context) {
|
|
25
|
+
context.res = onError(err, context);
|
|
22
26
|
return context;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
throw err;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
catch (err) {
|
|
35
|
-
return Promise.reject(err);
|
|
36
|
-
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
37
32
|
}
|
|
38
33
|
};
|
|
39
34
|
};
|
package/dist/context.js
CHANGED
|
@@ -35,8 +35,8 @@ class Context {
|
|
|
35
35
|
this._statusText = (0, http_status_1.getStatusText)(number);
|
|
36
36
|
}
|
|
37
37
|
newResponse(data, init = {}) {
|
|
38
|
-
init.status = init.status || this._status;
|
|
39
|
-
init.statusText = init.statusText || this._statusText;
|
|
38
|
+
init.status = init.status || this._status || 200;
|
|
39
|
+
init.statusText = init.statusText || this._statusText || (0, http_status_1.getStatusText)(init.status);
|
|
40
40
|
init.headers = Object.assign(Object.assign({}, this._headers), init.headers);
|
|
41
41
|
// Content-Length
|
|
42
42
|
let length = 0;
|
package/dist/hono.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
2
|
import { Context } from './context';
|
|
3
3
|
import type { Env } from './context';
|
|
4
|
-
import type {
|
|
4
|
+
import type { Router } from './router';
|
|
5
5
|
declare global {
|
|
6
6
|
interface Request<ParamKeyType = string> {
|
|
7
7
|
param: (key: ParamKeyType) => string;
|
|
@@ -26,8 +26,8 @@ export declare class Hono {
|
|
|
26
26
|
middlewareRouters: Router<MiddlewareHandler>[];
|
|
27
27
|
tempPath: string;
|
|
28
28
|
constructor(init?: Partial<Pick<Hono, 'routerClass' | 'strict'>>);
|
|
29
|
-
notFoundHandler
|
|
30
|
-
errorHandler
|
|
29
|
+
private notFoundHandler;
|
|
30
|
+
private errorHandler;
|
|
31
31
|
get<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
|
|
32
32
|
post<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
|
|
33
33
|
put<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
|
|
@@ -40,11 +40,12 @@ export declare class Hono {
|
|
|
40
40
|
use(path: string, middleware: MiddlewareHandler): void;
|
|
41
41
|
onError(handler: ErrorHandler): Hono;
|
|
42
42
|
notFound(handler: NotFoundHandler): Hono;
|
|
43
|
-
addRoute
|
|
44
|
-
matchRoute
|
|
43
|
+
private addRoute;
|
|
44
|
+
private matchRoute;
|
|
45
45
|
dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
|
|
46
46
|
handleEvent(event: FetchEvent): Promise<Response>;
|
|
47
47
|
fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
|
|
48
|
+
request(input: RequestInfo, requestInit?: RequestInit): Promise<Response>;
|
|
48
49
|
fire(): void;
|
|
49
50
|
}
|
|
50
51
|
export {};
|
package/dist/hono.js
CHANGED
|
@@ -128,6 +128,10 @@ class Hono {
|
|
|
128
128
|
async fetch(request, env, event) {
|
|
129
129
|
return this.dispatch(request, env, event);
|
|
130
130
|
}
|
|
131
|
+
request(input, requestInit) {
|
|
132
|
+
const req = new Request(input, requestInit);
|
|
133
|
+
return this.dispatch(req);
|
|
134
|
+
}
|
|
131
135
|
fire() {
|
|
132
136
|
addEventListener('fetch', (event) => {
|
|
133
137
|
event.respondWith(this.handleEvent(event));
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.basicAuth = void 0;
|
|
4
4
|
const buffer_1 = require("../../utils/buffer");
|
|
5
|
+
const crypto_1 = require("../../utils/crypto");
|
|
5
6
|
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
|
|
6
7
|
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
|
|
7
8
|
const auth = (req) => {
|
|
@@ -18,7 +19,7 @@ const auth = (req) => {
|
|
|
18
19
|
if (!match) {
|
|
19
20
|
return undefined;
|
|
20
21
|
}
|
|
21
|
-
const userPass = USER_PASS_REGEXP.exec((0,
|
|
22
|
+
const userPass = USER_PASS_REGEXP.exec((0, crypto_1.decodeBase64)(match[1]));
|
|
22
23
|
if (!userPass) {
|
|
23
24
|
return undefined;
|
|
24
25
|
}
|
|
@@ -36,8 +37,8 @@ const basicAuth = (options, ...users) => {
|
|
|
36
37
|
const requestUser = auth(ctx.req);
|
|
37
38
|
if (requestUser) {
|
|
38
39
|
for (const user of users) {
|
|
39
|
-
const usernameEqual = await (0, buffer_1.timingSafeEqual)(user.username, requestUser.username);
|
|
40
|
-
const passwordEqual = await (0, buffer_1.timingSafeEqual)(user.password, requestUser.password);
|
|
40
|
+
const usernameEqual = await (0, buffer_1.timingSafeEqual)(user.username, requestUser.username, options.hashFunction);
|
|
41
|
+
const passwordEqual = await (0, buffer_1.timingSafeEqual)(user.password, requestUser.password, options.hashFunction);
|
|
41
42
|
if (usernameEqual && passwordEqual) {
|
|
42
43
|
// Authorized OK
|
|
43
44
|
return next();
|
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.bodyParse = void 0;
|
|
4
|
+
const body_1 = require("../../utils/body");
|
|
4
5
|
const bodyParse = () => {
|
|
5
6
|
return async (ctx, next) => {
|
|
6
|
-
|
|
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
|
-
}
|
|
7
|
+
ctx.req.parsedBody = await (0, body_1.parseBody)(ctx.req);
|
|
24
8
|
await next();
|
|
25
9
|
};
|
|
26
10
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.etag = void 0;
|
|
4
|
+
const crypto_1 = require("../../utils/crypto");
|
|
5
|
+
const body_1 = require("../../utils/body");
|
|
6
|
+
const etag = (options = { weak: false }) => {
|
|
7
|
+
return async (c, next) => {
|
|
8
|
+
const ifNoneMatch = c.req.header('If-None-Match') || c.req.header('if-none-match');
|
|
9
|
+
await next();
|
|
10
|
+
const clone = c.res.clone();
|
|
11
|
+
const body = await (0, body_1.parseBody)(c.res);
|
|
12
|
+
const hash = await (0, crypto_1.sha1)(body);
|
|
13
|
+
const etag = options.weak ? `W/"${hash}"` : `"${hash}"`;
|
|
14
|
+
if (ifNoneMatch && ifNoneMatch === etag) {
|
|
15
|
+
await clone.blob(); // Force using body
|
|
16
|
+
c.res = new Response(null, {
|
|
17
|
+
status: 304,
|
|
18
|
+
statusText: 'Not Modified',
|
|
19
|
+
});
|
|
20
|
+
c.res.headers.delete('Content-Length');
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
c.res = new Response(clone.body, clone);
|
|
24
|
+
c.res.headers.append('ETag', etag);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
exports.etag = etag;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const parseBody: (r: Request | Response) => Promise<string | object | Record<string, string | File>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseBody = void 0;
|
|
4
|
+
const parseBody = async (r) => {
|
|
5
|
+
const contentType = r.headers.get('Content-Type') || '';
|
|
6
|
+
if (contentType.includes('application/json')) {
|
|
7
|
+
return await r.json();
|
|
8
|
+
}
|
|
9
|
+
else if (contentType.includes('application/text')) {
|
|
10
|
+
return r.text();
|
|
11
|
+
}
|
|
12
|
+
else if (contentType.startsWith('text')) {
|
|
13
|
+
return r.text();
|
|
14
|
+
}
|
|
15
|
+
else if (contentType.includes('form')) {
|
|
16
|
+
const form = {};
|
|
17
|
+
const data = [...(await r.formData())].reduce((acc, cur) => {
|
|
18
|
+
acc[cur[0]] = cur[1];
|
|
19
|
+
return acc;
|
|
20
|
+
}, form);
|
|
21
|
+
return data;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
exports.parseBody = parseBody;
|
package/dist/utils/buffer.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
1
|
export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const sha256: (a: string | object | boolean) => Promise<string>;
|
|
4
|
-
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean) => Promise<boolean>;
|
|
2
|
+
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
|
package/dist/utils/buffer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.timingSafeEqual = exports.
|
|
3
|
+
exports.timingSafeEqual = exports.equal = void 0;
|
|
4
|
+
const crypto_1 = require("./crypto");
|
|
4
5
|
const equal = (a, b) => {
|
|
5
6
|
if (a === b) {
|
|
6
7
|
return true;
|
|
@@ -19,52 +20,12 @@ const equal = (a, b) => {
|
|
|
19
20
|
return true;
|
|
20
21
|
};
|
|
21
22
|
exports.equal = equal;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const length = text.length;
|
|
26
|
-
const bytes = new Uint8Array(length);
|
|
27
|
-
for (let i = 0; i < length; i++) {
|
|
28
|
-
bytes[i] = text.charCodeAt(i);
|
|
29
|
-
}
|
|
30
|
-
const decoder = new TextDecoder();
|
|
31
|
-
return decoder.decode(bytes);
|
|
32
|
-
}
|
|
33
|
-
catch (_a) { }
|
|
34
|
-
try {
|
|
35
|
-
const { Buffer } = require('buffer');
|
|
36
|
-
return Buffer.from(str, 'base64').toString();
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
console.error('If you want to do "decodeBase64", polyfill "buffer" module.');
|
|
40
|
-
throw e;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
exports.decodeBase64 = decodeBase64;
|
|
44
|
-
const sha256 = async (a) => {
|
|
45
|
-
if (crypto && crypto.subtle) {
|
|
46
|
-
const buffer = await crypto.subtle.digest({
|
|
47
|
-
name: 'SHA-256',
|
|
48
|
-
}, new TextEncoder().encode(String(a)));
|
|
49
|
-
const hash = Array.prototype.map
|
|
50
|
-
.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
|
|
51
|
-
.join('');
|
|
52
|
-
return hash;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
const crypto = require('crypto');
|
|
56
|
-
const hash = crypto.createHash('sha256').update(a).digest('hex');
|
|
57
|
-
return hash;
|
|
23
|
+
const timingSafeEqual = async (a, b, hashFunction) => {
|
|
24
|
+
if (!hashFunction) {
|
|
25
|
+
hashFunction = crypto_1.sha256;
|
|
58
26
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw e;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
exports.sha256 = sha256;
|
|
65
|
-
const timingSafeEqual = async (a, b) => {
|
|
66
|
-
const sa = await (0, exports.sha256)(a);
|
|
67
|
-
const sb = await (0, exports.sha256)(b);
|
|
27
|
+
const sa = await hashFunction(a);
|
|
28
|
+
const sb = await hashFunction(b);
|
|
68
29
|
return sa === sb && a === b;
|
|
69
30
|
};
|
|
70
31
|
exports.timingSafeEqual = timingSafeEqual;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare type Algorithm = {
|
|
2
|
+
name: string;
|
|
3
|
+
alias: string;
|
|
4
|
+
};
|
|
5
|
+
declare type Data = string | object | boolean;
|
|
6
|
+
export declare const sha256: (data: Data) => Promise<string>;
|
|
7
|
+
export declare const sha1: (data: Data) => Promise<string>;
|
|
8
|
+
export declare const createHash: (data: Data, algorithm: Algorithm) => Promise<string>;
|
|
9
|
+
export declare const decodeBase64: (str: string) => any;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeBase64 = exports.createHash = exports.sha1 = exports.sha256 = void 0;
|
|
4
|
+
const sha256 = async (data) => {
|
|
5
|
+
const algorithm = { name: 'SHA-256', alias: 'sha256' };
|
|
6
|
+
const hash = await (0, exports.createHash)(data, algorithm);
|
|
7
|
+
return hash;
|
|
8
|
+
};
|
|
9
|
+
exports.sha256 = sha256;
|
|
10
|
+
const sha1 = async (data) => {
|
|
11
|
+
const algorithm = { name: 'SHA-1', alias: 'sha1' };
|
|
12
|
+
const hash = await (0, exports.createHash)(data, algorithm);
|
|
13
|
+
return hash;
|
|
14
|
+
};
|
|
15
|
+
exports.sha1 = sha1;
|
|
16
|
+
const createHash = async (data, algorithm) => {
|
|
17
|
+
if (crypto && crypto.subtle) {
|
|
18
|
+
const buffer = await crypto.subtle.digest({
|
|
19
|
+
name: algorithm.name,
|
|
20
|
+
}, new TextEncoder().encode(String(data)));
|
|
21
|
+
const hash = Array.prototype.map
|
|
22
|
+
.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
|
|
23
|
+
.join('');
|
|
24
|
+
return hash;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const crypto = require('crypto');
|
|
28
|
+
const hash = crypto.createHash(algorithm.alias).update(data).digest('hex');
|
|
29
|
+
return hash;
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
console.error(`If you want to create hash ${algorithm.name}, polyfill "crypto" module.`);
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
exports.createHash = createHash;
|
|
37
|
+
const decodeBase64 = (str) => {
|
|
38
|
+
try {
|
|
39
|
+
const text = atob(str);
|
|
40
|
+
const length = text.length;
|
|
41
|
+
const bytes = new Uint8Array(length);
|
|
42
|
+
for (let i = 0; i < length; i++) {
|
|
43
|
+
bytes[i] = text.charCodeAt(i);
|
|
44
|
+
}
|
|
45
|
+
const decoder = new TextDecoder();
|
|
46
|
+
return decoder.decode(bytes);
|
|
47
|
+
}
|
|
48
|
+
catch (_a) { }
|
|
49
|
+
try {
|
|
50
|
+
const { Buffer } = require('buffer');
|
|
51
|
+
return Buffer.from(str, 'base64').toString();
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.error('If you want to do "decodeBase64", polyfill "buffer" module.');
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
exports.decodeBase64 = decodeBase64;
|
package/dist/utils/mime.js
CHANGED
|
@@ -4,11 +4,10 @@ exports.getMimeType = void 0;
|
|
|
4
4
|
const getMimeType = (filename) => {
|
|
5
5
|
const regexp = /\.([a-zA-Z0-9]+?)$/;
|
|
6
6
|
const match = filename.match(regexp);
|
|
7
|
-
if (!match)
|
|
7
|
+
if (!match)
|
|
8
8
|
return;
|
|
9
|
-
}
|
|
10
9
|
let mimeType = mimes[match[1]];
|
|
11
|
-
if (mimeType.startsWith('text') || mimeType === 'application/json') {
|
|
10
|
+
if ((mimeType && mimeType.startsWith('text')) || mimeType === 'application/json') {
|
|
12
11
|
mimeType += '; charset=utf-8';
|
|
13
12
|
}
|
|
14
13
|
return mimeType;
|
|
@@ -43,6 +42,7 @@ const mimes = {
|
|
|
43
42
|
js: 'text/javascript',
|
|
44
43
|
json: 'application/json',
|
|
45
44
|
jsonld: 'application/ld+json',
|
|
45
|
+
map: 'application/json',
|
|
46
46
|
mid: 'audio/x-midi',
|
|
47
47
|
midi: 'audio/x-midi',
|
|
48
48
|
mjs: 'text/javascript',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "[炎] Ultrafast web framework for Cloudflare Workers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -78,11 +78,13 @@
|
|
|
78
78
|
],
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@cloudflare/workers-types": "^3.3.0",
|
|
81
|
+
"@types/crypto-js": "^4.1.1",
|
|
81
82
|
"@types/jest": "^27.4.0",
|
|
82
83
|
"@types/mustache": "^4.1.2",
|
|
83
84
|
"@types/node": "^17.0.8",
|
|
84
85
|
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
85
86
|
"@typescript-eslint/parser": "^5.9.0",
|
|
87
|
+
"crypto-js": "^4.1.1",
|
|
86
88
|
"eslint": "^7.26.0",
|
|
87
89
|
"eslint-config-prettier": "^8.3.0",
|
|
88
90
|
"eslint-define-config": "^1.2.1",
|
|
@@ -96,6 +98,7 @@
|
|
|
96
98
|
"jest-environment-miniflare": "^2.0.0",
|
|
97
99
|
"mustache": "^4.2.0",
|
|
98
100
|
"prettier": "^2.5.1",
|
|
101
|
+
"prettier-plugin-md-nocjsp": "^1.2.0",
|
|
99
102
|
"rimraf": "^3.0.2",
|
|
100
103
|
"ts-jest": "^27.1.2",
|
|
101
104
|
"typescript": "^4.5.5"
|