hono 4.5.4 → 4.5.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.
@@ -1,5 +1,6 @@
1
1
  // src/adapter/bun/serve-static.ts
2
2
  import { serveStatic as baseServeStatic } from "../../middleware/serve-static/index.js";
3
+ import { stat } from "node:fs/promises";
3
4
  var serveStatic = (options) => {
4
5
  return async function serveStatic2(c, next) {
5
6
  const getContent = async (path) => {
@@ -10,10 +11,20 @@ var serveStatic = (options) => {
10
11
  const pathResolve = (path) => {
11
12
  return `./${path}`;
12
13
  };
14
+ const isDir = async (path) => {
15
+ let isDir2;
16
+ try {
17
+ const stats = await stat(path);
18
+ isDir2 = stats.isDirectory();
19
+ } catch {
20
+ }
21
+ return isDir2;
22
+ };
13
23
  return baseServeStatic({
14
24
  ...options,
15
25
  getContent,
16
- pathResolve
26
+ pathResolve,
27
+ isDir
17
28
  })(c, next);
18
29
  };
19
30
  };
@@ -1,6 +1,6 @@
1
1
  // src/adapter/deno/serve-static.ts
2
2
  import { serveStatic as baseServeStatic } from "../../middleware/serve-static/index.js";
3
- var { open } = Deno;
3
+ var { open, lstatSync } = Deno;
4
4
  var serveStatic = (options) => {
5
5
  return async function serveStatic2(c, next) {
6
6
  const getContent = async (path) => {
@@ -14,10 +14,20 @@ var serveStatic = (options) => {
14
14
  const pathResolve = (path) => {
15
15
  return `./${path}`;
16
16
  };
17
+ const isDir = (path) => {
18
+ let isDir2;
19
+ try {
20
+ const stat = lstatSync(path);
21
+ isDir2 = stat.isDirectory;
22
+ } catch {
23
+ }
24
+ return isDir2;
25
+ };
17
26
  return baseServeStatic({
18
27
  ...options,
19
28
  getContent,
20
- pathResolve
29
+ pathResolve,
30
+ isDir
21
31
  })(c, next);
22
32
  };
23
33
  };
@@ -22,6 +22,7 @@ __export(serve_static_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(serve_static_exports);
24
24
  var import_serve_static = require("../../middleware/serve-static");
25
+ var import_promises = require("node:fs/promises");
25
26
  const serveStatic = (options) => {
26
27
  return async function serveStatic2(c, next) {
27
28
  const getContent = async (path) => {
@@ -32,10 +33,20 @@ const serveStatic = (options) => {
32
33
  const pathResolve = (path) => {
33
34
  return `./${path}`;
34
35
  };
36
+ const isDir = async (path) => {
37
+ let isDir2;
38
+ try {
39
+ const stats = await (0, import_promises.stat)(path);
40
+ isDir2 = stats.isDirectory();
41
+ } catch {
42
+ }
43
+ return isDir2;
44
+ };
35
45
  return (0, import_serve_static.serveStatic)({
36
46
  ...options,
37
47
  getContent,
38
- pathResolve
48
+ pathResolve,
49
+ isDir
39
50
  })(c, next);
40
51
  };
41
52
  };
@@ -22,7 +22,7 @@ __export(serve_static_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(serve_static_exports);
24
24
  var import_serve_static = require("../../middleware/serve-static");
25
- const { open } = Deno;
25
+ const { open, lstatSync } = Deno;
26
26
  const serveStatic = (options) => {
27
27
  return async function serveStatic2(c, next) {
28
28
  const getContent = async (path) => {
@@ -36,10 +36,20 @@ const serveStatic = (options) => {
36
36
  const pathResolve = (path) => {
37
37
  return `./${path}`;
38
38
  };
39
+ const isDir = (path) => {
40
+ let isDir2;
41
+ try {
42
+ const stat = lstatSync(path);
43
+ isDir2 = stat.isDirectory;
44
+ } catch {
45
+ }
46
+ return isDir2;
47
+ };
39
48
  return (0, import_serve_static.serveStatic)({
40
49
  ...options,
41
50
  getContent,
42
- pathResolve
51
+ pathResolve,
52
+ isDir
43
53
  })(c, next);
44
54
  };
45
55
  };
@@ -208,7 +208,9 @@ class JSXFunctionNode extends JSXNode {
208
208
  ...this.props,
209
209
  children: children.length <= 1 ? children[0] : children
210
210
  });
211
- if (res instanceof Promise) {
211
+ if (typeof res === "boolean" || res == null) {
212
+ return;
213
+ } else if (res instanceof Promise) {
212
214
  if (import_context.globalContexts.length === 0) {
213
215
  buffer.unshift("", res);
214
216
  } else {
@@ -303,11 +305,11 @@ const shallowEqual = (a, b) => {
303
305
  return true;
304
306
  };
305
307
  const memo = (component, propsAreEqual = shallowEqual) => {
306
- let computed = void 0;
308
+ let computed = null;
307
309
  let prevProps = void 0;
308
310
  return (props) => {
309
311
  if (prevProps && !propsAreEqual(prevProps, props)) {
310
- computed = void 0;
312
+ computed = null;
311
313
  }
312
314
  prevProps = props;
313
315
  return computed ||= component(props);
@@ -34,6 +34,15 @@ const serveStatic = (options) => {
34
34
  let filename = options.path ?? decodeURI(c.req.path);
35
35
  filename = options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename;
36
36
  const root = options.root;
37
+ if (!filename.endsWith("/") && options.isDir) {
38
+ const path2 = (0, import_filepath.getFilePathWithoutDefaultDocument)({
39
+ filename,
40
+ root
41
+ });
42
+ if (path2 && await options.isDir(path2)) {
43
+ filename = filename + "/";
44
+ }
45
+ }
37
46
  let path = (0, import_filepath.getFilePath)({
38
47
  filename,
39
48
  root,
@@ -41,13 +41,14 @@ const escapeRe = /[&<>'"]/;
41
41
  const stringBufferToString = async (buffer, callbacks) => {
42
42
  let str = "";
43
43
  callbacks ||= [];
44
- for (let i = buffer.length - 1; ; i--) {
45
- str += buffer[i];
44
+ const resolvedBuffer = await Promise.all(buffer);
45
+ for (let i = resolvedBuffer.length - 1; ; i--) {
46
+ str += resolvedBuffer[i];
46
47
  i--;
47
48
  if (i < 0) {
48
49
  break;
49
50
  }
50
- let r = await buffer[i];
51
+ let r = resolvedBuffer[i];
51
52
  if (typeof r === "object") {
52
53
  callbacks.push(...r.callbacks || []);
53
54
  }
@@ -25,7 +25,7 @@ var import_cookie = require("../helper/cookie");
25
25
  var import_http_exception = require("../http-exception");
26
26
  var import_buffer = require("../utils/buffer");
27
27
  const jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/;
28
- const multipartRegex = /^multipart\/form-data(; boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/;
28
+ const multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/;
29
29
  const urlencodedRegex = /^application\/x-www-form-urlencoded$/;
30
30
  const validator = (target, validationFunc) => {
31
31
  return async (c, next) => {
@@ -64,12 +64,13 @@ const validator = (target, validationFunc) => {
64
64
  const form = {};
65
65
  formData.forEach((value2, key) => {
66
66
  if (key.endsWith("[]")) {
67
- if (form[key] === void 0) {
68
- form[key] = [value2];
69
- } else if (Array.isArray(form[key])) {
70
- ;
71
- form[key].push(value2);
72
- }
67
+ ;
68
+ (form[key] ??= []).push(value2);
69
+ } else if (Array.isArray(form[key])) {
70
+ ;
71
+ form[key].push(value2);
72
+ } else if (key in form) {
73
+ form[key] = [form[key], value2];
73
74
  } else {
74
75
  form[key] = value2;
75
76
  }
package/dist/jsx/base.js CHANGED
@@ -171,7 +171,9 @@ var JSXFunctionNode = class extends JSXNode {
171
171
  ...this.props,
172
172
  children: children.length <= 1 ? children[0] : children
173
173
  });
174
- if (res instanceof Promise) {
174
+ if (typeof res === "boolean" || res == null) {
175
+ return;
176
+ } else if (res instanceof Promise) {
175
177
  if (globalContexts.length === 0) {
176
178
  buffer.unshift("", res);
177
179
  } else {
@@ -266,11 +268,11 @@ var shallowEqual = (a, b) => {
266
268
  return true;
267
269
  };
268
270
  var memo = (component, propsAreEqual = shallowEqual) => {
269
- let computed = void 0;
271
+ let computed = null;
270
272
  let prevProps = void 0;
271
273
  return (props) => {
272
274
  if (prevProps && !propsAreEqual(prevProps, props)) {
273
- computed = void 0;
275
+ computed = null;
274
276
  }
275
277
  prevProps = props;
276
278
  return computed ||= component(props);
@@ -12,6 +12,15 @@ var serveStatic = (options) => {
12
12
  let filename = options.path ?? decodeURI(c.req.path);
13
13
  filename = options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename;
14
14
  const root = options.root;
15
+ if (!filename.endsWith("/") && options.isDir) {
16
+ const path2 = getFilePathWithoutDefaultDocument({
17
+ filename,
18
+ root
19
+ });
20
+ if (path2 && await options.isDir(path2)) {
21
+ filename = filename + "/";
22
+ }
23
+ }
15
24
  let path = getFilePath({
16
25
  filename,
17
26
  root,
@@ -3,7 +3,8 @@ import type { Result } from './router';
3
3
  import type { Env, FetchEventLike, H, Input, NotFoundHandler, RouterRoute, TypedResponse } from './types';
4
4
  import type { RedirectStatusCode, StatusCode } from './utils/http-status';
5
5
  import type { InvalidJSONValue, IsAny, JSONParsed, JSONValue, SimplifyDeepArray } from './utils/types';
6
- type HeaderRecord = Record<string, string | string[]>;
6
+ import type { BaseMime } from './utils/mime';
7
+ type HeaderRecord = Record<'Content-Type', BaseMime> | Record<ResponseHeader, string | string[]> | Record<string, string | string[]>;
7
8
  /**
8
9
  * Data type can be a string, ArrayBuffer, or ReadableStream.
9
10
  */
@@ -158,6 +159,21 @@ type ContextOptions<E extends Env> = {
158
159
  matchResult?: Result<[H, RouterRoute]>;
159
160
  path?: string;
160
161
  };
162
+ interface SetHeadersOptions {
163
+ append?: boolean;
164
+ }
165
+ type ResponseHeader = 'Access-Control-Allow-Credentials' | 'Access-Control-Allow-Headers' | 'Access-Control-Allow-Methods' | 'Access-Control-Allow-Origin' | 'Access-Control-Expose-Headers' | 'Access-Control-Max-Age' | 'Age' | 'Allow' | 'Cache-Control' | 'Clear-Site-Data' | 'Content-Disposition' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-Range' | 'Content-Security-Policy' | 'Content-Security-Policy-Report-Only' | 'Content-Type' | 'Cookie' | 'Cross-Origin-Embedder-Policy' | 'Cross-Origin-Opener-Policy' | 'Cross-Origin-Resource-Policy' | 'Date' | 'ETag' | 'Expires' | 'Last-Modified' | 'Location' | 'Permissions-Policy' | 'Pragma' | 'Retry-After' | 'Save-Data' | 'Sec-CH-Prefers-Color-Scheme' | 'Sec-CH-Prefers-Reduced-Motion' | 'Sec-CH-UA' | 'Sec-CH-UA-Arch' | 'Sec-CH-UA-Bitness' | 'Sec-CH-UA-Form-Factor' | 'Sec-CH-UA-Full-Version' | 'Sec-CH-UA-Full-Version-List' | 'Sec-CH-UA-Mobile' | 'Sec-CH-UA-Model' | 'Sec-CH-UA-Platform' | 'Sec-CH-UA-Platform-Version' | 'Sec-CH-UA-WoW64' | 'Sec-Fetch-Dest' | 'Sec-Fetch-Mode' | 'Sec-Fetch-Site' | 'Sec-Fetch-User' | 'Sec-GPC' | 'Server' | 'Server-Timing' | 'Service-Worker-Navigation-Preload' | 'Set-Cookie' | 'Strict-Transport-Security' | 'Timing-Allow-Origin' | 'Trailer' | 'Transfer-Encoding' | 'Upgrade' | 'Vary' | 'WWW-Authenticate' | 'Warning' | 'X-Content-Type-Options' | 'X-DNS-Prefetch-Control' | 'X-Frame-Options' | 'X-Permitted-Cross-Domain-Policies' | 'X-Powered-By' | 'X-Robots-Tag' | 'X-XSS-Protection';
166
+ interface SetHeaders {
167
+ (name: 'Content-Type', value?: BaseMime, options?: SetHeadersOptions): void;
168
+ (name: ResponseHeader, value?: string, options?: SetHeadersOptions): void;
169
+ (name: string, value?: string, options?: SetHeadersOptions): void;
170
+ }
171
+ type ResponseHeadersInit = [string, string][] | Record<'Content-Type', BaseMime> | Record<ResponseHeader, string> | Record<string, string> | Headers;
172
+ interface ResponseInit {
173
+ headers?: ResponseHeadersInit;
174
+ status?: number;
175
+ statusText?: string;
176
+ }
161
177
  export declare const TEXT_PLAIN = "text/plain; charset=UTF-8";
162
178
  export declare class Context<E extends Env = any, P extends string = any, I extends Input = {}> {
163
179
  #private;
@@ -298,9 +314,7 @@ export declare class Context<E extends Env = any, P extends string = any, I exte
298
314
  * })
299
315
  * ```
300
316
  */
301
- header: (name: string, value: string | undefined, options?: {
302
- append?: boolean;
303
- }) => void;
317
+ header: SetHeaders;
304
318
  status: (status: StatusCode) => void;
305
319
  /**
306
320
  * `.set()` can set the value specified by the key.
@@ -3,7 +3,7 @@ import type { Context } from './context';
3
3
  import type { JSX as HonoJSX, IntrinsicElements as IntrinsicElementsDefined } from './intrinsic-elements';
4
4
  export type Props = Record<string, any>;
5
5
  export type FC<P = Props> = {
6
- (props: P): HtmlEscapedString | Promise<HtmlEscapedString>;
6
+ (props: P): HtmlEscapedString | Promise<HtmlEscapedString> | null;
7
7
  defaultProps?: Partial<P> | undefined;
8
8
  displayName?: string | undefined;
9
9
  };
@@ -149,7 +149,7 @@ export declare namespace JSX {
149
149
  contenteditable?: boolean | 'inherit' | undefined;
150
150
  contextmenu?: string | undefined;
151
151
  dir?: string | undefined;
152
- draggable?: 'true' | 'false' | undefined;
152
+ draggable?: 'true' | 'false' | boolean | undefined;
153
153
  enterkeyhint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send' | undefined;
154
154
  hidden?: boolean | undefined;
155
155
  id?: string | undefined;
@@ -381,13 +381,31 @@ export declare namespace JSX {
381
381
  preload?: string | undefined;
382
382
  src?: string | undefined;
383
383
  }
384
+ /**
385
+ * String literal types with auto-completion
386
+ * @see https://github.com/Microsoft/TypeScript/issues/29729
387
+ */
388
+ type LiteralUnion<T> = T | (string & Record<never, never>);
389
+ /**
390
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv
391
+ */
392
+ type MetaHttpEquiv = 'content-security-policy' | 'content-type' | 'default-style' | 'x-ua-compatible' | 'refresh';
393
+ /**
394
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name
395
+ */
396
+ type MetaName = 'application-name' | 'author' | 'description' | 'generator' | 'keywords' | 'referrer' | 'theme-color' | 'color-scheme' | 'viewport' | 'creator' | 'googlebot' | 'publisher' | 'robots';
397
+ /**
398
+ * @see https://ogp.me/
399
+ */
400
+ type MetaProperty = 'og:title' | 'og:type' | 'og:image' | 'og:url' | 'og:audio' | 'og:description' | 'og:determiner' | 'og:locale' | 'og:locale:alternate' | 'og:site_name' | 'og:video' | 'og:image:url' | 'og:image:secure_url' | 'og:image:type' | 'og:image:width' | 'og:image:height' | 'og:image:alt';
384
401
  interface MetaHTMLAttributes extends HTMLAttributes {
385
- charset?: string | undefined;
386
- 'http-equiv'?: string | undefined;
387
- name?: string | undefined;
402
+ charset?: LiteralUnion<'utf-8'> | undefined;
403
+ 'http-equiv'?: LiteralUnion<MetaHttpEquiv> | undefined;
404
+ name?: LiteralUnion<MetaName> | undefined;
388
405
  media?: string | undefined;
389
406
  content?: string | undefined;
390
- httpEquiv?: string | undefined;
407
+ property?: LiteralUnion<MetaProperty> | undefined;
408
+ httpEquiv?: LiteralUnion<MetaHttpEquiv> | undefined;
391
409
  }
392
410
  interface MeterHTMLAttributes extends HTMLAttributes {
393
411
  form?: string | undefined;
@@ -17,4 +17,5 @@ export type ServeStaticOptions<E extends Env = Env> = {
17
17
  export declare const serveStatic: <E extends Env = Env>(options: ServeStaticOptions<E> & {
18
18
  getContent: (path: string, c: Context<E, any, {}>) => Promise<Data | Response | null>;
19
19
  pathResolve?: ((path: string) => string) | undefined;
20
+ isDir?: ((path: string) => boolean | undefined | Promise<boolean | undefined>) | undefined;
20
21
  }) => MiddlewareHandler;
@@ -5,4 +5,8 @@
5
5
  export declare const getMimeType: (filename: string, mimes?: Record<string, string>) => string | undefined;
6
6
  export declare const getExtension: (mimeType: string) => string | undefined;
7
7
  export { baseMimes as mimes };
8
- declare const baseMimes: Record<string, string>;
8
+ /**
9
+ * Union types for BaseMime
10
+ */
11
+ export type BaseMime = 'audio/aac' | 'video/x-msvideo' | 'image/avif' | 'video/av1' | 'application/octet-stream' | 'image/bmp' | 'text/css' | 'text/csv' | 'application/vnd.ms-fontobject' | 'application/epub+zip' | 'image/gif' | 'application/gzip' | 'text/html' | 'image/x-icon' | 'text/calendar' | 'image/jpeg' | 'text/javascript' | 'application/json' | 'application/ld+json' | 'audio/x-midi' | 'audio/mpeg' | 'video/mp4' | 'video/mpeg' | 'audio/ogg' | 'video/ogg' | 'application/ogg' | 'audio/opus' | 'font/otf' | 'application/pdf' | 'image/png' | 'application/rtf' | 'image/svg+xml' | 'image/tiff' | 'video/mp2t' | 'font/ttf' | 'text/plain' | 'application/wasm' | 'video/webm' | 'audio/webm' | 'image/webp' | 'font/woff' | 'font/woff2' | 'application/xhtml+xml' | 'application/xml' | 'application/zip' | 'video/3gpp' | 'video/3gpp2' | 'model/gltf+json' | 'model/gltf-binary';
12
+ declare const baseMimes: Record<string, BaseMime>;
@@ -14,13 +14,14 @@ var escapeRe = /[&<>'"]/;
14
14
  var stringBufferToString = async (buffer, callbacks) => {
15
15
  let str = "";
16
16
  callbacks ||= [];
17
- for (let i = buffer.length - 1; ; i--) {
18
- str += buffer[i];
17
+ const resolvedBuffer = await Promise.all(buffer);
18
+ for (let i = resolvedBuffer.length - 1; ; i--) {
19
+ str += resolvedBuffer[i];
19
20
  i--;
20
21
  if (i < 0) {
21
22
  break;
22
23
  }
23
- let r = await buffer[i];
24
+ let r = resolvedBuffer[i];
24
25
  if (typeof r === "object") {
25
26
  callbacks.push(...r.callbacks || []);
26
27
  }
@@ -3,7 +3,7 @@ import { getCookie } from "../helper/cookie/index.js";
3
3
  import { HTTPException } from "../http-exception.js";
4
4
  import { bufferToFormData } from "../utils/buffer.js";
5
5
  var jsonRegex = /^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/;
6
- var multipartRegex = /^multipart\/form-data(; boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/;
6
+ var multipartRegex = /^multipart\/form-data(;\s?boundary=[a-zA-Z0-9'"()+_,\-./:=?]+)?$/;
7
7
  var urlencodedRegex = /^application\/x-www-form-urlencoded$/;
8
8
  var validator = (target, validationFunc) => {
9
9
  return async (c, next) => {
@@ -42,12 +42,13 @@ var validator = (target, validationFunc) => {
42
42
  const form = {};
43
43
  formData.forEach((value2, key) => {
44
44
  if (key.endsWith("[]")) {
45
- if (form[key] === void 0) {
46
- form[key] = [value2];
47
- } else if (Array.isArray(form[key])) {
48
- ;
49
- form[key].push(value2);
50
- }
45
+ ;
46
+ (form[key] ??= []).push(value2);
47
+ } else if (Array.isArray(form[key])) {
48
+ ;
49
+ form[key].push(value2);
50
+ } else if (key in form) {
51
+ form[key] = [form[key], value2];
51
52
  } else {
52
53
  form[key] = value2;
53
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.5.4",
3
+ "version": "4.5.6",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",