hono 4.11.7 → 4.12.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 (56) hide show
  1. package/dist/adapter/aws-lambda/conninfo.js +23 -0
  2. package/dist/adapter/aws-lambda/index.js +2 -0
  3. package/dist/adapter/cloudflare-pages/conninfo.js +9 -0
  4. package/dist/adapter/cloudflare-pages/index.js +2 -0
  5. package/dist/adapter/netlify/conninfo.js +9 -0
  6. package/dist/adapter/netlify/mod.js +2 -0
  7. package/dist/cjs/adapter/aws-lambda/conninfo.js +46 -0
  8. package/dist/cjs/adapter/aws-lambda/index.js +3 -0
  9. package/dist/cjs/adapter/cloudflare-pages/conninfo.js +32 -0
  10. package/dist/cjs/adapter/cloudflare-pages/index.js +3 -0
  11. package/dist/cjs/adapter/netlify/conninfo.js +32 -0
  12. package/dist/cjs/adapter/netlify/mod.js +3 -0
  13. package/dist/cjs/client/client.js +8 -2
  14. package/dist/cjs/context.js +11 -7
  15. package/dist/cjs/helper/ssg/index.js +5 -0
  16. package/dist/cjs/helper/ssg/plugins.js +71 -0
  17. package/dist/cjs/helper/ssg/ssg.js +2 -11
  18. package/dist/cjs/jsx/context.js +4 -2
  19. package/dist/cjs/jsx/dom/render.js +7 -9
  20. package/dist/cjs/middleware/basic-auth/index.js +6 -0
  21. package/dist/cjs/middleware/bearer-auth/index.js +1 -1
  22. package/dist/cjs/middleware/language/language.js +13 -2
  23. package/dist/cjs/middleware/trailing-slash/index.js +18 -4
  24. package/dist/cjs/router/trie-router/node.js +33 -16
  25. package/dist/cjs/utils/buffer.js +29 -2
  26. package/dist/cjs/utils/url.js +4 -2
  27. package/dist/client/client.js +8 -2
  28. package/dist/context.js +11 -7
  29. package/dist/helper/ssg/index.js +3 -0
  30. package/dist/helper/ssg/plugins.js +47 -0
  31. package/dist/helper/ssg/ssg.js +2 -10
  32. package/dist/jsx/context.js +4 -2
  33. package/dist/jsx/dom/render.js +7 -9
  34. package/dist/middleware/basic-auth/index.js +6 -0
  35. package/dist/middleware/bearer-auth/index.js +1 -1
  36. package/dist/middleware/language/language.js +13 -2
  37. package/dist/middleware/trailing-slash/index.js +18 -4
  38. package/dist/router/trie-router/node.js +33 -16
  39. package/dist/types/adapter/aws-lambda/conninfo.d.ts +27 -0
  40. package/dist/types/adapter/aws-lambda/index.d.ts +1 -0
  41. package/dist/types/adapter/cloudflare-pages/conninfo.d.ts +21 -0
  42. package/dist/types/adapter/cloudflare-pages/index.d.ts +1 -0
  43. package/dist/types/adapter/netlify/conninfo.d.ts +21 -0
  44. package/dist/types/adapter/netlify/mod.d.ts +1 -0
  45. package/dist/types/client/types.d.ts +39 -1
  46. package/dist/types/context.d.ts +4 -0
  47. package/dist/types/helper/ssg/index.d.ts +1 -0
  48. package/dist/types/helper/ssg/plugins.d.ts +27 -0
  49. package/dist/types/helper/ssg/ssg.d.ts +0 -8
  50. package/dist/types/middleware/basic-auth/index.d.ts +19 -0
  51. package/dist/types/middleware/jwt/jwt.d.ts +1 -1
  52. package/dist/types/middleware/trailing-slash/index.d.ts +43 -2
  53. package/dist/types/utils/buffer.d.ts +10 -1
  54. package/dist/utils/buffer.js +29 -2
  55. package/dist/utils/url.js +4 -2
  56. package/package.json +2 -2
