@honestjs/api-docs-plugin 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -9
- package/dist/index.d.mts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API Docs Plugin
|
|
2
2
|
|
|
3
|
-
Serves OpenAPI JSON and Swagger UI for your HonestJS application. Always generates the spec from an artifact—pass `{ routes, schemas }` directly or a context key
|
|
3
|
+
Serves OpenAPI JSON and Swagger UI for your HonestJS application. Always generates the spec from an artifact—pass `{ routes, schemas }` directly or a context key (default `'rpc.artifact'` when using with `@honestjs/rpc-plugin`).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ pnpm add @honestjs/api-docs-plugin
|
|
|
14
14
|
|
|
15
15
|
## Usage with RPC Plugin
|
|
16
16
|
|
|
17
|
-
The RPC plugin writes its artifact to the application context.
|
|
17
|
+
The RPC plugin writes its artifact to the application context. ApiDocs defaults to the context key `'rpc.artifact'`, so you can omit `artifact` when using both plugins. Ensure RPC runs before ApiDocs in the plugins array:
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
20
|
import { Application } from "honestjs"
|
|
@@ -23,16 +23,13 @@ import { ApiDocsPlugin } from "@honestjs/api-docs-plugin"
|
|
|
23
23
|
import AppModule from "./app.module"
|
|
24
24
|
|
|
25
25
|
const { hono } = await Application.create(AppModule, {
|
|
26
|
-
plugins: [
|
|
27
|
-
new RPCPlugin(),
|
|
28
|
-
new ApiDocsPlugin({ artifact: "rpc.artifact" }),
|
|
29
|
-
],
|
|
26
|
+
plugins: [new RPCPlugin(), new ApiDocsPlugin()],
|
|
30
27
|
})
|
|
31
28
|
|
|
32
29
|
export default hono
|
|
33
30
|
```
|
|
34
31
|
|
|
35
|
-
If RPC uses custom `context.namespace` / `context.keys.artifact`, pass the resulting full key (
|
|
32
|
+
If RPC uses custom `context.namespace` / `context.keys.artifact`, pass the resulting full key to `artifact` (e.g. `new ApiDocsPlugin({ artifact: "custom.artifact" })`).
|
|
36
33
|
|
|
37
34
|
By default:
|
|
38
35
|
|
|
@@ -66,8 +63,8 @@ plugins: [new ApiDocsPlugin({ artifact })]
|
|
|
66
63
|
|
|
67
64
|
```typescript
|
|
68
65
|
interface ApiDocsPluginOptions {
|
|
69
|
-
//
|
|
70
|
-
artifact
|
|
66
|
+
// Optional: artifact - direct object or context key. Default: 'rpc.artifact'
|
|
67
|
+
artifact?: OpenApiArtifactInput | string
|
|
71
68
|
|
|
72
69
|
// OpenAPI generation (when converting artifact to spec)
|
|
73
70
|
title?: string
|
package/dist/index.d.mts
CHANGED
|
@@ -45,10 +45,12 @@ declare function fromArtifactSync(artifact: OpenApiArtifactInput, options?: Open
|
|
|
45
45
|
declare function fromArtifact(artifact: OpenApiArtifactInput, options?: OpenApiGenerationOptions): Promise<OpenApiDocument>;
|
|
46
46
|
declare function write(openapi: OpenApiDocument, outputPath: string): Promise<string>;
|
|
47
47
|
|
|
48
|
+
/** Default context key when using with RPC plugin (writes to this key). */
|
|
49
|
+
declare const DEFAULT_ARTIFACT_KEY = "rpc.artifact";
|
|
48
50
|
type ArtifactInput = OpenApiArtifactInput | string;
|
|
49
51
|
interface ApiDocsPluginOptions extends OpenApiGenerationOptions {
|
|
50
|
-
/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`)
|
|
51
|
-
readonly artifact
|
|
52
|
+
/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */
|
|
53
|
+
readonly artifact?: ArtifactInput;
|
|
52
54
|
readonly openApiRoute?: string;
|
|
53
55
|
readonly uiRoute?: string;
|
|
54
56
|
readonly uiTitle?: string;
|
|
@@ -63,7 +65,7 @@ declare class ApiDocsPlugin implements IPlugin {
|
|
|
63
65
|
private readonly genOptions;
|
|
64
66
|
private app;
|
|
65
67
|
private cachedSpec;
|
|
66
|
-
constructor(options
|
|
68
|
+
constructor(options?: ApiDocsPluginOptions);
|
|
67
69
|
afterModulesRegistered: (app: Application, hono: Hono) => Promise<void>;
|
|
68
70
|
private normalizeRoute;
|
|
69
71
|
private resolveSpec;
|
|
@@ -73,4 +75,4 @@ declare class ApiDocsPlugin implements IPlugin {
|
|
|
73
75
|
private toErrorMessage;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
export { ApiDocsPlugin, type ApiDocsPluginOptions, type ArtifactInput, type OpenApiArtifactInput, type OpenApiDocument, type OpenApiGenerationOptions, type OpenApiParameterInput, type OpenApiRouteInput, type OpenApiSchemaInput, fromArtifact, fromArtifactSync, write };
|
|
78
|
+
export { ApiDocsPlugin, type ApiDocsPluginOptions, type ArtifactInput, DEFAULT_ARTIFACT_KEY, type OpenApiArtifactInput, type OpenApiDocument, type OpenApiGenerationOptions, type OpenApiParameterInput, type OpenApiRouteInput, type OpenApiSchemaInput, fromArtifact, fromArtifactSync, write };
|
package/dist/index.d.ts
CHANGED
|
@@ -45,10 +45,12 @@ declare function fromArtifactSync(artifact: OpenApiArtifactInput, options?: Open
|
|
|
45
45
|
declare function fromArtifact(artifact: OpenApiArtifactInput, options?: OpenApiGenerationOptions): Promise<OpenApiDocument>;
|
|
46
46
|
declare function write(openapi: OpenApiDocument, outputPath: string): Promise<string>;
|
|
47
47
|
|
|
48
|
+
/** Default context key when using with RPC plugin (writes to this key). */
|
|
49
|
+
declare const DEFAULT_ARTIFACT_KEY = "rpc.artifact";
|
|
48
50
|
type ArtifactInput = OpenApiArtifactInput | string;
|
|
49
51
|
interface ApiDocsPluginOptions extends OpenApiGenerationOptions {
|
|
50
|
-
/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`)
|
|
51
|
-
readonly artifact
|
|
52
|
+
/** Artifact: direct object `{ routes, schemas }` or context key string (e.g. `'rpc.artifact'`). Defaults to `'rpc.artifact'` when omitted. */
|
|
53
|
+
readonly artifact?: ArtifactInput;
|
|
52
54
|
readonly openApiRoute?: string;
|
|
53
55
|
readonly uiRoute?: string;
|
|
54
56
|
readonly uiTitle?: string;
|
|
@@ -63,7 +65,7 @@ declare class ApiDocsPlugin implements IPlugin {
|
|
|
63
65
|
private readonly genOptions;
|
|
64
66
|
private app;
|
|
65
67
|
private cachedSpec;
|
|
66
|
-
constructor(options
|
|
68
|
+
constructor(options?: ApiDocsPluginOptions);
|
|
67
69
|
afterModulesRegistered: (app: Application, hono: Hono) => Promise<void>;
|
|
68
70
|
private normalizeRoute;
|
|
69
71
|
private resolveSpec;
|
|
@@ -73,4 +75,4 @@ declare class ApiDocsPlugin implements IPlugin {
|
|
|
73
75
|
private toErrorMessage;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
export { ApiDocsPlugin, type ApiDocsPluginOptions, type ArtifactInput, type OpenApiArtifactInput, type OpenApiDocument, type OpenApiGenerationOptions, type OpenApiParameterInput, type OpenApiRouteInput, type OpenApiSchemaInput, fromArtifact, fromArtifactSync, write };
|
|
78
|
+
export { ApiDocsPlugin, type ApiDocsPluginOptions, type ArtifactInput, DEFAULT_ARTIFACT_KEY, type OpenApiArtifactInput, type OpenApiDocument, type OpenApiGenerationOptions, type OpenApiParameterInput, type OpenApiRouteInput, type OpenApiSchemaInput, fromArtifact, fromArtifactSync, write };
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ApiDocsPlugin: () => ApiDocsPlugin,
|
|
34
|
+
DEFAULT_ARTIFACT_KEY: () => DEFAULT_ARTIFACT_KEY,
|
|
34
35
|
fromArtifact: () => fromArtifact,
|
|
35
36
|
fromArtifactSync: () => fromArtifactSync,
|
|
36
37
|
write: () => write
|
|
@@ -67,7 +68,7 @@ function fromArtifactSync(artifact, options = {}) {
|
|
|
67
68
|
spec.servers = resolved.servers.map((server) => ({ ...server }));
|
|
68
69
|
}
|
|
69
70
|
for (const route of artifact.routes) {
|
|
70
|
-
const routePath = route.fullPath || buildFallbackPath(route);
|
|
71
|
+
const routePath = route.prefix != null || route.version != null || route.route != null ? buildFallbackPath(route) : route.fullPath || buildFallbackPath(route);
|
|
71
72
|
const openApiPath = toOpenApiPath(routePath);
|
|
72
73
|
const method = route.method.toLowerCase();
|
|
73
74
|
if (!spec.paths[openApiPath]) {
|
|
@@ -222,6 +223,7 @@ function buildFallbackPath(route) {
|
|
|
222
223
|
var DEFAULT_OPENAPI_ROUTE = "/openapi.json";
|
|
223
224
|
var DEFAULT_UI_ROUTE = "/docs";
|
|
224
225
|
var DEFAULT_UI_TITLE = "API Docs";
|
|
226
|
+
var DEFAULT_ARTIFACT_KEY = "rpc.artifact";
|
|
225
227
|
function isContextKey(artifact) {
|
|
226
228
|
return typeof artifact === "string";
|
|
227
229
|
}
|
|
@@ -239,8 +241,8 @@ var ApiDocsPlugin = class {
|
|
|
239
241
|
genOptions;
|
|
240
242
|
app = null;
|
|
241
243
|
cachedSpec = null;
|
|
242
|
-
constructor(options) {
|
|
243
|
-
this.artifact = options.artifact;
|
|
244
|
+
constructor(options = {}) {
|
|
245
|
+
this.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY;
|
|
244
246
|
this.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE);
|
|
245
247
|
this.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE);
|
|
246
248
|
this.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE;
|
|
@@ -347,6 +349,7 @@ var ApiDocsPlugin = class {
|
|
|
347
349
|
// Annotate the CommonJS export names for ESM import in node:
|
|
348
350
|
0 && (module.exports = {
|
|
349
351
|
ApiDocsPlugin,
|
|
352
|
+
DEFAULT_ARTIFACT_KEY,
|
|
350
353
|
fromArtifact,
|
|
351
354
|
fromArtifactSync,
|
|
352
355
|
write
|
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 } 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 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 = 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 { Hono } 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\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'`) resolved via app.getContext().get(key). OpenAPI is always generated from the artifact. */\n\treadonly artifact: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\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\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\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.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 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, (c) => {\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 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 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 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 { Hono } 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}\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\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.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 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, (c) => {\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 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 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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -28,7 +28,7 @@ function fromArtifactSync(artifact, options = {}) {
|
|
|
28
28
|
spec.servers = resolved.servers.map((server) => ({ ...server }));
|
|
29
29
|
}
|
|
30
30
|
for (const route of artifact.routes) {
|
|
31
|
-
const routePath = route.fullPath || buildFallbackPath(route);
|
|
31
|
+
const routePath = route.prefix != null || route.version != null || route.route != null ? buildFallbackPath(route) : route.fullPath || buildFallbackPath(route);
|
|
32
32
|
const openApiPath = toOpenApiPath(routePath);
|
|
33
33
|
const method = route.method.toLowerCase();
|
|
34
34
|
if (!spec.paths[openApiPath]) {
|
|
@@ -183,6 +183,7 @@ function buildFallbackPath(route) {
|
|
|
183
183
|
var DEFAULT_OPENAPI_ROUTE = "/openapi.json";
|
|
184
184
|
var DEFAULT_UI_ROUTE = "/docs";
|
|
185
185
|
var DEFAULT_UI_TITLE = "API Docs";
|
|
186
|
+
var DEFAULT_ARTIFACT_KEY = "rpc.artifact";
|
|
186
187
|
function isContextKey(artifact) {
|
|
187
188
|
return typeof artifact === "string";
|
|
188
189
|
}
|
|
@@ -200,8 +201,8 @@ var ApiDocsPlugin = class {
|
|
|
200
201
|
genOptions;
|
|
201
202
|
app = null;
|
|
202
203
|
cachedSpec = null;
|
|
203
|
-
constructor(options) {
|
|
204
|
-
this.artifact = options.artifact;
|
|
204
|
+
constructor(options = {}) {
|
|
205
|
+
this.artifact = options.artifact ?? DEFAULT_ARTIFACT_KEY;
|
|
205
206
|
this.openApiRoute = this.normalizeRoute(options.openApiRoute ?? DEFAULT_OPENAPI_ROUTE);
|
|
206
207
|
this.uiRoute = this.normalizeRoute(options.uiRoute ?? DEFAULT_UI_ROUTE);
|
|
207
208
|
this.uiTitle = options.uiTitle ?? DEFAULT_UI_TITLE;
|
|
@@ -307,6 +308,7 @@ var ApiDocsPlugin = class {
|
|
|
307
308
|
};
|
|
308
309
|
export {
|
|
309
310
|
ApiDocsPlugin,
|
|
311
|
+
DEFAULT_ARTIFACT_KEY,
|
|
310
312
|
fromArtifact,
|
|
311
313
|
fromArtifactSync,
|
|
312
314
|
write
|
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 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 = 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 { Hono } 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\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'`) resolved via app.getContext().get(key). OpenAPI is always generated from the artifact. */\n\treadonly artifact: ArtifactInput\n\treadonly openApiRoute?: string\n\treadonly uiRoute?: string\n\treadonly uiTitle?: string\n\treadonly reloadOnRequest?: boolean\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\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\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.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 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, (c) => {\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 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 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 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 { Hono } 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}\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\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.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 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, (c) => {\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 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 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.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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",
|