hono-openapi 0.4.8 → 0.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,409 @@
1
+ import { sValidator } from '@hono/standard-validator';
2
+ import { toJsonSchema } from '@standard-community/standard-json';
3
+ import { toOpenAPISchema } from '@standard-community/standard-openapi';
4
+
5
+ const uniqueSymbol = Symbol("openapi");
6
+ const ALLOWED_METHODS = [
7
+ "GET",
8
+ "PUT",
9
+ "POST",
10
+ "DELETE",
11
+ "OPTIONS",
12
+ "HEAD",
13
+ "PATCH",
14
+ "TRACE"
15
+ ];
16
+ const toOpenAPIPath = (path) => path.split("/").map((x) => {
17
+ let tmp = x;
18
+ if (tmp.startsWith(":")) {
19
+ const match = tmp.match(/^:([^{?]+)(?:{(.+)})?(\?)?$/);
20
+ if (match) {
21
+ const paramName = match[1];
22
+ tmp = `{${paramName}}`;
23
+ } else {
24
+ tmp = tmp.slice(1, tmp.length);
25
+ if (tmp.endsWith("?")) tmp = tmp.slice(0, -1);
26
+ tmp = `{${tmp}}`;
27
+ }
28
+ }
29
+ return tmp;
30
+ }).join("/");
31
+ const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
32
+ const generateOperationIdCache = /* @__PURE__ */ new Map();
33
+ const generateOperationId = (route) => {
34
+ const operationIdKey = `${route.method}:${route.path}`;
35
+ if (generateOperationIdCache.has(operationIdKey)) {
36
+ return generateOperationIdCache.get(operationIdKey);
37
+ }
38
+ let operationId = route.method;
39
+ if (route.path === "/") return `${operationId}Index`;
40
+ for (const segment of route.path.split("/")) {
41
+ if (segment.charCodeAt(0) === 123) {
42
+ operationId += `By${capitalize(segment.slice(1, -1))}`;
43
+ } else {
44
+ operationId += capitalize(segment);
45
+ }
46
+ }
47
+ generateOperationIdCache.set(operationIdKey, operationId);
48
+ return operationId;
49
+ };
50
+ const paramKey = (param) => "$ref" in param ? param.$ref : `${param.in} ${param.name}`;
51
+ function mergeParameters(...params) {
52
+ const _params = params.flatMap((x) => x ?? []);
53
+ const merged = _params.reduce((acc, param) => {
54
+ acc.set(paramKey(param), param);
55
+ return acc;
56
+ }, /* @__PURE__ */ new Map());
57
+ return Array.from(merged.values());
58
+ }
59
+ function getProperty(obj, key, defaultValue) {
60
+ if (obj != null && key in obj) {
61
+ return obj[key];
62
+ }
63
+ return defaultValue;
64
+ }
65
+ const specsByPathContext = /* @__PURE__ */ new Map();
66
+ function getPathContext(path) {
67
+ const keys = Array.from(specsByPathContext.keys());
68
+ const context = [];
69
+ for (const key of keys) {
70
+ if (path.match(key)) {
71
+ const data = specsByPathContext.get(key);
72
+ if (!data) continue;
73
+ context.push(data);
74
+ }
75
+ }
76
+ return context;
77
+ }
78
+ function mergeSpecs(...specs) {
79
+ return specs.reduce(
80
+ (prev, spec) => {
81
+ if (!spec) return prev;
82
+ return {
83
+ ...prev,
84
+ ...spec,
85
+ tags: Array.from(
86
+ /* @__PURE__ */ new Set([
87
+ ...getProperty(prev, "tags") ?? [],
88
+ ...getProperty(spec, "tags") ?? []
89
+ ])
90
+ ),
91
+ parameters: mergeParameters(
92
+ getProperty(prev, "parameters"),
93
+ getProperty(spec, "parameters")
94
+ ),
95
+ responses: {
96
+ ...getProperty(prev, "responses", {}),
97
+ ...getProperty(spec, "responses", {})
98
+ }
99
+ };
100
+ },
101
+ {}
102
+ );
103
+ }
104
+ function registerSchemaPath({
105
+ route,
106
+ specs,
107
+ paths
108
+ }) {
109
+ const path = toOpenAPIPath(route.path);
110
+ const method = route.method.toLowerCase();
111
+ if (method === "all") {
112
+ if (!specs) return;
113
+ if (specsByPathContext.has(path)) {
114
+ const prev = specsByPathContext.get(path) ?? {};
115
+ specsByPathContext.set(path, mergeSpecs(prev, specs));
116
+ } else {
117
+ specsByPathContext.set(path, specs);
118
+ }
119
+ } else {
120
+ const pathContext = getPathContext(path);
121
+ paths[path] = {
122
+ ...paths[path] ? paths[path] : {},
123
+ [method]: {
124
+ operationId: generateOperationId(route),
125
+ ...mergeSpecs(...pathContext, paths[path]?.[method], specs)
126
+ }
127
+ };
128
+ }
129
+ }
130
+ function removeExcludedPaths(paths, ctx) {
131
+ const { exclude, excludeStaticFile } = ctx.options;
132
+ const newPaths = {};
133
+ const _exclude = Array.isArray(exclude) ? exclude : [exclude];
134
+ for (const [key, value] of Object.entries(paths)) {
135
+ const isPathExcluded = !_exclude.some((x) => {
136
+ if (typeof x === "string") return key === x;
137
+ return x.test(key);
138
+ });
139
+ const isStaticFileExcluded = excludeStaticFile ? !key.includes(".") || key.includes("{") : true;
140
+ if (isPathExcluded && !(key.includes("*") && !key.includes("{")) && isStaticFileExcluded && value != null) {
141
+ for (const method of Object.keys(value)) {
142
+ const schema = value[method];
143
+ if (key.includes("{")) {
144
+ if (!schema.parameters) schema.parameters = [];
145
+ const pathParameters = key.split("/").filter(
146
+ (x) => x.startsWith("{") && !schema.parameters.find(
147
+ (params) => params.in === "path" && params.name === x.slice(1, x.length - 1)
148
+ )
149
+ );
150
+ for (const param of pathParameters) {
151
+ const paramName = param.slice(1, param.length - 1);
152
+ const index = schema.parameters.findIndex(
153
+ (x) => x.in === "param" && x.name === paramName
154
+ );
155
+ if (index !== -1) schema.parameters[index].in = "path";
156
+ else {
157
+ schema.parameters.push({
158
+ schema: { type: "string" },
159
+ in: "path",
160
+ name: paramName,
161
+ required: true
162
+ });
163
+ }
164
+ }
165
+ }
166
+ if (!schema.responses) {
167
+ schema.responses = {
168
+ 200: {}
169
+ };
170
+ }
171
+ }
172
+ newPaths[key] = value;
173
+ }
174
+ }
175
+ return newPaths;
176
+ }
177
+
178
+ const DEFAULT_OPTIONS = {
179
+ documentation: {},
180
+ excludeStaticFile: true,
181
+ exclude: [],
182
+ excludeMethods: ["OPTIONS"],
183
+ excludeTags: []
184
+ };
185
+ function openAPIRouteHandler(hono, options) {
186
+ let specs;
187
+ return async (c) => {
188
+ if (specs) return c.json(specs);
189
+ specs = await generateSpecs(hono, options, c);
190
+ return c.json(specs);
191
+ };
192
+ }
193
+ async function generateSpecs(hono, options = DEFAULT_OPTIONS, c) {
194
+ const ctx = {
195
+ components: {},
196
+ // @ts-expect-error
197
+ options: {
198
+ ...DEFAULT_OPTIONS,
199
+ ...options
200
+ }
201
+ };
202
+ const _documentation = ctx.options.documentation ?? {};
203
+ const schema = await generatePaths(hono, ctx);
204
+ for (const path in schema) {
205
+ for (const method in schema[path]) {
206
+ const isHidden = getHiddenValue({
207
+ valueOrFunc: schema[path][method]?.hide,
208
+ method,
209
+ path,
210
+ c
211
+ });
212
+ if (isHidden) {
213
+ delete schema[path][method];
214
+ }
215
+ }
216
+ }
217
+ return {
218
+ openapi: "3.1.0",
219
+ ..._documentation,
220
+ tags: _documentation.tags?.filter(
221
+ (tag) => !ctx.options.excludeTags?.includes(tag?.name)
222
+ ),
223
+ info: {
224
+ title: "Hono Documentation",
225
+ description: "Development documentation",
226
+ version: "0.0.0",
227
+ ..._documentation.info
228
+ },
229
+ paths: {
230
+ ...removeExcludedPaths(schema, ctx),
231
+ ..._documentation.paths
232
+ },
233
+ components: {
234
+ ..._documentation.components,
235
+ schemas: {
236
+ ...ctx.components.schemas,
237
+ ..._documentation.components?.schemas
238
+ }
239
+ }
240
+ };
241
+ }
242
+ async function generatePaths(hono, ctx) {
243
+ const paths = {};
244
+ for (const route of hono.routes) {
245
+ if (!(uniqueSymbol in route.handler)) {
246
+ if (ctx.options.includeEmptyPaths) {
247
+ registerSchemaPath({
248
+ route,
249
+ paths
250
+ });
251
+ }
252
+ continue;
253
+ }
254
+ const routeMethod = route.method;
255
+ if (routeMethod !== "ALL") {
256
+ if (ctx.options.excludeMethods?.includes(routeMethod)) {
257
+ continue;
258
+ }
259
+ if (!ALLOWED_METHODS.includes(routeMethod)) {
260
+ continue;
261
+ }
262
+ }
263
+ const middlewareHandler = route.handler[uniqueSymbol];
264
+ const defaultOptionsForThisMethod = ctx.options.defaultOptions?.[routeMethod];
265
+ const { schema: routeSpecs, components = {} } = await getSpec(
266
+ middlewareHandler,
267
+ defaultOptionsForThisMethod
268
+ );
269
+ ctx.components = {
270
+ ...ctx.components,
271
+ ...components,
272
+ schemas: {
273
+ ...ctx.components.schemas,
274
+ ...components.schemas
275
+ }
276
+ };
277
+ registerSchemaPath({
278
+ route,
279
+ specs: routeSpecs,
280
+ paths
281
+ });
282
+ }
283
+ return paths;
284
+ }
285
+ function getHiddenValue(options) {
286
+ const { valueOrFunc, c, method, path } = options;
287
+ if (valueOrFunc != null) {
288
+ if (typeof valueOrFunc === "boolean") {
289
+ return valueOrFunc;
290
+ } else if (typeof valueOrFunc === "function") {
291
+ if (c) {
292
+ return valueOrFunc(c);
293
+ } else {
294
+ console.warn(
295
+ `'c' is not defined, cannot evaluate hide function for ${method} ${path}`
296
+ );
297
+ }
298
+ }
299
+ }
300
+ return false;
301
+ }
302
+ async function getSpec(middlewareHandler, defaultOptions) {
303
+ if ("spec" in middlewareHandler) {
304
+ let components = {};
305
+ const tmp = {
306
+ ...defaultOptions,
307
+ ...middlewareHandler.spec,
308
+ responses: {
309
+ ...defaultOptions?.responses,
310
+ ...middlewareHandler.spec.responses
311
+ }
312
+ };
313
+ if (tmp.responses) {
314
+ for (const key of Object.keys(tmp.responses)) {
315
+ const response = tmp.responses[key];
316
+ if (!response || !("content" in response)) continue;
317
+ for (const contentKey of Object.keys(response.content ?? {})) {
318
+ const raw = response.content?.[contentKey];
319
+ if (!raw) continue;
320
+ if (raw.schema && "toOpenAPISchema" in raw.schema) {
321
+ const result2 = await raw.schema.toOpenAPISchema(defaultOptions);
322
+ raw.schema = result2.schema;
323
+ if (result2.components) {
324
+ components = {
325
+ ...components,
326
+ ...result2.components
327
+ };
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ return { schema: tmp, components };
334
+ }
335
+ const result = await middlewareHandler.toOpenAPISchema();
336
+ const docs = {};
337
+ if (middlewareHandler.target === "form" || middlewareHandler.target === "json") {
338
+ const media = middlewareHandler.target === "json" ? "application/json" : "multipart/form-data";
339
+ if (!docs.requestBody || !("content" in docs.requestBody) || !docs.requestBody.content) {
340
+ docs.requestBody = {
341
+ content: {
342
+ [media]: {
343
+ schema: result.schema
344
+ }
345
+ }
346
+ };
347
+ } else {
348
+ docs.requestBody.content[media] = {
349
+ schema: result.schema
350
+ };
351
+ }
352
+ } else {
353
+ const parameters = [];
354
+ if ("$ref" in result.schema) {
355
+ parameters.push({
356
+ in: middlewareHandler.target,
357
+ // @ts-expect-error
358
+ name: result.schema.$ref,
359
+ // @ts-expect-error
360
+ schema: result.schema
361
+ });
362
+ } else {
363
+ for (const [key, value] of Object.entries(
364
+ result.schema.properties ?? {}
365
+ )) {
366
+ parameters.push({
367
+ in: middlewareHandler.target,
368
+ name: key,
369
+ // @ts-expect-error
370
+ schema: value,
371
+ required: result.schema.required?.includes(key)
372
+ });
373
+ }
374
+ }
375
+ docs.parameters = parameters;
376
+ }
377
+ return { schema: docs, components: result.components };
378
+ }
379
+
380
+ function resolver(schema) {
381
+ return {
382
+ vendor: schema["~standard"].vendor,
383
+ validate: schema["~standard"].validate,
384
+ toJSONSchema: (options) => toJsonSchema(schema, options),
385
+ toOpenAPISchema: (options) => toOpenAPISchema(schema, options)
386
+ };
387
+ }
388
+ function validator(target, schema, hook, options) {
389
+ const middleware = sValidator(target, schema, hook);
390
+ return Object.assign(middleware, {
391
+ [uniqueSymbol]: {
392
+ target,
393
+ ...resolver(schema),
394
+ options
395
+ }
396
+ });
397
+ }
398
+ function describeRoute(spec) {
399
+ const middleware = async (_c, next) => {
400
+ await next();
401
+ };
402
+ return Object.assign(middleware, {
403
+ [uniqueSymbol]: {
404
+ spec
405
+ }
406
+ });
407
+ }
408
+
409
+ export { ALLOWED_METHODS, describeRoute, generateSpecs, openAPIRouteHandler, registerSchemaPath, removeExcludedPaths, resolver, uniqueSymbol, validator };
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "hono-openapi",
3
3
  "description": "OpenAPI schema generator for Hono",
4
- "version": "0.4.8",
5
- "types": "./index.d.ts",
6
- "main": "./index.js",
4
+ "version": "0.5.0-rc.1",
7
5
  "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
8
12
  "license": "MIT",
9
13
  "keywords": [
10
14
  "hono",
@@ -27,47 +31,35 @@
27
31
  "bugs": {
28
32
  "url": "https://github.com/rhinobase/hono-openapi/issues"
29
33
  },
30
- "dependencies": {
31
- "json-schema-walker": "^2.0.0"
34
+ "exports": {
35
+ "import": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
+ }
32
43
  },
33
44
  "peerDependencies": {
34
- "@hono/arktype-validator": "^2.0.0",
35
- "@hono/effect-validator": "^1.2.0",
36
- "@hono/typebox-validator": "^0.2.0 || ^0.3.0",
37
- "@hono/valibot-validator": "^0.5.1",
38
- "@hono/zod-validator": "^0.4.1",
45
+ "@hono/standard-validator": "^0.1.2",
39
46
  "@sinclair/typebox": "^0.34.9",
40
- "@valibot/to-json-schema": "^1.0.0-beta.3",
47
+ "@standard-community/standard-json": "^0.3.0-rc.1",
48
+ "@standard-community/standard-openapi": "^0.2.0-rc.1",
41
49
  "arktype": "^2.0.0",
42
- "effect": "^3.11.3",
43
- "hono": "^4.6.13",
50
+ "effect": "^3.16.12",
51
+ "hono": "^4.8.3",
44
52
  "openapi-types": "^12.1.3",
45
53
  "valibot": "^1.0.0-beta.9",
46
- "zod": "^3.23.8",
47
- "zod-openapi": "^4.0.0"
54
+ "zod": "^3.23.8"
48
55
  },
49
56
  "peerDependenciesMeta": {
50
- "@hono/arktype-validator": {
51
- "optional": true
52
- },
53
- "@hono/effect-validator": {
54
- "optional": true
55
- },
56
- "@hono/typebox-validator": {
57
- "optional": true
58
- },
59
- "@hono/valibot-validator": {
60
- "optional": true
61
- },
62
- "@hono/zod-validator": {
57
+ "@hono/standard-validator": {
63
58
  "optional": true
64
59
  },
65
60
  "@sinclair/typebox": {
66
61
  "optional": true
67
62
  },
68
- "@valibot/to-json-schema": {
69
- "optional": true
70
- },
71
63
  "arktype": {
72
64
  "optional": true
73
65
  },
@@ -82,76 +74,20 @@
82
74
  },
83
75
  "zod": {
84
76
  "optional": true
85
- },
86
- "zod-openapi": {
87
- "optional": true
88
77
  }
89
78
  },
90
79
  "devDependencies": {
91
- "@apidevtools/json-schema-ref-parser": "^12.0.1",
92
- "@rollup/plugin-terser": "^0.4.4",
93
- "@types/json-schema": "^7.0.15"
80
+ "@biomejs/biome": "^2.0.6",
81
+ "@standard-schema/spec": "^1.0.0",
82
+ "@types/json-schema": "^7.0.15",
83
+ "@valibot/to-json-schema": "^1.3.0",
84
+ "pkgroll": "^2.13.1",
85
+ "typescript": "^5.8.3",
86
+ "vitest": "^3.2.4"
94
87
  },
95
- "exports": {
96
- ".": {
97
- "import": {
98
- "types": "./index.d.ts",
99
- "default": "./index.js"
100
- },
101
- "require": {
102
- "types": "./index.d.cts",
103
- "default": "./index.cjs"
104
- }
105
- },
106
- "./zod": {
107
- "import": {
108
- "types": "./zod.d.ts",
109
- "default": "./zod.js"
110
- },
111
- "require": {
112
- "types": "./zod.d.cts",
113
- "default": "./zod.cjs"
114
- }
115
- },
116
- "./valibot": {
117
- "import": {
118
- "types": "./valibot.d.ts",
119
- "default": "./valibot.js"
120
- },
121
- "require": {
122
- "types": "./valibot.d.cts",
123
- "default": "./valibot.cjs"
124
- }
125
- },
126
- "./typebox": {
127
- "import": {
128
- "types": "./typebox.d.ts",
129
- "default": "./typebox.js"
130
- },
131
- "require": {
132
- "types": "./typebox.d.cts",
133
- "default": "./typebox.cjs"
134
- }
135
- },
136
- "./arktype": {
137
- "import": {
138
- "types": "./arktype.d.ts",
139
- "default": "./arktype.js"
140
- },
141
- "require": {
142
- "types": "./arktype.d.cts",
143
- "default": "./arktype.cjs"
144
- }
145
- },
146
- "./effect": {
147
- "import": {
148
- "types": "./effect.d.ts",
149
- "default": "./effect.js"
150
- },
151
- "require": {
152
- "types": "./effect.d.cts",
153
- "default": "./effect.cjs"
154
- }
155
- }
88
+ "scripts": {
89
+ "build": "pkgroll --clean-dist",
90
+ "format": "biome check --write .",
91
+ "test": "vitest"
156
92
  }
157
93
  }
package/arktype.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";var e=require("@hono/arktype-validator"),r=require("./toOpenAPISchema.cjs"),a=require("./utils.cjs");function t(e){return{builder:async()=>({schema:await r.convert(e.in.toJsonSchema())}),validator:r=>{e.assert(r)}}}require("json-schema-walker"),exports.resolver=t,exports.validator=function(r,o,s){const i=e.arktypeValidator(r,o,s);return Object.assign(i,{[a.uniqueSymbol]:{resolver:async e=>a.generateValidatorDocs(r,await t(o).builder(e))}})};
package/arktype.d.cts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/arktype";
package/arktype.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/arktype.js";
package/arktype.js DELETED
@@ -1 +0,0 @@
1
- import{arktypeValidator as a}from"@hono/arktype-validator";import{c as r}from"./toOpenAPISchema.js";import{u as o,g as t}from"./utils.js";import"json-schema-walker";function s(a){return{builder:async()=>({schema:await r(a.in.toJsonSchema())}),validator:r=>{a.assert(r)}}}function e(r,e,i){const n=a(r,e,i);return Object.assign(n,{[o]:{resolver:async a=>t(r,await s(e).builder(a))}})}export{s as resolver,e as validator};
package/effect.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";var e=require("@hono/effect-validator"),r=require("effect"),a=require("./toOpenAPISchema.cjs"),t=require("./utils.cjs");function i(e){return{builder:async()=>({schema:await a.convert(r.JSONSchema.make(e))}),validator:async a=>{await r.Schema.decodeUnknownPromise(e)(a)}}}require("json-schema-walker"),exports.resolver=i,exports.validator=function(r,a){const o=e.effectValidator(r,a);return Object.assign(o,{[t.uniqueSymbol]:{resolver:async e=>t.generateValidatorDocs(r,await i(a).builder(e))}})};
package/effect.d.cts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/effect";
package/effect.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/effect.js";
package/effect.js DELETED
@@ -1 +0,0 @@
1
- import{effectValidator as o}from"@hono/effect-validator";import{JSONSchema as a,Schema as r}from"effect";import{c as e}from"./toOpenAPISchema.js";import{u as t,g as i}from"./utils.js";import"json-schema-walker";function n(o){return{builder:async()=>({schema:await e(a.make(o))}),validator:async a=>{await r.decodeUnknownPromise(o)(a)}}}function s(a,r){const e=o(a,r);return Object.assign(e,{[t]:{resolver:async o=>i(a,await n(r).builder(o))}})}export{n as resolver,s as validator};
package/index.cjs DELETED
@@ -1 +0,0 @@
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=new Map,a=(e,t)=>{const n=`${e}:${t}`;if(o.has(n))return o.get(n);let a=e;if("/"===t)return`${a}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?a+=`By${s(e.slice(1,-1))}`:a+=s(e);return o.set(n,a),a},r=new Map;function c(e,t,n){return e&&t in e?e[t]??n:n}function i(...e){return e.reduce(((e,t)=>{if(!t)return e;let n;return("tags"in e&&e.tags||"tags"in t&&t.tags)&&(n=Array.from(new Set([...c(e,"tags",[]),...c(t,"tags",[])]))),{...e,...t,tags:n,responses:{...c(e,"responses",{}),...c(t,"responses",{})},parameters:l(e.parameters,t.parameters)}}),{})}function p({path:e,method:t,data:n,schema:s}){e=(e=>e.split("/").map((e=>{let t=e;if(t.startsWith(":")){const e=t.match(/^:([^{?]+)(?:{(.+)})?(\?)?$/);e?t=`{${e[1]}}`:(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`)}return t})).join("/"))(e);const o=t.toLowerCase();if("all"===o){if(!n)return;if(r.has(e)){const t=r.get(e)??{};r.set(e,{...t,...n,parameters:l(t.parameters,n.parameters)})}else r.set(e,n)}else{const t=function(e){const t=Array.from(r.keys());let n={};for(const s of t)e.match(s)&&(n=i(n,r.get(s)??{}));return n}(e);s[e]={...s[e]?s[e]:{},[o]:{responses:{},operationId:a(o,e),...i(t,s[e]?.[o],n)}}}}const u=e=>"$ref"in e?e.$ref:`${e.in} ${e.name}`;function l(...e){const t=e.flatMap((e=>e??[])).reduce(((e,t)=>(e.set(u(t),t),e)),new Map);return Array.from(t.values())}function m(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={},o=Array.isArray(n)?n:[n];for(const[n,a]of Object.entries(e))if(!o.some((e=>"string"==typeof e?n===e:e.test(n)))&&(!n.includes("*")||n.includes("{"))&&(!t||(!n.includes(".")||n.includes("{")))){for(const e of Object.keys(a)){const t=a[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]=a}return s}const f={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},d={version:"3.1.0",components:{}};async function h(t,s=f,o=d,a){const r={...f,...s},c={...d,...o},i=r.documentation??{},u=await async function(t,s,o){const a={};for(const r of t.routes){if(!(e.uniqueSymbol in r.handler)){s.includeEmptyPaths&&p({method:r.method,path:r.path,schema:a});continue}if(s.excludeMethods.includes(r.method))continue;if(!1===n.includes(r.method)&&"ALL"!==r.method)continue;const{resolver:t,metadata:c={}}=r.handler[e.uniqueSymbol],i=s.defaultOptions?.[r.method],{docs:u,components:l}=await t({...o,...c},i);o.components={...o.components,...l??{}},p({method:r.method,path:r.path,data:u,schema:a})}return a}(t,r,c);for(const e in u)for(const t in u[e]){const n=u[e][t]?.hide;if(n){let s=!1;"boolean"==typeof n?s=n:"function"==typeof n&&(a?s=n(a):console.warn(`'c' is not defined, cannot evaluate hide function for ${t} ${e}`)),s&&delete u[e][t]}}return{openapi:c.version,...{...i,tags:i.tags?.filter((e=>!r.excludeTags?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...i.info},paths:{...m(u,r),...i.paths},components:{...i.components,schemas:{...c.components,...i.components?.schemas}}}}}async function g(e,t,n={}){let s={};const o={...n,...t,responses:{...n?.responses,...t.responses}};if(o.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}}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,a=e.res.headers.get("content-type");if(o&&a){const r=n.responses[o];if(r&&"content"in r&&r.content){const n=a.split(";")[0],o=r.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)=>g(e,o,t)}})},exports.generateRouteSpecs=g,exports.generateSpecs=h,exports.openAPISpecs=function(e,t){const n={version:"3.1.0",components:{}};let s;return async o=>(s||(s=await h(e,t,n,o)),o.json(s))};
package/index.d.cts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/index";
package/index.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./src/index.js";
package/index.js DELETED
@@ -1 +0,0 @@
1
- import{u as e}from"./utils.js";export{g as generateValidatorDocs}from"./utils.js";import{HTTPException as t}from"hono/http-exception";const n=["GET","PUT","POST","DELETE","OPTIONS","HEAD","PATCH","TRACE"],s=e=>e.charAt(0).toUpperCase()+e.slice(1),o=new Map,a=(e,t)=>{const n=`${e}:${t}`;if(o.has(n))return o.get(n);let a=e;if("/"===t)return`${a}Index`;for(const e of t.split("/"))123===e.charCodeAt(0)?a+=`By${s(e.slice(1,-1))}`:a+=s(e);return o.set(n,a),a},r=new Map;function c(e,t,n){return e&&t in e?e[t]??n:n}function i(...e){return e.reduce(((e,t)=>{if(!t)return e;let n;return("tags"in e&&e.tags||"tags"in t&&t.tags)&&(n=Array.from(new Set([...c(e,"tags",[]),...c(t,"tags",[])]))),{...e,...t,tags:n,responses:{...c(e,"responses",{}),...c(t,"responses",{})},parameters:m(e.parameters,t.parameters)}}),{})}function p({path:e,method:t,data:n,schema:s}){e=(e=>e.split("/").map((e=>{let t=e;if(t.startsWith(":")){const e=t.match(/^:([^{?]+)(?:{(.+)})?(\?)?$/);e?t=`{${e[1]}}`:(t=t.slice(1,t.length),t.endsWith("?")&&(t=t.slice(0,-1)),t=`{${t}}`)}return t})).join("/"))(e);const o=t.toLowerCase();if("all"===o){if(!n)return;if(r.has(e)){const t=r.get(e)??{};r.set(e,{...t,...n,parameters:m(t.parameters,n.parameters)})}else r.set(e,n)}else{const t=function(e){const t=Array.from(r.keys());let n={};for(const s of t)e.match(s)&&(n=i(n,r.get(s)??{}));return n}(e);s[e]={...s[e]?s[e]:{},[o]:{responses:{},operationId:a(o,e),...i(t,s[e]?.[o],n)}}}}const f=e=>"$ref"in e?e.$ref:`${e.in} ${e.name}`;function m(...e){const t=e.flatMap((e=>e??[])).reduce(((e,t)=>(e.set(f(t),t),e)),new Map);return Array.from(t.values())}function l(e,{excludeStaticFile:t=!0,exclude:n=[]}){const s={},o=Array.isArray(n)?n:[n];for(const[n,a]of Object.entries(e))if(!o.some((e=>"string"==typeof e?n===e:e.test(n)))&&(!n.includes("*")||n.includes("{"))&&(!t||(!n.includes(".")||n.includes("{")))){for(const e of Object.keys(a)){const t=a[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]=a}return s}const u={documentation:{},excludeStaticFile:!0,exclude:[],excludeMethods:["OPTIONS"],excludeTags:[]},d={version:"3.1.0",components:{}};function h(e,t){const n={version:"3.1.0",components:{}};let s;return async o=>(s||(s=await y(e,t,n,o)),o.json(s))}async function y(t,s=u,o=d,a){const r={...u,...s},c={...d,...o},i=r.documentation??{},f=await async function(t,s,o){const a={};for(const r of t.routes){if(!(e in r.handler)){s.includeEmptyPaths&&p({method:r.method,path:r.path,schema:a});continue}if(s.excludeMethods.includes(r.method))continue;if(!1===n.includes(r.method)&&"ALL"!==r.method)continue;const{resolver:t,metadata:c={}}=r.handler[e],i=s.defaultOptions?.[r.method],{docs:f,components:m}=await t({...o,...c},i);o.components={...o.components,...m??{}},p({method:r.method,path:r.path,data:f,schema:a})}return a}(t,r,c);for(const e in f)for(const t in f[e]){const n=f[e][t]?.hide;if(n){let s=!1;"boolean"==typeof n?s=n:"function"==typeof n&&(a?s=n(a):console.warn(`'c' is not defined, cannot evaluate hide function for ${t} ${e}`)),s&&delete f[e][t]}}return{openapi:c.version,...{...i,tags:i.tags?.filter((e=>!r.excludeTags?.includes(e?.name))),info:{title:"Hono Documentation",description:"Development documentation",version:"0.0.0",...i.info},paths:{...l(f,r),...i.paths},components:{...i.components,schemas:{...c.components,...i.components?.schemas}}}}}function w(n){const{validateResponse:s,...o}=n;return Object.assign((async(e,o)=>{if(await o(),s&&n.responses){const o=e.res.status,a=e.res.headers.get("content-type");if(o&&a){const r=n.responses[o];if(r&&"content"in r&&r.content){const n=a.split(";")[0],o=r.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(n.status,{message:n.message,cause:e})}}}}}),{[e]:{resolver:(e,t)=>x(e,o,t)}})}async function x(e,t,n={}){let s={};const o={...n,...t,responses:{...n?.responses,...t.responses}};if(o.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}}export{w as describeRoute,x as generateRouteSpecs,y as generateSpecs,h as openAPISpecs,e as uniqueSymbol};
package/src/arktype.d.ts DELETED
@@ -1,36 +0,0 @@
1
- import { type Hook } from "@hono/arktype-validator";
2
- import type { Type } from "arktype";
3
- import type { Env, MiddlewareHandler, ValidationTargets } from "hono";
4
- import type { HasUndefined, ResolverResult } from "./types.js";
5
- /**
6
- * Generate a resolver for an Arktype schema
7
- * @param schema Arktype schema
8
- * @returns Resolver result
9
- */
10
- export declare function resolver<T extends Type>(schema: T): ResolverResult;
11
- /**
12
- * Create a validator middleware
13
- * @param target Target for validation
14
- * @param schema Arktype schema
15
- * @param hook Hook for validation
16
- * @returns Middleware handler
17
- */
18
- export declare function validator<T extends Type, Target extends keyof ValidationTargets, E extends Env, P extends string, I = T["inferIn"], O = T["infer"], V extends {
19
- in: HasUndefined<I> extends true ? {
20
- [K in Target]?: I;
21
- } : {
22
- [K in Target]: I;
23
- };
24
- out: {
25
- [K in Target]: O;
26
- };
27
- } = {
28
- in: HasUndefined<I> extends true ? {
29
- [K in Target]?: I;
30
- } : {
31
- [K in Target]: I;
32
- };
33
- out: {
34
- [K in Target]: O;
35
- };
36
- }>(target: Target, schema: T, hook?: Hook<T["infer"], E, P>): MiddlewareHandler<E, P, V>;