hono 0.4.2 → 0.5.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
@@ -23,25 +23,25 @@ app.fire()
23
23
 
24
24
  - **Ultrafast** - the router does not use linear loops.
25
25
  - **Zero-dependencies** - using only Web standard API.
26
- - **Middleware** - builtin middleware and your own middleware.
26
+ - **Middleware** - built-in middleware and ability to extend with your own middleware.
27
27
  - **Optimized** - for Cloudflare Workers.
28
28
 
29
29
  ## Benchmark
30
30
 
31
- **Hono is fastest** compared to other routers for Cloudflare Workers.
31
+ **Hono is fastest**, compared to other routers for Cloudflare Workers.
32
32
 
33
33
  ```plain
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)
34
+ hono x 809,503 ops/sec ±6.94% (73 runs sampled)
35
+ itty-router x 157,310 ops/sec ±4.31% (87 runs sampled)
36
+ sunder x 328,350 ops/sec ±2.30% (95 runs sampled)
37
+ worktop x 209,758 ops/sec ±4.28% (83 runs sampled)
38
38
  Fastest is hono
39
- ✨ Done in 58.29s.
39
+ ✨ Done in 60.66s.
40
40
  ```
41
41
 
42
42
  ## Hono in 1 minute
43
43
 
44
- A demonstration to create an application of Cloudflare Workers with Hono.
44
+ A demonstration to create an application for Cloudflare Workers with Hono.
45
45
 
