hono 0.0.11 → 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 +105 -29
- package/dist/context.d.ts +9 -0
- package/dist/context.js +47 -0
- package/dist/hono.d.ts +3 -8
- package/dist/hono.js +6 -22
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- 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 +1 -1
- package/dist/middleware/defaultFilter.js +1 -0
- package/dist/middleware/logger/logger.d.ts +2 -1
- package/dist/middleware/logger/logger.js +3 -1
- package/dist/middleware/poweredBy/poweredBy.d.ts +1 -1
- package/dist/middleware.d.ts +9 -3
- package/dist/middleware.js +2 -0
- package/dist/node.js +3 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +28 -1
- package/package.json +17 -1
- package/dist/methods.d.ts +0 -1
- package/dist/methods.js +0 -31
package/README.md
CHANGED
|
@@ -6,24 +6,24 @@ 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
|
-
##
|
|
16
|
+
## Features
|
|
17
17
|
|
|
18
|
-
- Fast - the router is implemented with Trie-Tree structure.
|
|
19
|
-
-
|
|
20
|
-
- Flexible - you can make your own
|
|
21
|
-
- Easy - simple API, builtin middleware, and TypeScript
|
|
22
|
-
- Optimized - for Cloudflare Workers or Fastly Compute@Edge.
|
|
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.
|
|
23
23
|
|
|
24
24
|
## Benchmark
|
|
25
25
|
|
|
26
|
-
Hono is fastest
|
|
26
|
+
**Hono is fastest** compared to other routers for Cloudflare Workers.
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
hono x 758,264 ops/sec ±5.41% (75 runs sampled)
|
|
@@ -33,8 +33,16 @@ Fastest is hono
|
|
|
33
33
|
✨ Done in 42.84s.
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## Hono in 1 minute
|
|
37
|
+
|
|
38
|
+
Below is a demonstration to create an application of Cloudflare Workers with Hono.
|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
36
42
|
## Install
|
|
37
43
|
|
|
44
|
+
You can install from npm registry:
|
|
45
|
+
|
|
38
46
|
```
|
|
39
47
|
$ yarn add hono
|
|
40
48
|
```
|
|
@@ -47,10 +55,13 @@ $ npm install hono
|
|
|
47
55
|
|
|
48
56
|
## Methods
|
|
49
57
|
|
|
58
|
+
Instance of `Hono` has these methods:
|
|
59
|
+
|
|
50
60
|
- app.**HTTP_METHOD**(path, handler)
|
|
51
61
|
- app.**all**(path, handler)
|
|
52
62
|
- app.**route**(path)
|
|
53
63
|
- app.**use**(path, middleware)
|
|
64
|
+
- app.**fire**()
|
|
54
65
|
|
|
55
66
|
## Routing
|
|
56
67
|
|
|
@@ -104,7 +115,7 @@ app
|
|
|
104
115
|
.put(() => {...})
|
|
105
116
|
```
|
|
106
117
|
|
|
107
|
-
##
|
|
118
|
+
## async/await
|
|
108
119
|
|
|
109
120
|
```js
|
|
110
121
|
app.get('/fetch-url', async () => {
|
|
@@ -122,13 +133,21 @@ const { Hono, Middleware } = require('hono')
|
|
|
122
133
|
|
|
123
134
|
...
|
|
124
135
|
|
|
125
|
-
app.use(
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
app.use(
|
|
137
|
+
'/auth/*',
|
|
138
|
+
Middleware.basicAuth({
|
|
139
|
+
username: 'hono',
|
|
140
|
+
password: 'acoolproject',
|
|
141
|
+
})
|
|
142
|
+
)
|
|
128
143
|
```
|
|
129
144
|
|
|
145
|
+
Available builtin middleware are listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
|
|
146
|
+
|
|
130
147
|
### Custom Middleware
|
|
131
148
|
|
|
149
|
+
You can write your own middleware:
|
|
150
|
+
|
|
132
151
|
```js
|
|
133
152
|
// Custom logger
|
|
134
153
|
app.use('*', async (c, next) => {
|
|
@@ -136,7 +155,7 @@ app.use('*', async (c, next) => {
|
|
|
136
155
|
await next()
|
|
137
156
|
})
|
|
138
157
|
|
|
139
|
-
// Add custom header
|
|
158
|
+
// Add a custom header
|
|
140
159
|
app.use('/message/*', async (c, next) => {
|
|
141
160
|
await next()
|
|
142
161
|
await c.res.headers.add('x-message', 'This is middleware!')
|
|
@@ -147,6 +166,8 @@ app.get('/message/hello', () => 'Hello Middleware!')
|
|
|
147
166
|
|
|
148
167
|
### Custom 404 Response
|
|
149
168
|
|
|
169
|
+
You can customize 404 Not Found response:
|
|
170
|
+
|
|
150
171
|
```js
|
|
151
172
|
app.use('*', async (c, next) => {
|
|
152
173
|
await next()
|
|
@@ -158,6 +179,8 @@ app.use('*', async (c, next) => {
|
|
|
158
179
|
|
|
159
180
|
### Complex Pattern
|
|
160
181
|
|
|
182
|
+
You can also do this:
|
|
183
|
+
|
|
161
184
|
```js
|
|
162
185
|
// Output response time
|
|
163
186
|
app.use('*', async (c, next) => {
|
|
@@ -177,7 +200,9 @@ app.use('*', async (c, next) => {
|
|
|
177
200
|
|
|
178
201
|
## Context
|
|
179
202
|
|
|
180
|
-
|
|
203
|
+
To handle Request and Reponse easily, you can use Context object:
|
|
204
|
+
|
|
205
|
+
### c.req
|
|
181
206
|
|
|
182
207
|
```js
|
|
183
208
|
|
|
@@ -200,7 +225,7 @@ app.get('/entry/:id', (c) => {
|
|
|
200
225
|
})
|
|
201
226
|
```
|
|
202
227
|
|
|
203
|
-
### res
|
|
228
|
+
### c.res
|
|
204
229
|
|
|
205
230
|
```js
|
|
206
231
|
// Response object
|
|
@@ -210,7 +235,9 @@ app.use('/', (c, next) => {
|
|
|
210
235
|
})
|
|
211
236
|
```
|
|
212
237
|
|
|
213
|
-
### text
|
|
238
|
+
### c.text()
|
|
239
|
+
|
|
240
|
+
Render text as `Content-Type:text/plain`:
|
|
214
241
|
|
|
215
242
|
```js
|
|
216
243
|
app.get('/say', (c) => {
|
|
@@ -218,13 +245,41 @@ app.get('/say', (c) => {
|
|
|
218
245
|
})
|
|
219
246
|
```
|
|
220
247
|
|
|
221
|
-
|
|
248
|
+
### c.json()
|
|
222
249
|
|
|
223
|
-
|
|
250
|
+
Render JSON as `Content-Type:application/json`:
|
|
224
251
|
|
|
225
|
-
|
|
252
|
+
```js
|
|
253
|
+
app.get('/api', (c) => {
|
|
254
|
+
return c.json({ message: 'Hello!' })
|
|
255
|
+
})
|
|
256
|
+
```
|
|
226
257
|
|
|
227
|
-
|
|
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.
|
|
228
283
|
|
|
229
284
|
### 1. Install Wrangler
|
|
230
285
|
|
|
@@ -254,7 +309,7 @@ $ wrangler init
|
|
|
254
309
|
|
|
255
310
|
### 4. `npm install hono`
|
|
256
311
|
|
|
257
|
-
Install `hono` from npm
|
|
312
|
+
Install `hono` from npm registry.
|
|
258
313
|
|
|
259
314
|
```
|
|
260
315
|
$ npm i hono
|
|
@@ -262,38 +317,59 @@ $ npm i hono
|
|
|
262
317
|
|
|
263
318
|
### 5. Write your app
|
|
264
319
|
|
|
265
|
-
Only 4
|
|
320
|
+
Only 4 lines!!
|
|
266
321
|
|
|
267
322
|
```js
|
|
268
323
|
const { Hono } = require('hono')
|
|
269
324
|
const app = new Hono()
|
|
270
325
|
|
|
271
|
-
app.get('/', () =>
|
|
326
|
+
app.get('/', (c) => c.text('Hello! Hono!'))
|
|
272
327
|
|
|
273
328
|
app.fire()
|
|
274
329
|
```
|
|
275
330
|
|
|
276
|
-
### 6. Run
|
|
331
|
+
### 6. Run
|
|
277
332
|
|
|
278
|
-
Run the development server locally.
|
|
333
|
+
Run the development server locally. Then, access like `http://127.0.0.1:8787/` in your Web browser.
|
|
279
334
|
|
|
280
335
|
```sh
|
|
281
336
|
$ wrangler dev
|
|
282
337
|
```
|
|
283
338
|
|
|
339
|
+
### Publish
|
|
340
|
+
|
|
341
|
+
Deploy to Cloudflare. That's all!
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
$ wrangler publish
|
|
345
|
+
```
|
|
346
|
+
|
|
284
347
|
## Related projects
|
|
285
348
|
|
|
286
|
-
-
|
|
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
|
+
|
|
287
351
|
- express <https://github.com/expressjs/express>
|
|
288
|
-
-
|
|
352
|
+
- koa <https://github.com/koajs/koa>
|
|
289
353
|
- itty-router <https://github.com/kwhitley/itty-router>
|
|
290
354
|
- Sunder <https://github.com/SunderJS/sunder>
|
|
291
355
|
- goblin <https://github.com/bmf-san/goblin>
|
|
292
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
|
+
|
|
293
369
|
## Author
|
|
294
370
|
|
|
295
371
|
Yusuke Wada <https://github.com/yusukebe>
|
|
296
372
|
|
|
297
373
|
## License
|
|
298
374
|
|
|
299
|
-
MIT
|
|
375
|
+
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
|
|
@@ -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
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
import {
|
|
2
|
+
import type { Result } from './node';
|
|
3
|
+
import { Node } from './node';
|
|
3
4
|
import { Middleware } from './middleware';
|
|
5
|
+
import { Context } from './context';
|
|
4
6
|
export { Middleware };
|
|
5
7
|
declare global {
|
|
6
8
|
interface Request {
|
|
@@ -8,13 +10,6 @@ declare global {
|
|
|
8
10
|
query: (key: string) => string | null;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
|
-
export declare class Context {
|
|
12
|
-
req: Request;
|
|
13
|
-
res: Response;
|
|
14
|
-
constructor(req: Request, res: Response);
|
|
15
|
-
newResponse(body?: BodyInit | null | undefined, init?: ResponseInit | undefined): Response;
|
|
16
|
-
text(body: string): Response;
|
|
17
|
-
}
|
|
18
13
|
declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
|
|
19
14
|
declare type MiddlwareHandler = (c: Context, next: Function) => Promise<void>;
|
|
20
15
|
export declare class Router<T> {
|
package/dist/hono.js
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Hono = exports.Router = exports.
|
|
3
|
+
exports.Hono = exports.Router = exports.Middleware = 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
8
|
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_1.Middleware; } });
|
|
9
|
+
const context_1 = require("./context");
|
|
9
10
|
const METHOD_NAME_OF_ALL = 'ALL';
|
|
10
|
-
class Context {
|
|
11
|
-
constructor(req, res) {
|
|
12
|
-
this.req = req;
|
|
13
|
-
this.res = res;
|
|
14
|
-
}
|
|
15
|
-
newResponse(body, init) {
|
|
16
|
-
return new Response(body, init);
|
|
17
|
-
}
|
|
18
|
-
text(body) {
|
|
19
|
-
return this.newResponse(body, {
|
|
20
|
-
status: 200,
|
|
21
|
-
headers: {
|
|
22
|
-
'Content-Type': 'text/plain',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
exports.Context = Context;
|
|
28
11
|
class Router {
|
|
29
12
|
constructor() {
|
|
30
13
|
this.node = new node_1.Node();
|
|
@@ -99,6 +82,7 @@ class Hono {
|
|
|
99
82
|
addRoute(method, arg, ...args) {
|
|
100
83
|
method = method.toUpperCase();
|
|
101
84
|
if (typeof arg === 'string') {
|
|
85
|
+
this.tempPath = arg;
|
|
102
86
|
this.router.add(method, arg, args);
|
|
103
87
|
}
|
|
104
88
|
else {
|
|
@@ -119,7 +103,7 @@ class Hono {
|
|
|
119
103
|
}
|
|
120
104
|
return '';
|
|
121
105
|
};
|
|
122
|
-
|
|
106
|
+
const handler = result ? result.handler[0] : this.notFound; // XXX
|
|
123
107
|
const middleware = [];
|
|
124
108
|
for (const mr of this.middlewareRouters) {
|
|
125
109
|
const mwResult = mr.match(METHOD_NAME_OF_ALL, path);
|
|
@@ -127,14 +111,14 @@ class Hono {
|
|
|
127
111
|
middleware.push(mwResult.handler);
|
|
128
112
|
}
|
|
129
113
|
}
|
|
130
|
-
|
|
114
|
+
const wrappedHandler = async (context, next) => {
|
|
131
115
|
context.res = await handler(context);
|
|
132
116
|
await next();
|
|
133
117
|
};
|
|
134
118
|
middleware.push(middleware_1.Middleware.defaultFilter);
|
|
135
119
|
middleware.push(wrappedHandler);
|
|
136
120
|
const composed = (0, compose_1.compose)(middleware);
|
|
137
|
-
const c = new Context(request, response);
|
|
121
|
+
const c = new context_1.Context(request, response);
|
|
138
122
|
await composed(c);
|
|
139
123
|
return c.res;
|
|
140
124
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { Hono, Middleware
|
|
1
|
+
export { Hono, Middleware } from './hono';
|
|
2
|
+
export { Context } from './context';
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,5 @@ 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
|
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return hono_1.Middleware; } });
|
|
7
|
-
|
|
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;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Context } from '../
|
|
1
|
+
import type { Context } from '../context';
|
|
2
2
|
export declare const defaultFilter: (c: Context, next: Function) => Promise<void>;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.defaultFilter = void 0;
|
|
4
4
|
const defaultFilter = async (c, next) => {
|
|
5
5
|
c.req.query = (key) => {
|
|
6
|
+
// eslint-disable-next-line
|
|
6
7
|
const url = new URL(c.req.url);
|
|
7
8
|
return url.searchParams.get(key);
|
|
8
9
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Context } from '../../
|
|
1
|
+
import type { Context } from '../../context';
|
|
2
2
|
export declare const logger: (fn?: {
|
|
3
3
|
(...data: any[]): void;
|
|
4
4
|
(...data: any[]): void;
|
|
5
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
5
6
|
}) => (c: Context, next: Function) => Promise<void>;
|
|
@@ -12,7 +12,9 @@ const humanize = (n, opts) => {
|
|
|
12
12
|
};
|
|
13
13
|
const time = (start) => {
|
|
14
14
|
const delta = Date.now() - start;
|
|
15
|
-
return humanize([
|
|
15
|
+
return humanize([
|
|
16
|
+
delta < 10000 ? delta + 'ms' : Math.round(delta / 1000) + 's',
|
|
17
|
+
]);
|
|
16
18
|
};
|
|
17
19
|
const LogPrefix = {
|
|
18
20
|
Outgoing: '-->',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Context } from '../../
|
|
1
|
+
import type { Context } from '../../context';
|
|
2
2
|
export declare const poweredBy: () => (c: Context, next: Function) => Promise<void>;
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
export declare class Middleware {
|
|
2
|
-
static defaultFilter: (c: import("./
|
|
3
|
-
static poweredBy: () => (c: import("./
|
|
2
|
+
static defaultFilter: (c: import("./context").Context, next: Function) => Promise<void>;
|
|
3
|
+
static poweredBy: () => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
4
4
|
static logger: (fn?: {
|
|
5
5
|
(...data: any[]): void;
|
|
6
6
|
(...data: any[]): void;
|
|
7
|
-
|
|
7
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
8
|
+
}) => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
9
|
+
static basicAuth: (options: {
|
|
10
|
+
username: string;
|
|
11
|
+
password: string;
|
|
12
|
+
realm?: string;
|
|
13
|
+
}) => (ctx: import("./context").Context, next: Function) => Promise<any>;
|
|
8
14
|
}
|
package/dist/middleware.js
CHANGED
|
@@ -4,9 +4,11 @@ exports.Middleware = void 0;
|
|
|
4
4
|
const defaultFilter_1 = require("./middleware/defaultFilter");
|
|
5
5
|
const poweredBy_1 = require("./middleware/poweredBy/poweredBy");
|
|
6
6
|
const logger_1 = require("./middleware/logger/logger");
|
|
7
|
+
const basic_auth_1 = require("./middleware/basic-auth/basic-auth");
|
|
7
8
|
class Middleware {
|
|
8
9
|
}
|
|
9
10
|
exports.Middleware = Middleware;
|
|
10
11
|
Middleware.defaultFilter = defaultFilter_1.defaultFilter;
|
|
11
12
|
Middleware.poweredBy = poweredBy_1.poweredBy;
|
|
12
13
|
Middleware.logger = logger_1.logger;
|
|
14
|
+
Middleware.basicAuth = basic_auth_1.basicAuth;
|
package/dist/node.js
CHANGED
|
@@ -23,6 +23,7 @@ class Node {
|
|
|
23
23
|
this.middlewares = [];
|
|
24
24
|
}
|
|
25
25
|
insert(method, path, handler) {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
26
27
|
let curNode = this;
|
|
27
28
|
const parts = (0, util_1.splitPath)(path);
|
|
28
29
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
@@ -38,9 +39,10 @@ class Node {
|
|
|
38
39
|
return curNode;
|
|
39
40
|
}
|
|
40
41
|
search(method, path) {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
41
43
|
let curNode = this;
|
|
42
44
|
const params = {};
|
|
43
|
-
|
|
45
|
+
const parts = (0, util_1.splitPath)(path);
|
|
44
46
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
45
47
|
const p = parts[i];
|
|
46
48
|
// '*' => match any path
|
package/dist/util.d.ts
CHANGED
package/dist/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
|
|
3
|
+
exports.timingSafeEqual = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
|
|
4
4
|
const splitPath = (path) => {
|
|
5
5
|
const paths = path.split(/\//); // faster than path.split('/')
|
|
6
6
|
if (paths[0] === '') {
|
|
@@ -34,3 +34,30 @@ const getPathFromURL = (url) => {
|
|
|
34
34
|
return '';
|
|
35
35
|
};
|
|
36
36
|
exports.getPathFromURL = getPathFromURL;
|
|
37
|
+
const bufferEqual = (a, b) => {
|
|
38
|
+
if (a === b) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (a.byteLength !== b.byteLength) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const va = new DataView(a);
|
|
45
|
+
const vb = new DataView(b);
|
|
46
|
+
let i = va.byteLength;
|
|
47
|
+
while (i--) {
|
|
48
|
+
if (va.getUint8(i) !== vb.getUint8(i)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
};
|
|
54
|
+
const timingSafeEqual = async (a, b) => {
|
|
55
|
+
const sa = await crypto.subtle.digest({
|
|
56
|
+
name: 'SHA-256',
|
|
57
|
+
}, new TextEncoder().encode(String(a)));
|
|
58
|
+
const sb = await crypto.subtle.digest({
|
|
59
|
+
name: 'SHA-256',
|
|
60
|
+
}, new TextEncoder().encode(String(b)));
|
|
61
|
+
return bufferEqual(sa, sb) && a === b;
|
|
62
|
+
};
|
|
63
|
+
exports.timingSafeEqual = timingSafeEqual;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Tiny web framework for Cloudflare Workers and others.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "jest",
|
|
12
|
+
"lint": "eslint --ext js,ts src .eslintrc.js test",
|
|
12
13
|
"build": "rimraf dist && tsc",
|
|
13
14
|
"watch": "tsc -w",
|
|
14
15
|
"prepublishOnly": "yarn build"
|
|
@@ -35,11 +36,26 @@
|
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@cloudflare/workers-types": "^3.3.0",
|
|
37
38
|
"@types/jest": "^27.4.0",
|
|
39
|
+
"@types/node": "^17.0.8",
|
|
38
40
|
"@types/service-worker-mock": "^2.0.1",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
42
|
+
"eslint": "^7.26.0",
|
|
43
|
+
"eslint-config-prettier": "^8.1.0",
|
|
44
|
+
"eslint-define-config": "^1.2.1",
|
|
45
|
+
"eslint-import-resolver-typescript": "^2.0.0",
|
|
46
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
47
|
+
"eslint-plugin-flowtype": "^5.7.2",
|
|
48
|
+
"eslint-plugin-import": "^2.20.2",
|
|
49
|
+
"eslint-plugin-node": "^11.1.0",
|
|
50
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
39
51
|
"jest": "^27.4.5",
|
|
40
52
|
"rimraf": "^3.0.2",
|
|
41
53
|
"service-worker-mock": "^2.0.5",
|
|
42
54
|
"ts-jest": "^27.1.2",
|
|
55
|
+
"@typescript-eslint/parser": "^5.9.0",
|
|
43
56
|
"typescript": "^4.5.4"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=11.0.0"
|
|
44
60
|
}
|
|
45
61
|
}
|
package/dist/methods.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const methods: string[];
|
package/dist/methods.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.methods = void 0;
|
|
4
|
-
exports.methods = [
|
|
5
|
-
'get',
|
|
6
|
-
'post',
|
|
7
|
-
'put',
|
|
8
|
-
'head',
|
|
9
|
-
'delete',
|
|
10
|
-
'options',
|
|
11
|
-
'trace',
|
|
12
|
-
'copy',
|
|
13
|
-
'lock',
|
|
14
|
-
'mkcol',
|
|
15
|
-
'move',
|
|
16
|
-
'patch',
|
|
17
|
-
'purge',
|
|
18
|
-
'propfind',
|
|
19
|
-
'proppatch',
|
|
20
|
-
'unlock',
|
|
21
|
-
'report',
|
|
22
|
-
'mkactivity',
|
|
23
|
-
'checkout',
|
|
24
|
-
'merge',
|
|
25
|
-
'm-search',
|
|
26
|
-
'notify',
|
|
27
|
-
'subscribe',
|
|
28
|
-
'unsubscribe',
|
|
29
|
-
'search',
|
|
30
|
-
'connect',
|
|
31
|
-
];
|