@hono/zod-openapi 0.19.1 → 0.19.3
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 +14 -0
- package/dist/{index.mjs → index.cjs} +52 -29
- package/dist/{index.d.mts → index.d.cts} +20 -14
- package/dist/index.d.ts +20 -14
- package/dist/index.js +29 -52
- package/package.json +20 -19
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Zod OpenAPI Hono
|
|
2
2
|
|
|
3
|
+
[](https://codecov.io/github/honojs/middleware)
|
|
4
|
+
|
|
3
5
|
**Zod OpenAPI Hono** is an extended Hono class that supports OpenAPI. With it, you can validate values and types using [**Zod**](https://zod.dev/) and generate OpenAPI Swagger documentation. This is based on [**Zod to OpenAPI**](https://github.com/asteasolutions/zod-to-openapi) (thanks a lot!). For details on creating schemas and defining routes, please refer to [the "Zod to OpenAPI" resource](https://github.com/asteasolutions/zod-to-openapi).
|
|
4
6
|
|
|
5
7
|
_Note: This is not standalone middleware but is hosted on the monorepo "[github.com/honojs/middleware](https://github.com/honojs/middleware)"._
|
|
@@ -470,6 +472,18 @@ Be careful when combining `OpenAPIHono` instances with plain `Hono` instances. `
|
|
|
470
472
|
|
|
471
473
|
If you're migrating from plain `Hono` to `OpenAPIHono`, we recommend porting your top-level app, then working your way down the router tree.
|
|
472
474
|
|
|
475
|
+
When using the `.route()` method to mount a child OpenAPIHono app that uses path parameters, you should use the Hono *:param* syntax in the parent route path, rather than the OpenAPI *{param}* syntax:
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
const bookActionsApp = new OpenAPIHono()
|
|
479
|
+
...
|
|
480
|
+
// ❌ Incorrect: This will not match the route
|
|
481
|
+
app.route('/books/{bookId}', bookActionsApp)
|
|
482
|
+
|
|
483
|
+
// ✅ Using Hono parameter syntax
|
|
484
|
+
app.route('/books/:bookId', bookActionsApp)
|
|
485
|
+
```
|
|
486
|
+
|
|
473
487
|
### Header keys
|
|
474
488
|
|
|
475
489
|
Header keys that you define in your schema must be in lowercase.
|
|
@@ -1,20 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
1
20
|
// src/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
extendZodWithOpenApi
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
OpenAPIHono: () => OpenAPIHono,
|
|
24
|
+
createRoute: () => createRoute,
|
|
25
|
+
extendZodWithOpenApi: () => import_zod_to_openapi.extendZodWithOpenApi,
|
|
26
|
+
z: () => import_zod.z
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
30
|
+
var import_zod_validator = require("@hono/zod-validator");
|
|
31
|
+
var import_hono = require("hono");
|
|
32
|
+
var import_url = require("hono/utils/url");
|
|
33
|
+
var import_zod = require("zod");
|
|
34
|
+
var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
13
35
|
openAPIRegistry;
|
|
14
36
|
defaultHook;
|
|
15
37
|
constructor(init) {
|
|
16
38
|
super(init);
|
|
17
|
-
this.openAPIRegistry = new OpenAPIRegistry();
|
|
39
|
+
this.openAPIRegistry = new import_zod_to_openapi.OpenAPIRegistry();
|
|
18
40
|
this.defaultHook = init?.defaultHook;
|
|
19
41
|
}
|
|
20
42
|
/**
|
|
@@ -54,19 +76,19 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
54
76
|
}
|
|
55
77
|
const validators = [];
|
|
56
78
|
if (route.request?.query) {
|
|
57
|
-
const validator = zValidator("query", route.request.query, hook);
|
|
79
|
+
const validator = (0, import_zod_validator.zValidator)("query", route.request.query, hook);
|
|
58
80
|
validators.push(validator);
|
|
59
81
|
}
|
|
60
82
|
if (route.request?.params) {
|
|
61
|
-
const validator = zValidator("param", route.request.params, hook);
|
|
83
|
+
const validator = (0, import_zod_validator.zValidator)("param", route.request.params, hook);
|
|
62
84
|
validators.push(validator);
|
|
63
85
|
}
|
|
64
86
|
if (route.request?.headers) {
|
|
65
|
-
const validator = zValidator("header", route.request.headers, hook);
|
|
87
|
+
const validator = (0, import_zod_validator.zValidator)("header", route.request.headers, hook);
|
|
66
88
|
validators.push(validator);
|
|
67
89
|
}
|
|
68
90
|
if (route.request?.cookies) {
|
|
69
|
-
const validator = zValidator("cookie", route.request.cookies, hook);
|
|
91
|
+
const validator = (0, import_zod_validator.zValidator)("cookie", route.request.cookies, hook);
|
|
70
92
|
validators.push(validator);
|
|
71
93
|
}
|
|
72
94
|
const bodyContent = route.request?.body?.content;
|
|
@@ -76,11 +98,11 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
76
98
|
continue;
|
|
77
99
|
}
|
|
78
100
|
const schema = bodyContent[mediaType]["schema"];
|
|
79
|
-
if (!(schema instanceof ZodType)) {
|
|
101
|
+
if (!(schema instanceof import_zod.ZodType)) {
|
|
80
102
|
continue;
|
|
81
103
|
}
|
|
82
104
|
if (isJSONContentType(mediaType)) {
|
|
83
|
-
const validator = zValidator("json", schema, hook);
|
|
105
|
+
const validator = (0, import_zod_validator.zValidator)("json", schema, hook);
|
|
84
106
|
if (route.request?.body?.required) {
|
|
85
107
|
validators.push(validator);
|
|
86
108
|
} else {
|
|
@@ -97,7 +119,7 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
97
119
|
}
|
|
98
120
|
}
|
|
99
121
|
if (isFormContentType(mediaType)) {
|
|
100
|
-
const validator = zValidator("form", schema, hook);
|
|
122
|
+
const validator = (0, import_zod_validator.zValidator)("form", schema, hook);
|
|
101
123
|
if (route.request?.body?.required) {
|
|
102
124
|
validators.push(validator);
|
|
103
125
|
} else {
|
|
@@ -126,12 +148,12 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
126
148
|
return this;
|
|
127
149
|
};
|
|
128
150
|
getOpenAPIDocument = (config) => {
|
|
129
|
-
const generator = new OpenApiGeneratorV3(this.openAPIRegistry.definitions);
|
|
151
|
+
const generator = new import_zod_to_openapi.OpenApiGeneratorV3(this.openAPIRegistry.definitions);
|
|
130
152
|
const document = generator.generateDocument(config);
|
|
131
153
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
132
154
|
};
|
|
133
155
|
getOpenAPI31Document = (config) => {
|
|
134
|
-
const generator = new OpenApiGeneratorV31(this.openAPIRegistry.definitions);
|
|
156
|
+
const generator = new import_zod_to_openapi.OpenApiGeneratorV31(this.openAPIRegistry.definitions);
|
|
135
157
|
const document = generator.generateDocument(config);
|
|
136
158
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
137
159
|
};
|
|
@@ -170,20 +192,20 @@ var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
|
170
192
|
case "route":
|
|
171
193
|
return this.openAPIRegistry.registerPath({
|
|
172
194
|
...def.route,
|
|
173
|
-
path: mergePath(
|
|
195
|
+
path: (0, import_url.mergePath)(
|
|
174
196
|
pathForOpenAPI,
|
|
175
197
|
// @ts-expect-error _basePath is private
|
|
176
|
-
app._basePath,
|
|
198
|
+
app._basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
177
199
|
def.route.path
|
|
178
200
|
)
|
|
179
201
|
});
|
|
180
202
|
case "webhook":
|
|
181
203
|
return this.openAPIRegistry.registerWebhook({
|
|
182
204
|
...def.webhook,
|
|
183
|
-
path: mergePath(
|
|
205
|
+
path: (0, import_url.mergePath)(
|
|
184
206
|
pathForOpenAPI,
|
|
185
207
|
// @ts-expect-error _basePath is private
|
|
186
|
-
app._basePath,
|
|
208
|
+
app._basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
187
209
|
def.webhook.path
|
|
188
210
|
)
|
|
189
211
|
});
|
|
@@ -215,11 +237,11 @@ var createRoute = (routeConfig) => {
|
|
|
215
237
|
};
|
|
216
238
|
return Object.defineProperty(route, "getRoutingPath", { enumerable: false });
|
|
217
239
|
};
|
|
218
|
-
extendZodWithOpenApi(z);
|
|
240
|
+
(0, import_zod_to_openapi.extendZodWithOpenApi)(import_zod.z);
|
|
219
241
|
function addBasePathToDocument(document, basePath) {
|
|
220
242
|
const updatedPaths = {};
|
|
221
243
|
Object.keys(document.paths).forEach((path) => {
|
|
222
|
-
updatedPaths[mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"), path)] = document.paths[path];
|
|
244
|
+
updatedPaths[(0, import_url.mergePath)(basePath.replaceAll(/:([^\/]+)/g, "{$1}"), path)] = document.paths[path];
|
|
223
245
|
});
|
|
224
246
|
return {
|
|
225
247
|
...document,
|
|
@@ -232,9 +254,10 @@ function isJSONContentType(contentType) {
|
|
|
232
254
|
function isFormContentType(contentType) {
|
|
233
255
|
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
234
256
|
}
|
|
235
|
-
export
|
|
257
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
258
|
+
0 && (module.exports = {
|
|
236
259
|
OpenAPIHono,
|
|
237
260
|
createRoute,
|
|
238
261
|
extendZodWithOpenApi,
|
|
239
262
|
z
|
|
240
|
-
};
|
|
263
|
+
});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
2
|
+
import * as _asteasolutions_zod_to_openapi_dist_v3_1_openapi_generator from '@asteasolutions/zod-to-openapi/dist/v3.1/openapi-generator';
|
|
2
3
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
3
|
-
import
|
|
4
|
+
import * as _asteasolutions_zod_to_openapi_dist_v3_0_openapi_generator from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
5
|
+
import { RouteConfig as RouteConfig$1, ZodRequestBody, ZodContentObject, ZodMediaTypeObject, OpenApiGeneratorV3, OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
|
4
6
|
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
5
7
|
import { MiddlewareHandler, TypedResponse, Env, ValidationTargets, Context, Input, Handler, Schema, Hono, ToSchema } from 'hono';
|
|
6
8
|
import { MergePath, MergeSchemaPath } from 'hono/types';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
9
|
+
import { InfoStatusCode, SuccessStatusCode, RedirectStatusCode, ClientErrorStatusCode, ServerErrorStatusCode, StatusCode } from 'hono/utils/http-status';
|
|
10
|
+
import { SimplifyDeepArray, JSONValue, JSONParsed, RemoveBlankRecord } from 'hono/utils/types';
|
|
11
|
+
import { ZodSchema, z, ZodError, ZodType } from 'zod';
|
|
10
12
|
export { z } from 'zod';
|
|
11
13
|
|
|
12
14
|
type MaybePromise<T> = Promise<T> | T;
|
|
@@ -72,8 +74,12 @@ type RouteConfigStatusCode = keyof StatusCodeRangeDefinitions | StatusCode;
|
|
|
72
74
|
type ExtractStatusCode<T extends RouteConfigStatusCode> = T extends keyof StatusCodeRangeDefinitions ? StatusCodeRangeDefinitions[T] : T;
|
|
73
75
|
type DefinedStatusCodes<R extends RouteConfig> = keyof R['responses'] & RouteConfigStatusCode;
|
|
74
76
|
type RouteConfigToTypedResponse<R extends RouteConfig> = {
|
|
75
|
-
[Status in DefinedStatusCodes<R>]:
|
|
76
|
-
|
|
77
|
+
[Status in DefinedStatusCodes<R>]: R['responses'][Status] extends {
|
|
78
|
+
content: infer Content;
|
|
79
|
+
} ? undefined extends Content ? never : ReturnJsonOrTextOrResponse<keyof R['responses'][Status]['content'], ExtractContent<R['responses'][Status]['content']>, Status> : TypedResponse<{}, ExtractStatusCode<Status>, string>;
|
|
80
|
+
}[DefinedStatusCodes<R>] | ('default' extends keyof R['responses'] ? R['responses']['default'] extends {
|
|
81
|
+
content: infer Content;
|
|
82
|
+
} ? undefined extends Content ? never : ReturnJsonOrTextOrResponse<keyof Content, ExtractContent<Content>, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>> : TypedResponse<{}, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>, string> : never);
|
|
77
83
|
type Hook<T, E extends Env, P extends string, R> = (result: {
|
|
78
84
|
target: keyof ValidationTargets;
|
|
79
85
|
} & ({
|
|
@@ -120,7 +126,7 @@ type MiddlewareToHandlerType<M extends MiddlewareHandler<any, any, any>[]> = M e
|
|
|
120
126
|
OfHandlerType<First>['path'], // Keep path from First
|
|
121
127
|
OfHandlerType<First>['input']>,
|
|
122
128
|
...Rest
|
|
123
|
-
]> : never : never : never : M extends [infer Last] ? Last :
|
|
129
|
+
]> : never : never : never : M extends [infer Last] ? Last : MiddlewareHandler<Env>;
|
|
124
130
|
type RouteMiddlewareParams<R extends RouteConfig> = OfHandlerType<MiddlewareToHandlerType<AsArray<R['middleware']>>>;
|
|
125
131
|
type RouteConfigToEnv<R extends RouteConfig> = RouteMiddlewareParams<R> extends never ? Env : RouteMiddlewareParams<R>['env'];
|
|
126
132
|
type RouteHandler<R extends RouteConfig, E extends Env = RouteConfigToEnv<R>, 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, R extends {
|
|
@@ -170,7 +176,7 @@ declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath e
|
|
|
170
176
|
* }
|
|
171
177
|
*)
|
|
172
178
|
*/
|
|
173
|
-
openapi: <R extends RouteConfig, I extends Input =
|
|
179
|
+
openapi: <R extends RouteConfig, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R["path"]>>({ middleware: routeMiddleware, hide, ...route }: R, handler: Handler<R["middleware"] extends MiddlewareHandler[] | MiddlewareHandler ? RouteMiddlewareParams<R>["env"] & E : E, P, I, R extends {
|
|
174
180
|
responses: {
|
|
175
181
|
[statusCode: number]: {
|
|
176
182
|
content: {
|
|
@@ -186,11 +192,11 @@ declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath e
|
|
|
186
192
|
};
|
|
187
193
|
};
|
|
188
194
|
};
|
|
189
|
-
} ? MaybePromise<RouteConfigToTypedResponse<R>> | undefined : MaybePromise<RouteConfigToTypedResponse<R>> | MaybePromise<Response> | undefined> | undefined) => OpenAPIHono<E, S & ToSchema<R[
|
|
190
|
-
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject
|
|
191
|
-
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject
|
|
192
|
-
doc: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<
|
|
193
|
-
doc31: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<
|
|
195
|
+
} ? MaybePromise<RouteConfigToTypedResponse<R>> | undefined : MaybePromise<RouteConfigToTypedResponse<R>> | MaybePromise<Response> | undefined> | undefined) => OpenAPIHono<E, S & ToSchema<R["method"], MergePath<BasePath, P>, I, RouteConfigToTypedResponse<R>>, BasePath>;
|
|
196
|
+
getOpenAPIDocument: (config: OpenAPIObjectConfig) => ReturnType<(config: _asteasolutions_zod_to_openapi_dist_v3_0_openapi_generator.OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject>;
|
|
197
|
+
getOpenAPI31Document: (config: OpenAPIObjectConfig) => ReturnType<(config: _asteasolutions_zod_to_openapi_dist_v3_1_openapi_generator.OpenAPIObjectConfigV31) => openapi3_ts_oas31.OpenAPIObject>;
|
|
198
|
+
doc: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<"get", P, {}, {}>, BasePath>;
|
|
199
|
+
doc31: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<"get", P, {}, {}>, BasePath>;
|
|
194
200
|
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>;
|
|
195
201
|
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
196
202
|
basePath<SubPath extends string>(path: SubPath): OpenAPIHono<E, S, MergePath<BasePath, SubPath>>;
|
|
@@ -199,7 +205,7 @@ type RoutingPath<P extends string> = P extends `${infer Head}/{${infer Param}}${
|
|
|
199
205
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
200
206
|
path: P;
|
|
201
207
|
}>(routeConfig: R) => R & {
|
|
202
|
-
getRoutingPath(): RoutingPath<R[
|
|
208
|
+
getRoutingPath(): RoutingPath<R["path"]>;
|
|
203
209
|
};
|
|
204
210
|
|
|
205
211
|
export { type DeepSimplify, type Hook, type MiddlewareToHandlerType, type OfHandlerType, OpenAPIHono, type OpenAPIHonoOptions, type OpenAPIObjectConfigure, type RouteConfig, type RouteConfigToEnv, type RouteConfigToTypedResponse, type RouteHandler, type RouteHook, createRoute };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as openapi3_ts_oas31 from 'openapi3-ts/oas31';
|
|
2
|
+
import * as _asteasolutions_zod_to_openapi_dist_v3_1_openapi_generator from '@asteasolutions/zod-to-openapi/dist/v3.1/openapi-generator';
|
|
2
3
|
import * as openapi3_ts_oas30 from 'openapi3-ts/oas30';
|
|
3
|
-
import
|
|
4
|
+
import * as _asteasolutions_zod_to_openapi_dist_v3_0_openapi_generator from '@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator';
|
|
5
|
+
import { RouteConfig as RouteConfig$1, ZodRequestBody, ZodContentObject, ZodMediaTypeObject, OpenApiGeneratorV3, OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
|
4
6
|
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
|
|
5
7
|
import { MiddlewareHandler, TypedResponse, Env, ValidationTargets, Context, Input, Handler, Schema, Hono, ToSchema } from 'hono';
|
|
6
8
|
import { MergePath, MergeSchemaPath } from 'hono/types';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
9
|
+
import { InfoStatusCode, SuccessStatusCode, RedirectStatusCode, ClientErrorStatusCode, ServerErrorStatusCode, StatusCode } from 'hono/utils/http-status';
|
|
10
|
+
import { SimplifyDeepArray, JSONValue, JSONParsed, RemoveBlankRecord } from 'hono/utils/types';
|
|
11
|
+
import { ZodSchema, z, ZodError, ZodType } from 'zod';
|
|
10
12
|
export { z } from 'zod';
|
|
11
13
|
|
|
12
14
|
type MaybePromise<T> = Promise<T> | T;
|
|
@@ -72,8 +74,12 @@ type RouteConfigStatusCode = keyof StatusCodeRangeDefinitions | StatusCode;
|
|
|
72
74
|
type ExtractStatusCode<T extends RouteConfigStatusCode> = T extends keyof StatusCodeRangeDefinitions ? StatusCodeRangeDefinitions[T] : T;
|
|
73
75
|
type DefinedStatusCodes<R extends RouteConfig> = keyof R['responses'] & RouteConfigStatusCode;
|
|
74
76
|
type RouteConfigToTypedResponse<R extends RouteConfig> = {
|
|
75
|
-
[Status in DefinedStatusCodes<R>]:
|
|
76
|
-
|
|
77
|
+
[Status in DefinedStatusCodes<R>]: R['responses'][Status] extends {
|
|
78
|
+
content: infer Content;
|
|
79
|
+
} ? undefined extends Content ? never : ReturnJsonOrTextOrResponse<keyof R['responses'][Status]['content'], ExtractContent<R['responses'][Status]['content']>, Status> : TypedResponse<{}, ExtractStatusCode<Status>, string>;
|
|
80
|
+
}[DefinedStatusCodes<R>] | ('default' extends keyof R['responses'] ? R['responses']['default'] extends {
|
|
81
|
+
content: infer Content;
|
|
82
|
+
} ? undefined extends Content ? never : ReturnJsonOrTextOrResponse<keyof Content, ExtractContent<Content>, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>> : TypedResponse<{}, Exclude<StatusCode, ExtractStatusCode<DefinedStatusCodes<R>>>, string> : never);
|
|
77
83
|
type Hook<T, E extends Env, P extends string, R> = (result: {
|
|
78
84
|
target: keyof ValidationTargets;
|
|
79
85
|
} & ({
|
|
@@ -120,7 +126,7 @@ type MiddlewareToHandlerType<M extends MiddlewareHandler<any, any, any>[]> = M e
|
|
|
120
126
|
OfHandlerType<First>['path'], // Keep path from First
|
|
121
127
|
OfHandlerType<First>['input']>,
|
|
122
128
|
...Rest
|
|
123
|
-
]> : never : never : never : M extends [infer Last] ? Last :
|
|
129
|
+
]> : never : never : never : M extends [infer Last] ? Last : MiddlewareHandler<Env>;
|
|
124
130
|
type RouteMiddlewareParams<R extends RouteConfig> = OfHandlerType<MiddlewareToHandlerType<AsArray<R['middleware']>>>;
|
|
125
131
|
type RouteConfigToEnv<R extends RouteConfig> = RouteMiddlewareParams<R> extends never ? Env : RouteMiddlewareParams<R>['env'];
|
|
126
132
|
type RouteHandler<R extends RouteConfig, E extends Env = RouteConfigToEnv<R>, 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, R extends {
|
|
@@ -170,7 +176,7 @@ declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath e
|
|
|
170
176
|
* }
|
|
171
177
|
*)
|
|
172
178
|
*/
|
|
173
|
-
openapi: <R extends RouteConfig, I extends Input =
|
|
179
|
+
openapi: <R extends RouteConfig, I extends Input = InputTypeParam<R> & InputTypeQuery<R> & InputTypeHeader<R> & InputTypeCookie<R> & InputTypeForm<R> & InputTypeJson<R>, P extends string = ConvertPathType<R["path"]>>({ middleware: routeMiddleware, hide, ...route }: R, handler: Handler<R["middleware"] extends MiddlewareHandler[] | MiddlewareHandler ? RouteMiddlewareParams<R>["env"] & E : E, P, I, R extends {
|
|
174
180
|
responses: {
|
|
175
181
|
[statusCode: number]: {
|
|
176
182
|
content: {
|
|
@@ -186,11 +192,11 @@ declare class OpenAPIHono<E extends Env = Env, S extends Schema = {}, BasePath e
|
|
|
186
192
|
};
|
|
187
193
|
};
|
|
188
194
|
};
|
|
189
|
-
} ? MaybePromise<RouteConfigToTypedResponse<R>> | undefined : MaybePromise<RouteConfigToTypedResponse<R>> | MaybePromise<Response> | undefined> | undefined) => OpenAPIHono<E, S & ToSchema<R[
|
|
190
|
-
getOpenAPIDocument: (config: OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject
|
|
191
|
-
getOpenAPI31Document: (config: OpenAPIObjectConfig) => openapi3_ts_oas31.OpenAPIObject
|
|
192
|
-
doc: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<
|
|
193
|
-
doc31: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<
|
|
195
|
+
} ? MaybePromise<RouteConfigToTypedResponse<R>> | undefined : MaybePromise<RouteConfigToTypedResponse<R>> | MaybePromise<Response> | undefined> | undefined) => OpenAPIHono<E, S & ToSchema<R["method"], MergePath<BasePath, P>, I, RouteConfigToTypedResponse<R>>, BasePath>;
|
|
196
|
+
getOpenAPIDocument: (config: OpenAPIObjectConfig) => ReturnType<(config: _asteasolutions_zod_to_openapi_dist_v3_0_openapi_generator.OpenAPIObjectConfig) => openapi3_ts_oas30.OpenAPIObject>;
|
|
197
|
+
getOpenAPI31Document: (config: OpenAPIObjectConfig) => ReturnType<(config: _asteasolutions_zod_to_openapi_dist_v3_1_openapi_generator.OpenAPIObjectConfigV31) => openapi3_ts_oas31.OpenAPIObject>;
|
|
198
|
+
doc: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<"get", P, {}, {}>, BasePath>;
|
|
199
|
+
doc31: <P extends string>(path: P, configure: OpenAPIObjectConfigure<E, P>) => OpenAPIHono<E, S & ToSchema<"get", P, {}, {}>, BasePath>;
|
|
194
200
|
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>;
|
|
195
201
|
route<SubPath extends string>(path: SubPath): Hono<E, RemoveBlankRecord<S>, BasePath>;
|
|
196
202
|
basePath<SubPath extends string>(path: SubPath): OpenAPIHono<E, S, MergePath<BasePath, SubPath>>;
|
|
@@ -199,7 +205,7 @@ type RoutingPath<P extends string> = P extends `${infer Head}/{${infer Param}}${
|
|
|
199
205
|
declare const createRoute: <P extends string, R extends Omit<RouteConfig, "path"> & {
|
|
200
206
|
path: P;
|
|
201
207
|
}>(routeConfig: R) => R & {
|
|
202
|
-
getRoutingPath(): RoutingPath<R[
|
|
208
|
+
getRoutingPath(): RoutingPath<R["path"]>;
|
|
203
209
|
};
|
|
204
210
|
|
|
205
211
|
export { type DeepSimplify, type Hook, type MiddlewareToHandlerType, type OfHandlerType, OpenAPIHono, type OpenAPIHonoOptions, type OpenAPIObjectConfigure, type RouteConfig, type RouteConfigToEnv, type RouteConfigToTypedResponse, type RouteHandler, type RouteHook, createRoute };
|
package/dist/index.js
CHANGED
|
@@ -1,42 +1,20 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
1
|
// src/index.ts
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
extendZodWithOpenApi
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
var
|
|
32
|
-
var import_url = require("hono/utils/url");
|
|
33
|
-
var import_zod = require("zod");
|
|
34
|
-
var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
2
|
+
import {
|
|
3
|
+
OpenAPIRegistry,
|
|
4
|
+
OpenApiGeneratorV3,
|
|
5
|
+
OpenApiGeneratorV31,
|
|
6
|
+
extendZodWithOpenApi
|
|
7
|
+
} from "@asteasolutions/zod-to-openapi";
|
|
8
|
+
import { zValidator } from "@hono/zod-validator";
|
|
9
|
+
import { Hono } from "hono";
|
|
10
|
+
import { mergePath } from "hono/utils/url";
|
|
11
|
+
import { ZodType, z } from "zod";
|
|
12
|
+
var OpenAPIHono = class _OpenAPIHono extends Hono {
|
|
35
13
|
openAPIRegistry;
|
|
36
14
|
defaultHook;
|
|
37
15
|
constructor(init) {
|
|
38
16
|
super(init);
|
|
39
|
-
this.openAPIRegistry = new
|
|
17
|
+
this.openAPIRegistry = new OpenAPIRegistry();
|
|
40
18
|
this.defaultHook = init?.defaultHook;
|
|
41
19
|
}
|
|
42
20
|
/**
|
|
@@ -76,19 +54,19 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
76
54
|
}
|
|
77
55
|
const validators = [];
|
|
78
56
|
if (route.request?.query) {
|
|
79
|
-
const validator =
|
|
57
|
+
const validator = zValidator("query", route.request.query, hook);
|
|
80
58
|
validators.push(validator);
|
|
81
59
|
}
|
|
82
60
|
if (route.request?.params) {
|
|
83
|
-
const validator =
|
|
61
|
+
const validator = zValidator("param", route.request.params, hook);
|
|
84
62
|
validators.push(validator);
|
|
85
63
|
}
|
|
86
64
|
if (route.request?.headers) {
|
|
87
|
-
const validator =
|
|
65
|
+
const validator = zValidator("header", route.request.headers, hook);
|
|
88
66
|
validators.push(validator);
|
|
89
67
|
}
|
|
90
68
|
if (route.request?.cookies) {
|
|
91
|
-
const validator =
|
|
69
|
+
const validator = zValidator("cookie", route.request.cookies, hook);
|
|
92
70
|
validators.push(validator);
|
|
93
71
|
}
|
|
94
72
|
const bodyContent = route.request?.body?.content;
|
|
@@ -98,11 +76,11 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
98
76
|
continue;
|
|
99
77
|
}
|
|
100
78
|
const schema = bodyContent[mediaType]["schema"];
|
|
101
|
-
if (!(schema instanceof
|
|
79
|
+
if (!(schema instanceof ZodType)) {
|
|
102
80
|
continue;
|
|
103
81
|
}
|
|
104
82
|
if (isJSONContentType(mediaType)) {
|
|
105
|
-
const validator =
|
|
83
|
+
const validator = zValidator("json", schema, hook);
|
|
106
84
|
if (route.request?.body?.required) {
|
|
107
85
|
validators.push(validator);
|
|
108
86
|
} else {
|
|
@@ -119,7 +97,7 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
119
97
|
}
|
|
120
98
|
}
|
|
121
99
|
if (isFormContentType(mediaType)) {
|
|
122
|
-
const validator =
|
|
100
|
+
const validator = zValidator("form", schema, hook);
|
|
123
101
|
if (route.request?.body?.required) {
|
|
124
102
|
validators.push(validator);
|
|
125
103
|
} else {
|
|
@@ -148,12 +126,12 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
148
126
|
return this;
|
|
149
127
|
};
|
|
150
128
|
getOpenAPIDocument = (config) => {
|
|
151
|
-
const generator = new
|
|
129
|
+
const generator = new OpenApiGeneratorV3(this.openAPIRegistry.definitions);
|
|
152
130
|
const document = generator.generateDocument(config);
|
|
153
131
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
154
132
|
};
|
|
155
133
|
getOpenAPI31Document = (config) => {
|
|
156
|
-
const generator = new
|
|
134
|
+
const generator = new OpenApiGeneratorV31(this.openAPIRegistry.definitions);
|
|
157
135
|
const document = generator.generateDocument(config);
|
|
158
136
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
159
137
|
};
|
|
@@ -192,20 +170,20 @@ var OpenAPIHono = class _OpenAPIHono extends import_hono.Hono {
|
|
|
192
170
|
case "route":
|
|
193
171
|
return this.openAPIRegistry.registerPath({
|
|
194
172
|
...def.route,
|
|
195
|
-
path:
|
|
173
|
+
path: mergePath(
|
|
196
174
|
pathForOpenAPI,
|
|
197
175
|
// @ts-expect-error _basePath is private
|
|
198
|
-
app._basePath,
|
|
176
|
+
app._basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
199
177
|
def.route.path
|
|
200
178
|
)
|
|
201
179
|
});
|
|
202
180
|
case "webhook":
|
|
203
181
|
return this.openAPIRegistry.registerWebhook({
|
|
204
182
|
...def.webhook,
|
|
205
|
-
path:
|
|
183
|
+
path: mergePath(
|
|
206
184
|
pathForOpenAPI,
|
|
207
185
|
// @ts-expect-error _basePath is private
|
|
208
|
-
app._basePath,
|
|
186
|
+
app._basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
209
187
|
def.webhook.path
|
|
210
188
|
)
|
|
211
189
|
});
|
|
@@ -237,11 +215,11 @@ var createRoute = (routeConfig) => {
|
|
|
237
215
|
};
|
|
238
216
|
return Object.defineProperty(route, "getRoutingPath", { enumerable: false });
|
|
239
217
|
};
|
|
240
|
-
|
|
218
|
+
extendZodWithOpenApi(z);
|
|
241
219
|
function addBasePathToDocument(document, basePath) {
|
|
242
220
|
const updatedPaths = {};
|
|
243
221
|
Object.keys(document.paths).forEach((path) => {
|
|
244
|
-
updatedPaths[
|
|
222
|
+
updatedPaths[mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"), path)] = document.paths[path];
|
|
245
223
|
});
|
|
246
224
|
return {
|
|
247
225
|
...document,
|
|
@@ -254,10 +232,9 @@ function isJSONContentType(contentType) {
|
|
|
254
232
|
function isFormContentType(contentType) {
|
|
255
233
|
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
256
234
|
}
|
|
257
|
-
|
|
258
|
-
0 && (module.exports = {
|
|
235
|
+
export {
|
|
259
236
|
OpenAPIHono,
|
|
260
237
|
createRoute,
|
|
261
238
|
extendZodWithOpenApi,
|
|
262
239
|
z
|
|
263
|
-
}
|
|
240
|
+
};
|
package/package.json
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/zod-openapi",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"description": "A wrapper class of Hono which supports OpenAPI.",
|
|
5
|
-
"
|
|
6
|
-
"module": "dist/index.
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"publint": "publint",
|
|
15
|
-
"
|
|
12
|
+
"build": "tsup ./src/index.ts",
|
|
13
|
+
"prepack": "yarn build",
|
|
14
|
+
"publint": "attw --pack && publint",
|
|
15
|
+
"typecheck": "tsc -b tsconfig.json",
|
|
16
|
+
"test": "vitest"
|
|
16
17
|
},
|
|
17
18
|
"exports": {
|
|
18
19
|
".": {
|
|
19
20
|
"import": {
|
|
20
|
-
"types": "./dist/index.d.mts",
|
|
21
|
-
"default": "./dist/index.mjs"
|
|
22
|
-
},
|
|
23
|
-
"require": {
|
|
24
21
|
"types": "./dist/index.d.ts",
|
|
25
22
|
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
},
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
},
|
|
34
35
|
"repository": {
|
|
35
36
|
"type": "git",
|
|
36
|
-
"url": "https://github.com/honojs/middleware.git"
|
|
37
|
+
"url": "git+https://github.com/honojs/middleware.git",
|
|
38
|
+
"directory": "packages/zod-openapi"
|
|
37
39
|
},
|
|
38
40
|
"homepage": "https://github.com/honojs/middleware",
|
|
39
41
|
"peerDependencies": {
|
|
@@ -41,18 +43,17 @@
|
|
|
41
43
|
"zod": "3.*"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"vitest": "^1.4.0",
|
|
46
|
+
"@arethetypeswrong/cli": "^0.17.4",
|
|
47
|
+
"publint": "^0.3.9",
|
|
48
|
+
"tsup": "^8.4.0",
|
|
49
|
+
"typescript": "^5.8.2",
|
|
50
|
+
"vitest": "^3.0.8",
|
|
50
51
|
"yaml": "^2.4.3",
|
|
51
52
|
"zod": "^3.22.1"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
55
|
"@asteasolutions/zod-to-openapi": "^7.1.0",
|
|
55
|
-
"@hono/zod-validator": "
|
|
56
|
+
"@hono/zod-validator": "workspace:^"
|
|
56
57
|
},
|
|
57
58
|
"engines": {
|
|
58
59
|
"node": ">=16.0.0"
|