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.
- package/constants.cjs.js +1 -0
- package/constants.esm.js +1 -0
- package/index.cjs.d.ts +1 -0
- package/index.cjs.js +1 -1
- package/index.esm.d.ts +1 -0
- package/index.esm.js +1 -0
- package/package.json +4 -11
- package/src/constants.d.ts +1 -0
- package/src/index.d.ts +2 -0
- package/src/openapi.d.ts +3 -0
- package/src/route.d.ts +33 -0
- package/src/types.d.ts +71 -0
- package/src/utils.d.ts +13 -0
- package/src/zod.d.ts +21 -0
- package/zod.cjs.d.ts +1 -0
- package/zod.cjs.js +1 -0
- package/zod.esm.d.ts +1 -0
- package/zod.esm.js +1 -0
package/constants.cjs.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const o=Symbol("openapi");exports.uniqueSymbol=o;
|
package/constants.esm.js
ADDED
|
@@ -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("./
|
|
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.
|
|
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
package/src/openapi.d.ts
ADDED
|
@@ -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};
|