hono 0.3.5 → 0.4.0

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,6 @@
1
1
  # Hono
2
2
 
3
- Hono[炎] - _**means flame🔥 in Japanese**_ - is small, simple, and ultrafast web flamework for a Service Workers API based serverless such as Cloudflare Workers and Fastly Compute@Edge.
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.
4
4
 
5
5
  ```js
6
6
  import { Hono } from 'hono'
@@ -13,7 +13,7 @@ app.fire()
13
13
 
14
14
  ## Features
15
15
 
16
- - **Ultra fast** - the router is implemented with Trie-Tree structure.
16
+ - **Ultra fast** - the router is implemented with Trie-Tree structure. Not use loops.
17
17
  - **Zero dependencies** - using only Web standard API.
18
18
  - **Middleware** - builtin middleware, and you can make your own middleware.
19
19
  - **Optimized** - for Cloudflare Workers.
@@ -37,9 +37,13 @@ Below is a demonstration to create an application of Cloudflare Workers with Hon
37
37
 
38
38
  ![Demo](https://user-images.githubusercontent.com/10682/151973526-342644f9-71c5-4fee-81f4-64a7558bb192.gif)
39
39
 
40
+ Now, the named path parameter has types.
41
+
42
+ ![Demo](https://user-images.githubusercontent.com/10682/154179671-9e491597-6778-44ac-a8e6-4483d7ad5393.png)
43
+
40
44
  ## Install
41
45
 
42
- You can install from npm registry:
46
+ You can install Hono from npm registry:
43
47
 
44
48
  ```sh
45
49
  yarn add hono
@@ -53,7 +57,7 @@ npm install hono
53
57
 
54
58
  ## Methods
55
59
 
56
- Instance of `Hono` has these methods:
60
+ An instance of `Hono` has these methods:
57
61
 
58
62
  - app.**HTTP_METHOD**(path, handler)
59
63
  - app.**all**(path, handler)
@@ -104,14 +108,17 @@ app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
104
108
  ...
105
109
  ```
106
110
 
107
- ### Chained Route
111
+ ### Nested route
108
112
 
109
113
  ```js
110
- app
111
- .route('/api/book')
112
- .get(() => {...})
113
- .post(() => {...})
114
- .put(() => {...})
114
+ const book = app.route('/book')
115
+ book.get('/', (c) => c.text('List Books')) // => GET /book
116
+ book.get('/:id', (c) => {
117
+ // => GET /book/:id
118
+ const id = c.req.param('id')
119
+ return c.text('Get Book: ' + id)
120
+ })
121
+ book.post('/', (c) => c.text('Create Book')) // => POST /book
115
122
  ```
116
123
 
117
124
  ### Custom 404 Response
@@ -124,6 +131,18 @@ app.get('*', (c) => {
124
131
  })
125
132
  ```
126
133
 
134
+ ### no strict
135
+
136
+ If `strict` is set `false`, `/hello`and`/hello/` are treated the same:
137
+
138
+ ```js
139
+ const app = new Hono({ strict: false })
140
+
141
+ app.get('/hello', (c) => c.text('/hello or /hello/'))
142
+ ```
143
+
144
+ Default is `true`.
145
+
127
146
  ## async/await
128
147
 
129
148
  ```js
@@ -147,15 +166,7 @@ const app = new Hono()
147
166
 
148
167
  app.use('*', poweredBy())
149
168
  app.use('*', logger())
150
- app.use(
151
- '/auth/*',
152
- basicAuth({
153
- username: 'hono',
154
- password: 'acoolproject',
155
- })
156
- )
157
-
158
- ...
169
+ app.use('/auth/*', basicAuth({ username: 'hono', password: 'acoolproject' }))
159
170
  ```
160
171
 
161
172
  Available builtin middleware are listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
