hono 0.3.2 → 0.3.6

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
@@ -253,7 +253,6 @@ app.get('/welcome', (c) => {
253
253
  c.header('X-Message', 'Hello!')
254
254
  c.header('Content-Type', 'text/plain')
255
255
  c.status(201)
256
- c.statusText('201 Content Created')
257
256
 
258
257
  return c.body('Thank you for comming')
259
258
 
@@ -261,7 +260,7 @@ app.get('/welcome', (c) => {
261
260
  Same as:
262
261
  return new Response('Thank you for comming', {
263
262
  status: 201,
264
- statusText: '201 Content Created',
263
+ statusText: 'Created',
265
264
  headers: {
266
265
  'X-Message': 'Hello',
267
266
  'Content-Type': 'text/plain',
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
@@ -2,6 +2,7 @@
2
2
  declare type Headers = {
3
3
  [key: string]: string;
4
4
  };
5
+ declare type Data = string | ArrayBuffer | ReadableStream;
5
6
  export interface Env {
6
7
  }
7
8
  export declare class Context {
@@ -20,9 +21,8 @@ export declare class Context {
20
21
  });
21
22
  header(name: string, value: string): void;
22
23
  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;
24
+ newResponse(data: Data, init?: ResponseInit): Response;
25
+ body(data: Data, status?: number, headers?: Headers): Response;
26
26
  text(text: string, status?: number, headers?: Headers): Response;
27
27
  json(object: object, status?: number, headers?: Headers): Response;
28
28
  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 setted.');
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);
@@ -52,7 +62,7 @@ class Context {
52
62
  if (typeof text !== 'string') {
53
63
  throw new TypeError('text method arg must be a string!');
54
64
  }
55
- headers['Content-Type'] = 'text/plain';
65
+ headers['Content-Type'] || (headers['Content-Type'] = 'text/plain; charset=UTF-8');
56
66
  return this.body(text, status, headers);
57
67
  }
58
68
  json(object, status = this._status, headers = {}) {
@@ -60,14 +70,14 @@ class Context {
60
70
  throw new TypeError('json method arg must be a object!');
61
71
  }
62
72
  const body = JSON.stringify(object);
63
- headers['Content-Type'] = 'application/json; charset=UTF-8';
73
+ headers['Content-Type'] || (headers['Content-Type'] = 'application/json; charset=UTF-8');
64
74
  return this.body(body, status, headers);
65
75
  }
66
76
  html(html, status = this._status, headers = {}) {
67
77
  if (typeof html !== 'string') {
68
78
  throw new TypeError('html method arg must be a string!');
69
79
  }
70
- headers['Content-Type'] = 'text/html; charset=UTF-8';
80
+ headers['Content-Type'] || (headers['Content-Type'] = 'text/html; charset=UTF-8');
71
81
  return this.body(html, status, headers);
72
82
  }
73
83
  redirect(location, status = 302) {
package/dist/hono.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { Env } from './context';
6
6
  declare global {
7
7
  interface Request {
8
8
  param: (key: string) => string;
9
- query: (key: string) => string | null;
9
+ query: (key: string) => string;
10
10
  header: (name: string) => string;
11
11
  parsedBody: any;
12
12
  }
@@ -40,6 +40,6 @@ export declare class Hono {
40
40
  handleEvent(event: FetchEvent): Promise<Response>;
41
41
  fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response>;
42
42
  fire(): void;
43
- onError(err: any): Response;
43
+ onError(err: Error): Response;
44
44
  notFound(): Response;
45
45
  }
package/dist/hono.js CHANGED
@@ -4,7 +4,6 @@ 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
8
  const METHOD_NAME_OF_ALL = 'ALL';
10
9
  class Router {
@@ -97,11 +96,18 @@ class Hono {
97
96
  async dispatch(request, env, event) {
98
97
  const [method, path] = [request.method, (0, url_1.getPathFromURL)(request.url)];
99
98
  const result = await this.matchRoute(method, path);
99
+ // Methods for Request object
100
100
  request.param = (key) => {
101
101
  if (result) {
102
102
  return result.params[key];
103
103
  }
104
- return '';
104
+ };
105
+ request.header = (name) => {
106
+ return request.headers.get(name);
107
+ };
108
+ request.query = (key) => {
109
+ const url = new URL(c.req.url);
110
+ return url.searchParams.get(key);
105
111
  };
106
112
  const handler = result ? result.handler[0] : this.notFound; // XXX
107
113
  const middleware = [];
@@ -112,10 +118,13 @@ class Hono {
112
118
  }
113
119
  }
114
120
  const wrappedHandler = async (context, next) => {
115
- context.res = await handler(context);
121
+ const res = await handler(context);
122
+ if (!(res instanceof Response)) {
123
+ throw new TypeError('response must be a instace of Response');
124
+ }
125
+ context.res = res;
116
126
  await next();
117
127
  };
118
- middleware.push(middleware_1.Middleware.default);
119
128
  middleware.push(wrappedHandler);
120
129
  const composed = (0, compose_1.compose)(middleware);
121
130
  const c = new context_1.Context(request, { env: env, event: event, res: null });
@@ -18,28 +18,12 @@ const auth = (req) => {
18
18
  if (!match) {
19
19
  return undefined;
20
20
  }
21
- const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1]));
21
+ const userPass = USER_PASS_REGEXP.exec((0, buffer_1.decodeBase64)(match[1]));
22
22
  if (!userPass) {
23
23
  return undefined;
24
24
  }
25
25
  return { username: userPass[1], password: userPass[2] };
26
26
  };
27
- function decodeBase64(str) {
28
- if (atob) {
29
- const text = atob(str);
30
- const length = text.length;
31
- const bytes = new Uint8Array(length);
32
- for (let i = 0; i < length; i++) {
33
- bytes[i] = text.charCodeAt(i);
34
- }
35
- const decoder = new TextDecoder();
36
- return decoder.decode(bytes);
37
- }
38
- else {
39
- const { Buffer } = require('buffer');
40
- return Buffer.from(str, 'base64').toString();
41
- }
42
- }
43
27
  const basicAuth = (options) => {
44
28
  if (!options.realm) {
45
29
  options.realm = 'Secure Area';
@@ -1,2 +1,6 @@
1
1
  import type { Context } from '../../context';
2
- export declare const mustache: () => (c: Context, next: Function) => Promise<void>;
2
+ declare type Options = {
3
+ root: string;
4
+ };
5
+ export declare const mustache: (opt?: Options) => (c: Context, next: Function) => Promise<void>;
6
+ export {};
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mustache = void 0;
4
4
  const cloudflare_1 = require("../../utils/cloudflare");
5
5
  const EXTENSION = '.mustache';
6
- const mustache = () => {
6
+ const DEFAULT_DOCUMENT = 'index.mustache';
7
+ const mustache = (opt = { root: '' }) => {
8
+ const { root } = opt;
7
9
  return async (c, next) => {
8
10
  let Mustache;
9
11
  try {
@@ -12,24 +14,30 @@ const mustache = () => {
12
14
  catch (_a) {
13
15
  throw new Error('If you want to use Mustache Middleware, install "mustache" package first.');
14
16
  }
15
- c.render = async (filename, view = {}, options) => {
16
- const buffer = await (0, cloudflare_1.getContentFromKVAsset)(`${filename}${EXTENSION}`);
17
+ c.render = async (filename, params = {}, options) => {
18
+ const path = (0, cloudflare_1.getKVFilePath)({ filename: `${filename}${EXTENSION}`, root: root, defaultDocument: DEFAULT_DOCUMENT });
19
+ const buffer = await (0, cloudflare_1.getContentFromKVAsset)(path);
17
20
  if (!buffer) {
18
- throw new Error(`Template "${filename}${EXTENSION}" is not found or blank.`);
21
+ throw new Error(`Template "${path}" is not found or blank.`);
19
22
  }
20
23
  const content = bufferToString(buffer);
21
24
  const partialArgs = {};
22
25
  if (options) {
23
26
  const partials = options;
24
27
  for (const key of Object.keys(partials)) {
25
- const partialBuffer = await (0, cloudflare_1.getContentFromKVAsset)(`${partials[key]}${EXTENSION}`);
28
+ const partialPath = (0, cloudflare_1.getKVFilePath)({
29
+ filename: `${partials[key]}${EXTENSION}`,
30
+ root: root,
31
+ defaultDocument: DEFAULT_DOCUMENT,
32
+ });
33
+ const partialBuffer = await (0, cloudflare_1.getContentFromKVAsset)(partialPath);
26
34
  if (!partialBuffer) {
27
- throw new Error(`Partial Template "${partials[key]}${EXTENSION}" is not found or blank.`);
35
+ throw new Error(`Partial Template "${partialPath}" is not found or blank.`);
28
36
  }
29
37
  partialArgs[key] = bufferToString(partialBuffer);
30
38
  }
31
39
  }
32
- const output = Mustache.render(content, view, partialArgs);
40
+ const output = Mustache.render(content, params, partialArgs);
33
41
  return c.html(output);
34
42
  };
35
43
  await next();
@@ -9,7 +9,7 @@ const serveStatic = (opt = { root: '' }) => {
9
9
  return async (c, next) => {
10
10
  await next();
11
11
  const url = new URL(c.req.url);
12
- const path = getKVPath(url.pathname, opt.root);
12
+ const path = (0, cloudflare_1.getKVFilePath)({ filename: url.pathname, root: opt.root, defaultDocument: DEFAULT_DOCUMENT });
13
13
  const content = await (0, cloudflare_1.getContentFromKVAsset)(path);
14
14
  if (content) {
15
15
  const mimeType = (0, mime_1.getMimeType)(path);
@@ -24,21 +24,3 @@ const serveStatic = (opt = { root: '' }) => {
24
24
  };
25
25
  };
26
26
  exports.serveStatic = serveStatic;
27
- const getKVPath = (filename, root) => {
28
- if (filename.endsWith('/')) {
29
- // /top/ => /top/index.html
30
- filename = filename.concat(DEFAULT_DOCUMENT);
31
- }
32
- else if (!(0, mime_1.getMimeType)(filename)) {
33
- // /top => /top/index.html
34
- filename = filename.concat('/' + DEFAULT_DOCUMENT);
35
- }
36
- // /foo.html => foo.html
37
- filename = filename.replace(/^\//, '');
38
- // assets/ => assets
39
- root = root.replace(/\/$/, '');
40
- // ./assets/foo.html => assets/foo.html
41
- let path = root + '/' + filename;
42
- path = path.replace(/^\.?\//, '');
43
- return path;
44
- };
@@ -1,3 +1,2 @@
1
1
  export declare class Middleware {
2
- static default: (c: import("./context").Context, next: Function) => Promise<void>;
3
2
  }
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Middleware = void 0;
4
- const default_1 = require("./middleware/default");
5
4
  class Middleware {
6
5
  }
7
6
  exports.Middleware = Middleware;
8
- Middleware.default = default_1.defaultMiddleware;
package/dist/node.d.ts CHANGED
@@ -16,7 +16,7 @@ export declare class Node<T> {
16
16
  [key: string]: Node<T>;
17
17
  };
18
18
  middlewares: [];
19
- constructor(method?: string, handler?: any, children?: {
19
+ constructor(method?: string, handler?: T, children?: {
20
20
  [key: string]: Node<T>;
21
21
  });
22
22
  insert(method: string, path: string, handler: T): Node<T>;
@@ -1,2 +1,4 @@
1
1
  export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
2
- export declare const timingSafeEqual: (a: any, b: any) => Promise<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>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.timingSafeEqual = exports.equal = void 0;
3
+ exports.timingSafeEqual = exports.sha256 = exports.decodeBase64 = exports.equal = void 0;
4
4
  const equal = (a, b) => {
5
5
  if (a === b) {
6
6
  return true;
@@ -19,13 +19,50 @@ const equal = (a, b) => {
19
19
  return true;
20
20
  };
21
21
  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.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('');
50
+ return hash;
51
+ }
52
+ try {
53
+ const crypto = require('crypto');
54
+ const hash = crypto.createHash('sha256').update(a).digest('hex');
55
+ return hash;
56
+ }
57
+ catch (e) {
58
+ console.error('If you want to do "sha256", polyfill "crypto" module.');
59
+ throw e;
60
+ }
61
+ };
62
+ exports.sha256 = sha256;
22
63
  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;
64
+ const sa = await (0, exports.sha256)(a);
65
+ const sb = await (0, exports.sha256)(b);
66
+ return sa === sb && a === b;
30
67
  };
31
68
  exports.timingSafeEqual = timingSafeEqual;
@@ -1 +1,8 @@
1
1
  export declare const getContentFromKVAsset: (path: string) => Promise<ArrayBuffer>;
2
+ declare type Options = {
3
+ filename: string;
4
+ root?: string;
5
+ defaultDocument?: string;
6
+ };
7
+ export declare const getKVFilePath: (opt: Options) => string;
8
+ export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getContentFromKVAsset = void 0;
3
+ exports.getKVFilePath = exports.getContentFromKVAsset = void 0;
4
4
  const getContentFromKVAsset = async (path) => {
5
5
  let ASSET_MANIFEST;
6
6
  if (typeof __STATIC_CONTENT_MANIFEST === 'string') {
@@ -21,3 +21,25 @@ const getContentFromKVAsset = async (path) => {
21
21
  return content;
22
22
  };
23
23
  exports.getContentFromKVAsset = getContentFromKVAsset;
24
+ const getKVFilePath = (opt) => {
25
+ let filename = opt.filename;
26
+ let root = opt.root || '';
27
+ const defaultDocument = opt.defaultDocument || 'index.html';
28
+ if (filename.endsWith('/')) {
29
+ // /top/ => /top/index.html
30
+ filename = filename.concat(defaultDocument);
31
+ }
32
+ else if (!filename.match(/\.[a-zA-Z0-9]+$/)) {
33
+ // /top => /top/index.html
34
+ filename = filename.concat('/' + defaultDocument);
35
+ }
36
+ // /foo.html => foo.html
37
+ filename = filename.replace(/^\//, '');
38
+ // assets/ => assets
39
+ root = root.replace(/\/$/, '');
40
+ // ./assets/foo.html => assets/foo.html
41
+ let path = root ? root + '/' + filename : filename;
42
+ path = path.replace(/^\.?\//, '');
43
+ return path;
44
+ };
45
+ exports.getKVFilePath = getKVFilePath;
@@ -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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "0.3.2",
3
+ "version": "0.3.6",
4
4
  "description": "[炎] Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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;