hono 4.5.0 → 4.5.1

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.
@@ -157,8 +157,15 @@ const hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {
157
157
  "ws"
158
158
  );
159
159
  const targetUrl = new URL(webSocketUrl);
160
- for (const key in opts.args[0]?.query) {
161
- targetUrl.searchParams.set(key, opts.args[0].query[key]);
160
+ const queryParams = opts.args[0]?.query;
161
+ if (queryParams) {
162
+ Object.entries(queryParams).forEach(([key, value]) => {
163
+ if (Array.isArray(value)) {
164
+ value.forEach((item) => targetUrl.searchParams.append(key, item));
165
+ } else {
166
+ targetUrl.searchParams.set(key, value);
167
+ }
168
+ });
162
169
  }
163
170
  return new WebSocket(targetUrl.toString());
164
171
  }
@@ -33,28 +33,30 @@ const bearerAuth = (options) => {
33
33
  if (!options.realm) {
34
34
  options.realm = "";
35
35
  }
36
- if (!options.prefix) {
36
+ if (options.prefix === void 0) {
37
37
  options.prefix = PREFIX;
38
38
  }
39
39
  const realm = options.realm?.replace(/"/g, '\\"');
40
+ const prefixRegexStr = options.prefix === "" ? "" : `${options.prefix} +`;
41
+ const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`);
42
+ const wwwAuthenticatePrefix = options.prefix === "" ? "" : `${options.prefix} `;
40
43
  return async function bearerAuth2(c, next) {
41
44
  const headerToken = c.req.header(options.headerName || HEADER);
42
45
  if (!headerToken) {
43
46
  const res = new Response("Unauthorized", {
44
47
  status: 401,
45
48
  headers: {
46
- "WWW-Authenticate": `${options.prefix} realm="` + realm + '"'
49
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}realm="` + realm + '"'
47
50
  }
48
51
  });
49
52
  throw new import_http_exception.HTTPException(401, { res });