@@ -3,5 +3,6 @@
3
3
  * AWS Lambda Adapter for Hono.
4
4
  */
5
5
  export { handle, streamHandle, defaultIsContentTypeBinary } from './handler';
6
+ export { getConnInfo } from './conninfo';
6
7
  export type { APIGatewayProxyResult, LambdaEvent } from './handler';
7
8
  export type { ApiGatewayRequestContext, ApiGatewayRequestContextV2, ALBRequestContext, LambdaContext, } from './types';
@@ -0,0 +1,21 @@
1
+ import type { GetConnInfo } from '../../helper/conninfo';
2
+ /**
3
+ * Get connection information from Cloudflare Pages
4
+ * @param c - Context
5
+ * @returns Connection information including remote address
6
+ * @example
7
+ * ```ts
8
+ * import { Hono } from 'hono'
9
+ * import { handle, getConnInfo } from 'hono/cloudflare-pages'
10
+ *
11
+ * const app = new Hono()
12
+ *
13
+ * app.get('/', (c) => {
14
+ * const info = getConnInfo(c)
15
+ * return c.text(`Your IP: ${info.remote.address}`)
16
+ * })
17
+ *
18
+ * export const onRequest = handle(app)
19
+ * ```
20
+ */
21
+ export declare const getConnInfo: GetConnInfo;
@@ -3,4 +3,5 @@
3
3
  * Cloudflare Pages Adapter for Hono.
4
4
  */
5
5
  export { handle, handleMiddleware, serveStatic } from './handler';
6
+ export { getConnInfo } from './conninfo';
6
7
  export type { EventContext } from './handler';
@@ -0,0 +1,21 @@
1
+ import type { GetConnInfo } from '../../helper/conninfo';
2
+ /**
3
+ * Get connection information from Netlify
4
+ * @param c - Context
5
+ * @returns Connection information including remote address
6
+ * @example
7
+ * ```ts
8
+ * import { Hono } from 'hono'
9
+ * import { handle, getConnInfo } from 'hono/netlify'
10
+ *
11
+ * const app = new Hono()
12
+ *
13
+ * app.get('/', (c) => {
14
+ * const info = getConnInfo(c)
15
+ * return c.text(`Your IP: ${info.remote.address}`)
16
+ * })
17
+ *
18
+ * export default handle(app)
19
+ * ```
20
+ */
21
+ export declare const getConnInfo: GetConnInfo;
@@ -1 +1,2 @@
1
1
  export { handle } from './handler';
2
+ export { getConnInfo } from './conninfo';
@@ -1,7 +1,7 @@
1
1
  import type { Hono } from '../hono';
2
2
  import type { HonoBase } from '../hono-base';
3
3
  import type { METHODS, METHOD_NAME_ALL_LOWERCASE } from '../router';
4
- import type { Endpoint, ResponseFormat, Schema } from '../types';
4
+ import type { Endpoint, KnownResponseFormat, ResponseFormat, Schema } from '../types';
5
5
  import type { StatusCode, SuccessStatusCode } from '../utils/http-status';
6
6
  import type { HasRequiredKeys } from '../utils/types';
