hono 2.0.6 → 2.0.9

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
@@ -46,13 +46,13 @@ export default app
46
46
  **Hono is fastest**, compared to other routers for Cloudflare Workers.
47
47
 
48
48
  ```plain
49
- hono - trie-router(default) x 389,510 ops/sec ±3.16% (85 runs sampled)
50
- hono - regexp-router x 452,290 ops/sec ±2.64% (84 runs sampled)
51
- itty-router x 206,013 ops/sec ±3.39% (90 runs sampled)
52
- sunder x 323,131 ops/sec ±0.75% (97 runs sampled)
53
- worktop x 191,218 ops/sec ±2.70% (91 runs sampled)
49
+ hono - trie-router(default) x 424,449 ops/sec ±4.98% (77 runs sampled)
50
+ hono - regexp-router x 516,228 ops/sec ±4.79% (81 runs sampled)
51
+ itty-router x 206,641 ops/sec ±3.59% (87 runs sampled)
52
+ sunder x 319,500 ops/sec ±1.33% (93 runs sampled)
53
+ worktop x 187,280 ops/sec ±3.09% (87 runs sampled)
54
54
  Fastest is hono - regexp-router
55
- ✨ Done in 43.56s.
55
+ ✨ Done in 38.32s.
56
56
  ```
57
57
 
58
58
  ## Documentation
package/dist/compose.d.ts CHANGED
@@ -1,6 +1,2 @@
1
1
  import type { ErrorHandler, NotFoundHandler } from './hono';
2
- export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler<{
3
- [x: string]: any;
4
- }> | undefined, onNotFound?: NotFoundHandler<{
5
- [x: string]: any;
6
- }> | undefined) => (context: C, next?: Function | undefined) => Promise<C>;
2
+ export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler, onNotFound?: NotFoundHandler) => (context: C, next?: Function) => Promise<C>;
package/dist/compose.js CHANGED
@@ -4,42 +4,45 @@ 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, onNotFound) => {
7
+ const middlewareLength = middleware.length;
7
8
  return (context, next) => {
8
9
  let index = -1;
9
10
  return dispatch(0);
10
11
  async function dispatch(i) {
11
12
  if (i <= index) {
12
- return Promise.reject(new Error('next() called multiple times'));
13
+ throw new Error('next() called multiple times');
13
14
  }
14
15
  let handler = middleware[i];
15
16
  index = i;
16
- if (i === middleware.length && next)
17
+ if (i === middlewareLength && next)
17
18
  handler = next;
18
19
  if (!handler) {
19
20
  if (context instanceof context_1.HonoContext && context.finalized === false && onNotFound) {
20
21
  context.res = await onNotFound(context);
21
22
  }
22
- return Promise.resolve(context);
23
- }
24
- return Promise.resolve(handler(context, () => dispatch(i + 1)))
25
- .then((res) => {
26
- // If handler return Response like `return c.text('foo')`
27
- if (res && context instanceof context_1.HonoContext) {
28
- context.res = res;
29
- }
30
23
  return context;
31
- })
32
- .catch((err) => {
24
+ }
25
+ let res;
26
+ let isError = false;
27
+ try {
28
+ const tmp = handler(context, () => dispatch(i + 1));
29
+ res = tmp instanceof Promise ? await tmp : tmp;
30
+ }
31
+ catch (err) {
33
32
  if (context instanceof context_1.HonoContext && onError) {
34
33
  if (err instanceof Error) {
35
- context.res = onError(err, context);
34
+ isError = true;
35
+ res = onError(err, context);
36
36
  }
37
- return context;
38
37
  }
39
- else {
38
+ if (!res) {
40
39
  throw err;
41
40
  }
42
- });
41
+ }
42
+ if (res && context instanceof context_1.HonoContext && (!context.finalized || isError)) {
43
+ context.res = res;
44
+ }
45
+ return context;
43
46
  }
44
47
  };
45
48
  };
package/dist/context.d.ts CHANGED
@@ -52,8 +52,10 @@ export declare class HonoContext<RequestParamKeyType extends string = string, E
52
52
  set res(_res: Response);
53
53
  header(name: string, value: string): void;
54
54
  status(status: StatusCode): void;
55
+ set<Key extends keyof ContextVariableMap>(key: Key, value: ContextVariableMap[Key]): void;
55
56
  set(key: string, value: any): void;
