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
@@ -83,20 +83,12 @@ const normalizeLanguage = (lang, options) => {
83
83
  }
84
84
  };
85
85
  const detectFromQuery = (c, options) => {
86
- try {
87
- const query = c.req.query(options.lookupQueryString);
88
- return normalizeLanguage(query, options);
89
- } catch {
90
- return void 0;
91
- }
86
+ const query = c.req.query(options.lookupQueryString);
87
+ return normalizeLanguage(query, options);
92
88
  };
93
89
  const detectFromCookie = (c, options) => {
94
- try {
95
- const cookie = (0, import_cookie.getCookie)(c, options.lookupCookie);
96
- return normalizeLanguage(cookie, options);
97
- } catch {
98
- return void 0;
99
- }
90
+ const cookie = (0, import_cookie.getCookie)(c, options.lookupCookie);
91
+ return normalizeLanguage(cookie, options);
100
92
  };
101
93
  function detectFromHeader(c, options) {
102
94
  try {
@@ -117,14 +109,10 @@ function detectFromHeader(c, options) {
117
109
  }
118
110
  }
119
111
  function detectFromPath(c, options) {
120
- try {
121
- const url = new URL(c.req.url);
122
- const pathSegments = url.pathname.split("/").filter(Boolean);
123
- const langSegment = pathSegments[options.lookupFromPathIndex];
124
- return normalizeLanguage(langSegment, options);
125
- } catch {
126
- return void 0;
127
- }
112
+ const url = new URL(c.req.url);
113
+ const pathSegments = url.pathname.split("/").filter(Boolean);
114
+ const langSegment = pathSegments[options.lookupFromPathIndex];
115
+ return normalizeLanguage(langSegment, options);
128
116
  }
129
117
  const detectors = {
130
118
  querystring: detectFromQuery,
@@ -159,9 +147,6 @@ const detectLanguage = (c, options) => {
159
147
  let detectedLang;
160
148
  for (const detectorName of options.order) {
161
149
  const detector = detectors[detectorName];
162
- if (!detector) {
163
- continue;
164
- }
165
150
  try {
166
151
  detectedLang = detector(c, options);
167
152
  if (detectedLang) {
@@ -194,15 +179,8 @@ const languageDetector = (userOptions) => {
194
179
  };
195
180
  validateOptions(options);
196
181
  return async function languageDetector2(ctx, next) {
197
- try {
198
- const lang = detectLanguage(ctx, options);
199
- ctx.set("language", lang);
200
- } catch (error) {
201
- if (options.debug) {
202
- console.error("Language detection failed:", error);
203
- }
204
- ctx.set("language", options.fallbackLanguage);
205
- }
182
+ const lang = detectLanguage(ctx, options);
183
+ ctx.set("language", lang);
206
184
  await next();
207
185
  };
208
186
  };
@@ -45,7 +45,7 @@ const serveStatic = (options) => {
45
45
  } else {
46
46
  try {
47
47
  filename = (0, import_url.tryDecodeURI)(c.req.path);
48
- if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}/.test(filename)) {
48
+ if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}|\\/.test(filename)) {
49
49
  throw new Error();
50
50
  }
51
51
  } catch {
@@ -56,7 +56,9 @@ const timing = (config) => {
56
56
  endTime(c, "total");
57
57
  }
58
58
  if (options.autoEnd) {
59
- timers.forEach((_, key) => endTime(c, key));
59
+ timers.forEach((_, key) => {
60
+ endTime(c, key);
61
+ });
60
62
  }
61
63
  const enabled = typeof options.enabled === "function" ? options.enabled(c) : options.enabled;
62
64
  if (enabled) {
@@ -169,6 +169,21 @@ class HonoRequest {
169
169
  arrayBuffer() {
170
170
  return this.#cachedBody("arrayBuffer");
171
171
  }
172
+ /**
173
+ * `.bytes()` parses the request body as a `Uint8Array`.
174
+ *
175
+ * @see {@link https://hono.dev/docs/api/request#bytes}
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * app.post('/entry', async (c) => {
180
+ * const body = await c.req.bytes()
181
+ * })
182
+ * ```
183
+ */
184
+ bytes() {
185
+ return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer));
186
+ }
172
187
  /**
173
188
  * Parses the request body as a `Blob`.
174
189
  * @example
@@ -20,7 +20,7 @@ __export(compress_exports, {
20
20
  COMPRESSIBLE_CONTENT_TYPE_REGEX: () => COMPRESSIBLE_CONTENT_TYPE_REGEX
21
21
  });
22
22
  module.exports = __toCommonJS(compress_exports);
23
- const 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;
23
+ const 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;
24
24
  // Annotate the CommonJS export names for ESM import in node:
25
25
  0 && (module.exports = {
26
26
  COMPRESSIBLE_CONTENT_TYPE_REGEX
@@ -72,14 +72,14 @@ const parse = (cookie, name) => {
72
72
  return {};
73
73
  }
74
74
  const pairs = cookie.split(";");
75
- const parsedCookie = {};
75
+ const parsedCookie = /* @__PURE__ */ Object.create(null);
76
76
  for (const pairStr of pairs) {
77
77
  const valueStartPos = pairStr.indexOf("=");
78
78
  if (valueStartPos === -1) {
79
79
  continue;
80
80
  }
81
81
  const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));
82
- if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName)) {
82
+ if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) {
83
83
  continue;
84
84
  }
85
85
  let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));
