hono 0.5.2 → 0.5.5

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,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
+ &#x000B7;
6
+ <a href="https://github.com/yusukebe/hono/blob/master/docs/README.ja.md">日本語</a>
7
+ </p>
2
8
 
3
9
  [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/yusukebe/hono/ci)](https://github.com/yusukebe/hono/actions)
4
10
  [![GitHub](https://img.shields.io/github/license/yusukebe/hono)](https://github.com/yusukebe/hono/blob/master/LICENSE)
@@ -8,7 +14,7 @@
8
14
  [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/yusukebe/hono)](https://github.com/yusukebe/hono/pulse)
9
15
  [![GitHub last commit](https://img.shields.io/github/last-commit/yusukebe/hono)](https://github.com/yusukebe/hono/commits/master)
10
16
 
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.
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,7 +28,7 @@ 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.
31
+ - **Zero-dependencies** - using only Service Worker and Web standard API.
26
32
  - **Middleware** - built-in middleware and ability to extend with your own middleware.
27
33
  - **Optimized** - for Cloudflare Workers.
28
34
 
@@ -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
- ```sh
62
+ ```
57
63
  $ yarn add hono
58
64
  ```
59
65
 
60
66
  or
61
67
 
62
- ```sh
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
 
@@ -149,6 +156,8 @@ app.get('/fetch-url', async (c) => {
149
156
 
150
157
  ### Built-in Middleware
151
158
 
159
+ Hono has built-in middleware.
160
+
152
161
  ```js
153
162
  import { Hono } from 'hono'
154
163
  import { poweredBy } from 'hono/powered-by'
@@ -213,7 +222,7 @@ app.onError((err, c) => {
213
222
 
214
223
  ## Context
215
224
 
216
- To handle Request and Reponse, you can use Context object.
225
+ To handle Request and Reponse, you can use `Context` object.
217
226
 
218
227
  ### c.req
219
228
 
@@ -303,7 +312,7 @@ app.get('/', (c) => {
303
312
 
304
313
  ### c.notFound()
305
314
 
306
- Return the `404 Not Found` Response.
315
+ Return the `Not Found` Response.
307
316
 
308
317
  ```js
309
318
  app.get('/notfound', (c) => {
@@ -379,6 +388,17 @@ export default app
379
388
  */
380
389
  ```
381
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
+
382
402
  ## Cloudflare Workers with Hono
383
403
 
384
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.
@@ -400,10 +420,10 @@ Let's write your first code for Cloudflare Workers with Hono.
400
420
 
401
421
  Make a npm skeleton directory.
402
422
 
403
- ```sh
404
- mkdir hono-example
405
- cd hono-example
406
- npm init -y
423
+ ```
424
+ $ mkdir hono-example
425
+ $ cd hono-example
426
+ $ npm init -y
407
427
  ```
408
428
 
409
429
  ### 2. `wrangler init`
@@ -426,7 +446,7 @@ Would you like to create a Worker at src/index.js? (y/n) <--- n
426
446
 
427
447
  Install `hono` from the npm registry.
428
448
 
429
- ```sh
449
+ ```
430
450
  $ npm i hono
431
451
  ```
432
452
 
@@ -448,7 +468,7 @@ app.fire()
448
468
 
449
469
  Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
450
470
 
451
- ```sh
471
+ ```
452
472
  $ npx wrangler@beta dev index.js
453
473
  ```
454
474
 
@@ -456,7 +476,7 @@ $ npx wrangler@beta dev index.js
456
476
 
457
477
  Deploy to Cloudflare. That's all!
458
478
 
459
- ```sh
479
+ ```
460
480
  $ npx wrangler@beta publish index.js
461
481
  ```
462
482
 
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 { Result, Router } from './router';
4
+ import type { Router } from './router';
5
5
  declare global {
6
6
  interface Request<ParamKeyType = string> {
7
7
  param: (key: ParamKeyType) => string;
@@ -26,25 +26,34 @@ export declare class Hono {
26
26
  middlewareRouters: Router<MiddlewareHandler>[];
27
27
  tempPath: string;
28
28
  constructor(init?: Partial<Pick<Hono, 'routerClass' | 'strict'>>);
29
- notFoundHandler: NotFoundHandler;
30
- errorHandler: ErrorHandler;
29
+ private notFoundHandler;
30
+ private errorHandler;
31
31
  get<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
32
+ get(path: string, handler: Handler<string>): Hono;
32
33
  post<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
34
+ post(path: string, handler: Handler<string>): Hono;
33
35
  put<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
36
+ put(path: string, handler: Handler<string>): Hono;
34
37
  head<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
38
+ head(path: string, handler: Handler<string>): Hono;
35
39
  delete<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
40
+ delete(path: string, handler: Handler<string>): Hono;
36
41
  options<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
42
+ options(path: string, handler: Handler<string>): Hono;
37
43
  patch<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
44
+ patch(path: string, handler: Handler<string>): Hono;
38
45
  all<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
46
+ all(path: string, handler: Handler<string>): Hono;
39
47
  route(path: string): Hono;
40
48
  use(path: string, middleware: MiddlewareHandler): void;
41
49
  onError(handler: ErrorHandler): Hono;
42
50
  notFound(handler: NotFoundHandler): Hono;
43
- addRoute(method: string, path: string, handler: Handler): Hono;
44
- matchRoute(method: string, path: string): Promise<Result<Handler>>;
51
+ private addRoute;
52
+ private matchRoute;
45
53
  dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
46
54
  handleEvent(event: FetchEvent): Promise<Response>;
47
55
  fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
56
+ request(input: RequestInfo, requestInit?: RequestInit): Promise<Response>;
48
57
  fire(): void;
49
58
  }
50
59
  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));
@@ -5,9 +5,7 @@ export interface Context {
5
5
  export declare class Node {
6
6
  index?: number;
7
7
  varIndex?: number;
8
- children: {
9
- [key: string]: Node;
10
- };
8
+ children: Record<string, Node>;
11
9
  insert(tokens: readonly string[], index: number, paramMap: ParamMap, context: Context): void;
12
10
  buildRegExpStr(): string;
13
11
  }
@@ -1,15 +1,7 @@
1
1
  import { Router, Result } from '../../router';
2
- import type { ParamMap, ReplacementMap } from './trie';
3
2
  declare type Route<T> = [string, T];
4
- declare type HandlerData<T> = [T, ParamMap];
5
- declare type Matcher<T> = [RegExp | true, ReplacementMap, HandlerData<T>[]];
6
3
  export declare class RegExpRouter<T> extends Router<T> {
7
- routes?: {
8
- [method: string]: Route<T>[];
9
- };
10
- matchers?: {
11
- [method: string]: Matcher<T> | null;
12
- };
4
+ routes?: Record<string, Route<T>[]>;
13
5
  add(method: string, path: string, handler: T): void;
14
6
  match(method: string, path: string): Result<T> | null;
15
7
  private buildAllMatchers;
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RegExpRouter = void 0;
4
4
  const router_1 = require("../../router");
5
5
  const trie_1 = require("./trie");
6
+ const regExpMatchAll = new RegExp('');
7
+ const emptyParam = {};
6
8
  class RegExpRouter extends router_1.Router {
7
9
  constructor() {
8
10
  super(...arguments);
9
11
  this.routes = {};
10
- this.matchers = null;
11
12
  }
12
13
  add(method, path, handler) {
13
14
  var _a;
@@ -18,43 +19,54 @@ class RegExpRouter extends router_1.Router {
18
19
  this.routes[method].push([path, handler]);
19
20
  }
20
21
  match(method, path) {
21
- if (!this.matchers) {
22
- this.buildAllMatchers();
23
- }
24
- const matcher = this.matchers[method] || this.matchers[router_1.METHOD_NAME_OF_ALL];
25
- if (!matcher) {
26
- return null;
27
- }
28
- const [regexp, replacementMap, handlers] = matcher;
29
- if (regexp === true) {
30
- // '*'
31
- return new router_1.Result(handlers[0][0], {});
32
- }
33
- const match = path.match(regexp);
34
- if (!match) {
35
- return null;
36
- }
37
- if (!replacementMap) {
38
- // there is only one route and no capture
39
- return new router_1.Result(handlers[0][0], {});
40
- }
41
- const index = match.indexOf('', 1);
42
- const [handler, paramMap] = handlers[replacementMap[index]];
43
- const params = {};
44
- for (let i = 0; i < paramMap.length; i++) {
45
- params[paramMap[i][0]] = match[paramMap[i][1]];
22
+ const matchers = this.buildAllMatchers();
23
+ let match;
24
+ // Optimization for middleware
25
+ const methods = Object.keys(matchers);
26
+ if (methods.length === 1 && methods[0] === router_1.METHOD_NAME_OF_ALL) {
27
+ const [regexp, handlers] = matchers[router_1.METHOD_NAME_OF_ALL];
28
+ if (handlers.length === 1) {
29
+ const result = new router_1.Result(handlers[0][0], emptyParam);
30
+ if (regexp === regExpMatchAll) {
31
+ match = () => result;
32
+ }
33
+ else if (handlers.length === 1 && !handlers[0][1]) {
34
+ match = (_, path) => (regexp.test(path) ? result : null);
35
+ }
36
+ }
46
37
  }
47
- return new router_1.Result(handler, params);
38
+ match || (match = (method, path) => {
39
+ const matcher = matchers[method] || matchers[router_1.METHOD_NAME_OF_ALL];
40
+ if (!matcher) {
41
+ return null;
42
+ }
43
+ const match = path.match(matcher[0]);
44
+ if (!match) {
45
+ return null;
46
+ }
47
+ const index = match.indexOf('', 1);
48
+ const [handler, paramMap] = matcher[1][index];
49
+ if (!paramMap) {
50
+ return new router_1.Result(handler, emptyParam);
51
+ }
52
+ const params = {};
53
+ for (let i = 0; i < paramMap.length; i++) {
54
+ params[paramMap[i][0]] = match[paramMap[i][1]];
55
+ }
56
+ return new router_1.Result(handler, params);
57
+ });
58
+ this.match = match;
59
+ return this.match(method, path);
48
60
  }
49
61
  buildAllMatchers() {
50
- this.matchers || (this.matchers = {});
62
+ const matchers = {};
51
63
  Object.keys(this.routes).forEach((method) => {
52
- this.buildMatcher(method);
64
+ matchers[method] = this.buildMatcher(method);
53
65
  });
54
66
  delete this.routes; // to reduce memory usage
67
+ return matchers;
55
68
  }
56
69
  buildMatcher(method) {
57
- this.matchers || (this.matchers = {});
58
70
  const trie = new trie_1.Trie();
59
71
  const handlers = [];
60
72
  const targetMethods = [method];
@@ -63,34 +75,40 @@ class RegExpRouter extends router_1.Router {
63
75
  }
64
76
  const routes = targetMethods.flatMap((method) => this.routes[method] || []);
65
77
  if (routes.length === 0) {
66
- this.matchers[method] = null;
67
- return;
68
- }
69
- if (routes.length === 1 && routes[0][0] === '*') {
70
- this.matchers[method] = [true, null, [[routes[0][1], []]]];
71
- return;
78
+ return null;
72
79
  }
73
- if (routes.length === 1 && !routes[0][0].match(/:/)) {
74
- // there is only one route and no capture
75
- const tmp = routes[0][0].endsWith('*')
76
- ? routes[0][0].replace(/\/\*$/, '(?:$|/)') // /path/to/* => /path/to(?:$|/)
77
- : `${routes[0][0]}$`; // /path/to/action => /path/to/action$
78
- const regExpStr = `^${tmp.replace(/\*/g, '[^/]+')}`; // /prefix/*/path/to => /prefix/[^/]+/path/to
79
- this.matchers[method] = [new RegExp(regExpStr), null, [[routes[0][1], []]]];
80
- return;
80
+ if (method === router_1.METHOD_NAME_OF_ALL) {
81
+ if (routes.length === 1 && routes[0][0] === '*') {
82
+ return [regExpMatchAll, [[routes[0][1], null]]];
83
+ }
84
+ if (routes.length === 1 && !routes[0][0].match(/:/)) {
85
+ // there is only one route and no capture
86
+ const tmp = routes[0][0].endsWith('*')
87
+ ? routes[0][0].replace(/\/\*$/, '(?:$|/)') // /path/to/* => /path/to(?:$|/)
88
+ : `${routes[0][0]}$`; // /path/to/action => /path/to/action$
89
+ const regExpStr = `^${tmp.replace(/\*/g, '[^/]+')}`; // /prefix/*/path/to => /prefix/[^/]+/path/to
90
+ return [new RegExp(regExpStr), [[routes[0][1], null]]];
91
+ }
81
92
  }
82
93
  for (let i = 0; i < routes.length; i++) {
83
94
  const paramMap = trie.insert(routes[i][0], i);
84
- handlers[i] = [routes[i][1], paramMap];
95
+ handlers[i] = [routes[i][1], Object.keys(paramMap).length !== 0 ? paramMap : null];
85
96
  }
86
97
  const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
87
98
  for (let i = 0; i < handlers.length; i++) {
88
99
  const paramMap = handlers[i][1];
89
- for (let i = 0; i < paramMap.length; i++) {
90
- paramMap[i][1] = paramReplacementMap[paramMap[i][1]];
100
+ if (paramMap) {
101
+ for (let i = 0; i < paramMap.length; i++) {
102
+ paramMap[i][1] = paramReplacementMap[paramMap[i][1]];
103
+ }
91
104
  }
92
105
  }
93
- this.matchers[method] = [new RegExp(regexp), indexReplacementMap, handlers];
106
+ const handlerMap = [];
107
+ // using `in` because indexReplacementMap is a sparse array
108
+ for (const i in indexReplacementMap) {
109
+ handlerMap[i] = handlers[indexReplacementMap[i]];
110
+ }
111
+ return [regexp, handlerMap];
94
112
  }
95
113
  }
96
114
  exports.RegExpRouter = RegExpRouter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "0.5.2",
3
+ "version": "0.5.5",
4
4
  "description": "[炎] Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,10 +20,12 @@
20
20
  "./body-parse": "./dist/middleware/body-parse/body-parse.js",
21
21
  "./cookie": "./dist/middleware/cookie/cookie.js",
22
22
  "./cors": "./dist/middleware/cors/cors.js",
23
+ "./etag": "./dist/middleware/etag/etag.js",
23
24
  "./logger": "./dist/middleware/logger/logger.js",
24
25
  "./mustache": "./dist/middleware/mustache/mustache.js",
25
26
  "./powered-by": "./dist/middleware/powered-by/powered-by.js",
26
27
  "./serve-static": "./dist/middleware/serve-static/serve-static.js",
28
+ "./router/trie-router": "./dist/router/trie-router/router.js",
27
29
  "./router/reg-exp-router": "./dist/router/reg-exp-router/index.js"
28
30
  },
29
31
  "typesVersions": {
@@ -40,6 +42,9 @@
40
42
  "cors": [
41
43
  "./dist/middleware/cors/cors.d.ts"
42
44
  ],
45
+ "etag": [
46
+ "./dist/middleware/etag/etag.d.ts"
47
+ ],
43
48
  "logger": [
44
49
  "./dist/middleware/logger/logger.d.ts"
45
50
  ],
@@ -52,6 +57,9 @@
52
57
  "serve-static": [
53
58
  "./dist/middleware/serve-static/serve-static.d.ts"
54
59
  ],
60
+ "router/trie-router": [
61
+ "./dist/router/trie-router/router.d.ts"
62
+ ],
55
63
  "router/reg-exp-router": [
56
64
  "./dist/router/reg-exp-router/router.d.ts"
57
65
  ]
@@ -98,6 +106,7 @@
98
106
  "jest-environment-miniflare": "^2.0.0",
99
107
  "mustache": "^4.2.0",
100
108
  "prettier": "^2.5.1",
109
+ "prettier-plugin-md-nocjsp": "^1.2.0",
101
110
  "rimraf": "^3.0.2",
102
111
  "ts-jest": "^27.1.2",
103
112
  "typescript": "^4.5.5"