@hono/zod-openapi 0.5.1 → 0.7.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.
- package/README.md +52 -1
- package/dist/index.cjs +6 -1
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +6 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -174,6 +174,57 @@ app.openapi(
|
|
|
174
174
|
)
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
### A DRY approach to handling validation errors
|
|
178
|
+
|
|
179
|
+
In the case that you have a common error formatter, you can initialize the `OpenAPIHono` instance with a `defaultHook`.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const app = new OpenAPIHono({
|
|
183
|
+
defaultHook: (result, c) => {
|
|
184
|
+
if (!result.success) {
|
|
185
|
+
return c.jsonT(
|
|
186
|
+
{
|
|
187
|
+
ok: false,
|
|
188
|
+
errors: formatZodErrors(result),
|
|
189
|
+
source: 'custom_error_handler',
|
|
190
|
+
},
|
|
191
|
+
422
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
You can still override the `defaultHook` by providing the hook at the call site when appropriate.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// uses the defaultHook
|
|
202
|
+
app.openapi(createPostRoute, (c) => {
|
|
203
|
+
const { title } = c.req.valid('json')
|
|
204
|
+
return c.jsonT({ title })
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
// override the defaultHook by passing in a hook
|
|
208
|
+
app.openapi(
|
|
209
|
+
createBookRoute,
|
|
210
|
+
(c) => {
|
|
211
|
+
const { title } = c.req.valid('json')
|
|
212
|
+
return c.jsonT({ title })
|
|
213
|
+
},
|
|
214
|
+
(result, c) => {
|
|
215
|
+
if (!result.success) {
|
|
216
|
+
return c.jsonT(
|
|
217
|
+
{
|
|
218
|
+
ok: false,
|
|
219
|
+
source: 'routeHook' as const,
|
|
220
|
+
},
|
|
221
|
+
400
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
177
228
|
### OpenAPI v3.1
|
|
178
229
|
|
|
179
230
|
You can generate OpenAPI v3.1 spec using the following methods:
|
|
@@ -211,7 +262,7 @@ You can configure middleware for each endpoint from a route created by `createRo
|
|
|
211
262
|
import { prettyJSON } from 'hono/pretty-json'
|
|
212
263
|
import { cache } from 'honoc/cache'
|
|
213
264
|
|
|
214
|
-
app.use(route.getRoutingPath(), prettyJSON(), cache({ cacheName:
|
|
265
|
+
app.use(route.getRoutingPath(), prettyJSON(), cache({ cacheName: 'my-cache' }))
|
|
215
266
|
app.openapi(route, handler)
|
|
216
267
|
```
|
|
217
268
|
|
package/dist/index.cjs
CHANGED
|
@@ -32,11 +32,13 @@ var import_hono = require("hono");
|
|
|
32
32
|
var import_zod = require("zod");
|
|
33
33
|
var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
34
34
|
openAPIRegistry;
|
|
35
|
+
defaultHook;
|
|
35
36
|
constructor(init) {
|
|
36
37
|
super(init);
|
|
37
38
|
this.openAPIRegistry = new import_zod_to_openapi.OpenAPIRegistry();
|
|
39
|
+
this.defaultHook = init?.defaultHook;
|
|
38
40
|
}
|
|
39
|
-
openapi = (route, handler, hook) => {
|
|
41
|
+
openapi = (route, handler, hook = this.defaultHook) => {
|
|
40
42
|
this.openAPIRegistry.registerPath(route);
|
|
41
43
|
const validators = [];
|
|
42
44
|
if (route.request?.query) {
|
|
@@ -133,6 +135,9 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
133
135
|
});
|
|
134
136
|
return this;
|
|
135
137
|
}
|
|
138
|
+
basePath(path) {
|
|
139
|
+
return new _OpenAPIHono(super.basePath(path));
|
|
140
|
+
}
|
|
136
141
|
};
|
|
137
142
|
var createRoute = (routeConfig) => {
|
|
138
143
|
return {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
2
2
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
3
|
-
import {
|
|
3
|
+
import { RouteConfig, OpenAPIRegistry, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
4
4
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
5
|
-
import { Env,
|
|
5
|
+
import { Env, Input, Handler, Schema, Hono, ToSchema, Context, TypedResponse } from 'hono';
|
|
6
6
|
import { MergeSchemaPath, MergePath } from 'hono/dist/types/types';
|
|
7
7
|
import { RemoveBlankRecord } from 'hono/utils/types';
|
|
8
8
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
@@ -42,6 +42,10 @@ type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes ?
|
|
|
42
42
|
form: z.output<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>;
|
|
43
43
|
};
|
|
44
44
|
} : {} : {} : {} : {};
|
|
45
|
+
type InputTypeParam<R extends RouteConfig> = InputTypeBase<R, 'params', 'param'>;
|
|
46
|
+
type InputTypeQuery<R extends RouteConfig> = InputTypeBase<R, 'query', 'query'>;
|
|
47
|
+
type InputTypeHeader<R extends RouteConfig> = InputTypeBase<R, 'headers', 'header'>;
|
|
48
|
+
type InputTypeCookie<R extends RouteConfig> = InputTypeBase<R, 'cookies', 'cookie'>;
|
|
45
49
|
type OutputType<R extends RouteConfig> = R['responses'] extends Record<infer _, infer C> ? C extends ResponseConfig ? C['content'] extends ZodContentObject ? IsJson<keyof C['content']> extends never ? {} : C['content'][keyof C['content']]['schema'] extends ZodSchema ? z.infer<C['content'][keyof C['content']]['schema']> : {} : {} : {} : {};
|
|
46
50
|
type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
47
51
|
success: true;
|
|
@@ -52,17 +56,24 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
52
56
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
53
57
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
54
58
|
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
55
|
-
type
|
|
59
|
+
type OpenAPIHonoOptions<E extends Env> = {
|
|
60
|
+
defaultHook?: Hook<any, E, any, any>;
|
|
61
|
+
};
|
|
62
|
+
type HonoInit<E extends Env> = ConstructorParameters<typeof Hono>[0] & OpenAPIHonoOptions<E>;
|
|
63
|
+
type RouteHandler<R extends RouteConfig, E extends Env = Env, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R['path']>> = Handler<E, P, I, HandlerResponse<OutputType<R>>>;
|
|
64
|
+
type RouteHook<R extends RouteConfig, E extends Env = Env, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R['path']>> = Hook<I, E, P, OutputType<R>>;
|
|
56
65
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
57
66
|
openAPIRegistry: OpenAPIRegistry;
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
defaultHook?: OpenAPIHonoOptions<E>['defaultHook'];
|
|
68
|
+
constructor(init?: HonoInit<E>);
|
|
69
|
+
openapi: <R extends RouteConfig, I extends Input = InputTypeBase<R, "params", "param"> & InputTypeBase<R, "query", "query"> & InputTypeBase<R, "headers", "header"> & InputTypeBase<R, "cookies", "cookie"> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R["path"]>>(route: R, handler: Handler<E, P, I, HandlerResponse<OutputType<R>>>, hook?: Hook<I, E, P, OutputType<R>> | undefined) => OpenAPIHono<E, S & ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
60
70
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
61
71
|
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject;
|
|
62
72
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
63
73
|
doc31: (path: string, config: OpenAPIObjectConfig) => void;
|
|
64
74
|
route<SubPath extends string, SubEnv extends Env, SubSchema extends Schema, SubBasePath extends string>(path: SubPath, app: Hono<SubEnv, SubSchema, SubBasePath>): OpenAPIHono<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> & S, BasePath>;
|
|
65
75
|
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
76
|
+
basePath<SubPath extends string>(path: SubPath): OpenAPIHono<E, S, MergePath<BasePath, SubPath>>;
|
|
66
77
|
}
|
|
67
78
|
type RoutingPath<P extends string> = P extends `${infer Head}/{${infer Param}}${infer Tail}` ? `${Head}/:${Param}${RoutingPath<Tail>}` : P;
|
|
68
79
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
@@ -71,4 +82,4 @@ declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"
|
|
|
71
82
|
getRoutingPath(): RoutingPath<R["path"]>;
|
|
72
83
|
};
|
|
73
84
|
|
|
74
|
-
export { OpenAPIHono, createRoute };
|
|
85
|
+
export { OpenAPIHono, OpenAPIHonoOptions, RouteHandler, RouteHook, createRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
2
2
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
3
|
-
import {
|
|
3
|
+
import { RouteConfig, OpenAPIRegistry, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
4
4
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
5
|
-
import { Env,
|
|
5
|
+
import { Env, Input, Handler, Schema, Hono, ToSchema, Context, TypedResponse } from 'hono';
|
|
6
6
|
import { MergeSchemaPath, MergePath } from 'hono/dist/types/types';
|
|
7
7
|
import { RemoveBlankRecord } from 'hono/utils/types';
|
|
8
8
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
@@ -42,6 +42,10 @@ type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes ?
|
|
|
42
42
|
form: z.output<R['request']['body']['content'][keyof R['request']['body']['content']]['schema']>;
|
|
43
43
|
};
|
|
44
44
|
} : {} : {} : {} : {};
|
|
45
|
+
type InputTypeParam<R extends RouteConfig> = InputTypeBase<R, 'params', 'param'>;
|
|
46
|
+
type InputTypeQuery<R extends RouteConfig> = InputTypeBase<R, 'query', 'query'>;
|
|
47
|
+
type InputTypeHeader<R extends RouteConfig> = InputTypeBase<R, 'headers', 'header'>;
|
|
48
|
+
type InputTypeCookie<R extends RouteConfig> = InputTypeBase<R, 'cookies', 'cookie'>;
|
|
45
49
|
type OutputType<R extends RouteConfig> = R['responses'] extends Record<infer _, infer C> ? C extends ResponseConfig ? C['content'] extends ZodContentObject ? IsJson<keyof C['content']> extends never ? {} : C['content'][keyof C['content']]['schema'] extends ZodSchema ? z.infer<C['content'][keyof C['content']]['schema']> : {} : {} : {} : {};
|
|
46
50
|
type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
47
51
|
success: true;
|
|
@@ -52,17 +56,24 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
52
56
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
53
57
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
54
58
|
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
55
|
-
type
|
|
59
|
+
type OpenAPIHonoOptions<E extends Env> = {
|
|
60
|
+
defaultHook?: Hook<any, E, any, any>;
|
|
61
|
+
};
|
|
62
|
+
type HonoInit<E extends Env> = ConstructorParameters<typeof Hono>[0] & OpenAPIHonoOptions<E>;
|
|
63
|
+
type RouteHandler<R extends RouteConfig, E extends Env = Env, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R['path']>> = Handler<E, P, I, HandlerResponse<OutputType<R>>>;
|
|
64
|
+
type RouteHook<R extends RouteConfig, E extends Env = Env, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R['path']>> = Hook<I, E, P, OutputType<R>>;
|
|
56
65
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
57
66
|
openAPIRegistry: OpenAPIRegistry;
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
defaultHook?: OpenAPIHonoOptions<E>['defaultHook'];
|
|
68
|
+
constructor(init?: HonoInit<E>);
|
|
69
|
+
openapi: <R extends RouteConfig, I extends Input = InputTypeBase<R, "params", "param"> & InputTypeBase<R, "query", "query"> & InputTypeBase<R, "headers", "header"> & InputTypeBase<R, "cookies", "cookie"> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R["path"]>>(route: R, handler: Handler<E, P, I, HandlerResponse<OutputType<R>>>, hook?: Hook<I, E, P, OutputType<R>> | undefined) => OpenAPIHono<E, S & ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
60
70
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
61
71
|
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject;
|
|
62
72
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
63
73
|
doc31: (path: string, config: OpenAPIObjectConfig) => void;
|
|
64
74
|
route<SubPath extends string, SubEnv extends Env, SubSchema extends Schema, SubBasePath extends string>(path: SubPath, app: Hono<SubEnv, SubSchema, SubBasePath>): OpenAPIHono<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> & S, BasePath>;
|
|
65
75
|
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
76
|
+
basePath<SubPath extends string>(path: SubPath): OpenAPIHono<E, S, MergePath<BasePath, SubPath>>;
|
|
66
77
|
}
|
|
67
78
|
type RoutingPath<P extends string> = P extends `${infer Head}/{${infer Param}}${infer Tail}` ? `${Head}/:${Param}${RoutingPath<Tail>}` : P;
|
|
68
79
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
@@ -71,4 +82,4 @@ declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"
|
|
|
71
82
|
getRoutingPath(): RoutingPath<R["path"]>;
|
|
72
83
|
};
|
|
73
84
|
|
|
74
|
-
export { OpenAPIHono, createRoute };
|
|
85
|
+
export { OpenAPIHono, OpenAPIHonoOptions, RouteHandler, RouteHook, createRoute };
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,13 @@ import { Hono } from "hono";
|
|
|
10
10
|
import { z, ZodType } from "zod";
|
|
11
11
|
var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
12
12
|
openAPIRegistry;
|
|
13
|
+
defaultHook;
|
|
13
14
|
constructor(init) {
|
|
14
15
|
super(init);
|
|
15
16
|
this.openAPIRegistry = new OpenAPIRegistry();
|
|
17
|
+
this.defaultHook = init?.defaultHook;
|
|
16
18
|
}
|
|
17
|
-
openapi = (route, handler, hook) => {
|
|
19
|
+
openapi = (route, handler, hook = this.defaultHook) => {
|
|
18
20
|
this.openAPIRegistry.registerPath(route);
|
|
19
21
|
const validators = [];
|
|
20
22
|
if (route.request?.query) {
|
|
@@ -111,6 +113,9 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
111
113
|
});
|
|
112
114
|
return this;
|
|
113
115
|
}
|
|
116
|
+
basePath(path) {
|
|
117
|
+
return new _OpenAPIHono(super.basePath(path));
|
|
118
|
+
}
|
|
114
119
|
};
|
|
115
120
|
var createRoute = (routeConfig) => {
|
|
116
121
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/zod-openapi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A wrapper class of Hono which supports OpenAPI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,14 +31,15 @@
|
|
|
31
31
|
"zod": "3.*"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"hono": "^
|
|
34
|
+
"@hono/zod-validator": "^0.1.9",
|
|
35
|
+
"hono": "^3.7.2",
|
|
35
36
|
"zod": "^3.22.1"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@asteasolutions/zod-to-openapi": "^5.5.0",
|
|
39
|
-
"@hono/zod-validator": "^0.1.
|
|
40
|
+
"@hono/zod-validator": "^0.1.9"
|
|
40
41
|
},
|
|
41
42
|
"engines": {
|
|
42
43
|
"node": ">=16.0.0"
|
|
43
44
|
}
|
|
44
|
-
}
|
|
45
|
+
}
|