hono 0.0.15 → 0.2.1

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,19 +23,19 @@ app.fire()
23
23
  **Hono is fastest** compared to other routers for Cloudflare Workers.
24
24
 
25
25
  ```plain
26
- hono x 748,188 ops/sec ±5.40% (77 runs sampled)
27
- itty-router x 158,817 ops/sec ±3.62% (87 runs sampled)
28
- sunder x 332,339 ops/sec ±1.11% (95 runs sampled)
29
- worktop x 205,906 ops/sec ±4.43% (83 runs sampled)
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)
30
30
  Fastest is hono
31
- ✨ Done in 52.79s.
31
+ ✨ Done in 57.83s.
32
32
  ```
33
33
 
34
34
  ## Hono in 1 minute
35
35
 
36
36
  Below is a demonstration to create an application of Cloudflare Workers with Hono.
37
37
 
38
- ![Demo](https://user-images.githubusercontent.com/10682/148223268-2484a891-57c1-472f-9df3-936a5586f002.gif)
38
+ ![Demo](https://user-images.githubusercontent.com/10682/151102477-be0f950e-8d23-49c5-b6d8-d8ecb6b7484e.gif)
39
39
 
40
40
  ## Install
41
41
 
@@ -90,7 +90,7 @@ app.all('/hello', (c) => c.text('Any Method /hello'))
90
90
 
91
91
  ```js
92
92
  app.get('/user/:name', (c) => {
93
- const name = c.req.params('name')
93
+ const name = c.req.param('name')
94
94
  ...
95
95
  })
96
96
  ```
@@ -99,8 +99,8 @@ app.get('/user/:name', (c) => {
99
99
 
100
100
  ```js
101
101
  app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
102
- const date = c.req.params('date')
103
- const title = c.req.params('title')
102
+ const date = c.req.param('date')
103
+ const title = c.req.param('title')
104
104
  ...
105
105
  ```
106
106
 
@@ -114,6 +114,16 @@ app
114
114
  .put(() => {...})
115
115
  ```
116
116
 
117
+ ### Custom 404 Response
118
+
119
+ You can customize 404 Not Found response:
120
+
121
+ ```js
122
+ app.get('*', (c) => {
123
+ return c.text('Custom 404 Error', 404)
124
+ })
125
+ ```
126
+
117
127
  ## async/await
118
128
 
119
129
  ```js
@@ -165,19 +175,6 @@ app.use('/message/*', async (c, next) => {
165
175
  app.get('/message/hello', (c) => c.text('Hello Middleware!'))
166
176
  ```
167
177
 
168
- ### Custom 404 Response
169
-
170
- You can customize 404 Not Found response:
171
-
172
- ```js
173
- app.use('*', async (c, next) => {
174
- await next()
175
- if (c.res.status === 404) {
176
- c.res = new Response('Custom 404 Not Found', { status: 404 })
177
- }
178
- })
179
- ```
180
-
181
178
  ### Handling Error
182
179
 
183
180
  ```js
@@ -186,7 +183,7 @@ app.use('*', async (c, next) => {
186
183
  await next()
187
184
  } catch (err) {
188
185
  console.error(`${err}`)
189
- c.res = new Response('Custom Error Message', { status: 500 })
186
+ c.res = c.text('Custom Error Message', { status: 500 })
190
187
  }
191
188
  })
192
189
  ```
@@ -219,13 +216,18 @@ To handle Request and Reponse easily, you can use Context object:
219
216
  ### c.req
220
217
 
221
218
  ```js
222
-
223
219
  // Get Request object
224
220
  app.get('/hello', (c) => {
225
221
  const userAgent = c.req.headers.get('User-Agent')
226
222
  ...
227
223
  })
228
224
 
225
+ // Shortcut to get a header value
226
+ app.get('/shortcut', (c) => {
227
+ const userAgent = c.req.header('User-Agent')
228
+ ...
229
+ })
230
+
229
231
  // Query params
230
232
  app.get('/search', (c) => {
231
233
  const query = c.req.query('q')
@@ -234,40 +236,34 @@ app.get('/search', (c) => {
234
236
 
235
237
  // Captured params
236
238
  app.get('/entry/:id', (c) => {
237
- const id = c.req.params('id')
239
+ const id = c.req.param('id')
238
240
  ...
239
241
  })
240
242
  ```
241
243
 
242
- ### c.res
244
+ ### Shortcuts for Response
243
245
 
244
246
  ```js
245
- // Response object
246
- app.use('/', (c, next) => {
247
- next()
248
- c.res.headers.append('X-Debug', 'Debug message')
249
- })
250
- ```
251
-
252
- ### c.event
253
-
254
- ```js
255
- // FetchEvent object
256
- app.use('*', async (c, next) => {
257
- c.event.waitUntil(
258
- ...
259
- )
260
- await next()
261
- })
262
- ```
263
-
264
- ### c.env
265
-
266
- ```js
267
- // Environment object for Cloudflare Workers
268
- app.get('*', async c => {
269
- const counter = c.env.COUNTER
270
- ...
247
+ app.get('/welcome', (c) => {
248
+ c.header('X-Message', 'Hello!')
249
+ c.header('Content-Type', 'text/plain')
250
+ c.status(201)
251
+ c.statusText('201 Content Created')
252
+
253
+ return c.body('Thank you for comming')
254
+
255
+ /*
256
+ Same as:
257
+ return new Response('Thank you for comming', {
258
+ status: 201,
259
+ statusText: '201 Content Created',
260
+ headers: {
261
+ 'X-Message': 'Hello',
262
+ 'Content-Type': 'text/plain',
263
+ 'Content-Length: '22'
264
+ }
265
+ })
266
+ */
271
267
  })
272
268
  ```
273
269
 
@@ -310,6 +306,38 @@ app.get('/redirect', (c) => c.redirect('/'))
310
306
  app.get('/redirect-permanently', (c) => c.redirect('/', 301))
311
307
  ```
312
308
 
309
+ ### c.res
310
+
311
+ ```js
312
+ // Response object
313
+ app.use('/', (c, next) => {
314
+ next()
315
+ c.res.headers.append('X-Debug', 'Debug message')
316
+ })
317
+ ```
318
+
319
+ ### c.event
320
+
321
+ ```js
322
+ // FetchEvent object
323
+ app.use('*', async (c, next) => {
324
+ c.event.waitUntil(
325
+ ...
326
+ )
327
+ await next()
328
+ })
329
+ ```
330
+
331
+ ### c.env
332
+
333
+ ```js
334
+ // Environment object for Cloudflare Workers
335
+ app.get('*', async c => {
336
+ const counter = c.env.COUNTER
337
+ ...
338
+ })
339
+ ```
340
+
313
341
  ## fire
314
342
 
315
343
  `app.fire()` do:
package/dist/context.d.ts CHANGED
@@ -9,12 +9,20 @@ export declare class Context {
9
9
  res: Response;
10
10
  env: Env;
11
11
  event: FetchEvent;
12
+ private _headers;
13
+ private _status;
14
+ private _statusText;
15
+ render: (template: string, params?: object, options?: object) => Promise<Response>;
12
16
  constructor(req: Request, opts?: {
13
17
  res: Response;
14
18
  env: Env;
15
19
  event: FetchEvent;
16
20
  });
17
- newResponse(body?: BodyInit | null | undefined, init?: ResponseInit | undefined): Response;
21
+ header(name: string, value: string): void;
22
+ 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;
18
26
  text(text: string, status?: number, headers?: Headers): Response;
19
27
  json(object: object, status?: number, headers?: Headers): Response;
20
28
  html(html: string, status?: number, headers?: Headers): Response;
package/dist/context.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Context = void 0;
4
- const util_1 = require("./util");
4
+ const url_1 = require("./utils/url");
5
5
  class Context {
6
6
  constructor(req, opts) {
7
7
  this.req = req;
@@ -10,46 +10,58 @@ class Context {
10
10
  this.env = opts.env;
11
11
  this.event = opts.event;
12
12
  }
13
+ this._headers = {};
13
14
  }
14
- newResponse(body, init) {
15
- return new Response(body, init);
15
+ header(name, value) {
16
+ this._headers[name] = value;
16
17
  }
17
- text(text, status = 200, headers = {}) {
18
+ status(number) {
19
+ this._status = number;
20
+ }
21
+ statusText(text) {
22
+ this._statusText = text;
23
+ }
24
+ newResponse(data, init = {}) {
25
+ init.status = init.status || this._status;
26
+ init.statusText = init.statusText || this._statusText;
27
+ const Encoder = new TextEncoder();
28
+ const length = data ? data.bytelength || Encoder.encode(data).byteLength : 0;
29
+ init.headers = Object.assign(Object.assign(Object.assign({}, this._headers), init.headers), { 'Content-Length': String(length) });
30
+ return new Response(data, init);
31
+ }
32
+ body(data, status = this._status, headers = this._headers) {
33
+ return this.newResponse(data, {
34
+ status: status,
35
+ headers: headers,
36
+ });
37
+ }
38
+ text(text, status = this._status, headers = {}) {
18
39
  if (typeof text !== 'string') {
19
40
  throw new TypeError('text method arg must be a string!');
20
41
  }
21
42
  headers['Content-Type'] = 'text/plain';
22
- return this.newResponse(text, {
23
- status: status,
24
- headers: headers,
25
- });
43
+ return this.body(text, status, headers);
26
44
  }
27
- json(object, status = 200, headers = {}) {
45
+ json(object, status = this._status, headers = {}) {
28
46
  if (typeof object !== 'object') {
29
47
  throw new TypeError('json method arg must be a object!');
30
48
  }
31
49
  const body = JSON.stringify(object);
32
50
  headers['Content-Type'] = 'application/json; charset=UTF-8';
33
- return this.newResponse(body, {
34
- status: status,
35
- headers: headers,
36
- });
51
+ return this.body(body, status, headers);
37
52
  }
38
- html(html, status = 200, headers = {}) {
53
+ html(html, status = this._status, headers = {}) {
39
54
  if (typeof html !== 'string') {
40
55
  throw new TypeError('html method arg must be a string!');
41
56
  }
42
57
  headers['Content-Type'] = 'text/html; charset=UTF-8';
43
- return this.newResponse(html, {
44
- status: status,
45
- headers: headers,
46
- });
58
+ return this.body(html, status, headers);
47
59
  }
48
60
  redirect(location, status = 302) {
49
61
  if (typeof location !== 'string') {
50
62
  throw new TypeError('location must be a string!');
51
63
  }
52
- if (!(0, util_1.isAbsoluteURL)(location)) {
64
+ if (!(0, url_1.isAbsoluteURL)(location)) {
53
65
  const url = new URL(this.req.url);
54
66
  url.pathname = location;
55
67
  location = url.toString();
package/dist/hono.d.ts CHANGED
@@ -5,9 +5,9 @@ import { Context } from './context';
5
5
  import type { Env } from './context';
6
6
  declare global {
7
7
  interface Request {
8
- params: (key: string) => string;
8
+ param: (key: string) => string;
9
9
  query: (key: string) => string | null;
10
- parsedBody: any;
10
+ header: (name: string) => string;
11
11
  }
12
12
  }
13
13
  export declare type Handler = (c: Context, next?: Function) => Response | Promise<Response>;
package/dist/hono.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Hono = exports.Router = void 0;
4
4
  const node_1 = require("./node");
5
5
  const compose_1 = require("./compose");
6
- const util_1 = require("./util");
6
+ const url_1 = require("./utils/url");
7
7
  const middleware_1 = require("./middleware");
8
8
  const context_1 = require("./context");
9
9
  const METHOD_NAME_OF_ALL = 'ALL';
@@ -94,9 +94,9 @@ class Hono {
94
94
  return this.router.match(method, path);
95
95
  }
96
96
  async dispatch(request, env, event) {
97
- const [method, path] = [request.method, (0, util_1.getPathFromURL)(request.url)];
97
+ const [method, path] = [request.method, (0, url_1.getPathFromURL)(request.url)];
98
98
  const result = await this.matchRoute(method, path);
99
- request.params = (key) => {
99
+ request.param = (key) => {
100
100
  if (result) {
101
101
  return result.params[key];
102
102
  }
@@ -137,11 +137,23 @@ class Hono {
137
137
  });
138
138
  }
139
139
  onError(err) {
140
- console.error(err);
141
- return new Response('Internal Server Error', { status: 500 });
140
+ console.error(`${err}`);
141
+ const message = 'Internal Server Error';
142
+ return new Response(message, {
143
+ status: 500,
144
+ headers: {
145
+ 'Content-Length': message.length.toString(),
146
+ },
147
+ });
142
148
  }
143
149
  notFound() {
144
- return new Response('Not Found', { status: 404 });
150
+ const message = 'Not Found';
151
+ return new Response('Not Found', {
152
+ status: 404,
153
+ headers: {
154
+ 'Content-Length': message.length.toString(),
155
+ },
156
+ });
145
157
  }
146
158
  }
147
159
  exports.Hono = Hono;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.basicAuth = void 0;
4
- const util_1 = require("../../util");
4
+ const buffer_1 = require("../../utils/buffer");
5
5
  const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
6
6
  const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
7
7
  const auth = (req) => {
@@ -33,8 +33,8 @@ const basicAuth = (options) => {
33
33
  }
34
34
  return async (ctx, next) => {
35
35
  const user = auth(ctx.req);
36
- const usernameEqual = user && await (0, util_1.timingSafeEqual)(options.username, user.username);
37
- const passwordEqual = user && await (0, util_1.timingSafeEqual)(options.password, user.password);
36
+ const usernameEqual = user && await (0, buffer_1.timingSafeEqual)(options.username, user.username);
37
+ const passwordEqual = user && await (0, buffer_1.timingSafeEqual)(options.password, user.password);
38
38
  if (!user || !usernameEqual || !passwordEqual) {
39
39
  ctx.res = new Response('Unauthorized', {
40
40
  status: 401,
@@ -1,2 +1,7 @@
1
1
  import type { Context } from '../../context';
2
+ declare global {
3
+ interface Request {
4
+ parsedBody: any;
5
+ }
6
+ }
2
7
  export declare const bodyParse: () => (ctx: Context, next: Function) => Promise<void>;
@@ -0,0 +1,25 @@
1
+ import type { Context } from '../../context';
2
+ declare global {
3
+ interface Request {
4
+ cookie: (name: string) => string;
5
+ }
6
+ }
7
+ declare module '../../context' {
8
+ interface Context {
9
+ cookie: (name: string, value: string, options?: CookieOptions) => void;
10
+ }
11
+ }
12
+ export declare type Cookie = {
13
+ [key: string]: string;
14
+ };
15
+ export declare type CookieOptions = {
16
+ domain?: string;
17
+ expires?: Date;
18
+ httpOnly?: boolean;
19
+ maxAge?: number;
20
+ path?: string;
21
+ secure?: boolean;
22
+ signed?: boolean;
23
+ sameSite?: 'Strict' | 'Lax' | 'None';
24
+ };
25
+ export declare const cookie: () => (c: Context, next: Function) => Promise<void>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cookie = void 0;
4
+ const cookie = () => {
5
+ return async (c, next) => {
6
+ c.req.cookie = (name) => {
7
+ const cookie = c.req.headers.get('Cookie');
8
+ const obj = parse(cookie);
9
+ const value = obj[name];
10
+ return value;
11
+ };
12
+ c.cookie = (name, value, opt) => {
13
+ const cookie = serialize(name, value, opt);
14
+ c.header('Set-Cookie', cookie);
15
+ };
16
+ await next();
17
+ };
18
+ };
19
+ exports.cookie = cookie;
20
+ const parse = (cookie) => {
21
+ const pairs = cookie.split(/;\s*/g);
22
+ const parsedCookie = {};
23
+ for (let i = 0, len = pairs.length; i < len; i++) {
24
+ const pair = pairs[i].split(/\s*=\s*([^\s]+)/);
25
+ parsedCookie[pair[0]] = decodeURIComponent(pair[1]);
26
+ }
27
+ return parsedCookie;
28
+ };
29
+ const serialize = (name, value, opt = {}) => {
30
+ value = encodeURIComponent(value);
31
+ let cookie = `${name}=${value}`;
32
+ if (opt.maxAge) {
33
+ cookie += `; Max-Age=${Math.floor(opt.maxAge)}`;
34
+ }
35
+ if (opt.domain) {
36
+ cookie += '; Domain=' + opt.domain;
37
+ }
38
+ if (opt.path) {
39
+ cookie += '; Path=' + opt.path;
40
+ }
41
+ if (opt.expires) {
42
+ cookie += '; Expires=' + opt.expires.toUTCString();
43
+ }
44
+ if (opt.httpOnly) {
45
+ cookie += '; HttpOnly';
46
+ }
47
+ if (opt.secure) {
48
+ cookie += '; Secure';
49
+ }
50
+ if (opt.sameSite) {
51
+ cookie += `; SameSaite=${opt.sameSite}`;
52
+ }
53
+ return cookie;
54
+ };
@@ -0,0 +1,11 @@
1
+ import type { Context } from '../../context';
2
+ declare type CORSOptions = {
3
+ origin: string;
4
+ allowMethods?: string[];
5
+ allowHeaders?: string[];
6
+ maxAge?: number;
7
+ credentials?: boolean;
8
+ exposeHeaders?: string[];
9
+ };
10
+ export declare const cors: (options?: CORSOptions) => (c: Context, next: Function) => Promise<void>;
11
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cors = void 0;
4
+ const cors = (options) => {
5
+ const defaults = {
6
+ origin: '*',
7
+ allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
8
+ allowHeaders: [],
9
+ exposeHeaders: [],
10
+ };
11
+ const opts = Object.assign(Object.assign({}, defaults), options);
12
+ return async (c, next) => {
13
+ await next();
14
+ function set(key, value) {
15
+ c.res.headers.append(key, value);
16
+ }
17
+ set('Access-Control-Allow-Origin', opts.origin);
18
+ // Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
19
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
20
+ if (opts.origin !== '*') {
21
+ set('Vary', 'Origin');
22
+ }
23
+ if (opts.credentials) {
24
+ set('Access-Control-Allow-Credentials', 'true');
25
+ }
26
+ if (opts.exposeHeaders.length) {
27
+ set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','));
28
+ }
29
+ if (c.req.method === 'OPTIONS') {
30
+ // Preflight
31
+ if (opts.maxAge != null) {
32
+ set('Access-Control-Max-Age', opts.maxAge.toString());
33
+ }
34
+ if (opts.allowMethods.length) {
35
+ set('Access-Control-Allow-Methods', opts.allowMethods.join(','));
36
+ }
37
+ let headers = opts.allowHeaders;
38
+ if (!headers.length) {
39
+ const requestHeaders = c.req.headers.get('Access-Control-Request-Headers');
40
+ if (requestHeaders) {
41
+ headers = requestHeaders.split(/\s*,\s*/);
42
+ }
43
+ }
44
+ if (headers.length) {
45
+ set('Access-Control-Allow-Headers', headers.join(','));
46
+ set('Vary', 'Access-Control-Request-Headers');
47
+ }
48
+ c.res.headers.delete('Content-Length');
49
+ c.res.headers.delete('Content-Type');
50
+ c.res = new Response(null, {
51
+ headers: c.res.headers,
52
+ status: 204,
53
+ statusText: c.res.statusText,
54
+ });
55
+ }
56
+ };
57
+ };
58
+ exports.cors = cors;
@@ -7,10 +7,9 @@ const defaultMiddleware = async (c, next) => {
7
7
  const url = new URL(c.req.url);
8
8
  return url.searchParams.get(key);
9
9
  };
10
+ c.req.header = (name) => {
11
+ return c.req.headers.get(name);
12
+ };
10
13
  await next();
11
- if (c.res.body) {
12
- const buff = await c.res.clone().arrayBuffer();
13
- c.res.headers.append('Content-Length', buff.byteLength.toString());
14
- }
15
14
  };
16
15
  exports.defaultMiddleware = defaultMiddleware;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.logger = void 0;
4
- const util_1 = require("../../util");
4
+ const url_1 = require("../../utils/url");
5
5
  const humanize = (n, opts) => {
6
6
  const options = opts || {};
7
7
  const d = options.delimiter || ',';
@@ -33,16 +33,16 @@ const colorStatus = (status = 0) => {
33
33
  };
34
34
  return out[(status / 100) | 0];
35
35
  };
36
- function log(fn, prefix, method, path, status, elasped) {
36
+ function log(fn, prefix, method, path, status, elasped, contentLength) {
37
37
  const out = prefix === LogPrefix.Incoming
38
38
  ? ` ${prefix} ${method} ${path}`
39
- : ` ${prefix} ${method} ${path} ${colorStatus(status)} ${elasped}`;
39
+ : ` ${prefix} ${method} ${path} ${colorStatus(status)} ${elasped} ${contentLength}`;
40
40
  fn(out);
41
41
  }
42
42
  const logger = (fn = console.log) => {
43
43
  return async (c, next) => {
44
44
  const { method } = c.req;
45
- const path = (0, util_1.getPathFromURL)(c.req.url);
45
+ const path = (0, url_1.getPathFromURL)(c.req.url);
46
46
  log(fn, LogPrefix.Incoming, method, path);
47
47
  const start = Date.now();
48
48
  try {
@@ -52,7 +52,13 @@ const logger = (fn = console.log) => {
52
52
  log(fn, LogPrefix.Error, method, path, c.res.status || 500, time(start));
53
53
  throw e;
54
54
  }
55
- log(fn, LogPrefix.Outgoing, method, path, c.res.status, time(start));
55
+ const len = parseFloat(c.res.headers.get('Content-Length'));
56
+ const contentLength = isNaN(len)
57
+ ? '0'
58
+ : len < 1024
59
+ ? `${len}b`
60
+ : `${len / 1024}kB`;
61
+ log(fn, LogPrefix.Outgoing, method, path, c.res.status, time(start), contentLength);
56
62
  };
57
63
  };
58
64
  exports.logger = logger;
@@ -0,0 +1,2 @@
1
+ import type { Context } from '../../context';
2
+ export declare const mustache: () => (c: Context, next: Function) => Promise<void>;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.mustache = void 0;
23
+ const cloudflare_1 = require("../../utils/cloudflare");
24
+ const EXTENSION = '.mustache';
25
+ const mustache = () => {
26
+ return async (c, next) => {
27
+ let Mustache;
28
+ try {
29
+ Mustache = await Promise.resolve().then(() => __importStar(require('mustache')));
30
+ }
31
+ catch (e) {
32
+ console.error(`Mustache is not found! ${e}`);
33
+ throw new Error(`${e}`);
34
+ }
35
+ c.render = async (filename, view = {}, options) => {
36
+ const content = await (0, cloudflare_1.getContentFromKVAsset)(`${filename}${EXTENSION}`);
37
+ if (!content) {
38
+ throw new Error(`Template "${filename}${EXTENSION}" is not found`);
39
+ }
40
+ const partialArgs = {};
41
+ if (options) {
42
+ const partials = options;
43
+ for (const key of Object.keys(partials)) {
44
+ const partialContent = await (0, cloudflare_1.getContentFromKVAsset)(`${partials[key]}${EXTENSION}`);
45
+ if (!partialContent) {
46
+ throw new Error(`Partial Template "${partials[key]}${EXTENSION}" is not found`);
47
+ }
48
+ partialArgs[key] = partialContent;
49
+ }
50
+ }
51
+ const output = Mustache.render(content, view, partialArgs);
52
+ return c.html(output);
53
+ };
54
+ await next();
55
+ };
56
+ };
57
+ exports.mustache = mustache;
@@ -12,4 +12,14 @@ export declare class Middleware {
12
12
  realm?: string;
13
13
  }) => (ctx: import("./context").Context, next: Function) => Promise<any>;
14
14
  static bodyParse: () => (ctx: import("./context").Context, next: Function) => Promise<void>;
15
+ static cors: (options?: {
16
+ origin: string;
17
+ allowMethods?: string[];
18
+ allowHeaders?: string[];
19
+ maxAge?: number;
20
+ credentials?: boolean;
21
+ exposeHeaders?: string[];
22
+ }) => (c: import("./context").Context, next: Function) => Promise<void>;
23
+ static cookie: () => (c: import("./context").Context, next: Function) => Promise<void>;
24
+ static mustache: () => (c: import("./context").Context, next: Function) => Promise<void>;
15
25
  }
@@ -6,6 +6,9 @@ const powered_by_1 = require("./middleware/powered-by/powered-by");
6
6
  const logger_1 = require("./middleware/logger/logger");
7
7
  const basic_auth_1 = require("./middleware/basic-auth/basic-auth");
8
8
  const body_parse_1 = require("./middleware/body-parse/body-parse");
9
+ const cors_1 = require("./middleware/cors/cors");
10
+ const cookie_1 = require("./middleware/cookie/cookie");
11
+ const mustache_1 = require("./middleware/mustache/mustache");
9
12
  class Middleware {
10
13
  }
11
14
  exports.Middleware = Middleware;
@@ -14,3 +17,6 @@ Middleware.poweredBy = powered_by_1.poweredBy;
14
17
  Middleware.logger = logger_1.logger;
15
18
  Middleware.basicAuth = basic_auth_1.basicAuth;
16
19
  Middleware.bodyParse = body_parse_1.bodyParse;
20
+ Middleware.cors = cors_1.cors;
21
+ Middleware.cookie = cookie_1.cookie;
22
+ Middleware.mustache = mustache_1.mustache;
package/dist/node.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Node = exports.Result = void 0;
4
- const util_1 = require("./util");
4
+ const url_1 = require("./utils/url");
5
5
  const METHOD_NAME_OF_ALL = 'ALL';
6
6
  class Result {
7
7
  constructor(handler, params) {
@@ -25,7 +25,7 @@ class Node {
25
25
  insert(method, path, handler) {
26
26
  // eslint-disable-next-line @typescript-eslint/no-this-alias
27
27
  let curNode = this;
28
- const parts = (0, util_1.splitPath)(path);
28
+ const parts = (0, url_1.splitPath)(path);
29
29
  for (let i = 0, len = parts.length; i < len; i++) {
30
30
  const p = parts[i];
31
31
  if (Object.keys(curNode.children).includes(p)) {
@@ -42,7 +42,7 @@ class Node {
42
42
  // eslint-disable-next-line @typescript-eslint/no-this-alias
43
43
  let curNode = this;
44
44
  const params = {};
45
- const parts = (0, util_1.splitPath)(path);
45
+ const parts = (0, url_1.splitPath)(path);
46
46
  for (let i = 0, len = parts.length; i < len; i++) {
47
47
  const p = parts[i];
48
48
  // '*' => match any path
@@ -74,7 +74,7 @@ class Node {
74
74
  isWildcard = true;
75
75
  break;
76
76
  }
77
- const pattern = (0, util_1.getPattern)(key);
77
+ const pattern = (0, url_1.getPattern)(key);
78
78
  // Named match
79
79
  if (pattern) {
80
80
  const match = p.match(new RegExp(pattern[1]));
@@ -0,0 +1,2 @@
1
+ export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
2
+ export declare const timingSafeEqual: (a: any, b: any) => Promise<boolean>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.timingSafeEqual = exports.equal = void 0;
4
+ const equal = (a, b) => {
5
+ if (a === b) {
6
+ return true;
7
+ }
8
+ if (a.byteLength !== b.byteLength) {
9
+ return false;
10
+ }
11
+ const va = new DataView(a);
12
+ const vb = new DataView(b);
13
+ let i = va.byteLength;
14
+ while (i--) {
15
+ if (va.getUint8(i) !== vb.getUint8(i)) {
16
+ return false;
17
+ }
18
+ }
19
+ return true;
20
+ };
21
+ exports.equal = equal;
22
+ const timingSafeEqual = async (a, b) => {
23
+ const sa = await crypto.subtle.digest({
24
+ name: 'SHA-256',
25
+ }, new TextEncoder().encode(String(a)));
26
+ const sb = await crypto.subtle.digest({
27
+ name: 'SHA-256',
28
+ }, new TextEncoder().encode(String(b)));
29
+ return (0, exports.equal)(sa, sb) && a === b;
30
+ };
31
+ exports.timingSafeEqual = timingSafeEqual;
@@ -0,0 +1 @@
1
+ export declare const getContentFromKVAsset: (path: string) => Promise<string>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getContentFromKVAsset = void 0;
4
+ const getContentFromKVAsset = async (path) => {
5
+ let ASSET_MANIFEST;
6
+ if (typeof __STATIC_CONTENT_MANIFEST === 'string') {
7
+ ASSET_MANIFEST = JSON.parse(__STATIC_CONTENT_MANIFEST);
8
+ }
9
+ else {
10
+ ASSET_MANIFEST = __STATIC_CONTENT_MANIFEST;
11
+ }
12
+ const ASSET_NAMESPACE = __STATIC_CONTENT;
13
+ const key = ASSET_MANIFEST[path] || path;
14
+ if (!key) {
15
+ return;
16
+ }
17
+ let content;
18
+ content = await ASSET_NAMESPACE.get(key, { type: 'text' });
19
+ if (content) {
20
+ content = content;
21
+ }
22
+ return content;
23
+ };
24
+ exports.getContentFromKVAsset = getContentFromKVAsset;
@@ -2,4 +2,3 @@ export declare const splitPath: (path: string) => string[];
2
2
  export declare const getPattern: (label: string) => string[] | null;
3
3
  export declare const getPathFromURL: (url: string) => string;
4
4
  export declare const isAbsoluteURL: (url: string) => boolean;
5
- export declare const timingSafeEqual: (a: any, b: any) => Promise<boolean>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.timingSafeEqual = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
3
+ 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('/')
@@ -27,7 +27,6 @@ const getPattern = (label) => {
27
27
  };
28
28
  exports.getPattern = getPattern;
29
29
  const getPathFromURL = (url) => {
30
- // XXX
31
30
  const match = url.match(URL_REGEXP);
32
31
  if (match) {
33
32
  return match[5];
@@ -43,30 +42,3 @@ const isAbsoluteURL = (url) => {
43
42
  return false;
44
43
  };
45
44
  exports.isAbsoluteURL = isAbsoluteURL;
46
- const bufferEqual = (a, b) => {
47
- if (a === b) {
48
- return true;
49
- }
50
- if (a.byteLength !== b.byteLength) {
51
- return false;
52
- }
53
- const va = new DataView(a);
54
- const vb = new DataView(b);
55
- let i = va.byteLength;
56
- while (i--) {
57
- if (va.getUint8(i) !== vb.getUint8(i)) {
58
- return false;
59
- }
60
- }
61
- return true;
62
- };
63
- const timingSafeEqual = async (a, b) => {
64
- const sa = await crypto.subtle.digest({
65
- name: 'SHA-256',
66
- }, new TextEncoder().encode(String(a)));
67
- const sb = await crypto.subtle.digest({
68
- name: 'SHA-256',
69
- }, new TextEncoder().encode(String(b)));
70
- return bufferEqual(sa, sb) && a === b;
71
- };
72
- exports.timingSafeEqual = timingSafeEqual;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "0.0.15",
3
+ "version": "0.2.1",
4
4
  "description": "[炎] Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -36,6 +36,7 @@
36
36
  "devDependencies": {
37
37
  "@cloudflare/workers-types": "^3.3.0",
38
38
  "@types/jest": "^27.4.0",
39
+ "@types/mustache": "^4.1.2",
39
40
  "@types/node": "^17.0.8",
40
41
  "@typescript-eslint/eslint-plugin": "^5.9.0",
41
42
  "@typescript-eslint/parser": "^5.9.0",
@@ -51,6 +52,7 @@
51
52
  "form-data": "^4.0.0",
52
53
  "jest": "^27.4.5",
53
54
  "jest-environment-miniflare": "^2.0.0",
55
+ "mustache": "^4.2.0",
54
56
  "rimraf": "^3.0.2",
55
57
  "ts-jest": "^27.1.2",
56
58
  "typescript": "^4.5.4"