hono 4.1.7 → 4.2.0

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 (59) hide show
  1. package/dist/cjs/helper/ssg/ssg.js +17 -14
  2. package/dist/cjs/jsx/dom/index.js +3 -0
  3. package/dist/cjs/jsx/hooks/index.js +4 -0
  4. package/dist/cjs/jsx/index.js +3 -0
  5. package/dist/cjs/middleware/basic-auth/index.js +22 -9
  6. package/dist/cjs/middleware/bearer-auth/index.js +4 -2
  7. package/dist/cjs/middleware/cache/index.js +22 -3
  8. package/dist/cjs/middleware/cors/index.js +1 -1
  9. package/dist/cjs/middleware/jwt/index.js +12 -6
  10. package/dist/cjs/middleware/method-override/index.js +106 -0
  11. package/dist/cjs/middleware/trailing-slash/index.js +49 -0
  12. package/dist/cjs/request.js +13 -4
  13. package/dist/cjs/test-utils/setup-vitest.js +5 -2
  14. package/dist/cjs/utils/jwt/index.js +2 -7
  15. package/dist/cjs/utils/jwt/jwa.js +43 -0
  16. package/dist/cjs/utils/jwt/jws.js +212 -0
  17. package/dist/cjs/utils/jwt/jwt.js +26 -72
  18. package/dist/cjs/utils/jwt/types.js +21 -8
  19. package/dist/cjs/utils/jwt/utf8.js +31 -0
  20. package/dist/cjs/validator/validator.js +2 -27
  21. package/dist/helper/ssg/ssg.js +16 -14
  22. package/dist/jsx/dom/index.js +3 -0
  23. package/dist/jsx/hooks/index.js +3 -0
  24. package/dist/jsx/index.js +3 -0
  25. package/dist/middleware/basic-auth/index.js +22 -9
  26. package/dist/middleware/bearer-auth/index.js +4 -2
  27. package/dist/middleware/cache/index.js +22 -3
  28. package/dist/middleware/cors/index.js +1 -1
  29. package/dist/middleware/jwt/index.js +12 -6
  30. package/dist/middleware/method-override/index.js +83 -0
  31. package/dist/middleware/trailing-slash/index.js +25 -0
  32. package/dist/request.js +13 -4
  33. package/dist/test-utils/setup-vitest.js +5 -2
  34. package/dist/types/helper/ssg/ssg.d.ts +3 -1
  35. package/dist/types/helper.d.ts +12 -0
  36. package/dist/types/jsx/dom/index.d.ts +3 -2
  37. package/dist/types/jsx/hooks/index.d.ts +1 -0
  38. package/dist/types/jsx/index.d.ts +3 -2
  39. package/dist/types/middleware/basic-auth/index.d.ts +9 -2
  40. package/dist/types/middleware/bearer-auth/index.d.ts +10 -2
  41. package/dist/types/middleware/cache/index.d.ts +1 -0
  42. package/dist/types/middleware/cors/index.d.ts +2 -1
  43. package/dist/types/middleware/jwt/index.d.ts +4 -3
  44. package/dist/types/middleware/method-override/index.d.ts +36 -0
  45. package/dist/types/middleware/trailing-slash/index.d.ts +13 -0
  46. package/dist/types/utils/jwt/index.d.ts +8 -1
  47. package/dist/types/utils/jwt/jwa.d.ts +16 -0
  48. package/dist/types/utils/jwt/jws.d.ts +4 -0
  49. package/dist/types/utils/jwt/jwt.d.ts +10 -5
  50. package/dist/types/utils/jwt/types.d.ts +30 -4
  51. package/dist/types/utils/jwt/utf8.d.ts +2 -0
  52. package/dist/utils/jwt/index.js +2 -1
  53. package/dist/utils/jwt/jwa.js +20 -0
  54. package/dist/utils/jwt/jws.js +188 -0
  55. package/dist/utils/jwt/jwt.js +24 -53
  56. package/dist/utils/jwt/types.js +19 -7
  57. package/dist/utils/jwt/utf8.js +7 -0
  58. package/dist/validator/validator.js +2 -27
  59. package/package.json +14 -1
