hono-openapi 1.3.0 → 1.3.2

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 CHANGED
@@ -6,6 +6,7 @@ var standardJson = require('@standard-community/standard-json');
6
6
  var standardOpenapi = require('@standard-community/standard-openapi');
7
7
 
8
8
  const uniqueSymbol = Symbol("openapi");
9
+ const VALIDATION_MARKER = "__HonoOpenAPIValidator__";
9
10
  const ALLOWED_METHODS = [
10
11
  "GET",
11
12
  "PUT",
@@ -58,7 +59,9 @@ const specsByPathContext = /* @__PURE__ */ new Map();
58
59
  function getPathContext(path) {
59
60
  const context = [];
60
61
  for (const [key, data] of specsByPathContext) {
61
- if (data && path.match(key)) {
62
+ if (!data) continue;
63
+ const prefix = key.endsWith("/*") ? key.slice(0, -2) : key;
64
+ if (path === prefix || path.startsWith(`${prefix}/`)) {
62
65
  context.push(data);
63
66
  }
64
67
  }
@@ -159,6 +162,8 @@ function removeExcludedPaths(paths, ctx) {
159
162
  for (const method of Object.keys(value)) {
160
163
  const schema = value[method];
161
164
  if (schema == null) continue;
165
+ const hasValidation = schema[VALIDATION_MARKER] === true;
166
+ delete schema[VALIDATION_MARKER];
162
167
  if (key.includes("{")) {
163
168
  schema.parameters = schema.parameters ? [...schema.parameters] : [];
164
169
  const pathParameters = key.split("/").filter(
@@ -198,6 +203,16 @@ function removeExcludedPaths(paths, ctx) {
198
203
  200: {}
199
204
  };
200
205
  }
206
+ if (hasValidation && ctx.options.defaultValidationErrorResponse !== false && !schema.responses["400"]) {
207
+ const errorResponse = ctx.options.defaultValidationErrorResponse;
208
+ if (typeof errorResponse === "object") {
209
+ schema.responses["400"] = structuredClone(errorResponse);
210
+ } else if (ctx.validationErrorResponse) {
211
+ schema.responses["400"] = structuredClone(
212
+ ctx.validationErrorResponse
213
+ );
214
+ }
215
+ }
201
216
  }
202
217
  const filteredValue = {};
203
218
  for (const method of Object.keys(value)) {
@@ -217,7 +232,27 @@ const DEFAULT_OPTIONS = {
217
232
  excludeStaticFile: true,
218
233
  exclude: [],
219
234
  excludeMethods: ["OPTIONS"],
220
- excludeTags: []
235
+ excludeTags: [],
236
+ defaultValidationErrorResponse: true
237
+ };
238
+ const DEFAULT_VALIDATION_ERROR = {
239
+ description: "Validation Error",
240
+ content: {
241
+ "application/json": {
242
+ schema: {
243
+ type: "object",
244
+ properties: {
245
+ success: { type: "boolean", enum: [false] },
246
+ error: {
247
+ type: "array",
248
+ items: {}
249
+ },
250
+ data: {}
251
+ },
252
+ required: ["success", "error", "data"]
253
+ }
254
+ }
255
+ }
221
256
  };
222
257
  function openAPIRouteHandler(hono, options) {
223
258
  let specs;
@@ -234,9 +269,14 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
234
269
  options: {
235
270
  ...DEFAULT_OPTIONS,
236
271
  ...options
237
- }
272
+ },
273
+ validationErrorResponse: DEFAULT_VALIDATION_ERROR
238
274
  };
239
275
  const _documentation = ctx.options.documentation ?? {};
276
+ const documentation = {
277
+ ..._documentation,
278
+ components: _documentation.components && { ..._documentation.components }
279
+ };
240
280
  clearSpecsContext();
241
281
  const paths = await generatePaths(hono, ctx);
242
282
  for (const path in paths) {
@@ -252,27 +292,30 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
252
292
  }
253
293
  }
254
294
  }
255
- const { components: resolvedDocComponents } = _documentation.components?.responses ? await resolveResponseSchemas(_documentation.components.responses) : { components: {} };
295
+ const resolvedDocumentation = documentation.components?.responses ? await resolveResponseSchemas(documentation.components.responses) : void 0;
296
+ if (resolvedDocumentation && documentation.components) {
297
+ documentation.components.responses = resolvedDocumentation.responses;
298
+ }
256
299
  const components = mergeComponentsObjects(
257
- _documentation.components,
258
- resolvedDocComponents,
300
+ documentation.components,
301
+ resolvedDocumentation?.components,
259
302
  ctx.components
260
303
  );
261
304
  return {
262
305
  openapi: "3.1.0",
263
- ..._documentation,
264
- tags: _documentation.tags?.filter(
306
+ ...documentation,
307
+ tags: documentation.tags?.filter(
265
308
  (tag) => !ctx.options.excludeTags?.includes(tag?.name)
266
309
  ),
267
310
  info: {
268
311
  title: "Hono Documentation",
269
312
  description: "Development documentation",
270
313
  version: "0.0.0",
271
- ..._documentation.info
314
+ ...documentation.info
272
315
  },
273
316
  paths: {
274
317
  ...removeExcludedPaths(paths, ctx),
275
- ..._documentation.paths
318
+ ...documentation.paths
276
319
  },
277
320
  components
278
321
  };
@@ -338,12 +381,27 @@ async function getSpec(middlewareHandler, defaultOptions) {
338
381
  let components = {};
339
382
  if (tmp.responses) {
340
383
  const resolved = await resolveResponseSchemas(tmp.responses);
384
+ tmp.responses = resolved.responses;
341
385
  components = resolved.components;
342
386
  }
387
+ if (tmp.requestBody && "content" in tmp.requestBody) {
388
+ const resolved = await resolveContentSchemas(tmp.requestBody.content);
389
+ tmp.requestBody = {
390
+ ...tmp.requestBody,
391
+ content: resolved.content
392
+ };
393
+ components = mergeComponentsObjects(components, resolved.components);
394
+ }
343
395
  return { schema: tmp, components };
344
396
  }
345
397
  const result = await middlewareHandler.toOpenAPISchema();
346
- const docs = { ...defaultOptions };
398
+ liftSchemaDefs(result);
399
+ const docs = {
400
+ ...defaultOptions,
401
+ // Mark this operation as validator-derived so a default 400 validation
402
+ // error response can be auto-injected later (see removeExcludedPaths).
403
+ [VALIDATION_MARKER]: true
404
+ };
347
405
  if (middlewareHandler.target === "form" || middlewareHandler.target === "json") {
348
406
  const media = middlewareHandler.options?.media ?? middlewareHandler.target === "json" ? "application/json" : "multipart/form-data";
349
407
  if (!docs.requestBody || !("content" in docs.requestBody) || !docs.requestBody.content) {
@@ -381,51 +439,113 @@ async function getSpec(middlewareHandler, defaultOptions) {
381
439
  });
382
440
  }
383
441
  } else {
384
- parameters = generateParameters(middlewareHandler.target, result.schema);
442
+ parameters = generateParameters(
443
+ middlewareHandler.target,
444
+ result.schema,
445
+ result.components?.schemas
446
+ );
385
447
  }
386
448
  docs.parameters = parameters;
387
449
  }
388
450
  return { schema: docs, components: result.components };
389
451
  }
390
- function generateParameters(target, schema) {
452
+ function generateParameters(target, schema, schemas) {
391
453
  const parameters = [];
392
- for (const [key, value] of Object.entries(schema.properties ?? {})) {
454
+ for (const [key, property] of collectParameterProperties(schema, schemas)) {
393
455
  const def = {
394
456
  in: target === "param" ? "path" : target,
395
457
  name: key,
396
- // @ts-expect-error
397
- schema: value
458
+ // @ts-expect-error ParameterObject uses the OpenAPI 3.0 schema type.
459
+ schema: property.schema
398
460
  };
399
- const isRequired = schema.required?.includes(key);
400
- if (isRequired) {
461
+ if (property.required) {
401
462
  def.required = true;
402
463
  }
403
464
  if (def.schema && "description" in def.schema && def.schema.description) {
404
465
  def.description = def.schema.description;
405
- def.schema.description = void 0;
466
+ def.schema = { ...def.schema, description: void 0 };
406
467
  }
407
468
  parameters.push(def);
408
469
  }
409
470
  return parameters;
410
471
  }
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);
472
+ function collectParameterProperties(schema, schemas) {
473
+ const properties = /* @__PURE__ */ new Map();
474
+ const resolvingRefs = /* @__PURE__ */ new Set();
475
+ const collect = (current) => {
476
+ for (const [key, value] of Object.entries(current.properties ?? {})) {
477
+ const existing = properties.get(key);
478
+ properties.set(key, {
479
+ schema: existing ? { allOf: [existing.schema, value] } : value,
480
+ required: Boolean(current.required?.includes(key)) || existing?.required
481
+ });
482
+ }
483
+ for (const member of current.allOf ?? []) {
484
+ if ("$ref" in member) {
485
+ const prefix = "#/components/schemas/";
486
+ if (!member.$ref.startsWith(prefix)) continue;
487
+ const name = member.$ref.slice(prefix.length).replaceAll("~1", "/").replaceAll("~0", "~");
488
+ const referencedSchema = schemas?.[name];
489
+ if (referencedSchema && !resolvingRefs.has(name)) {
490
+ resolvingRefs.add(name);
491
+ collect(referencedSchema);
492
+ resolvingRefs.delete(name);
424
493
  }
494
+ } else {
495
+ collect(member);
425
496
  }
426
497
  }
498
+ };
499
+ collect(schema);
500
+ return properties;
501
+ }
502
+ async function resolveContentSchemas(content) {
503
+ let components = {};
504
+ const resolvedContent = {};
505
+ for (const [contentKey, raw] of Object.entries(content)) {
506
+ if (!raw) continue;
507
+ if (raw.schema && "toOpenAPISchema" in raw.schema) {
508
+ const result = await raw.schema.toOpenAPISchema();
509
+ liftSchemaDefs(result);
510
+ resolvedContent[contentKey] = {
511
+ ...raw,
512
+ schema: result.schema
513
+ };
514
+ if (result.components) {
515
+ components = mergeComponentsObjects(components, result.components);
516
+ }
517
+ } else {
518
+ resolvedContent[contentKey] = raw;
519
+ }
427
520
  }
428
- return { responses, components };
521
+ return { content: resolvedContent, components };
522
+ }
523
+ async function resolveResponseSchemas(responses) {
524
+ let components = {};
525
+ const resolvedResponses = {};
526
+ for (const [key, response] of Object.entries(responses)) {
527
+ if (!response || !("content" in response) || !response.content) {
528
+ resolvedResponses[key] = response;
529
+ continue;
530
+ }
531
+ const resolved = await resolveContentSchemas(response.content);
532
+ resolvedResponses[key] = {
533
+ ...response,
534
+ content: resolved.content
535
+ };
536
+ components = mergeComponentsObjects(components, resolved.components);
537
+ }
538
+ return { responses: resolvedResponses, components };
539
+ }
540
+ function liftSchemaDefs(result) {
541
+ const defs = result.schema.$defs;
542
+ if (!defs || typeof defs !== "object") return;
543
+ result.components ??= {};
544
+ result.components.schemas = {
545
+ ...defs,
546
+ ...result.components.schemas
547
+ };
548
+ delete result.schema.$defs;
429
549
  }
430
550
  function mergeComponentsObjects(...components) {
431
551
  return components.reduce(
@@ -510,6 +630,10 @@ function injectZodV4DateOverride(schema, userDefined, custom) {
510
630
  }
511
631
  function validator(target, schema, hook, options) {
512
632
  const middleware = standardValidator.sValidator(target, schema, hook);
633
+ Object.defineProperty(middleware, "name", {
634
+ value: "validator",
635
+ configurable: true
636
+ });
513
637
  return Object.assign(middleware, {
514
638
  [uniqueSymbol]: {
515
639
  target,
@@ -522,6 +646,10 @@ function describeRoute(spec) {
522
646
  const middleware = async (_c, next) => {
523
647
  await next();
524
648
  };
649
+ Object.defineProperty(middleware, "name", {
650
+ value: "describeRoute",
651
+ configurable: true
652
+ });
525
653
  return Object.assign(middleware, {
526
654
  [uniqueSymbol]: {
527
655
  spec
@@ -563,6 +691,7 @@ function describeResponse(handler, responses, options) {
563
691
  }
564
692
 
565
693
  exports.ALLOWED_METHODS = ALLOWED_METHODS;
694
+ exports.VALIDATION_MARKER = VALIDATION_MARKER;
566
695
  exports.clearSpecsContext = clearSpecsContext;
567
696
  exports.describeResponse = describeResponse;
568
697
  exports.describeRoute = describeRoute;
package/dist/index.d.cts CHANGED
@@ -5,67 +5,12 @@ import { TypedResponse, RouterRoute, ValidationTargets as ValidationTargets$1, B
5
5
  import { loadVendor as loadVendor$2, ToOpenAPISchemaContext } from '@standard-community/standard-openapi';
6
6
  import { Hook } from '@hono/standard-validator';
7
7
  import { loadVendor as loadVendor$1 } from '@standard-community/standard-json';
8
+ import { StandardSchemaV1 } from '@standard-schema/spec';
8
9
  import { StatusCode } from 'hono/utils/http-status';
9
10
  import { JSONParsed } from 'hono/utils/types';
11
+ import { InferInput } from 'hono/validator';
10
12
  import { JSONSchema7 } from 'json-schema';
11
13
 
12
- /** The Standard Schema interface. */
13
- interface StandardSchemaV1<Input = unknown, Output = Input> {
14
- /** The Standard Schema properties. */
15
- readonly "~standard": StandardSchemaV1.Props<Input, Output>;
16
- }
17
- declare namespace StandardSchemaV1 {
18
- /** The Standard Schema properties interface. */
19
- export interface Props<Input = unknown, Output = Input> {
20
- /** The version number of the standard. */
21
- readonly version: 1;
22
- /** The vendor name of the schema library. */
23
- readonly vendor: string;
24
- /** Validates unknown input values. */
25
- readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
26
- /** Inferred types associated with the schema. */
27
- readonly types?: Types<Input, Output> | undefined;
28
- }
29
- /** The result interface of the validate function. */
30
- export type Result<Output> = SuccessResult<Output> | FailureResult;
31
- /** The result interface if validation succeeds. */
32
- export interface SuccessResult<Output> {
33
- /** The typed output value. */
34
- readonly value: Output;
35
- /** The non-existent issues. */
36
- readonly issues?: undefined;
37
- }
38
- /** The result interface if validation fails. */
39
- export interface FailureResult {
40
- /** The issues of failed validation. */
41
- readonly issues: ReadonlyArray<Issue>;
42
- }
43
- /** The issue interface of the failure output. */
44
- export interface Issue {
45
- /** The error message of the issue. */
46
- readonly message: string;
47
- /** The path of the issue, if any. */
48
- readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
49
- }
50
- /** The path segment interface of the issue. */
51
- export interface PathSegment {
52
- /** The key representing a path segment. */
53
- readonly key: PropertyKey;
54
- }
55
- /** The Standard Schema types interface. */
56
- export interface Types<Input = unknown, Output = Input> {
57
- /** The input type of the schema. */
58
- readonly input: Input;
59
- /** The output type of the schema. */
60
- readonly output: Output;
61
- }
62
- /** Infers the input type of a Standard Schema. */
63
- export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
64
- /** Infers the output type of a Standard Schema. */
65
- export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
66
- export { };
67
- }
68
-
69
14
  declare function loadVendor(vendor: string, fn: {
70
15
  toJSONSchema?: Parameters<typeof loadVendor$1>[1];
71
16
  toOpenAPISchema?: Parameters<typeof loadVendor$2>[1];
@@ -94,13 +39,9 @@ type HasUndefined<T> = undefined extends T ? true : false;
94
39
  */
95
40
  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 = {
96
41
  in: HasUndefined<In> extends true ? {
97
- [K in Target]?: In extends ValidationTargets[K] ? In : {
98
- [K2 in keyof In]?: ValidationTargets[K][K2];
99
- };
42
+ [K in Target]?: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
100
43
  } : {
101
- [K in Target]: In extends ValidationTargets[K] ? In : {
102
- [K2 in keyof In]: ValidationTargets[K][K2];
103
- };
44
+ [K in Target]: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
104
45
  };
105
46
  out: {
106
47
  [K in Target]: Out;
@@ -132,6 +73,14 @@ declare function describeResponse<E extends Env, P extends string, I extends Inp
132
73
  * The unique symbol for the middlewares, which makes it easier to identify them. Not meant to be used directly, unless you're creating a custom middleware.
133
74
  */
134
75
  declare const uniqueSymbol: unique symbol;
76
+ /**
77
+ * Internal marker key set on an operation's spec when it is produced by a
78
+ * `validator()` middleware. It is used to decide whether to auto-inject the
79
+ * default 400 validation error response, and is stripped from the operation
80
+ * before the spec is emitted. Must be an enumerable string key so it survives
81
+ * the `Object.entries`-based merge in `mergeSpecs`.
82
+ */
83
+ declare const VALIDATION_MARKER = "__HonoOpenAPIValidator__";
135
84
  declare const ALLOWED_METHODS: readonly ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];
136
85
  type AllowedMethods = (typeof ALLOWED_METHODS)[number];
137
86
  declare function clearSpecsContext(): void;
@@ -158,13 +107,17 @@ type HandlerUniqueProperty = (ResolverReturnType & {
158
107
  type MediaTypeObjectWithResolver = Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
159
108
  schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
160
109
  };
110
+ type ContentWithResolver = {
111
+ [media: string]: MediaTypeObjectWithResolver;
112
+ };
161
113
  /**
162
114
  * A response object that accepts resolver() output in schema positions.
163
115
  */
164
116
  type ResponseObjectWithResolver = (Omit<OpenAPIV3_1.ResponseObject, "content"> & {
165
- content?: {
166
- [media: string]: MediaTypeObjectWithResolver;
167
- };
117
+ content?: ContentWithResolver;
118
+ }) | OpenAPIV3_1.ReferenceObject;
119
+ type RequestBodyObjectWithResolver = (Omit<OpenAPIV3_1.RequestBodyObject, "content"> & {
120
+ content: ContentWithResolver;
168
121
  }) | OpenAPIV3_1.ReferenceObject;
169
122
  /**
170
123
  * A responses map that accepts resolver() output in schema positions.
@@ -217,10 +170,23 @@ type GenerateSpecOptions = {
217
170
  * Default options for `describeRoute` method
218
171
  */
219
172
  defaultOptions: Partial<Record<AllowedMethods | "ALL", DescribeRouteOptions>>;
173
+ /**
174
+ * Automatically include a 400 validation error response for routes
175
+ * that use `validator()`. Set to `false` to disable, `true` to use
176
+ * the built-in schema, or provide a custom `ResponseObject`.
177
+ *
178
+ * @default true
179
+ */
180
+ defaultValidationErrorResponse: boolean | OpenAPIV3_1.ResponseObject;
220
181
  };
221
182
  type OperationId = string | ((route: RouterRoute) => string);
222
- type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "responses" | "operationId"> & {
183
+ type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "requestBody" | "responses" | "operationId"> & {
223
184
  operationId?: OperationId;
185
+ /**
186
+ * Request body metadata. `resolver()` values are converted to OpenAPI
187
+ * schemas while generating the document.
188
+ */
189
+ requestBody?: RequestBodyObjectWithResolver;
224
190
  /**
225
191
  * Pass `true` to hide route from OpenAPI/swagger document
226
192
  */
@@ -241,11 +207,12 @@ type RegisterSchemaPathOptions = {
241
207
  };
242
208
  paths: Partial<OpenAPIV3_1.PathsObject>;
243
209
  };
244
- type HaveDefaultValues = "documentation" | "excludeStaticFile" | "exclude" | "excludeMethods" | "excludeTags";
210
+ type HaveDefaultValues = "documentation" | "excludeStaticFile" | "exclude" | "excludeMethods" | "excludeTags" | "defaultValidationErrorResponse";
245
211
  type SanitizedGenerateSpecOptions = Pick<GenerateSpecOptions, HaveDefaultValues> & Omit<Partial<GenerateSpecOptions>, HaveDefaultValues>;
246
212
  type SpecContext = {
247
213
  components: OpenAPIV3_1.ComponentsObject;
248
214
  options: SanitizedGenerateSpecOptions;
215
+ validationErrorResponse?: OpenAPIV3_1.ResponseObject;
249
216
  };
250
217
 
251
218
  /**
@@ -298,4 +265,4 @@ declare function generateSpecs<E extends Env = BlankEnv, P extends string = stri
298
265
  jsonSchemaDialect?: string;
299
266
  }>;
300
267
 
301
- 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 };
268
+ export { ALLOWED_METHODS, type AllowedMethods, type ContentWithResolver, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type ResponsesWithResolver, type SpecContext, VALIDATION_MARKER, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
package/dist/index.d.ts CHANGED
@@ -5,67 +5,12 @@ import { TypedResponse, RouterRoute, ValidationTargets as ValidationTargets$1, B
5
5
  import { loadVendor as loadVendor$2, ToOpenAPISchemaContext } from '@standard-community/standard-openapi';
6
6
  import { Hook } from '@hono/standard-validator';
7
7
  import { loadVendor as loadVendor$1 } from '@standard-community/standard-json';
8
+ import { StandardSchemaV1 } from '@standard-schema/spec';
8
9
  import { StatusCode } from 'hono/utils/http-status';
9
10
  import { JSONParsed } from 'hono/utils/types';
11
+ import { InferInput } from 'hono/validator';
10
12
  import { JSONSchema7 } from 'json-schema';
11
13
 
12
- /** The Standard Schema interface. */
13
- interface StandardSchemaV1<Input = unknown, Output = Input> {
14
- /** The Standard Schema properties. */
15
- readonly "~standard": StandardSchemaV1.Props<Input, Output>;
16
- }
17
- declare namespace StandardSchemaV1 {
18
- /** The Standard Schema properties interface. */
19
- export interface Props<Input = unknown, Output = Input> {
20
- /** The version number of the standard. */
21
- readonly version: 1;
22
- /** The vendor name of the schema library. */
23
- readonly vendor: string;
24
- /** Validates unknown input values. */
25
- readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
26
- /** Inferred types associated with the schema. */
27
- readonly types?: Types<Input, Output> | undefined;
28
- }
29
- /** The result interface of the validate function. */
30
- export type Result<Output> = SuccessResult<Output> | FailureResult;
31
- /** The result interface if validation succeeds. */
32
- export interface SuccessResult<Output> {
33
- /** The typed output value. */
34
- readonly value: Output;
35
- /** The non-existent issues. */
36
- readonly issues?: undefined;
37
- }
38
- /** The result interface if validation fails. */
39
- export interface FailureResult {
40
- /** The issues of failed validation. */
41
- readonly issues: ReadonlyArray<Issue>;
42
- }
43
- /** The issue interface of the failure output. */
44
- export interface Issue {
45
- /** The error message of the issue. */
46
- readonly message: string;
47
- /** The path of the issue, if any. */
48
- readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
49
- }
50
- /** The path segment interface of the issue. */
51
- export interface PathSegment {
52
- /** The key representing a path segment. */
53
- readonly key: PropertyKey;
54
- }
55
- /** The Standard Schema types interface. */
56
- export interface Types<Input = unknown, Output = Input> {
57
- /** The input type of the schema. */
58
- readonly input: Input;
59
- /** The output type of the schema. */
60
- readonly output: Output;
61
- }
62
- /** Infers the input type of a Standard Schema. */
63
- export type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
64
- /** Infers the output type of a Standard Schema. */
65
- export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
66
- export { };
67
- }
68
-
69
14
  declare function loadVendor(vendor: string, fn: {
70
15
  toJSONSchema?: Parameters<typeof loadVendor$1>[1];
71
16
  toOpenAPISchema?: Parameters<typeof loadVendor$2>[1];
@@ -94,13 +39,9 @@ type HasUndefined<T> = undefined extends T ? true : false;
94
39
  */
95
40
  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 = {
96
41
  in: HasUndefined<In> extends true ? {
97
- [K in Target]?: In extends ValidationTargets[K] ? In : {
98
- [K2 in keyof In]?: ValidationTargets[K][K2];
99
- };
42
+ [K in Target]?: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
100
43
  } : {
101
- [K in Target]: In extends ValidationTargets[K] ? In : {
102
- [K2 in keyof In]: ValidationTargets[K][K2];
103
- };
44
+ [K in Target]: [In] extends [ValidationTargets[K]] ? In : InferInput<In, K>;
104
45
  };
105
46
  out: {
106
47
  [K in Target]: Out;
@@ -132,6 +73,14 @@ declare function describeResponse<E extends Env, P extends string, I extends Inp
132
73
  * The unique symbol for the middlewares, which makes it easier to identify them. Not meant to be used directly, unless you're creating a custom middleware.
133
74
  */
134
75
  declare const uniqueSymbol: unique symbol;
76
+ /**
77
+ * Internal marker key set on an operation's spec when it is produced by a
78
+ * `validator()` middleware. It is used to decide whether to auto-inject the
79
+ * default 400 validation error response, and is stripped from the operation
80
+ * before the spec is emitted. Must be an enumerable string key so it survives
81
+ * the `Object.entries`-based merge in `mergeSpecs`.
82
+ */
83
+ declare const VALIDATION_MARKER = "__HonoOpenAPIValidator__";
135
84
  declare const ALLOWED_METHODS: readonly ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];
136
85
  type AllowedMethods = (typeof ALLOWED_METHODS)[number];
137
86
  declare function clearSpecsContext(): void;
@@ -158,13 +107,17 @@ type HandlerUniqueProperty = (ResolverReturnType & {
158
107
  type MediaTypeObjectWithResolver = Omit<OpenAPIV3_1.MediaTypeObject, "schema"> & {
159
108
  schema?: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject | ResolverReturnType;
160
109
  };
110
+ type ContentWithResolver = {
111
+ [media: string]: MediaTypeObjectWithResolver;
112
+ };
161
113
  /**
162
114
  * A response object that accepts resolver() output in schema positions.
163
115
  */
164
116
  type ResponseObjectWithResolver = (Omit<OpenAPIV3_1.ResponseObject, "content"> & {
165
- content?: {
166
- [media: string]: MediaTypeObjectWithResolver;
167
- };
117
+ content?: ContentWithResolver;
118
+ }) | OpenAPIV3_1.ReferenceObject;
119
+ type RequestBodyObjectWithResolver = (Omit<OpenAPIV3_1.RequestBodyObject, "content"> & {
120
+ content: ContentWithResolver;
168
121
  }) | OpenAPIV3_1.ReferenceObject;
169
122
  /**
170
123
  * A responses map that accepts resolver() output in schema positions.
@@ -217,10 +170,23 @@ type GenerateSpecOptions = {
217
170
  * Default options for `describeRoute` method
218
171
  */
219
172
  defaultOptions: Partial<Record<AllowedMethods | "ALL", DescribeRouteOptions>>;
173
+ /**
174
+ * Automatically include a 400 validation error response for routes
175
+ * that use `validator()`. Set to `false` to disable, `true` to use
176
+ * the built-in schema, or provide a custom `ResponseObject`.
177
+ *
178
+ * @default true
179
+ */
180
+ defaultValidationErrorResponse: boolean | OpenAPIV3_1.ResponseObject;
220
181
  };
221
182
  type OperationId = string | ((route: RouterRoute) => string);
222
- type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "responses" | "operationId"> & {
183
+ type DescribeRouteOptions = Omit<OpenAPIV3_1.OperationObject, "requestBody" | "responses" | "operationId"> & {
223
184
  operationId?: OperationId;
185
+ /**
186
+ * Request body metadata. `resolver()` values are converted to OpenAPI
187
+ * schemas while generating the document.
188
+ */
189
+ requestBody?: RequestBodyObjectWithResolver;
224
190
  /**
225
191
  * Pass `true` to hide route from OpenAPI/swagger document
226
192
  */
@@ -241,11 +207,12 @@ type RegisterSchemaPathOptions = {
241
207
  };
242
208
  paths: Partial<OpenAPIV3_1.PathsObject>;
243
209
  };
244
- type HaveDefaultValues = "documentation" | "excludeStaticFile" | "exclude" | "excludeMethods" | "excludeTags";
210
+ type HaveDefaultValues = "documentation" | "excludeStaticFile" | "exclude" | "excludeMethods" | "excludeTags" | "defaultValidationErrorResponse";
245
211
  type SanitizedGenerateSpecOptions = Pick<GenerateSpecOptions, HaveDefaultValues> & Omit<Partial<GenerateSpecOptions>, HaveDefaultValues>;
246
212
  type SpecContext = {
247
213
  components: OpenAPIV3_1.ComponentsObject;
248
214
  options: SanitizedGenerateSpecOptions;
215
+ validationErrorResponse?: OpenAPIV3_1.ResponseObject;
249
216
  };
250
217
 
251
218
  /**
@@ -298,4 +265,4 @@ declare function generateSpecs<E extends Env = BlankEnv, P extends string = stri
298
265
  jsonSchemaDialect?: string;
299
266
  }>;
300
267
 
301
- 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 };
268
+ export { ALLOWED_METHODS, type AllowedMethods, type ContentWithResolver, type DescribeRouteOptions, type GenerateSpecOptions, type HandlerUniqueProperty, type PromiseOr, type RegisterSchemaPathOptions, type ResolverReturnType, type ResponsesWithResolver, type SpecContext, VALIDATION_MARKER, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { loadVendor as loadVendor$1, toJsonSchema } from '@standard-community/st
4
4
  import { loadVendor as loadVendor$2, toOpenAPISchema } from '@standard-community/standard-openapi';
5
5
 
6
6
  const uniqueSymbol = Symbol("openapi");
7
+ const VALIDATION_MARKER = "__HonoOpenAPIValidator__";
7
8
  const ALLOWED_METHODS = [
8
9
  "GET",
9
10
  "PUT",
@@ -56,7 +57,9 @@ const specsByPathContext = /* @__PURE__ */ new Map();
56
57
  function getPathContext(path) {
57
58
  const context = [];
58
59
  for (const [key, data] of specsByPathContext) {
59
- if (data && path.match(key)) {
60
+ if (!data) continue;
61
+ const prefix = key.endsWith("/*") ? key.slice(0, -2) : key;
62
+ if (path === prefix || path.startsWith(`${prefix}/`)) {
60
63
  context.push(data);
61
64
  }
62
65
  }
@@ -157,6 +160,8 @@ function removeExcludedPaths(paths, ctx) {
157
160
  for (const method of Object.keys(value)) {
158
161
  const schema = value[method];
159
162
  if (schema == null) continue;
163
+ const hasValidation = schema[VALIDATION_MARKER] === true;
164
+ delete schema[VALIDATION_MARKER];
160
165
  if (key.includes("{")) {
161
166
  schema.parameters = schema.parameters ? [...schema.parameters] : [];
162
167
  const pathParameters = key.split("/").filter(
@@ -196,6 +201,16 @@ function removeExcludedPaths(paths, ctx) {
196
201
  200: {}
197
202
  };
198
203
  }
204
+ if (hasValidation && ctx.options.defaultValidationErrorResponse !== false && !schema.responses["400"]) {
205
+ const errorResponse = ctx.options.defaultValidationErrorResponse;
206
+ if (typeof errorResponse === "object") {
207
+ schema.responses["400"] = structuredClone(errorResponse);
208
+ } else if (ctx.validationErrorResponse) {
209
+ schema.responses["400"] = structuredClone(
210
+ ctx.validationErrorResponse
211
+ );
212
+ }
213
+ }
199
214
  }
200
215
  const filteredValue = {};
201
216
  for (const method of Object.keys(value)) {
@@ -215,7 +230,27 @@ const DEFAULT_OPTIONS = {
215
230
  excludeStaticFile: true,
216
231
  exclude: [],
217
232
  excludeMethods: ["OPTIONS"],
218
- excludeTags: []
233
+ excludeTags: [],
234
+ defaultValidationErrorResponse: true
235
+ };
236
+ const DEFAULT_VALIDATION_ERROR = {
237
+ description: "Validation Error",
238
+ content: {
239
+ "application/json": {
240
+ schema: {
241
+ type: "object",
242
+ properties: {
243
+ success: { type: "boolean", enum: [false] },
244
+ error: {
245
+ type: "array",
246
+ items: {}
247
+ },
248
+ data: {}
249
+ },
250
+ required: ["success", "error", "data"]
251
+ }
252
+ }
253
+ }
219
254
  };
220
255
  function openAPIRouteHandler(hono, options) {
221
256
  let specs;
@@ -232,9 +267,14 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
232
267
  options: {
233
268
  ...DEFAULT_OPTIONS,
234
269
  ...options
235
- }
270
+ },
271
+ validationErrorResponse: DEFAULT_VALIDATION_ERROR
236
272
  };
237
273
  const _documentation = ctx.options.documentation ?? {};
274
+ const documentation = {
275
+ ..._documentation,
276
+ components: _documentation.components && { ..._documentation.components }
277
+ };
238
278
  clearSpecsContext();
239
279
  const paths = await generatePaths(hono, ctx);
240
280
  for (const path in paths) {
@@ -250,27 +290,30 @@ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
250
290
  }
251
291
  }
252
292
  }
253
- const { components: resolvedDocComponents } = _documentation.components?.responses ? await resolveResponseSchemas(_documentation.components.responses) : { components: {} };
293
+ const resolvedDocumentation = documentation.components?.responses ? await resolveResponseSchemas(documentation.components.responses) : void 0;
294
+ if (resolvedDocumentation && documentation.components) {
295
+ documentation.components.responses = resolvedDocumentation.responses;
296
+ }
254
297
  const components = mergeComponentsObjects(
255
- _documentation.components,
256
- resolvedDocComponents,
298
+ documentation.components,
299
+ resolvedDocumentation?.components,
257
300
  ctx.components
258
301
  );
259
302
  return {
260
303
  openapi: "3.1.0",
261
- ..._documentation,
262
- tags: _documentation.tags?.filter(
304
+ ...documentation,
305
+ tags: documentation.tags?.filter(
263
306
  (tag) => !ctx.options.excludeTags?.includes(tag?.name)
264
307
  ),
265
308
  info: {
266
309
  title: "Hono Documentation",
267
310
  description: "Development documentation",
268
311
  version: "0.0.0",
269
- ..._documentation.info
312
+ ...documentation.info
270
313
  },
271
314
  paths: {
272
315
  ...removeExcludedPaths(paths, ctx),
273
- ..._documentation.paths
316
+ ...documentation.paths
274
317
  },
275
318
  components
276
319
  };
@@ -336,12 +379,27 @@ async function getSpec(middlewareHandler, defaultOptions) {
336
379
  let components = {};
337
380
  if (tmp.responses) {
338
381
  const resolved = await resolveResponseSchemas(tmp.responses);
382
+ tmp.responses = resolved.responses;
339
383
  components = resolved.components;
340
384
  }
385
+ if (tmp.requestBody && "content" in tmp.requestBody) {
386
+ const resolved = await resolveContentSchemas(tmp.requestBody.content);
387
+ tmp.requestBody = {
388
+ ...tmp.requestBody,
389
+ content: resolved.content
390
+ };
391
+ components = mergeComponentsObjects(components, resolved.components);
392
+ }
341
393
  return { schema: tmp, components };
342
394
  }
343
395
  const result = await middlewareHandler.toOpenAPISchema();
344
- const docs = { ...defaultOptions };
396
+ liftSchemaDefs(result);
397
+ const docs = {
398
+ ...defaultOptions,
399
+ // Mark this operation as validator-derived so a default 400 validation
400
+ // error response can be auto-injected later (see removeExcludedPaths).
401
+ [VALIDATION_MARKER]: true
402
+ };
345
403
  if (middlewareHandler.target === "form" || middlewareHandler.target === "json") {
346
404
  const media = middlewareHandler.options?.media ?? middlewareHandler.target === "json" ? "application/json" : "multipart/form-data";
347
405
  if (!docs.requestBody || !("content" in docs.requestBody) || !docs.requestBody.content) {
@@ -379,51 +437,113 @@ async function getSpec(middlewareHandler, defaultOptions) {
379
437
  });
380
438
  }
381
439
  } else {
382
- parameters = generateParameters(middlewareHandler.target, result.schema);
440
+ parameters = generateParameters(
441
+ middlewareHandler.target,
442
+ result.schema,
443
+ result.components?.schemas
444
+ );
383
445
  }
384
446
  docs.parameters = parameters;
385
447
  }
386
448
  return { schema: docs, components: result.components };
387
449
  }
388
- function generateParameters(target, schema) {
450
+ function generateParameters(target, schema, schemas) {
389
451
  const parameters = [];
390
- for (const [key, value] of Object.entries(schema.properties ?? {})) {
452
+ for (const [key, property] of collectParameterProperties(schema, schemas)) {
391
453
  const def = {
392
454
  in: target === "param" ? "path" : target,
393
455
  name: key,
394
- // @ts-expect-error
395
- schema: value
456
+ // @ts-expect-error ParameterObject uses the OpenAPI 3.0 schema type.
457
+ schema: property.schema
396
458
  };
397
- const isRequired = schema.required?.includes(key);
398
- if (isRequired) {
459
+ if (property.required) {
399
460
  def.required = true;
400
461
  }
401
462
  if (def.schema && "description" in def.schema && def.schema.description) {
402
463
  def.description = def.schema.description;
403
- def.schema.description = void 0;
464
+ def.schema = { ...def.schema, description: void 0 };
404
465
  }
405
466
  parameters.push(def);
406
467
  }
407
468
  return parameters;
408
469
  }
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);
470
+ function collectParameterProperties(schema, schemas) {
471
+ const properties = /* @__PURE__ */ new Map();
472
+ const resolvingRefs = /* @__PURE__ */ new Set();
473
+ const collect = (current) => {
474
+ for (const [key, value] of Object.entries(current.properties ?? {})) {
475
+ const existing = properties.get(key);
476
+ properties.set(key, {
477
+ schema: existing ? { allOf: [existing.schema, value] } : value,
478
+ required: Boolean(current.required?.includes(key)) || existing?.required
479
+ });
480
+ }
481
+ for (const member of current.allOf ?? []) {
482
+ if ("$ref" in member) {
483
+ const prefix = "#/components/schemas/";
484
+ if (!member.$ref.startsWith(prefix)) continue;
485
+ const name = member.$ref.slice(prefix.length).replaceAll("~1", "/").replaceAll("~0", "~");
486
+ const referencedSchema = schemas?.[name];
487
+ if (referencedSchema && !resolvingRefs.has(name)) {
488
+ resolvingRefs.add(name);
489
+ collect(referencedSchema);
490
+ resolvingRefs.delete(name);
422
491
  }
492
+ } else {
493
+ collect(member);
423
494
  }
424
495
  }
496
+ };
497
+ collect(schema);
498
+ return properties;
499
+ }
500
+ async function resolveContentSchemas(content) {
501
+ let components = {};
502
+ const resolvedContent = {};
503
+ for (const [contentKey, raw] of Object.entries(content)) {
504
+ if (!raw) continue;
505
+ if (raw.schema && "toOpenAPISchema" in raw.schema) {
506
+ const result = await raw.schema.toOpenAPISchema();
507
+ liftSchemaDefs(result);
508
+ resolvedContent[contentKey] = {
509
+ ...raw,
510
+ schema: result.schema
511
+ };
512
+ if (result.components) {
513
+ components = mergeComponentsObjects(components, result.components);
514
+ }
515
+ } else {
516
+ resolvedContent[contentKey] = raw;
517
+ }
425
518
  }
426
- return { responses, components };
519
+ return { content: resolvedContent, components };
520
+ }
521
+ async function resolveResponseSchemas(responses) {
522
+ let components = {};
523
+ const resolvedResponses = {};
524
+ for (const [key, response] of Object.entries(responses)) {
525
+ if (!response || !("content" in response) || !response.content) {
526
+ resolvedResponses[key] = response;
527
+ continue;
528
+ }
529
+ const resolved = await resolveContentSchemas(response.content);
530
+ resolvedResponses[key] = {
531
+ ...response,
532
+ content: resolved.content
533
+ };
534
+ components = mergeComponentsObjects(components, resolved.components);
535
+ }
536
+ return { responses: resolvedResponses, components };
537
+ }
538
+ function liftSchemaDefs(result) {
539
+ const defs = result.schema.$defs;
540
+ if (!defs || typeof defs !== "object") return;
541
+ result.components ??= {};
542
+ result.components.schemas = {
543
+ ...defs,
544
+ ...result.components.schemas
545
+ };
546
+ delete result.schema.$defs;
427
547
  }
428
548
  function mergeComponentsObjects(...components) {
429
549
  return components.reduce(
@@ -508,6 +628,10 @@ function injectZodV4DateOverride(schema, userDefined, custom) {
508
628
  }
509
629
  function validator(target, schema, hook, options) {
510
630
  const middleware = sValidator(target, schema, hook);
631
+ Object.defineProperty(middleware, "name", {
632
+ value: "validator",
633
+ configurable: true
634
+ });
511
635
  return Object.assign(middleware, {
512
636
  [uniqueSymbol]: {
513
637
  target,
@@ -520,6 +644,10 @@ function describeRoute(spec) {
520
644
  const middleware = async (_c, next) => {
521
645
  await next();
522
646
  };
647
+ Object.defineProperty(middleware, "name", {
648
+ value: "describeRoute",
649
+ configurable: true
650
+ });
523
651
  return Object.assign(middleware, {
524
652
  [uniqueSymbol]: {
525
653
  spec
@@ -560,4 +688,4 @@ function describeResponse(handler, responses, options) {
560
688
  });
561
689
  }
562
690
 
563
- export { ALLOWED_METHODS, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
691
+ export { ALLOWED_METHODS, VALIDATION_MARKER, clearSpecsContext, describeResponse, describeRoute, generateSpecs, loadVendor, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
package/package.json CHANGED
@@ -1,86 +1,78 @@
1
- {
2
- "name": "hono-openapi",
3
- "description": "OpenAPI schema generator for Hono",
4
- "version": "1.3.0",
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
- "scripts": {
14
- "build": "pkgroll --clean-dist",
15
- "lint": "biome check .",
16
- "format": "biome check --write .",
17
- "prepare": "is-ci || husky",
18
- "test": "vitest"
19
- },
20
- "keywords": [
21
- "hono",
22
- "openapi",
23
- "zod",
24
- "valibot",
25
- "typebox",
26
- "arktype",
27
- "effect"
28
- ],
29
- "homepage": "https://github.com/rhinobase/hono-openapi",
30
- "publishConfig": {
31
- "access": "public"
32
- },
33
- "repository": {
34
- "type": "git",
35
- "url": "git+https://github.com/rhinobase/hono-openapi.git"
36
- },
37
- "bugs": {
38
- "url": "https://github.com/rhinobase/hono-openapi/issues"
39
- },
40
- "exports": {
41
- "import": {
42
- "types": "./dist/index.d.ts",
43
- "default": "./dist/index.js"
44
- },
45
- "require": {
46
- "types": "./dist/index.d.cts",
47
- "default": "./dist/index.cjs"
48
- }
49
- },
50
- "peerDependencies": {
51
- "@hono/standard-validator": "^0.2.0",
52
- "@standard-community/standard-json": "^0.3.5",
53
- "@standard-community/standard-openapi": "^0.2.9",
54
- "@types/json-schema": "^7.0.15",
55
- "hono": "^4.8.3",
56
- "openapi-types": "^12.1.3"
57
- },
58
- "peerDependenciesMeta": {
59
- "@hono/standard-validator": {
60
- "optional": true
61
- },
62
- "hono": {
63
- "optional": true
64
- }
65
- },
66
- "devDependencies": {
67
- "@biomejs/biome": "^2.0.6",
68
- "@standard-schema/spec": "^1.0.0",
69
- "@valibot/to-json-schema": "^1.3.0",
70
- "arktype": "^2.1.22",
71
- "effect": "^3.17.13",
72
- "husky": "^9.1.7",
73
- "is-ci": "^4.1.0",
74
- "nano-staged": "^0.8.0",
75
- "pkg-pr-new": "^0.0.60",
76
- "pkgroll": "^2.13.1",
77
- "typebox": "^1.0.17",
78
- "typescript": "^5.8.3",
79
- "sury": "^10.0.0",
80
- "valibot": "^1.1.0",
81
- "vitest": "^3.2.4",
82
- "zod": "^3.23.8",
83
- "zod-openapi": "^4"
84
- },
85
- "packageManager": "pnpm@10.0.0"
86
- }
1
+ {
2
+ "name": "hono-openapi",
3
+ "description": "OpenAPI schema generator for Hono",
4
+ "version": "1.3.2",
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
+ "dependencies": {
44
+ "@hono/standard-validator": "^0.4.0",
45
+ "@standard-community/standard-json": "^0.3.5",
46
+ "@standard-community/standard-openapi": "^0.2.9",
47
+ "@standard-schema/spec": "^1.0.0",
48
+ "@types/json-schema": "^7.0.15",
49
+ "openapi-types": "^12.1.3"
50
+ },
51
+ "peerDependencies": {
52
+ "hono": "^4.11.2"
53
+ },
54
+ "devDependencies": {
55
+ "@biomejs/biome": "^2.0.6",
56
+ "@valibot/to-json-schema": "^1.3.0",
57
+ "arktype": "^2.1.22",
58
+ "effect": "^3.17.14",
59
+ "husky": "^9.1.7",
60
+ "is-ci": "^4.1.0",
61
+ "nano-staged": "^0.8.0",
62
+ "pkg-pr-new": "^0.0.60",
63
+ "pkgroll": "^2.13.1",
64
+ "typebox": "^1.0.17",
65
+ "typescript": "^5.8.3",
66
+ "sury": "^10.0.0",
67
+ "valibot": "^1.1.0",
68
+ "vitest": "^3.2.4",
69
+ "zod": "^3.23.8",
70
+ "zod-openapi": "^4"
71
+ },
72
+ "scripts": {
73
+ "build": "pkgroll --clean-dist",
74
+ "lint": "biome check .",
75
+ "format": "biome check --write .",
76
+ "test": "vitest"
77
+ }
78
+ }