hono 0.0.9 → 0.0.10
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 +101 -8
- package/package.json +13 -1
- package/src/compose.js +1 -2
- package/src/compose.test.js +10 -10
- package/src/hono.d.ts +46 -27
- package/src/hono.js +8 -6
- package/src/hono.test.js +17 -13
- package/src/middleware/defaultFilter.js +3 -3
- package/src/middleware/poweredBy.js +3 -3
- package/.github/workflows/ci.yml +0 -24
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ app.get('/', () => new Response('Hono!!'))
|
|
|
11
11
|
app.fire()
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+

|
|
15
|
+
|
|
14
16
|
## Feature
|
|
15
17
|
|
|
16
18
|
- Fast - the router is implemented with Trie-Tree structure.
|
|
@@ -100,6 +102,15 @@ app
|
|
|
100
102
|
.put(() => {...})
|
|
101
103
|
```
|
|
102
104
|
|
|
105
|
+
## Async
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
app.get('/fetch-url', async () => {
|
|
109
|
+
const response = await fetch('https://example.com/')
|
|
110
|
+
return new Response(`Status is ${response.status}`)
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
103
114
|
## Middleware
|
|
104
115
|
|
|
105
116
|
### Builtin Middleware
|
|
@@ -116,14 +127,14 @@ app.use('*', Middleware.poweredBy)
|
|
|
116
127
|
### Custom Middleware
|
|
117
128
|
|
|
118
129
|
```js
|
|
119
|
-
const logger = (c, next) => {
|
|
130
|
+
const logger = async (c, next) => {
|
|
120
131
|
console.log(`[${c.req.method}] ${c.req.url}`)
|
|
121
|
-
next()
|
|
132
|
+
await next()
|
|
122
133
|
}
|
|
123
134
|
|
|
124
|
-
const addHeader = (c, next) => {
|
|
125
|
-
next()
|
|
126
|
-
c.res.headers.add('x-message', 'This is middleware!')
|
|
135
|
+
const addHeader = async (c, next) => {
|
|
136
|
+
await next()
|
|
137
|
+
await c.res.headers.add('x-message', 'This is middleware!')
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
app.use('*', logger)
|
|
@@ -135,8 +146,8 @@ app.get('/message/hello', () => 'Hello Middleware!')
|
|
|
135
146
|
### Custom 404 Response
|
|
136
147
|
|
|
137
148
|
```js
|
|
138
|
-
const customNotFound = (c, next) => {
|
|
139
|
-
next()
|
|
149
|
+
const customNotFound = async (c, next) => {
|
|
150
|
+
await next()
|
|
140
151
|
if (c.res.status === 404) {
|
|
141
152
|
c.res = new Response('Custom 404 Not Found', { status: 404 })
|
|
142
153
|
}
|
|
@@ -145,13 +156,32 @@ const customNotFound = (c, next) => {
|
|
|
145
156
|
app.use('*', customNotFound)
|
|
146
157
|
```
|
|
147
158
|
|
|
159
|
+
### Complex Pattern
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// Log response time
|
|
163
|
+
app.use('*', async (c, next) => {
|
|
164
|
+
await next()
|
|
165
|
+
const responseTime = await c.res.headers.get('X-Response-Time')
|
|
166
|
+
console.log(`X-Response-Time: ${responseTime}`)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// Add X-Response-Time header
|
|
170
|
+
app.use('*', async (c, next) => {
|
|
171
|
+
const start = Date.now()
|
|
172
|
+
await next()
|
|
173
|
+
const ms = Date.now() - start
|
|
174
|
+
await c.res.headers.append('X-Response-Time', `${ms}ms`)
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
148
178
|
## Context
|
|
149
179
|
|
|
150
180
|
### req
|
|
151
181
|
|
|
152
182
|
```js
|
|
153
183
|
app.get('/hello', (c) => {
|
|
154
|
-
const userAgent = c.req.headers('User-Agent')
|
|
184
|
+
const userAgent = c.req.headers.get('User-Agent')
|
|
155
185
|
...
|
|
156
186
|
})
|
|
157
187
|
```
|
|
@@ -185,6 +215,69 @@ app.get('/entry/:id', (c) => {
|
|
|
185
215
|
})
|
|
186
216
|
```
|
|
187
217
|
|
|
218
|
+
## Hono in 1 minute
|
|
219
|
+
|
|
220
|
+
Create your first Cloudflare Workers with Hono from scratch.
|
|
221
|
+
|
|
222
|
+
### Demo
|
|
223
|
+
|
|
224
|
+

|
|
225
|
+
|
|
226
|
+
### 1. Install Wrangler
|
|
227
|
+
|
|
228
|
+
Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)"
|
|
229
|
+
|
|
230
|
+
```sh
|
|
231
|
+
$ npm i @cloudflare/wrangler -g
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 2. `npm init`
|
|
235
|
+
|
|
236
|
+
Make npm skeleton directory.
|
|
237
|
+
|
|
238
|
+
```sh
|
|
239
|
+
$ mkdir hono-example
|
|
240
|
+
$ ch hono-example
|
|
241
|
+
$ npm init -y
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 3. `wrangler init`
|
|
245
|
+
|
|
246
|
+
Init as a wrangler project.
|
|
247
|
+
|
|
248
|
+
```sh
|
|
249
|
+
$ wrangler init
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 4. `npm install hono`
|
|
253
|
+
|
|
254
|
+
Install `hono` from npm repository.
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
$ npm i hono
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 5. Write your app
|
|
261
|
+
|
|
262
|
+
Only 4 line!!
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
const { Hono } = require('hono')
|
|
266
|
+
const app = new Hono()
|
|
267
|
+
|
|
268
|
+
app.get('/', () => new Response('Hello! Hono!'))
|
|
269
|
+
|
|
270
|
+
app.fire()
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### 6. Run!
|
|
274
|
+
|
|
275
|
+
Run the development server locally.
|
|
276
|
+
|
|
277
|
+
```sh
|
|
278
|
+
$ wrangler dev
|
|
279
|
+
```
|
|
280
|
+
|
|
188
281
|
## Related projects
|
|
189
282
|
|
|
190
283
|
- koa <https://github.com/koajs/koa>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Minimal web framework for Cloudflare Workers and Fastly Compute@Edge",
|
|
5
5
|
"main": "src/hono.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/yusukebe/hono.git"
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/yusukebe/hono",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"web",
|
|
18
|
+
"app",
|
|
19
|
+
"http",
|
|
20
|
+
"application",
|
|
21
|
+
"framework",
|
|
22
|
+
"router",
|
|
23
|
+
"cloudflare",
|
|
24
|
+
"workers",
|
|
25
|
+
"fastly",
|
|
26
|
+
"compute@edge"
|
|
27
|
+
],
|
|
16
28
|
"devDependencies": {
|
|
17
29
|
"jest": "^27.4.5",
|
|
18
30
|
"node-fetch": "^2.6.6"
|
package/src/compose.js
CHANGED
|
@@ -4,8 +4,7 @@ const compose = (middleware) => {
|
|
|
4
4
|
let index = -1
|
|
5
5
|
return dispatch(0)
|
|
6
6
|
function dispatch(i) {
|
|
7
|
-
if (i <= index)
|
|
8
|
-
return Promise.reject(new Error('next() called multiple times'))
|
|
7
|
+
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
|
|
9
8
|
index = i
|
|
10
9
|
let fn = middleware[i]
|
|
11
10
|
if (i === middleware.length) fn = next
|
package/src/compose.test.js
CHANGED
|
@@ -3,21 +3,21 @@ const compose = require('./compose')
|
|
|
3
3
|
describe('compose middleware', () => {
|
|
4
4
|
const middleware = []
|
|
5
5
|
|
|
6
|
-
const a = (c, next) => {
|
|
6
|
+
const a = async (c, next) => {
|
|
7
7
|
c.req['log'] = 'log'
|
|
8
|
-
next()
|
|
8
|
+
await next()
|
|
9
9
|
}
|
|
10
10
|
middleware.push(a)
|
|
11
11
|
|
|
12
|
-
const b = (c, next) => {
|
|
13
|
-
next()
|
|
12
|
+
const b = async (c, next) => {
|
|
13
|
+
await next()
|
|
14
14
|
c.res['header'] = `${c.res.header}-custom-header`
|
|
15
15
|
}
|
|
16
16
|
middleware.push(b)
|
|
17
17
|
|
|
18
|
-
const handler = (c, next) => {
|
|
18
|
+
const handler = async (c, next) => {
|
|
19
19
|
c.req['log'] = `${c.req.log} message`
|
|
20
|
-
next()
|
|
20
|
+
await next()
|
|
21
21
|
c.res = { message: 'new response' }
|
|
22
22
|
}
|
|
23
23
|
middleware.push(handler)
|
|
@@ -25,17 +25,17 @@ describe('compose middleware', () => {
|
|
|
25
25
|
const request = {}
|
|
26
26
|
const response = {}
|
|
27
27
|
|
|
28
|
-
it('Request', () => {
|
|
28
|
+
it('Request', async () => {
|
|
29
29
|
const c = { req: request, res: response }
|
|
30
30
|
const composed = compose(middleware)
|
|
31
|
-
composed(c)
|
|
31
|
+
await composed(c)
|
|
32
32
|
expect(c.req['log']).not.toBeNull()
|
|
33
33
|
expect(c.req['log']).toBe('log message')
|
|
34
34
|
})
|
|
35
|
-
it('Response', () => {
|
|
35
|
+
it('Response', async () => {
|
|
36
36
|
const c = { req: request, res: response }
|
|
37
37
|
const composed = compose(middleware)
|
|
38
|
-
composed(c)
|
|
38
|
+
await composed(c)
|
|
39
39
|
expect(c.res['header']).not.toBeNull()
|
|
40
40
|
expect(c.res['message']).toBe('new response')
|
|
41
41
|
})
|
package/src/hono.d.ts
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
type Result = {
|
|
2
|
+
handler: any
|
|
3
|
+
params: {}
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare class Node {
|
|
7
|
+
method: string
|
|
8
|
+
handler: any
|
|
9
|
+
children: Node[]
|
|
10
|
+
middlewares: any[]
|
|
11
|
+
|
|
12
|
+
insert(method: string, path: string, handler: any): Node
|
|
13
|
+
search(method: string, path: string): Result
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare class Context {
|
|
17
|
+
req: Request
|
|
18
|
+
res: Response
|
|
19
|
+
newResponse(params: {}): Response
|
|
20
|
+
}
|
|
5
21
|
|
|
6
|
-
|
|
22
|
+
type Handler = (c: Context, next: () => void) => Response | void
|
|
7
23
|
|
|
8
24
|
declare class Router {
|
|
9
25
|
tempPath: string
|
|
10
26
|
node: Node
|
|
11
27
|
|
|
12
|
-
add(method: string, path: string,
|
|
28
|
+
add(method: string, path: string, handler: Handler): Node
|
|
13
29
|
match(method: string, path: string): Node
|
|
14
30
|
}
|
|
15
31
|
|
|
@@ -17,32 +33,35 @@ export class Hono {
|
|
|
17
33
|
router: Router
|
|
18
34
|
middlewareRouters: Router[]
|
|
19
35
|
|
|
20
|
-
|
|
21
|
-
addRoute(method: string, args: any[]): Hono
|
|
22
|
-
matchRoute(method: string, path: string): Node
|
|
23
|
-
createContext(req: Request, res: Response): Context
|
|
24
|
-
dispatch(req: Request, res: Response): Response
|
|
25
|
-
handleEvent(event: FetchEvent): Response
|
|
26
|
-
fire(): void
|
|
27
|
-
|
|
28
|
-
notFound(): Response
|
|
36
|
+
use(path: string, middleware: Handler): void
|
|
29
37
|
|
|
30
38
|
route(path: string): Hono
|
|
39
|
+
fire(): void
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
all(path: string, handler: Handler): Hono
|
|
42
|
+
get(path: string, handler: Handler): Hono
|
|
43
|
+
post(path: string, handler: Handler): Hono
|
|
44
|
+
put(path: string, handler: Handler): Hono
|
|
45
|
+
head(path: string, handler: Handler): Hono
|
|
46
|
+
delete(path: string, handler: Handler): Hono
|
|
33
47
|
|
|
34
|
-
|
|
35
|
-
get(path: string, handler: any): Hono
|
|
36
|
-
post(path: string, handler: any): Hono
|
|
37
|
-
put(path: string, handler: any): Hono
|
|
38
|
-
head(path: string, handler: any): Hono
|
|
39
|
-
delete(path: string, handler: any): Hono
|
|
40
|
-
}
|
|
48
|
+
notFound(): Response
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
getRouter(): Router
|
|
51
|
+
addRoute(method: string, args: any[]): Hono
|
|
52
|
+
matchRoute(method: string, path: string): Promise<Node>
|
|
53
|
+
createContext(req: Request, res: Response): Promise<Context>
|
|
54
|
+
dispatch(req: Request, res: Response): Promise<Response>
|
|
55
|
+
handleEvent(event: FetchEvent): Promise<Response>
|
|
56
|
+
}
|
|
44
57
|
|
|
45
58
|
export class Middleware {
|
|
46
|
-
static defaultFilter:
|
|
47
|
-
|
|
59
|
+
static defaultFilter: Handler
|
|
60
|
+
// Add builtin middlewares
|
|
61
|
+
static poweredBy: Handler
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface FetchEvent extends Event {
|
|
65
|
+
request: Request
|
|
66
|
+
respondWith(response: Promise<Response> | Response): Promise<Response>
|
|
48
67
|
}
|
package/src/hono.js
CHANGED
|
@@ -59,8 +59,8 @@ class Hono {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
use(path, middleware) {
|
|
62
|
-
if (middleware.constructor.name !== '
|
|
63
|
-
throw new TypeError('middleware must be a function!')
|
|
62
|
+
if (middleware.constructor.name !== 'AsyncFunction') {
|
|
63
|
+
throw new TypeError('middleware must be a async function!')
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const router = new Router()
|
|
@@ -92,7 +92,7 @@ class Hono {
|
|
|
92
92
|
|
|
93
93
|
let handler = result ? result.handler[0] : this.notFound // XXX
|
|
94
94
|
|
|
95
|
-
const middleware = [
|
|
95
|
+
const middleware = []
|
|
96
96
|
|
|
97
97
|
for (const mr of this.middlewareRouters) {
|
|
98
98
|
const mwResult = mr.match(METHOD_NAME_OF_ALL, path)
|
|
@@ -102,15 +102,17 @@ class Hono {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
let wrappedHandler = async (context, next) => {
|
|
105
|
-
context.res = handler(context)
|
|
106
|
-
next()
|
|
105
|
+
context.res = await handler(context)
|
|
106
|
+
await next()
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
middleware.push(Middleware.defaultFilter)
|
|
109
110
|
middleware.push(wrappedHandler)
|
|
111
|
+
|
|
110
112
|
const composed = compose(middleware)
|
|
111
113
|
const c = await this.createContext(request, response)
|
|
112
114
|
|
|
113
|
-
composed(c)
|
|
115
|
+
await composed(c)
|
|
114
116
|
|
|
115
117
|
return c.res
|
|
116
118
|
}
|
package/src/hono.test.js
CHANGED
|
@@ -63,22 +63,22 @@ describe('params and query', () => {
|
|
|
63
63
|
describe('Middleware', () => {
|
|
64
64
|
const app = new Hono()
|
|
65
65
|
|
|
66
|
-
const logger = (c, next) => {
|
|
66
|
+
const logger = async (c, next) => {
|
|
67
67
|
console.log(`${c.req.method} : ${c.req.url}`)
|
|
68
|
-
next()
|
|
68
|
+
await next()
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const rootHeader = (c, next) => {
|
|
72
|
-
next()
|
|
73
|
-
c.res.headers.append('x-custom', 'root')
|
|
71
|
+
const rootHeader = async (c, next) => {
|
|
72
|
+
await next()
|
|
73
|
+
await c.res.headers.append('x-custom', 'root')
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const customHeader = (c, next) => {
|
|
77
|
-
next()
|
|
78
|
-
c.res.headers.append('x-message', 'custom-header')
|
|
76
|
+
const customHeader = async (c, next) => {
|
|
77
|
+
await next()
|
|
78
|
+
await c.res.headers.append('x-message', 'custom-header')
|
|
79
79
|
}
|
|
80
|
-
const customHeader2 = (c, next) => {
|
|
81
|
-
next()
|
|
80
|
+
const customHeader2 = async (c, next) => {
|
|
81
|
+
await next()
|
|
82
82
|
c.res.headers.append('x-message-2', 'custom-header-2')
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -86,6 +86,7 @@ describe('Middleware', () => {
|
|
|
86
86
|
app.use('*', rootHeader)
|
|
87
87
|
app.use('/hello', customHeader)
|
|
88
88
|
app.use('/hello/*', customHeader2)
|
|
89
|
+
|
|
89
90
|
app.get('/hello', () => {
|
|
90
91
|
return new fetch.Response('hello')
|
|
91
92
|
})
|
|
@@ -117,8 +118,8 @@ describe('Middleware', () => {
|
|
|
117
118
|
describe('Custom 404', () => {
|
|
118
119
|
const app = new Hono()
|
|
119
120
|
|
|
120
|
-
const customNotFound = (c, next) => {
|
|
121
|
-
next()
|
|
121
|
+
const customNotFound = async (c, next) => {
|
|
122
|
+
await next()
|
|
122
123
|
if (c.res.status === 404) {
|
|
123
124
|
c.res = new fetch.Response('Custom 404 Not Found', { status: 404 })
|
|
124
125
|
}
|
|
@@ -146,9 +147,12 @@ describe('Custom 404', () => {
|
|
|
146
147
|
describe('Error Handling', () => {
|
|
147
148
|
const app = new Hono()
|
|
148
149
|
|
|
149
|
-
it('Middleware must be function', () => {
|
|
150
|
+
it('Middleware must be async function', () => {
|
|
150
151
|
expect(() => {
|
|
151
152
|
app.use('*', {})
|
|
152
153
|
}).toThrow(TypeError)
|
|
154
|
+
expect(() => {
|
|
155
|
+
app.use('*', () => '')
|
|
156
|
+
}).toThrow(TypeError)
|
|
153
157
|
})
|
|
154
158
|
})
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const defaultFilter = (c, next) => {
|
|
1
|
+
const defaultFilter = async (c, next) => {
|
|
2
2
|
c.req.query = (key) => {
|
|
3
3
|
const url = new URL(c.req.url)
|
|
4
4
|
return url.searchParams.get(key)
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
next()
|
|
7
|
+
await next()
|
|
8
8
|
|
|
9
9
|
if (typeof c.res === 'string') {
|
|
10
|
-
c.res = new
|
|
10
|
+
c.res = new Response(c.res, {
|
|
11
11
|
status: 200,
|
|
12
12
|
headers: {
|
|
13
13
|
'Conten-Type': 'text/plain',
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: ci
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches: [ master ]
|
|
5
|
-
pull_request:
|
|
6
|
-
branches: [ master ]
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
ci:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
strategy:
|
|
13
|
-
matrix:
|
|
14
|
-
node-version: [16.x]
|
|
15
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- uses: actions/checkout@v2
|
|
19
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
-
uses: actions/setup-node@v2
|
|
21
|
-
with:
|
|
22
|
-
node-version: ${{ matrix.node-version }}
|
|
23
|
-
- run: yarn install --frozen-lockfile
|
|
24
|
-
- run: npm test
|