hono 4.12.18 → 4.12.26

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.
Files changed (65) hide show
  1. package/dist/adapter/aws-lambda/handler.js +5 -3
  2. package/dist/adapter/bun/serve-static.js +1 -1
  3. package/dist/adapter/cloudflare-workers/serve-static.js +1 -1
  4. package/dist/adapter/deno/serve-static.js +1 -1
  5. package/dist/adapter/deno/websocket.js +5 -1
  6. package/dist/adapter/lambda-edge/handler.js +8 -2
  7. package/dist/cjs/adapter/aws-lambda/handler.js +5 -3
  8. package/dist/cjs/adapter/bun/serve-static.js +1 -1
  9. package/dist/cjs/adapter/cloudflare-workers/serve-static.js +1 -1
  10. package/dist/cjs/adapter/deno/serve-static.js +1 -1
  11. package/dist/cjs/adapter/deno/websocket.js +5 -1
  12. package/dist/cjs/adapter/lambda-edge/handler.js +8 -2
  13. package/dist/cjs/hono-base.js +9 -4
  14. package/dist/cjs/index.js +3 -0
  15. package/dist/cjs/middleware/bearer-auth/index.js +1 -1
  16. package/dist/cjs/middleware/cache/index.js +30 -25
  17. package/dist/cjs/middleware/compress/index.js +31 -5
  18. package/dist/cjs/middleware/cors/index.js +2 -5
  19. package/dist/cjs/middleware/ip-restriction/index.js +58 -28
  20. package/dist/cjs/middleware/jwk/jwk.js +1 -1
  21. package/dist/cjs/middleware/jwt/jwt.js +1 -1
  22. package/dist/cjs/middleware/language/language.js +10 -32
  23. package/dist/cjs/middleware/serve-static/index.js +1 -1
  24. package/dist/cjs/middleware/timing/timing.js +3 -1
  25. package/dist/cjs/request.js +15 -0
  26. package/dist/cjs/utils/compress.js +1 -1
  27. package/dist/cjs/utils/cookie.js +4 -4
  28. package/dist/cjs/utils/filepath.js +1 -1
  29. package/dist/cjs/utils/ipaddr.js +193 -10
  30. package/dist/cjs/utils/mime.js +15 -17
  31. package/dist/cjs/utils/stream.js +4 -2
  32. package/dist/hono-base.js +9 -4
  33. package/dist/index.js +2 -0
  34. package/dist/middleware/bearer-auth/index.js +1 -1
  35. package/dist/middleware/cache/index.js +30 -25
  36. package/dist/middleware/compress/index.js +30 -5
  37. package/dist/middleware/cors/index.js +2 -5
  38. package/dist/middleware/ip-restriction/index.js +60 -29
  39. package/dist/middleware/jwk/jwk.js +1 -1
  40. package/dist/middleware/jwt/jwt.js +1 -1
  41. package/dist/middleware/language/language.js +10 -32
  42. package/dist/middleware/serve-static/index.js +1 -1
  43. package/dist/middleware/timing/timing.js +3 -1
  44. package/dist/request.js +15 -0
  45. package/dist/tsconfig.build.tsbuildinfo +1 -1
  46. package/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
  47. package/dist/types/adapter/bun/serve-static.d.ts +1 -1
  48. package/dist/types/adapter/cloudflare-workers/serve-static.d.ts +2 -2
  49. package/dist/types/adapter/deno/serve-static.d.ts +1 -1
  50. package/dist/types/index.d.ts +2 -1
  51. package/dist/types/jsx/base.d.ts +2 -2
  52. package/dist/types/jsx/index.d.ts +1 -1
  53. package/dist/types/middleware/bearer-auth/index.d.ts +6 -5
  54. package/dist/types/middleware/cache/index.d.ts +1 -1
  55. package/dist/types/middleware/compress/index.d.ts +13 -2
  56. package/dist/types/request.d.ts +13 -0
  57. package/dist/types/utils/ipaddr.d.ts +4 -0
  58. package/dist/types/utils/mime.d.ts +11 -11
  59. package/dist/utils/compress.js +1 -1
  60. package/dist/utils/cookie.js +4 -4
  61. package/dist/utils/filepath.js +1 -1
  62. package/dist/utils/ipaddr.js +192 -10
  63. package/dist/utils/mime.js +15 -17
  64. package/dist/utils/stream.js +4 -2
  65. package/package.json +22 -16
