@okay-e9g/hono-config 0.0.2 → 0.0.4

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.
package/package.json CHANGED
@@ -37,5 +37,5 @@
37
37
  "prepublishOnly": "bun run typecheck && bun test",
38
38
  "typecheck": "tsc --noEmit"
39
39
  },
40
- "version": "0.0.2"
40
+ "version": "0.0.4"
41
41
  }
@@ -10,11 +10,17 @@ import { statusText } from '../schemas';
10
10
  /**
11
11
  * Creates the shared 404 handler used by the configured Hono factories.
12
12
  *
13
+ * Falls back to a plain text 404 when no response helper has been installed.
14
+ *
13
15
  * @internal
14
16
  */
15
17
  export const notFound = <T extends Env>(): NotFoundHandler<T> => {
16
18
  return (c) => {
17
- const { response } = c.var as ResponseEnv['Variables'];
19
+ const { response } = c.var as Partial<ResponseEnv['Variables']>;
20
+
21
+ if (response === undefined) {
22
+ return c.text(statusText[404], 404);
23
+ }
18
24
 
19
25
  if (response.type === 'api') {
20
26
  return c.json(response.error(404), 404);
@@ -7,16 +7,32 @@ import type { Env, ErrorHandler } from 'hono/types';
7
7
  import { z } from 'zod';
8
8
 
9
9
  import type { ResponseEnv } from '../middlewares/ensure-rt';
10
- import { type ClientErrorStatusCode, type ServerErrorStatusCode, type StatusCode, statusText } from '../schemas';
10
+ import { type ClientErrorStatusCode, type ServerErrorStatusCode, statusText } from '../schemas';
11
+
12
+ interface TypedHTTPException extends Omit<HTTPException, 'status'> {
13
+ status: ClientErrorStatusCode & ServerErrorStatusCode;
14
+ }
15
+
16
+ const assertHTTPException = (err: Error): err is TypedHTTPException => {
17
+ return err instanceof HTTPException || (err instanceof Error && 'getResponse' in err && 'status' in err);
18
+ };
11
19
 
12
20
  /**
13
21
  * Creates the shared error handler used by the configured Hono factories.
14
22
  *
23
+ * Errors are rethrown when no response helper has been installed, allowing
24
+ * Hono's fallback error handling to handle apps that are not using this
25
+ * package's response envelopes.
26
+ *
15
27
  * @internal
16
28
  */
17
29
  export const onError = <T extends Env>(): ErrorHandler<T> => {
18
30
  return (err, c) => {
19
- const { response } = c.var as ResponseEnv['Variables'];
31
+ const { response } = c.var as Partial<ResponseEnv['Variables']>;
32
+
33
+ if (response === undefined) {
34
+ throw err;
35
+ }
20
36
 
21
37
  if (err instanceof z.ZodError) {
22
38
  if (response.type === 'api') {
@@ -26,14 +42,14 @@ export const onError = <T extends Env>(): ErrorHandler<T> => {
26
42
  return c.json(response.error(z.flattenError(err)));
27
43
  }
28
44
 
29
- if (err instanceof HTTPException) {
45
+ if (assertHTTPException(err)) {
30
46
  const { message, status } = err;
31
47
 
32
48
  if (response.type === 'api') {
33
- return c.json(response.error(status as ClientErrorStatusCode | ServerErrorStatusCode, message), status);
49
+ return c.json(response.error(status, message), status);
34
50
  }
35
51
 
36
- return c.json(response.error(message === '' ? statusText[status as StatusCode] : message));
52
+ return c.json(response.error(message === '' ? statusText[status] : message));
37
53
  }
38
54
 
39
55
  const errMessage = err instanceof Error ? err.message : err;
@@ -30,7 +30,7 @@ export type OpenAPIHonoFactory<T extends Env> = Omit<Factory<T>, 'createApp'> &
30
30
  *
31
31
  * The generated apps use non-strict routing, throw validation failures from the
32
32
  * OpenAPI default hook, install the shared not-found and error handlers, enable
33
- * SuperJSON response serialization, apply any provided application middleware,
33
+ * SuperJSON response serialization, apply application middleware when present,
34
34
  * and finish with the response-type guard.
35
35
  *
36
36
  * Include exactly one response-type middleware, such as `apiResponseType()` or
@@ -61,9 +61,12 @@ export const createOpenAPIHonoFactory = <T extends Env>({
61
61
 
62
62
  app.notFound(notFoundHandler());
63
63
  app.onError(onErrorHandler());
64
-
65
64
  app.use(superjson());
66
- app.use(...appMiddlewares);
65
+
66
+ if (appMiddlewares.length) {
67
+ app.use(...appMiddlewares);
68
+ }
69
+
67
70
  app.use(ensureResponseType());
68
71
 
69
72
  return app;
@@ -36,8 +36,8 @@ export type CreateHonoFactoryOpts<T extends Env> = {
36
36
  *
37
37
  * The generated apps use non-strict routing, the shared not-found and error
38
38
  * handlers, SuperJSON response serialization, any provided application
39
- * middleware, and the response-type guard used by `apiResponseType` and
40
- * `rpcResponseType`.
39
+ * middleware when present, and the response-type guard used by
40
+ * `apiResponseType` and `rpcResponseType`.
41
41
  *
42
42
  * Include exactly one response-type middleware, such as `apiResponseType()` or
43
43
  * `rpcResponseType()`, in `appMiddlewares` so the shared handlers can build the
@@ -59,9 +59,12 @@ export const createHonoFactory = <T extends Env>({
59
59
  initApp: (app) => {
60
60
  app.notFound(notFoundHandler());
61
61
  app.onError(onErrorHandler());
62
-
63
62
  app.use(superjson());
64
- app.use(...appMiddlewares);
63
+
64
+ if (appMiddlewares.length) {
65
+ app.use(...appMiddlewares);
66
+ }
67
+
65
68
  app.use(ensureResponseType());
66
69
  },
67
70
  });
@@ -33,12 +33,6 @@ type ErrorStatusCode = ClientErrorStatusCode | ServerErrorStatusCode;
33
33
 
34
34
  type ResultStatusCode = Exclude<SuccessStatusCode, 204 | 205>;
35
35
 
36
- const assertResponseTypeNotInitialized = (response: unknown): void => {
37
- if (response !== undefined) {
38
- throw new Error('Response type middleware already initialized.');
39
- }
40
- };
41
-
42
36
  /**
43
37
  * Hono environment contract added by `apiResponseType`.
44
38
  */
@@ -71,29 +65,31 @@ export interface ApiResponseEnv extends Env {
71
65
  * Installs `c.var.response` helpers for conventional HTTP API responses.
72
66
  *
73
67
  * The helpers produce `{ success: false, error }` and `{ success: true, result }`
74
- * envelopes validated by the exported API schemas.
68
+ * envelopes validated by the exported API schemas. If another response helper is
69
+ * already installed, the existing helper is kept so applications can compose
70
+ * middleware safely.
75
71
  *
76
72
  * @returns Hono middleware that adds the API response helper contract.
77
73
  */
78
74
  export const apiResponseType = (): MiddlewareHandler<ApiResponseEnv> => {
79
75
  return createMiddleware(async (c, next) => {
80
- assertResponseTypeNotInitialized(c.var.response);
81
-
82
- c.set('response', {
83
- error: <T, U extends ErrorStatusCode>(status: U, error?: T) => {
84
- return apiError.parse({
85
- error: error === undefined || error === '' ? statusText[status] : error,
86
- success: false,
87
- }) as TypedApiError<T>;
88
- },
89
- result: <T, U extends ResultStatusCode>(status: U, result?: T) => {
90
- return apiResult.parse({
91
- result: result === undefined || result === '' ? statusText[status] : result,
92
- success: true,
93
- }) as TypedApiResult<T>;
94
- },
95
- type: 'api',
96
- });
76
+ if (c.var.response === undefined) {
77
+ c.set('response', {
78
+ error: <T, U extends ErrorStatusCode>(status: U, error?: T) => {
79
+ return apiError.parse({
80
+ error: error === undefined || error === '' ? statusText[status] : error,
81
+ success: false,
82
+ }) as TypedApiError<T>;
83
+ },
84
+ result: <T, U extends ResultStatusCode>(status: U, result?: T) => {
85
+ return apiResult.parse({
86
+ result: result === undefined || result === '' ? statusText[status] : result,
87
+ success: true,
88
+ }) as TypedApiResult<T>;
89
+ },
90
+ type: 'api',
91
+ });
92
+ }
97
93
 
98
94
  await next();
99
95
  });
@@ -136,23 +132,25 @@ export interface RpcResponseEnv extends Env {
136
132
  * Installs `c.var.response` helpers for JSON-RPC 2.0 responses.
137
133
  *
138
134
  * The helpers produce `error` and `result` envelopes validated by the exported
139
- * JSON-RPC schemas and default missing ids to the schema default.
135
+ * JSON-RPC schemas and default missing ids to the schema default. If another
136
+ * response helper is already installed, the existing helper is kept so
137
+ * applications can compose middleware safely.
140
138
  *
141
139
  * @returns Hono middleware that adds the RPC response helper contract.
142
140
  */
143
141
  export const rpcResponseType = (): MiddlewareHandler<RpcResponseEnv> => {
144
142
  return createMiddleware(async (c, next) => {
145
- assertResponseTypeNotInitialized(c.var.response);
146
-
147
- c.set('response', {
148
- error: <T>(error?: T, id?: JsonRpc['id']) => {
149
- return rpcError.parse({ error, id }) as TypedRpcError<T>;
150
- },
151
- result: <T>(result?: T, id?: JsonRpc['id']) => {
152
- return rpcResult.parse({ id, result }) as TypedRpcResult<T>;
153
- },
154
- type: 'rpc',
155
- });
143
+ if (c.var.response === undefined) {
144
+ c.set('response', {
145
+ error: <T>(error?: T, id?: JsonRpc['id']) => {
146
+ return rpcError.parse({ error, id }) as TypedRpcError<T>;
147
+ },
148
+ result: <T>(result?: T, id?: JsonRpc['id']) => {
149
+ return rpcResult.parse({ id, result }) as TypedRpcResult<T>;
150
+ },
151
+ type: 'rpc',
152
+ });
153
+ }
156
154
 
157
155
  await next();
158
156
  });