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 CHANGED
@@ -11,6 +11,8 @@ app.get('/', () => new Response('Hono!!'))
11
11
  app.fire()
12
12
  ```
13
13
 
14
+ ![carbon](https://user-images.githubusercontent.com/10682/147877725-bce9bd46-953d-4d70-9c2b-3eae47ad4df9.png)
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
+ ![Demo](https://user-images.githubusercontent.com/10682/147877447-ff5907cd-49be-4976-b3b4-5df2ac6dfda4.gif)
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.9",
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
@@ -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
- declare class FetchEvent {}
2
- declare class Request {}
3
- declare class Response {}
4
- declare class Context {}
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
- declare class Node {}
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, handlers: any[]): Node
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
- getRouter(): Router
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
- use(path: string, middleware: any): void
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
- all(path: string, handler: any): Hono
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
- // XXX
43
- declare interface BuiltinMiddleware {}
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: BuiltinMiddleware
47
- static poweredBy: BuiltinMiddleware
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 !== 'Function') {
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 = [Middleware.defaultFilter] // add defaultFilter later
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 Reponse(c.res, {
10
+ c.res = new Response(c.res, {
11
11
  status: 200,
12
12
  headers: {
13
13
  'Conten-Type': 'text/plain',
@@ -1,6 +1,6 @@
1
- const poweredBy = (c, next) => {
2
- next()
3
- c.res.headers.append('X-Powered-By', 'Hono')
1
+ const poweredBy = async (c, next) => {
2
+ await next()
3
+ await c.res.headers.append('X-Powered-By', 'Hono')
4
4
  }
5
5
 
6
6
  module.exports = poweredBy
@@ -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