@@ -174,7 +185,7 @@ app.use('*', async (c, next) => {
174
185
  // Add a custom header
175
186
  app.use('/message/*', async (c, next) => {
176
187
  await next()
177
- await c.res.headers.add('x-message', 'This is middleware!')
188
+ await c.header('x-message', 'This is middleware!')
178
189
  })
179
190
 
180
191
  app.get('/message/hello', (c) => c.text('Hello Middleware!'))
@@ -193,30 +204,9 @@ app.use('*', async (c, next) => {
193
204
  })
194
205
  ```
195
206
 
196
- ### Complex Pattern
197
-
198
- You can also do this:
199
-
200
- ```js
201
- // Output response time
202
- app.use('*', async (c, next) => {
203
- await next()
204
- const responseTime = await c.res.headers.get('X-Response-Time')
205
- console.log(`X-Response-Time: ${responseTime}`)
206
- })
207
-
208
- // Add X-Response-Time header
209
- app.use('*', async (c, next) => {
210
- const start = Date.now()
211
- await next()
212
- const ms = Date.now() - start
213
- await c.res.headers.append('X-Response-Time', `${ms}ms`)
214
- })
215
- ```
216
-
217
207
  ## Context
218
208
 
219
- To handle Request and Reponse easily, you can use Context object:
209
+ To handle Request and Reponse, you can use Context object:
220
210
 
221
211
  ### c.req
222
212
 
@@ -253,7 +243,6 @@ app.get('/welcome', (c) => {
253
243
  c.header('X-Message', 'Hello!')
254
244
  c.header('Content-Type', 'text/plain')
255
245
  c.status(201)
256
- c.statusText('201 Content Created')
257
246
 
258
247
  return c.body('Thank you for comming')
259
248
 
@@ -261,7 +250,7 @@ app.get('/welcome', (c) => {
261
250
  Same as:
262
251
  return new Response('Thank you for comming', {
263
252
  status: 201,
264
- statusText: '201 Content Created',
253
+ statusText: 'Created',
265
254
  headers: {
266
255
  'X-Message': 'Hello',
267
256
  'Content-Type': 'text/plain',
@@ -431,7 +420,7 @@ Run the development server locally. Then, access like `http://127.0.0.1:8787/` i
431
420
  wrangler dev
432
421
  ```
433
422
 
434
- ### Publish
423
+ ### 7. Publish
435
424
 
436
425
  Deploy to Cloudflare. That's all!
437
426
 
@@ -462,6 +451,10 @@ Contributions Welcome! You can contribute by the following way:
462
451
 
463
452
  If you can, let's make Hono together!
464
453
 
454
+ ## Contributors
455
+
456
+ Thanks to [all contributors](https://github.com/yusukebe/hono/graphs/contributors)!
457
+
465
458
  ## Author
466
459
 
467
460
  Yusuke Wada <https://github.com/yusukebe>
package/dist/compose.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const compose: (middleware: any) => (context: any, next?: Function) => any;
1
+ export declare const compose: <T>(middleware: Function[]) => (context: T, next?: Function) => Promise<void | object>;
package/dist/compose.js CHANGED
@@ -7,7 +7,7 @@ const compose = (middleware) => {
7
7
  return function (context, next) {
8
8
  let index = -1;
9
9
  return dispatch(0);
10
- function dispatch(i) {
10
+ async function dispatch(i) {
11
11
  if (i <= index)
12
12
  return Promise.reject(new Error('next() called multiple times'));
13
13
  index = i;
package/dist/context.d.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- declare type Headers = {
3
- [key: string]: string;
4
- };
2
+ declare type Headers = Record<string, string>;
3
+ declare type Data = string | ArrayBuffer | ReadableStream;
5
4
  export interface Env {
6
5
  }
7
- export declare class Context {
8
- req: Request;
6
+ export declare class Context<RequestParamKeyType = string> {
7
+ req: Request<RequestParamKeyType>;
9
8
  res: Response;
10
9
  env: Env;
11
10
  event: FetchEvent;
@@ -13,16 +12,15 @@ export declare class Context {
13
12
  private _status;
14
13
  private _statusText;
15
14
  render: (template: string, params?: object, options?: object) => Promise<Response>;
16
- constructor(req: Request, opts?: {
15
+ constructor(req: Request<RequestParamKeyType>, opts?: {
17
16
  res: Response;
18
17
  env: Env;
19
18
  event: FetchEvent;
20
19
  });
21
20
  header(name: string, value: string): void;
22
21
  status(number: number): void;
23
- statusText(text: string): void;
24
- newResponse(data: any, init?: ResponseInit): Response;
25
- body(data: any, status?: number, headers?: Headers): Response;
22
+ newResponse(data: Data, init?: ResponseInit): Response;
23
+ body(data: Data, status?: number, headers?: Headers): Response;
26
24
  text(text: string, status?: number, headers?: Headers): Response;
27
25
  json(object: object, status?: number, headers?: Headers): Response;
28
26
  html(html: string, status?: number, headers?: Headers): Response;
package/dist/context.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Context = void 0;
4
4
  const url_1 = require("./utils/url");
5
+ const http_status_1 = require("./utils/http-status");
5
6
  class Context {
6
7
  constructor(req, opts) {
7
8
  this.req = req;
@@ -13,13 +14,25 @@ class Context {
13
14
  this._headers = {};
14
15
  }
15
16
  header(name, value) {
17
+ /*
18
+ XXX:
19
+ app.use('*', (c, next) => {
20
+ next()
21
+ c.header('foo', 'bar') // => c.res.headers.set(...)
22
+ })
23
+ */
24
+ if (this.res) {
25
+ this.res.headers.set(name, value);
26
+ }
16
27
  this._headers[name] = value;
17
28
  }
18
29
  status(number) {
30
+ if (this.res) {
31
+ console.warn('c.res.status is already set.');
32
+ return;
33
+ }
19
34
  this._status = number;
20
- }
21
- statusText(text) {
22
- this._statusText = text;
35
+ this._statusText = (0, http_status_1.getStatusText)(number);
23
36
  }
24
37
  newResponse(data, init = {}) {
25
38
  init.status = init.status || this._status;
@@ -35,9 +48,6 @@ class Context {
35
48
  const Encoder = new TextEncoder();
36
49
  length = Encoder.encode(data).byteLength || 0;
37
50
  }
38
- else {
39
- length = data.bytelength;
40
- }
41
51
  }
42
52
  init.headers = Object.assign(Object.assign({}, init.headers), { 'Content-Length': length.toString() });
43
53
  return new Response(data, init);
package/dist/hono.d.ts CHANGED
@@ -4,42 +4,50 @@ import { Node } from './node';
4
4
  import { Context } from './context';
5
5
  import type { Env } from './context';
6
6
  declare global {
7
- interface Request {
8
- param: (key: string) => string;
9
- query: (key: string) => string | null;
7
+ interface Request<ParamKeyType = string> {
8
+ param: (key: ParamKeyType) => string;
9
+ query: (key: string) => string;
10
10
  header: (name: string) => string;
11
11
  parsedBody: any;
12
12
  }
13
13
  }
14
- export declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
14
+ export declare type Handler<RequestParamKeyType = string> = (c: Context<RequestParamKeyType>, next?: Function) => Response | Promise<Response>;
15
15
  export declare type MiddlewareHandler = (c: Context, next: Function) => Promise<void>;
16
+ declare type ParamKeyName<NameWithPattern> = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern;
17
+ declare type ParamKey<Component> = Component extends `:${infer NameWithPattern}` ? ParamKeyName<NameWithPattern> : never;
18
+ declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
16
19
  export declare class Router<T> {
17
20
  node: Node<T>;
18
21
  constructor();
19
22
  add(method: string, path: string, handler: T): void;
20
23
  match(method: string, path: string): Result<T> | null;
21
24
  }
25
+ declare type Init = {
26
+ strict?: boolean;
27
+ };
22
28
  export declare class Hono {
23
29
  router: Router<Handler[]>;
24
30
  middlewareRouters: Router<MiddlewareHandler>[];
25
31
  tempPath: string;
26
- constructor();
27
- get(arg: string | Handler, ...args: Handler[]): Hono;
28
- post(arg: string | Handler, ...args: Handler[]): Hono;
29
- put(arg: string | Handler, ...args: Handler[]): Hono;
30
- head(arg: string | Handler, ...args: Handler[]): Hono;
31
- delete(arg: string | Handler, ...args: Handler[]): Hono;
32
- options(arg: string | Handler, ...args: Handler[]): Hono;
33
- patch(arg: string | Handler, ...args: Handler[]): Hono;
34
- all(arg: string | Handler, ...args: Handler[]): Hono;
32
+ strict: boolean;
33
+ constructor(init?: Init);
34
+ get<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
35
+ post<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
36
+ put<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
37
+ head<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
38
+ delete<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
39
+ options<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
40
+ patch<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
41
+ all<Path extends string>(path: Path, ...args: Handler<ParamKeys<Path>>[]): Hono;
35
42
  route(path: string): Hono;
36
43
  use(path: string, middleware: MiddlewareHandler): void;
37
- addRoute(method: string, arg: string | Handler, ...args: Handler[]): Hono;
44
+ addRoute(method: string, path: string, ...args: Handler[]): Hono;
38
45
  matchRoute(method: string, path: string): Promise<Result<Handler[]>>;
39
46
  dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
40
47
  handleEvent(event: FetchEvent): Promise<Response>;
41
48
  fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
42
49
  fire(): void;
43
- onError(err: any): Response;
50
+ onError(err: Error): Response;
44
51
  notFound(): Response;
45
52
  }
53
+ export {};
package/dist/hono.js CHANGED
@@ -4,9 +4,7 @@ exports.Hono = exports.Router = void 0;
4
4
  const node_1 = require("./node");
5
5
  const compose_1 = require("./compose");
6
6
  const url_1 = require("./utils/url");
7
- const middleware_1 = require("./middleware");
8
7
  const context_1 = require("./context");
9
- const METHOD_NAME_OF_ALL = 'ALL';
10
8
  class Router {
11
9
  constructor() {
12
10
  this.node = new node_1.Node();
@@ -20,102 +18,95 @@ class Router {
20
18
  }
21
19
  exports.Router = Router;
22
20
  class Hono {
23
- constructor() {
21
+ constructor(init = { strict: true }) {
24
22
  this.router = new Router();
25
23
  this.middlewareRouters = [];
26
- this.tempPath = '/';
27
- }
28
- /* HTTP METHODS */
29
- get(arg, ...args) {
30
- return this.addRoute('get', arg, ...args);
31
- }
32
- post(arg, ...args) {
33
- return this.addRoute('post', arg, ...args);
34
- }
35
- put(arg, ...args) {
36
- return this.addRoute('put', arg, ...args);
37
- }
38
- head(arg, ...args) {
39
- return this.addRoute('head', arg, ...args);
40
- }
41
- delete(arg, ...args) {
42
- return this.addRoute('delete', arg, ...args);
43
- }
44
- options(arg, ...args) {
45
- return this.addRoute('options', arg, ...args);
46
- }
47
- patch(arg, ...args) {
48
- return this.addRoute('patch', arg, ...args);
49
- }
50
- /*
51
- We may implement these HTTP methods:
52
- trace
53
- copy
54
- lock
55
- purge
56
- unlock
57
- report
58
- checkout
59
- merge
60
- notify
61
- subscribe
62
- unsubscribe
63
- search
64
- connect
65
- */
66
- all(arg, ...args) {
67
- return this.addRoute('all', arg, ...args);
24
+ this.tempPath = null;
25
+ this.strict = init.strict; // strict routing - default is true
26
+ }
27
+ get(path, ...args) {
28
+ return this.addRoute('get', path, ...args);
29
+ }
30
+ post(path, ...args) {
31
+ return this.addRoute('post', path, ...args);
32
+ }
33
+ put(path, ...args) {
34
+ return this.addRoute('put', path, ...args);
35
+ }
36
+ head(path, ...args) {
37
+ return this.addRoute('head', path, ...args);
38
+ }
39
+ delete(path, ...args) {
40
+ return this.addRoute('delete', path, ...args);
41
+ }
42
+ options(path, ...args) {
43
+ return this.addRoute('options', path, ...args);
44
+ }
45
+ patch(path, ...args) {
46
+ return this.addRoute('patch', path, ...args);
47
+ }
48
+ all(path, ...args) {
49
+ return this.addRoute('all', path, ...args);
68
50
  }
69
51
  route(path) {
70
- this.tempPath = path;
71
- return this;
52
+ const newHono = new Hono();
53
+ newHono.tempPath = path;
54
+ newHono.router = this.router;
55
+ return newHono;
72
56
  }
73
57
  use(path, middleware) {
74
58
  if (middleware.constructor.name !== 'AsyncFunction') {
75
59
  throw new TypeError('middleware must be a async function!');
76
60
  }
77
61
  const router = new Router();
78
- router.add(METHOD_NAME_OF_ALL, path, middleware);
62
+ router.add(node_1.METHOD_NAME_OF_ALL, path, middleware);
79
63
  this.middlewareRouters.push(router);
80
64
  }
81
65
  // addRoute('get', '/', handler)
82
- addRoute(method, arg, ...args) {
66
+ addRoute(method, path, ...args) {
83
67
  method = method.toUpperCase();
84
- if (typeof arg === 'string') {
85
- this.tempPath = arg;
86
- this.router.add(method, arg, args);
87
- }
88
- else {
89
- args.unshift(arg);
90
- this.router.add(method, this.tempPath, args);
68
+ if (this.tempPath) {
69
+ path = (0, url_1.mergePath)(this.tempPath, path);
91
70
  }
71
+ this.router.add(method, path, args);
92
72
  return this;
93
73
  }
94
74
  async matchRoute(method, path) {
95
75
  return this.router.match(method, path);
96
76
  }
97
77
  async dispatch(request, env, event) {
98
- const [method, path] = [request.method, (0, url_1.getPathFromURL)(request.url)];
78
+ const path = (0, url_1.getPathFromURL)(request.url, { strict: this.strict });
79
+ const method = request.method;
99
80
  const result = await this.matchRoute(method, path);
81
+ // Methods for Request object
100
82
  request.param = (key) => {
101
83
  if (result) {
102
84
  return result.params[key];
103
85
  }
104
- return '';
86
+ };
87
+ request.header = (name) => {
88
+ return request.headers.get(name);
89
+ };
90
+ request.query = (key) => {
91
+ const url = new URL(c.req.url);
92
+ return url.searchParams.get(key);
105
93
  };
106
94
  const handler = result ? result.handler[0] : this.notFound; // XXX
107
95
  const middleware = [];
108
96
  for (const mr of this.middlewareRouters) {
109
- const mwResult = mr.match(METHOD_NAME_OF_ALL, path);
97
+ const mwResult = mr.match(node_1.METHOD_NAME_OF_ALL, path);
110
98
  if (mwResult) {
111
99
  middleware.push(mwResult.handler);
112
100
  }
113
101
  }
114
102
  const wrappedHandler = async (context, next) => {
115
- context.res = await handler(context);
103
+ const res = await handler(context);
104
+ if (!(res instanceof Response)) {
105
+ throw new TypeError('response must be a instace of Response');
106
+ }
107
+ context.res = res;
116
108
  await next();
117
109
  };
118
- middleware.push(middleware_1.Middleware.default);
119
110
  middleware.push(wrappedHandler);
120
111
  const composed = (0, compose_1.compose)(middleware);
121
112
  const c = new context_1.Context(request, { env: env, event: event, res: null });
@@ -149,7 +140,7 @@ class Hono {
149
140
  }
150
141
  notFound() {
151
142
  const message = 'Not Found';
152
- return new Response('Not Found', {
143
+ return new Response(message, {
153
144
  status: 404,
154
145
  headers: {
155
146
  'Content-Length': message.length.toString(),
@@ -1,6 +1,6 @@
1
1
  import type { Context } from '../../context';
2
- declare type Options = {
2
+ declare type Init = {
3
3
  root: string;
4
4
  };
5
- export declare const mustache: (opt?: Options) => (c: Context, next: Function) => Promise<void>;
5
+ export declare const mustache: (init?: Init) => (c: Context, next: Function) => Promise<void>;
6
6
  export {};
@@ -4,8 +4,8 @@ exports.mustache = void 0;
4
4
  const cloudflare_1 = require("../../utils/cloudflare");
5
5
  const EXTENSION = '.mustache';
6
6
  const DEFAULT_DOCUMENT = 'index.mustache';
7
- const mustache = (opt = { root: '' }) => {
8
- const { root } = opt;
7
+ const mustache = (init = { root: '' }) => {
8
+ const { root } = init;
9
9
  return async (c, next) => {
10
10
  let Mustache;
11
11
  try {
@@ -14,8 +14,12 @@ const mustache = (opt = { root: '' }) => {
14
14
  catch (_a) {
15
15
  throw new Error('If you want to use Mustache Middleware, install "mustache" package first.');
16
16
  }
17
- c.render = async (filename, view = {}, options) => {
18
- const path = (0, cloudflare_1.getKVFilePath)({ filename: `${filename}${EXTENSION}`, root: root, defaultDocument: DEFAULT_DOCUMENT });
17
+ c.render = async (filename, params = {}, options) => {
18
+ const path = (0, cloudflare_1.getKVFilePath)({
19
+ filename: `${filename}${EXTENSION}`,
20
+ root: root,
21
+ defaultDocument: DEFAULT_DOCUMENT,
22
+ });
19
23
  const buffer = await (0, cloudflare_1.getContentFromKVAsset)(path);
20
24
  if (!buffer) {
21
25
  throw new Error(`Template "${path}" is not found or blank.`);
@@ -37,7 +41,7 @@ const mustache = (opt = { root: '' }) => {
37
41
  partialArgs[key] = bufferToString(partialBuffer);
38
42
  }
39
43
  }
40
- const output = Mustache.render(content, view, partialArgs);
44
+ const output = Mustache.render(content, params, partialArgs);
41
45
  return c.html(output);
42
46
  };
43
47
  await next();
package/dist/node.d.ts CHANGED
@@ -1,24 +1,15 @@
1
+ export declare const METHOD_NAME_OF_ALL = "ALL";
1
2
  export declare class Result<T> {
2
3
  handler: T;
3
- params: {
4
- [key: string]: string;
5
- };
6
- constructor(handler: T, params: {
7
- [key: string]: string;
8
- });
4
+ params: Record<string, string>;
5
+ constructor(handler: T, params: Record<string, string>);
9
6
  }
10
7
  export declare class Node<T> {
11
- method: {
12
- [key: string]: T;
13
- };
8
+ method: Record<string, T>;
14
9
  handler: T;
15
- children: {
16
- [key: string]: Node<T>;
17
- };
10
+ children: Record<string, Node<T>>;
18
11
  middlewares: [];
19
- constructor(method?: string, handler?: any, children?: {
20
- [key: string]: Node<T>;
21
- });
12
+ constructor(method?: string, handler?: T, children?: Record<string, Node<T>>);
22
13
  insert(method: string, path: string, handler: T): Node<T>;
23
14
  search(method: string, path: string): Result<T>;
24
15
  }
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Node = exports.Result = void 0;
3
+ exports.Node = exports.Result = exports.METHOD_NAME_OF_ALL = void 0;
4
4
  const url_1 = require("./utils/url");
5
- const METHOD_NAME_OF_ALL = 'ALL';
5
+ exports.METHOD_NAME_OF_ALL = 'ALL';
6
6
  class Result {
7
7
  constructor(handler, params) {
8
8
  this.handler = handler;
@@ -95,7 +95,7 @@ class Node {
95
95
  return noRoute();
96
96
  }
97
97
  }
98
- const handler = curNode.method[METHOD_NAME_OF_ALL] || curNode.method[method];
98
+ const handler = curNode.method[exports.METHOD_NAME_OF_ALL] || curNode.method[method];
99
99
  if (!handler) {
100
100
  return noRoute();
101
101
  }
@@ -1,4 +1,4 @@
1
1
  export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
2
2
  export declare const decodeBase64: (str: string) => any;
3
- export declare const sha256: (a: string) => Promise<string>;
4
- export declare const timingSafeEqual: (a: any, b: any) => Promise<boolean>;
3
+ export declare const sha256: (a: string | object | boolean) => Promise<string>;
4
+ export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean) => Promise<boolean>;
@@ -0,0 +1 @@
1
+ export declare const getStatusText: (statusNumber: number) => string;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStatusText = void 0;
4
+ const getStatusText = (statusNumber) => {
5
+ const text = statuses[statusNumber];
6
+ return text;
7
+ };
8
+ exports.getStatusText = getStatusText;
9
+ const statuses = {
10
+ 200: 'OK',
11
+ 201: 'Created',
12
+ 202: 'Accepted',
13
+ 204: 'No Content',
14
+ 206: 'Partial Content',
15
+ 301: 'Moved Permanently',
16
+ 302: 'Moved Temporarily',
17
+ 303: 'See Other',
18
+ 304: 'Not Modified',
19
+ 307: 'Temporary Redirect',
20
+ 308: 'Permanent Redirect',
21
+ 400: 'Bad Request',
22
+ 401: 'Unauthorized',
23
+ 402: 'Payment Required',
24
+ 403: 'Forbidden',
25
+ 404: 'Not Found',
26
+ 405: 'Not Allowed',
27
+ 406: 'Not Acceptable',
28
+ 408: 'Request Time-out',
29
+ 409: 'Conflict',
30
+ 410: 'Gone',
31
+ 411: 'Length Required',
32
+ 412: 'Precondition Failed',
33
+ 413: 'Request Entity Too Large',
34
+ 414: 'Request-URI Too Large',
35
+ 415: 'Unsupported Media Type',
36
+ 416: 'Requested Range Not Satisfiable',
37
+ 421: 'Misdirected Request',
38
+ 429: 'Too Many Requests',
39
+ 500: 'Internal Server Error',
40
+ 501: 'Not Implemented',
41
+ 502: 'Bad Gateway',
42
+ 503: 'Service Temporarily Unavailable',
43
+ 504: 'Gateway Time-out',
44
+ 505: 'HTTP Version Not Supported',
45
+ 507: 'Insufficient Storage',
46
+ };
@@ -1,4 +1,9 @@
1
1
  export declare const splitPath: (path: string) => string[];
2
2
  export declare const getPattern: (label: string) => string[] | null;
3
- export declare const getPathFromURL: (url: string) => string;
3
+ declare type Params = {
4
+ strict: boolean;
5
+ };
6
+ export declare const getPathFromURL: (url: string, params?: Params) => string;
4
7
  export declare const isAbsoluteURL: (url: string) => boolean;
8
+ export declare const mergePath: (...paths: string[]) => string;
9
+ export {};
package/dist/utils/url.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
3
+ exports.mergePath = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
4
4
  const URL_REGEXP = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
5
5
  const splitPath = (path) => {
6
6
  const paths = path.split(/\//); // faster than path.split('/')
@@ -26,7 +26,12 @@ const getPattern = (label) => {
26
26
  return null;
27
27
  };
28
28
  exports.getPattern = getPattern;
29
- const getPathFromURL = (url) => {
29
+ const getPathFromURL = (url, params = { strict: true }) => {
30
+ // if strict routing is false => `/hello/hey/` and `/hello/hey` are treated the same
31
+ // default is true
32
+ if (!params.strict && url.endsWith('/')) {
33
+ url = url.slice(0, -1);
34
+ }
30
35
  const match = url.match(URL_REGEXP);
31
36
  if (match) {
32
37
  return match[5];
@@ -42,3 +47,27 @@ const isAbsoluteURL = (url) => {
42
47
  return false;
43
48
  };
44
49
  exports.isAbsoluteURL = isAbsoluteURL;
50
+ const mergePath = (...paths) => {
51
+ let p = '';
52
+ let endsWithSlash = false;
53
+ for (let path of paths) {
54
+ /* ['/hey/','/say'] => ['/hey', '/say'] */
55
+ if (p.endsWith('/')) {
56
+ p = p.slice(0, -1);
57
+ endsWithSlash = true;
58
+ }
59
+ /* ['/hey','say'] => ['/hey', '/say'] */
60
+ if (!path.startsWith('/')) {
61
+ path = `/${path}`;
62
+ }
63
+ /* ['/hey/', '/'] => `/hey/` */
64
+ if (path === '/' && endsWithSlash) {
65
+ p = `${p}/`;
66
+ }
67
+ else if (path !== '/') {
68
+ p = `${p}${path}`;
69
+ }
70
+ }
71
+ return p;
72
+ };
73
+ exports.mergePath = mergePath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "[炎] Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,7 +16,9 @@
16
16
  "./logger": "./dist/middleware/logger/logger.js",
17
17
  "./mustache": "./dist/middleware/mustache/mustache.js",
18
18
  "./powered-by": "./dist/middleware/powered-by/powered-by.js",
19
- "./serve-static": "./dist/middleware/serve-static/serve-static.js"
19
+ "./serve-static": "./dist/middleware/serve-static/serve-static.js",
20
+ "./utils/buffer": "./dist/utils/buffer.js",
21
+ "./package.json": "./package.json"
20
22
  },
21
23
  "typesVersions": {
22
24
  "*": {
@@ -1,2 +0,0 @@
1
- import type { Context } from '../context';
2
- export declare const defaultMiddleware: (c: Context, next: Function) => Promise<void>;
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.defaultMiddleware = void 0;
4
- const defaultMiddleware = async (c, next) => {
5
- c.req.query = (key) => {
6
- // eslint-disable-next-line
7
- const url = new URL(c.req.url);
8
- return url.searchParams.get(key);
9
- };
10
- c.req.header = (name) => {
11
- return c.req.headers.get(name);
12
- };
13
- await next();
14
- };
15
- exports.defaultMiddleware = defaultMiddleware;
@@ -1,3 +0,0 @@
1
- export declare class Middleware {
2
- static default: (c: import("./context").Context, next: Function) => Promise<void>;
3
- }
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Middleware = void 0;
4
- const default_1 = require("./middleware/default");
5
- class Middleware {
6
- }
7
- exports.Middleware = Middleware;
8
- Middleware.default = default_1.defaultMiddleware;