@@ -0,0 +1,188 @@
1
+ // src/utils/jwt/jws.ts
2
+ import { getRuntimeKey } from "../../helper.js";
3
+ import { decodeBase64 } from "../encode.js";
4
+ import { JwtAlgorithmNotImplemented } from "./types.js";
5
+ import { CryptoKeyUsage } from "./types.js";
6
+ import { utf8Encoder } from "./utf8.js";
7
+ async function signing(privateKey, alg, data) {
8
+ const algorithm = getKeyAlgorithm(alg);
9
+ const cryptoKey = await importPrivateKey(privateKey, algorithm);
10
+ return await crypto.subtle.sign(algorithm, cryptoKey, data);
11
+ }
12
+ async function verifying(publicKey, alg, signature, data) {
13
+ const algorithm = getKeyAlgorithm(alg);
14
+ const cryptoKey = await importPublicKey(publicKey, algorithm);
15
+ return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);
16
+ }
17
+ function pemToBinary(pem) {
18
+ return decodeBase64(pem.replace(/-+(BEGIN|END).*/g, "").replace(/\s/g, ""));
19
+ }
20
+ async function importPrivateKey(key, alg) {
21
+ if (!crypto.subtle || !crypto.subtle.importKey) {
22
+ throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");
23
+ }
24
+ if (isCryptoKey(key)) {
25
+ if (key.type !== "private") {
26
+ throw new Error(`unexpected non private key: CryptoKey.type is ${key.type}`);
27
+ }
28
+ return key;
29
+ }
30
+ const usages = [CryptoKeyUsage.Sign];
31
+ if (typeof key === "object") {
32
+ return await crypto.subtle.importKey("jwk", key, alg, false, usages);
33
+ }
34
+ if (key.includes("PRIVATE")) {
35
+ return await crypto.subtle.importKey("pkcs8", pemToBinary(key), alg, false, usages);
36
+ }
37
+ return await crypto.subtle.importKey("raw", utf8Encoder.encode(key), alg, false, usages);
38
+ }
39
+ async function importPublicKey(key, alg) {
40
+ if (!crypto.subtle || !crypto.subtle.importKey) {
41
+ throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");
42
+ }
43
+ if (isCryptoKey(key)) {
44
+ if (key.type === "public" || key.type === "secret") {
45
+ return key;
46
+ }
47
+ key = await exportPublicJwkFrom(key);
48
+ }
49
+ if (typeof key === "string" && key.includes("PRIVATE")) {
50
+ const privateKey = await crypto.subtle.importKey("pkcs8", pemToBinary(key), alg, true, [
51
+ CryptoKeyUsage.Sign
52
+ ]);
53
+ key = await exportPublicJwkFrom(privateKey);
54
+ }
55
+ const usages = [CryptoKeyUsage.Verify];
56
+ if (typeof key === "object") {
57
+ return await crypto.subtle.importKey("jwk", key, alg, false, usages);
58
+ }
59
+ if (key.includes("PUBLIC")) {
60
+ return await crypto.subtle.importKey("spki", pemToBinary(key), alg, false, usages);
61
+ }
62
+ return await crypto.subtle.importKey("raw", utf8Encoder.encode(key), alg, false, usages);
63
+ }
64
+ async function exportPublicJwkFrom(privateKey) {
65
+ if (privateKey.type !== "private") {
66
+ throw new Error(`unexpected key type: ${privateKey.type}`);
67
+ }
68
+ if (!privateKey.extractable) {
69
+ throw new Error("unexpected private key is unextractable");
70
+ }
71
+ const jwk = await crypto.subtle.exportKey("jwk", privateKey);
72
+ const { kty } = jwk;
73
+ const { alg, e, n } = jwk;
74
+ const { crv, x, y } = jwk;
75
+ return { kty, alg, e, n, crv, x, y, key_ops: [CryptoKeyUsage.Verify] };
76
+ }
77
+ function getKeyAlgorithm(name) {
78
+ switch (name) {
79
+ case "HS256":
80
+ return {
81
+ name: "HMAC",
82
+ hash: {
83
+ name: "SHA-256"
84
+ }
85
+ };
86
+ case "HS384":
87
+ return {
88
+ name: "HMAC",
89
+ hash: {
90
+ name: "SHA-384"
91
+ }
92
+ };
93
+ case "HS512":
94
+ return {
95
+ name: "HMAC",
96
+ hash: {
97
+ name: "SHA-512"
98
+ }
99
+ };
100
+ case "RS256":
101
+ return {
102
+ name: "RSASSA-PKCS1-v1_5",
103
+ hash: {
104
+ name: "SHA-256"
105
+ }
106
+ };
107
+ case "RS384":
108
+ return {
109
+ name: "RSASSA-PKCS1-v1_5",
110
+ hash: {
111
+ name: "SHA-384"
112
+ }
113
+ };
114
+ case "RS512":
115
+ return {
116
+ name: "RSASSA-PKCS1-v1_5",
117
+ hash: {
118
+ name: "SHA-512"
119
+ }
120
+ };
121
+ case "PS256":
122
+ return {
123
+ name: "RSA-PSS",
124
+ hash: {
125
+ name: "SHA-256"
126
+ },
127
+ saltLength: 32
128
+ };
129
+ case "PS384":
130
+ return {
131
+ name: "RSA-PSS",
132
+ hash: {
133
+ name: "SHA-384"
134
+ },
135
+ saltLength: 48
136
+ };
137
+ case "PS512":
138
+ return {
139
+ name: "RSA-PSS",
140
+ hash: {
141
+ name: "SHA-512"
142
+ },
143
+ saltLength: 64
144
+ };
145
+ case "ES256":
146
+ return {
147
+ name: "ECDSA",
148
+ hash: {
149
+ name: "SHA-256"
150
+ },
151
+ namedCurve: "P-256"
152
+ };
153
+ case "ES384":
154
+ return {
155
+ name: "ECDSA",
156
+ hash: {
157
+ name: "SHA-384"
158
+ },
159
+ namedCurve: "P-384"
160
+ };
161
+ case "ES512":
162
+ return {
163
+ name: "ECDSA",
164
+ hash: {
165
+ name: "SHA-512"
166
+ },
167
+ namedCurve: "P-521"
168
+ };
169
+ case "EdDSA":
170
+ return {
171
+ name: "Ed25519",
172
+ namedCurve: "Ed25519"
173
+ };
174
+ default:
175
+ throw new JwtAlgorithmNotImplemented(name);
176
+ }
177
+ }
178
+ function isCryptoKey(key) {
179
+ const runtime = getRuntimeKey();
180
+ if (runtime === "node" && !!crypto.webcrypto) {
181
+ return key instanceof crypto.webcrypto.CryptoKey;
182
+ }
183
+ return key instanceof CryptoKey;
184
+ }
185
+ export {
186
+ signing,
187
+ verifying
188
+ };
@@ -1,73 +1,39 @@
1
1
  // src/utils/jwt/jwt.ts