@@ -96,7 +96,7 @@ const parse = (cookie, name) => {
96
96
  return parsedCookie;
97
97
  };
98
98
  const parseSigned = async (cookie, secret, name) => {
99
- const parsedCookie = {};
99
+ const parsedCookie = /* @__PURE__ */ Object.create(null);
100
100
  const secretKey = await getCryptoKey(secret);
101
101
  for (const [key, value] of Object.entries(parse(cookie, name))) {
102
102
  const signatureStartPos = value.lastIndexOf(".");
@@ -132,7 +132,7 @@ const _serialize = (name, value, opt = {}) => {
132
132
  throw new Error("__Host- Cookie must not have Domain attributes");
133
133
  }
134
134
  }
135
- for (const key of ["domain", "path"]) {
135
+ for (const key of ["domain", "path", "sameSite", "priority"]) {
136
136
  if (opt[key] && /[;\r\n]/.test(opt[key])) {
137
137
  throw new Error(`${key} must not contain ";", "\\r", or "\\n"`);
138
138
  }
@@ -42,7 +42,7 @@ const getFilePathWithoutDefaultDocument = (options) => {
42
42
  return;
43
43
  }
44
44
  filename = filename.replace(/^\.?[\/\\]/, "");
45
- filename = filename.replace(/\\/, "/");
45
+ filename = filename.replace(/\\/g, "/");
46
46
  root = root.replace(/\/$/, "");
47
47
  let path = root ? root + "/" + filename : filename;
48
48
  path = path.replace(/^\.?\//, "");
@@ -17,6 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var ipaddr_exports = {};
19
19
  __export(ipaddr_exports, {
20
+ INVALID_IP_ADDRESS_ERROR_CODE: () => INVALID_IP_ADDRESS_ERROR_CODE,
20
21
  convertIPv4BinaryToString: () => convertIPv4BinaryToString,
21
22
  convertIPv4MappedIPv6ToIPv4: () => convertIPv4MappedIPv6ToIPv4,
22
23
  convertIPv4ToBinary: () => convertIPv4ToBinary,
@@ -42,7 +43,9 @@ const expandIPv6 = (ipV6) => {
42
43
  if (node !== "") {
43
44
  sections[i] = node.padStart(4, "0");
44
45
  } else {
45
- sections[i + 1] === "" && sections.splice(i + 1, 1);
46
+ while (sections[i + 1] === "") {
47
+ sections.splice(i + 1, 1);
48
+ }
46
49
  sections[i] = new Array(8 - sections.length + 1).fill("0000").join(":");
47
50
  }
48
51
  }
@@ -50,6 +53,16 @@ const expandIPv6 = (ipV6) => {
50
53
  };
51
54
  const IPV4_OCTET_PART = "(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])";
52
55
  const IPV4_REGEX = new RegExp(`^(?:${IPV4_OCTET_PART}\\.){3}${IPV4_OCTET_PART}$`);
56
+ const INVALID_IP_ADDRESS_ERROR_CODE = "ERR_INVALID_IP_ADDRESS";
57
+ const CHAR_CODE_0 = 48;
58
+ const CHAR_CODE_9 = 57;
59
+ const CHAR_CODE_A = 65;
60
+ const CHAR_CODE_F = 70;
61
+ const CHAR_CODE_a = 97;
62
+ const CHAR_CODE_f = 102;
63
+ const CHAR_CODE_DOT = 46;
64
+ const CHAR_CODE_COLON = 58;
65
+ const CHAR_CODE_PERCENT = 37;
53
66
  const distinctRemoteAddr = (remoteAddr) => {
54
67
  if (IPV4_REGEX.test(remoteAddr)) {
55
68
  return "IPv4";
@@ -58,21 +71,187 @@ const distinctRemoteAddr = (remoteAddr) => {
58
71
  return "IPv6";
59
72
  }
60
73
  };
61
- const convertIPv4ToBinary = (ipv4) => {
62
- const parts = ipv4.split(".");
74
+ const createInvalidIPAddressError = (message) => {
75
+ const error = new TypeError(message);
76
+ error.code = INVALID_IP_ADDRESS_ERROR_CODE;
77
+ return error;
78
+ };
79
+ const throwInvalidIPv4Address = (ipv4) => {
80
+ throw createInvalidIPAddressError(`Invalid IPv4 address: ${ipv4}`);
81
+ };
82
+ const throwInvalidIPv6Address = (ipv6) => {
83
+ throw createInvalidIPAddressError(`Invalid IPv6 address: ${ipv6}`);
84
+ };
85
+ const parseIPv4ToBinary = (ipv4, start, end, onInvalid) => {
63
86
  let result = 0n;
64
- for (let i = 0; i < 4; i++) {
65
- result <<= 8n;
66
- result += BigInt(parts[i]);
87
+ let octets = 0;
88
+ let octet = 0;
89
+ let digits = 0;
90
+ let firstDigit = 0;
91
+ for (let i = start; i <= end; i++) {
92
+ const code = i < end ? ipv4.charCodeAt(i) : CHAR_CODE_DOT;
93
+ if (code >= CHAR_CODE_0 && code <= CHAR_CODE_9) {
94
+ if (digits === 0) {
95
+ firstDigit = code;
96
+ } else if (firstDigit === CHAR_CODE_0) {
97
+ onInvalid();
98
+ }
99
+ octet = octet * 10 + code - CHAR_CODE_0;
100
+ if (octet > 255) {
101
+ onInvalid();
102
+ }
103
+ digits++;
104
+ continue;
105
+ }
106
+ if (code !== CHAR_CODE_DOT || digits === 0 || octets === 4) {
107
+ onInvalid();
108
+ }
109
+ result = (result << 8n) + BigInt(octet);
110
+ octets++;
111
+ octet = 0;
112
+ digits = 0;
113
+ }
114
+ if (octets !== 4) {
115
+ onInvalid();
67
116
  }
68
117
  return result;
69
118
  };
119
+ const parseIPv6HexCode = (code) => {
120
+ if (code >= CHAR_CODE_0 && code <= CHAR_CODE_9) {
121
+ return code - CHAR_CODE_0;
122
+ }
123
+ if (code >= CHAR_CODE_A && code <= CHAR_CODE_F) {
124
+ return code - CHAR_CODE_A + 10;
125
+ }
126
+ if (code >= CHAR_CODE_a && code <= CHAR_CODE_f) {
127
+ return code - CHAR_CODE_a + 10;
128
+ }
129
+ return -1;
130
+ };
131
+ const isIPv6LinkLocal = (ipv6binary) => ipv6binary >> 118n === 0x3fan;
132
+ const convertIPv4ToBinary = (ipv4) => {
133
+ return parseIPv4ToBinary(ipv4, 0, ipv4.length, () => throwInvalidIPv4Address(ipv4));
134
+ };
70
135
  const convertIPv6ToBinary = (ipv6) => {
71
- const sections = expandIPv6(ipv6).split(":");
136
+ const length = ipv6.length;
137
+ const sections = [];
138
+ let hasZoneId = false;
139
+ let compressAt = -1;
140
+ let index = 0;
141
+ if (length === 0) {
142
+ throwInvalidIPv6Address(ipv6);
143
+ }
144
+ while (index < length) {
145
+ if (sections.length > 8) {
146
+ throwInvalidIPv6Address(ipv6);
147
+ }
148
+ let code = ipv6.charCodeAt(index);
149
+ if (code === CHAR_CODE_PERCENT) {
150
+ if (index + 1 === length) {
151
+ throwInvalidIPv6Address(ipv6);
152
+ }
153
+ hasZoneId = true;
154
+ break;
155
+ }
156
+ if (code === CHAR_CODE_COLON) {
157
+ if (index + 1 < length && ipv6.charCodeAt(index + 1) === CHAR_CODE_COLON) {
158
+ if (compressAt !== -1) {
159
+ throwInvalidIPv6Address(ipv6);
160
+ }
161
+ compressAt = sections.length;
162
+ index += 2;
163
+ continue;
164
+ }
165
+ throwInvalidIPv6Address(ipv6);
166
+ }
167
+ let value = 0;
168
+ let digits = 0;
169
+ const sectionStart = index;
170
+ while (index < length) {
171
+ code = ipv6.charCodeAt(index);
172
+ const hex = parseIPv6HexCode(code);
173
+ if (hex === -1) {
174
+ break;
175
+ }
176
+ if (digits === 4) {
177
+ throwInvalidIPv6Address(ipv6);
178
+ }
179
+ value = value << 4 | hex;
180
+ digits++;
181
+ index++;
182
+ }
183
+ if (index < length && ipv6.charCodeAt(index) === CHAR_CODE_DOT) {
184
+ let ipv4End = length;
185
+ for (let i = index; i < length; i++) {
186
+ if (ipv6.charCodeAt(i) === CHAR_CODE_PERCENT) {
187
+ if (i + 1 === length) {
188
+ throwInvalidIPv6Address(ipv6);
189
+ }
190
+ hasZoneId = true;
191
+ ipv4End = i;
192
+ break;
193
+ }
194
+ }
195
+ const ipv4 = parseIPv4ToBinary(
196
+ ipv6,
197
+ sectionStart,
198
+ ipv4End,
199
+ () => throwInvalidIPv6Address(ipv6)
200
+ );
201
+ sections.push(Number(ipv4 >> 16n & 0xffffn), Number(ipv4 & 0xffffn));
202
+ index = length;
203
+ break;
204
+ }
205
+ if (digits === 0) {
206
+ throwInvalidIPv6Address(ipv6);
207
+ }
208
+ sections.push(value);
209
+ if (index === length) {
210
+ break;
211
+ }
212
+ code = ipv6.charCodeAt(index);
213
+ if (code === CHAR_CODE_PERCENT) {
214
+ if (index + 1 === length) {
215
+ throwInvalidIPv6Address(ipv6);
216
+ }
217
+ hasZoneId = true;
218
+ break;
219
+ }
220
+ if (code !== CHAR_CODE_COLON) {
221
+ throwInvalidIPv6Address(ipv6);
222
+ }
223
+ if (index + 1 < length && ipv6.charCodeAt(index + 1) === CHAR_CODE_COLON) {
224
+ if (compressAt !== -1) {
225
+ throwInvalidIPv6Address(ipv6);
226
+ }
227
+ compressAt = sections.length;
228
+ index += 2;
229
+ continue;
230
+ }
231
+ index++;
232
+ if (index === length) {
233
+ throwInvalidIPv6Address(ipv6);
234
+ }
235
+ }
236
+ if (compressAt === -1 ? sections.length !== 8 : sections.length >= 8) {
237
+ throwInvalidIPv6Address(ipv6);
238
+ }
72
239
  let result = 0n;
73
- for (let i = 0; i < 8; i++) {
240
+ const zeros = compressAt === -1 ? 0 : 8 - sections.length;
241
+ const firstSectionEnd = compressAt === -1 ? sections.length : compressAt;
242
+ for (let i = 0; i < firstSectionEnd; i++) {
74
243
  result <<= 16n;
75
- result += BigInt(parseInt(sections[i], 16));
244
+ result += BigInt(sections[i]);
245
+ }
246
+ for (let i = 0; i < zeros; i++) {
247
+ result <<= 16n;
248
+ }
249
+ for (let i = firstSectionEnd; i < sections.length; i++) {
250
+ result <<= 16n;
251
+ result += BigInt(sections[i]);
252
+ }
253
+ if (hasZoneId && !isIPv6LinkLocal(result)) {
254
+ throwInvalidIPv6Address(ipv6);
76
255
  }
77
256
  return result;
78
257
  };
@@ -86,6 +265,9 @@ const convertIPv4BinaryToString = (ipV4) => {
86
265
  const isIPv4MappedIPv6 = (ipv6binary) => ipv6binary >> 32n === 0xffffn;
87
266
  const convertIPv4MappedIPv6ToIPv4 = (ipv6binary) => ipv6binary & 0xffffffffn;
88
267
  const convertIPv6BinaryToString = (ipV6) => {
268
+ if (ipV6 === 0n) {
269
+ return "::";
270
+ }
89
271
  if (isIPv4MappedIPv6(ipV6)) {
90
272
  return `::ffff:${convertIPv4BinaryToString(convertIPv4MappedIPv6ToIPv4(ipV6))}`;
91
273
  }
@@ -117,13 +299,14 @@ const convertIPv6BinaryToString = (ipV6) => {
117
299
  maxZeroEnd = 8;
118
300
  }
119
301
  }
120
- if (maxZeroStart !== -1) {
302
+ if (maxZeroStart !== -1 && maxZeroEnd - maxZeroStart > 1) {
121
303
  sections.splice(maxZeroStart, maxZeroEnd - maxZeroStart, ":");
122
304
  }
123
305
  return sections.join(":").replace(/:{2,}/g, "::");
124
306
  };
125
307
  // Annotate the CommonJS export names for ESM import in node:
126
308
  0 && (module.exports = {
309
+ INVALID_IP_ADDRESS_ERROR_CODE,
127
310
  convertIPv4BinaryToString,
128
311
  convertIPv4MappedIPv6ToIPv4,
129
312
  convertIPv4ToBinary,
@@ -28,15 +28,13 @@ const getMimeType = (filename, mimes = baseMimes) => {
28
28
  if (!match) {
29
29
  return;
30
30
  }
31
- let mimeType = mimes[match[1].toLowerCase()];
32
- if (mimeType && mimeType.startsWith("text")) {
33
- mimeType += "; charset=utf-8";
34
- }
35
- return mimeType;
31
+ return mimes[match[1].toLowerCase()];
36
32
  };
37
33
  const getExtension = (mimeType) => {
34
+ const baseType = mimeType.split(";", 1)[0].trim();
38
35
  for (const ext in baseMimes) {
39
- if (baseMimes[ext] === mimeType) {
36
+ const stored = baseMimes[ext];
37
+ if (stored === mimeType || stored.split(";", 1)[0].trim() === baseType) {
40
38
  return ext;
41
39
  }
42
40
  }
@@ -48,25 +46,25 @@ const _baseMimes = {
48
46
  av1: "video/av1",
49
47
  bin: "application/octet-stream",
50
48
  bmp: "image/bmp",
51
- css: "text/css",
52
- csv: "text/csv",
49
+ css: "text/css; charset=utf-8",
50
+ csv: "text/csv; charset=utf-8",
53
51
  eot: "application/vnd.ms-fontobject",
54
52
  epub: "application/epub+zip",
55
53
  gif: "image/gif",
56
54
  gz: "application/gzip",
57
- htm: "text/html",
58
- html: "text/html",
55
+ htm: "text/html; charset=utf-8",
56
+ html: "text/html; charset=utf-8",
59
57
  ico: "image/x-icon",
60
- ics: "text/calendar",
58
+ ics: "text/calendar; charset=utf-8",
61
59
  jpeg: "image/jpeg",
62
60
  jpg: "image/jpeg",
63
- js: "text/javascript",
61
+ js: "text/javascript; charset=utf-8",
64
62
  json: "application/json",
65
63
  jsonld: "application/ld+json",
66
64
  map: "application/json",
67
65
  mid: "audio/x-midi",
68
66
  midi: "audio/x-midi",
69
- mjs: "text/javascript",
67
+ mjs: "text/javascript; charset=utf-8",
70
68
  mp3: "audio/mpeg",
71
69
  mp4: "video/mp4",
72
70
  mpeg: "video/mpeg",
@@ -78,12 +76,12 @@ const _baseMimes = {
78
76
  pdf: "application/pdf",
79
77
  png: "image/png",
80
78
  rtf: "application/rtf",
81
- svg: "image/svg+xml",
79
+ svg: "image/svg+xml; charset=utf-8",
82
80
  tif: "image/tiff",
83
81
  tiff: "image/tiff",
84
82
  ts: "video/mp2t",
85
83
  ttf: "font/ttf",
86
- txt: "text/plain",
84
+ txt: "text/plain; charset=utf-8",
87
85
  wasm: "application/wasm",
88
86
  webm: "video/webm",
89
87
  weba: "audio/webm",
@@ -91,8 +89,8 @@ const _baseMimes = {
91
89
  webp: "image/webp",
92
90
  woff: "font/woff",
93
91
  woff2: "font/woff2",
94
- xhtml: "application/xhtml+xml",
95
- xml: "application/xml",
92
+ xhtml: "application/xhtml+xml; charset=utf-8",
93
+ xml: "application/xml; charset=utf-8",
96
94
  zip: "application/zip",
97
95
  "3gp": "video/3gpp",
98
96
  "3g2": "video/3gpp2",
@@ -48,7 +48,9 @@ class StreamingApi {
48
48
  done ? controller.close() : controller.enqueue(value);
49
49
  },
50
50
  cancel: () => {
51
- this.abort();
51
+ if (!this.closed) {
52
+ this.abort();
53
+ }
52
54
  }
53
55
  });
54
56
  }
@@ -70,11 +72,11 @@ class StreamingApi {
70
72
  return new Promise((res) => setTimeout(res, ms));
71
73
  }
72
74
  async close() {
75
+ this.closed = true;
73
76
  try {
74
77
  await this.writer.close();
75
78
  } catch {
76
79
  }
77
- this.closed = true;
78
80
  }
79
81
  async pipe(body) {
80
82
  this.writer.releaseLock();
package/dist/hono-base.js CHANGED
@@ -118,7 +118,7 @@ var Hono = class _Hono {
118
118
  handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
119
119
  handler[COMPOSED_HANDLER] = r.handler;
120
120
  }
121
- subApp.#addRoute(r.method, r.path, handler);
121
+ subApp.#addRoute(r.method, r.path, handler, r.basePath);
122
122
  });
123
123
  return this;
124
124
  }
@@ -242,7 +242,7 @@ var Hono = class _Hono {
242
242
  const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
243
243
  return (request) => {
244
244
  const url = new URL(request.url);
245
- url.pathname = url.pathname.slice(pathPrefixLength) || "/";
245
+ url.pathname = this.getPath(request).slice(pathPrefixLength) || "/";
246
246
  return new Request(url, request);
247
247
  };
248
248
  })();
@@ -256,10 +256,15 @@ var Hono = class _Hono {
256
256
  this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
257
257
  return this;
258
258
  }
259
- #addRoute(method, path, handler) {
259
+ #addRoute(method, path, handler, baseRoutePath) {
260
260
  method = method.toUpperCase();
261
261
  path = mergePath(this._basePath, path);
262
- const r = { basePath: this._basePath, path, method, handler };
262
+ const r = {
263
+ basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath,
264
+ path,
265
+ method,
266
+ handler
267
+ };
263
268
  this.router.add(method, path, [handler, r]);
264
269
  this.routes.push(r);
265
270
  }
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/index.ts
2
2
  import { Hono } from "./hono.js";
3
+ import { Context } from "./context.js";
3
4
  export {
5
+ Context,
4
6
  Hono
5
7
  };
@@ -6,7 +6,7 @@ var PREFIX = "Bearer";
6
6
  var HEADER = "Authorization";
7
7
  var bearerAuth = (options) => {
8
8
  if (!("token" in options || "verifyToken" in options)) {
9
- throw new Error('bearer auth middleware requires options for "token"');
9
+ throw new Error('bearer auth middleware requires options for "token" or "verifyToken"');
10
10
  }
11
11
  if (!options.realm) {
12
12
  options.realm = "";