7
7
  /**
@@ -74,6 +74,22 @@ export type ClientRequest<Prefix extends string, Path extends string, S extends
74
74
  } ? {
75
75
  query: Q;
76
76
  } : {} : {}) | undefined = undefined>(arg?: Arg) => HonoURL<Prefix, Path, Arg>;
77
+ $path: <const Arg extends (S[keyof S] extends {
78
+ input: infer R;
79
+ } ? R extends {
80
+ param: infer P;
81
+ } ? R extends {
82
+ query: infer Q;
83
+ } ? {
84
+ param: P;
85
+ query: Q;
86
+ } : {
87
+ param: P;
88
+ } : R extends {
89
+ query: infer Q;
90
+ } ? {
91
+ query: Q;
92
+ } : {} : {}) | undefined = undefined>(arg?: Arg) => BuildPath<Path, Arg>;
77
93
  } & (S['$get'] extends {
78
94
  outputFormat: 'ws';
79
95
  } ? S['$get'] extends {
@@ -108,6 +124,7 @@ type BuildSearch<Arg, Key extends 'query'> = Arg extends {
108
124
  type BuildPathname<P extends string, Arg> = Arg extends {
109
125
  param: infer Param;
110
126
  } ? `${ApplyParam<TrimStartSlash<P>, Param>}` : `/${TrimStartSlash<P>}`;
127
+ type BuildPath<P extends string, Arg> = `${BuildPathname<P, Arg>}${BuildSearch<Arg, 'query'>}`;
111
128
  type BuildTypedURL<Protocol extends string, Host extends string, Port extends string, P extends string, Arg> = TypedURL<`${Protocol}:`, Host, Port, BuildPathname<P, Arg>, BuildSearch<Arg, 'query'>>;
112
129
  type HonoURL<Prefix extends string, Path extends string, Arg> = IsLiteral<Prefix> extends true ? TrimEndSlash<Prefix> extends `${infer Protocol}://${infer Rest}` ? Rest extends `${infer Hostname}/${infer P}` ? ParseHostName<Hostname> extends [infer Host extends string, infer Port extends string] ? BuildTypedURL<Protocol, Host, Port, P, Arg> : never : ParseHostName<Rest> extends [infer Host extends string, infer Port extends string] ? BuildTypedURL<Protocol, Host, Port, Path, Arg> : never : URL : URL;
113
130
  type ParseHostName<T extends string> = T extends `${infer Host}:${infer Port}` ? [Host, Port] : [T, ''];
@@ -165,4 +182,25 @@ interface CallbackOptions {
165
182
  export type ObjectType<T = unknown> = {
166
183
  [key: string]: T;
167
184
  };
185
+ type GlobalResponseDefinition = {
186
+ [S in StatusCode]?: {
187
+ [F in KnownResponseFormat]?: unknown;
188
+ };
189
+ };
190
+ type ToEndpoints<Def extends GlobalResponseDefinition, R> = {
191
+ [S in keyof Def & StatusCode]: {
192
+ [F in keyof Def[S] & KnownResponseFormat]: Omit<R, 'output' | 'status' | 'outputFormat'> & {
193
+ output: Def[S][F];
194
+ status: S;
195
+ outputFormat: F;
196
+ };
197
+ }[keyof Def[S] & KnownResponseFormat];
198
+ }[keyof Def & StatusCode];
199
+ type ModRoute<R, Def extends GlobalResponseDefinition> = R extends Endpoint ? R | ToEndpoints<Def, R> : R;
200
+ type ModSchema<D, Def extends GlobalResponseDefinition> = {
201
+ [K in keyof D]: {
202
+ [M in keyof D[K]]: ModRoute<D[K][M], Def>;
203
+ };
204
+ };
205
+ export type ApplyGlobalResponse<App, Def extends GlobalResponseDefinition> = App extends HonoBase<infer E, infer D extends Schema, infer B> ? Hono<E, ModSchema<D, Def> extends Schema ? ModSchema<D, Def> : never, B> : never;
168
206
  export {};
@@ -28,6 +28,10 @@ export interface ExecutionContext {
28
28
  * For compatibility with Wrangler 4.x.
29
29
  */
30
30
  props: any;
31
+ /**
32
+ * For compatibility with Wrangler 4.x.
33
+ */
34
+ exports?: any;
31
35
  }
