@honestjs/api-docs-plugin 1.1.0 → 1.1.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/README.md +58 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# API Docs Plugin
|
|
2
2
|
|
|
3
|
-
Serves OpenAPI JSON and Swagger UI for your HonestJS application. Always
|
|
4
|
-
|
|
5
|
-
context key (default `'rpc.artifact'` when using with `@honestjs/rpc-plugin`).
|
|
3
|
+
Serves OpenAPI JSON and Swagger UI for your HonestJS application. Always generates the spec from an artifact-pass
|
|
4
|
+
`{ routes, schemas }` directly or a context key (default `'rpc.artifact'` when using with `@honestjs/rpc-plugin`).
|
|
6
5
|
|
|
7
6
|
## Installation
|
|
8
7
|
|
|
@@ -16,25 +15,23 @@ pnpm add @honestjs/api-docs-plugin
|
|
|
16
15
|
|
|
17
16
|
## Usage with RPC Plugin
|
|
18
17
|
|
|
19
|
-
The RPC plugin writes its artifact to the application context. ApiDocs defaults
|
|
20
|
-
|
|
21
|
-
plugins. Ensure RPC runs before ApiDocs in the plugins array:
|
|
18
|
+
The RPC plugin writes its artifact to the application context. ApiDocs defaults to the context key `'rpc.artifact'`, so
|
|
19
|
+
you can omit `artifact` when using both plugins. Ensure RPC runs before ApiDocs in the plugins array:
|
|
22
20
|
|
|
23
21
|
```typescript
|
|
24
|
-
import { Application } from
|
|
25
|
-
import { RPCPlugin } from
|
|
26
|
-
import { ApiDocsPlugin } from
|
|
27
|
-
import AppModule from
|
|
22
|
+
import { Application } from 'honestjs'
|
|
23
|
+
import { RPCPlugin } from '@honestjs/rpc-plugin'
|
|
24
|
+
import { ApiDocsPlugin } from '@honestjs/api-docs-plugin'
|
|
25
|
+
import AppModule from './app.module'
|
|
28
26
|
|
|
29
27
|
const { hono } = await Application.create(AppModule, {
|
|
30
|
-
|
|
31
|
-
})
|
|
28
|
+
plugins: [new RPCPlugin(), new ApiDocsPlugin()]
|
|
29
|
+
})
|
|
32
30
|
|
|
33
|
-
export default hono
|
|
31
|
+
export default hono
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
If RPC uses custom `context.namespace` / `context.keys.artifact`, pass the
|
|
37
|
-
resulting full key to `artifact` (e.g.
|
|
34
|
+
If RPC uses custom `context.namespace` / `context.keys.artifact`, pass the resulting full key to `artifact` (e.g.
|
|
38
35
|
`new ApiDocsPlugin({ artifact: "custom.artifact" })`).
|
|
39
36
|
|
|
40
37
|
By default:
|
|
@@ -47,44 +44,44 @@ By default:
|
|
|
47
44
|
Pass the artifact object directly:
|
|
48
45
|
|
|
49
46
|
```typescript
|
|
50
|
-
import { ApiDocsPlugin } from
|
|
47
|
+
import { ApiDocsPlugin } from '@honestjs/api-docs-plugin'
|
|
51
48
|
|
|
52
49
|
const artifact = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
plugins: [new ApiDocsPlugin({ artifact })]
|
|
50
|
+
routes: [
|
|
51
|
+
{
|
|
52
|
+
method: 'GET',
|
|
53
|
+
handler: 'list',
|
|
54
|
+
controller: 'UsersController',
|
|
55
|
+
fullPath: '/users',
|
|
56
|
+
parameters: []
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
schemas: []
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
plugins: [new ApiDocsPlugin({ artifact })]
|
|
66
63
|
```
|
|
67
64
|
|
|
68
65
|
## Configuration Options
|
|
69
66
|
|
|
70
67
|
```typescript
|
|
71
68
|
interface ApiDocsPluginOptions {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
69
|
+
// Optional: artifact - direct object or context key. Default: 'rpc.artifact'
|
|
70
|
+
artifact?: OpenApiArtifactInput | string
|
|
71
|
+
|
|
72
|
+
// OpenAPI generation (when converting artifact to spec)
|
|
73
|
+
title?: string
|
|
74
|
+
version?: string
|
|
75
|
+
description?: string
|
|
76
|
+
servers?: readonly { url: string; description?: string }[]
|
|
77
|
+
|
|
78
|
+
// Serving
|
|
79
|
+
openApiRoute?: string // default: '/openapi.json'
|
|
80
|
+
uiRoute?: string // default: '/docs'
|
|
81
|
+
uiTitle?: string // default: 'API Docs'
|
|
82
|
+
reloadOnRequest?: boolean // default: false
|
|
83
|
+
onOpenApiRequest?: (c, next) => void | Response | Promise<void | Response> // optional auth hook
|
|
84
|
+
onUiRequest?: (c, next) => void | Response | Promise<void | Response> // optional auth hook
|
|
88
85
|
}
|
|
89
86
|
```
|
|
90
87
|
|
|
@@ -116,28 +113,26 @@ Use optional hooks to protect docs routes:
|
|
|
116
113
|
|
|
117
114
|
```typescript
|
|
118
115
|
const plugin = new ApiDocsPlugin({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
})
|
|
116
|
+
artifact: 'rpc.artifact',
|
|
117
|
+
onOpenApiRequest: async (c, next) => {
|
|
118
|
+
if (c.req.header('x-api-key') !== 'secret') {
|
|
119
|
+
return new Response('Unauthorized', { status: 401 })
|
|
120
|
+
}
|
|
121
|
+
await next()
|
|
122
|
+
},
|
|
123
|
+
onUiRequest: async (_c, next) => {
|
|
124
|
+
await next()
|
|
125
|
+
}
|
|
126
|
+
})
|
|
130
127
|
```
|
|
131
128
|
|
|
132
129
|
## Known Limitations
|
|
133
130
|
|
|
134
|
-
- When `artifact` is a context key, the producer plugin must run before
|
|
135
|
-
|
|
136
|
-
- OpenAPI generation currently reflects artifact shape and inferred primitive
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
- Swagger UI is served from CDN assets by default; environments with restricted
|
|
140
|
-
outbound access should account for this.
|
|
131
|
+
- When `artifact` is a context key, the producer plugin must run before `ApiDocsPlugin` and write a valid artifact
|
|
132
|
+
object to that key.
|
|
133
|
+
- OpenAPI generation currently reflects artifact shape and inferred primitive parameter types; advanced custom schema
|
|
134
|
+
mappings should be done upstream in the artifact producer.
|
|
135
|
+
- Swagger UI is served from CDN assets by default; environments with restricted outbound access should account for this.
|
|
141
136
|
|
|
142
137
|
## License
|
|
143
138
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/openapi.generator.ts","../src/api-docs.plugin.ts"],"sourcesContent":["export { ApiDocsPlugin, DEFAULT_ARTIFACT_KEY } from './api-docs.plugin'\nexport type { ApiDocsPluginOptions, ArtifactInput } from './api-docs.plugin'\nexport { fromArtifact, fromArtifactSync, write } from './openapi.generator'\nexport type {\n\tOpenApiArtifactInput,\n\tOpenApiDocument,\n\tOpenApiGenerationOptions,\n\tOpenApiParameterInput,\n\tOpenApiRouteInput,\n\tOpenApiSchemaInput\n} from './openapi.generator'\nexport type { OpenAPIV3 } from 'openapi-types'\n","import fs from 'fs/promises'\nimport path from 'path'\nimport type { OpenAPIV3 } from 'openapi-types'\n\nexport interface OpenApiGenerationOptions {\n\treadonly title?: string\n\treadonly version?: string\n\treadonly description?: string\n\treadonly servers?: readonly { url: string; description?: string }[]\n}\n\nexport interface OpenApiArtifactInput {\n\treadonly artifactVersion?: string\n\treadonly routes: readonly OpenApiRouteInput[]\n\treadonly schemas: readonly OpenApiSchemaInput[]\n}\n\nexport interface OpenApiRouteInput {\n\treadonly method: string\n\treadonly handler: string\n\treadonly controller: string\n\treadonly fullPath: string\n\treadonly path?: string\n\treadonly prefix?: string\n\treadonly version?: string\n\treadonly route?: string\n\treadonly returns?: string\n\treadonly parameters?: readonly OpenApiParameterInput[]\n}\n\nexport interface OpenApiParameterInput {\n\treadonly name: string\n\treadonly data?: string\n\treadonly type: string\n\treadonly required?: boolean\n\treadonly decoratorType: string\n}\n\nexport interface OpenApiSchemaInput {\n\treadonly type: string\n\treadonly schema: Record<string, any>\n}\n\n/** OpenAPI 3.x document type (openapi-types). */\nexport type OpenApiDocument = OpenAPIV3.Document\n\ninterface ResolvedOpenApiOptions {\n\treadonly title: string\n\treadonly version: string\n\treadonly description: string\n\treadonly servers: readonly { url: string; description?: string }[]\n}\n\nfunction resolveOptions(options: OpenApiGenerationOptions = {}): ResolvedOpenApiOptions {\n\treturn {\n\t\ttitle: options.title ?? 'API',\n\t\tversion: options.version ?? '1.0.0',\n\t\tdescription: options.description ?? '',\n\t\tservers: options.servers ?? []\n\t}\n}\n\nexport function fromArtifactSync(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): OpenApiDocument {\n\tconst resolved = resolveOptions(options)\n\tconst schemaMap = buildSchemaMap(artifact.schemas)\n\tconst spec: OpenAPIV3.Document = {\n\t\topenapi: '3.0.3',\n\t\tinfo: {\n\t\t\ttitle: resolved.title,\n\t\t\tversion: resolved.version,\n\t\t\tdescription: resolved.description\n\t\t},\n\t\tpaths: {},\n\t\tcomponents: {\n\t\t\tschemas: schemaMap as OpenAPIV3.ComponentsObject['schemas']\n\t\t}\n\t}\n\n\tif (resolved.servers.length > 0) {\n\t\tspec.servers = resolved.servers.map((server) => ({ ...server }))\n\t}\n\n\tfor (const route of artifact.routes) {\n\t\tconst routePath =\n\t\t\troute.prefix != null || route.version != null || route.route != null\n\t\t\t\t? buildFallbackPath(route)\n\t\t\t\t: (route.fullPath || buildFallbackPath(route))\n\t\tconst openApiPath = toOpenApiPath(routePath)\n\t\tconst method = route.method.toLowerCase() as keyof OpenAPIV3.PathItemObject\n\t\tif (!spec.paths[openApiPath]) {\n\t\t\tspec.paths[openApiPath] = {}\n\t\t}\n\t\t;(spec.paths[openApiPath] as Record<string, OpenAPIV3.OperationObject>)[method] = buildOperation(\n\t\t\troute,\n\t\t\tschemaMap\n\t\t)\n\t}\n\n\treturn spec\n}\n\nexport async function fromArtifact(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): Promise<OpenApiDocument> {\n\treturn fromArtifactSync(artifact, options)\n}\n\nexport async function write(openapi: OpenApiDocument, outputPath: string): Promise<string> {\n\tconst absolute = path.isAbsolute(outputPath) ? outputPath : path.resolve(process.cwd(), outputPath)\n\tawait fs.mkdir(path.dirname(absolute), { recursive: true })\n\tawait fs.writeFile(absolute, JSON.stringify(openapi, null, 2), 'utf-8')\n\treturn absolute\n}\n\nfunction buildOperation(\n\troute: OpenApiRouteInput,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.OperationObject {\n\tconst controllerName = route.controller.replace(/Controller$/, '')\n\tconst parameters = route.parameters ?? []\n\tconst operation: OpenAPIV3.OperationObject = {\n\t\toperationId: route.handler,\n\t\ttags: [controllerName],\n\t\tresponses: buildResponses(route.returns, schemaMap)\n\t}\n\n\tconst openApiParameters = buildParameters(parameters)\n\tif (openApiParameters.length > 0) {\n\t\toperation.parameters = openApiParameters\n\t}\n\n\tconst requestBody = buildRequestBody(parameters, schemaMap)\n\tif (requestBody) {\n\t\toperation.requestBody = requestBody\n\t}\n\n\treturn operation\n}\n\nfunction buildParameters(parameters: readonly OpenApiParameterInput[]): OpenAPIV3.ParameterObject[] {\n\tconst result: OpenAPIV3.ParameterObject[] = []\n\n\tfor (const param of parameters) {\n\t\tif (param.decoratorType === 'param') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'path',\n\t\t\t\trequired: true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t} else if (param.decoratorType === 'query') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'query',\n\t\t\t\trequired: param.required === true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunction buildRequestBody(\n\tparameters: readonly OpenApiParameterInput[],\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.RequestBodyObject | null {\n\tconst bodyParam = parameters.find((param) => param.decoratorType === 'body')\n\tif (!bodyParam) return null\n\n\tconst typeName = extractBaseTypeName(bodyParam.type)\n\tconst schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject =\n\t\ttypeName && schemaMap[typeName]\n\t\t\t? { $ref: `#/components/schemas/${typeName}` }\n\t\t\t: { type: 'object' as const }\n\n\treturn {\n\t\trequired: true,\n\t\tcontent: {\n\t\t\t'application/json': { schema }\n\t\t}\n\t}\n}\n\nfunction buildResponses(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.ResponsesObject {\n\tconst responseSchema = resolveResponseSchema(returns, schemaMap)\n\n\tif (!responseSchema) {\n\t\treturn { '200': { description: 'Successful response' } }\n\t}\n\n\treturn {\n\t\t'200': {\n\t\t\tdescription: 'Successful response',\n\t\t\tcontent: {\n\t\t\t\t'application/json': { schema: responseSchema }\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resolveResponseSchema(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): Record<string, any> | null {\n\tif (!returns) return null\n\n\tlet innerType = returns\n\tconst promiseMatch = returns.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) {\n\t\tinnerType = promiseMatch[1]\n\t}\n\n\tconst isArray = innerType.endsWith('[]')\n\tconst baseType = isArray ? innerType.slice(0, -2) : innerType\n\tif (['string', 'number', 'boolean'].includes(baseType)) {\n\t\tconst primitiveSchema = tsTypeToJsonSchema(baseType)\n\t\treturn isArray ? { type: 'array', items: primitiveSchema } : primitiveSchema\n\t}\n\tif (['void', 'any', 'unknown'].includes(baseType)) return null\n\tif (schemaMap[baseType]) {\n\t\tconst ref = { $ref: `#/components/schemas/${baseType}` }\n\t\treturn isArray ? { type: 'array', items: ref } : ref\n\t}\n\treturn null\n}\n\nfunction buildSchemaMap(schemas: readonly OpenApiSchemaInput[]): Record<string, Record<string, any>> {\n\tconst result: Record<string, Record<string, any>> = {}\n\tfor (const schemaInfo of schemas) {\n\t\tconst definition = schemaInfo.schema?.definitions?.[schemaInfo.type]\n\t\tif (definition) {\n\t\t\tresult[schemaInfo.type] = definition\n\t\t}\n\t}\n\treturn result\n}\n\nfunction tsTypeToJsonSchema(tsType: string): Record<string, unknown> {\n\tswitch (tsType) {\n\t\tcase 'number':\n\t\t\treturn { type: 'number' as const }\n\t\tcase 'boolean':\n\t\t\treturn { type: 'boolean' as const }\n\t\tcase 'string':\n\t\tdefault:\n\t\t\treturn { type: 'string' as const }\n\t}\n}\n\nfunction extractBaseTypeName(tsType: string): string | null {\n\tif (!tsType) return null\n\n\tlet type = tsType\n\tconst promiseMatch = type.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) type = promiseMatch[1]\n\ttype = type.replace(/\\[\\]$/, '')\n\tconst genericMatch = type.match(/^\\w+<(\\w+)>$/)\n\tif (genericMatch) type = genericMatch[1]\n\tif (['string', 'number', 'boolean', 'any', 'void', 'unknown', 'object'].includes(type)) {\n\t\treturn null\n\t}\n\treturn type\n}\n\nfunction toOpenApiPath(expressPath: string): string {\n\treturn expressPath.replace(/:(\\w+)/g, '{$1}')\n}\n\nfunction buildFallbackPath(route: OpenApiRouteInput): string {\n\tconst parts = [route.prefix, route.version, route.route, route.path]\n\t\t.filter((part) => part !== undefined && part !== null && part !== '')\n\t\t.map((part) => String(part).replace(/^\\/+|\\/+$/g, ''))\n\t\t.filter((part) => part.length > 0)\n\tconst joined = parts.join('/')\n\treturn `/${joined}`\n}\n","import type { Application, IPlugin } from 'honestjs'\nimport type { Context, Hono, Next } from 'hono'\n\nimport { fromArtifactSync } from './openapi.generator'\nimport type { OpenApiArtifactInput, OpenApiGenerationOptions } from './openapi.generator'\n\nconst DEFAULT_OPENAPI_ROUTE = '/openapi.json'\nconst DEFAULT_UI_ROUTE = '/docs'\nconst DEFAULT_UI_TITLE = 'API Docs'\n/** Default context key when using with RPC plugin (writes to this key). */\nexport const DEFAULT_ARTIFACT_KEY = 'rpc.artifact'\n\nexport type ArtifactInput = OpenApiArtifactInput | string\n\nfunction isContextKey(artifact: ArtifactInput): artifact is string {\n\treturn typeof artifact === 'string'\n}\n\nfunction isArtifact(value: unknown): value is OpenApiArtifactInput {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst obj = value as Record<string, unknown>\n\treturn Array.isArray(obj.routes) && Array.isArray(obj.schemas)\n}\n\nexport interface ApiDocsPluginOptions extends OpenApiGenerationOptions {\n\t/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */\n\treadonly artifact?: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\n\treadonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\treadonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n}\n\nexport class ApiDocsPlugin implements IPlugin {\n\tprivate readonly artifact: ArtifactInput\n\tprivate readonly openApiRoute: string\n\tprivate readonly uiRoute: string\n\tprivate readonly uiTitle: string\n\tprivate readonly reloadOnRequest: boolean\n\tprivate readonly genOptions: OpenApiGenerationOptions\n\tprivate readonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\tprivate readonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\n\tprivate app: Application | null = null\n\tprivate cachedSpec: Record<string, unknown> | null = null\n\n\tconstructor(options: ApiDocsPluginOptions = {}) {\n\t\tthis.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY\n\t\tthis.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE)\n\t\tthis.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE)\n\t\tthis.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE\n\t\tthis.reloadOnRequest = options.reloadOnRequest ?? false\n\t\tthis.onOpenApiRequest = options.onOpenApiRequest\n\t\tthis.onUiRequest = options.onUiRequest\n\t\tthis.genOptions = {\n\t\t\ttitle: options.title,\n\t\t\tversion: options.version,\n\t\t\tdescription: options.description,\n\t\t\tservers: options.servers\n\t\t}\n\t}\n\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\n\t\thono.get(this.openApiRoute, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst earlyResponse = await this.runHook(this.onOpenApiRequest, c)\n\t\t\t\tif (earlyResponse) return earlyResponse\n\t\t\t\tconst spec = await this.resolveSpec()\n\t\t\t\treturn c.json(spec)\n\t\t\t} catch (error) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: 'Failed to load OpenAPI spec',\n\t\t\t\t\t\tmessage: this.toErrorMessage(error)\n\t\t\t\t\t},\n\t\t\t\t\t500\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\thono.get(this.uiRoute, async (c) => {\n\t\t\tconst earlyResponse = await this.runHook(this.onUiRequest, c)\n\t\t\tif (earlyResponse) return earlyResponse\n\t\t\treturn c.html(this.renderSwaggerUiHtml())\n\t\t})\n\t}\n\n\tprivate normalizeRoute(input: string): string {\n\t\tconst trimmed = input.trim()\n\t\tif (!trimmed) return '/'\n\t\tlet normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`\n\t\tif (normalized.length > 1) {\n\t\t\tnormalized = normalized.replace(/\\/+$/g, '')\n\t\t}\n\t\treturn normalized || '/'\n\t}\n\n\tprivate async resolveSpec(): Promise<Record<string, unknown>> {\n\t\tif (!this.reloadOnRequest && this.cachedSpec) {\n\t\t\treturn this.cachedSpec\n\t\t}\n\n\t\tlet artifact: OpenApiArtifactInput\n\n\t\tif (isContextKey(this.artifact)) {\n\t\t\tif (!this.app) {\n\t\t\t\tthrow new Error('ApiDocsPlugin: app not available when resolving artifact from context')\n\t\t\t}\n\t\t\tconst value = this.app.getContext().get<unknown>(this.artifact)\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: no artifact at context key '${this.artifact}'. Ensure RPC plugin (or another producer) runs before ApiDocs and writes to this key.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!isArtifact(value)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: value at '${this.artifact}' is not a valid artifact (expected object with routes and schemas)`\n\t\t\t\t)\n\t\t\t}\n\t\t\tartifact = value\n\t\t} else {\n\t\t\tartifact = this.artifact\n\t\t}\n\n\t\tconst artifactVersion = (artifact as { artifactVersion?: unknown }).artifactVersion\n\t\tif (artifactVersion !== undefined && artifactVersion !== '1') {\n\t\t\tthrow new Error(\n\t\t\t\t`ApiDocsPlugin: unsupported artifactVersion '${String(artifactVersion)}'. Supported versions: 1.`\n\t\t\t)\n\t\t}\n\n\t\tconst spec = fromArtifactSync(artifact, this.genOptions) as unknown as Record<string, unknown>\n\t\tif (!this.reloadOnRequest) this.cachedSpec = spec\n\t\treturn spec\n\t}\n\n\tprivate async runHook(\n\t\thook: ((c: Context, next: Next) => void | Response | Promise<void | Response>) | undefined,\n\t\tc: Context\n\t): Promise<Response | undefined> {\n\t\tif (!hook) return undefined\n\t\tlet nextCalled = false\n\t\tconst maybeResponse = await hook(c, async () => {\n\t\t\tnextCalled = true\n\t\t})\n\t\tif (maybeResponse instanceof Response) {\n\t\t\treturn maybeResponse\n\t\t}\n\t\tif (!nextCalled) {\n\t\t\treturn new Response('Forbidden', { status: 403 })\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprivate renderSwaggerUiHtml(): string {\n\t\tconst title = this.escapeHtml(this.uiTitle)\n\t\tconst openApiRoute = this.escapeJsString(this.openApiRoute)\n\n\t\treturn `<!doctype html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>${title}</title>\n\t<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n</head>\n<body>\n\t<div id=\"swagger-ui\"></div>\n\t<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n\t<script>\n\t\twindow.onload = function () {\n\t\t\twindow.ui = SwaggerUIBundle({\n\t\t\t\turl: '${openApiRoute}',\n\t\t\t\tdom_id: '#swagger-ui'\n\t\t\t});\n\t\t};\n\t</script>\n</body>\n</html>`\n\t}\n\n\tprivate escapeHtml(value: string): string {\n\t\treturn value\n\t\t\t.replace(/&/g, '&')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/\"/g, '"')\n\t\t\t.replace(/'/g, ''')\n\t}\n\n\tprivate escapeJsString(value: string): string {\n\t\treturn value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n\t}\n\n\tprivate toErrorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error)\n\t}\n}\n"],"mappings":"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","names":["path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/openapi.generator.ts","../src/api-docs.plugin.ts"],"sourcesContent":["export { ApiDocsPlugin, DEFAULT_ARTIFACT_KEY } from './api-docs.plugin'\nexport type { ApiDocsPluginOptions, ArtifactInput } from './api-docs.plugin'\nexport { fromArtifact, fromArtifactSync, write } from './openapi.generator'\nexport type {\n\tOpenApiArtifactInput,\n\tOpenApiDocument,\n\tOpenApiGenerationOptions,\n\tOpenApiParameterInput,\n\tOpenApiRouteInput,\n\tOpenApiSchemaInput\n} from './openapi.generator'\nexport type { OpenAPIV3 } from 'openapi-types'\n","import fs from 'fs/promises'\nimport path from 'path'\nimport type { OpenAPIV3 } from 'openapi-types'\n\nexport interface OpenApiGenerationOptions {\n\treadonly title?: string\n\treadonly version?: string\n\treadonly description?: string\n\treadonly servers?: readonly { url: string; description?: string }[]\n}\n\nexport interface OpenApiArtifactInput {\n\treadonly artifactVersion?: string\n\treadonly routes: readonly OpenApiRouteInput[]\n\treadonly schemas: readonly OpenApiSchemaInput[]\n}\n\nexport interface OpenApiRouteInput {\n\treadonly method: string\n\treadonly handler: string\n\treadonly controller: string\n\treadonly fullPath: string\n\treadonly path?: string\n\treadonly prefix?: string\n\treadonly version?: string\n\treadonly route?: string\n\treadonly returns?: string\n\treadonly parameters?: readonly OpenApiParameterInput[]\n}\n\nexport interface OpenApiParameterInput {\n\treadonly name: string\n\treadonly data?: string\n\treadonly type: string\n\treadonly required?: boolean\n\treadonly decoratorType: string\n}\n\nexport interface OpenApiSchemaInput {\n\treadonly type: string\n\treadonly schema: Record<string, any>\n}\n\n/** OpenAPI 3.x document type (openapi-types). */\nexport type OpenApiDocument = OpenAPIV3.Document\n\ninterface ResolvedOpenApiOptions {\n\treadonly title: string\n\treadonly version: string\n\treadonly description: string\n\treadonly servers: readonly { url: string; description?: string }[]\n}\n\nfunction resolveOptions(options: OpenApiGenerationOptions = {}): ResolvedOpenApiOptions {\n\treturn {\n\t\ttitle: options.title ?? 'API',\n\t\tversion: options.version ?? '1.0.0',\n\t\tdescription: options.description ?? '',\n\t\tservers: options.servers ?? []\n\t}\n}\n\nexport function fromArtifactSync(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): OpenApiDocument {\n\tconst resolved = resolveOptions(options)\n\tconst schemaMap = buildSchemaMap(artifact.schemas)\n\tconst spec: OpenAPIV3.Document = {\n\t\topenapi: '3.0.3',\n\t\tinfo: {\n\t\t\ttitle: resolved.title,\n\t\t\tversion: resolved.version,\n\t\t\tdescription: resolved.description\n\t\t},\n\t\tpaths: {},\n\t\tcomponents: {\n\t\t\tschemas: schemaMap as OpenAPIV3.ComponentsObject['schemas']\n\t\t}\n\t}\n\n\tif (resolved.servers.length > 0) {\n\t\tspec.servers = resolved.servers.map((server) => ({ ...server }))\n\t}\n\n\tfor (const route of artifact.routes) {\n\t\tconst routePath =\n\t\t\troute.prefix != null || route.version != null || route.route != null\n\t\t\t\t? buildFallbackPath(route)\n\t\t\t\t: route.fullPath || buildFallbackPath(route)\n\t\tconst openApiPath = toOpenApiPath(routePath)\n\t\tconst method = route.method.toLowerCase() as keyof OpenAPIV3.PathItemObject\n\t\tif (!spec.paths[openApiPath]) {\n\t\t\tspec.paths[openApiPath] = {}\n\t\t}\n\t\t;(spec.paths[openApiPath] as Record<string, OpenAPIV3.OperationObject>)[method] = buildOperation(\n\t\t\troute,\n\t\t\tschemaMap\n\t\t)\n\t}\n\n\treturn spec\n}\n\nexport async function fromArtifact(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): Promise<OpenApiDocument> {\n\treturn fromArtifactSync(artifact, options)\n}\n\nexport async function write(openapi: OpenApiDocument, outputPath: string): Promise<string> {\n\tconst absolute = path.isAbsolute(outputPath) ? outputPath : path.resolve(process.cwd(), outputPath)\n\tawait fs.mkdir(path.dirname(absolute), { recursive: true })\n\tawait fs.writeFile(absolute, JSON.stringify(openapi, null, 2), 'utf-8')\n\treturn absolute\n}\n\nfunction buildOperation(\n\troute: OpenApiRouteInput,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.OperationObject {\n\tconst controllerName = route.controller.replace(/Controller$/, '')\n\tconst parameters = route.parameters ?? []\n\tconst operation: OpenAPIV3.OperationObject = {\n\t\toperationId: route.handler,\n\t\ttags: [controllerName],\n\t\tresponses: buildResponses(route.returns, schemaMap)\n\t}\n\n\tconst openApiParameters = buildParameters(parameters)\n\tif (openApiParameters.length > 0) {\n\t\toperation.parameters = openApiParameters\n\t}\n\n\tconst requestBody = buildRequestBody(parameters, schemaMap)\n\tif (requestBody) {\n\t\toperation.requestBody = requestBody\n\t}\n\n\treturn operation\n}\n\nfunction buildParameters(parameters: readonly OpenApiParameterInput[]): OpenAPIV3.ParameterObject[] {\n\tconst result: OpenAPIV3.ParameterObject[] = []\n\n\tfor (const param of parameters) {\n\t\tif (param.decoratorType === 'param') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'path',\n\t\t\t\trequired: true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t} else if (param.decoratorType === 'query') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'query',\n\t\t\t\trequired: param.required === true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunction buildRequestBody(\n\tparameters: readonly OpenApiParameterInput[],\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.RequestBodyObject | null {\n\tconst bodyParam = parameters.find((param) => param.decoratorType === 'body')\n\tif (!bodyParam) return null\n\n\tconst typeName = extractBaseTypeName(bodyParam.type)\n\tconst schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject =\n\t\ttypeName && schemaMap[typeName] ? { $ref: `#/components/schemas/${typeName}` } : { type: 'object' as const }\n\n\treturn {\n\t\trequired: true,\n\t\tcontent: {\n\t\t\t'application/json': { schema }\n\t\t}\n\t}\n}\n\nfunction buildResponses(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.ResponsesObject {\n\tconst responseSchema = resolveResponseSchema(returns, schemaMap)\n\n\tif (!responseSchema) {\n\t\treturn { '200': { description: 'Successful response' } }\n\t}\n\n\treturn {\n\t\t'200': {\n\t\t\tdescription: 'Successful response',\n\t\t\tcontent: {\n\t\t\t\t'application/json': { schema: responseSchema }\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resolveResponseSchema(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): Record<string, any> | null {\n\tif (!returns) return null\n\n\tlet innerType = returns\n\tconst promiseMatch = returns.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) {\n\t\tinnerType = promiseMatch[1]\n\t}\n\n\tconst isArray = innerType.endsWith('[]')\n\tconst baseType = isArray ? innerType.slice(0, -2) : innerType\n\tif (['string', 'number', 'boolean'].includes(baseType)) {\n\t\tconst primitiveSchema = tsTypeToJsonSchema(baseType)\n\t\treturn isArray ? { type: 'array', items: primitiveSchema } : primitiveSchema\n\t}\n\tif (['void', 'any', 'unknown'].includes(baseType)) return null\n\tif (schemaMap[baseType]) {\n\t\tconst ref = { $ref: `#/components/schemas/${baseType}` }\n\t\treturn isArray ? { type: 'array', items: ref } : ref\n\t}\n\treturn null\n}\n\nfunction buildSchemaMap(schemas: readonly OpenApiSchemaInput[]): Record<string, Record<string, any>> {\n\tconst result: Record<string, Record<string, any>> = {}\n\tfor (const schemaInfo of schemas) {\n\t\tconst definition = schemaInfo.schema?.definitions?.[schemaInfo.type]\n\t\tif (definition) {\n\t\t\tresult[schemaInfo.type] = definition\n\t\t}\n\t}\n\treturn result\n}\n\nfunction tsTypeToJsonSchema(tsType: string): Record<string, unknown> {\n\tswitch (tsType) {\n\t\tcase 'number':\n\t\t\treturn { type: 'number' as const }\n\t\tcase 'boolean':\n\t\t\treturn { type: 'boolean' as const }\n\t\tcase 'string':\n\t\tdefault:\n\t\t\treturn { type: 'string' as const }\n\t}\n}\n\nfunction extractBaseTypeName(tsType: string): string | null {\n\tif (!tsType) return null\n\n\tlet type = tsType\n\tconst promiseMatch = type.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) type = promiseMatch[1]\n\ttype = type.replace(/\\[\\]$/, '')\n\tconst genericMatch = type.match(/^\\w+<(\\w+)>$/)\n\tif (genericMatch) type = genericMatch[1]\n\tif (['string', 'number', 'boolean', 'any', 'void', 'unknown', 'object'].includes(type)) {\n\t\treturn null\n\t}\n\treturn type\n}\n\nfunction toOpenApiPath(expressPath: string): string {\n\treturn expressPath.replace(/:(\\w+)/g, '{$1}')\n}\n\nfunction buildFallbackPath(route: OpenApiRouteInput): string {\n\tconst parts = [route.prefix, route.version, route.route, route.path]\n\t\t.filter((part) => part !== undefined && part !== null && part !== '')\n\t\t.map((part) => String(part).replace(/^\\/+|\\/+$/g, ''))\n\t\t.filter((part) => part.length > 0)\n\tconst joined = parts.join('/')\n\treturn `/${joined}`\n}\n","import type { Application, IPlugin } from 'honestjs'\nimport type { Context, Hono, Next } from 'hono'\n\nimport { fromArtifactSync } from './openapi.generator'\nimport type { OpenApiArtifactInput, OpenApiGenerationOptions } from './openapi.generator'\n\nconst DEFAULT_OPENAPI_ROUTE = '/openapi.json'\nconst DEFAULT_UI_ROUTE = '/docs'\nconst DEFAULT_UI_TITLE = 'API Docs'\n/** Default context key when using with RPC plugin (writes to this key). */\nexport const DEFAULT_ARTIFACT_KEY = 'rpc.artifact'\n\nexport type ArtifactInput = OpenApiArtifactInput | string\n\nfunction isContextKey(artifact: ArtifactInput): artifact is string {\n\treturn typeof artifact === 'string'\n}\n\nfunction isArtifact(value: unknown): value is OpenApiArtifactInput {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst obj = value as Record<string, unknown>\n\treturn Array.isArray(obj.routes) && Array.isArray(obj.schemas)\n}\n\nexport interface ApiDocsPluginOptions extends OpenApiGenerationOptions {\n\t/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */\n\treadonly artifact?: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\n\treadonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\treadonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n}\n\nexport class ApiDocsPlugin implements IPlugin {\n\tprivate readonly artifact: ArtifactInput\n\tprivate readonly openApiRoute: string\n\tprivate readonly uiRoute: string\n\tprivate readonly uiTitle: string\n\tprivate readonly reloadOnRequest: boolean\n\tprivate readonly genOptions: OpenApiGenerationOptions\n\tprivate readonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\tprivate readonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\n\tprivate app: Application | null = null\n\tprivate cachedSpec: Record<string, unknown> | null = null\n\n\tconstructor(options: ApiDocsPluginOptions = {}) {\n\t\tthis.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY\n\t\tthis.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE)\n\t\tthis.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE)\n\t\tthis.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE\n\t\tthis.reloadOnRequest = options.reloadOnRequest ?? false\n\t\tthis.onOpenApiRequest = options.onOpenApiRequest\n\t\tthis.onUiRequest = options.onUiRequest\n\t\tthis.genOptions = {\n\t\t\ttitle: options.title,\n\t\t\tversion: options.version,\n\t\t\tdescription: options.description,\n\t\t\tservers: options.servers\n\t\t}\n\t}\n\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\n\t\thono.get(this.openApiRoute, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst earlyResponse = await this.runHook(this.onOpenApiRequest, c)\n\t\t\t\tif (earlyResponse) return earlyResponse\n\t\t\t\tconst spec = await this.resolveSpec()\n\t\t\t\treturn c.json(spec)\n\t\t\t} catch (error) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: 'Failed to load OpenAPI spec',\n\t\t\t\t\t\tmessage: this.toErrorMessage(error)\n\t\t\t\t\t},\n\t\t\t\t\t500\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\thono.get(this.uiRoute, async (c) => {\n\t\t\tconst earlyResponse = await this.runHook(this.onUiRequest, c)\n\t\t\tif (earlyResponse) return earlyResponse\n\t\t\treturn c.html(this.renderSwaggerUiHtml())\n\t\t})\n\t}\n\n\tprivate normalizeRoute(input: string): string {\n\t\tconst trimmed = input.trim()\n\t\tif (!trimmed) return '/'\n\t\tlet normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`\n\t\tif (normalized.length > 1) {\n\t\t\tnormalized = normalized.replace(/\\/+$/g, '')\n\t\t}\n\t\treturn normalized || '/'\n\t}\n\n\tprivate async resolveSpec(): Promise<Record<string, unknown>> {\n\t\tif (!this.reloadOnRequest && this.cachedSpec) {\n\t\t\treturn this.cachedSpec\n\t\t}\n\n\t\tlet artifact: OpenApiArtifactInput\n\n\t\tif (isContextKey(this.artifact)) {\n\t\t\tif (!this.app) {\n\t\t\t\tthrow new Error('ApiDocsPlugin: app not available when resolving artifact from context')\n\t\t\t}\n\t\t\tconst value = this.app.getContext().get<unknown>(this.artifact)\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: no artifact at context key '${this.artifact}'. Ensure RPC plugin (or another producer) runs before ApiDocs and writes to this key.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!isArtifact(value)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: value at '${this.artifact}' is not a valid artifact (expected object with routes and schemas)`\n\t\t\t\t)\n\t\t\t}\n\t\t\tartifact = value\n\t\t} else {\n\t\t\tartifact = this.artifact\n\t\t}\n\n\t\tconst artifactVersion = (artifact as { artifactVersion?: unknown }).artifactVersion\n\t\tif (artifactVersion !== undefined && artifactVersion !== '1') {\n\t\t\tthrow new Error(\n\t\t\t\t`ApiDocsPlugin: unsupported artifactVersion '${String(artifactVersion)}'. Supported versions: 1.`\n\t\t\t)\n\t\t}\n\n\t\tconst spec = fromArtifactSync(artifact, this.genOptions) as unknown as Record<string, unknown>\n\t\tif (!this.reloadOnRequest) this.cachedSpec = spec\n\t\treturn spec\n\t}\n\n\tprivate async runHook(\n\t\thook: ((c: Context, next: Next) => void | Response | Promise<void | Response>) | undefined,\n\t\tc: Context\n\t): Promise<Response | undefined> {\n\t\tif (!hook) return undefined\n\t\tlet nextCalled = false\n\t\tconst maybeResponse = await hook(c, async () => {\n\t\t\tnextCalled = true\n\t\t})\n\t\tif (maybeResponse instanceof Response) {\n\t\t\treturn maybeResponse\n\t\t}\n\t\tif (!nextCalled) {\n\t\t\treturn new Response('Forbidden', { status: 403 })\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprivate renderSwaggerUiHtml(): string {\n\t\tconst title = this.escapeHtml(this.uiTitle)\n\t\tconst openApiRoute = this.escapeJsString(this.openApiRoute)\n\n\t\treturn `<!doctype html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>${title}</title>\n\t<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n</head>\n<body>\n\t<div id=\"swagger-ui\"></div>\n\t<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n\t<script>\n\t\twindow.onload = function () {\n\t\t\twindow.ui = SwaggerUIBundle({\n\t\t\t\turl: '${openApiRoute}',\n\t\t\t\tdom_id: '#swagger-ui'\n\t\t\t});\n\t\t};\n\t</script>\n</body>\n</html>`\n\t}\n\n\tprivate escapeHtml(value: string): string {\n\t\treturn value\n\t\t\t.replace(/&/g, '&')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/\"/g, '"')\n\t\t\t.replace(/'/g, ''')\n\t}\n\n\tprivate escapeJsString(value: string): string {\n\t\treturn value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n\t}\n\n\tprivate toErrorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error)\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,sBAAe;AACf,kBAAiB;AAoDjB,SAAS,eAAe,UAAoC,CAAC,GAA2B;AACvF,SAAO;AAAA,IACN,OAAO,QAAQ,SAAS;AAAA,IACxB,SAAS,QAAQ,WAAW;AAAA,IAC5B,aAAa,QAAQ,eAAe;AAAA,IACpC,SAAS,QAAQ,WAAW,CAAC;AAAA,EAC9B;AACD;AAEO,SAAS,iBACf,UACA,UAAoC,CAAC,GACnB;AAClB,QAAM,WAAW,eAAe,OAAO;AACvC,QAAM,YAAY,eAAe,SAAS,OAAO;AACjD,QAAM,OAA2B;AAAA,IAChC,SAAS;AAAA,IACT,MAAM;AAAA,MACL,OAAO,SAAS;AAAA,MAChB,SAAS,SAAS;AAAA,MAClB,aAAa,SAAS;AAAA,IACvB;AAAA,IACA,OAAO,CAAC;AAAA,IACR,YAAY;AAAA,MACX,SAAS;AAAA,IACV;AAAA,EACD;AAEA,MAAI,SAAS,QAAQ,SAAS,GAAG;AAChC,SAAK,UAAU,SAAS,QAAQ,IAAI,CAAC,YAAY,EAAE,GAAG,OAAO,EAAE;AAAA,EAChE;AAEA,aAAW,SAAS,SAAS,QAAQ;AACpC,UAAM,YACL,MAAM,UAAU,QAAQ,MAAM,WAAW,QAAQ,MAAM,SAAS,OAC7D,kBAAkB,KAAK,IACvB,MAAM,YAAY,kBAAkB,KAAK;AAC7C,UAAM,cAAc,cAAc,SAAS;AAC3C,UAAM,SAAS,MAAM,OAAO,YAAY;AACxC,QAAI,CAAC,KAAK,MAAM,WAAW,GAAG;AAC7B,WAAK,MAAM,WAAW,IAAI,CAAC;AAAA,IAC5B;AACA;AAAC,IAAC,KAAK,MAAM,WAAW,EAAgD,MAAM,IAAI;AAAA,MACjF;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;AAEA,eAAsB,aACrB,UACA,UAAoC,CAAC,GACV;AAC3B,SAAO,iBAAiB,UAAU,OAAO;AAC1C;AAEA,eAAsB,MAAM,SAA0B,YAAqC;AAC1F,QAAM,WAAW,YAAAA,QAAK,WAAW,UAAU,IAAI,aAAa,YAAAA,QAAK,QAAQ,QAAQ,IAAI,GAAG,UAAU;AAClG,QAAM,gBAAAC,QAAG,MAAM,YAAAD,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,QAAM,gBAAAC,QAAG,UAAU,UAAU,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,OAAO;AACtE,SAAO;AACR;AAEA,SAAS,eACR,OACA,WAC4B;AAC5B,QAAM,iBAAiB,MAAM,WAAW,QAAQ,eAAe,EAAE;AACjE,QAAM,aAAa,MAAM,cAAc,CAAC;AACxC,QAAM,YAAuC;AAAA,IAC5C,aAAa,MAAM;AAAA,IACnB,MAAM,CAAC,cAAc;AAAA,IACrB,WAAW,eAAe,MAAM,SAAS,SAAS;AAAA,EACnD;AAEA,QAAM,oBAAoB,gBAAgB,UAAU;AACpD,MAAI,kBAAkB,SAAS,GAAG;AACjC,cAAU,aAAa;AAAA,EACxB;AAEA,QAAM,cAAc,iBAAiB,YAAY,SAAS;AAC1D,MAAI,aAAa;AAChB,cAAU,cAAc;AAAA,EACzB;AAEA,SAAO;AACR;AAEA,SAAS,gBAAgB,YAA2E;AACnG,QAAM,SAAsC,CAAC;AAE7C,aAAW,SAAS,YAAY;AAC/B,QAAI,MAAM,kBAAkB,SAAS;AACpC,aAAO,KAAK;AAAA,QACX,MAAM,MAAM,QAAQ,MAAM;AAAA,QAC1B,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,QAAQ,mBAAmB,MAAM,IAAI;AAAA,MACtC,CAAC;AAAA,IACF,WAAW,MAAM,kBAAkB,SAAS;AAC3C,aAAO,KAAK;AAAA,QACX,MAAM,MAAM,QAAQ,MAAM;AAAA,QAC1B,IAAI;AAAA,QACJ,UAAU,MAAM,aAAa;AAAA,QAC7B,QAAQ,mBAAmB,MAAM,IAAI;AAAA,MACtC,CAAC;AAAA,IACF;AAAA,EACD;AAEA,SAAO;AACR;AAEA,SAAS,iBACR,YACA,WACqC;AACrC,QAAM,YAAY,WAAW,KAAK,CAAC,UAAU,MAAM,kBAAkB,MAAM;AAC3E,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,WAAW,oBAAoB,UAAU,IAAI;AACnD,QAAM,SACL,YAAY,UAAU,QAAQ,IAAI,EAAE,MAAM,wBAAwB,QAAQ,GAAG,IAAI,EAAE,MAAM,SAAkB;AAE5G,SAAO;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,MACR,oBAAoB,EAAE,OAAO;AAAA,IAC9B;AAAA,EACD;AACD;AAEA,SAAS,eACR,SACA,WAC4B;AAC5B,QAAM,iBAAiB,sBAAsB,SAAS,SAAS;AAE/D,MAAI,CAAC,gBAAgB;AACpB,WAAO,EAAE,OAAO,EAAE,aAAa,sBAAsB,EAAE;AAAA,EACxD;AAEA,SAAO;AAAA,IACN,OAAO;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,QACR,oBAAoB,EAAE,QAAQ,eAAe;AAAA,MAC9C;AAAA,IACD;AAAA,EACD;AACD;AAEA,SAAS,sBACR,SACA,WAC6B;AAC7B,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,YAAY;AAChB,QAAM,eAAe,QAAQ,MAAM,iBAAiB;AACpD,MAAI,cAAc;AACjB,gBAAY,aAAa,CAAC;AAAA,EAC3B;AAEA,QAAM,UAAU,UAAU,SAAS,IAAI;AACvC,QAAM,WAAW,UAAU,UAAU,MAAM,GAAG,EAAE,IAAI;AACpD,MAAI,CAAC,UAAU,UAAU,SAAS,EAAE,SAAS,QAAQ,GAAG;AACvD,UAAM,kBAAkB,mBAAmB,QAAQ;AACnD,WAAO,UAAU,EAAE,MAAM,SAAS,OAAO,gBAAgB,IAAI;AAAA,EAC9D;AACA,MAAI,CAAC,QAAQ,OAAO,SAAS,EAAE,SAAS,QAAQ,EAAG,QAAO;AAC1D,MAAI,UAAU,QAAQ,GAAG;AACxB,UAAM,MAAM,EAAE,MAAM,wBAAwB,QAAQ,GAAG;AACvD,WAAO,UAAU,EAAE,MAAM,SAAS,OAAO,IAAI,IAAI;AAAA,EAClD;AACA,SAAO;AACR;AAEA,SAAS,eAAe,SAA6E;AACpG,QAAM,SAA8C,CAAC;AACrD,aAAW,cAAc,SAAS;AACjC,UAAM,aAAa,WAAW,QAAQ,cAAc,WAAW,IAAI;AACnE,QAAI,YAAY;AACf,aAAO,WAAW,IAAI,IAAI;AAAA,IAC3B;AAAA,EACD;AACA,SAAO;AACR;AAEA,SAAS,mBAAmB,QAAyC;AACpE,UAAQ,QAAQ;AAAA,IACf,KAAK;AACJ,aAAO,EAAE,MAAM,SAAkB;AAAA,IAClC,KAAK;AACJ,aAAO,EAAE,MAAM,UAAmB;AAAA,IACnC,KAAK;AAAA,IACL;AACC,aAAO,EAAE,MAAM,SAAkB;AAAA,EACnC;AACD;AAEA,SAAS,oBAAoB,QAA+B;AAC3D,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,OAAO;AACX,QAAM,eAAe,KAAK,MAAM,iBAAiB;AACjD,MAAI,aAAc,QAAO,aAAa,CAAC;AACvC,SAAO,KAAK,QAAQ,SAAS,EAAE;AAC/B,QAAM,eAAe,KAAK,MAAM,cAAc;AAC9C,MAAI,aAAc,QAAO,aAAa,CAAC;AACvC,MAAI,CAAC,UAAU,UAAU,WAAW,OAAO,QAAQ,WAAW,QAAQ,EAAE,SAAS,IAAI,GAAG;AACvF,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAEA,SAAS,cAAc,aAA6B;AACnD,SAAO,YAAY,QAAQ,WAAW,MAAM;AAC7C;AAEA,SAAS,kBAAkB,OAAkC;AAC5D,QAAM,QAAQ,CAAC,MAAM,QAAQ,MAAM,SAAS,MAAM,OAAO,MAAM,IAAI,EACjE,OAAO,CAAC,SAAS,SAAS,UAAa,SAAS,QAAQ,SAAS,EAAE,EACnE,IAAI,CAAC,SAAS,OAAO,IAAI,EAAE,QAAQ,cAAc,EAAE,CAAC,EACpD,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AAClC,QAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,SAAO,IAAI,MAAM;AAClB;;;ACnRA,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAElB,IAAM,uBAAuB;AAIpC,SAAS,aAAa,UAA6C;AAClE,SAAO,OAAO,aAAa;AAC5B;AAEA,SAAS,WAAW,OAA+C;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,MAAM;AACZ,SAAO,MAAM,QAAQ,IAAI,MAAM,KAAK,MAAM,QAAQ,IAAI,OAAO;AAC9D;AAaO,IAAM,gBAAN,MAAuC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,MAA0B;AAAA,EAC1B,aAA6C;AAAA,EAErD,YAAY,UAAgC,CAAC,GAAG;AAC/C,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,eAAe,KAAK,eAAe,QAAQ,gBAAgB,qBAAqB;AACrF,SAAK,UAAU,KAAK,eAAe,QAAQ,WAAW,gBAAgB;AACtE,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,kBAAkB,QAAQ,mBAAmB;AAClD,SAAK,mBAAmB,QAAQ;AAChC,SAAK,cAAc,QAAQ;AAC3B,SAAK,aAAa;AAAA,MACjB,OAAO,QAAQ;AAAA,MACf,SAAS,QAAQ;AAAA,MACjB,aAAa,QAAQ;AAAA,MACrB,SAAS,QAAQ;AAAA,IAClB;AAAA,EACD;AAAA,EAEA,yBAAyB,OAAO,KAAkB,SAA8B;AAC/E,SAAK,MAAM;AAEX,SAAK,IAAI,KAAK,cAAc,OAAO,MAAM;AACxC,UAAI;AACH,cAAM,gBAAgB,MAAM,KAAK,QAAQ,KAAK,kBAAkB,CAAC;AACjE,YAAI,cAAe,QAAO;AAC1B,cAAM,OAAO,MAAM,KAAK,YAAY;AACpC,eAAO,EAAE,KAAK,IAAI;AAAA,MACnB,SAAS,OAAO;AACf,eAAO,EAAE;AAAA,UACR;AAAA,YACC,OAAO;AAAA,YACP,SAAS,KAAK,eAAe,KAAK;AAAA,UACnC;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAAA,IACD,CAAC;AAED,SAAK,IAAI,KAAK,SAAS,OAAO,MAAM;AACnC,YAAM,gBAAgB,MAAM,KAAK,QAAQ,KAAK,aAAa,CAAC;AAC5D,UAAI,cAAe,QAAO;AAC1B,aAAO,EAAE,KAAK,KAAK,oBAAoB,CAAC;AAAA,IACzC,CAAC;AAAA,EACF;AAAA,EAEQ,eAAe,OAAuB;AAC7C,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,aAAa,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AAChE,QAAI,WAAW,SAAS,GAAG;AAC1B,mBAAa,WAAW,QAAQ,SAAS,EAAE;AAAA,IAC5C;AACA,WAAO,cAAc;AAAA,EACtB;AAAA,EAEA,MAAc,cAAgD;AAC7D,QAAI,CAAC,KAAK,mBAAmB,KAAK,YAAY;AAC7C,aAAO,KAAK;AAAA,IACb;AAEA,QAAI;AAEJ,QAAI,aAAa,KAAK,QAAQ,GAAG;AAChC,UAAI,CAAC,KAAK,KAAK;AACd,cAAM,IAAI,MAAM,uEAAuE;AAAA,MACxF;AACA,YAAM,QAAQ,KAAK,IAAI,WAAW,EAAE,IAAa,KAAK,QAAQ;AAC9D,UAAI,UAAU,QAAW;AACxB,cAAM,IAAI;AAAA,UACT,8CAA8C,KAAK,QAAQ;AAAA,QAC5D;AAAA,MACD;AACA,UAAI,CAAC,WAAW,KAAK,GAAG;AACvB,cAAM,IAAI;AAAA,UACT,4BAA4B,KAAK,QAAQ;AAAA,QAC1C;AAAA,MACD;AACA,iBAAW;AAAA,IACZ,OAAO;AACN,iBAAW,KAAK;AAAA,IACjB;AAEA,UAAM,kBAAmB,SAA2C;AACpE,QAAI,oBAAoB,UAAa,oBAAoB,KAAK;AAC7D,YAAM,IAAI;AAAA,QACT,+CAA+C,OAAO,eAAe,CAAC;AAAA,MACvE;AAAA,IACD;AAEA,UAAM,OAAO,iBAAiB,UAAU,KAAK,UAAU;AACvD,QAAI,CAAC,KAAK,gBAAiB,MAAK,aAAa;AAC7C,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,QACb,MACA,GACgC;AAChC,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,aAAa;AACjB,UAAM,gBAAgB,MAAM,KAAK,GAAG,YAAY;AAC/C,mBAAa;AAAA,IACd,CAAC;AACD,QAAI,yBAAyB,UAAU;AACtC,aAAO;AAAA,IACR;AACA,QAAI,CAAC,YAAY;AAChB,aAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjD;AACA,WAAO;AAAA,EACR;AAAA,EAEQ,sBAA8B;AACrC,UAAM,QAAQ,KAAK,WAAW,KAAK,OAAO;AAC1C,UAAM,eAAe,KAAK,eAAe,KAAK,YAAY;AAE1D,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKC,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YASH,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOvB;AAAA,EAEQ,WAAW,OAAuB;AACzC,WAAO,MACL,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAAA,EACxB;AAAA,EAEQ,eAAe,OAAuB;AAC7C,WAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AAAA,EACxD;AAAA,EAEQ,eAAe,OAAwB;AAC9C,WAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,EAC7D;AACD;","names":["path","fs"]}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/openapi.generator.ts","../src/api-docs.plugin.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport type { OpenAPIV3 } from 'openapi-types'\n\nexport interface OpenApiGenerationOptions {\n\treadonly title?: string\n\treadonly version?: string\n\treadonly description?: string\n\treadonly servers?: readonly { url: string; description?: string }[]\n}\n\nexport interface OpenApiArtifactInput {\n\treadonly artifactVersion?: string\n\treadonly routes: readonly OpenApiRouteInput[]\n\treadonly schemas: readonly OpenApiSchemaInput[]\n}\n\nexport interface OpenApiRouteInput {\n\treadonly method: string\n\treadonly handler: string\n\treadonly controller: string\n\treadonly fullPath: string\n\treadonly path?: string\n\treadonly prefix?: string\n\treadonly version?: string\n\treadonly route?: string\n\treadonly returns?: string\n\treadonly parameters?: readonly OpenApiParameterInput[]\n}\n\nexport interface OpenApiParameterInput {\n\treadonly name: string\n\treadonly data?: string\n\treadonly type: string\n\treadonly required?: boolean\n\treadonly decoratorType: string\n}\n\nexport interface OpenApiSchemaInput {\n\treadonly type: string\n\treadonly schema: Record<string, any>\n}\n\n/** OpenAPI 3.x document type (openapi-types). */\nexport type OpenApiDocument = OpenAPIV3.Document\n\ninterface ResolvedOpenApiOptions {\n\treadonly title: string\n\treadonly version: string\n\treadonly description: string\n\treadonly servers: readonly { url: string; description?: string }[]\n}\n\nfunction resolveOptions(options: OpenApiGenerationOptions = {}): ResolvedOpenApiOptions {\n\treturn {\n\t\ttitle: options.title ?? 'API',\n\t\tversion: options.version ?? '1.0.0',\n\t\tdescription: options.description ?? '',\n\t\tservers: options.servers ?? []\n\t}\n}\n\nexport function fromArtifactSync(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): OpenApiDocument {\n\tconst resolved = resolveOptions(options)\n\tconst schemaMap = buildSchemaMap(artifact.schemas)\n\tconst spec: OpenAPIV3.Document = {\n\t\topenapi: '3.0.3',\n\t\tinfo: {\n\t\t\ttitle: resolved.title,\n\t\t\tversion: resolved.version,\n\t\t\tdescription: resolved.description\n\t\t},\n\t\tpaths: {},\n\t\tcomponents: {\n\t\t\tschemas: schemaMap as OpenAPIV3.ComponentsObject['schemas']\n\t\t}\n\t}\n\n\tif (resolved.servers.length > 0) {\n\t\tspec.servers = resolved.servers.map((server) => ({ ...server }))\n\t}\n\n\tfor (const route of artifact.routes) {\n\t\tconst routePath =\n\t\t\troute.prefix != null || route.version != null || route.route != null\n\t\t\t\t? buildFallbackPath(route)\n\t\t\t\t: (route.fullPath || buildFallbackPath(route))\n\t\tconst openApiPath = toOpenApiPath(routePath)\n\t\tconst method = route.method.toLowerCase() as keyof OpenAPIV3.PathItemObject\n\t\tif (!spec.paths[openApiPath]) {\n\t\t\tspec.paths[openApiPath] = {}\n\t\t}\n\t\t;(spec.paths[openApiPath] as Record<string, OpenAPIV3.OperationObject>)[method] = buildOperation(\n\t\t\troute,\n\t\t\tschemaMap\n\t\t)\n\t}\n\n\treturn spec\n}\n\nexport async function fromArtifact(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): Promise<OpenApiDocument> {\n\treturn fromArtifactSync(artifact, options)\n}\n\nexport async function write(openapi: OpenApiDocument, outputPath: string): Promise<string> {\n\tconst absolute = path.isAbsolute(outputPath) ? outputPath : path.resolve(process.cwd(), outputPath)\n\tawait fs.mkdir(path.dirname(absolute), { recursive: true })\n\tawait fs.writeFile(absolute, JSON.stringify(openapi, null, 2), 'utf-8')\n\treturn absolute\n}\n\nfunction buildOperation(\n\troute: OpenApiRouteInput,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.OperationObject {\n\tconst controllerName = route.controller.replace(/Controller$/, '')\n\tconst parameters = route.parameters ?? []\n\tconst operation: OpenAPIV3.OperationObject = {\n\t\toperationId: route.handler,\n\t\ttags: [controllerName],\n\t\tresponses: buildResponses(route.returns, schemaMap)\n\t}\n\n\tconst openApiParameters = buildParameters(parameters)\n\tif (openApiParameters.length > 0) {\n\t\toperation.parameters = openApiParameters\n\t}\n\n\tconst requestBody = buildRequestBody(parameters, schemaMap)\n\tif (requestBody) {\n\t\toperation.requestBody = requestBody\n\t}\n\n\treturn operation\n}\n\nfunction buildParameters(parameters: readonly OpenApiParameterInput[]): OpenAPIV3.ParameterObject[] {\n\tconst result: OpenAPIV3.ParameterObject[] = []\n\n\tfor (const param of parameters) {\n\t\tif (param.decoratorType === 'param') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'path',\n\t\t\t\trequired: true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t} else if (param.decoratorType === 'query') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'query',\n\t\t\t\trequired: param.required === true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunction buildRequestBody(\n\tparameters: readonly OpenApiParameterInput[],\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.RequestBodyObject | null {\n\tconst bodyParam = parameters.find((param) => param.decoratorType === 'body')\n\tif (!bodyParam) return null\n\n\tconst typeName = extractBaseTypeName(bodyParam.type)\n\tconst schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject =\n\t\ttypeName && schemaMap[typeName]\n\t\t\t? { $ref: `#/components/schemas/${typeName}` }\n\t\t\t: { type: 'object' as const }\n\n\treturn {\n\t\trequired: true,\n\t\tcontent: {\n\t\t\t'application/json': { schema }\n\t\t}\n\t}\n}\n\nfunction buildResponses(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.ResponsesObject {\n\tconst responseSchema = resolveResponseSchema(returns, schemaMap)\n\n\tif (!responseSchema) {\n\t\treturn { '200': { description: 'Successful response' } }\n\t}\n\n\treturn {\n\t\t'200': {\n\t\t\tdescription: 'Successful response',\n\t\t\tcontent: {\n\t\t\t\t'application/json': { schema: responseSchema }\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resolveResponseSchema(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): Record<string, any> | null {\n\tif (!returns) return null\n\n\tlet innerType = returns\n\tconst promiseMatch = returns.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) {\n\t\tinnerType = promiseMatch[1]\n\t}\n\n\tconst isArray = innerType.endsWith('[]')\n\tconst baseType = isArray ? innerType.slice(0, -2) : innerType\n\tif (['string', 'number', 'boolean'].includes(baseType)) {\n\t\tconst primitiveSchema = tsTypeToJsonSchema(baseType)\n\t\treturn isArray ? { type: 'array', items: primitiveSchema } : primitiveSchema\n\t}\n\tif (['void', 'any', 'unknown'].includes(baseType)) return null\n\tif (schemaMap[baseType]) {\n\t\tconst ref = { $ref: `#/components/schemas/${baseType}` }\n\t\treturn isArray ? { type: 'array', items: ref } : ref\n\t}\n\treturn null\n}\n\nfunction buildSchemaMap(schemas: readonly OpenApiSchemaInput[]): Record<string, Record<string, any>> {\n\tconst result: Record<string, Record<string, any>> = {}\n\tfor (const schemaInfo of schemas) {\n\t\tconst definition = schemaInfo.schema?.definitions?.[schemaInfo.type]\n\t\tif (definition) {\n\t\t\tresult[schemaInfo.type] = definition\n\t\t}\n\t}\n\treturn result\n}\n\nfunction tsTypeToJsonSchema(tsType: string): Record<string, unknown> {\n\tswitch (tsType) {\n\t\tcase 'number':\n\t\t\treturn { type: 'number' as const }\n\t\tcase 'boolean':\n\t\t\treturn { type: 'boolean' as const }\n\t\tcase 'string':\n\t\tdefault:\n\t\t\treturn { type: 'string' as const }\n\t}\n}\n\nfunction extractBaseTypeName(tsType: string): string | null {\n\tif (!tsType) return null\n\n\tlet type = tsType\n\tconst promiseMatch = type.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) type = promiseMatch[1]\n\ttype = type.replace(/\\[\\]$/, '')\n\tconst genericMatch = type.match(/^\\w+<(\\w+)>$/)\n\tif (genericMatch) type = genericMatch[1]\n\tif (['string', 'number', 'boolean', 'any', 'void', 'unknown', 'object'].includes(type)) {\n\t\treturn null\n\t}\n\treturn type\n}\n\nfunction toOpenApiPath(expressPath: string): string {\n\treturn expressPath.replace(/:(\\w+)/g, '{$1}')\n}\n\nfunction buildFallbackPath(route: OpenApiRouteInput): string {\n\tconst parts = [route.prefix, route.version, route.route, route.path]\n\t\t.filter((part) => part !== undefined && part !== null && part !== '')\n\t\t.map((part) => String(part).replace(/^\\/+|\\/+$/g, ''))\n\t\t.filter((part) => part.length > 0)\n\tconst joined = parts.join('/')\n\treturn `/${joined}`\n}\n","import type { Application, IPlugin } from 'honestjs'\nimport type { Context, Hono, Next } from 'hono'\n\nimport { fromArtifactSync } from './openapi.generator'\nimport type { OpenApiArtifactInput, OpenApiGenerationOptions } from './openapi.generator'\n\nconst DEFAULT_OPENAPI_ROUTE = '/openapi.json'\nconst DEFAULT_UI_ROUTE = '/docs'\nconst DEFAULT_UI_TITLE = 'API Docs'\n/** Default context key when using with RPC plugin (writes to this key). */\nexport const DEFAULT_ARTIFACT_KEY = 'rpc.artifact'\n\nexport type ArtifactInput = OpenApiArtifactInput | string\n\nfunction isContextKey(artifact: ArtifactInput): artifact is string {\n\treturn typeof artifact === 'string'\n}\n\nfunction isArtifact(value: unknown): value is OpenApiArtifactInput {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst obj = value as Record<string, unknown>\n\treturn Array.isArray(obj.routes) && Array.isArray(obj.schemas)\n}\n\nexport interface ApiDocsPluginOptions extends OpenApiGenerationOptions {\n\t/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */\n\treadonly artifact?: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\n\treadonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\treadonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n}\n\nexport class ApiDocsPlugin implements IPlugin {\n\tprivate readonly artifact: ArtifactInput\n\tprivate readonly openApiRoute: string\n\tprivate readonly uiRoute: string\n\tprivate readonly uiTitle: string\n\tprivate readonly reloadOnRequest: boolean\n\tprivate readonly genOptions: OpenApiGenerationOptions\n\tprivate readonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\tprivate readonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\n\tprivate app: Application | null = null\n\tprivate cachedSpec: Record<string, unknown> | null = null\n\n\tconstructor(options: ApiDocsPluginOptions = {}) {\n\t\tthis.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY\n\t\tthis.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE)\n\t\tthis.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE)\n\t\tthis.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE\n\t\tthis.reloadOnRequest = options.reloadOnRequest ?? false\n\t\tthis.onOpenApiRequest = options.onOpenApiRequest\n\t\tthis.onUiRequest = options.onUiRequest\n\t\tthis.genOptions = {\n\t\t\ttitle: options.title,\n\t\t\tversion: options.version,\n\t\t\tdescription: options.description,\n\t\t\tservers: options.servers\n\t\t}\n\t}\n\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\n\t\thono.get(this.openApiRoute, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst earlyResponse = await this.runHook(this.onOpenApiRequest, c)\n\t\t\t\tif (earlyResponse) return earlyResponse\n\t\t\t\tconst spec = await this.resolveSpec()\n\t\t\t\treturn c.json(spec)\n\t\t\t} catch (error) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: 'Failed to load OpenAPI spec',\n\t\t\t\t\t\tmessage: this.toErrorMessage(error)\n\t\t\t\t\t},\n\t\t\t\t\t500\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\thono.get(this.uiRoute, async (c) => {\n\t\t\tconst earlyResponse = await this.runHook(this.onUiRequest, c)\n\t\t\tif (earlyResponse) return earlyResponse\n\t\t\treturn c.html(this.renderSwaggerUiHtml())\n\t\t})\n\t}\n\n\tprivate normalizeRoute(input: string): string {\n\t\tconst trimmed = input.trim()\n\t\tif (!trimmed) return '/'\n\t\tlet normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`\n\t\tif (normalized.length > 1) {\n\t\t\tnormalized = normalized.replace(/\\/+$/g, '')\n\t\t}\n\t\treturn normalized || '/'\n\t}\n\n\tprivate async resolveSpec(): Promise<Record<string, unknown>> {\n\t\tif (!this.reloadOnRequest && this.cachedSpec) {\n\t\t\treturn this.cachedSpec\n\t\t}\n\n\t\tlet artifact: OpenApiArtifactInput\n\n\t\tif (isContextKey(this.artifact)) {\n\t\t\tif (!this.app) {\n\t\t\t\tthrow new Error('ApiDocsPlugin: app not available when resolving artifact from context')\n\t\t\t}\n\t\t\tconst value = this.app.getContext().get<unknown>(this.artifact)\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: no artifact at context key '${this.artifact}'. Ensure RPC plugin (or another producer) runs before ApiDocs and writes to this key.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!isArtifact(value)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: value at '${this.artifact}' is not a valid artifact (expected object with routes and schemas)`\n\t\t\t\t)\n\t\t\t}\n\t\t\tartifact = value\n\t\t} else {\n\t\t\tartifact = this.artifact\n\t\t}\n\n\t\tconst artifactVersion = (artifact as { artifactVersion?: unknown }).artifactVersion\n\t\tif (artifactVersion !== undefined && artifactVersion !== '1') {\n\t\t\tthrow new Error(\n\t\t\t\t`ApiDocsPlugin: unsupported artifactVersion '${String(artifactVersion)}'. Supported versions: 1.`\n\t\t\t)\n\t\t}\n\n\t\tconst spec = fromArtifactSync(artifact, this.genOptions) as unknown as Record<string, unknown>\n\t\tif (!this.reloadOnRequest) this.cachedSpec = spec\n\t\treturn spec\n\t}\n\n\tprivate async runHook(\n\t\thook: ((c: Context, next: Next) => void | Response | Promise<void | Response>) | undefined,\n\t\tc: Context\n\t): Promise<Response | undefined> {\n\t\tif (!hook) return undefined\n\t\tlet nextCalled = false\n\t\tconst maybeResponse = await hook(c, async () => {\n\t\t\tnextCalled = true\n\t\t})\n\t\tif (maybeResponse instanceof Response) {\n\t\t\treturn maybeResponse\n\t\t}\n\t\tif (!nextCalled) {\n\t\t\treturn new Response('Forbidden', { status: 403 })\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprivate renderSwaggerUiHtml(): string {\n\t\tconst title = this.escapeHtml(this.uiTitle)\n\t\tconst openApiRoute = this.escapeJsString(this.openApiRoute)\n\n\t\treturn `<!doctype html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>${title}</title>\n\t<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n</head>\n<body>\n\t<div id=\"swagger-ui\"></div>\n\t<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n\t<script>\n\t\twindow.onload = function () {\n\t\t\twindow.ui = SwaggerUIBundle({\n\t\t\t\turl: '${openApiRoute}',\n\t\t\t\tdom_id: '#swagger-ui'\n\t\t\t});\n\t\t};\n\t</script>\n</body>\n</html>`\n\t}\n\n\tprivate escapeHtml(value: string): string {\n\t\treturn value\n\t\t\t.replace(/&/g, '&')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/\"/g, '"')\n\t\t\t.replace(/'/g, ''')\n\t}\n\n\tprivate escapeJsString(value: string): string {\n\t\treturn value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n\t}\n\n\tprivate toErrorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/openapi.generator.ts","../src/api-docs.plugin.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport type { OpenAPIV3 } from 'openapi-types'\n\nexport interface OpenApiGenerationOptions {\n\treadonly title?: string\n\treadonly version?: string\n\treadonly description?: string\n\treadonly servers?: readonly { url: string; description?: string }[]\n}\n\nexport interface OpenApiArtifactInput {\n\treadonly artifactVersion?: string\n\treadonly routes: readonly OpenApiRouteInput[]\n\treadonly schemas: readonly OpenApiSchemaInput[]\n}\n\nexport interface OpenApiRouteInput {\n\treadonly method: string\n\treadonly handler: string\n\treadonly controller: string\n\treadonly fullPath: string\n\treadonly path?: string\n\treadonly prefix?: string\n\treadonly version?: string\n\treadonly route?: string\n\treadonly returns?: string\n\treadonly parameters?: readonly OpenApiParameterInput[]\n}\n\nexport interface OpenApiParameterInput {\n\treadonly name: string\n\treadonly data?: string\n\treadonly type: string\n\treadonly required?: boolean\n\treadonly decoratorType: string\n}\n\nexport interface OpenApiSchemaInput {\n\treadonly type: string\n\treadonly schema: Record<string, any>\n}\n\n/** OpenAPI 3.x document type (openapi-types). */\nexport type OpenApiDocument = OpenAPIV3.Document\n\ninterface ResolvedOpenApiOptions {\n\treadonly title: string\n\treadonly version: string\n\treadonly description: string\n\treadonly servers: readonly { url: string; description?: string }[]\n}\n\nfunction resolveOptions(options: OpenApiGenerationOptions = {}): ResolvedOpenApiOptions {\n\treturn {\n\t\ttitle: options.title ?? 'API',\n\t\tversion: options.version ?? '1.0.0',\n\t\tdescription: options.description ?? '',\n\t\tservers: options.servers ?? []\n\t}\n}\n\nexport function fromArtifactSync(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): OpenApiDocument {\n\tconst resolved = resolveOptions(options)\n\tconst schemaMap = buildSchemaMap(artifact.schemas)\n\tconst spec: OpenAPIV3.Document = {\n\t\topenapi: '3.0.3',\n\t\tinfo: {\n\t\t\ttitle: resolved.title,\n\t\t\tversion: resolved.version,\n\t\t\tdescription: resolved.description\n\t\t},\n\t\tpaths: {},\n\t\tcomponents: {\n\t\t\tschemas: schemaMap as OpenAPIV3.ComponentsObject['schemas']\n\t\t}\n\t}\n\n\tif (resolved.servers.length > 0) {\n\t\tspec.servers = resolved.servers.map((server) => ({ ...server }))\n\t}\n\n\tfor (const route of artifact.routes) {\n\t\tconst routePath =\n\t\t\troute.prefix != null || route.version != null || route.route != null\n\t\t\t\t? buildFallbackPath(route)\n\t\t\t\t: route.fullPath || buildFallbackPath(route)\n\t\tconst openApiPath = toOpenApiPath(routePath)\n\t\tconst method = route.method.toLowerCase() as keyof OpenAPIV3.PathItemObject\n\t\tif (!spec.paths[openApiPath]) {\n\t\t\tspec.paths[openApiPath] = {}\n\t\t}\n\t\t;(spec.paths[openApiPath] as Record<string, OpenAPIV3.OperationObject>)[method] = buildOperation(\n\t\t\troute,\n\t\t\tschemaMap\n\t\t)\n\t}\n\n\treturn spec\n}\n\nexport async function fromArtifact(\n\tartifact: OpenApiArtifactInput,\n\toptions: OpenApiGenerationOptions = {}\n): Promise<OpenApiDocument> {\n\treturn fromArtifactSync(artifact, options)\n}\n\nexport async function write(openapi: OpenApiDocument, outputPath: string): Promise<string> {\n\tconst absolute = path.isAbsolute(outputPath) ? outputPath : path.resolve(process.cwd(), outputPath)\n\tawait fs.mkdir(path.dirname(absolute), { recursive: true })\n\tawait fs.writeFile(absolute, JSON.stringify(openapi, null, 2), 'utf-8')\n\treturn absolute\n}\n\nfunction buildOperation(\n\troute: OpenApiRouteInput,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.OperationObject {\n\tconst controllerName = route.controller.replace(/Controller$/, '')\n\tconst parameters = route.parameters ?? []\n\tconst operation: OpenAPIV3.OperationObject = {\n\t\toperationId: route.handler,\n\t\ttags: [controllerName],\n\t\tresponses: buildResponses(route.returns, schemaMap)\n\t}\n\n\tconst openApiParameters = buildParameters(parameters)\n\tif (openApiParameters.length > 0) {\n\t\toperation.parameters = openApiParameters\n\t}\n\n\tconst requestBody = buildRequestBody(parameters, schemaMap)\n\tif (requestBody) {\n\t\toperation.requestBody = requestBody\n\t}\n\n\treturn operation\n}\n\nfunction buildParameters(parameters: readonly OpenApiParameterInput[]): OpenAPIV3.ParameterObject[] {\n\tconst result: OpenAPIV3.ParameterObject[] = []\n\n\tfor (const param of parameters) {\n\t\tif (param.decoratorType === 'param') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'path',\n\t\t\t\trequired: true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t} else if (param.decoratorType === 'query') {\n\t\t\tresult.push({\n\t\t\t\tname: param.data ?? param.name,\n\t\t\t\tin: 'query',\n\t\t\t\trequired: param.required === true,\n\t\t\t\tschema: tsTypeToJsonSchema(param.type)\n\t\t\t})\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunction buildRequestBody(\n\tparameters: readonly OpenApiParameterInput[],\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.RequestBodyObject | null {\n\tconst bodyParam = parameters.find((param) => param.decoratorType === 'body')\n\tif (!bodyParam) return null\n\n\tconst typeName = extractBaseTypeName(bodyParam.type)\n\tconst schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject =\n\t\ttypeName && schemaMap[typeName] ? { $ref: `#/components/schemas/${typeName}` } : { type: 'object' as const }\n\n\treturn {\n\t\trequired: true,\n\t\tcontent: {\n\t\t\t'application/json': { schema }\n\t\t}\n\t}\n}\n\nfunction buildResponses(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): OpenAPIV3.ResponsesObject {\n\tconst responseSchema = resolveResponseSchema(returns, schemaMap)\n\n\tif (!responseSchema) {\n\t\treturn { '200': { description: 'Successful response' } }\n\t}\n\n\treturn {\n\t\t'200': {\n\t\t\tdescription: 'Successful response',\n\t\t\tcontent: {\n\t\t\t\t'application/json': { schema: responseSchema }\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resolveResponseSchema(\n\treturns: string | undefined,\n\tschemaMap: Record<string, Record<string, any>>\n): Record<string, any> | null {\n\tif (!returns) return null\n\n\tlet innerType = returns\n\tconst promiseMatch = returns.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) {\n\t\tinnerType = promiseMatch[1]\n\t}\n\n\tconst isArray = innerType.endsWith('[]')\n\tconst baseType = isArray ? innerType.slice(0, -2) : innerType\n\tif (['string', 'number', 'boolean'].includes(baseType)) {\n\t\tconst primitiveSchema = tsTypeToJsonSchema(baseType)\n\t\treturn isArray ? { type: 'array', items: primitiveSchema } : primitiveSchema\n\t}\n\tif (['void', 'any', 'unknown'].includes(baseType)) return null\n\tif (schemaMap[baseType]) {\n\t\tconst ref = { $ref: `#/components/schemas/${baseType}` }\n\t\treturn isArray ? { type: 'array', items: ref } : ref\n\t}\n\treturn null\n}\n\nfunction buildSchemaMap(schemas: readonly OpenApiSchemaInput[]): Record<string, Record<string, any>> {\n\tconst result: Record<string, Record<string, any>> = {}\n\tfor (const schemaInfo of schemas) {\n\t\tconst definition = schemaInfo.schema?.definitions?.[schemaInfo.type]\n\t\tif (definition) {\n\t\t\tresult[schemaInfo.type] = definition\n\t\t}\n\t}\n\treturn result\n}\n\nfunction tsTypeToJsonSchema(tsType: string): Record<string, unknown> {\n\tswitch (tsType) {\n\t\tcase 'number':\n\t\t\treturn { type: 'number' as const }\n\t\tcase 'boolean':\n\t\t\treturn { type: 'boolean' as const }\n\t\tcase 'string':\n\t\tdefault:\n\t\t\treturn { type: 'string' as const }\n\t}\n}\n\nfunction extractBaseTypeName(tsType: string): string | null {\n\tif (!tsType) return null\n\n\tlet type = tsType\n\tconst promiseMatch = type.match(/^Promise<(.+)>$/)\n\tif (promiseMatch) type = promiseMatch[1]\n\ttype = type.replace(/\\[\\]$/, '')\n\tconst genericMatch = type.match(/^\\w+<(\\w+)>$/)\n\tif (genericMatch) type = genericMatch[1]\n\tif (['string', 'number', 'boolean', 'any', 'void', 'unknown', 'object'].includes(type)) {\n\t\treturn null\n\t}\n\treturn type\n}\n\nfunction toOpenApiPath(expressPath: string): string {\n\treturn expressPath.replace(/:(\\w+)/g, '{$1}')\n}\n\nfunction buildFallbackPath(route: OpenApiRouteInput): string {\n\tconst parts = [route.prefix, route.version, route.route, route.path]\n\t\t.filter((part) => part !== undefined && part !== null && part !== '')\n\t\t.map((part) => String(part).replace(/^\\/+|\\/+$/g, ''))\n\t\t.filter((part) => part.length > 0)\n\tconst joined = parts.join('/')\n\treturn `/${joined}`\n}\n","import type { Application, IPlugin } from 'honestjs'\nimport type { Context, Hono, Next } from 'hono'\n\nimport { fromArtifactSync } from './openapi.generator'\nimport type { OpenApiArtifactInput, OpenApiGenerationOptions } from './openapi.generator'\n\nconst DEFAULT_OPENAPI_ROUTE = '/openapi.json'\nconst DEFAULT_UI_ROUTE = '/docs'\nconst DEFAULT_UI_TITLE = 'API Docs'\n/** Default context key when using with RPC plugin (writes to this key). */\nexport const DEFAULT_ARTIFACT_KEY = 'rpc.artifact'\n\nexport type ArtifactInput = OpenApiArtifactInput | string\n\nfunction isContextKey(artifact: ArtifactInput): artifact is string {\n\treturn typeof artifact === 'string'\n}\n\nfunction isArtifact(value: unknown): value is OpenApiArtifactInput {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return false\n\tconst obj = value as Record<string, unknown>\n\treturn Array.isArray(obj.routes) && Array.isArray(obj.schemas)\n}\n\nexport interface ApiDocsPluginOptions extends OpenApiGenerationOptions {\n\t/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */\n\treadonly artifact?: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\n\treadonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\treadonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n}\n\nexport class ApiDocsPlugin implements IPlugin {\n\tprivate readonly artifact: ArtifactInput\n\tprivate readonly openApiRoute: string\n\tprivate readonly uiRoute: string\n\tprivate readonly uiTitle: string\n\tprivate readonly reloadOnRequest: boolean\n\tprivate readonly genOptions: OpenApiGenerationOptions\n\tprivate readonly onOpenApiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\tprivate readonly onUiRequest?: (c: Context, next: Next) => void | Response | Promise<void | Response>\n\n\tprivate app: Application | null = null\n\tprivate cachedSpec: Record<string, unknown> | null = null\n\n\tconstructor(options: ApiDocsPluginOptions = {}) {\n\t\tthis.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY\n\t\tthis.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE)\n\t\tthis.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE)\n\t\tthis.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE\n\t\tthis.reloadOnRequest = options.reloadOnRequest ?? false\n\t\tthis.onOpenApiRequest = options.onOpenApiRequest\n\t\tthis.onUiRequest = options.onUiRequest\n\t\tthis.genOptions = {\n\t\t\ttitle: options.title,\n\t\t\tversion: options.version,\n\t\t\tdescription: options.description,\n\t\t\tservers: options.servers\n\t\t}\n\t}\n\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\n\t\thono.get(this.openApiRoute, async (c) => {\n\t\t\ttry {\n\t\t\t\tconst earlyResponse = await this.runHook(this.onOpenApiRequest, c)\n\t\t\t\tif (earlyResponse) return earlyResponse\n\t\t\t\tconst spec = await this.resolveSpec()\n\t\t\t\treturn c.json(spec)\n\t\t\t} catch (error) {\n\t\t\t\treturn c.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror: 'Failed to load OpenAPI spec',\n\t\t\t\t\t\tmessage: this.toErrorMessage(error)\n\t\t\t\t\t},\n\t\t\t\t\t500\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\thono.get(this.uiRoute, async (c) => {\n\t\t\tconst earlyResponse = await this.runHook(this.onUiRequest, c)\n\t\t\tif (earlyResponse) return earlyResponse\n\t\t\treturn c.html(this.renderSwaggerUiHtml())\n\t\t})\n\t}\n\n\tprivate normalizeRoute(input: string): string {\n\t\tconst trimmed = input.trim()\n\t\tif (!trimmed) return '/'\n\t\tlet normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`\n\t\tif (normalized.length > 1) {\n\t\t\tnormalized = normalized.replace(/\\/+$/g, '')\n\t\t}\n\t\treturn normalized || '/'\n\t}\n\n\tprivate async resolveSpec(): Promise<Record<string, unknown>> {\n\t\tif (!this.reloadOnRequest && this.cachedSpec) {\n\t\t\treturn this.cachedSpec\n\t\t}\n\n\t\tlet artifact: OpenApiArtifactInput\n\n\t\tif (isContextKey(this.artifact)) {\n\t\t\tif (!this.app) {\n\t\t\t\tthrow new Error('ApiDocsPlugin: app not available when resolving artifact from context')\n\t\t\t}\n\t\t\tconst value = this.app.getContext().get<unknown>(this.artifact)\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: no artifact at context key '${this.artifact}'. Ensure RPC plugin (or another producer) runs before ApiDocs and writes to this key.`\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (!isArtifact(value)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`ApiDocsPlugin: value at '${this.artifact}' is not a valid artifact (expected object with routes and schemas)`\n\t\t\t\t)\n\t\t\t}\n\t\t\tartifact = value\n\t\t} else {\n\t\t\tartifact = this.artifact\n\t\t}\n\n\t\tconst artifactVersion = (artifact as { artifactVersion?: unknown }).artifactVersion\n\t\tif (artifactVersion !== undefined && artifactVersion !== '1') {\n\t\t\tthrow new Error(\n\t\t\t\t`ApiDocsPlugin: unsupported artifactVersion '${String(artifactVersion)}'. Supported versions: 1.`\n\t\t\t)\n\t\t}\n\n\t\tconst spec = fromArtifactSync(artifact, this.genOptions) as unknown as Record<string, unknown>\n\t\tif (!this.reloadOnRequest) this.cachedSpec = spec\n\t\treturn spec\n\t}\n\n\tprivate async runHook(\n\t\thook: ((c: Context, next: Next) => void | Response | Promise<void | Response>) | undefined,\n\t\tc: Context\n\t): Promise<Response | undefined> {\n\t\tif (!hook) return undefined\n\t\tlet nextCalled = false\n\t\tconst maybeResponse = await hook(c, async () => {\n\t\t\tnextCalled = true\n\t\t})\n\t\tif (maybeResponse instanceof Response) {\n\t\t\treturn maybeResponse\n\t\t}\n\t\tif (!nextCalled) {\n\t\t\treturn new Response('Forbidden', { status: 403 })\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprivate renderSwaggerUiHtml(): string {\n\t\tconst title = this.escapeHtml(this.uiTitle)\n\t\tconst openApiRoute = this.escapeJsString(this.openApiRoute)\n\n\t\treturn `<!doctype html>\n<html lang=\"en\">\n<head>\n\t<meta charset=\"utf-8\" />\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>${title}</title>\n\t<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\" />\n</head>\n<body>\n\t<div id=\"swagger-ui\"></div>\n\t<script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n\t<script>\n\t\twindow.onload = function () {\n\t\t\twindow.ui = SwaggerUIBundle({\n\t\t\t\turl: '${openApiRoute}',\n\t\t\t\tdom_id: '#swagger-ui'\n\t\t\t});\n\t\t};\n\t</script>\n</body>\n</html>`\n\t}\n\n\tprivate escapeHtml(value: string): string {\n\t\treturn value\n\t\t\t.replace(/&/g, '&')\n\t\t\t.replace(/</g, '<')\n\t\t\t.replace(/>/g, '>')\n\t\t\t.replace(/\"/g, '"')\n\t\t\t.replace(/'/g, ''')\n\t}\n\n\tprivate escapeJsString(value: string): string {\n\t\treturn value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n\t}\n\n\tprivate toErrorMessage(error: unknown): string {\n\t\treturn error instanceof Error ? error.message : String(error)\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@honestjs/api-docs-plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "API Docs plugin for HonestJS - generates OpenAPI from artifact object or context key and serves Swagger UI",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"directory": "packages/api-docs-plugin"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"honestjs": "^0.1.
|
|
34
|
-
"hono": "^4.12.
|
|
33
|
+
"honestjs": "^0.1.11",
|
|
34
|
+
"hono": "^4.12.8",
|
|
35
35
|
"reflect-metadata": "^0.2.2",
|
|
36
36
|
"vitest": "^3.2.4"
|
|
37
37
|
},
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"clean": "rm -rf dist",
|
|
50
50
|
"type-check": "tsc --noEmit",
|
|
51
51
|
"test": "vitest run",
|
|
52
|
-
"test:watch": "vitest"
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"lint": "prettier --check ."
|
|
53
54
|
}
|
|
54
55
|
}
|