56
- get(key: string): any;
57
+ get<Key extends keyof ContextVariableMap>(key: Key): ContextVariableMap[Key];
58
+ get<T = any>(key: string): T;
57
59
  pretty(prettyJSON: boolean, space?: number): void;
58
60
  newResponse(data: Data | null, status: StatusCode, headers?: Headers): Response;
59
61
  body(data: Data | null, status?: StatusCode, headers?: Headers): Response;
package/dist/context.js CHANGED
@@ -39,7 +39,7 @@ class HonoContext {
39
39
  }
40
40
  header(name, value) {
41
41
  this._headers || (this._headers = {});
42
- this._headers[name] = value;
42
+ this._headers[name.toLowerCase()] = value;
43
43
  if (this.finalized) {
44
44
  this.res.headers.set(name, value);
45
45
  }
@@ -62,7 +62,7 @@ class HonoContext {
62
62
  this._prettySpace = space;
63
63
  }
64
64
  newResponse(data, status, headers = {}) {
65
- const _headers = { ...this._headers, ...headers };
65
+ const _headers = { ...this._headers };
66
66
  if (this._res) {
67
67
  this._res.headers.forEach((v, k) => {
68
68
  _headers[k] = v;
@@ -70,25 +70,25 @@ class HonoContext {
70
70
  }
71
71
  return new Response(data, {
72
72
  status: status || this._status || 200,
73
- headers: _headers,
73
+ headers: { ..._headers, ...headers },
74
74
  });
75
75
  }
76
76
  body(data, status = this._status, headers = {}) {
77
77
  return this.newResponse(data, status, headers);
78
78
  }
79
79
  text(text, status = this._status, headers = {}) {
80
- headers['Content-Type'] || (headers['Content-Type'] = 'text/plain; charset=UTF-8');
80
+ headers['content-type'] = 'text/plain; charset=UTF-8';
81
81
  return this.body(text, status, headers);
82
82
  }
83
83
  json(object, status = this._status, headers = {}) {
84
84
  const body = this._pretty
85
85
  ? JSON.stringify(object, null, this._prettySpace)
86
86
  : JSON.stringify(object);
87
- headers['Content-Type'] || (headers['Content-Type'] = 'application/json; charset=UTF-8');
87
+ headers['content-type'] = 'application/json; charset=UTF-8';
88
88
  return this.body(body, status, headers);
89
89
  }
90
90
  html(html, status = this._status, headers = {}) {
91
- headers['Content-Type'] || (headers['Content-Type'] = 'text/html; charset=UTF-8');
91
+ headers['content-type'] = 'text/html; charset=UTF-8';
92
92
  return this.body(html, status, headers);
93
93
  }
94
94
  redirect(location, status = 302) {
@@ -103,7 +103,7 @@ class HonoContext {
103
103
  }
104
104
  cookie(name, value, opt) {
105
105
  const cookie = (0, cookie_1.serialize)(name, value, opt);
106
- this.header('Set-Cookie', cookie);
106
+ this.header('set-cookie', cookie);
107
107
  }
108
108
  notFound() {
109
109
  return this.notFoundHandler(this);
package/dist/hono.d.ts CHANGED
@@ -50,7 +50,7 @@ export declare class Hono<E extends Env = Env, P extends string = '/'> extends H
50
50
  private matchRoute;
51
51
  private dispatch;
52
52
  handleEvent(event: FetchEvent): Promise<Response>;
53
- fetch: (request: Request, env?: E | undefined, executionCtx?: ExecutionContext | undefined) => Promise<Response>;
53
+ fetch: (request: Request, env?: E, executionCtx?: ExecutionContext) => Promise<Response>;
54
54
  request(input: RequestInfo, requestInit?: RequestInit): Promise<Response>;
55
55
  }
56
56
  export {};
@@ -4,5 +4,5 @@ declare type EncodingType = 'gzip' | 'deflate';
4
4
  interface CompressionOptions {
5
5
  encoding?: EncodingType;
6
6
  }
7
- export declare const compress: (options?: CompressionOptions | undefined) => (ctx: Context, next: Next) => Promise<void>;
7
+ export declare const compress: (options?: CompressionOptions) => (ctx: Context, next: Next) => Promise<void>;
8
8
  export {};
@@ -8,5 +8,5 @@ declare type CORSOptions = {
8
8
  credentials?: boolean;
9
9
  exposeHeaders?: string[];
10
10
  };
11
- export declare const cors: (options?: CORSOptions | undefined) => (c: Context, next: Next) => Promise<void>;
11
+ export declare const cors: (options?: CORSOptions) => (c: Context, next: Next) => Promise<void>;
12
12
  export {};
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.etag = void 0;
4
- const body_1 = require("../../utils/body");
5
4
  const crypto_1 = require("../../utils/crypto");
6
5
  const etag = (options = { weak: false }) => {
7
6
  return async (c, next) => {
@@ -9,8 +8,7 @@ const etag = (options = { weak: false }) => {
9
8
  await next();
10
9
  const res = c.res;
11
10
  const clone = res.clone();
12
- const body = await (0, body_1.parseBody)(res);
13
- const hash = await (0, crypto_1.sha1)(body);
11
+ const hash = await (0, crypto_1.sha1)(res.body || '');
14
12
  const etag = options.weak ? `W/"${hash}"` : `"${hash}"`;
15
13
  if (ifNoneMatch && ifNoneMatch === etag) {
16
14
  await clone.blob(); // Force using body
@@ -1,5 +1,3 @@
1
- export declare type HtmlEscapedString = string & {
2
- isEscaped: true;
3
- };
1
+ import type { HtmlEscapedString } from '../../utils/html';
4
2
  export declare const raw: (value: any) => HtmlEscapedString;
5
3
  export declare const html: (strings: TemplateStringsArray, ...values: any[]) => HtmlEscapedString;
@@ -9,24 +9,28 @@ const raw = (value) => {
9
9
  };
10
10
  exports.raw = raw;
11
11
  const html = (strings, ...values) => {
12
- let result = '';
12
+ const buffer = [''];
13
13
  for (let i = 0, len = strings.length - 1; i < len; i++) {
14
- result += strings[i];
14
+ buffer[0] += strings[i];
15
15
  const children = values[i] instanceof Array ? values[i].flat(Infinity) : [values[i]];
16
16
  for (let i = 0, len = children.length; i < len; i++) {
17
17
  const child = children[i];
18
- if (typeof child === 'boolean' || child === null || child === undefined) {
18
+ if (typeof child === 'string') {
19
+ (0, html_1.escapeToBuffer)(child, buffer);
20
+ }
21
+ else if (typeof child === 'boolean' || child === null || child === undefined) {
19
22
  continue;
20
23
  }
21
- else if (typeof child === 'object' && child.isEscaped) {
22
- result += child;
24
+ else if ((typeof child === 'object' && child.isEscaped) ||
25
+ typeof child === 'number') {
26
+ buffer[0] += child;
23
27
  }
24
28
  else {
25
- result += (0, html_1.escape)(child.toString());
29
+ (0, html_1.escapeToBuffer)(child.toString(), buffer);
26
30
  }
27
31
  }
28
32
  }
29
- result += strings[strings.length - 1];
30
- return (0, exports.raw)(result);
33
+ buffer[0] += strings[strings.length - 1];
34
+ return (0, exports.raw)(buffer[0]);
31
35
  };
32
36
  exports.html = html;
@@ -1,4 +1,4 @@
1
- import type { HtmlEscapedString } from '../../utils/html';
1
+ import type { StringBuffer, HtmlEscaped, HtmlEscapedString } from '../../utils/html';
2
2
  declare global {
3
3
  namespace jsx.JSX {
4
4
  interface IntrinsicElements {
@@ -6,11 +6,21 @@ declare global {
6
6
  }
7
7
  }
8
8
  }
9
+ declare type Child = string | number | JSXNode | Child[];
10
+ export declare class JSXNode implements HtmlEscaped {
11
+ tag: string | Function;
12
+ props: Record<string, any>;
13
+ children: Child[];
14
+ isEscaped: true;
15
+ constructor(tag: string | Function, props: Record<string, any>, children: Child[]);
16
+ toString(): string;
17
+ toStringToBuffer(buffer: StringBuffer): void;
18
+ }
9
19
  export { jsxFn as jsx };
10
- declare const jsxFn: (tag: string | Function, props: Record<string, any>, ...children: (string | HtmlEscapedString)[]) => HtmlEscapedString;
20
+ declare const jsxFn: (tag: string | Function, props: Record<string, any>, ...children: (string | HtmlEscapedString)[]) => JSXNode;
11
21
  declare type FC<T = Record<string, any>> = (props: T) => HtmlEscapedString;
12
22
  export declare const memo: <T>(component: FC<T>, propsAreEqual?: (prevProps: Readonly<T>, nextProps: Readonly<T>) => boolean) => FC<T>;
13
23
  export declare const Fragment: (props: {
14
24
  key?: string;
15
25
  children?: any;
16
- }) => HtmlEscapedString;
26
+ }) => JSXNode;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Fragment = exports.memo = exports.jsx = void 0;
3
+ exports.Fragment = exports.memo = exports.jsx = exports.JSXNode = void 0;
4
4
  const html_1 = require("../../utils/html");
5
5
  const emptyTags = [
6
6
  'area',
@@ -19,68 +19,144 @@ const emptyTags = [
19
19
  'track',
20
20
  'wbr',
21
21
  ];
22
- const booleanAttributes = ['checked', 'selected', 'disabled', 'readonly', 'multiple'];
23
- const newHtmlEscapedString = (str) => {
24
- const escapedString = new String(str);
25
- escapedString.isEscaped = true;
26
- return escapedString;
27
- };
28
- const jsxFn = (tag, props, ...children) => {
29
- if (typeof tag === 'function') {
30
- return tag.call(null, { ...props, children: children.length <= 1 ? children[0] : children });
31
- }
32
- let result = tag !== '' ? `<${tag}` : '';
33
- const propsKeys = Object.keys(props || {});
34
- for (let i = 0, len = propsKeys.length; i < len; i++) {
35
- const v = props[propsKeys[i]];
36
- if (typeof v === 'string') {
37
- result += ` ${propsKeys[i]}="${(0, html_1.escape)(v)}"`;
38
- }
39
- else if (typeof v === 'number') {
40
- result += ` ${propsKeys[i]}="${v}"`;
22
+ const booleanAttributes = [
23
+ 'allowfullscreen',
24
+ 'async',
25
+ 'autofocus',
26
+ 'autoplay',
27
+ 'checked',
28
+ 'controls',
29
+ 'default',
30
+ 'defer',
31
+ 'disabled',
32
+ 'formnovalidate',
33
+ 'hidden',
34
+ 'inert',
35
+ 'ismap',
36
+ 'itemscope',
37
+ 'loop',
38
+ 'multiple',
39
+ 'muted',
40
+ 'nomodule',
41
+ 'novalidate',
42
+ 'open',
43
+ 'playsinline',
44
+ 'readonly',
45
+ 'required',
46
+ 'reversed',
47
+ 'selected',
48
+ ];
49
+ const childrenToStringToBuffer = (children, buffer) => {
50
+ for (let i = 0, len = children.length; i < len; i++) {
51
+ const child = children[i];
52
+ if (typeof child === 'string') {
53
+ (0, html_1.escapeToBuffer)(child, buffer);
41
54
  }
42
- else if (v === null || v === undefined) {
43
- // Do nothing
55
+ else if (typeof child === 'boolean' || child === null || child === undefined) {
56
+ continue;
44
57
  }
45
- else if (typeof v === 'boolean' && booleanAttributes.includes(propsKeys[i])) {
46
- if (v) {
47
- result += ` ${propsKeys[i]}=""`;
48
- }
58
+ else if (child instanceof JSXNode) {
59
+ child.toStringToBuffer(buffer);
49
60
  }
50
- else if (propsKeys[i] === 'dangerouslySetInnerHTML') {
51
- if (children.length > 0) {
52
- throw 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.';
53
- }
54
- children = [newHtmlEscapedString(v.__html)];
61
+ else if (typeof child === 'number' || child.isEscaped) {
62
+ buffer[0] += child;
55
63
  }
56
64
  else {
57
- result += ` ${propsKeys[i]}="${(0, html_1.escape)(v.toString())}"`;
65
+ // `child` type is `Child[]`, so stringify recursively
66
+ childrenToStringToBuffer(child, buffer);
58
67
  }
59
68
  }
60
- if (emptyTags.includes(tag)) {
61
- result += '/>';
62
- return newHtmlEscapedString(result);
69
+ };
70
+ class JSXNode {
71
+ constructor(tag, props, children) {
72
+ this.isEscaped = true;
73
+ this.tag = tag;
74
+ this.props = props;
75
+ this.children = children;
63
76
  }
64
- if (tag !== '') {
65
- result += '>';
77
+ toString() {
78
+ const buffer = [''];
79
+ this.toStringToBuffer(buffer);
80
+ return buffer[0];
66
81
  }
67
- const flattenChildren = children.flat(Infinity);
68
- for (let i = 0, len = flattenChildren.length; i < len; i++) {
69
- const child = flattenChildren[i];
70
- if (typeof child === 'boolean' || child === null || child === undefined) {
71
- continue;
82
+ toStringToBuffer(buffer) {
83
+ const tag = this.tag;
84
+ const props = this.props;
85
+ let { children } = this;
86
+ buffer[0] += `<${tag}`;
87
+ const propsKeys = Object.keys(props || {});
88
+ for (let i = 0, len = propsKeys.length; i < len; i++) {
89
+ const v = props[propsKeys[i]];
90
+ if (typeof v === 'string') {
91
+ buffer[0] += ` ${propsKeys[i]}="`;
92
+ (0, html_1.escapeToBuffer)(v, buffer);
93
+ buffer[0] += '"';
94
+ }
95
+ else if (typeof v === 'number') {
96
+ buffer[0] += ` ${propsKeys[i]}="${v}"`;
97
+ }
98
+ else if (v === null || v === undefined) {
99
+ // Do nothing
100
+ }
101
+ else if (typeof v === 'boolean' && booleanAttributes.includes(propsKeys[i])) {
102
+ if (v) {
103
+ buffer[0] += ` ${propsKeys[i]}=""`;
104
+ }
105
+ }
106
+ else if (propsKeys[i] === 'dangerouslySetInnerHTML') {
107
+ if (children.length > 0) {
108
+ throw 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.';
109
+ }
110
+ const escapedString = new String(v.__html);
111
+ escapedString.isEscaped = true;
112
+ children = [escapedString];
113
+ }
114
+ else {
115
+ buffer[0] += ` ${propsKeys[i]}="`;
116
+ (0, html_1.escapeToBuffer)(v.toString(), buffer);
117
+ buffer[0] += '"';
118
+ }
119
+ }
120
+ if (emptyTags.includes(tag)) {
121
+ buffer[0] += '/>';
122
+ return;
72
123
  }
73
- else if (typeof child === 'object' && child.isEscaped) {
74
- result += child;
124
+ buffer[0] += '>';
125
+ childrenToStringToBuffer(children, buffer);
126
+ buffer[0] += `</${tag}>`;
127
+ }
128
+ }
129
+ exports.JSXNode = JSXNode;
130
+ class JSXFunctionNode extends JSXNode {
131
+ toStringToBuffer(buffer) {
132
+ const { children } = this;
133
+ const res = this.tag.call(null, {
134
+ ...this.props,
135
+ children: children.length <= 1 ? children[0] : children,
136
+ });
137
+ if (res instanceof JSXNode) {
138
+ res.toStringToBuffer(buffer);
139
+ }
140
+ else if (typeof res === 'number' || res.isEscaped) {
141
+ buffer[0] += res;
75
142
  }
76
143
  else {
77
- result += (0, html_1.escape)(child.toString());
144
+ (0, html_1.escapeToBuffer)(res, buffer);
78
145
  }
79
146
  }
80
- if (tag !== '') {
81
- result += `</${tag}>`;
147
+ }
148
+ class JSXFragmentNode extends JSXNode {
149
+ toStringToBuffer(buffer) {
150
+ childrenToStringToBuffer(this.children, buffer);
151
+ }
152
+ }
153
+ const jsxFn = (tag, props, ...children) => {
154
+ if (typeof tag === 'function') {
155
+ return new JSXFunctionNode(tag, props, children);
156
+ }
157
+ else {
158
+ return new JSXNode(tag, props, children);
82
159
  }
83
- return newHtmlEscapedString(result);
84
160
  };
85
161
  exports.jsx = jsxFn;
86
162
  const shallowEqual = (a, b) => {
@@ -112,6 +188,6 @@ const memo = (component, propsAreEqual = shallowEqual) => {
112
188
  };
113
189
  exports.memo = memo;
114
190
  const Fragment = (props) => {
115
- return jsxFn('', {}, ...(props.children || []));
191
+ return new JSXFragmentNode('', {}, props.children || []);
116
192
  };
117
193
  exports.Fragment = Fragment;
@@ -1,2 +1,2 @@
1
- import type { HtmlEscapedString } from '../html';
2
- export declare function jsxDEV(tag: string | Function, props: Record<string, any>): HtmlEscapedString;
1
+ import type { JSXNode } from '.';
2
+ export declare function jsxDEV(tag: string | Function, props: Record<string, any>): JSXNode;
@@ -2,5 +2,6 @@ import type { Context } from '../../context';
2
2
  import type { Next } from '../../hono';
3
3
  export declare const jwt: (options: {
4
4
  secret: string;
5
+ cookie?: string;
5
6
  alg?: string;
6
7
  }) => (ctx: Context, next: Next) => Promise<void>;
@@ -11,17 +11,26 @@ const jwt = (options) => {
11
11
  }
12
12
  return async (ctx, next) => {
13
13
  const credentials = ctx.req.headers.get('Authorization');
14
- if (!credentials) {
15
- ctx.res = new Response('Unauthorized', {
16
- status: 401,
17
- headers: {
18
- 'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="no authorization included in request"`,
19
- },
20
- });
21
- return;
14
+ let token;
15
+ if (credentials) {
16
+ const parts = credentials.split(/\s+/);
17
+ if (parts.length !== 2) {
18
+ ctx.res = new Response('Unauthorized', {
19
+ status: 401,
20
+ headers: {
21
+ 'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="invalid credentials structure"`,
22
+ },
23
+ });
24
+ return;
25
+ }
26
+ else {
27
+ token = parts[1];
28
+ }
29
+ }
30
+ else if (options.cookie) {
31
+ token = ctx.req.cookie(options.cookie);
22
32
  }
23
- const parts = credentials.split(/\s+/);
24
- if (parts.length !== 2) {
33
+ if (!token) {
25
34
  ctx.res = new Response('Unauthorized', {
26
35
  status: 401,
27
36
  headers: {
@@ -33,7 +42,7 @@ const jwt = (options) => {
33
42
  let authorized = false;
34
43
  let msg = '';
35
44
  try {
36
- authorized = await jwt_1.Jwt.verify(parts[1], options.secret, options.alg);
45
+ authorized = await jwt_1.Jwt.verify(token, options.secret, options.alg);
37
46
  }
38
47
  catch (e) {
39
48
  msg = `${e}`;
package/dist/request.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Body } from './utils/body';
1
2
  import type { Cookie } from './utils/cookie';
2
3
  declare global {
3
4
  interface Request<ParamKeyType extends string = string> {
@@ -22,9 +23,9 @@ declare global {
22
23
  (name: string): string;
23
24
  (): Cookie;
24
25
  };
25
- parsedBody?: Promise<any>;
26
+ parsedBody?: Promise<Body>;
26
27
  parseBody: {
27
- (): Promise<any>;
28
+ (): Promise<Body>;
28
29
  };
29
30
  }
30
31
  }
@@ -1 +1,2 @@
1
- export declare const parseBody: (r: Request | Response) => Promise<string | object | Record<string, string | File>>;
1
+ export declare type Body = string | object | Record<string, string | File> | ArrayBuffer;
2
+ export declare const parseBody: (r: Request | Response) => Promise<Body>;
@@ -25,6 +25,7 @@ const parseBody = async (r) => {
25
25
  }, form);
26
26
  return data;
27
27
  }
28
- return r.arrayBuffer();
28
+ const arrayBuffer = await r.arrayBuffer();
29
+ return arrayBuffer;
29
30
  };
30
31
  exports.parseBody = parseBody;
@@ -1,3 +1,3 @@
1
1
  export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
2
- export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function | undefined) => Promise<boolean>;
2
+ export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
3
3
  export declare const bufferToString: (buffer: ArrayBuffer) => string;
@@ -3,4 +3,4 @@ export declare type KVAssetOptions = {
3
3
  manifest?: object | string;
4
4
  namespace?: KVNamespace;
5
5
  };
6
- export declare const getContentFromKVAsset: (path: string, options?: KVAssetOptions | undefined) => Promise<ArrayBuffer | null>;
6
+ export declare const getContentFromKVAsset: (path: string, options?: KVAssetOptions) => Promise<ArrayBuffer | null>;
@@ -2,7 +2,7 @@ declare type Algorithm = {
2
2
  name: string;
3
3
  alias: string;
4
4
  };
5
- declare type Data = string | object | boolean;
5
+ declare type Data = string | boolean | number | object | ArrayBufferView | ArrayBuffer | ReadableStream;
6
6
  export declare const sha256: (data: Data) => Promise<string | null>;
7
7
  export declare const sha1: (data: Data) => Promise<string | null>;
8
8
  export declare const md5: (data: Data) => Promise<string | null>;
@@ -20,10 +20,29 @@ const md5 = async (data) => {
20
20
  };
21
21
  exports.md5 = md5;
22
22
  const createHash = async (data, algorithm) => {
23
+ let sourceBuffer;
24
+ if (data instanceof ReadableStream) {
25
+ let body = '';
26
+ const reader = data.getReader();
27
+ await reader?.read().then(async (chuck) => {
28
+ const value = await (0, exports.createHash)(chuck.value || '', algorithm);
29
+ body += value;
30
+ });
31
+ return body;
32
+ }
33
+ if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) {
34
+ sourceBuffer = data;
35
+ }
36
+ else {
37
+ if (typeof data === 'object') {
38
+ data = JSON.stringify(data);
39
+ }
40
+ sourceBuffer = new TextEncoder().encode(String(data));
41
+ }
23
42
  if (crypto && crypto.subtle) {
24
43
  const buffer = await crypto.subtle.digest({
25
44
  name: algorithm.name,
26
- }, new TextEncoder().encode(String(data)));
45
+ }, sourceBuffer);
27
46
  const hash = Array.prototype.map
28
47
  .call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
29
48
  .join('');
@@ -1,4 +1,6 @@
1
- export declare type HtmlEscapedString = string & {
1
+ export declare type HtmlEscaped = {
2
2
  isEscaped: true;
3
3
  };
4
- export declare const escape: (str: string) => string;
4
+ export declare type HtmlEscapedString = string & HtmlEscaped;
5
+ export declare type StringBuffer = [string];
6
+ export declare const escapeToBuffer: (str: string, buffer: StringBuffer) => void;
@@ -1,15 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.escape = void 0;
4
- const entityMap = {
5
- '&': '&amp;',
6
- '<': '&lt;',
7
- '>': '&gt;',
8
- '"': '&quot;',
3
+ exports.escapeToBuffer = void 0;
4
+ // The `escapeToBuffer` implementation is based on code from the MIT licensed `react-dom` package.
5
+ // https://github.com/facebook/react/blob/main/packages/react-dom/src/server/escapeTextForBrowser.js
6
+ const escapeRe = /[&<>"]/;
7
+ const escapeToBuffer = (str, buffer) => {
8
+ const match = str.search(escapeRe);
9
+ if (match === -1) {
10
+ buffer[0] += str;
11
+ return;
12
+ }
13
+ let escape;
14
+ let index;
15
+ let lastIndex = 0;
16
+ for (index = match; index < str.length; index++) {
17
+ switch (str.charCodeAt(index)) {
18
+ case 34: // "
19
+ escape = '&quot;';
20
+ break;
21
+ case 38: // &
22
+ escape = '&amp;';
23
+ break;
24
+ case 60: // <
25
+ escape = '&lt;';
26
+ break;
27
+ case 62: // >
28
+ escape = '&gt;';
29
+ break;
30
+ default:
31
+ continue;
32
+ }
33
+ buffer[0] += str.substring(lastIndex, index) + escape;
34
+ lastIndex = index + 1;
35
+ }
36
+ buffer[0] += str.substring(lastIndex, index);
9
37
  };
10
- const escapeRe = new RegExp(`[${Object.keys(entityMap).join('')}]`, 'g');
11
- const replaceFn = (m) => entityMap[m];
12
- const escape = (str) => {
13
- return str.replace(escapeRe, replaceFn);
14
- };
15
- exports.escape = escape;
38
+ exports.escapeToBuffer = escapeToBuffer;
@@ -34,7 +34,7 @@ const mimes = {
34
34
  gif: 'image/gif',
35
35
  htm: 'text/html',
36
36
  html: 'text/html',
37
- ico: 'image/vnd.microsoft.icon',
37
+ ico: 'image/x-icon',
38
38
  ics: 'text/calendar',
39
39
  jar: 'application/java-archive',
40
40
  jpeg: 'image/jpeg',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "2.0.6",
3
+ "version": "2.0.9",
4
4
  "description": "Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",