2
2
  import { encodeBase64Url, decodeBase64Url } from "../../utils/encode.js";
3
- import { JwtTokenIssuedAt } from "./types.js";
3
+ import { AlgorithmTypes } from "./jwa.js";
4
+ import { signing, verifying } from "./jws.js";
5
+ import { JwtHeaderInvalid } from "./types.js";
4
6
  import {
5
7
  JwtTokenInvalid,
6
8
  JwtTokenNotBefore,
7
9
  JwtTokenExpired,
8
10
  JwtTokenSignatureMismatched,
9
- JwtAlgorithmNotImplemented
11
+ JwtTokenIssuedAt
10
12
  } from "./types.js";
11
- var utf8Encoder = new TextEncoder();
12
- var utf8Decoder = new TextDecoder();
13
+ import { utf8Decoder, utf8Encoder } from "./utf8.js";
13
14
  var encodeJwtPart = (part) => encodeBase64Url(utf8Encoder.encode(JSON.stringify(part))).replace(/=/g, "");
14
15
  var encodeSignaturePart = (buf) => encodeBase64Url(buf).replace(/=/g, "");
15
16
  var decodeJwtPart = (part) => JSON.parse(utf8Decoder.decode(decodeBase64Url(part)));
16
- var param = (name) => {
17
- switch (name.toUpperCase()) {
18
- case "HS256":
19
- return {
20
- name: "HMAC",
21
- hash: {
22
- name: "SHA-256"
23
- }
24
- };
25
- case "HS384":
26
- return {
27
- name: "HMAC",
28
- hash: {
29
- name: "SHA-384"
30
- }
31
- };
32
- case "HS512":
33
- return {
34
- name: "HMAC",
35
- hash: {
36
- name: "SHA-512"
37
- }
38
- };
39
- default:
40
- throw new JwtAlgorithmNotImplemented(name);
41
- }
42
- };
43
- var signing = async (data, secret, alg = "HS256") => {
44
- if (!crypto.subtle || !crypto.subtle.importKey) {
45
- throw new Error("`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.");
46
- }
47
- const utf8Encoder2 = new TextEncoder();
48
- const cryptoKey = await crypto.subtle.importKey(
49
- "raw" /* RAW */,
50
- utf8Encoder2.encode(secret),
51
- param(alg),
52
- false,
53
- ["sign" /* Sign */]
54
- );
55
- return await crypto.subtle.sign(param(alg), cryptoKey, utf8Encoder2.encode(data));
56
- };
57
- var sign = async (payload, secret, alg = "HS256") => {
17
+ function isTokenHeader(obj) {
18
+ return typeof obj === "object" && obj !== null && "alg" in obj && Object.values(AlgorithmTypes).includes(obj.alg) && "typ" in obj && obj.typ === "JWT";
19
+ }
20
+ var sign = async (payload, privateKey, alg = "HS256") => {
58
21
  const encodedPayload = encodeJwtPart(payload);
59
22
  const encodedHeader = encodeJwtPart({ alg, typ: "JWT" });
60
23
  const partialToken = `${encodedHeader}.${encodedPayload}`;
61
- const signaturePart = await signing(partialToken, secret, alg);
24
+ const signaturePart = await signing(privateKey, alg, utf8Encoder.encode(partialToken));
62
25
  const signature = encodeSignaturePart(signaturePart);
63
26
  return `${partialToken}.${signature}`;
64
27
  };
65
- var verify = async (token, secret, alg = "HS256") => {
28
+ var verify = async (token, publicKey, alg = "HS256") => {
66
29
  const tokenParts = token.split(".");
67
30
  if (tokenParts.length !== 3) {
68
31
  throw new JwtTokenInvalid(token);
69
32
  }
70
- const { payload } = decode(token);
33
+ const { header, payload } = decode(token);
34
+ if (!isTokenHeader(header)) {
35
+ throw new JwtHeaderInvalid(header);
36
+ }
71
37
  const now = Math.floor(Date.now() / 1e3);
72
38
  if (payload.nbf && payload.nbf > now) {
73
39
  throw new JwtTokenNotBefore(token);
@@ -78,10 +44,14 @@ var verify = async (token, secret, alg = "HS256") => {
78
44
  if (payload.iat && now < payload.iat) {
79
45
  throw new JwtTokenIssuedAt(now, payload.iat);
80
46
  }
81
- const signaturePart = tokenParts.slice(0, 2).join(".");
82
- const signature = await signing(signaturePart, secret, alg);
83
- const encodedSignature = encodeSignaturePart(signature);
84
- if (encodedSignature !== tokenParts[2]) {
47
+ const headerPayload = token.substring(0, token.lastIndexOf("."));
48
+ const verified = await verifying(
49
+ publicKey,
50
+ alg,
51
+ decodeBase64Url(tokenParts[2]),
52
+ utf8Encoder.encode(headerPayload)
53
+ );
54
+ if (!verified) {
85
55
  throw new JwtTokenSignatureMismatched(token);
86
56
  }
87
57
  return payload;
@@ -101,6 +71,7 @@ var decode = (token) => {
101
71
  };
102
72
  export {
103
73
  decode,
74
+ isTokenHeader,
104
75
  sign,
105
76
  verify
106
77
  };
@@ -29,21 +29,33 @@ var JwtTokenIssuedAt = class extends Error {
29
29
  this.name = "JwtTokenIssuedAt";
30
30
  }
31
31
  };
32
+ var JwtHeaderInvalid = class extends Error {
33
+ constructor(header) {
34
+ super(`jwt header is invalid: ${JSON.stringify(header)}`);
35
+ this.name = "JwtHeaderInvalid";
36
+ }
37
+ };
32
38
  var JwtTokenSignatureMismatched = class extends Error {
33
39
  constructor(token) {
34
40
  super(`token(${token}) signature mismatched`);
35
41
  this.name = "JwtTokenSignatureMismatched";
36
42
  }
37
43
  };
38
- var AlgorithmTypes = /* @__PURE__ */ ((AlgorithmTypes2) => {
39
- AlgorithmTypes2["HS256"] = "HS256";
40
- AlgorithmTypes2["HS384"] = "HS384";
41
- AlgorithmTypes2["HS512"] = "HS512";
42
- return AlgorithmTypes2;
43
- })(AlgorithmTypes || {});
44
+ var CryptoKeyUsage = /* @__PURE__ */ ((CryptoKeyUsage2) => {
45
+ CryptoKeyUsage2["Encrypt"] = "encrypt";
46
+ CryptoKeyUsage2["Decrypt"] = "decrypt";
47
+ CryptoKeyUsage2["Sign"] = "sign";
48
+ CryptoKeyUsage2["Verify"] = "verify";
49
+ CryptoKeyUsage2["DeriveKey"] = "deriveKey";
50
+ CryptoKeyUsage2["DeriveBits"] = "deriveBits";
51
+ CryptoKeyUsage2["WrapKey"] = "wrapKey";
52
+ CryptoKeyUsage2["UnwrapKey"] = "unwrapKey";
53
+ return CryptoKeyUsage2;
54
+ })(CryptoKeyUsage || {});
44
55
  export {
45
- AlgorithmTypes,
56
+ CryptoKeyUsage,
46
57
  JwtAlgorithmNotImplemented,
58
+ JwtHeaderInvalid,
47
59
  JwtTokenExpired,
48
60
  JwtTokenInvalid,
49
61
  JwtTokenIssuedAt,
@@ -0,0 +1,7 @@
1
+ // src/utils/jwt/utf8.ts
2
+ var utf8Encoder = new TextEncoder();
3
+ var utf8Decoder = new TextDecoder();
4
+ export {
5
+ utf8Decoder,
6
+ utf8Encoder
7
+ };
@@ -6,30 +6,14 @@ var validator = (target, validationFunc) => {
6
6
  return async (c, next) => {
7
7
  let value = {};
8
8
  const contentType = c.req.header("Content-Type");
9
- const bodyTypes = ["text", "arrayBuffer", "blob"];
10
9
  switch (target) {
11
10
  case "json":
12
11
  if (!contentType || !contentType.startsWith("application/json")) {
13
12
  const message = `Invalid HTTP header: Content-Type=${contentType}`;
14
13
  throw new HTTPException(400, { message });
15
14
  }
16
- if (c.req.bodyCache.json) {
17
- value = await c.req.bodyCache.json;
18
- break;
19
- }
20
15
  try {
21
- let arrayBuffer = void 0;
22
- for (const type of bodyTypes) {
23
- const body = c.req.bodyCache[type];
24
- if (body) {
25
- arrayBuffer = await new Response(await body).arrayBuffer();
26
- break;
27
- }
28
- }
29
- arrayBuffer ??= await c.req.raw.arrayBuffer();
30
- value = await new Response(arrayBuffer).json();
31
- c.req.bodyCache.json = value;
32
- c.req.bodyCache.arrayBuffer = arrayBuffer;
16
+ value = await c.req.json();
33
17
  } catch {
34
18
  const message = "Malformed JSON in request body";
35
19
  throw new HTTPException(400, { message });
@@ -44,15 +28,7 @@ var validator = (target, validationFunc) => {
44
28
  break;
45
29
  }
46
30
  try {
47
- let arrayBuffer = void 0;
48
- for (const type of bodyTypes) {
49
- const body = c.req.bodyCache[type];
50
- if (body) {
51
- arrayBuffer = await new Response(await body).arrayBuffer();
52
- break;
53
- }
54
- }
55
- arrayBuffer ??= await c.req.arrayBuffer();
31
+ const arrayBuffer = await c.req.arrayBuffer();
56
32
  const formData = await bufferToFormData(arrayBuffer, contentType);
57
33
  const form = {};
58
34
  formData.forEach((value2, key) => {
@@ -60,7 +36,6 @@ var validator = (target, validationFunc) => {
60
36
  });
61
37
  value = form;
62
38
  c.req.bodyCache.formData = formData;
63
- c.req.bodyCache.arrayBuffer = arrayBuffer;
64
39
  } catch (e) {
65
40
  let message = "Malformed FormData request.";
66
41
  message += e instanceof Error ? ` ${e.message}` : ` ${String(e)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.1.7",
3
+ "version": "4.2.0",
4
4
  "description": "Ultrafast web framework for the Edges",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -114,6 +114,11 @@
114
114
  "import": "./dist/middleware/etag/index.js",
115
115
  "require": "./dist/cjs/middleware/etag/index.js"
116
116
  },
117
+ "./trailing-slash": {
118
+ "types": "./dist/types/middleware/trailing-slash/index.d.ts",
119
+ "import": "./dist/middleware/trailing-slash/index.js",
120
+ "require": "./dist/cjs/middleware/trailing-slash/index.js"
121
+ },
117
122
  "./html": {
118
123
  "types": "./dist/types/helper/html/index.d.ts",
119
124
  "import": "./dist/helper/html/index.js",
@@ -184,6 +189,11 @@
184
189
  "import": "./dist/middleware/logger/index.js",
185
190
  "require": "./dist/cjs/middleware/logger/index.js"
186
191
  },
192
+ "./method-override": {
193
+ "types": "./dist/types/middleware/method-override/index.d.ts",
194
+ "import": "./dist/middleware/method-override/index.js",
195
+ "require": "./dist/cjs/middleware/method-override/index.js"
196
+ },
187
197
  "./powered-by": {
188
198
  "types": "./dist/types/middleware/powered-by/index.d.ts",
189
199
  "import": "./dist/middleware/powered-by/index.js",
@@ -408,6 +418,9 @@
408
418
  "logger": [
409
419
  "./dist/types/middleware/logger"
410
420
  ],
421
+ "method-override": [
422
+ "./dist/types/middleware/method-override"
423
+ ],
411
424
  "powered-by": [
412
425
  "./dist/types/middleware/powered-by"
413
426
  ],