@hono/zod-openapi 0.3.1 → 0.4.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 +11 -37
- package/dist/index.cjs +55 -3
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +56 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -174,6 +174,15 @@ app.openapi(
|
|
|
174
174
|
)
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
### OpenAPI v3.1
|
|
178
|
+
|
|
179
|
+
You can generate OpenAPI v3.1 spec using the following methods:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
app.doc31('/docs', {openapi: '3.1.0'}) // new endpoint
|
|
183
|
+
app.getOpenAPI31Document(, {openapi: '3.1.0'}) // raw json
|
|
184
|
+
```
|
|
185
|
+
|
|
177
186
|
### The Registry
|
|
178
187
|
|
|
179
188
|
You can access the [`OpenAPIRegistry`](https://github.com/asteasolutions/zod-to-openapi#the-registry) object via `app.openAPIRegistry`:
|
|
@@ -214,44 +223,9 @@ const client = hc<typeof appRoutes>('http://localhost:8787/')
|
|
|
214
223
|
|
|
215
224
|
## Limitations
|
|
216
225
|
|
|
217
|
-
|
|
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)
|
|
226
|
+
Be careful when combining `OpenAPIHono` instances with plain `Hono` instances. `OpenAPIHono` will merge the definitions of direct subapps, but plain `Hono` knows nothing about the OpenAPI spec additions. Similarly `OpenAPIHono` will not "dig" for instances deep inside a branch of plain `Hono` instances.
|
|
252
227
|
|
|
253
|
-
|
|
254
|
-
```
|
|
228
|
+
If you're migrating from plain `Hono` to `OpenAPIHono`, we recommend porting your top-level app, then working your way down the router tree.
|
|
255
229
|
|
|
256
230
|
## References
|
|
257
231
|
|
package/dist/index.cjs
CHANGED
|
@@ -30,10 +30,10 @@ var import_zod_to_openapi2 = require("@asteasolutions/zod-to-openapi");
|
|
|
30
30
|
var import_zod_validator = require("@hono/zod-validator");
|
|
31
31
|
var import_hono = require("hono");
|
|
32
32
|
var import_zod = require("zod");
|
|
33
|
-
var OpenAPIHono = class extends import_hono.Hono {
|
|
33
|
+
var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
34
34
|
openAPIRegistry;
|
|
35
|
-
constructor() {
|
|
36
|
-
super();
|
|
35
|
+
constructor(init) {
|
|
36
|
+
super(init);
|
|
37
37
|
this.openAPIRegistry = new import_zod_to_openapi.OpenAPIRegistry();
|
|
38
38
|
}
|
|
39
39
|
openapi = (route, handler, hook) => {
|
|
@@ -82,12 +82,64 @@ var OpenAPIHono = class extends import_hono.Hono {
|
|
|
82
82
|
const document = generator.generateDocument(config);
|
|
83
83
|
return document;
|
|
84
84
|
};
|
|
85
|
+
getOpenAPI31Document = (config) => {
|
|
86
|
+
const generator = new import_zod_to_openapi.OpenApiGeneratorV31(this.openAPIRegistry.definitions);
|
|
87
|
+
const document = generator.generateDocument(config);
|
|
88
|
+
return document;
|
|
89
|
+
};
|
|
85
90
|
doc = (path, config) => {
|
|
86
91
|
this.get(path, (c) => {
|
|
87
92
|
const document = this.getOpenAPIDocument(config);
|
|
88
93
|
return c.json(document);
|
|
89
94
|
});
|
|
90
95
|
};
|
|
96
|
+
doc31 = (path, config) => {
|
|
97
|
+
this.get(path, (c) => {
|
|
98
|
+
const document = this.getOpenAPI31Document(config);
|
|
99
|
+
return c.json(document);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
route(path, app) {
|
|
103
|
+
super.route(path, app);
|
|
104
|
+
if (!(app instanceof _OpenAPIHono)) {
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
app.openAPIRegistry.definitions.forEach((def) => {
|
|
108
|
+
switch (def.type) {
|
|
109
|
+
case "component":
|
|
110
|
+
return this.openAPIRegistry.registerComponent(
|
|
111
|
+
def.componentType,
|
|
112
|
+
def.name,
|
|
113
|
+
def.component
|
|
114
|
+
);
|
|
115
|
+
case "route":
|
|
116
|
+
return this.openAPIRegistry.registerPath({
|
|
117
|
+
...def.route,
|
|
118
|
+
path: `${path}${def.route.path}`
|
|
119
|
+
});
|
|
120
|
+
case "webhook":
|
|
121
|
+
return this.openAPIRegistry.registerWebhook({
|
|
122
|
+
...def.webhook,
|
|
123
|
+
path: `${path}${def.webhook.path}`
|
|
124
|
+
});
|
|
125
|
+
case "schema":
|
|
126
|
+
return this.openAPIRegistry.register(
|
|
127
|
+
def.schema._def.openapi._internal.refId,
|
|
128
|
+
def.schema
|
|
129
|
+
);
|
|
130
|
+
case "parameter":
|
|
131
|
+
return this.openAPIRegistry.registerParameter(
|
|
132
|
+
def.schema._def.openapi._internal.refId,
|
|
133
|
+
def.schema
|
|
134
|
+
);
|
|
135
|
+
default: {
|
|
136
|
+
const errorIfNotExhaustive = def;
|
|
137
|
+
throw new Error(`Unknown registry type: ${errorIfNotExhaustive}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
91
143
|
};
|
|
92
144
|
var createRoute = (routeConfig) => routeConfig;
|
|
93
145
|
(0, import_zod_to_openapi2.extendZodWithOpenApi)(import_zod.z);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
1
2
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
2
3
|
import { OpenAPIRegistry, RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
3
4
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
4
5
|
import { Env, Schema, Hono, Input, Handler, ToSchema, Context, TypedResponse } from 'hono';
|
|
6
|
+
import { MergeSchemaPath, MergePath } from 'hono/dist/types/types';
|
|
7
|
+
import { RemoveBlankRecord } from 'hono/utils/types';
|
|
5
8
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
6
9
|
export { z } from 'zod';
|
|
7
10
|
|
|
@@ -49,12 +52,17 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
49
52
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
50
53
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
51
54
|
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
55
|
+
type HonoInit = ConstructorParameters<typeof Hono>[0];
|
|
52
56
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
53
57
|
openAPIRegistry: OpenAPIRegistry;
|
|
54
|
-
constructor();
|
|
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) =>
|
|
58
|
+
constructor(init?: HonoInit);
|
|
59
|
+
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, ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
56
60
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
61
|
+
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject;
|
|
57
62
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
63
|
+
doc31: (path: string, config: OpenAPIObjectConfig) => void;
|
|
64
|
+
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
|
+
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
58
66
|
}
|
|
59
67
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
60
68
|
path: P;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
1
2
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
2
3
|
import { OpenAPIRegistry, RouteConfig, ZodRequestBody, ZodContentObject, ResponseConfig } from '@asteasolutions/zod-to-openapi';
|
|
3
4
|
import { OpenAPIObjectConfig } from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
4
5
|
import { Env, Schema, Hono, Input, Handler, ToSchema, Context, TypedResponse } from 'hono';
|
|
6
|
+
import { MergeSchemaPath, MergePath } from 'hono/dist/types/types';
|
|
7
|
+
import { RemoveBlankRecord } from 'hono/utils/types';
|
|
5
8
|
import { AnyZodObject, z, ZodSchema, ZodError, ZodType } from 'zod';
|
|
6
9
|
export { z } from 'zod';
|
|
7
10
|
|
|
@@ -49,12 +52,17 @@ type Hook<T, E extends Env, P extends string, O> = (result: {
|
|
|
49
52
|
}, c: Context<E, P>) => TypedResponse<O> | Promise<TypedResponse<T>> | void;
|
|
50
53
|
type ConvertPathType<T extends string> = T extends `${infer _}/{${infer Param}}${infer _}` ? `/:${Param}` : T;
|
|
51
54
|
type HandlerResponse<O> = TypedResponse<O> | Promise<TypedResponse<O>>;
|
|
55
|
+
type HonoInit = ConstructorParameters<typeof Hono>[0];
|
|
52
56
|
declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'> extends Hono<E, S, BasePath> {
|
|
53
57
|
openAPIRegistry: OpenAPIRegistry;
|
|
54
|
-
constructor();
|
|
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) =>
|
|
58
|
+
constructor(init?: HonoInit);
|
|
59
|
+
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, ToSchema<R["method"], P, I["in"], OutputType<R>>, BasePath>;
|
|
56
60
|
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject;
|
|
61
|
+
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject;
|
|
57
62
|
doc: (path: string, config: OpenAPIObjectConfig) => void;
|
|
63
|
+
doc31: (path: string, config: OpenAPIObjectConfig) => void;
|
|
64
|
+
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
|
+
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
58
66
|
}
|
|
59
67
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
60
68
|
path: P;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { OpenApiGeneratorV3, OpenAPIRegistry } from "@asteasolutions/zod-to-openapi";
|
|
2
|
+
import { OpenApiGeneratorV3, OpenApiGeneratorV31, OpenAPIRegistry } from "@asteasolutions/zod-to-openapi";
|
|
3
3
|
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
4
4
|
import { zValidator } from "@hono/zod-validator";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { z, ZodType } from "zod";
|
|
7
|
-
var OpenAPIHono = class extends Hono {
|
|
7
|
+
var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
8
8
|
openAPIRegistry;
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
9
|
+
constructor(init) {
|
|
10
|
+
super(init);
|
|
11
11
|
this.openAPIRegistry = new OpenAPIRegistry();
|
|
12
12
|
}
|
|
13
13
|
openapi = (route, handler, hook) => {
|
|
@@ -56,12 +56,64 @@ var OpenAPIHono = class extends Hono {
|
|
|
56
56
|
const document = generator.generateDocument(config);
|
|
57
57
|
return document;
|
|
58
58
|
};
|
|
59
|
+
getOpenAPI31Document = (config) => {
|
|
60
|
+
const generator = new OpenApiGeneratorV31(this.openAPIRegistry.definitions);
|
|
61
|
+
const document = generator.generateDocument(config);
|
|
62
|
+
return document;
|
|
63
|
+
};
|
|
59
64
|
doc = (path, config) => {
|
|
60
65
|
this.get(path, (c) => {
|
|
61
66
|
const document = this.getOpenAPIDocument(config);
|
|
62
67
|
return c.json(document);
|
|
63
68
|
});
|
|
64
69
|
};
|
|
70
|
+
doc31 = (path, config) => {
|
|
71
|
+
this.get(path, (c) => {
|
|
72
|
+
const document = this.getOpenAPI31Document(config);
|
|
73
|
+
return c.json(document);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
route(path, app) {
|
|
77
|
+
super.route(path, app);
|
|
78
|
+
if (!(app instanceof _OpenAPIHono)) {
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
app.openAPIRegistry.definitions.forEach((def) => {
|
|
82
|
+
switch (def.type) {
|
|
83
|
+
case "component":
|
|
84
|
+
return this.openAPIRegistry.registerComponent(
|
|
85
|
+
def.componentType,
|
|
86
|
+
def.name,
|
|
87
|
+
def.component
|
|
88
|
+
);
|
|
89
|
+
case "route":
|
|
90
|
+
return this.openAPIRegistry.registerPath({
|
|
91
|
+
...def.route,
|
|
92
|
+
path: `${path}${def.route.path}`
|
|
93
|
+
});
|
|
94
|
+
case "webhook":
|
|
95
|
+
return this.openAPIRegistry.registerWebhook({
|
|
96
|
+
...def.webhook,
|
|
97
|
+
path: `${path}${def.webhook.path}`
|
|
98
|
+
});
|
|
99
|
+
case "schema":
|
|
100
|
+
return this.openAPIRegistry.register(
|
|
101
|
+
def.schema._def.openapi._internal.refId,
|
|
102
|
+
def.schema
|
|
103
|
+
);
|
|
104
|
+
case "parameter":
|
|
105
|
+
return this.openAPIRegistry.registerParameter(
|
|
106
|
+
def.schema._def.openapi._internal.refId,
|
|
107
|
+
def.schema
|
|
108
|
+
);
|
|
109
|
+
default: {
|
|
110
|
+
const errorIfNotExhaustive = def;
|
|
111
|
+
throw new Error(`Unknown registry type: ${errorIfNotExhaustive}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
65
117
|
};
|
|
66
118
|
var createRoute = (routeConfig) => routeConfig;
|
|
67
119
|
extendZodWithOpenApi(z);
|