@hono/zod-openapi 0.2.0 → 0.3.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.
- package/README.md +49 -4
- package/dist/index.cjs +51 -71
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +51 -72
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
_Note: This is not standalone middleware but is hosted on the monorepo "[github.com/honojs/middleware](https://github.com/honojs/middleware)"._
|
|
6
6
|
|
|
7
|
-
## Limitations
|
|
8
|
-
|
|
9
|
-
- An instance of Zod OpenAPI Hono cannot be used as a "subApp" in conjunction with `rootApp.route('/api', subApp)`.
|
|
10
|
-
|
|
11
7
|
## Usage
|
|
12
8
|
|
|
13
9
|
### Installation
|
|
@@ -178,6 +174,14 @@ app.openapi(
|
|
|
178
174
|
)
|
|
179
175
|
```
|
|
180
176
|
|
|
177
|
+
### The Registry
|
|
178
|
+
|
|
179
|
+
You can access the [`OpenAPIRegistry`](https://github.com/asteasolutions/zod-to-openapi#the-registry) object via `app.openAPIRegistry`:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const registry = app.openAPIRegistry
|
|
183
|
+
```
|
|
184
|
+
|
|
181
185
|
### Middleware
|
|
182
186
|
|
|
183
187
|
Zod OpenAPI Hono is an extension of Hono, so you can use Hono's middleware in the same way:
|
|
@@ -208,6 +212,47 @@ const appRoutes = app.openapi(route, (c) => {
|
|
|
208
212
|
const client = hc<typeof appRoutes>('http://localhost:8787/')
|
|
209
213
|
```
|
|
210
214
|
|
|
215
|
+
## Limitations
|
|
216
|
+
|
|
217
|
+
An instance of Zod OpenAPI Hono cannot be used as a "subApp" in conjunction with `rootApp.route('/api', subApp)`.
|
|
218
|
+
Use `app.mount('/api', subApp.fetch)` instead.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const api = OpenAPIHono()
|
|
222
|
+
|
|
223
|
+
// ...
|
|
224
|
+
|
|
225
|
+
// Set the `/api` as a base path in the document.
|
|
226
|
+
api.get('/doc', (c) => {
|
|
227
|
+
const url = new URL(c.req.url)
|
|
228
|
+
url.pathname = '/api'
|
|
229
|
+
url.search = ''
|
|
230
|
+
|
|
231
|
+
return c.json(
|
|
232
|
+
// `api.getOpenAPIDocument()` will return a JSON object of the docs.
|
|
233
|
+
api.getOpenAPIDocument({
|
|
234
|
+
openapi: '3.0.0',
|
|
235
|
+
info: {
|
|
236
|
+
version: '1.0.0',
|
|
237
|
+
title: 'My API',
|
|
238
|
+
},
|
|
239
|
+
servers: [
|
|
240
|
+
{
|
|
241
|
+
url: `${url.toString()}`,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
})
|
|
245
|
+
)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const app = new Hono()
|
|
249
|
+
|
|
250
|
+
// Mount the Open API app to `/api` in the main app.
|
|
251
|
+
app.mount('/api', api.fetch)
|
|
252
|
+
|
|
253
|
+
export default app
|
|
254
|
+
```
|
|
255
|
+
|
|
211
256
|
## References
|
|
212
257
|
|
|
213
258
|
- [Hono](https://hono.dev/)
|
package/dist/index.cjs
CHANGED
|
@@ -16,24 +16,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var __accessCheck = (obj, member, msg) => {
|
|
20
|
-
if (!member.has(obj))
|
|
21
|
-
throw TypeError("Cannot " + msg);
|
|
22
|
-
};
|
|
23
|
-
var __privateGet = (obj, member, getter) => {
|
|
24
|
-
__accessCheck(obj, member, "read from private field");
|
|
25
|
-
return getter ? getter.call(obj) : member.get(obj);
|
|
26
|
-
};
|
|
27
|
-
var __privateAdd = (obj, member, value) => {
|
|
28
|
-
if (member.has(obj))
|
|
29
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
30
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
31
|
-
};
|
|
32
|
-
var __privateSet = (obj, member, value, setter) => {
|
|
33
|
-
__accessCheck(obj, member, "write to private field");
|
|
34
|
-
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
35
|
-
return value;
|
|
36
|
-
};
|
|
37
19
|
|
|
38
20
|
// src/index.ts
|
|
39
21
|
var src_exports = {};
|
|
@@ -48,67 +30,65 @@ var import_zod_to_openapi2 = require("@asteasolutions/zod-to-openapi");
|
|
|
48
30
|
var import_zod_validator = require("@hono/zod-validator");
|
|
49
31
|
var import_hono = require("hono");
|
|
50
32
|
var import_zod = require("zod");
|
|
51
|
-
var _registry;
|
|
52
33
|
var OpenAPIHono = class extends import_hono.Hono {
|
|
34
|
+
openAPIRegistry;
|
|
53
35
|
constructor() {
|
|
54
36
|
super();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
37
|
+
this.openAPIRegistry = new import_zod_to_openapi.OpenAPIRegistry();
|
|
38
|
+
}
|
|
39
|
+
openapi = (route, handler, hook) => {
|
|
40
|
+
this.openAPIRegistry.registerPath(route);
|
|
41
|
+
const validators = [];
|
|
42
|
+
if (route.request?.query) {
|
|
43
|
+
const validator = (0, import_zod_validator.zValidator)("query", route.request.query, hook);
|
|
44
|
+
validators.push(validator);
|
|
45
|
+
}
|
|
46
|
+
if (route.request?.params) {
|
|
47
|
+
const validator = (0, import_zod_validator.zValidator)("param", route.request.params, hook);
|
|
48
|
+
validators.push(validator);
|
|
49
|
+
}
|
|
50
|
+
if (route.request?.headers) {
|
|
51
|
+
const validator = (0, import_zod_validator.zValidator)("header", route.request.headers, hook);
|
|
52
|
+
validators.push(validator);
|
|
53
|
+
}
|
|
54
|
+
if (route.request?.cookies) {
|
|
55
|
+
const validator = (0, import_zod_validator.zValidator)("cookie", route.request.cookies, hook);
|
|
56
|
+
validators.push(validator);
|
|
57
|
+
}
|
|
58
|
+
const bodyContent = route.request?.body?.content;
|
|
59
|
+
if (bodyContent) {
|
|
60
|
+
for (const mediaType of Object.keys(bodyContent)) {
|
|
61
|
+
if (mediaType.startsWith("application/json")) {
|
|
62
|
+
const schema = bodyContent[mediaType]["schema"];
|
|
63
|
+
if (schema instanceof import_zod.ZodType) {
|
|
64
|
+
const validator = (0, import_zod_validator.zValidator)("json", schema, hook);
|
|
65
|
+
validators.push(validator);
|
|
84
66
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
67
|
+
}
|
|
68
|
+
if (mediaType.startsWith("multipart/form-data") || mediaType.startsWith("application/x-www-form-urlencoded")) {
|
|
69
|
+
const schema = bodyContent[mediaType]["schema"];
|
|
70
|
+
if (schema instanceof import_zod.ZodType) {
|
|
71
|
+
const validator = (0, import_zod_validator.zValidator)("form", schema, hook);
|
|
72
|
+
validators.push(validator);
|
|
91
73
|
}
|
|
92
74
|
}
|
|
93
75
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
}
|
|
76
|
+
}
|
|
77
|
+
this.on([route.method], route.path.replaceAll(/\/{(.+?)}/g, "/:$1"), ...validators, handler);
|
|
78
|
+
return this;
|
|
79
|
+
};
|
|
80
|
+
getOpenAPIDocument = (config) => {
|
|
81
|
+
const generator = new import_zod_to_openapi.OpenApiGeneratorV3(this.openAPIRegistry.definitions);
|
|
82
|
+
const document = generator.generateDocument(config);
|
|
83
|
+
return document;
|
|
84
|
+
};
|
|
85
|
+
doc = (path, config) => {
|
|
86
|
+
this.get(path, (c) => {
|
|
87
|
+
const document = this.getOpenAPIDocument(config);
|
|
88
|
+
return c.json(document);
|
|
89
|
+
});
|
|
90
|
+
};
|
|
110
91
|
};
|
|
111
|
-
_registry = new WeakMap();
|
|
112
92
|
var createRoute = (routeConfig) => routeConfig;
|
|
113
93
|
(0, import_zod_to_openapi2.extendZodWithOpenApi)(import_zod.z);
|
|
114
94
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
2
|
-
import { RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import { OpenAPIRegistry, RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
3
3
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
4
4
|
import { Env, Schema, Hono, Input, Handler, ToSchema, Context, TypedResponse } from 'hono';
|
|
5
5
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
@@ -48,10 +48,11 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
48
48
|
error: ZodError;
|
|
49
49
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
50
50
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
51
|
+
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
51
52
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
52
|
-
|
|
53
|
+
openAPIRegistry: OpenAPIRegistry;
|
|
53
54
|
constructor();
|
|
54
|
-
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, OutputType<R
|
|
55
|
+
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) => Hono<E, ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
55
56
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
56
57
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
57
58
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
2
|
-
import { RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
2
|
+
import { OpenAPIRegistry, RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
3
3
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
4
4
|
import { Env, Schema, Hono, Input, Handler, ToSchema, Context, TypedResponse } from 'hono';
|
|
5
5
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
@@ -48,10 +48,11 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
48
48
|
error: ZodError;
|
|
49
49
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
50
50
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
51
|
+
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
51
52
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
52
|
-
|
|
53
|
+
openAPIRegistry: OpenAPIRegistry;
|
|
53
54
|
constructor();
|
|
54
|
-
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, OutputType<R
|
|
55
|
+
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) => Hono<E, ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
55
56
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
56
57
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
57
58
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,89 +1,68 @@
|
|
|
1
|
-
var __accessCheck = (obj, member, msg) => {
|
|
2
|
-
if (!member.has(obj))
|
|
3
|
-
throw TypeError("Cannot " + msg);
|
|
4
|
-
};
|
|
5
|
-
var __privateGet = (obj, member, getter) => {
|
|
6
|
-
__accessCheck(obj, member, "read from private field");
|
|
7
|
-
return getter ? getter.call(obj) : member.get(obj);
|
|
8
|
-
};
|
|
9
|
-
var __privateAdd = (obj, member, value) => {
|
|
10
|
-
if (member.has(obj))
|
|
11
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
12
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
13
|
-
};
|
|
14
|
-
var __privateSet = (obj, member, value, setter) => {
|
|
15
|
-
__accessCheck(obj, member, "write to private field");
|
|
16
|
-
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
17
|
-
return value;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
1
|
// src/index.ts
|
|
21
2
|
import { OpenApiGeneratorV3, OpenAPIRegistry } from "@asteasolutions/zod-to-openapi";
|
|
22
3
|
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
23
4
|
import { zValidator } from "@hono/zod-validator";
|
|
24
5
|
import { Hono } from "hono";
|
|
25
6
|
import { z, ZodType } from "zod";
|
|
26
|
-
var _registry;
|
|
27
7
|
var OpenAPIHono = class extends Hono {
|
|
8
|
+
openAPIRegistry;
|
|
28
9
|
constructor() {
|
|
29
10
|
super();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
11
|
+
this.openAPIRegistry = new OpenAPIRegistry();
|
|
12
|
+
}
|
|
13
|
+
openapi = (route, handler, hook) => {
|
|
14
|
+
this.openAPIRegistry.registerPath(route);
|
|
15
|
+
const validators = [];
|
|
16
|
+
if (route.request?.query) {
|
|
17
|
+
const validator = zValidator("query", route.request.query, hook);
|
|
18
|
+
validators.push(validator);
|
|
19
|
+
}
|
|
20
|
+
if (route.request?.params) {
|
|
21
|
+
const validator = zValidator("param", route.request.params, hook);
|
|
22
|
+
validators.push(validator);
|
|
23
|
+
}
|
|
24
|
+
if (route.request?.headers) {
|
|
25
|
+
const validator = zValidator("header", route.request.headers, hook);
|
|
26
|
+
validators.push(validator);
|
|
27
|
+
}
|
|
28
|
+
if (route.request?.cookies) {
|
|
29
|
+
const validator = zValidator("cookie", route.request.cookies, hook);
|
|
30
|
+
validators.push(validator);
|
|
31
|
+
}
|
|
32
|
+
const bodyContent = route.request?.body?.content;
|
|
33
|
+
if (bodyContent) {
|
|
34
|
+
for (const mediaType of Object.keys(bodyContent)) {
|
|
35
|
+
if (mediaType.startsWith("application/json")) {
|
|
36
|
+
const schema = bodyContent[mediaType]["schema"];
|
|
37
|
+
if (schema instanceof ZodType) {
|
|
38
|
+
const validator = zValidator("json", schema, hook);
|
|
39
|
+
validators.push(validator);
|
|
59
40
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
}
|
|
42
|
+
if (mediaType.startsWith("multipart/form-data") || mediaType.startsWith("application/x-www-form-urlencoded")) {
|
|
43
|
+
const schema = bodyContent[mediaType]["schema"];
|
|
44
|
+
if (schema instanceof ZodType) {
|
|
45
|
+
const validator = zValidator("form", schema, hook);
|
|
46
|
+
validators.push(validator);
|
|
66
47
|
}
|
|
67
48
|
}
|
|
68
49
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
}
|
|
50
|
+
}
|
|
51
|
+
this.on([route.method], route.path.replaceAll(/\/{(.+?)}/g, "/:$1"), ...validators, handler);
|
|
52
|
+
return this;
|
|
53
|
+
};
|
|
54
|
+
getOpenAPIDocument = (config) => {
|
|
55
|
+
const generator = new OpenApiGeneratorV3(this.openAPIRegistry.definitions);
|
|
56
|
+
const document = generator.generateDocument(config);
|
|
57
|
+
return document;
|
|
58
|
+
};
|
|
59
|
+
doc = (path, config) => {
|
|
60
|
+
this.get(path, (c) => {
|
|
61
|
+
const document = this.getOpenAPIDocument(config);
|
|
62
|
+
return c.json(document);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
85
65
|
};
|
|
86
|
-
_registry = new WeakMap();
|
|
87
66
|
var createRoute = (routeConfig) => routeConfig;
|
|
88
67
|
extendZodWithOpenApi(z);
|
|
89
68
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/zod-openapi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A wrapper class of Hono which supports OpenAPI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"zod": "3.*"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"hono": "^3.5.
|
|
34
|
+
"hono": "^3.5.8",
|
|
35
35
|
"zod": "^3.22.1"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
@@ -41,4 +41,4 @@
|
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=16.0.0"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|