@@ -14,7 +14,7 @@ import type { StatusCode } from '../../utils/http-status';
14
14
  * @param {string | Function} options.cacheName - The name of the cache. Can be used to store multiple caches with different identifiers.
15
15
  * @param {boolean} [options.wait=false] - A boolean indicating if Hono should wait for the Promise of the `cache.put` function to resolve before continuing with the request. Required to be true for the Deno environment.
16
16
  * @param {string} [options.cacheControl] - A string of directives for the `Cache-Control` header.
17
- * @param {string | string[]} [options.vary] - Sets the `Vary` header in the response. If the original response header already contains a `Vary` header, the values are merged, removing any duplicates.
17
+ * @param {string | string[]} [options.vary] - Adds the configured request headers to the cache key variants and sets the `Vary` header in the response. If the original response header already contains a `Vary` header, the values are merged, removing any duplicates.
18
18
  * @param {Function} [options.keyGenerator] - Generates keys for every request in the `cacheName` store. This can be used to cache data based on request parameters or context parameters.
19
19
  * @param {number[]} [options.cacheableStatusCodes=[200]] - An array of status codes that can be cached.
20
20
  * @param {Function | false} [options.onCacheNotAvailable] - A callback invoked when `globalThis.caches` is not available. By default, a message is logged to the console. Set to `false` to suppress the log, or provide a custom function.
@@ -3,10 +3,15 @@
3
3
  * Compress Middleware for Hono.
4
4
  */
5
5
  import type { MiddlewareHandler } from '../../types';
6
+ import { COMPRESSIBLE_CONTENT_TYPE_REGEX } from '../../utils/compress';
7
+ export { COMPRESSIBLE_CONTENT_TYPE_REGEX };
6
8
  declare const ENCODING_TYPES: readonly ["gzip", "deflate"];
9
+ type Encoding = (typeof ENCODING_TYPES)[number];
10
+ type ContentTypeFilter = RegExp | ((contentType: string) => boolean);
7
11
  interface CompressionOptions {
8
- encoding?: (typeof ENCODING_TYPES)[number];
12
+ encoding?: Encoding;
9
13
  threshold?: number;
14
+ contentTypeFilter?: ContentTypeFilter;
10
15
  }
11
16
  /**
12
17
  * Compress Middleware for Hono.
@@ -16,6 +21,7 @@ interface CompressionOptions {
16
21
  * @param {CompressionOptions} [options] - The options for the compress middleware.
17
22
  * @param {'gzip' | 'deflate'} [options.encoding] - The compression scheme to allow for response compression. Either 'gzip' or 'deflate'. If not defined, both are allowed and will be used based on the Accept-Encoding header. 'gzip' is prioritized if this option is not provided and the client provides both in the Accept-Encoding header.
18
23
  * @param {number} [options.threshold=1024] - The minimum size in bytes to compress. Defaults to 1024 bytes.
24
+ * @param {RegExp | Function} [options.contentTypeFilter=COMPRESSIBLE_CONTENT_TYPE_REGEX] - A RegExp or function to determine if the response Content-Type should be compressed.
19
25
  * @returns {MiddlewareHandler} The middleware handler function.
20
26
  *
21
27
  * @example
@@ -23,7 +29,12 @@ interface CompressionOptions {
23
29
  * const app = new Hono()
24
30
  *
25
31
  * app.use(compress())
32
+ *
33
+ * // Compress only JSON responses
34
+ * app.use(compress({ contentTypeFilter: /^application\/json/ }))
35
+ *
36
+ * // Compress based on custom Content-Type logic
37
+ * app.use(compress({ contentTypeFilter: (type) => COMPRESSIBLE_CONTENT_TYPE_REGEX.test(type) || type === "application/x-myformat" }))
26
38
  * ```
27
39
  */
28
40
  export declare const compress: (options?: CompressionOptions) => MiddlewareHandler;
29
- export {};
@@ -164,6 +164,19 @@ export declare class HonoRequest<P extends string = '/', I extends Input['out']
164
164
  * ```
165
165
  */
166
166
  arrayBuffer(): Promise<ArrayBuffer>;
167
+ /**
168
+ * `.bytes()` parses the request body as a `Uint8Array`.
169
+ *
170
+ * @see {@link https://hono.dev/docs/api/request#bytes}
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * app.post('/entry', async (c) => {
175
+ * const body = await c.req.bytes()
176
+ * })
177
+ * ```
178
+ */
179
+ bytes(): Promise<Uint8Array>;
167
180
  /**
168
181
  * Parses the request body as a `Blob`.
169
182
  * @example
@@ -9,6 +9,10 @@ import type { AddressType } from '../helper/conninfo';
9
9
  * @return expanded IPv6 Address
10
10
  */