32
36
  /**
33
37
  * Interface for context variable mapping.
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export * from './ssg';
6
6
  export { X_HONO_DISABLE_SSG_HEADER_KEY, ssgParams, isSSGContext, disableSSG, onlySSG, } from './middleware';
7
+ export { defaultPlugin, redirectPlugin } from './plugins';
@@ -0,0 +1,27 @@
1
+ import type { SSGPlugin } from './ssg';
2
+ /**
3
+ * The default plugin that defines the recommended behavior.
4
+ *
5
+ * @experimental
6
+ * `defaultPlugin` is an experimental feature.
7
+ * The API might be changed.
8
+ */
9
+ export declare const defaultPlugin: () => SSGPlugin;
10
+ /**
11
+ * The redirect plugin that generates HTML redirect pages for HTTP redirect responses for status codes 301, 302, 303, 307 and 308.
12
+ *
13
+ * When used with `defaultPlugin`, place `redirectPlugin` before it, because `defaultPlugin` skips non-200 responses.
14
+ *
15
+ * ```ts
16
+ * // ✅ Will work as expected
17
+ * toSSG(app, fs, { plugins: [redirectPlugin(), defaultPlugin()] })
18
+ *
19
+ * // ❌ Will not work as expected
20
+ * toSSG(app, fs, { plugins: [defaultPlugin(), redirectPlugin()] })
21
+ * ```
22
+ *
23
+ * @experimental
24
+ * `redirectPlugin` is an experimental feature.
25
+ * The API might be changed.
26
+ */
27
+ export declare const redirectPlugin: () => SSGPlugin;
@@ -83,14 +83,6 @@ export interface ToSSGInterface {
83
83
  export interface ToSSGAdaptorInterface<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> {
84
84
  (app: Hono<E, S, BasePath>, options?: ToSSGOptions): Promise<ToSSGResult>;
85
85
  }
86
- /**
87
- * The default plugin that defines the recommended behavior.
88
- *
89
- * @experimental
90
- * `defaultPlugin` is an experimental feature.
91
- * The API might be changed.
92
- */
93
- export declare const defaultPlugin: SSGPlugin;
94
86
  /**
95
87
  * @experimental
96
88
  * `toSSG` is an experimental feature.
@@ -11,11 +11,13 @@ type BasicAuthOptions = {
11
11
  realm?: string;
12
12
  hashFunction?: Function;
13
13
  invalidUserMessage?: string | object | MessageFunction;
14
+ onAuthSuccess?: (c: Context, username: string) => void | Promise<void>;
14
15
  } | {
15
16
  verifyUser: (username: string, password: string, c: Context) => boolean | Promise<boolean>;
16
17
  realm?: string;
17
18
  hashFunction?: Function;
18
19
  invalidUserMessage?: string | object | MessageFunction;
20
+ onAuthSuccess?: (c: Context, username: string) => void | Promise<void>;
19
21
  };
20
22
  /**
21
23
  * Basic Auth Middleware for Hono.
@@ -29,6 +31,7 @@ type BasicAuthOptions = {
29
31
  * @param {Function} [options.hashFunction] - The hash function used for secure comparison.
30
32
  * @param {Function} [options.verifyUser] - The function to verify user credentials.
31
33
  * @param {string | object | MessageFunction} [options.invalidUserMessage="Unauthorized"] - The invalid user message.
34
+ * @param {Function} [options.onAuthSuccess] - Callback function called on successful authentication.
32
35
  * @returns {MiddlewareHandler} The middleware handler function.
33
36
  * @throws {HTTPException} If neither "username and password" nor "verifyUser" options are provided.
34
37
  *
@@ -48,6 +51,22 @@ type BasicAuthOptions = {
48
51
  * return c.text('You are authorized')
49
52
  * })
50
53
  * ```
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * // With onAuthSuccess callback
58
+ * app.use(
59
+ * '/auth/*',
60
+ * basicAuth({
61
+ * username: 'hono',
62
+ * password: 'ahotproject',
63
+ * onAuthSuccess: (c, username) => {
64
+ * c.set('user', { name: username, role: 'admin' })
65
+ * console.log(`User ${username} authenticated`)
66
+ * },
67
+ * })
68
+ * )
69
+ * ```
51
70
  */
52
71
  export declare const basicAuth: (options: BasicAuthOptions, ...users: {
53
72
  username: string;
@@ -17,7 +17,7 @@ export type JwtVariables<T = any> = {
17
17
  * @see {@link https://hono.dev/docs/middleware/builtin/jwt}
18
18
  *
19
19
  * @param {object} options - The options for the JWT middleware.
20
- * @param {SignatureKey} [options.secret] - A value of your secret key.
20
+ * @param {SignatureKey} options.secret - A value of your secret key.
21
21
  * @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.
22
22
  * @param {SignatureAlgorithm} options.alg - An algorithm type that is used for verifying (required). Available types are `HS256` | `HS384` | `HS512` | `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA`.
23
23
  * @param {string} [options.headerName='Authorization'] - The name of the header to look for the JWT token. Default is 'Authorization'.
@@ -3,11 +3,22 @@
3
3
  * Trailing Slash Middleware for Hono.
4
4
  */
5
5
  import type { MiddlewareHandler } from '../../types';
6
+ type TrimTrailingSlashOptions = {
7
+ /**
8
+ * If `true`, the middleware will always redirect requests with a trailing slash
9
+ * before executing handlers.
10
+ * This is useful for routes with wildcards (`*`).
11
+ * If `false` (default), it will only redirect when the route is not found (404).
12
+ * @default false
13
+ */
14
+ alwaysRedirect?: boolean;
15
+ };
6
16
  /**
7
17
  * Trailing Slash Middleware for Hono.
8
18
  *
9
19
  * @see {@link https://hono.dev/docs/middleware/builtin/trailing-slash}
10
20
  *
21
+ * @param {TrimTrailingSlashOptions} options - The options for the middleware.
11
22
  * @returns {MiddlewareHandler} The middleware handler function.
12
23
  *
13
24
  * @example
@@ -17,14 +28,34 @@ import type { MiddlewareHandler } from '../../types';
17
28
  * app.use(trimTrailingSlash())
18
29
  * app.get('/about/me/', (c) => c.text('With Trailing Slash'))
19
30
  * ```
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * // With alwaysRedirect option for wildcard routes
35
+ * const app = new Hono()
36
+ *
37
+ * app.use(trimTrailingSlash({ alwaysRedirect: true }))
38
+ * app.get('/my-path/*', (c) => c.text('Wildcard route'))
39
+ * ```
20
40
  */
21
- export declare const trimTrailingSlash: () => MiddlewareHandler;
41
+ export declare const trimTrailingSlash: (options?: TrimTrailingSlashOptions) => MiddlewareHandler;
42
+ type AppendTrailingSlashOptions = {
43
+ /**
44
+ * If `true`, the middleware will always redirect requests without a trailing slash
45
+ * before executing handlers.
46
+ * This is useful for routes with wildcards (`*`).
47
+ * If `false` (default), it will only redirect when the route is not found (404).
48
+ * @default false
49
+ */
50
+ alwaysRedirect?: boolean;
51
+ };
22
52
  /**
23
53
  * Append trailing slash middleware for Hono.
24
54
  * Append a trailing slash to the URL if it doesn't have one. For example, `/path/to/page` will be redirected to `/path/to/page/`.
25
55
  *
26
56
  * @see {@link https://hono.dev/docs/middleware/builtin/trailing-slash}
27
57
  *
58
+ * @param {AppendTrailingSlashOptions} options - The options for the middleware.
28
59
  * @returns {MiddlewareHandler} The middleware handler function.
29
60
  *
30
61
  * @example
@@ -33,5 +64,15 @@ export declare const trimTrailingSlash: () => MiddlewareHandler;
33
64
  *
34
65
  * app.use(appendTrailingSlash())
35
66
  * ```
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * // With alwaysRedirect option for wildcard routes
71
+ * const app = new Hono()
72
+ *
73
+ * app.use(appendTrailingSlash({ alwaysRedirect: true }))
74
+ * app.get('/my-path/*', (c) => c.text('Wildcard route'))
75
+ * ```
36
76
  */
37
- export declare const appendTrailingSlash: () => MiddlewareHandler;
77
+ export declare const appendTrailingSlash: (options?: AppendTrailingSlashOptions) => MiddlewareHandler;
78
+ export {};
@@ -3,6 +3,15 @@
3
3
  * Buffer utility.
4
4
  */
5
5
  export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
6
- export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
6
+ type StringHashFunction = (input: string) => string | null | Promise<string | null>;
7
+ type TimingSafeEqual = {
8
+ (a: string, b: string, hashFunction?: StringHashFunction): Promise<boolean>;
9
+ /**
10
+ * @deprecated object and boolean signatures that take boolean as first and second arguments, and functions with signatures that take non-string arguments have been deprecated
11
+ */
12
+ (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function): Promise<boolean>;
13
+ };
14
+ export declare const timingSafeEqual: TimingSafeEqual;
7
15
  export declare const bufferToString: (buffer: ArrayBuffer) => string;
8
16
  export declare const bufferToFormData: (arrayBuffer: ArrayBuffer, contentType: string) => Promise<FormData>;
17
+ export {};
@@ -17,15 +17,42 @@ var equal = (a, b) => {
17
17
  }
18
18
  return true;
19
19
  };
20
+ var constantTimeEqualString = (a, b) => {
21
+ const aLen = a.length;
22
+ const bLen = b.length;
23
+ const maxLen = Math.max(aLen, bLen);
24
+ let out = aLen ^ bLen;
25
+ for (let i = 0; i < maxLen; i++) {
26
+ const aChar = i < aLen ? a.charCodeAt(i) : 0;
27
+ const bChar = i < bLen ? b.charCodeAt(i) : 0;
28
+ out |= aChar ^ bChar;
29
+ }
30
+ return out === 0;
31
+ };
32
+ var timingSafeEqualString = async (a, b, hashFunction) => {
33
+ if (!hashFunction) {
34
+ hashFunction = sha256;
35
+ }
36
+ const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]);
37
+ if (sa == null || sb == null || typeof sa !== "string" || typeof sb !== "string") {
38
+ return false;
39
+ }
40
+ const hashEqual = constantTimeEqualString(sa, sb);
41
+ const originalEqual = constantTimeEqualString(a, b);
42
+ return hashEqual && originalEqual;
43
+ };
20
44
  var timingSafeEqual = async (a, b, hashFunction) => {
45
+ if (typeof a === "string" && typeof b === "string") {
46
+ return timingSafeEqualString(a, b, hashFunction);
47
+ }
21
48
  if (!hashFunction) {
22
49
  hashFunction = sha256;
23
50
  }
24
51
  const [sa, sb] = await Promise.all([hashFunction(a), hashFunction(b)]);
25
- if (!sa || !sb) {
52
+ if (!sa || !sb || typeof sa !== "string" || typeof sb !== "string") {
26
53
  return false;
27
54
  }
28
- return sa === sb && a === b;
55
+ return timingSafeEqualString(sa, sb);
29
56
  };
30
57
  var bufferToString = (buffer) => {
31
58
  if (buffer instanceof ArrayBuffer) {
package/dist/utils/url.js CHANGED
@@ -73,9 +73,11 @@ var getPath = (request) => {
73
73
  const charCode = url.charCodeAt(i);
74
74
  if (charCode === 37) {
75
75
  const queryIndex = url.indexOf("?", i);
76
- const path = url.slice(start, queryIndex === -1 ? void 0 : queryIndex);
76
+ const hashIndex = url.indexOf("#", i);
77
+ const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);
78
+ const path = url.slice(start, end);
77
79
  return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path);
78
- } else if (charCode === 63) {
80
+ } else if (charCode === 63 || charCode === 35) {
79
81
  break;
80
82
  }
81
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "4.11.7",
3
+ "version": "4.12.0",
4
4
  "description": "Web framework built on Web Standards",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -661,7 +661,7 @@
661
661
  "@types/glob": "^9.0.0",
662
662
  "@types/jsdom": "^21.1.7",
663
663
  "@types/node": "^24.3.0",
664
- "@typescript/native-preview": "7.0.0-dev.20251220.1",
664
+ "@typescript/native-preview": "7.0.0-dev.20260210.1",
665
665
  "@vitest/coverage-v8": "^3.2.4",
666
666
  "arg": "^5.0.2",
667
667
  "bun-types": "^1.2.20",