hono-openapi 0.1.0 → 0.1.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.
@@ -0,0 +1 @@
1
+ "use strict";const o=Symbol("openapi");exports.uniqueSymbol=o;
@@ -0,0 +1 @@
1
+ const o=Symbol("openapi");export{o as u};
package/index.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/index.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("./utils.cjs.js");exports.describeRoute=function(t){return Object.assign((async(e,t)=>{await t()}),{[e.uniqueSymbol]:{resolver:e=>{const o={...t};let n={};if(o.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=o.schema.builder(e);o.schema=t.schema,t.components&&(n={...n,...t.components})}}}return{docs:o,components:n}}}})},exports.openAPISpecs=function(t,{documentation:o={},excludeStaticFile:n=!0,exclude:s=[],excludeMethods:c=["OPTIONS"],excludeTags:i=[]}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]}){const a={version:"3.0.3",components:{}},r={};for(const o of t.routes){if(!(e.uniqueSymbol in o.handler))continue;if(c.includes(o.method))continue;if(!1===e.ALLOWED_METHODS.includes(o.method)&&"ALL"!==o.method)continue;const{resolver:t,metadata:n={}}=o.handler[e.uniqueSymbol],{docs:s,components:i}=t({...a,...n});if(a.components={...a.components,...i??{}},"ALL"===o.method)for(const t of e.ALLOWED_METHODS)e.registerSchemaPath({path:o.path,data:s,method:t,schema:r});else e.registerSchemaPath({method:o.method,path:o.path,data:s,schema:r})}for(const e in r)for(const t in r[e])r[e][t].hide&&delete r[e][t];const m={openapi:a.version,...{...o,tags:o.tags?.filter((e=>!i?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...o.info},paths:{...e.filterPaths(r,{excludeStaticFile:n,exclude:Array.isArray(s)?s:[s]}),...o.paths},components:{...o.components,schemas:{...a.components,...o.components?.schemas}}}};return e=>e.json(m)};
1
+ "use strict";var e=require("./constants.cjs.js");const t=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],n=e=>e.charAt(0).toUpperCase()+e.slice(1),s=(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 o({path:e,method:t,data:n,schema:o}){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();o[e]={...o[e]?o[e]:{},[c]:{responses:{},...o[e]?.[c]??{},operationId:s(c,e),...n}}}function c(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={};for(const[o,c]of Object.entries(e))if(!(n.some((e=>"string"==typeof e?o===e:e.test(o)))||o.includes("*")||t&&o.includes("."))){for(const e of Object.keys(c)){const t=c[e];o.includes("{")&&(t.parameters||(t.parameters=[]),t.parameters=[...o.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1))))).map((e=>({schema:{type:"string"},in:"path",name:e.slice(1,e.length-1),required:!0}))),...t.parameters]),t.responses||(t.responses={200:{}})}s[o]=c}return s}exports.describeRoute=function(t){return Object.assign((async(e,t)=>{await t()}),{[e.uniqueSymbol]:{resolver:e=>{const n={...t};let s={};if(n.responses)for(const t of Object.keys(n.responses)){const o=n.responses[t];if(!o||"content"in o)for(const t of Object.keys(o.content??{})){const n=o.content?.[t];if(n&&(n.schema&&"builder"in n.schema)){const t=n.schema.builder(e);n.schema=t.schema,t.components&&(s={...s,...t.components})}}}return{docs:n,components:s}}}})},exports.openAPISpecs=function(n,{documentation:s={},excludeStaticFile:i=!0,exclude:r=[],excludeMethods:a=["OPTIONS"],excludeTags:l=[]}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]}){const d={version:"3.0.3",components:{}},m={};for(const s of n.routes){if(!(e.uniqueSymbol in s.handler))continue;if(a.includes(s.method))continue;if(!1===t.includes(s.method)&&"ALL"!==s.method)continue;const{resolver:n,metadata:c={}}=s.handler[e.uniqueSymbol],{docs:i,components:r}=n({...d,...c});if(d.components={...d.components,...r??{}},"ALL"===s.method)for(const e of t)o({path:s.path,data:i,method:e,schema:m});else o({method:s.method,path:s.path,data:i,schema:m})}for(const e in m)for(const t in m[e])m[e][t].hide&&delete m[e][t];const p={openapi:d.version,...{...s,tags:s.tags?.filter((e=>!l?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...s.info},paths:{...c(m,{excludeStaticFile:i,exclude:Array.isArray(r)?r:[r]}),...s.paths},components:{...s.components,schemas:{...d.components,...s.components?.schemas}}}};return e=>e.json(p)};
package/index.esm.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/index";
package/index.esm.js ADDED
@@ -0,0 +1 @@
1
+ import{u as e}from"./constants.esm.js";function t(t){return Object.assign((async(e,t)=>{await t()}),{[e]:{resolver:e=>{const n={...t};let s={};if(n.responses)for(const t of Object.keys(n.responses)){const o=n.responses[t];if(!o||"content"in o)for(const t of Object.keys(o.content??{})){const n=o.content?.[t];if(n&&(n.schema&&"builder"in n.schema)){const t=n.schema.builder(e);n.schema=t.schema,t.components&&(s={...s,...t.components})}}}return{docs:n,components:s}}}})}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 i(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={};for(const[o,c]of Object.entries(e))if(!(n.some((e=>"string"==typeof e?o===e:e.test(o)))||o.includes("*")||t&&o.includes("."))){for(const e of Object.keys(c)){const t=c[e];o.includes("{")&&(t.parameters||(t.parameters=[]),t.parameters=[...o.split("/").filter((e=>e.startsWith("{")&&!t.parameters.find((t=>"path"===t.in&&t.name===e.slice(1,e.length-1))))).map((e=>({schema:{type:"string"},in:"path",name:e.slice(1,e.length-1),required:!0}))),...t.parameters]),t.responses||(t.responses={200:{}})}s[o]=c}return s}function a(t,{documentation:s={},excludeStaticFile:o=!0,exclude:a=[],excludeMethods:r=["OPTIONS"],excludeTags:d=[]}={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]}){const m={version:"3.0.3",components:{}},l={};for(const s of t.routes){if(!(e 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],{docs:i,components:a}=t({...m,...o});if(m.components={...m.components,...a??{}},"ALL"===s.method)for(const e of n)c({path:s.path,data:i,method:e,schema:l});else c({method:s.method,path:s.path,data:i,schema:l})}for(const e in l)for(const t in l[e])l[e][t].hide&&delete l[e][t];const p={openapi:m.version,...{...s,tags:s.tags?.filter((e=>!d?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...s.info},paths:{...i(l,{excludeStaticFile:o,exclude:Array.isArray(a)?a:[a]}),...s.paths},components:{...s.components,schemas:{...m.components,...s.components?.schemas}}}};return e=>e.json(p)}export{t as describeRoute,a as openAPISpecs};
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.1.0",
4
+ "version": "0.1.2",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "hono",
@@ -19,17 +19,8 @@
19
19
  "bugs": {
20
20
  "url": "https://github.com/rhinobase/hono-openapi/issues"
21
21
  },
22
- "dependencies": {},
23
- "main": "./index.cjs.js",
24
- "module": "./index.esm.js",
25
- "files": [
26
- "dist",
27
- "!**/*.tsbuildinfo"
28
- ],
29
22
  "peerDependencies": {
30
- "hono": "^4.6.4"
31
- },
32
- "optionalDependencies": {
23
+ "hono": "^4.6.4",
33
24
  "@hono/zod-validator": "^0.4.1",
34
25
  "zod": "^3.23.8",
35
26
  "zod-openapi": "^3.1.1"
@@ -50,5 +41,7 @@
50
41
  "types": "./zod.cjs.d.ts"
51
42
  }
52
43
  },
44
+ "module": "./index.esm.js",
45
+ "main": "./index.cjs.js",
53
46
  "types": "./index.esm.d.ts"
54
47
  }
