hono 4.12.29 → 4.12.31

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.
@@ -36,7 +36,7 @@ const mergePath = (base, path) => {
36
36
  };
37
37
  const replaceUrlParam = (urlString, params) => {
38
38
  for (const [k, v] of Object.entries(params)) {
39
- const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??");
39
+ const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??(?=/|$)");
40
40
  urlString = urlString.replace(reg, v ? `/${v}` : "");
41
41
  }
42
42
  return urlString;
@@ -33,8 +33,9 @@ class SSEStreamingApi extends import_stream.StreamingApi {
33
33
  const dataLines = data.split(/\r\n|\r|\n/).map((line) => {
34
34
  return `data: ${line}`;
35
35
  }).join("\n");
36
- for (const key of ["event", "id", "retry"]) {
37
- if (message[key] && /[\r\n]/.test(message[key])) {
36
+ for (const key of ["event", "id"]) {
37
+ const value = message[key];
38
+ if (value && /[\r\n]/.test(value)) {
38
39
  throw new Error(`${key} must not contain "\\r" or "\\n"`);
39
40
  }
40
41
  }
@@ -42,7 +43,7 @@ class SSEStreamingApi extends import_stream.StreamingApi {
42
43
  message.event && `event: ${message.event}`,
43
44
  dataLines,
44
45
  message.id && `id: ${message.id}`,
45
- message.retry && `retry: ${message.retry}`
46
+ message.retry !== void 0 && `retry: ${message.retry}`
46
47
  ].filter(Boolean).join("\n") + "\n\n";
47
48
  await this.write(sseData);
48
49
  }
@@ -55,7 +55,7 @@ const cache = (options) => {
55
55
  );
56
56
  const addHeader = (c, responseVary) => {
57
57
  if (cacheControlDirectives) {
58
- const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0]) ?? [];
58
+ const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0].toLowerCase()) ?? [];
59
59
  for (const directive of cacheControlDirectives) {
60
60
  let [name, value] = directive.trim().split("=", 2);
61
61
  name = name.toLowerCase();
@@ -57,7 +57,8 @@ const compress = (options) => {
57
57
  return async function compress2(ctx, next) {
58
58
  await next();
59
59
  const contentLength = ctx.res.headers.get("Content-Length");
60
- if (ctx.res.headers.has("Content-Encoding") || // already encoded
60
+ if (ctx.res.status === 206 || // partial content, Content-Range refers to the uncompressed bytes
61
+ ctx.res.headers.has("Content-Encoding") || // already encoded
61
62
  ctx.res.headers.has("Transfer-Encoding") || // already encoded or chunked
62
63
  ctx.req.method === "HEAD" || // HEAD request
63
64
  contentLength && Number(contentLength) < threshold || // content-length below threshold
@@ -80,11 +80,13 @@ const methodOverride = (options) => async function methodOverride2(c, next) {
80
80
  if (method) {
81
81
  const url = new URL(c.req.url);
82
82
  url.searchParams.delete(queryName);
83
- const request = new Request(url.toString(), {
83
+ const requestInit = {
84
84
  body: c.req.raw.body,
85
85
  headers: c.req.raw.headers,
86
- method
87
- });
86
+ method,
87
+ duplex: c.req.raw.body ? "half" : void 0
88
+ };
89
+ const request = new Request(url.toString(), requestInit);
88
90
  return app.fetch(request, c.env, getExecutionCtx(c));
89
91
  }
90
92
  }
@@ -316,11 +316,16 @@ const cloneRawRequest = async (req) => {
316
316
  message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly."
317
317
  });
318
318
  }
319
+ const body = await req[cacheKey]();
320
+ const headers = req.header();
321
+ if (body instanceof FormData) {
322
+ delete headers["content-type"];
323
+ }
319
324
  const requestInit = {
320
- body: await req[cacheKey](),
325
+ body,
321
326
  cache: req.raw.cache,
322
327
  credentials: req.raw.credentials,
323
- headers: req.header(),
328
+ headers,
324
329
  integrity: req.raw.integrity,
325
330
  keepalive: req.raw.keepalive,
326
331
  method: req.method,
@@ -33,6 +33,12 @@ const parseBody = async (request, options = /* @__PURE__ */ Object.create(null))
33
33
  return {};
34
34
  };
35
35
  async function parseFormData(request, options) {
36
+ if (!isRawRequest(request) && request.bodyCache.formData) {
37
+ return convertFormDataToBodyData(
38
+ await request.bodyCache.formData,
39
+ options
40
+ );
41
+ }
36
42
  const headers = isRawRequest(request) ? request.headers : request.raw.headers;
37
43
  const arrayBuffer = await request.arrayBuffer();
38
44
  const formDataPromise = (0, import_buffer.bufferToFormData)(arrayBuffer, headers.get("Content-Type") || "");
@@ -8,7 +8,7 @@ var mergePath = (base, path) => {
8
8
  };
9
9
  var replaceUrlParam = (urlString, params) => {
10
10
  for (const [k, v] of Object.entries(params)) {
11
- const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??");
11
+ const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??(?=/|$)");
12
12
  urlString = urlString.replace(reg, v ? `/${v}` : "");
13
13
  }
14
14
  return urlString;
@@ -11,8 +11,9 @@ var SSEStreamingApi = class extends StreamingApi {
11
11
  const dataLines = data.split(/\r\n|\r|\n/).map((line) => {
12
12
  return `data: ${line}`;
13
13
  }).join("\n");
14
- for (const key of ["event", "id", "retry"]) {
15
- if (message[key] && /[\r\n]/.test(message[key])) {
14
+ for (const key of ["event", "id"]) {
15
+ const value = message[key];
16
+ if (value && /[\r\n]/.test(value)) {
16
17
  throw new Error(`${key} must not contain "\\r" or "\\n"`);
17
18
  }
18
19
  }
@@ -20,7 +21,7 @@ var SSEStreamingApi = class extends StreamingApi {
20
21
  message.event && `event: ${message.event}`,
21
22
  dataLines,
22
23
  message.id && `id: ${message.id}`,
23
- message.retry && `retry: ${message.retry}`
24
+ message.retry !== void 0 && `retry: ${message.retry}`
24
25
  ].filter(Boolean).join("\n") + "\n\n";
25
26
  await this.write(sseData);
26
27
  }
@@ -34,7 +34,7 @@ var cache = (options) => {
34
34
  );
35
35
  const addHeader = (c, responseVary) => {
36
36
  if (cacheControlDirectives) {
37
- const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0]) ?? [];
37
+ const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0].toLowerCase()) ?? [];
38
38
  for (const directive of cacheControlDirectives) {
39
39
  let [name, value] = directive.trim().split("=", 2);
40
40
  name = name.toLowerCase();
@@ -35,7 +35,8 @@ var compress = (options) => {
35
35
  return async function compress2(ctx, next) {
36
36
  await next();
37
37
  const contentLength = ctx.res.headers.get("Content-Length");
38
- if (ctx.res.headers.has("Content-Encoding") || // already encoded
38
+ if (ctx.res.status === 206 || // partial content, Content-Range refers to the uncompressed bytes
39
+ ctx.res.headers.has("Content-Encoding") || // already encoded
39
40
  ctx.res.headers.has("Transfer-Encoding") || // already encoded or chunked
40
41
  ctx.req.method === "HEAD" || // HEAD request
41
42
  contentLength && Number(contentLength) < threshold || // content-length below threshold
@@ -59,11 +59,13 @@ var methodOverride = (options) => async function methodOverride2(c, next) {
59
59
  if (method) {
60
60
  const url = new URL(c.req.url);
61
61
  url.searchParams.delete(queryName);
62
- const request = new Request(url.toString(), {
62
+ const requestInit = {
63
63
  body: c.req.raw.body,
64
64
  headers: c.req.raw.headers,
65
- method
66
- });
65
+ method,
66
+ duplex: c.req.raw.body ? "half" : void 0
67
+ };
68
+ const request = new Request(url.toString(), requestInit);
67
69
  return app.fetch(request, c.env, getExecutionCtx(c));
68
70
  }
69
71
  }
package/dist/request.js CHANGED
@@ -294,11 +294,16 @@ var cloneRawRequest = async (req) => {
294
294
  message: "Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly."
295
295
  });
296
296
  }
297
+ const body = await req[cacheKey]();
298
+ const headers = req.header();
299
+ if (body instanceof FormData) {
300
+ delete headers["content-type"];
301
+ }
297
302
  const requestInit = {
298
- body: await req[cacheKey](),
303
+ body,
299
304
  cache: req.raw.cache,
300
305
  credentials: req.raw.credentials,
301
- headers: req.header(),
306
+ headers,
302
307
  integrity: req.raw.integrity,
303
308
  keepalive: req.raw.keepalive,
304
309
  method: req.method,
@@ -31,7 +31,7 @@ export type WSReadyState = 0 | 1 | 2 | 3;
31
31
  * An argument for WSContext class
32
32
  */
33
33
  export interface WSContextInit<T = unknown> {
34
- send(data: string | ArrayBuffer | Uint8Array, options: SendOptions): void;
34
+ send(data: string | ArrayBuffer | Uint8Array<ArrayBuffer>, options: SendOptions): void;
35
35
  close(code?: number, reason?: string): void;
36
36
  raw?: T;
37
37
  readyState: WSReadyState;
@@ -71,7 +71,7 @@ export declare const every: (...middleware: (MiddlewareHandler | Condition)[]) =
71
71
  * @example
72
72
  * ```ts
73
73
  * import { except } from 'hono/combine'
74
- * import { bearerAuth } from 'hono/bearer-auth
74
+ * import { bearerAuth } from 'hono/bearer-auth'
75
75
  *
76
76
  * // If client is accessing public API, then skip authentication.
77
77
  * // Otherwise, require a valid token.
@@ -12,6 +12,12 @@ var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) =
12
12
  return {};
13
13
  };
14
14
  async function parseFormData(request, options) {
15
+ if (!isRawRequest(request) && request.bodyCache.formData) {
16
+ return convertFormDataToBodyData(
17
+ await request.bodyCache.formData,
18
+ options
19
+ );
20
+ }
15
21
  const headers = isRawRequest(request) ? request.headers : request.raw.headers;
16
22
  const arrayBuffer = await request.arrayBuffer();
17
23
  const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.12.29",
3
+ "version": "4.12.31",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -666,7 +666,7 @@
666
666
  "nodejs"
667
667
  ],
668
668
  "devDependencies": {
669
- "@hono/eslint-config": "^2.1.0",
669
+ "@hono/eslint-config": "^2.1.1",
670
670
  "@hono/node-server": "^2.0.2",
671
671
  "@types/jsdom": "^21.1.7",
672
672
  "@types/node": "^24.3.0",
@@ -684,7 +684,7 @@
684
684
  "pkg-pr-new": "^0.0.53",
685
685
  "prettier": "3.7.4",
686
686
  "publint": "0.3.15",
687
- "typescript": "^5.9.2",
687
+ "typescript": "^6.0.3",
688
688
  "undici": "^6.27.0",
689
689
  "vite-plugin-fastly-js-compute": "^0.4.2",
690
690
  "vitest": "^4.1.9",