46
46
  ![Demo](https://user-images.githubusercontent.com/10682/151973526-342644f9-71c5-4fee-81f4-64a7558bb192.gif)
47
47
 
@@ -71,6 +71,8 @@ An instance of `Hono` has these methods.
71
71
  - app.**all**(path, handler)
72
72
  - app.**route**(path)
73
73
  - app.**use**(path, middleware)
74
+ - app.**notFound**(handler)
75
+ - app.**onError**(err, handler)
74
76
  - app.**fire**()
75
77
  - app.**fetch**(request, env, event)
76
78
 
@@ -126,10 +128,10 @@ book.post('/', (c) => c.text('Create Book')) // POST /book
126
128
 
127
129
  ### no strict
128
130
 
129
- If `strict` is set `false`, `/hello`and`/hello/` are treated the same. Default is `true`.
131
+ If `strict` is set false, `/hello`and`/hello/` are treated the same.
130
132
 
131
133
  ```js
132
- const app = new Hono({ strict: false })
134
+ const app = new Hono({ strict: false }) // Default is true
133
135
 
134
136
  app.get('/hello', (c) => c.text('/hello or /hello/'))
135
137
  ```
@@ -145,7 +147,7 @@ app.get('/fetch-url', async (c) => {
145
147
 
146
148
  ## Middleware
147
149
 
148
- ### Builtin Middleware
150
+ ### Built-in Middleware
149
151
 
150
152
  ```js
151
153
  import { Hono } from 'hono'
@@ -166,7 +168,7 @@ app.use(
166
168
  )
167
169
  ```
168
170
 
169
- Available builtin middleware are listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
171
+ Available built-in middleware is listed on [src/middleware](https://github.com/yusukebe/hono/tree/master/src/middleware).
170
172
 
171
173
  ### Custom Middleware
172
174
 
@@ -182,12 +184,33 @@ app.use('*', async (c, next) => {
182
184
  // Add a custom header
183
185
  app.use('/message/*', async (c, next) => {
184
186
  await next()
185
- await c.header('x-message', 'This is middleware!')
187
+ c.header('x-message', 'This is middleware!')
186
188
  })
187
189
 
188
190
  app.get('/message/hello', (c) => c.text('Hello Middleware!'))
189
191
  ```
190
192
 
193
+ ## Not Found
194
+
195
+ `app.notFound` for customizing Not Found Response.
196
+
197
+ ```js
198
+ app.notFound((c) => {
199
+ return c.text('Custom 404 Message', 404)
200
+ })
201
+ ```
202
+
203
+ ## Error Handling
204
+
205
+ `app.onError` handle the error and return the customized Response.
206
+
207
+ ```js
208
+ app.onError((err, c) => {
209
+ console.error(`${err}`)
210
+ return c.text('Custom Error Message', 500)
211
+ })
212
+ ```
213
+
191
214
  ## Context
192
215
 
193
216
  To handle Request and Reponse, you can use Context object.
@@ -224,10 +247,12 @@ app.get('/entry/:id', (c) => {
224
247
 
225
248
  ```js
226
249
  app.get('/welcome', (c) => {
250
+ // Set headers
227
251
  c.header('X-Message', 'Hello!')
228
252
  c.header('Content-Type', 'text/plain')
253
+ // Set HTTP status code
229
254
  c.status(201)
230
-
255
+ // Return the response body
231
256
  return c.body('Thank you for comming')
232
257
  })
233
258
  ```
@@ -248,7 +273,7 @@ new Response('Thank you for comming', {
248
273
 
249
274
  ### c.text()
250
275
 
251
- Render texts as `Content-Type:text/plain`.
276
+ Render text as `Content-Type:text/plain`.
252
277
 
253
278
  ```js
254
279
  app.get('/say', (c) => {
@@ -278,7 +303,7 @@ app.get('/', (c) => {
278
303
 
279
304
  ### c.notFound()
280
305
 
281
- Return the default `404 Not Found` Response.
306
+ Return the `404 Not Found` Response.
282
307
 
283
308
  ```js
284
309
  app.get('/notfound', (c) => {
@@ -327,26 +352,6 @@ app.get('*', async c => {
327
352
  })
328
353
  ```
329
354
 
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
-
350
355
  ## fire
351
356
 
352
357
  `app.fire()` do this.
@@ -359,7 +364,7 @@ addEventListener('fetch', (event) => {
359
364
 
360
365
  ## fetch
361
366
 
362
- `app.fetch()` is for Cloudflare Module Worker syntax.
367
+ `app.fetch` for Cloudflare Module Worker syntax.
363
368
 
364
369
  ```js
365
370
  export default {
@@ -376,19 +381,22 @@ export default app
376
381
 
377
382
  ## Cloudflare Workers with Hono
378
383
 
379
- Using `wrangler` or `miniflare`, you can develop the application locally and publish it with few commands.
384
+ 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.
380
385
 
381
386
  Let's write your first code for Cloudflare Workers with Hono.
382
387
 
383
- ### 1. Install Wrangler
388
+ ---
384
389
 
385
- Install Cloudflare Command Line "[Wrangler](https://github.com/cloudflare/wrangler)".
390
+ ### Caution
386
391
 
387
- ```sh
388
- $ npm i @cloudflare/wrangler -g
389
- ```
392
+ **Wrangler 1.x** does not support importing middleware. We recommend two ways:
393
+
394
+ 1. Use [Wragler 2.0 Beta](https://github.com/cloudflare/wrangler2).
395
+ 2. Build without webpack 4.x. For example, you can use esbuild. See [the starter template](https://github.com/yusukebe/hono-minimal).
396
+
397
+ ---
390
398
 
391
- ### 2. `npm init`
399
+ ### 1. `npm init`
392
400
 
393
401
  Make a npm skeleton directory.
394
402
 
@@ -398,15 +406,23 @@ cd hono-example
398
406
  npm init -y
399
407
  ```
400
408
 
401
- ### 3. `wrangler init`
409
+ ### 2. `wrangler init`
402
410
 
403
- Init as a wrangler project.
411
+ Initialize as a wrangler project.
404
412
 
405
- ```sh
406
- $ wrangler init
413
+ ```
414
+ $ npx wrangler@beta init
415
+ ```
416
+
417
+ Answer the questions. If you want, you can answer `y`.
418
+
419
+ ```
420
+ Would you like to install wrangler into your package.json? (y/n) <--- n
421
+ Would you like to use TypeScript? (y/n) <--- n
422
+ Would you like to create a Worker at src/index.js? (y/n) <--- n
407
423
  ```
408
424
 
409
- ### 4. `npm install hono`
425
+ ### 3. `npm install hono`
410
426
 
411
427
  Install `hono` from the npm registry.
412
428
 
@@ -414,11 +430,12 @@ Install `hono` from the npm registry.
414
430
  $ npm i hono
415
431
  ```
416
432
 
417
- ### 5. Write your app
433
+ ### 4. Write your app
418
434
 
419
435
  Only 4 lines!!
420
436
 
421
437
  ```js
438
+ // index.js
422
439
  import { Hono } from 'hono'
423
440
  const app = new Hono()
424
441
 
@@ -427,25 +444,25 @@ app.get('/', (c) => c.text('Hello! Hono!'))
427
444
  app.fire()
428
445
  ```
429
446
 
430
- ### 6. Run
447
+ ### 5. Run
431
448
 
432
449
  Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
433
450
 
434
451
  ```sh
435
- $ wrangler dev
452
+ $ npx wrangler@beta dev index.js
436
453
  ```
437
454
 
438
- ### 7. Publish
455
+ ### 6. Publish
439
456
 
440
457
  Deploy to Cloudflare. That's all!
441
458
 
442
459
  ```sh
443
- $ wrangler publish
460
+ $ npx wrangler@beta publish index.js
444
461
  ```
445
462
 
446
463
  ## Starter template
447
464
 
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.
465
+ You can start making your Cloudflare Workers application with [the starter template](https://github.com/yusukebe/hono-minimal). It is really minimal using TypeScript, esbuild, and Miniflare.
449
466
 
450
467
  To generate a project skelton, run this command.
451
468
 
package/dist/compose.d.ts CHANGED
@@ -1 +1,2 @@
1
- export declare const compose: <T>(middleware: Function[], onError?: Function) => (context: T, next?: Function) => Promise<void | object>;
1
+ import type { ErrorHandler } from './hono';
2
+ export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler) => (context: C) => Promise<C>;
package/dist/compose.js CHANGED
@@ -4,33 +4,31 @@ exports.compose = void 0;
4
4
  const context_1 = require("./context");
5
5
  // Based on the code in the MIT licensed `koa-compose` package.
6
6
  const compose = (middleware, onError) => {
7
- const errors = [];
8
- return function (context, next) {
7
+ return function (context) {
9
8
  let index = -1;
10
9
  return dispatch(0);
11
10
  async function dispatch(i) {
12
- if (i <= index)
13
- return Promise.reject(new Error('next() called multiple times'));
14
- index = i;
15
- let fn = middleware[i];
16
- if (i === middleware.length)
17
- fn = next;
18
- if (!fn)
19
- return Promise.resolve();
20
- try {
21
- return Promise.resolve(fn(context, dispatch.bind(null, i + 1))).catch((e) => {
22
- errors.push(e);
23
- if (onError && context instanceof context_1.Context) {
24
- context.res = onError(errors[0], context);
25
- }
26
- else {
27
- throw errors[0];
28
- }
29
- });
11
+ if (i === middleware.length) {
12
+ return context;
30
13
  }
31
- catch (err) {
32
- return Promise.reject(err);
14
+ if (i <= index) {
15
+ return Promise.reject(new Error('next() called multiple times'));
33
16
  }
17
+ const handler = middleware[i];
18
+ index = i;
19
+ return Promise.resolve(handler(context, dispatch.bind(null, i + 1)))
20
+ .then(() => {
21
+ return context;
22
+ })
23
+ .catch((err) => {
24
+ if (onError && context instanceof context_1.Context) {
25
+ context.res = onError(err, context);
26
+ return context;
27
+ }
28
+ else {
29
+ throw err;
30
+ }
31
+ });
34
32
  }
35
33
  };
36
34
  };
package/dist/hono.d.ts CHANGED
@@ -12,6 +12,8 @@ declare global {
12
12
  }
13
13
  export declare type Handler<RequestParamKeyType = string> = (c: Context<RequestParamKeyType>, next?: Function) => Response | Promise<Response>;
14
14
  export declare type MiddlewareHandler = (c: Context, next: Function) => Promise<void>;
15
+ export declare type NotFoundHandler = (c: Context) => Response;
16
+ export declare type ErrorHandler = (err: Error, c: Context) => Response;
15
17
  declare type ParamKeyName<NameWithPattern> = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern;
16
18
  declare type ParamKey<Component> = Component extends `:${infer NameWithPattern}` ? ParamKeyName<NameWithPattern> : never;
17
19
  declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
@@ -24,6 +26,8 @@ export declare class Hono {
24
26
  middlewareRouters: Router<MiddlewareHandler>[];
25
27
  tempPath: string;
26
28
  constructor(init?: Partial<Pick<Hono, 'routerClass' | 'strict'>>);
29
+ notFoundHandler: NotFoundHandler;
30
+ errorHandler: ErrorHandler;
27
31
  get<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
28
32
  post<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
29
33
  put<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
@@ -34,13 +38,13 @@ export declare class Hono {
34
38
  all<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>>): Hono;
35
39
  route(path: string): Hono;
36
40
  use(path: string, middleware: MiddlewareHandler): void;
41
+ onError(handler: ErrorHandler): Hono;
42
+ notFound(handler: NotFoundHandler): Hono;
37
43
  addRoute(method: string, path: string, handler: Handler): Hono;
38
44
  matchRoute(method: string, path: string): Promise<Result<Handler>>;
39
45
  dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
40
46
  handleEvent(event: FetchEvent): Promise<Response>;
41
47
  fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
42
48
  fire(): void;
43
- onError(err: Error, c: Context): Response;
44
- notFound(c: Context): Response;
45
49
  }
46
50
  export {};
package/dist/hono.js CHANGED
@@ -10,6 +10,15 @@ class Hono {
10
10
  constructor(init = {}) {
11
11
  this.routerClass = trie_router_1.TrieRouter;
12
12
  this.strict = true; // strict routing - default is true
13
+ this.notFoundHandler = (c) => {
14
+ const message = '404 Not Found';
15
+ return c.text(message, 404);
16
+ };
17
+ this.errorHandler = (err, c) => {
18
+ console.error(`${err.message}`);
19
+ const message = 'Internal Server Error';
20
+ return c.text(message, 500);
21
+ };
13
22
  Object.assign(this, init);
14
23
  this.router = new this.routerClass();
15
24
  this.middlewareRouters = [];
@@ -53,6 +62,14 @@ class Hono {
53
62
  router.add(router_1.METHOD_NAME_OF_ALL, path, middleware);
54
63
  this.middlewareRouters.push(router);
55
64
  }
65
+ onError(handler) {
66
+ this.errorHandler = handler;
67
+ return this;
68
+ }
69
+ notFound(handler) {
70
+ this.notFoundHandler = handler;
71
+ return this;
72
+ }
56
73
  // addRoute('get', '/', handler)
57
74
  addRoute(method, path, handler) {
58
75
  method = method.toUpperCase();
@@ -82,7 +99,7 @@ class Hono {
82
99
  const url = new URL(c.req.url);
83
100
  return url.searchParams.get(key);
84
101
  };
85
- const handler = result ? result.handler : this.notFound;
102
+ const handler = result ? result.handler : this.notFoundHandler;
86
103
  const middleware = [];
87
104
  for (const mr of this.middlewareRouters) {
88
105
  const mwResult = mr.match(router_1.METHOD_NAME_OF_ALL, path);
@@ -99,11 +116,11 @@ class Hono {
99
116
  await next();
100
117
  };
101
118
  middleware.push(wrappedHandler);
102
- const composed = (0, compose_1.compose)(middleware, this.onError);
119
+ const composed = (0, compose_1.compose)(middleware, this.errorHandler);
103
120
  const c = new context_1.Context(request, { env: env, event: event, res: null });
104
- c.notFound = () => this.notFound(c);
105
- await composed(c);
106
- return c.res;
121
+ c.notFound = () => this.notFoundHandler(c);
122
+ const context = await composed(c);
123
+ return context.res;
107
124
  }
108
125
  async handleEvent(event) {
109
126
  return this.dispatch(event.request, {}, event);
@@ -116,16 +133,5 @@ class Hono {
116
133
  event.respondWith(this.handleEvent(event));
117
134
  });
118
135
  }
119
- // Default error Response
120
- onError(err, c) {
121
- console.error(`${err.message}`);
122
- const message = 'Internal Server Error';
123
- return c.text(message, 500);
124
- }
125
- // Default 404 not found Response
126
- notFound(c) {
127
- const message = 'Not Found';
128
- return c.text(message, 404);
129
- }
130
136
  }
131
137
  exports.Hono = Hono;
@@ -3,6 +3,7 @@ export declare const basicAuth: (options: {
3
3
  username: string;
4
4
  password: string;
5
5
  realm?: string;
6
+ hashFunction?: Function;
6
7
  }, ...users: {
7
8
  username: string;
8
9
  password: string;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.basicAuth = void 0;
4
4
  const buffer_1 = require("../../utils/buffer");
5
+ const crypto_1 = require("../../utils/crypto");
5
6
  const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
6
7
  const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
7
8
  const auth = (req) => {
@@ -18,7 +19,7 @@ const auth = (req) => {
18
19
  if (!match) {
19
20
  return undefined;
20
21
  }
21
- const userPass = USER_PASS_REGEXP.exec((0, buffer_1.decodeBase64)(match[1]));
22
+ const userPass = USER_PASS_REGEXP.exec((0, crypto_1.decodeBase64)(match[1]));
22
23
  if (!userPass) {
23
24
  return undefined;
24
25
  }
@@ -36,8 +37,8 @@ const basicAuth = (options, ...users) => {
36
37
  const requestUser = auth(ctx.req);
37
38
  if (requestUser) {
38
39
  for (const user of users) {
39
- const usernameEqual = await (0, buffer_1.timingSafeEqual)(user.username, requestUser.username);
40
- const passwordEqual = await (0, buffer_1.timingSafeEqual)(user.password, requestUser.password);
40
+ const usernameEqual = await (0, buffer_1.timingSafeEqual)(user.username, requestUser.username, options.hashFunction);
41
+ const passwordEqual = await (0, buffer_1.timingSafeEqual)(user.password, requestUser.password, options.hashFunction);
41
42
  if (usernameEqual && passwordEqual) {
42
43
  // Authorized OK
43
44
  return next();
@@ -1,26 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bodyParse = void 0;
4
+ const body_1 = require("../../utils/body");
4
5
  const bodyParse = () => {
5
6
  return async (ctx, next) => {
6
- const contentType = ctx.req.headers.get('Content-Type') || '';
7
- if (contentType.includes('application/json')) {
8
- ctx.req.parsedBody = await ctx.req.json();
9
- }
10
- else if (contentType.includes('application/text')) {
11
- ctx.req.parsedBody = await ctx.req.text();
12
- }
13
- else if (contentType.includes('text/html')) {
14
- ctx.req.parsedBody = await ctx.req.text();
15
- }
16
- else if (contentType.includes('form')) {
17
- const form = {};
18
- const data = [...(await ctx.req.formData())].reduce((acc, cur) => {
19
- acc[cur[0]] = cur[1];
20
- return acc;
21
- }, form);
22
- ctx.req.parsedBody = data;
23
- }
7
+ ctx.req.parsedBody = await (0, body_1.parseBody)(ctx.req);
24
8
  await next();
25
9
  };
26
10
  };
@@ -0,0 +1,6 @@
1
+ import type { Context } from '../../context';
2
+ declare type ETagOptions = {
3
+ weak: boolean;
4
+ };
5
+ export declare const etag: (options?: ETagOptions) => (c: Context, next: Function) => Promise<void>;
6
+ export {};
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.etag = void 0;
4
+ const crypto_1 = require("../../utils/crypto");
5
+ const body_1 = require("../../utils/body");
6
+ const etag = (options = { weak: false }) => {
7
+ return async (c, next) => {
8
+ const ifNoneMatch = c.req.header('If-None-Match') || c.req.header('if-none-match');
9
+ await next();
10
+ const clone = c.res.clone();
11
+ const body = await (0, body_1.parseBody)(c.res);
12
+ const hash = await (0, crypto_1.sha1)(body);
13
+ const etag = options.weak ? `W/"${hash}"` : `"${hash}"`;
14
+ if (ifNoneMatch && ifNoneMatch === etag) {
15
+ await clone.blob(); // Force using body
16
+ c.res = new Response(null, {
17
+ status: 304,
18
+ statusText: 'Not Modified',
19
+ });
20
+ c.res.headers.delete('Content-Length');
21
+ }
22
+ else {
23
+ c.res = new Response(clone.body, clone);
24
+ c.res.headers.append('ETag', etag);
25
+ }
26
+ };
27
+ };
28
+ exports.etag = etag;
@@ -0,0 +1 @@
1
+ export declare const parseBody: (r: Request | Response) => Promise<string | object | Record<string, string | File>>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBody = void 0;
4
+ const parseBody = async (r) => {
5
+ const contentType = r.headers.get('Content-Type') || '';
6
+ if (contentType.includes('application/json')) {
7
+ return await r.json();
8
+ }
9
+ else if (contentType.includes('application/text')) {
10
+ return r.text();
11
+ }
12
+ else if (contentType.startsWith('text')) {
13
+ return r.text();
14
+ }
15
+ else if (contentType.includes('form')) {
16
+ const form = {};
17
+ const data = [...(await r.formData())].reduce((acc, cur) => {
18
+ acc[cur[0]] = cur[1];
19
+ return acc;
20
+ }, form);
21
+ return data;
22
+ }
23
+ };
24
+ exports.parseBody = parseBody;
@@ -1,4 +1,2 @@
1
1
  export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
2
- export declare const decodeBase64: (str: string) => any;
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>;
2
+ export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.timingSafeEqual = exports.sha256 = exports.decodeBase64 = exports.equal = void 0;
3
+ exports.timingSafeEqual = exports.equal = void 0;
4
+ const crypto_1 = require("./crypto");
4
5
  const equal = (a, b) => {
5
6
  if (a === b) {
6
7
  return true;
@@ -19,52 +20,12 @@ const equal = (a, b) => {
19
20
  return true;
20
21
  };
21
22
  exports.equal = equal;
22
- const decodeBase64 = (str) => {
23
- try {
24
- const text = atob(str);
25
- const length = text.length;
26
- const bytes = new Uint8Array(length);
27
- for (let i = 0; i < length; i++) {
28
- bytes[i] = text.charCodeAt(i);
29
- }
30
- const decoder = new TextDecoder();
31
- return decoder.decode(bytes);
32
- }
33
- catch (_a) { }
34
- try {
35
- const { Buffer } = require('buffer');
36
- return Buffer.from(str, 'base64').toString();
37
- }
38
- catch (e) {
39
- console.error('If you want to do "decodeBase64", polyfill "buffer" module.');
40
- throw e;
41
- }
42
- };
43
- exports.decodeBase64 = decodeBase64;
44
- const sha256 = async (a) => {
45
- if (crypto && crypto.subtle) {
46
- const buffer = await crypto.subtle.digest({
47
- name: 'SHA-256',
48
- }, new TextEncoder().encode(String(a)));
49
- const hash = Array.prototype.map
50
- .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
51
- .join('');
52
- return hash;
53
- }
54
- try {
55
- const crypto = require('crypto');
56
- const hash = crypto.createHash('sha256').update(a).digest('hex');
57
- return hash;
23
+ const timingSafeEqual = async (a, b, hashFunction) => {
24
+ if (!hashFunction) {
25
+ hashFunction = crypto_1.sha256;
58
26
  }
59
- catch (e) {
60
- console.error('If you want to do "sha256", polyfill "crypto" module.');
61
- throw e;
62
- }
63
- };
64
- exports.sha256 = sha256;
65
- const timingSafeEqual = async (a, b) => {
66
- const sa = await (0, exports.sha256)(a);
67
- const sb = await (0, exports.sha256)(b);
27
+ const sa = await hashFunction(a);
28
+ const sb = await hashFunction(b);
68
29
  return sa === sb && a === b;
69
30
  };
70
31
  exports.timingSafeEqual = timingSafeEqual;
@@ -0,0 +1,10 @@
1
+ declare type Algorithm = {
2
+ name: string;
3
+ alias: string;
4
+ };
5
+ declare type Data = string | object | boolean;
6
+ export declare const sha256: (data: Data) => Promise<string>;
7
+ export declare const sha1: (data: Data) => Promise<string>;
8
+ export declare const createHash: (data: Data, algorithm: Algorithm) => Promise<string>;
9
+ export declare const decodeBase64: (str: string) => any;
10
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeBase64 = exports.createHash = exports.sha1 = exports.sha256 = void 0;
4
+ const sha256 = async (data) => {
5
+ const algorithm = { name: 'SHA-256', alias: 'sha256' };
6
+ const hash = await (0, exports.createHash)(data, algorithm);
7
+ return hash;
8
+ };
9
+ exports.sha256 = sha256;
10
+ const sha1 = async (data) => {
11
+ const algorithm = { name: 'SHA-1', alias: 'sha1' };
12
+ const hash = await (0, exports.createHash)(data, algorithm);
13
+ return hash;
14
+ };
15
+ exports.sha1 = sha1;
16
+ const createHash = async (data, algorithm) => {
17
+ if (crypto && crypto.subtle) {
18
+ const buffer = await crypto.subtle.digest({
19
+ name: algorithm.name,
20
+ }, new TextEncoder().encode(String(data)));
21
+ const hash = Array.prototype.map
22
+ .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
23
+ .join('');
24
+ return hash;
25
+ }
26
+ try {
27
+ const crypto = require('crypto');
28
+ const hash = crypto.createHash(algorithm.alias).update(data).digest('hex');
29
+ return hash;
30
+ }
31
+ catch (e) {
32
+ console.error(`If you want to create hash ${algorithm.name}, polyfill "crypto" module.`);
33
+ throw e;
34
+ }
35
+ };
36
+ exports.createHash = createHash;
37
+ const decodeBase64 = (str) => {
38
+ try {
39
+ const text = atob(str);
40
+ const length = text.length;
41
+ const bytes = new Uint8Array(length);
42
+ for (let i = 0; i < length; i++) {
43
+ bytes[i] = text.charCodeAt(i);
44
+ }
45
+ const decoder = new TextDecoder();
46
+ return decoder.decode(bytes);
47
+ }
48
+ catch (_a) { }
49
+ try {
50
+ const { Buffer } = require('buffer');
51
+ return Buffer.from(str, 'base64').toString();
52
+ }
53
+ catch (e) {
54
+ console.error('If you want to do "decodeBase64", polyfill "buffer" module.');
55
+ throw e;
56
+ }
57
+ };
58
+ exports.decodeBase64 = decodeBase64;
@@ -4,11 +4,10 @@ exports.getMimeType = void 0;
4
4
  const getMimeType = (filename) => {
5
5
  const regexp = /\.([a-zA-Z0-9]+?)$/;
6
6
  const match = filename.match(regexp);
7
- if (!match) {
7
+ if (!match)
8
8
  return;
9
- }
10
9
  let mimeType = mimes[match[1]];
11
- if (mimeType.startsWith('text') || mimeType === 'application/json') {
10
+ if ((mimeType && mimeType.startsWith('text')) || mimeType === 'application/json') {
12
11
  mimeType += '; charset=utf-8';
13
12
  }
14
13
  return mimeType;
@@ -43,6 +42,7 @@ const mimes = {
43
42
  js: 'text/javascript',
44
43
  json: 'application/json',
45
44
  jsonld: 'application/ld+json',
45
+ map: 'application/json',
46
46
  mid: 'audio/x-midi',
47
47
  midi: 'audio/x-midi',
48
48
  mjs: 'text/javascript',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "0.4.2",
3
+ "version": "0.5.2",
4
4
  "description": "[炎] Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -78,11 +78,13 @@
78
78
  ],
79
79
  "devDependencies": {
80
80
  "@cloudflare/workers-types": "^3.3.0",
81
+ "@types/crypto-js": "^4.1.1",
81
82
  "@types/jest": "^27.4.0",
82
83
  "@types/mustache": "^4.1.2",
83
84
  "@types/node": "^17.0.8",
84
85
  "@typescript-eslint/eslint-plugin": "^5.9.0",
85
86
  "@typescript-eslint/parser": "^5.9.0",
87
+ "crypto-js": "^4.1.1",
86
88
  "eslint": "^7.26.0",
87
89
  "eslint-config-prettier": "^8.3.0",
88
90
  "eslint-define-config": "^1.2.1",