@@ -0,0 +1 @@
1
+ export declare const uniqueSymbol: unique symbol;
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { describeRoute } from "./route";
2
+ export { openAPISpecs } from "./openapi";
@@ -0,0 +1,3 @@
1
+ import type { Context, Env, Hono, Input } from "hono";
2
+ import type { OpenApiSpecsOptions } from "./types";
3
+ export declare function openAPISpecs<E extends Env = Env, P extends string = string, I extends Input = Input>(hono: Hono, { documentation, excludeStaticFile, exclude, excludeMethods, excludeTags, }?: OpenApiSpecsOptions): (c: Context<E, P, I>) => any;
package/src/route.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Env, Input, MiddlewareHandler } from "hono/types";
2
+ import type { DescribeRouteOptions, OpenAPIRouteHandlerConfig } from "./types";
3
+ import { uniqueSymbol } from "./constants";
4
+ export declare function describeRoute<E extends Env = Env, P extends string = string, I extends Input = Input>(specs: DescribeRouteOptions): MiddlewareHandler<E, P, I> & {
5
+ [uniqueSymbol]: {
6
+ resolver: (config: OpenAPIRouteHandlerConfig) => {
7
+ docs: {
8
+ tags?: string[] | undefined;
9
+ summary?: string | undefined;
10
+ description?: string | undefined;
11
+ externalDocs?: import("openapi-types").OpenAPIV3.ExternalDocumentationObject | undefined;
12
+ operationId?: string | undefined;
13
+ callbacks?: {
14
+ [callback: string]: import("openapi-types").OpenAPIV3.ReferenceObject | import("openapi-types").OpenAPIV3.CallbackObject;
15
+ } | undefined;
16
+ deprecated?: boolean | undefined;
17
+ security?: import("openapi-types").OpenAPIV3.SecurityRequirementObject[] | undefined;
18
+ servers?: import("openapi-types").OpenAPIV3.ServerObject[] | undefined;
19
+ hide?: boolean;
20
+ responses?: {
21
+ [key: string]: import("openapi-types").OpenAPIV3.ResponseObject & {
22
+ content?: {
23
+ [key: string]: Omit<import("openapi-types").OpenAPIV3.MediaTypeObject, "schema"> & {
24
+ schema?: import("openapi-types").OpenAPIV3.ReferenceObject | import("openapi-types").OpenAPIV3.SchemaObject | import("./types").ResolverResult;
25
+ };
26
+ };
27
+ };
28
+ };
29
+ };
30
+ components: {};
31
+ };
32
+ };
33
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ import type { OpenAPIV3 } from "openapi-types";
2
+ import type { ALLOWED_METHODS } from "./utils";
3
+ export type OpenAPIRouteHandlerConfig = {
4
+ version: "3.0.0" | "3.0.1" | "3.0.2" | "3.0.3" | "3.1.0";
5
+ components: OpenAPIV3.ComponentsObject["schemas"];
6
+ };
7
+ export type ResolverResult = {
8
+ builder: (options?: OpenAPIRouteHandlerConfig) => {
9
+ schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject;
10
+ components?: OpenAPIV3.ComponentsObject["schemas"];
11
+ };
12
+ validator: (values: unknown) => void | Promise<void>;
13
+ };
14
+ export type HandlerResponse = {
15
+ resolver: (config: OpenAPIRouteHandlerConfig) => {
16
+ docs: OpenAPIV3.OperationObject;
17
+ components?: OpenAPIV3.ComponentsObject["schemas"];
18
+ };
19
+ metadata?: Record<string, unknown>;
20
+ };
21
+ export type DescribeRouteOptions = Omit<OpenAPIV3.OperationObject, "responses" | "requestBody" | "parameters"> & {
22
+ /**
23
+ * Pass `true` to hide route from OpenAPI/swagger document
24
+ */
25
+ hide?: boolean;
26
+ /**
27
+ * Responses of the request
28
+ */
29
+ responses?: {
30
+ [key: string]: OpenAPIV3.ResponseObject & {
31
+ content?: {
32
+ [key: string]: Omit<OpenAPIV3.MediaTypeObject, "schema"> & {
33
+ schema?: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject | ResolverResult;
34
+ };
35
+ };
36
+ };
37
+ };
38
+ };
39
+ export interface OpenAPIRoute {
40
+ path: string;
41
+ method: (typeof ALLOWED_METHODS)[number];
42
+ data: DescribeRouteOptions | Pick<OpenAPIV3.OperationObject, "parameters" | "requestBody">;
43
+ }
44
+ export type OpenApiSpecsOptions = {
45
+ /**
46
+ * Customize OpenAPI config, refers to Swagger 2.0 config
47
+ *
48
+ * @see https://swagger.io/specification/v2/
49
+ */
50
+ documentation?: Omit<Partial<OpenAPIV3.Document>, "x-express-openapi-additional-middleware" | "x-express-openapi-validation-strict">;
51
+ /**
52
+ * Determine if Swagger should exclude static files.
53
+ *
54
+ * @default true
55
+ */
56
+ excludeStaticFile?: boolean;
57
+ /**
58
+ * Paths to exclude from OpenAPI endpoint
59
+ *
60
+ * @default []
61
+ */
62
+ exclude?: string | RegExp | Array<string | RegExp>;
63
+ /**
64
+ * Exclude methods from Open API
65
+ */
66
+ excludeMethods?: (typeof ALLOWED_METHODS)[number][];
67
+ /**
68
+ * Exclude tags from OpenAPI
69
+ */
70
+ excludeTags?: string[];
71
+ };
package/src/utils.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { OpenAPIV3 } from "openapi-types";
2
+ import type { OpenAPIRoute } from "./types";
3
+ export declare const ALLOWED_METHODS: readonly ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];
4
+ export declare const toOpenAPIPath: (path: string) => string;
5
+ export declare const capitalize: (word: string) => string;
6
+ export declare const generateOperationId: (method: string, paths: string) => string;
7
+ export declare function registerSchemaPath({ path, method: _method, data, schema, }: OpenAPIRoute & {
8
+ schema: Partial<OpenAPIV3.PathsObject>;
9
+ }): void;
10
+ export declare function filterPaths(paths: OpenAPIV3.PathsObject, { excludeStaticFile, exclude, }: {
11
+ excludeStaticFile: boolean;
12
+ exclude: (string | RegExp)[];
13
+ }): OpenAPIV3.PathsObject<{}, {}>;
package/src/zod.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { z, ZodSchema } from "zod";
2
+ import { type Hook } from "@hono/zod-validator";
3
+ import type { ResolverResult } from "./types";
4
+ import type { Env, Input, MiddlewareHandler, ValidationTargets } from "hono";
5
+ export declare function resolver<T extends ZodSchema>(schema: T): ResolverResult;
6
+ type HasUndefined<T> = undefined extends T ? true : false;
7
+ export declare function validator<T extends ZodSchema, Target extends keyof ValidationTargets, E extends Env, P extends string, In = z.input<T>, Out = z.output<T>, I extends Input = {
8
+ in: HasUndefined<In> extends true ? {
9
+ [K in Target]?: In extends ValidationTargets[K] ? In : {
10
+ [K2 in keyof In]?: ValidationTargets[K][K2];
11
+ };
12
+ } : {
13
+ [K in Target]: In extends ValidationTargets[K] ? In : {
14
+ [K2 in keyof In]: ValidationTargets[K][K2];
15
+ };
16
+ };
17
+ out: {
18
+ [K in Target]: Out;
19
+ };
20
+ }, V extends I = I>(target: Target, schema: T, hook?: Hook<z.infer<T>, E, P, Target>): MiddlewareHandler<E, P, V>;
21
+ export {};
package/zod.cjs.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/zod";
package/zod.cjs.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e=require("@hono/zod-validator"),o=require("zod-openapi"),s=require("./constants.cjs.js");function n(e){return{builder:s=>o.createSchema(e,s?{openapi:s.version,components:s.components}:void 0),validator:e.parse}}exports.resolver=n,exports.validator=function(o,r,t){const a=e.zValidator(o,r,t);return Object.assign(a,{[s.uniqueSymbol]:{resolver:e=>{const s=n(r).builder(e),t={};if("form"===o||"json"===o)t.requestBody={content:{["json"===o?"application/json":"application/x-www-form-urlencoded"]:{schema:s.schema}}};else{const e=[];if("$ref"in s.schema)e.push({in:o,name:s.schema.$ref,schema:s.schema});else for(const[n,r]of Object.entries(s.schema.properties??{}))e.push({in:o,name:n,schema:r});t.parameters=e}return{docs:t,components:s.components}},metadata:{schemaType:"input"}}})};
package/zod.esm.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./src/zod";
package/zod.esm.js ADDED
@@ -0,0 +1 @@
1
+ import{zValidator as e}from"@hono/zod-validator";import{createSchema as o}from"zod-openapi";import{u as n}from"./constants.esm.js";function s(e){return{builder:n=>o(e,n?{openapi:n.version,components:n.components}:void 0),validator:e.parse}}function t(o,t,r){const a=e(o,t,r);return Object.assign(a,{[n]:{resolver:e=>{const n=s(t).builder(e),r={};if("form"===o||"json"===o)r.requestBody={content:{["json"===o?"application/json":"application/x-www-form-urlencoded"]:{schema:n.schema}}};else{const e=[];if("$ref"in n.schema)e.push({in:o,name:n.schema.$ref,schema:n.schema});else for(const[s,t]of Object.entries(n.schema.properties??{}))e.push({in:o,name:s,schema:t});r.parameters=e}return{docs:r,components:n.components}},metadata:{schemaType:"input"}}})}export{s as resolver,t as validator};