hono 0.3.8 → 0.4.2

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
@@ -1,6 +1,14 @@
1
1
  # Hono
2
2
 
3
- Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web framework for a Service Workers API based serverless such as Cloudflare Workers and Fastly Compute@Edge.
3
+ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/yusukebe/hono/ci)](https://github.com/yusukebe/hono/actions)
4
+ [![GitHub](https://img.shields.io/github/license/yusukebe/hono)](https://github.com/yusukebe/hono/blob/master/LICENSE)
5
+ [![npm](https://img.shields.io/npm/v/hono)](https://www.npmjs.com/package/hono)
6
+ [![npm](https://img.shields.io/npm/dm/hono)](https://www.npmjs.com/package/hono)
7
+ [![npm type definitions](https://img.shields.io/npm/types/hono)](https://www.npmjs.com/package/hono)
8
+ [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/yusukebe/hono)](https://github.com/yusukebe/hono/pulse)
9
+ [![GitHub last commit](https://img.shields.io/github/last-commit/yusukebe/hono)](https://github.com/yusukebe/hono/commits/master)
10
+
11
+ Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web framework for Service Worker based serverless applications like Cloudflare Workers and Fastly Compute@Edge.
4
12
 
5
13
  ```js
6
14
  import { Hono } from 'hono'
@@ -13,9 +21,9 @@ app.fire()
13
21
 
14
22
  ## Features
15
23
 
16
- - **Ultra fast** - the router is implemented with Trie-Tree structure. Not use loops.
17
- - **Zero dependencies** - using only Web standard API.
18
- - **Middleware** - builtin middleware, and you can make your own middleware.
24
+ - **Ultrafast** - the router does not use linear loops.
25
+ - **Zero-dependencies** - using only Web standard API.
26
+ - **Middleware** - builtin middleware and your own middleware.
19
27
  - **Optimized** - for Cloudflare Workers.
20
28
 
21
29
  ## Benchmark
@@ -23,37 +31,41 @@ app.fire()
23
31
  **Hono is fastest** compared to other routers for Cloudflare Workers.
24
32
 
25
33
  ```plain
26
- hono x 708,671 ops/sec ±2.58% (58 runs sampled)
27
- itty-router x 159,610 ops/sec ±2.86% (87 runs sampled)
28
- sunder x 322,846 ops/sec ±2.24% (86 runs sampled)
29
- worktop x 223,625 ops/sec ±2.01% (95 runs sampled)
34
+ hono x 779,197 ops/sec ±6.55% (78 runs sampled)
35
+ itty-router x 161,813 ops/sec ±3.87% (87 runs sampled)
36
+ sunder x 334,096 ops/sec ±1.33% (93 runs sampled)
37
+ worktop x 212,661 ops/sec ±4.40% (81 runs sampled)
30
38
  Fastest is hono
31
- ✨ Done in 57.83s.
39
+ ✨ Done in 58.29s.
32
40
  ```
33
41
 
34
42
  ## Hono in 1 minute
35
43
 
36
- Below is a demonstration to create an application of Cloudflare Workers with Hono.
44
+ A demonstration to create an application of Cloudflare Workers with Hono.
37
45
 
38
46
  ![Demo](https://user-images.githubusercontent.com/10682/151973526-342644f9-71c5-4fee-81f4-64a7558bb192.gif)
39
47
 
48
+ Now, the named path parameter has types.
49
+
50
+ ![Demo](https://user-images.githubusercontent.com/10682/154179671-9e491597-6778-44ac-a8e6-4483d7ad5393.png)
51
+
40
52
  ## Install
41
53
 
42
- You can install from npm registry:
54
+ You can install Hono from the npm registry.
43
55
 
44
56
  ```sh
45
- yarn add hono
57
+ $ yarn add hono
46
58
  ```
47
59
 
48
60
  or
49
61
 
50
62
  ```sh
51
- npm install hono
63
+ $ npm install hono
52
64
  ```
53
65
 
54
66
  ## Methods
55
67
 
56
- Instance of `Hono` has these methods:
68
+ An instance of `Hono` has these methods.
57
69
 
58
70
  - app.**HTTP_METHOD**(path, handler)
59
71
  - app.**all**(path, handler)
@@ -66,8 +78,6 @@ Instance of `Hono` has these methods:
66
78
 
67
79
  ### Basic
68
80
 
69
- `app.HTTP_METHOD`
70
-
71
81
  ```js
72
82
  // HTTP Methods
73
83
  app.get('/', (c) => c.text('GET /'))
@@ -77,11 +87,7 @@ app.post('/', (c) => c.text('POST /'))
77
87
  app.get('/wild/*/card', (c) => {
78
88
  return c.text('GET /wild/*/card')
79
89
  })
80
- ```
81
-
82
- `app.all`
83
90
 
84
- ```js
85
91
  // Any HTTP methods
86
92
  app.all('/hello', (c) => c.text('Any Method /hello'))
87
93
  ```
@@ -102,29 +108,33 @@ app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
102
108
  const date = c.req.param('date')
103
109
  const title = c.req.param('title')
104
110
  ...
111
+ })
105
112
  ```
106
113
 
107
- ### Chained Route
114
+ ### Nested route
108
115
 
109
116
  ```js
110
- app
111
- .route('/api/book')
112
- .get(() => {...})
113
- .post(() => {...})
114
- .put(() => {...})
117
+ const book = app.route('/book')
118
+ book.get('/', (c) => c.text('List Books')) // GET /book
119
+ book.get('/:id', (c) => {
120
+ // GET /book/:id
121
+ const id = c.req.param('id')
122
+ return c.text('Get Book: ' + id)
123
+ })
124
+ book.post('/', (c) => c.text('Create Book')) // POST /book
115
125
  ```
116
126
 
117
- ### Custom 404 Response
127
+ ### no strict
118
128
 
119
- You can customize 404 Not Found response:
129
+ If `strict` is set `false`, `/hello`and`/hello/` are treated the same. Default is `true`.
120
130
 
121
131
  ```js
122
- app.get('*', (c) => {
123
- return c.text('Custom 404 Error', 404)
124
- })
132
+ const app = new Hono({ strict: false })
133
+
134
+ app.get('/hello', (c) => c.text('/hello or /hello/'))
125
135
  ```
126
136
 
127
- ## async/await
137
+ ### async/await
128
138
 
129
139
  ```js
130
140
  app.get('/fetch-url', async (c) => {
@@ -154,15 +164,13 @@ app.use(
154
164
  password: 'acoolproject',
155
165
  })
156
166
  )
157
-
158
- ...
159
167
  ```
160
168
 
161
169
  Available builtin middleware are listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
162
170
 
163
171
  ### Custom Middleware
164
172
 
165
- You can write your own middleware:
173
+ You can write your own middleware.
166
174
 
167
175
  ```js
168
176
  // Custom logger
@@ -180,22 +188,9 @@ app.use('/message/*', async (c, next) => {
180
188
  app.get('/message/hello', (c) => c.text('Hello Middleware!'))
181
189
  ```
182
190
 
183
- ### Handling Error
184
-
185
- ```js
186
- app.use('*', async (c, next) => {
187
- try {
188
- await next()
189
- } catch (err) {
190
- console.error(`${err}`)
191
- c.res = c.text('Custom Error Message', { status: 500 })
192
- }
193
- })
194
- ```
195
-
196
191
  ## Context
197
192
 
198
- To handle Request and Reponse, you can use Context object:
193
+ To handle Request and Reponse, you can use Context object.
199
194
 
200
195
  ### c.req
201
196
 
@@ -234,25 +229,26 @@ app.get('/welcome', (c) => {
234
229
  c.status(201)
235
230
 
236
231
  return c.body('Thank you for comming')
232
+ })
233
+ ```
237
234
 
238
- /*
239
- Same as:
240
- return new Response('Thank you for comming', {
241
- status: 201,
242
- statusText: 'Created',
243
- headers: {
244
- 'X-Message': 'Hello',
245
- 'Content-Type': 'text/plain',
246
- 'Content-Length: '22'
247
- }
248
- })
249
- */
235
+ The Response is the same as below.
236
+
237
+ ```js
238
+ new Response('Thank you for comming', {
239
+ status: 201,
240
+ statusText: 'Created',
241
+ headers: {
242
+ 'X-Message': 'Hello',
243
+ 'Content-Type': 'text/plain',
244
+ 'Content-Length': '22',
245
+ },
250
246
  })
251
247
  ```
252
248
 
253
249
  ### c.text()
254
250
 
255
- Render text as `Content-Type:text/plain`:
251
+ Render texts as `Content-Type:text/plain`.
256
252
 
257
253
  ```js
258
254
  app.get('/say', (c) => {
@@ -262,7 +258,7 @@ app.get('/say', (c) => {
262
258
 
263
259
  ### c.json()
264
260
 
265
- Render JSON as `Content-Type:application/json`:
261
+ Render JSON as `Content-Type:application/json`.
266
262
 
267
263
  ```js
268
264
  app.get('/api', (c) => {
@@ -272,7 +268,7 @@ app.get('/api', (c) => {
272
268
 
273
269
  ### c.html()
274
270
 
275
- Render HTML as `Content-Type:text/html`:
271
+ Render HTML as `Content-Type:text/html`.
276
272
 
277
273
  ```js
278
274
  app.get('/', (c) => {
@@ -280,9 +276,19 @@ app.get('/', (c) => {
280
276
  })
281
277
  ```
282
278
 
279
+ ### c.notFound()
280
+
281
+ Return the default `404 Not Found` Response.
282
+
283
+ ```js
284
+ app.get('/notfound', (c) => {
285
+ return c.notFound()
286
+ })
287
+ ```
288
+
283
289
  ### c.redirect()
284
290
 
285
- Redirect, default status code is `302`:
291
+ Redirect, default status code is `302`.
286
292
 
287
293
  ```js
288
294
  app.get('/redirect', (c) => c.redirect('/'))
@@ -321,9 +327,29 @@ app.get('*', async c => {
321
327
  })
322
328
  ```
323
329
 
330
+ ## Not Found
331
+
332
+ You can write the default `404 Not Found` Response.
333
+
334
+ ```js
335
+ app.notFound = (c) => {
336
+ return c.text('This is default 404 Not Found', 404)
337
+ }
338
+ ```
339
+
340
+ ## Error handling
341
+
342
+ You can handle errors in your way.
343
+
344
+ ```js
345
+ app.onError = (err, c) => {
346
+ return c.text(`This is error message: ${err.mssage}`, 500)
347
+ }
348
+ ```
349
+
324
350
  ## fire
325
351
 
326
- `app.fire()` do:
352
+ `app.fire()` do this.
327
353
 
328
354
  ```js
329
355
  addEventListener('fetch', (event) => {
@@ -343,7 +369,7 @@ export default {
343
369
  }
344
370
 
345
371
  /*
346
- or just do this:
372
+ or just do:
347
373
  export default app
348
374
  */
349
375
  ```
@@ -356,15 +382,15 @@ Let's write your first code for Cloudflare Workers with Hono.
356
382
 
357
383
  ### 1. Install Wrangler
358
384
 
359
- Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)"
385
+ Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)".
360
386
 
361
387
  ```sh
362
- npm i @cloudflare/wrangler -g
388
+ $ npm i @cloudflare/wrangler -g
363
389
  ```
364
390
 
365
391
  ### 2. `npm init`
366
392
 
367
- Make npm skeleton directory.
393
+ Make a npm skeleton directory.
368
394
 
369
395
  ```sh
370
396
  mkdir hono-example
@@ -377,15 +403,15 @@ npm init -y
377
403
  Init as a wrangler project.
378
404
 
379
405
  ```sh
380
- wrangler init
406
+ $ wrangler init
381
407
  ```
382
408
 
383
409
  ### 4. `npm install hono`
384
410
 
385
- Install `hono` from npm registry.
411
+ Install `hono` from the npm registry.
386
412
 
387
413
  ```sh
388
- npm i hono
414
+ $ npm i hono
389
415
  ```
390
416
 
391
417
  ### 5. Write your app
@@ -403,10 +429,10 @@ app.fire()
403
429
 
404
430
  ### 6. Run
405
431
 
406
- Run the development server locally. Then, access like `http://127.0.0.1:8787/` in your Web browser.
432
+ Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
407
433
 
408
434
  ```sh
409
- wrangler dev
435
+ $ wrangler dev
410
436
  ```
411
437
 
412
438
  ### 7. Publish
@@ -414,12 +440,22 @@ wrangler dev
414
440
  Deploy to Cloudflare. That's all!
415
441
 
416
442
  ```sh
417
- wrangler publish
443
+ $ wrangler publish
444
+ ```
445
+
446
+ ## Starter template
447
+
448
+ You can start making your application of Cloudflare Workers with [the starter template](https://github.com/yusukebe/hono-minimal). It is a realy minimal using TypeScript, esbuild, and Miniflare.
449
+
450
+ To generate a project skelton, run this command.
451
+
452
+ ```
453
+ $ wrangler generate my-app https://github.com/yusukebe/hono-minimal
418
454
  ```
419
455
 
420
456
  ## Related projects
421
457
 
422
- 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.
458
+ Implementation of the original router `TrieRouter` is inspired by [goblin](https://github.com/bmf-san/goblin). `RegExpRouter` is inspired by [Router::Boom](https://github.com/tokuhirom/Router-Boom). 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.
423
459
 
424
460
  - express <https://github.com/expressjs/express>
425
461
  - koa <https://github.com/koajs/koa>
@@ -427,10 +463,11 @@ Implementation of the router is inspired by [goblin](https://github.com/bmf-san/
427
463
  - Sunder <https://github.com/SunderJS/sunder>
428
464
  - goblin <https://github.com/bmf-san/goblin>
429
465
  - worktop <https://github.com/lukeed/worktop>
466
+ - Router::Boom <https://github.com/tokuhirom/Router-Boom>
430
467
 
431
468
  ## Contributing
432
469
 
433
- Contributions Welcome! You can contribute by the following way:
470
+ Contributions Welcome! You can contribute by the following way.
434
471
 
435
472
  - Write or fix documents
436
473
  - Write code of middleware
@@ -438,7 +475,11 @@ Contributions Welcome! You can contribute by the following way:
438
475
  - Refactor the code
439
476
  - etc.
440
477
 
441
- If you can, let's make Hono together!
478
+ Let's make Hono together!
479
+
480
+ ## Contributors
481
+
482
+ Thanks to [all contributors](https://github.com/yusukebe/hono/graphs/contributors)!
442
483
 
443
484
  ## Author
444
485
 
package/dist/compose.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const compose: <T>(middleware: Function[]) => (context: T, next?: Function) => Promise<void | object>;
1
+ export declare const compose: <T>(middleware: Function[], onError?: Function) => (context: T, next?: Function) => Promise<void | object>;
package/dist/compose.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.compose = void 0;
4
+ const context_1 = require("./context");
4
5
  // Based on the code in the MIT licensed `koa-compose` package.
5
- const compose = (middleware) => {
6
+ const compose = (middleware, onError) => {
6
7
  const errors = [];
7
8
  return function (context, next) {
8
9
  let index = -1;
@@ -19,7 +20,12 @@ const compose = (middleware) => {
19
20
  try {
20
21
  return Promise.resolve(fn(context, dispatch.bind(null, i + 1))).catch((e) => {
21
22
  errors.push(e);
22
- throw errors[0]; // XXX
23
+ if (onError && context instanceof context_1.Context) {
24
+ context.res = onError(errors[0], context);
25
+ }
26
+ else {
27
+ throw errors[0];
28
+ }
23
29
  });
24
30
  }
25
31
  catch (err) {
package/dist/context.d.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- declare type Headers = {
3
- [key: string]: string;
4
- };
2
+ declare type Headers = Record<string, string>;
5
3
  declare type Data = string | ArrayBuffer | ReadableStream;
6
4
  export interface Env {
7
5
  }
@@ -14,6 +12,7 @@ export declare class Context<RequestParamKeyType = string> {
14
12
  private _status;
15
13
  private _statusText;
16
14
  render: (template: string, params?: object, options?: object) => Promise<Response>;
15
+ notFound: () => Response;
17
16
  constructor(req: Request<RequestParamKeyType>, opts?: {
18
17
  res: Response;
19
18
  env: Env;
package/dist/hono.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- import type { Result } from './node';
3
- import { Node } from './node';
4
2
  import { Context } from './context';
5
3
  import type { Env } from './context';
4
+ import type { Result, Router } from './router';
6
5
  declare global {
7
6
  interface Request<ParamKeyType = string> {
8
7
  param: (key: ParamKeyType) => string;
@@ -16,42 +15,32 @@ export declare type MiddlewareHandler = (c: Context, next: Function) => Promise<
16
15
  declare type ParamKeyName<NameWithPattern> = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern;
17
16
  declare type ParamKey<Component> = Component extends `:${infer NameWithPattern}` ? ParamKeyName<NameWithPattern> : never;
18
17
  declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
19
- export declare class Router<T> {
20
- node: Node<T>;
21
- constructor();
22
- add(method: string, path: string, handler: T): void;
23
- match(method: string, path: string): Result<T> | null;
24
- }
25
18
  export declare class Hono {
26
- router: Router<Handler[]>;
19
+ routerClass: {
20
+ new (): Router<any>;
21
+ };
22
+ strict: boolean;
23
+ router: Router<Handler>;
27
24
  middlewareRouters: Router<MiddlewareHandler>[];
28
25
  tempPath: string;
29
- constructor();
30
- get<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
31
- get(arg: Handler<never>, ...args: Handler<never>[]): Hono;
32
- post<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
33
- post(arg: Handler, ...args: Handler[]): Hono;
34
- put<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
35
- put(arg: Handler, ...args: Handler[]): Hono;
36
- head<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
37
- head(arg: Handler, ...args: Handler[]): Hono;
38
- delete<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
39
- delete(arg: Handler, ...args: Handler[]): Hono;
40
- options<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
41
- options(arg: Handler, ...args: Handler[]): Hono;
42
- patch<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
43
- patch(arg: Handler, ...args: Handler[]): Hono;
44
- all<Path extends string>(arg: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
45
- all(arg: Handler<never>, ...args: Handler<never>[]): Hono;
26
+ constructor(init?: Partial<Pick<Hono, 'routerClass' | 'strict'>>);
27
+ get<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
28
+ post<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
29
+ put<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
30
+ head<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
31
+ delete<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
32
+ options<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
33
+ patch<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
34
+ all<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
46
35
  route(path: string): Hono;
47
36
  use(path: string, middleware: MiddlewareHandler): void;
48
- addRoute(method: string, arg: string | Handler, ...args: Handler[]): Hono;
49
- matchRoute(method: string, path: string): Promise<Result<Handler[]>>;
37
+ addRoute(method: string, path: string, handler: Handler): Hono;
38
+ matchRoute(method: string, path: string): Promise<Result<Handler>>;
50
39
  dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
51
40
  handleEvent(event: FetchEvent): Promise<Response>;
52
41
  fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
53
42
  fire(): void;
54
- onError(err: Error): Response;
55
- notFound(): Response;
43
+ onError(err: Error, c: Context): Response;
44
+ notFound(c: Context): Response;
56
45
  }
57
46
  export {};