hono-openapi 1.2.0 → 1.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/dist/index.cjs +78 -21
- package/dist/index.d.cts +36 -18
- package/dist/index.d.ts +36 -18
- package/dist/index.js +78 -21
- package/package.json +84 -86
package/dist/index.cjs
CHANGED
|
@@ -252,8 +252,10 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
|
+
const { components: resolvedDocComponents } = _documentation.components?.responses ? await resolveResponseSchemas(_documentation.components.responses) : { components: {} };
|
|
255
256
|
const components = mergeComponentsObjects(
|
|
256
257
|
_documentation.components,
|
|
258
|
+
resolvedDocComponents,
|
|
257
259
|
ctx.components
|
|
258
260
|
);
|
|
259
261
|
return {
|
|
@@ -325,7 +327,6 @@ function getHiddenValue(options) {
|
|
|
325
327
|
}
|
|
326
328
|
async function getSpec(middlewareHandler, defaultOptions) {
|
|
327
329
|
if ("spec" in middlewareHandler) {
|
|
328
|
-
let components = {};
|
|
329
330
|
const tmp = {
|
|
330
331
|
...defaultOptions,
|
|
331
332
|
...middlewareHandler.spec,
|
|
@@ -334,25 +335,10 @@ async function getSpec(middlewareHandler, defaultOptions) {
|
|
|
334
335
|
...middlewareHandler.spec.responses
|
|
335
336
|
}
|
|
336
337
|
};
|
|
338
|
+
let components = {};
|
|
337
339
|
if (tmp.responses) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (!response || !("content" in response)) continue;
|
|
341
|
-
for (const contentKey of Object.keys(response.content ?? {})) {
|
|
342
|
-
const raw = response.content?.[contentKey];
|
|
343
|
-
if (!raw) continue;
|
|
344
|
-
if (raw.schema && "toOpenAPISchema" in raw.schema) {
|
|
345
|
-
const result2 = await raw.schema.toOpenAPISchema();
|
|
346
|
-
raw.schema = result2.schema;
|
|
347
|
-
if (result2.components) {
|
|
348
|
-
components = mergeComponentsObjects(
|
|
349
|
-
components,
|
|
350
|
-
result2.components
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
340
|
+
const resolved = await resolveResponseSchemas(tmp.responses);
|
|
341
|
+
components = resolved.components;
|
|
356
342
|
}
|
|
357
343
|
return { schema: tmp, components };
|
|
358
344
|
}
|
|
@@ -362,6 +348,7 @@ async function getSpec(middlewareHandler, defaultOptions) {
|
|
|
362
348
|
const media = middlewareHandler.options?.media ?? middlewareHandler.target === "json" ? "application/json" : "multipart/form-data";
|
|
363
349
|
if (!docs.requestBody || !("content" in docs.requestBody) || !docs.requestBody.content) {
|
|
364
350
|
docs.requestBody = {
|
|
351
|
+
required: true,
|
|
365
352
|
content: {
|
|
366
353
|
[media]: {
|
|
367
354
|
schema: result.schema
|
|
@@ -421,6 +408,25 @@ function generateParameters(target, schema) {
|
|
|
421
408
|
}
|
|
422
409
|
return parameters;
|
|
423
410
|
}
|
|
411
|
+
async function resolveResponseSchemas(responses) {
|
|
412
|
+
let components = {};
|
|
413
|
+
for (const key of Object.keys(responses)) {
|
|
414
|
+
const response = responses[key];
|
|
415
|
+
if (!response || !("content" in response)) continue;
|
|
416
|
+
for (const contentKey of Object.keys(response.content ?? {})) {
|
|
417
|
+
const raw = response.content?.[contentKey];
|
|
418
|
+
if (!raw) continue;
|
|
419
|
+
if (raw.schema && "toOpenAPISchema" in raw.schema) {
|
|
420
|
+
const result = await raw.schema.toOpenAPISchema();
|
|
421
|
+
raw.schema = result.schema;
|
|
422
|
+
if (result.components) {
|
|
423
|
+
components = mergeComponentsObjects(components, result.components);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return { responses, components };
|
|
429
|
+
}
|
|
424
430
|
function mergeComponentsObjects(...components) {
|
|
425
431
|
return components.reduce(
|
|
426
432
|
(prev, component, index) => {
|
|
@@ -451,16 +457,63 @@ function loadVendor(vendor, fn) {
|
|
|
451
457
|
standardOpenapi.loadVendor(vendor, fn.toOpenAPISchema);
|
|
452
458
|
}
|
|
453
459
|
}
|
|
460
|
+
const arktypeMorphFallback = (ctx) => ctx.base;
|
|
461
|
+
const zodV4DateOverride = (ctx) => {
|
|
462
|
+
if (ctx.zodSchema._zod.def.type === "date") {
|
|
463
|
+
ctx.jsonSchema.type = "string";
|
|
464
|
+
ctx.jsonSchema.format = "date-time";
|
|
465
|
+
}
|
|
466
|
+
};
|
|
454
467
|
function resolver(schema, userDefinedOptions) {
|
|
468
|
+
const vendor = schema["~standard"].vendor;
|
|
455
469
|
return {
|
|
456
|
-
vendor
|
|
470
|
+
vendor,
|
|
457
471
|
validate: schema["~standard"].validate,
|
|
458
472
|
toJSONSchema: (customOptions) => standardJson.toJsonSchema(schema, { ...userDefinedOptions, ...customOptions }),
|
|
459
|
-
toOpenAPISchema: (customOptions) => standardOpenapi.toOpenAPISchema(schema, {
|
|
473
|
+
toOpenAPISchema: (customOptions) => standardOpenapi.toOpenAPISchema(schema, {
|
|
474
|
+
...userDefinedOptions,
|
|
475
|
+
...customOptions,
|
|
476
|
+
...vendor === "arktype" ? injectArktypeFallback(userDefinedOptions, customOptions) : void 0,
|
|
477
|
+
...vendor === "zod" ? injectZodV4DateOverride(schema, userDefinedOptions, customOptions) : void 0
|
|
478
|
+
})
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function injectArktypeFallback(userDefined, custom) {
|
|
482
|
+
const userNested = userDefined?.options;
|
|
483
|
+
const customNested = custom?.options;
|
|
484
|
+
if (userDefined?.fallback || userNested?.fallback || custom?.fallback || customNested?.fallback) {
|
|
485
|
+
return void 0;
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
options: {
|
|
489
|
+
fallback: arktypeMorphFallback,
|
|
490
|
+
...userNested,
|
|
491
|
+
...customNested
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function injectZodV4DateOverride(schema, userDefined, custom) {
|
|
496
|
+
if (!("_zod" in schema)) return void 0;
|
|
497
|
+
const userNested = userDefined?.options;
|
|
498
|
+
const customNested = custom?.options;
|
|
499
|
+
if (userDefined?.override || userNested?.override || custom?.override || customNested?.override) {
|
|
500
|
+
return void 0;
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
options: {
|
|
504
|
+
unrepresentable: "any",
|
|
505
|
+
override: zodV4DateOverride,
|
|
506
|
+
...userNested,
|
|
507
|
+
...customNested
|
|
508
|
+
}
|
|
460
509
|
};
|
|
461
510
|
}
|
|
462
511
|
function validator(target, schema, hook, options) {
|
|
463
512
|
const middleware = standardValidator.sValidator(target, schema, hook);
|
|
513
|
+
Object.defineProperty(middleware, "name", {
|
|
514
|
+
value: "validator",
|
|
515
|
+
configurable: true
|
|
516
|
+
});
|
|
464
517
|
return Object.assign(middleware, {
|
|
465
518
|
[uniqueSymbol]: {
|
|
466
519
|
target,
|
|
@@ -473,6 +526,10 @@ function describeRoute(spec) {
|
|
|
473
526
|
const middleware = async (_c, next) => {
|
|
474
527
|
await next();
|
|
475
528
|
};
|
|
529
|
+
Object.defineProperty(middleware, "name", {
|
|
530
|
+
value: "describeRoute",
|
|
531
|
+
configurable: true
|
|
532
|
+
});
|
|
476
533
|
return Object.assign(middleware, {
|
|
477
534
|
[uniqueSymbol]: {
|
|
478
535
|
spec
|
package/dist/index.d.cts
CHANGED
|
@@ -6,6 +6,8 @@ import { loadVendor as loadVendor$2, ToOpenAPISchemaContext } from '@standard-co
|
|
|
6
6
|
import { Hook } from '@hono/standard-validator';
|
|
7
7
|
import { loadVendor as loadVendor$1 } from '@standard-community/standard-json';
|
|
8
8
|
import { StatusCode } from 'hono/utils/http-status';
|
|
9
|
+
import { JSONParsed } from 'hono/utils/types';
|
|
10
|
+
import { InferInput } from 'hono/validator';
|
|
9
11
|
import { JSONSchema7 } from 'json-schema';
|
|
10
12
|
|
|
11
13
|
/** The Standard Schema interface. */
|
|
@@ -93,13 +95,9 @@ type HasUndefined<T> = undefined extends T ? true : false;
|
|
|
93
95
|
*/
|
|
94
96
|
declare function validator<Schema extends StandardSchemaV1, Target extends keyof ValidationTargets, E extends Env, P extends string, In = StandardSchemaV1.InferInput<Schema>, Out = StandardSchemaV1.InferOutput<Schema>, I extends Input = {
|
|
95
97
|
in: HasUndefined<In> extends true ? {
|
|
96
|
-
[K in Target]?: In extends ValidationTargets[K] ? In :
|
|
97
|
-
[K2 in keyof In]?: ValidationTargets[K][K2];
|
|
98
|
-
};
|
|
98
|
+
[K in Target]?: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
|
|
99
99
|
} : {
|
|
100
|
-
[K in Target]: In extends ValidationTargets[K] ? In :
|
|
101
|
-
[K2 in keyof In]: ValidationTargets[K][K2];
|
|
102
|
-
};
|
|
100
|
+
[K in Target]: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
|
|
103
101
|
};
|
|
104
102
|
out: {
|
|
105
103
|
[K in Target]: Out;
|
|
@@ -122,7 +120,7 @@ type ResponseObject<T extends Partial<Record<StatusCode, StandardSchemaV1>>> = {
|
|
|
122
120
|
};
|
|
123
121
|
type Num<T> = T extends `${infer N extends number}` ? N : T;
|
|
124
122
|
type HandlerResponse<T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>> = PromiseOr<{
|
|
125
|
-
[K in keyof T]: T[K] extends StandardSchemaV1 ? TypedResponse<StandardSchemaV1.InferOutput<T[K]
|
|
123
|
+
[K in keyof T]: T[K] extends StandardSchemaV1 ? TypedResponse<JSONParsed<StandardSchemaV1.InferOutput<T[K]>>, Num<K> extends StatusCode ? Num<K> : never> : never;
|
|
126
124
|
}[keyof T]>;
|
|
127
125
|
type Handler<E extends Env, P extends string, I extends Input, T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>> = (c: Context<E, P, I>, next: Next) => HandlerResponse<T>;
|
|
128
126
|
declare function describeResponse<E extends Env, P extends string, I extends Input, T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>>(handler: Handler<E, P, I, T>, responses: ResponseObject<T>, options?: Record<string, unknown>): Handler<E, P, I, T>;
|
|
@@ -151,13 +149,41 @@ type HandlerUniqueProperty = (ResolverReturnType & {
|
|
|
151
149
|
}) | {
|
|
152
150
|
spec: DescribeRouteOptions;
|
|
153
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* A media type object that accepts resolver() output in addition to standard schema types.
|
|
154
|
+
*/
|
|
155
|
+
type MediaTypeObjectWithResolver = Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
|
|
156
|
+
schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* A response object that accepts resolver() output in schema positions.
|
|
160
|
+
*/
|
|
161
|
+
type ResponseObjectWithResolver = (Omit<OpenAPIV3_1.ResponseObject, "content"> & {
|
|
162
|
+
content?: {
|
|
163
|
+
[media: string]: MediaTypeObjectWithResolver;
|
|
164
|
+
};
|
|
165
|
+
}) | OpenAPIV3_1.ReferenceObject;
|
|
166
|
+
/**
|
|
167
|
+
* A responses map that accepts resolver() output in schema positions.
|
|
168
|
+
*/
|
|
169
|
+
type ResponsesWithResolver = {
|
|
170
|
+
[key: string]: ResponseObjectWithResolver;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Extended document type that allows resolver() in documentation.components.responses
|
|
174
|
+
*/
|
|
175
|
+
type DocumentWithResolver = Omit<Partial<OpenAPIV3_1.Document>, "x-express-openapi-additional-middleware" | "x-express-openapi-validation-strict" | "components"> & {
|
|
176
|
+
components?: Omit<OpenAPIV3_1.ComponentsObject, "responses"> & {
|
|
177
|
+
responses?: ResponsesWithResolver;
|
|
178
|
+
};
|
|
179
|
+
};
|
|
154
180
|
type GenerateSpecOptions = {
|
|
155
181
|
/**
|
|
156
182
|
* Customize OpenAPI config, refers to Swagger 2.0 config
|
|
157
183
|
*
|
|
158
184
|
* @see https://swagger.io/specification/v2/
|
|
159
185
|
*/
|
|
160
|
-
documentation:
|
|
186
|
+
documentation: DocumentWithResolver;
|
|
161
187
|
/**
|
|
162
188
|
* Include paths which don't have the handlers.
|
|
163
189
|
* This is useful when you want to document the
|
|
@@ -203,15 +229,7 @@ type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "responses" | "ope
|
|
|
203
229
|
/**
|
|
204
230
|
* Responses of the request
|
|
205
231
|
*/
|
|
206
|
-
responses?:
|
|
207
|
-
[key: string]: (OpenAPIV3_1.ResponseObject & {
|
|
208
|
-
content?: {
|
|
209
|
-
[key: string]: Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
|
|
210
|
-
schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
|
|
211
|
-
};
|
|
212
|
-
};
|
|
213
|
-
}) | OpenAPIV3_1.ReferenceObject;
|
|
214
|
-
};
|
|
232
|
+
responses?: ResponsesWithResolver;
|
|
215
233
|
};
|
|
216
234
|
type RegisterSchemaPathOptions = {
|
|
217
235
|
route: RouterRoute;
|
|
@@ -277,4 +295,4 @@ declare function generateSpecs<E extends Env = BlankEnv, P extends string = stri
|
|
|
277
295
|
jsonSchemaDialect?: string;
|
|
278
296
|
}>;
|
|
279
297
|
|
|
280
|
-
export { ALLOWED_METHODS, type AllowedMethods, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type SpecContext, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
|
|
298
|
+
export { ALLOWED_METHODS, type AllowedMethods, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type ResponsesWithResolver, type SpecContext, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { loadVendor as loadVendor$2, ToOpenAPISchemaContext } from '@standard-co
|
|
|
6
6
|
import { Hook } from '@hono/standard-validator';
|
|
7
7
|
import { loadVendor as loadVendor$1 } from '@standard-community/standard-json';
|
|
8
8
|
import { StatusCode } from 'hono/utils/http-status';
|
|
9
|
+
import { JSONParsed } from 'hono/utils/types';
|
|
10
|
+
import { InferInput } from 'hono/validator';
|
|
9
11
|
import { JSONSchema7 } from 'json-schema';
|
|
10
12
|
|
|
11
13
|
/** The Standard Schema interface. */
|
|
@@ -93,13 +95,9 @@ type HasUndefined<T> = undefined extends T ? true : false;
|
|
|
93
95
|
*/
|
|
94
96
|
declare function validator<Schema extends StandardSchemaV1, Target extends keyof ValidationTargets, E extends Env, P extends string, In = StandardSchemaV1.InferInput<Schema>, Out = StandardSchemaV1.InferOutput<Schema>, I extends Input = {
|
|
95
97
|
in: HasUndefined<In> extends true ? {
|
|
96
|
-
[K in Target]?: In extends ValidationTargets[K] ? In :
|
|
97
|
-
[K2 in keyof In]?: ValidationTargets[K][K2];
|
|
98
|
-
};
|
|
98
|
+
[K in Target]?: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
|
|
99
99
|
} : {
|
|
100
|
-
[K in Target]: In extends ValidationTargets[K] ? In :
|
|
101
|
-
[K2 in keyof In]: ValidationTargets[K][K2];
|
|
102
|
-
};
|
|
100
|
+
[K in Target]: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
|
|
103
101
|
};
|
|
104
102
|
out: {
|
|
105
103
|
[K in Target]: Out;
|
|
@@ -122,7 +120,7 @@ type ResponseObject<T extends Partial<Record<StatusCode, StandardSchemaV1>>> = {
|
|
|
122
120
|
};
|
|
123
121
|
type Num<T> = T extends `${infer N extends number}` ? N : T;
|
|
124
122
|
type HandlerResponse<T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>> = PromiseOr<{
|
|
125
|
-
[K in keyof T]: T[K] extends StandardSchemaV1 ? TypedResponse<StandardSchemaV1.InferOutput<T[K]
|
|
123
|
+
[K in keyof T]: T[K] extends StandardSchemaV1 ? TypedResponse<JSONParsed<StandardSchemaV1.InferOutput<T[K]>>, Num<K> extends StatusCode ? Num<K> : never> : never;
|
|
126
124
|
}[keyof T]>;
|
|
127
125
|
type Handler<E extends Env, P extends string, I extends Input, T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>> = (c: Context<E, P, I>, next: Next) => HandlerResponse<T>;
|
|
128
126
|
declare function describeResponse<E extends Env, P extends string, I extends Input, T extends Partial<Record<StatusCode, StandardSchemaV1>> = Partial<Record<StatusCode, StandardSchemaV1>>>(handler: Handler<E, P, I, T>, responses: ResponseObject<T>, options?: Record<string, unknown>): Handler<E, P, I, T>;
|
|
@@ -151,13 +149,41 @@ type HandlerUniqueProperty = (ResolverReturnType & {
|
|
|
151
149
|
}) | {
|
|
152
150
|
spec: DescribeRouteOptions;
|
|
153
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* A media type object that accepts resolver() output in addition to standard schema types.
|
|
154
|
+
*/
|
|
155
|
+
type MediaTypeObjectWithResolver = Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
|
|
156
|
+
schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* A response object that accepts resolver() output in schema positions.
|
|
160
|
+
*/
|
|
161
|
+
type ResponseObjectWithResolver = (Omit<OpenAPIV3_1.ResponseObject, "content"> & {
|
|
162
|
+
content?: {
|
|
163
|
+
[media: string]: MediaTypeObjectWithResolver;
|
|
164
|
+
};
|
|
165
|
+
}) | OpenAPIV3_1.ReferenceObject;
|
|
166
|
+
/**
|
|
167
|
+
* A responses map that accepts resolver() output in schema positions.
|
|
168
|
+
*/
|
|
169
|
+
type ResponsesWithResolver = {
|
|
170
|
+
[key: string]: ResponseObjectWithResolver;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Extended document type that allows resolver() in documentation.components.responses
|
|
174
|
+
*/
|
|
175
|
+
type DocumentWithResolver = Omit<Partial<OpenAPIV3_1.Document>, "x-express-openapi-additional-middleware" | "x-express-openapi-validation-strict" | "components"> & {
|
|
176
|
+
components?: Omit<OpenAPIV3_1.ComponentsObject, "responses"> & {
|
|
177
|
+
responses?: ResponsesWithResolver;
|
|
178
|
+
};
|
|
179
|
+
};
|
|
154
180
|
type GenerateSpecOptions = {
|
|
155
181
|
/**
|
|
156
182
|
* Customize OpenAPI config, refers to Swagger 2.0 config
|
|
157
183
|
*
|
|
158
184
|
* @see https://swagger.io/specification/v2/
|
|
159
185
|
*/
|
|
160
|
-
documentation:
|
|
186
|
+
documentation: DocumentWithResolver;
|
|
161
187
|
/**
|
|
162
188
|
* Include paths which don't have the handlers.
|
|
163
189
|
* This is useful when you want to document the
|
|
@@ -203,15 +229,7 @@ type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "responses" | "ope
|
|
|
203
229
|
/**
|
|
204
230
|
* Responses of the request
|
|
205
231
|
*/
|
|
206
|
-
responses?:
|
|
207
|
-
[key: string]: (OpenAPIV3_1.ResponseObject & {
|
|
208
|
-
content?: {
|
|
209
|
-
[key: string]: Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
|
|
210
|
-
schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
|
|
211
|
-
};
|
|
212
|
-
};
|
|
213
|
-
}) | OpenAPIV3_1.ReferenceObject;
|
|
214
|
-
};
|
|
232
|
+
responses?: ResponsesWithResolver;
|
|
215
233
|
};
|
|
216
234
|
type RegisterSchemaPathOptions = {
|
|
217
235
|
route: RouterRoute;
|
|
@@ -277,4 +295,4 @@ declare function generateSpecs<E extends Env = BlankEnv, P extends string = stri
|
|
|
277
295
|
jsonSchemaDialect?: string;
|
|
278
296
|
}>;
|
|
279
297
|
|
|
280
|
-
export { ALLOWED_METHODS, type AllowedMethods, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type SpecContext, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
|
|
298
|
+
export { ALLOWED_METHODS, type AllowedMethods, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type ResponsesWithResolver, type SpecContext, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
|
package/dist/index.js
CHANGED
|
@@ -250,8 +250,10 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
|
+
const { components: resolvedDocComponents } = _documentation.components?.responses ? await resolveResponseSchemas(_documentation.components.responses) : { components: {} };
|
|
253
254
|
const components = mergeComponentsObjects(
|
|
254
255
|
_documentation.components,
|
|
256
|
+
resolvedDocComponents,
|
|
255
257
|
ctx.components
|
|
256
258
|
);
|
|
257
259
|
return {
|
|
@@ -323,7 +325,6 @@ function getHiddenValue(options) {
|
|
|
323
325
|
}
|
|
324
326
|
async function getSpec(middlewareHandler, defaultOptions) {
|
|
325
327
|
if ("spec" in middlewareHandler) {
|
|
326
|
-
let components = {};
|
|
327
328
|
const tmp = {
|
|
328
329
|
...defaultOptions,
|
|
329
330
|
...middlewareHandler.spec,
|
|
@@ -332,25 +333,10 @@ async function getSpec(middlewareHandler, defaultOptions) {
|
|
|
332
333
|
...middlewareHandler.spec.responses
|
|
333
334
|
}
|
|
334
335
|
};
|
|
336
|
+
let components = {};
|
|
335
337
|
if (tmp.responses) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (!response || !("content" in response)) continue;
|
|
339
|
-
for (const contentKey of Object.keys(response.content ?? {})) {
|
|
340
|
-
const raw = response.content?.[contentKey];
|
|
341
|
-
if (!raw) continue;
|
|
342
|
-
if (raw.schema && "toOpenAPISchema" in raw.schema) {
|
|
343
|
-
const result2 = await raw.schema.toOpenAPISchema();
|
|
344
|
-
raw.schema = result2.schema;
|
|
345
|
-
if (result2.components) {
|
|
346
|
-
components = mergeComponentsObjects(
|
|
347
|
-
components,
|
|
348
|
-
result2.components
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
338
|
+
const resolved = await resolveResponseSchemas(tmp.responses);
|
|
339
|
+
components = resolved.components;
|
|
354
340
|
}
|
|
355
341
|
return { schema: tmp, components };
|
|
356
342
|
}
|
|
@@ -360,6 +346,7 @@ async function getSpec(middlewareHandler, defaultOptions) {
|
|
|
360
346
|
const media = middlewareHandler.options?.media ?? middlewareHandler.target === "json" ? "application/json" : "multipart/form-data";
|
|
361
347
|
if (!docs.requestBody || !("content" in docs.requestBody) || !docs.requestBody.content) {
|
|
362
348
|
docs.requestBody = {
|
|
349
|
+
required: true,
|
|
363
350
|
content: {
|
|
364
351
|
[media]: {
|
|
365
352
|
schema: result.schema
|
|
@@ -419,6 +406,25 @@ function generateParameters(target, schema) {
|
|
|
419
406
|
}
|
|
420
407
|
return parameters;
|
|
421
408
|
}
|
|
409
|
+
async function resolveResponseSchemas(responses) {
|
|
410
|
+
let components = {};
|
|
411
|
+
for (const key of Object.keys(responses)) {
|
|
412
|
+
const response = responses[key];
|
|
413
|
+
if (!response || !("content" in response)) continue;
|
|
414
|
+
for (const contentKey of Object.keys(response.content ?? {})) {
|
|
415
|
+
const raw = response.content?.[contentKey];
|
|
416
|
+
if (!raw) continue;
|
|
417
|
+
if (raw.schema && "toOpenAPISchema" in raw.schema) {
|
|
418
|
+
const result = await raw.schema.toOpenAPISchema();
|
|
419
|
+
raw.schema = result.schema;
|
|
420
|
+
if (result.components) {
|
|
421
|
+
components = mergeComponentsObjects(components, result.components);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return { responses, components };
|
|
427
|
+
}
|
|
422
428
|
function mergeComponentsObjects(...components) {
|
|
423
429
|
return components.reduce(
|
|
424
430
|
(prev, component, index) => {
|
|
@@ -449,16 +455,63 @@ function loadVendor(vendor, fn) {
|
|
|
449
455
|
loadVendor$2(vendor, fn.toOpenAPISchema);
|
|
450
456
|
}
|
|
451
457
|
}
|
|
458
|
+
const arktypeMorphFallback = (ctx) => ctx.base;
|
|
459
|
+
const zodV4DateOverride = (ctx) => {
|
|
460
|
+
if (ctx.zodSchema._zod.def.type === "date") {
|
|
461
|
+
ctx.jsonSchema.type = "string";
|
|
462
|
+
ctx.jsonSchema.format = "date-time";
|
|
463
|
+
}
|
|
464
|
+
};
|
|
452
465
|
function resolver(schema, userDefinedOptions) {
|
|
466
|
+
const vendor = schema["~standard"].vendor;
|
|
453
467
|
return {
|
|
454
|
-
vendor
|
|
468
|
+
vendor,
|
|
455
469
|
validate: schema["~standard"].validate,
|
|
456
470
|
toJSONSchema: (customOptions) => toJsonSchema(schema, { ...userDefinedOptions, ...customOptions }),
|
|
457
|
-
toOpenAPISchema: (customOptions) => toOpenAPISchema(schema, {
|
|
471
|
+
toOpenAPISchema: (customOptions) => toOpenAPISchema(schema, {
|
|
472
|
+
...userDefinedOptions,
|
|
473
|
+
...customOptions,
|
|
474
|
+
...vendor === "arktype" ? injectArktypeFallback(userDefinedOptions, customOptions) : void 0,
|
|
475
|
+
...vendor === "zod" ? injectZodV4DateOverride(schema, userDefinedOptions, customOptions) : void 0
|
|
476
|
+
})
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function injectArktypeFallback(userDefined, custom) {
|
|
480
|
+
const userNested = userDefined?.options;
|
|
481
|
+
const customNested = custom?.options;
|
|
482
|
+
if (userDefined?.fallback || userNested?.fallback || custom?.fallback || customNested?.fallback) {
|
|
483
|
+
return void 0;
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
options: {
|
|
487
|
+
fallback: arktypeMorphFallback,
|
|
488
|
+
...userNested,
|
|
489
|
+
...customNested
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function injectZodV4DateOverride(schema, userDefined, custom) {
|
|
494
|
+
if (!("_zod" in schema)) return void 0;
|
|
495
|
+
const userNested = userDefined?.options;
|
|
496
|
+
const customNested = custom?.options;
|
|
497
|
+
if (userDefined?.override || userNested?.override || custom?.override || customNested?.override) {
|
|
498
|
+
return void 0;
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
options: {
|
|
502
|
+
unrepresentable: "any",
|
|
503
|
+
override: zodV4DateOverride,
|
|
504
|
+
...userNested,
|
|
505
|
+
...customNested
|
|
506
|
+
}
|
|
458
507
|
};
|
|
459
508
|
}
|
|
460
509
|
function validator(target, schema, hook, options) {
|
|
461
510
|
const middleware = sValidator(target, schema, hook);
|
|
511
|
+
Object.defineProperty(middleware, "name", {
|
|
512
|
+
value: "validator",
|
|
513
|
+
configurable: true
|
|
514
|
+
});
|
|
462
515
|
return Object.assign(middleware, {
|
|
463
516
|
[uniqueSymbol]: {
|
|
464
517
|
target,
|
|
@@ -471,6 +524,10 @@ function describeRoute(spec) {
|
|
|
471
524
|
const middleware = async (_c, next) => {
|
|
472
525
|
await next();
|
|
473
526
|
};
|
|
527
|
+
Object.defineProperty(middleware, "name", {
|
|
528
|
+
value: "describeRoute",
|
|
529
|
+
configurable: true
|
|
530
|
+
});
|
|
474
531
|
return Object.assign(middleware, {
|
|
475
532
|
[uniqueSymbol]: {
|
|
476
533
|
spec
|
package/package.json
CHANGED
|
@@ -1,86 +1,84 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "hono-openapi",
|
|
3
|
-
"description": "OpenAPI schema generator for Hono",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.cjs",
|
|
7
|
-
"module": "dist/index.js",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
|
-
"files": [
|
|
10
|
-
"dist"
|
|
11
|
-
],
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
"hono":
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
"packageManager": "pnpm@10.0.0"
|
|
86
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "hono-openapi",
|
|
3
|
+
"description": "OpenAPI schema generator for Hono",
|
|
4
|
+
"version": "1.3.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"hono",
|
|
15
|
+
"openapi",
|
|
16
|
+
"zod",
|
|
17
|
+
"valibot",
|
|
18
|
+
"typebox",
|
|
19
|
+
"arktype",
|
|
20
|
+
"effect"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/rhinobase/hono-openapi",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/rhinobase/hono-openapi.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/rhinobase/hono-openapi/issues"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/index.d.cts",
|
|
40
|
+
"default": "./dist/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@hono/standard-validator": "^0.2.0",
|
|
45
|
+
"@standard-community/standard-json": "^0.3.5",
|
|
46
|
+
"@standard-community/standard-openapi": "^0.2.9",
|
|
47
|
+
"@types/json-schema": "^7.0.15",
|
|
48
|
+
"hono": "^4.11.2",
|
|
49
|
+
"openapi-types": "^12.1.3"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"@hono/standard-validator": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"hono": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@biomejs/biome": "^2.0.6",
|
|
61
|
+
"@standard-schema/spec": "^1.0.0",
|
|
62
|
+
"@valibot/to-json-schema": "^1.3.0",
|
|
63
|
+
"arktype": "^2.1.22",
|
|
64
|
+
"effect": "^3.17.13",
|
|
65
|
+
"husky": "^9.1.7",
|
|
66
|
+
"is-ci": "^4.1.0",
|
|
67
|
+
"nano-staged": "^0.8.0",
|
|
68
|
+
"pkg-pr-new": "^0.0.60",
|
|
69
|
+
"pkgroll": "^2.13.1",
|
|
70
|
+
"typebox": "^1.0.17",
|
|
71
|
+
"typescript": "^5.8.3",
|
|
72
|
+
"sury": "^10.0.0",
|
|
73
|
+
"valibot": "^1.1.0",
|
|
74
|
+
"vitest": "^3.2.4",
|
|
75
|
+
"zod": "^3.23.8",
|
|
76
|
+
"zod-openapi": "^4"
|
|
77
|
+
},
|
|
78
|
+
"scripts": {
|
|
79
|
+
"build": "pkgroll --clean-dist",
|
|
80
|
+
"lint": "biome check .",
|
|
81
|
+
"format": "biome check --write .",
|
|
82
|
+
"test": "vitest"
|
|
83
|
+
}
|
|
84
|
+
}
|