11
11
  export declare const expandIPv6: (ipV6: string) => string;
12
+ export declare const INVALID_IP_ADDRESS_ERROR_CODE = "ERR_INVALID_IP_ADDRESS";
13
+ export type InvalidIPAddressError = TypeError & {
14
+ code: typeof INVALID_IP_ADDRESS_ERROR_CODE;
15
+ };
12
16
  /**
13
17
  * Distinct Remote Addr
14
18
  * @param remoteAddr Remote Addr
@@ -16,25 +16,25 @@ declare const _baseMimes: {
16
16
  readonly av1: "video/av1";
17
17
  readonly bin: "application/octet-stream";
18
18
  readonly bmp: "image/bmp";
19
- readonly css: "text/css";
20
- readonly csv: "text/csv";
19
+ readonly css: "text/css; charset=utf-8";
20
+ readonly csv: "text/csv; charset=utf-8";
21
21
  readonly eot: "application/vnd.ms-fontobject";
22
22
  readonly epub: "application/epub+zip";
23
23
  readonly gif: "image/gif";
24
24
  readonly gz: "application/gzip";
25
- readonly htm: "text/html";
26
- readonly html: "text/html";
25
+ readonly htm: "text/html; charset=utf-8";
26
+ readonly html: "text/html; charset=utf-8";
27
27
  readonly ico: "image/x-icon";
28
- readonly ics: "text/calendar";
28
+ readonly ics: "text/calendar; charset=utf-8";
29
29
  readonly jpeg: "image/jpeg";
30
30
  readonly jpg: "image/jpeg";
31
- readonly js: "text/javascript";
31
+ readonly js: "text/javascript; charset=utf-8";
32
32
  readonly json: "application/json";
33
33
  readonly jsonld: "application/ld+json";
34
34
  readonly map: "application/json";
35
35
  readonly mid: "audio/x-midi";
36
36
  readonly midi: "audio/x-midi";
37
- readonly mjs: "text/javascript";
37
+ readonly mjs: "text/javascript; charset=utf-8";
38
38
  readonly mp3: "audio/mpeg";
39
39
  readonly mp4: "video/mp4";
40
40
  readonly mpeg: "video/mpeg";
@@ -46,12 +46,12 @@ declare const _baseMimes: {
46
46
  readonly pdf: "application/pdf";
47
47
  readonly png: "image/png";
48
48
  readonly rtf: "application/rtf";
49
- readonly svg: "image/svg+xml";
49
+ readonly svg: "image/svg+xml; charset=utf-8";
50
50
  readonly tif: "image/tiff";
51
51
  readonly tiff: "image/tiff";
52
52
  readonly ts: "video/mp2t";
53
53
  readonly ttf: "font/ttf";
54
- readonly txt: "text/plain";
54
+ readonly txt: "text/plain; charset=utf-8";
55
55
  readonly wasm: "application/wasm";
56
56
  readonly webm: "video/webm";
57
57
  readonly weba: "audio/webm";
@@ -59,8 +59,8 @@ declare const _baseMimes: {
59
59
  readonly webp: "image/webp";
60
60
  readonly woff: "font/woff";
61
61
  readonly woff2: "font/woff2";
62
- readonly xhtml: "application/xhtml+xml";
63
- readonly xml: "application/xml";
62
+ readonly xhtml: "application/xhtml+xml; charset=utf-8";
63
+ readonly xml: "application/xml; charset=utf-8";
64
64
  readonly zip: "application/zip";
65
65
  readonly '3gp': "video/3gpp";
66
66
  readonly '3g2': "video/3gpp2";
@@ -1,5 +1,5 @@
1
1
  // src/utils/compress.ts
2
- var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
2
+ var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|msgpack|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|vnd\.msgpack|wasm|x-httpd-php|x-javascript|x-msgpack|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml|msgpack))(?:[;\s]|$)/i;
3
3
  export {
4
4
  COMPRESSIBLE_CONTENT_TYPE_REGEX
5
5
  };
@@ -48,14 +48,14 @@ var parse = (cookie, name) => {
48
48
  return {};
49
49
  }
50
50
  const pairs = cookie.split(";");
51
- const parsedCookie = {};
51
+ const parsedCookie = /* @__PURE__ */ Object.create(null);
52
52
  for (const pairStr of pairs) {
53
53
  const valueStartPos = pairStr.indexOf("=");
54
54
  if (valueStartPos === -1) {
55
55
  continue;
56
56
  }
57
57
  const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));