50
53
  } else {
51
- const regexp = new RegExp("^" + options.prefix + " +(" + TOKEN_STRINGS + ") *$");
52
54
  const match = regexp.exec(headerToken);
53
55
  if (!match) {
54
56
  const res = new Response("Bad Request", {
55
57
  status: 400,
56
58
  headers: {
57
- "WWW-Authenticate": `${options.prefix} error="invalid_request"`
59
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_request"`
58
60
  }
59
61
  });
60
62
  throw new import_http_exception.HTTPException(400, { res });
@@ -76,7 +78,7 @@ const bearerAuth = (options) => {
76
78
  const res = new Response("Unauthorized", {
77
79
  status: 401,
78
80
  headers: {
79
- "WWW-Authenticate": `${options.prefix} error="invalid_token"`
81
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_token"`
80
82
  }
81
83
  });
82
84
  throw new import_http_exception.HTTPException(401, { res });
@@ -24,15 +24,17 @@ module.exports = __toCommonJS(validator_exports);
24
24
  var import_cookie = require("../helper/cookie");
25
25
  var import_http_exception = require("../http-exception");
26
26
  var import_buffer = require("../utils/buffer");
27
+ const jsonRegex = /^application\/([a-z-\.]+\+)?json$/;
28
+ const multipartRegex = /^multipart\/form-data(; boundary=[A-Za-z0-9'()+_,\-./:=?]+)?$/;
29
+ const urlencodedRegex = /^application\/x-www-form-urlencoded$/;
27
30
  const validator = (target, validationFunc) => {
28
31
  return async (c, next) => {
29
32
  let value = {};
30
33
  const contentType = c.req.header("Content-Type");
31
34
  switch (target) {
32
35
  case "json":
33
- if (!contentType || !/^application\/([a-z-\.]+\+)?json/.test(contentType)) {
34
- const message = `Invalid HTTP header: Content-Type=${contentType}`;
35
- throw new import_http_exception.HTTPException(400, { message });
36
+ if (!contentType || !jsonRegex.test(contentType)) {
37
+ break;
36
38
  }
37
39
  try {
38
40
  value = await c.req.json();
@@ -42,7 +44,7 @@ const validator = (target, validationFunc) => {
42
44
  }
43
45
  break;
44
46
  case "form": {
45
- if (!contentType) {
47
+ if (!contentType || !(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))) {
46
48
  break;
47
49
  }
48
50
  let formData;
@@ -141,8 +141,15 @@ var hc = (baseUrl, options) => createProxy(function proxyCallback(opts) {
141
141
  "ws"
142
142
  );
143
143
  const targetUrl = new URL(webSocketUrl);
144
- for (const key in opts.args[0]?.query) {
145
- targetUrl.searchParams.set(key, opts.args[0].query[key]);
144
+ const queryParams = opts.args[0]?.query;
145
+ if (queryParams) {
146
+ Object.entries(queryParams).forEach(([key, value]) => {
147
+ if (Array.isArray(value)) {
148
+ value.forEach((item) => targetUrl.searchParams.append(key, item));
149
+ } else {
150
+ targetUrl.searchParams.set(key, value);
151
+ }
152
+ });
146
153
  }
147
154
  return new WebSocket(targetUrl.toString());
148
155
  }
@@ -11,28 +11,30 @@ var bearerAuth = (options) => {
11
11
  if (!options.realm) {
12
12
  options.realm = "";
13
13
  }
14
- if (!options.prefix) {
14
+ if (options.prefix === void 0) {
15
15
  options.prefix = PREFIX;
16
16
  }
17
17
  const realm = options.realm?.replace(/"/g, '\\"');
18
+ const prefixRegexStr = options.prefix === "" ? "" : `${options.prefix} +`;
19
+ const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`);
20
+ const wwwAuthenticatePrefix = options.prefix === "" ? "" : `${options.prefix} `;
18
21
  return async function bearerAuth2(c, next) {
19
22
  const headerToken = c.req.header(options.headerName || HEADER);
20
23
  if (!headerToken) {
21
24
  const res = new Response("Unauthorized", {
22
25
  status: 401,
23
26
  headers: {
24
- "WWW-Authenticate": `${options.prefix} realm="` + realm + '"'
27
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}realm="` + realm + '"'
25
28
  }
26
29
  });
27
30
  throw new HTTPException(401, { res });
28
31
  } else {
29
- const regexp = new RegExp("^" + options.prefix + " +(" + TOKEN_STRINGS + ") *$");
30
32
  const match = regexp.exec(headerToken);
31
33
  if (!match) {
32
34
  const res = new Response("Bad Request", {
33
35
  status: 400,
34
36
  headers: {
35
- "WWW-Authenticate": `${options.prefix} error="invalid_request"`
37
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_request"`
36
38
  }
37
39
  });
38
40
  throw new HTTPException(400, { res });
@@ -54,7 +56,7 @@ var bearerAuth = (options) => {
54
56
  const res = new Response("Unauthorized", {
55
57
  status: 401,
56
58
  headers: {
57
- "WWW-Authenticate": `${options.prefix} error="invalid_token"`
59
+ "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_token"`
58
60
  }
59
61
  });
60
62
  throw new HTTPException(401, { res });
@@ -62,10 +62,6 @@ export declare class Factory<E extends Env = any, P extends string = any> {
62
62
  constructor(init?: {
63
63
  initApp?: InitApp<E>;
64
64
  });
65
- /**
66
- * @experimental
67
- * `createApp` is an experimental feature.
68
- */
69
65
  createApp: () => Hono<E>;
70
66
  createMiddleware: <I extends Input = {}>(middleware: MiddlewareHandler<E, P, I>) => MiddlewareHandler<E, P, I>;
71
67
  createHandlers: CreateHandlersInterface<E, P>;
@@ -26,7 +26,7 @@ type BearerAuthOptions = {
26
26
  * @param {string | string[]} [options.token] - The string or array of strings to validate the incoming bearer token against.
27
27
  * @param {Function} [options.verifyToken] - The function to verify the token.
28
28
  * @param {string} [options.realm=""] - The domain name of the realm, as part of the returned WWW-Authenticate challenge header.
29
- * @param {string} [options.prefix="Bearer"] - The prefix (or known as `schema`) for the Authorization header value.
29
+ * @param {string} [options.prefix="Bearer"] - The prefix (or known as `schema`) for the Authorization header value. If set to the empty string, no prefix is expected.
30
30
  * @param {string} [options.headerName=Authorization] - The header name.
31
31
  * @param {Function} [options.hashFunction] - A function to handle hashing for safe comparison of authentication tokens.
32
32
  * @returns {MiddlewareHandler} The middleware handler function.
@@ -6,6 +6,7 @@ import type { MiddlewareHandler } from '../../types';
6
6
  import type { CookiePrefixOptions } from '../../utils/cookie';
7
7
  import '../../context';
8
8
  import type { SignatureAlgorithm } from '../../utils/jwt/jwa';
9
+ import type { SignatureKey } from '../../utils/jwt/jws';
9
10
  export type JwtVariables = {
10
11
  jwtPayload: any;
11
12
  };
@@ -15,7 +16,7 @@ export type JwtVariables = {
15
16
  * @see {@link https://hono.dev/docs/middleware/builtin/jwt}
16
17
  *
17
18
  * @param {object} options - The options for the JWT middleware.
18
- * @param {string} [options.secret] - A value of your secret key.
19
+ * @param {SignatureKey} [options.secret] - A value of your secret key.
19
20
  * @param {string} [options.cookie] - If this value is set, then the value is retrieved from the cookie header using that value as a key, which is then validated as a token.
20
21
  * @param {SignatureAlgorithm} [options.alg=HS256] - An algorithm type that is used for verifying. Available types are `HS256` | `HS384` | `HS512` | `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA`.
21
22
  * @returns {MiddlewareHandler} The middleware handler function.
@@ -37,7 +38,7 @@ export type JwtVariables = {
37
38
  * ```
38
39
  */
39
40
  export declare const jwt: (options: {
40
- secret: string;
41
+ secret: SignatureKey;
41
42
  cookie?: string | {
42
43
  key: string;
43
44
  secret?: string | BufferSource;
@@ -45,9 +46,9 @@ export declare const jwt: (options: {
45
46
  };
46
47
  alg?: SignatureAlgorithm;
47
48
  }) => MiddlewareHandler;
48
- export declare const verify: (token: string, publicKey: import("../../utils/jwt/jws").SignatureKey, alg?: "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA") => Promise<import("../../utils/jwt/types").JWTPayload>;
49
+ export declare const verify: (token: string, publicKey: SignatureKey, alg?: "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA") => Promise<import("../../utils/jwt/types").JWTPayload>;
49
50
  export declare const decode: (token: string) => {
50
51
  header: import("../../utils/jwt/jwt").TokenHeader;
51
52
  payload: import("../../utils/jwt/types").JWTPayload;
52
53
  };
53
- export declare const sign: (payload: import("../../utils/jwt/types").JWTPayload, privateKey: import("../../utils/jwt/jws").SignatureKey, alg?: "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA") => Promise<string>;
54
+ export declare const sign: (payload: import("../../utils/jwt/types").JWTPayload, privateKey: SignatureKey, alg?: "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA") => Promise<string>;
@@ -2,15 +2,17 @@
2
2
  import { getCookie } from "../helper/cookie/index.js";
3
3
  import { HTTPException } from "../http-exception.js";
4
4
  import { bufferToFormData } from "../utils/buffer.js";
5
+ var jsonRegex = /^application\/([a-z-\.]+\+)?json$/;
6
+ var multipartRegex = /^multipart\/form-data(; boundary=[A-Za-z0-9'()+_,\-./:=?]+)?$/;
7
+ var urlencodedRegex = /^application\/x-www-form-urlencoded$/;
5
8
  var validator = (target, validationFunc) => {
6
9
  return async (c, next) => {
7
10
  let value = {};
8
11
  const contentType = c.req.header("Content-Type");
9
12
  switch (target) {
10
13
  case "json":
11
- if (!contentType || !/^application\/([a-z-\.]+\+)?json/.test(contentType)) {
12
- const message = `Invalid HTTP header: Content-Type=${contentType}`;
13
- throw new HTTPException(400, { message });
14
+ if (!contentType || !jsonRegex.test(contentType)) {
15
+ break;
14
16
  }
15
17
  try {
16
18
  value = await c.req.json();
@@ -20,7 +22,7 @@ var validator = (target, validationFunc) => {
20
22
  }
21
23
  break;
22
24
  case "form": {
23
- if (!contentType) {
25
+ if (!contentType || !(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))) {
24
26
  break;
25
27
  }
26
28
  let formData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.5.0",
3
+ "version": "4.5.1",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -25,12 +25,13 @@
25
25
  "format": "prettier --check --cache \"src/**/*.{js,ts,tsx}\" \"runtime_tests/**/*.{js,ts,tsx}\"",
26
26
  "format:fix": "prettier --write --cache --cache-strategy metadata \"src/**/*.{js,ts,tsx}\" \"runtime_tests/**/*.{js,ts,tsx}\"",
27
27
  "copy:package.cjs.json": "cp ./package.cjs.json ./dist/cjs/package.json && cp ./package.cjs.json ./dist/types/package.json ",
28
- "build": "rimraf dist && bun ./build.ts && bun run copy:package.cjs.json",
28
+ "build": "bun run --shell bun remove-dist && bun ./build.ts && bun run copy:package.cjs.json",
29
29
  "postbuild": "publint",
30
- "watch": "rimraf dist && bun ./build.ts --watch && bun run copy:package.cjs.json",
30
+ "watch": "bun run --shell bun remove-dist && bun ./build.ts --watch && bun run copy:package.cjs.json",
31
31
  "coverage": "vitest --run --coverage",
32
32
  "prerelease": "bun test:deno && bun run build",
33
- "release": "np"
33
+ "release": "np",
34
+ "remove-dist": "rm -rf dist"
34
35
  },
35
36
  "exports": {
36
37
  ".": {
@@ -627,7 +628,6 @@
627
628
  "np": "7.7.0",
628
629
  "prettier": "^2.6.2",
629
630
  "publint": "^0.1.8",
630
- "rimraf": "^3.0.2",
631
631
  "supertest": "^6.3.3",
632
632
  "typescript": "^5.3.3",
633
633
  "vite-plugin-fastly-js-compute": "^0.4.2",
@@ -1,49 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (let key of __getOwnPropNames(from))
11
- if (!__hasOwnProp.call(to, key) && key !== except)
12
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
18
- mod
19
- ));
20
- var nodeCrypto = __toESM(require("node:crypto"), 1);
21
- var import_vitest = require("vitest");
22
- if (!globalThis.crypto) {
23
- import_vitest.vi.stubGlobal("crypto", nodeCrypto);
24
- import_vitest.vi.stubGlobal("CryptoKey", nodeCrypto.webcrypto.CryptoKey);
25
- }
26
- class MockCache {
27
- name;
28
- store;
29
- constructor(name, store) {
30
- this.name = name;
31
- this.store = store;
32
- }
33
- async match(key) {
34
- return this.store.get(key) || null;
35
- }
36
- async keys() {
37
- return this.store.keys();
38
- }
39
- async put(key, response) {
40
- this.store.set(key, response);
41
- }
42
- }
43
- const globalStore = /* @__PURE__ */ new Map();
44
- const caches = {
45
- open: (name) => {
46
- return new MockCache(name, globalStore);
47
- }
48
- };
49
- import_vitest.vi.stubGlobal("caches", caches);
@@ -1,31 +0,0 @@
1
- // src/test-utils/setup-vitest.ts
2
- import * as nodeCrypto from "node:crypto";
3
- import { vi } from "vitest";
4
- if (!globalThis.crypto) {
5
- vi.stubGlobal("crypto", nodeCrypto);
6
- vi.stubGlobal("CryptoKey", nodeCrypto.webcrypto.CryptoKey);
7
- }
8
- var MockCache = class {
9
- name;
10
- store;
11
- constructor(name, store) {
12
- this.name = name;
13
- this.store = store;
14
- }
15
- async match(key) {
16
- return this.store.get(key) || null;
17
- }
18
- async keys() {
19
- return this.store.keys();
20
- }
21
- async put(key, response) {
22
- this.store.set(key, response);
23
- }
24
- };
25
- var globalStore = /* @__PURE__ */ new Map();
26
- var caches = {
27
- open: (name) => {
28
- return new MockCache(name, globalStore);
29
- }
30
- };
31
- vi.stubGlobal("caches", caches);