hono-openapi 0.4.3 → 0.4.4

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/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("hono/http-exception"),t=require("./utils.cjs");const n=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],s=e=>e.charAt(0).toUpperCase()+e.slice(1),o=(e,t)=>{let n=e;if("/"===t)return`${n}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?n+=`By${s(e.slice(1,-1))}`:n+=s(e);return n};function c({path:e,method:t,data:n,schema:s}){e=(e=>e.split("/").map((e=>{let t=e;return t.startsWith(":")&&(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`),t})).join("/"))(e);const c=t.toLowerCase();s[e]={...s[e]?s[e]:{},[c]:{responses:{},...s[e]?.[c]??{},operationId:o(c,e),...n}}}function a(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={},o=Array.isArray(n)?n:[n];for(const[n,c]of Object.entries(e))if(!(o.some((e=>"string"==typeof e?n===e:e.test(n)))||n.includes("*")||t&&n.includes("."))){for(const e of Object.keys(c)){const t=c[e];if(n.includes("{")){t.parameters||(t.parameters=[]);const e=n.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1)))));for(const n of e){const e=n.slice(1,n.length-1),s=t.parameters.findIndex((t=>"param"===t.in&&t.name===e));-1!==s?t.parameters[s].in="path":t.parameters.push({schema:{type:"string"},in:"path",name:e,required:!0})}}t.responses||(t.responses={200:{}})}s[n]=c}return s}async function i(e,{documentation:s={},excludeStaticFile:o=!0,exclude:i=[],excludeMethods:r=["OPTIONS"],excludeTags:p=[],defaultOptions:l}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},{version:d="3.1.0",components:m={}}={version:"3.1.0",components:{}},u){const h={version:d,components:m},f={};for(const s of e.routes){if(!(t.uniqueSymbol in s.handler))continue;if(r.includes(s.method))continue;if(!1===n.includes(s.method)&&"ALL"!==s.method)continue;const{resolver:e,metadata:o={}}=s.handler[t.uniqueSymbol],a=l?.[s.method],{docs:i,components:p}=await e({...h,...o},a);if(h.components={...h.components,...p??{}},"ALL"===s.method)for(const e of n)c({path:s.path,data:i,method:e,schema:f});else c({method:s.method,path:s.path,data:i,schema:f})}for(const e in f)for(const t in f[e]){const n=f[e][t]?.hide;n&&("boolean"==typeof n?n:u&&n(u))&&delete f[e][t]}return{openapi:h.version,...{...s,tags:s.tags?.filter((e=>!p?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...s.info},paths:{...a(f,{excludeStaticFile:o,exclude:i}),...s.paths},components:{...s.components,schemas:{...h.components,...s.components?.schemas}}}}}exports.describeRoute=function(n){const{validateResponse:s,...o}=n;return Object.assign((async(t,o)=>{if(await o(),s&&n.responses){const s=t.res.status,o=t.res.headers.get("content-type");if(s&&o){const c=n.responses[s];if(c&&"content"in c&&c.content){const n=o.split(";")[0],s=c.content[n];if(s?.schema&&"validator"in s.schema)try{let e;const o=t.res.clone();if("application/json"===n?e=await o.json():"text/plain"===n&&(e=await o.text()),!e)throw new Error("No data to validate!");await s.schema.validator(e)}catch(t){throw new e.HTTPException(400,{message:"Response validation failed!",cause:t})}}}}}),{[t.uniqueSymbol]:{resolver:(e,t)=>async function(e,t,n={}){let s={};const o={...n,...t,responses:{...n?.responses,...t.responses}};for(const t of Object.keys(o.responses)){const n=o.responses[t];if(n&&"content"in n)for(const t of Object.keys(n.content??{})){const o=n.content?.[t];if(o&&(o.schema&&"builder"in o.schema)){const t=await o.schema.builder(e);o.schema=t.schema,t.components&&(s={...s,...t.components})}}}return{docs:o,components:s}}(e,o,t)}})},exports.generateSpecs=i,exports.openAPISpecs=function(e,t){const n={version:"3.1.0",components:{}};let s=null;return async o=>(s||(s=await i(e,t,n,o)),o.json(s))};
1
+ "use strict";var e=require("./utils.cjs"),t=require("hono/http-exception");const n=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],s=e=>e.charAt(0).toUpperCase()+e.slice(1),o=(e,t)=>{let n=e;if("/"===t)return`${n}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?n+=`By${s(e.slice(1,-1))}`:n+=s(e);return n};function c({path:e,method:t,data:n,schema:s}){e=(e=>e.split("/").map((e=>{let t=e;return t.startsWith(":")&&(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`),t})).join("/"))(e);const c=t.toLowerCase();s[e]={...s[e]?s[e]:{},[c]:{responses:{},...s[e]?.[c]??{},operationId:o(c,e),...n}}}function a(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={},o=Array.isArray(n)?n:[n];for(const[n,c]of Object.entries(e))if(!(o.some((e=>"string"==typeof e?n===e:e.test(n)))||n.includes("*")||t&&n.includes("."))){for(const e of Object.keys(c)){const t=c[e];if(n.includes("{")){t.parameters||(t.parameters=[]);const e=n.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1)))));for(const n of e){const e=n.slice(1,n.length-1),s=t.parameters.findIndex((t=>"param"===t.in&&t.name===e));-1!==s?t.parameters[s].in="path":t.parameters.push({schema:{type:"string"},in:"path",name:e,required:!0})}}t.responses||(t.responses={200:{}})}s[n]=c}return s}async function i(t,{documentation:s={},excludeStaticFile:o=!0,exclude:i=[],excludeMethods:r=["OPTIONS"],excludeTags:p=[],defaultOptions:l}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},{version:d="3.1.0",components:u={}}={version:"3.1.0",components:{}},m){const h={version:d,components:u},f={};for(const s of t.routes){if(!(e.uniqueSymbol in s.handler))continue;if(r.includes(s.method))continue;if(!1===n.includes(s.method)&&"ALL"!==s.method)continue;const{resolver:t,metadata:o={}}=s.handler[e.uniqueSymbol],a=l?.[s.method],{docs:i,components:p}=await t({...h,...o},a);if(h.components={...h.components,...p??{}},"ALL"===s.method)for(const e of n)c({path:s.path,data:i,method:e,schema:f});else c({method:s.method,path:s.path,data:i,schema:f})}for(const e in f)for(const t in f[e]){const n=f[e][t]?.hide;n&&("boolean"==typeof n?n:m&&n(m))&&delete f[e][t]}return{openapi:h.version,...{...s,tags:s.tags?.filter((e=>!p?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...s.info},paths:{...a(f,{excludeStaticFile:o,exclude:i}),...s.paths},components:{...s.components,schemas:{...h.components,...s.components?.schemas}}}}}exports.generateValidatorDocs=e.generateValidatorDocs,exports.uniqueSymbol=e.uniqueSymbol,exports.describeRoute=function(n){const{validateResponse:s,...o}=n;return Object.assign((async(e,o)=>{if(await o(),s&&n.responses){const o=e.res.status,c=e.res.headers.get("content-type");if(o&&c){const a=n.responses[o];if(a&&"content"in a&&a.content){const n=c.split(";")[0],o=a.content[n];if(o?.schema&&"validator"in o.schema)try{let t;const s=e.res.clone();if("application/json"===n?t=await s.json():"text/plain"===n&&(t=await s.text()),!t)throw new Error("No data to validate!");await o.schema.validator(t)}catch(e){let n={status:500,message:"Response validation failed!"};throw"object"==typeof s&&(n={...n,...s}),new t.HTTPException(n.status,{message:n.message,cause:e})}}}}}),{[e.uniqueSymbol]:{resolver:(e,t)=>async function(e,t,n={}){let s={};const o={...n,...t,responses:{...n?.responses,...t.responses}};for(const t of Object.keys(o.responses)){const n=o.responses[t];if(n&&"content"in n)for(const t of Object.keys(n.content??{})){const o=n.content?.[t];if(o&&(o.schema&&"builder"in o.schema)){const t=await o.schema.builder(e);o.schema=t.schema,t.components&&(s={...s,...t.components})}}}return{docs:o,components:s}}(e,o,t)}})},exports.generateSpecs=i,exports.openAPISpecs=function(e,t){const n={version:"3.1.0",components:{}};let s=null;return async o=>(s||(s=await i(e,t,n,o)),o.json(s))};
package/index.js CHANGED
@@ -1 +1 @@
1
- import{HTTPException as e}from"hono/http-exception";import{u as t}from"./utils.js";function n(n){const{validateResponse:s,...o}=n;return Object.assign((async(t,o)=>{if(await o(),s&&n.responses){const s=t.res.status,o=t.res.headers.get("content-type");if(s&&o){const c=n.responses[s];if(c&&"content"in c&&c.content){const n=o.split(";")[0],s=c.content[n];if(s?.schema&&"validator"in s.schema)try{let e;const o=t.res.clone();if("application/json"===n?e=await o.json():"text/plain"===n&&(e=await o.text()),!e)throw new Error("No data to validate!");await s.schema.validator(e)}catch(t){throw new e(400,{message:"Response validation failed!",cause:t})}}}}}),{[t]:{resolver:(e,t)=>async function(e,t,n={}){let s={};const o={...n,...t,responses:{...n?.responses,...t.responses}};for(const t of Object.keys(o.responses)){const n=o.responses[t];if(n&&"content"in n)for(const t of Object.keys(n.content??{})){const o=n.content?.[t];if(o&&(o.schema&&"builder"in o.schema)){const t=await o.schema.builder(e);o.schema=t.schema,t.components&&(s={...s,...t.components})}}}return{docs:o,components:s}}(e,o,t)}})}const s=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],o=e=>e.charAt(0).toUpperCase()+e.slice(1),c=(e,t)=>{let n=e;if("/"===t)return`${n}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?n+=`By${o(e.slice(1,-1))}`:n+=o(e);return n};function a({path:e,method:t,data:n,schema:s}){e=(e=>e.split("/").map((e=>{let t=e;return t.startsWith(":")&&(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`),t})).join("/"))(e);const o=t.toLowerCase();s[e]={...s[e]?s[e]:{},[o]:{responses:{},...s[e]?.[o]??{},operationId:c(o,e),...n}}}function i(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={},o=Array.isArray(n)?n:[n];for(const[n,c]of Object.entries(e))if(!(o.some((e=>"string"==typeof e?n===e:e.test(n)))||n.includes("*")||t&&n.includes("."))){for(const e of Object.keys(c)){const t=c[e];if(n.includes("{")){t.parameters||(t.parameters=[]);const e=n.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1)))));for(const n of e){const e=n.slice(1,n.length-1),s=t.parameters.findIndex((t=>"param"===t.in&&t.name===e));-1!==s?t.parameters[s].in="path":t.parameters.push({schema:{type:"string"},in:"path",name:e,required:!0})}}t.responses||(t.responses={200:{}})}s[n]=c}return s}function r(e,t){const n={version:"3.1.0",components:{}};let s=null;return async o=>(s||(s=await p(e,t,n,o)),o.json(s))}async function p(e,{documentation:n={},excludeStaticFile:o=!0,exclude:c=[],excludeMethods:r=["OPTIONS"],excludeTags:p=[],defaultOptions:l}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},{version:d="3.1.0",components:m={}}={version:"3.1.0",components:{}},h){const f={version:d,components:m},u={};for(const n of e.routes){if(!(t in n.handler))continue;if(r.includes(n.method))continue;if(!1===s.includes(n.method)&&"ALL"!==n.method)continue;const{resolver:e,metadata:o={}}=n.handler[t],c=l?.[n.method],{docs:i,components:p}=await e({...f,...o},c);if(f.components={...f.components,...p??{}},"ALL"===n.method)for(const e of s)a({path:n.path,data:i,method:e,schema:u});else a({method:n.method,path:n.path,data:i,schema:u})}for(const e in u)for(const t in u[e]){const n=u[e][t]?.hide;n&&("boolean"==typeof n?n:h&&n(h))&&delete u[e][t]}return{openapi:f.version,...{...n,tags:n.tags?.filter((e=>!p?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...n.info},paths:{...i(u,{excludeStaticFile:o,exclude:c}),...n.paths},components:{...n.components,schemas:{...f.components,...n.components?.schemas}}}}}export{n as describeRoute,p as generateSpecs,r as openAPISpecs};
1
+ import{u as e}from"./utils.js";export{g as generateValidatorDocs}from"./utils.js";import{HTTPException as t}from"hono/http-exception";const s=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],n=e=>e.charAt(0).toUpperCase()+e.slice(1),o=(e,t)=>{let s=e;if("/"===t)return`${s}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?s+=`By${n(e.slice(1,-1))}`:s+=n(e);return s};function a({path:e,method:t,data:s,schema:n}){e=(e=>e.split("/").map((e=>{let t=e;return t.startsWith(":")&&(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`),t})).join("/"))(e);const a=t.toLowerCase();n[e]={...n[e]?n[e]:{},[a]:{responses:{},...n[e]?.[a]??{},operationId:o(a,e),...s}}}function c(e,{excludeStaticFile:t=!0,exclude:s=[]}){const n={},o=Array.isArray(s)?s:[s];for(const[s,a]of Object.entries(e))if(!(o.some((e=>"string"==typeof e?s===e:e.test(s)))||s.includes("*")||t&&s.includes("."))){for(const e of Object.keys(a)){const t=a[e];if(s.includes("{")){t.parameters||(t.parameters=[]);const e=s.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1)))));for(const s of e){const e=s.slice(1,s.length-1),n=t.parameters.findIndex((t=>"param"===t.in&&t.name===e));-1!==n?t.parameters[n].in="path":t.parameters.push({schema:{type:"string"},in:"path",name:e,required:!0})}}t.responses||(t.responses={200:{}})}n[s]=a}return n}function i(e,t){const s={version:"3.1.0",components:{}};let n=null;return async o=>(n||(n=await r(e,t,s,o)),o.json(n))}async function r(t,{documentation:n={},excludeStaticFile:o=!0,exclude:i=[],excludeMethods:r=["OPTIONS"],excludeTags:p=[],defaultOptions:l}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},{version:m="3.1.0",components:d={}}={version:"3.1.0",components:{}},h){const u={version:m,components:d},f={};for(const n of t.routes){if(!(e in n.handler))continue;if(r.includes(n.method))continue;if(!1===s.includes(n.method)&&"ALL"!==n.method)continue;const{resolver:t,metadata:o={}}=n.handler[e],c=l?.[n.method],{docs:i,components:p}=await t({...u,...o},c);if(u.components={...u.components,...p??{}},"ALL"===n.method)for(const e of s)a({path:n.path,data:i,method:e,schema:f});else a({method:n.method,path:n.path,data:i,schema:f})}for(const e in f)for(const t in f[e]){const s=f[e][t]?.hide;s&&("boolean"==typeof s?s:h&&s(h))&&delete f[e][t]}return{openapi:u.version,...{...n,tags:n.tags?.filter((e=>!p?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...n.info},paths:{...c(f,{excludeStaticFile:o,exclude:i}),...n.paths},components:{...n.components,schemas:{...u.components,...n.components?.schemas}}}}}function p(s){const{validateResponse:n,...o}=s;return Object.assign((async(e,o)=>{if(await o(),n&&s.responses){const o=e.res.status,a=e.res.headers.get("content-type");if(o&&a){const c=s.responses[o];if(c&&"content"in c&&c.content){const s=a.split(";")[0],o=c.content[s];if(o?.schema&&"validator"in o.schema)try{let t;const n=e.res.clone();if("application/json"===s?t=await n.json():"text/plain"===s&&(t=await n.text()),!t)throw new Error("No data to validate!");await o.schema.validator(t)}catch(e){let s={status:500,message:"Response validation failed!"};throw"object"==typeof n&&(s={...s,...n}),new t(s.status,{message:s.message,cause:e})}}}}}),{[e]:{resolver:(e,t)=>async function(e,t,s={}){let n={};const o={...s,...t,responses:{...s?.responses,...t.responses}};for(const t of Object.keys(o.responses)){const s=o.responses[t];if(s&&"content"in s)for(const t of Object.keys(s.content??{})){const o=s.content?.[t];if(o&&(o.schema&&"builder"in o.schema)){const t=await o.schema.builder(e);o.schema=t.schema,t.components&&(n={...n,...t.components})}}}return{docs:o,components:n}}(e,o,t)}})}export{p as describeRoute,r as generateSpecs,i as openAPISpecs,e as uniqueSymbol};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hono-openapi",
3
3
  "description": "OpenAPI schema generator for Hono",
4
- "version": "0.4.3",
4
+ "version": "0.4.4",
5
5
  "types": "./index.d.ts",
6
6
  "main": "./index.js",
7
7
  "type": "module",
package/src/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { generateSpecs, openAPISpecs } from "./openapi.js";
1
2
  export { describeRoute } from "./route.js";
2
- export { openAPISpecs, generateSpecs } from "./openapi.js";
3
3
  export type * from "./types.js";
4
+ export * from "./utils.js";
package/src/route.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { MiddlewareHandler } from "hono/types";
2
+ import type { ClientErrorStatusCode, ServerErrorStatusCode } from "hono/utils/http-status";
2
3
  import type { DescribeRouteOptions, OpenAPIRouteHandlerConfig } from "./types.js";
3
4
  /**
4
5
  * Describe a route with OpenAPI specs.
@@ -37,7 +38,10 @@ export declare function generateRouteSpecs(config: OpenAPIRouteHandlerConfig, do
37
38
  security?: import("openapi-types").OpenAPIV3.SecurityRequirementObject[];
38
39
  servers?: import("openapi-types").OpenAPIV3.ServerObject[];
39
40
  hide?: boolean | (<E extends import("hono").Env = import("hono").Env, P extends string = string, I extends import("hono").Input = import("hono/types").BlankInput>(c: import("hono").Context<E, P, I>) => boolean);
40
- validateResponse?: boolean;
41
+ validateResponse?: boolean | {
42
+ status: ClientErrorStatusCode | ServerErrorStatusCode;
43
+ message?: string;
44
+ };
41
45
  };
42
46
  components: {};
43
47
  }>;
package/src/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Context, Env, Input } from "hono";
2
2
  import type { BlankInput } from "hono/types";
3
+ import type { ClientErrorStatusCode, ServerErrorStatusCode } from "hono/utils/http-status";
3
4
  import type { OpenAPIV3 } from "openapi-types";
4
5
  import type { ALLOWED_METHODS } from "./helper.js";
5
6
  export type HasUndefined<T> = undefined extends T ? true : false;
@@ -32,7 +33,10 @@ export type DescribeRouteOptions = Omit<OpenAPIV3.OperationObject, "responses">
32
33
  /**
33
34
  * Validate response of the route
34
35
  */
35
- validateResponse?: boolean;
36
+ validateResponse?: boolean | {
37
+ status: ClientErrorStatusCode | ServerErrorStatusCode;
38
+ message?: string;
39
+ };
36
40
  /**
37
41
  * Responses of the request
38
42
  */
package/src/utils.d.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  import type { ValidationTargets } from "hono";
2
2
  import type { OpenAPIV3 } from "openapi-types";
3
3
  import type { ResolverResult } from "./types.js";
4
+ /**
5
+ * 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.
6
+ */
4
7
  export declare const uniqueSymbol: unique symbol;
8
+ /**
9
+ * Generate OpenAPI docs for a validator middleware. Not meant to be used directly, unless you're creating a custom middleware.
10
+ */
5
11
  export declare function generateValidatorDocs<Target extends keyof ValidationTargets>(target: Target, _result: ReturnType<ResolverResult["builder"]>): Promise<{
6
12
  docs: Pick<{
7
13
  tags?: string[];