58
- if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName)) {
58
+ if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) {
59
59
  continue;
60
60
  }
61
61
  let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));
@@ -72,7 +72,7 @@ var parse = (cookie, name) => {
72
72
  return parsedCookie;
73
73
  };
74
74
  var parseSigned = async (cookie, secret, name) => {
75
- const parsedCookie = {};
75
+ const parsedCookie = /* @__PURE__ */ Object.create(null);
76
76
  const secretKey = await getCryptoKey(secret);
77
77
  for (const [key, value] of Object.entries(parse(cookie, name))) {
78
78
  const signatureStartPos = value.lastIndexOf(".");
@@ -108,7 +108,7 @@ var _serialize = (name, value, opt = {}) => {
108
108
  throw new Error("__Host- Cookie must not have Domain attributes");
109
109
  }
110
110
  }
111
- for (const key of ["domain", "path"]) {
111
+ for (const key of ["domain", "path", "sameSite", "priority"]) {
112
112
  if (opt[key] && /[;\r\n]/.test(opt[key])) {
113
113
  throw new Error(`${key} must not contain ";", "\\r", or "\\n"`);
114
114
  }
@@ -20,7 +20,7 @@ var getFilePathWithoutDefaultDocument = (options) => {
20
20
  return;
21
21
  }
22
22
  filename = filename.replace(/^\.?[\/\\]/, "");
23
- filename = filename.replace(/\\/, "/");
23
+ filename = filename.replace(/\\/g, "/");
24
24
  root = root.replace(/\/$/, "");
25
25
  let path = root ? root + "/" + filename : filename;
26
26
  path = path.replace(/^\.?\//, "");
@@ -14,7 +14,9 @@ var expandIPv6 = (ipV6) => {
14
14
  if (node !== "") {
15
15
  sections[i] = node.padStart(4, "0");
16
16
  } else {
17
- sections[i + 1] === "" && sections.splice(i + 1, 1);
17
+ while (sections[i + 1] === "") {
18
+ sections.splice(i + 1, 1);
19
+ }
18
20
  sections[i] = new Array(8 - sections.length + 1).fill("0000").join(":");
19
21
  }
20
22
  }
@@ -22,6 +24,16 @@ var expandIPv6 = (ipV6) => {
22
24
  };
23
25
  var IPV4_OCTET_PART = "(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])";
24
26
  var IPV4_REGEX = new RegExp(`^(?:${IPV4_OCTET_PART}\\.){3}${IPV4_OCTET_PART}$`);
27
+ var INVALID_IP_ADDRESS_ERROR_CODE = "ERR_INVALID_IP_ADDRESS";
28
+ var CHAR_CODE_0 = 48;
29
+ var CHAR_CODE_9 = 57;
30
+ var CHAR_CODE_A = 65;
31
+ var CHAR_CODE_F = 70;
32
+ var CHAR_CODE_a = 97;
33
+ var CHAR_CODE_f = 102;
34
+ var CHAR_CODE_DOT = 46;
35
+ var CHAR_CODE_COLON = 58;
36
+ var CHAR_CODE_PERCENT = 37;
25
37
  var distinctRemoteAddr = (remoteAddr) => {
26
38
  if (IPV4_REGEX.test(remoteAddr)) {
27
39
  return "IPv4";
@@ -30,21 +42,187 @@ var distinctRemoteAddr = (remoteAddr) => {
30
42
  return "IPv6";
31
43
  }
32
44
  };
33
- var convertIPv4ToBinary = (ipv4) => {
34
- const parts = ipv4.split(".");
45
+ var createInvalidIPAddressError = (message) => {
46
+ const error = new TypeError(message);
47
+ error.code = INVALID_IP_ADDRESS_ERROR_CODE;
48
+ return error;
49
+ };
50
+ var throwInvalidIPv4Address = (ipv4) => {
51
+ throw createInvalidIPAddressError(`Invalid IPv4 address: ${ipv4}`);
52
+ };
53
+ var throwInvalidIPv6Address = (ipv6) => {
54
+ throw createInvalidIPAddressError(`Invalid IPv6 address: ${ipv6}`);
55
+ };
56
+ var parseIPv4ToBinary = (ipv4, start, end, onInvalid) => {
35
57
  let result = 0n;
36
- for (let i = 0; i < 4; i++) {
37
- result <<= 8n;
38
- result += BigInt(parts[i]);
58
+ let octets = 0;
59
+ let octet = 0;
60
+ let digits = 0;
61
+ let firstDigit = 0;
62
+ for (let i = start; i <= end; i++) {
63
+ const code = i < end ? ipv4.charCodeAt(i) : CHAR_CODE_DOT;
64
+ if (code >= CHAR_CODE_0 && code <= CHAR_CODE_9) {
65
+ if (digits === 0) {
66
+ firstDigit = code;
67
+ } else if (firstDigit === CHAR_CODE_0) {
68
+ onInvalid();
69
+ }
70
+ octet = octet * 10 + code - CHAR_CODE_0;
71
+ if (octet > 255) {
72
+ onInvalid();
73
+ }
74
+ digits++;
75
+ continue;
76
+ }
77
+ if (code !== CHAR_CODE_DOT || digits === 0 || octets === 4) {
78
+ onInvalid();
79
+ }
80
+ result = (result << 8n) + BigInt(octet);
81
+ octets++;
82
+ octet = 0;
83
+ digits = 0;
84
+ }
85
+ if (octets !== 4) {
86
+ onInvalid();
39
87
  }
40
88
  return result;
41
89
  };
90
+ var parseIPv6HexCode = (code) => {
91
+ if (code >= CHAR_CODE_0 && code <= CHAR_CODE_9) {
92
+ return code - CHAR_CODE_0;
93
+ }
94
+ if (code >= CHAR_CODE_A && code <= CHAR_CODE_F) {
95
+ return code - CHAR_CODE_A + 10;
96
+ }
97
+ if (code >= CHAR_CODE_a && code <= CHAR_CODE_f) {
98
+ return code - CHAR_CODE_a + 10;
99
+ }
100
+ return -1;
101
+ };
102
+ var isIPv6LinkLocal = (ipv6binary) => ipv6binary >> 118n === 0x3fan;
103
+ var convertIPv4ToBinary = (ipv4) => {
104
+ return parseIPv4ToBinary(ipv4, 0, ipv4.length, () => throwInvalidIPv4Address(ipv4));
105
+ };
42
106
  var convertIPv6ToBinary = (ipv6) => {
43
- const sections = expandIPv6(ipv6).split(":");
107
+ const length = ipv6.length;
108
+ const sections = [];
109
+ let hasZoneId = false;
110
+ let compressAt = -1;
111
+ let index = 0;
112
+ if (length === 0) {
113
+ throwInvalidIPv6Address(ipv6);
114
+ }
115
+ while (index < length) {
116
+ if (sections.length > 8) {
117
+ throwInvalidIPv6Address(ipv6);
118
+ }
119
+ let code = ipv6.charCodeAt(index);
120
+ if (code === CHAR_CODE_PERCENT) {
121
+ if (index + 1 === length) {
122
+ throwInvalidIPv6Address(ipv6);
123
+ }
124
+ hasZoneId = true;
125
+ break;
126
+ }
127
+ if (code === CHAR_CODE_COLON) {
128
+ if (index + 1 < length && ipv6.charCodeAt(index + 1) === CHAR_CODE_COLON) {
129
+ if (compressAt !== -1) {
130
+ throwInvalidIPv6Address(ipv6);
131
+ }
132
+ compressAt = sections.length;
133
+ index += 2;
134
+ continue;
135
+ }
136
+ throwInvalidIPv6Address(ipv6);
137
+ }
138
+ let value = 0;
139
+ let digits = 0;
140
+ const sectionStart = index;
141
+ while (index < length) {
142
+ code = ipv6.charCodeAt(index);
143
+ const hex = parseIPv6HexCode(code);
144
+ if (hex === -1) {
145
+ break;
146
+ }
147
+ if (digits === 4) {
148
+ throwInvalidIPv6Address(ipv6);
149
+ }
150
+ value = value << 4 | hex;
151
+ digits++;
152
+ index++;
153
+ }
154
+ if (index < length && ipv6.charCodeAt(index) === CHAR_CODE_DOT) {
155
+ let ipv4End = length;
156
+ for (let i = index; i < length; i++) {
157
+ if (ipv6.charCodeAt(i) === CHAR_CODE_PERCENT) {
158
+ if (i + 1 === length) {
159
+ throwInvalidIPv6Address(ipv6);
160
+ }
161
+ hasZoneId = true;
162
+ ipv4End = i;
163
+ break;
164
+ }
165
+ }
166
+ const ipv4 = parseIPv4ToBinary(
167
+ ipv6,
168
+ sectionStart,
169
+ ipv4End,
170
+ () => throwInvalidIPv6Address(ipv6)
171
+ );
172
+ sections.push(Number(ipv4 >> 16n & 0xffffn), Number(ipv4 & 0xffffn));
173
+ index = length;
174
+ break;
175
+ }
176
+ if (digits === 0) {
177
+ throwInvalidIPv6Address(ipv6);
178
+ }
179
+ sections.push(value);
180
+ if (index === length) {
181
+ break;
182
+ }
183
+ code = ipv6.charCodeAt(index);
184
+ if (code === CHAR_CODE_PERCENT) {
185
+ if (index + 1 === length) {
186
+ throwInvalidIPv6Address(ipv6);
187
+ }
188
+ hasZoneId = true;
189
+ break;
190
+ }
191
+ if (code !== CHAR_CODE_COLON) {
192
+ throwInvalidIPv6Address(ipv6);
193
+ }
194
+ if (index + 1 < length && ipv6.charCodeAt(index + 1) === CHAR_CODE_COLON) {
195
+ if (compressAt !== -1) {
196
+ throwInvalidIPv6Address(ipv6);
197
+ }
198
+ compressAt = sections.length;
199
+ index += 2;
200
+ continue;
201
+ }
202
+ index++;
203
+ if (index === length) {
204
+ throwInvalidIPv6Address(ipv6);
205
+ }
206
+ }
207
+ if (compressAt === -1 ? sections.length !== 8 : sections.length >= 8) {
208
+ throwInvalidIPv6Address(ipv6);
209
+ }
44
210
  let result = 0n;
45
- for (let i = 0; i < 8; i++) {
211
+ const zeros = compressAt === -1 ? 0 : 8 - sections.length;
212
+ const firstSectionEnd = compressAt === -1 ? sections.length : compressAt;
213
+ for (let i = 0; i < firstSectionEnd; i++) {
46
214
  result <<= 16n;
47
- result += BigInt(parseInt(sections[i], 16));
215
+ result += BigInt(sections[i]);
216
+ }
217
+ for (let i = 0; i < zeros; i++) {
218
+ result <<= 16n;
219
+ }
220
+ for (let i = firstSectionEnd; i < sections.length; i++) {
221
+ result <<= 16n;
222
+ result += BigInt(sections[i]);
223
+ }
224
+ if (hasZoneId && !isIPv6LinkLocal(result)) {
225
+ throwInvalidIPv6Address(ipv6);
48
226
  }
49
227
  return result;
50
228
  };
@@ -58,6 +236,9 @@ var convertIPv4BinaryToString = (ipV4) => {
58
236
  var isIPv4MappedIPv6 = (ipv6binary) => ipv6binary >> 32n === 0xffffn;
59
237
  var convertIPv4MappedIPv6ToIPv4 = (ipv6binary) => ipv6binary & 0xffffffffn;
60
238
  var convertIPv6BinaryToString = (ipV6) => {
239
+ if (ipV6 === 0n) {
240
+ return "::";
241
+ }
61
242
  if (isIPv4MappedIPv6(ipV6)) {
62
243
  return `::ffff:${convertIPv4BinaryToString(convertIPv4MappedIPv6ToIPv4(ipV6))}`;
63
244
  }
@@ -89,12 +270,13 @@ var convertIPv6BinaryToString = (ipV6) => {
89
270
  maxZeroEnd = 8;
90
271
  }
91
272
  }
92
- if (maxZeroStart !== -1) {
273
+ if (maxZeroStart !== -1 && maxZeroEnd - maxZeroStart > 1) {
93
274
  sections.splice(maxZeroStart, maxZeroEnd - maxZeroStart, ":");
94
275
  }
95
276
  return sections.join(":").replace(/:{2,}/g, "::");
96
277
  };
97
278
  export {
279
+ INVALID_IP_ADDRESS_ERROR_CODE,
98
280
  convertIPv4BinaryToString,
99
281
  convertIPv4MappedIPv6ToIPv4,
100
282
  convertIPv4ToBinary,
@@ -5,15 +5,13 @@ var getMimeType = (filename, mimes = baseMimes) => {
5
5
  if (!match) {
6
6
  return;
7
7
  }
8
- let mimeType = mimes[match[1].toLowerCase()];
9
- if (mimeType && mimeType.startsWith("text")) {
10
- mimeType += "; charset=utf-8";
11
- }
12
- return mimeType;
8
+ return mimes[match[1].toLowerCase()];
13
9
  };
14
10
  var getExtension = (mimeType) => {
11
+ const baseType = mimeType.split(";", 1)[0].trim();
15
12
  for (const ext in baseMimes) {
16
- if (baseMimes[ext] === mimeType) {
13
+ const stored = baseMimes[ext];
14
+ if (stored === mimeType || stored.split(";", 1)[0].trim() === baseType) {
17
15
  return ext;
18
16
  }
19
17
  }
@@ -25,25 +23,25 @@ var _baseMimes = {
25
23
  av1: "video/av1",
26
24
  bin: "application/octet-stream",
27
25
  bmp: "image/bmp",
28
- css: "text/css",
29
- csv: "text/csv",
26
+ css: "text/css; charset=utf-8",
27
+ csv: "text/csv; charset=utf-8",
30
28
  eot: "application/vnd.ms-fontobject",
31
29
  epub: "application/epub+zip",
32
30
  gif: "image/gif",
33
31
  gz: "application/gzip",
34
- htm: "text/html",
35
- html: "text/html",
32
+ htm: "text/html; charset=utf-8",
33
+ html: "text/html; charset=utf-8",
36
34
  ico: "image/x-icon",
37
- ics: "text/calendar",
35
+ ics: "text/calendar; charset=utf-8",
38
36
  jpeg: "image/jpeg",
39
37
  jpg: "image/jpeg",
40
- js: "text/javascript",
38
+ js: "text/javascript; charset=utf-8",
41
39
  json: "application/json",
42
40
  jsonld: "application/ld+json",
43
41
  map: "application/json",
44
42
  mid: "audio/x-midi",
45
43
  midi: "audio/x-midi",
46
- mjs: "text/javascript",
44
+ mjs: "text/javascript; charset=utf-8",
47
45
  mp3: "audio/mpeg",
48
46
  mp4: "video/mp4",
49
47
  mpeg: "video/mpeg",
@@ -55,12 +53,12 @@ var _baseMimes = {
55
53
  pdf: "application/pdf",
56
54
  png: "image/png",
57
55
  rtf: "application/rtf",
58
- svg: "image/svg+xml",
56
+ svg: "image/svg+xml; charset=utf-8",
59
57
  tif: "image/tiff",
60
58
  tiff: "image/tiff",
61
59
  ts: "video/mp2t",
62
60
  ttf: "font/ttf",
63
- txt: "text/plain",
61
+ txt: "text/plain; charset=utf-8",
64
62
  wasm: "application/wasm",
65
63
  webm: "video/webm",
66
64
  weba: "audio/webm",
@@ -68,8 +66,8 @@ var _baseMimes = {
68
66
  webp: "image/webp",
69
67
  woff: "font/woff",
70
68
  woff2: "font/woff2",
71
- xhtml: "application/xhtml+xml",
72
- xml: "application/xml",
69
+ xhtml: "application/xhtml+xml; charset=utf-8",
70
+ xml: "application/xml; charset=utf-8",
73
71
  zip: "application/zip",
74
72
  "3gp": "video/3gpp",
75
73
  "3g2": "video/3gpp2",
@@ -27,7 +27,9 @@ var StreamingApi = class {
27
27
  done ? controller.close() : controller.enqueue(value);
28
28
  },
29
29
  cancel: () => {
30
- this.abort();
30
+ if (!this.closed) {
31
+ this.abort();
32
+ }
31
33
  }
32
34
  });
33
35
  }
@@ -49,11 +51,11 @@ var StreamingApi = class {
49
51
  return new Promise((res) => setTimeout(res, ms));
50
52
  }
51
53
  async close() {
54
+ this.closed = true;
52
55
  try {
53
56
  await this.writer.close();
54
57
  } catch {
55
58
  }
56
- this.closed = true;
57
59
  }
58
60
  async pipe(body) {
59
61
  this.writer.releaseLock();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.12.18",
3
+ "version": "4.12.26",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "watch": "bun run --shell bun remove-dist && bun ./build/build.ts --watch && bun run copy:package.cjs.json",
32
32
  "coverage": "vitest --run --coverage",
33
33
  "prerelease": "bun test:deno && bun run build",
34
- "release": "np",
34
+ "release": "np --no-publish",
35
35
  "remove-dist": "rm -rf dist"
36
36
  },
37
37
  "exports": {
@@ -506,9 +506,18 @@
506
506
  "jsx/dom/server": [
507
507
  "./dist/types/jsx/dom/server.d.ts"
508
508
  ],
509
+ "jsx/dom/jsx-dev-runtime": [
510
+ "./dist/types/jsx/dom/jsx-dev-runtime.d.ts"
511
+ ],
512
+ "jsx/dom/jsx-runtime": [
513
+ "./dist/types/jsx/dom/jsx-runtime.d.ts"
514
+ ],
509
515
  "jwt": [
510
516
  "./dist/types/middleware/jwt"
511
517
  ],
518
+ "jwk": [
519
+ "./dist/types/middleware/jwk"
520
+ ],
512
521
  "timeout": [
513
522
  "./dist/types/middleware/timeout"
514
523
  ],
@@ -552,19 +561,19 @@
552
561
  "./dist/types/router.d.ts"
553
562
  ],
554
563
  "router/reg-exp-router": [
555
- "./dist/types/router/reg-exp-router/router.d.ts"
564
+ "./dist/types/router/reg-exp-router"
556
565
  ],
557
566
  "router/smart-router": [
558
- "./dist/types/router/smart-router/router.d.ts"
567
+ "./dist/types/router/smart-router"
559
568
  ],
560
569
  "router/trie-router": [
561
- "./dist/types/router/trie-router/router.d.ts"
570
+ "./dist/types/router/trie-router"
562
571
  ],
563
572
  "router/pattern-router": [
564
- "./dist/types/router/pattern-router/router.d.ts"
573
+ "./dist/types/router/pattern-router"
565
574
  ],
566
575
  "router/linear-router": [
567
- "./dist/types/router/linear-router/router.d.ts"
576
+ "./dist/types/router/linear-router"
568
577
  ],
569
578
  "utils/jwt": [
570
579
  "./dist/types/utils/jwt/index.d.ts"
@@ -596,15 +605,15 @@
596
605
  "bun": [
597
606
  "./dist/types/adapter/bun"
598
607
  ],
599
- "nextjs": [
600
- "./dist/types/adapter/nextjs"
601
- ],
602
608
  "aws-lambda": [
603
609
  "./dist/types/adapter/aws-lambda"
604
610
  ],
605
611
  "vercel": [
606
612
  "./dist/types/adapter/vercel"
607
613
  ],
614
+ "netlify": [
615
+ "./dist/types/adapter/netlify"
616
+ ],
608
617
  "lambda-edge": [
609
618
  "./dist/types/adapter/lambda-edge"
610
619
  ],
@@ -657,19 +666,16 @@
657
666
  ],
658
667
  "devDependencies": {
659
668
  "@hono/eslint-config": "^2.1.0",
660
- "@hono/node-server": "^1.13.5",
661
- "@types/glob": "^9.0.0",
669
+ "@hono/node-server": "^2.0.2",
662
670
  "@types/jsdom": "^21.1.7",
663
671
  "@types/node": "^24.3.0",
664
672
  "@types/ws": "^8.18.1",
665
673
  "@typescript/native-preview": "7.0.0-dev.20260210.1",
666
- "@vitest/coverage-v8": "^3.2.4",
667
- "arg": "^5.0.2",
674
+ "@vitest/coverage-v8": "^4.1.7",
668
675
  "bun-types": "^1.2.20",
669
676
  "editorconfig-checker": "6.1.1",
670
677
  "esbuild": "^0.27.1",
671
678
  "eslint": "^9.39.3",
672
- "glob": "^11.0.0",
673
679
  "jsdom": "22.1.0",
674
680
  "msw": "^2.6.0",
675
681
  "np": "10.2.0",
@@ -680,7 +686,7 @@
680
686
  "typescript": "^5.9.2",
681
687
  "undici": "^6.21.3",
682
688
  "vite-plugin-fastly-js-compute": "^0.4.2",
683
- "vitest": "^3.2.4",
689
+ "vitest": "^4.1.7",
684
690
  "wrangler": "4.12.0",
685
691
  "ws": "^8.18.0",
686
692
  "zod": "^3.23.8"