@forinda/kickjs-swagger 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +79 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -210,6 +210,74 @@ function buildOpenAPISpec(options = {}) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
+
const queryParamsConfig = Reflect.getMetadata(METADATA.QUERY_PARAMS, controllerClass, route.handlerName);
|
|
214
|
+
if (queryParamsConfig) {
|
|
215
|
+
if (queryParamsConfig.filterable?.length) {
|
|
216
|
+
op.parameters.push({
|
|
217
|
+
name: "filter",
|
|
218
|
+
in: "query",
|
|
219
|
+
required: false,
|
|
220
|
+
description: `Filter fields: ${queryParamsConfig.filterable.join(", ")}. Format: \`field:operator:value\`. Operators: eq, neq, gt, gte, lt, lte, contains, starts, ends, in, between`,
|
|
221
|
+
schema: {
|
|
222
|
+
type: "array",
|
|
223
|
+
items: {
|
|
224
|
+
type: "string"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
style: "form",
|
|
228
|
+
explode: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (queryParamsConfig.sortable?.length) {
|
|
232
|
+
op.parameters.push({
|
|
233
|
+
name: "sort",
|
|
234
|
+
in: "query",
|
|
235
|
+
required: false,
|
|
236
|
+
description: `Sort fields: ${queryParamsConfig.sortable.join(", ")}. Format: \`field:asc\` or \`field:desc\``,
|
|
237
|
+
schema: {
|
|
238
|
+
type: "array",
|
|
239
|
+
items: {
|
|
240
|
+
type: "string"
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
style: "form",
|
|
244
|
+
explode: true
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (queryParamsConfig.searchable?.length) {
|
|
248
|
+
op.parameters.push({
|
|
249
|
+
name: "q",
|
|
250
|
+
in: "query",
|
|
251
|
+
required: false,
|
|
252
|
+
description: `Search across: ${queryParamsConfig.searchable.join(", ")}`,
|
|
253
|
+
schema: {
|
|
254
|
+
type: "string"
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
op.parameters.push({
|
|
259
|
+
name: "page",
|
|
260
|
+
in: "query",
|
|
261
|
+
required: false,
|
|
262
|
+
description: "Page number (default: 1)",
|
|
263
|
+
schema: {
|
|
264
|
+
type: "integer",
|
|
265
|
+
minimum: 1,
|
|
266
|
+
default: 1
|
|
267
|
+
}
|
|
268
|
+
}, {
|
|
269
|
+
name: "limit",
|
|
270
|
+
in: "query",
|
|
271
|
+
required: false,
|
|
272
|
+
description: "Items per page (default: 20, max: 100)",
|
|
273
|
+
schema: {
|
|
274
|
+
type: "integer",
|
|
275
|
+
minimum: 1,
|
|
276
|
+
maximum: 100,
|
|
277
|
+
default: 20
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
213
281
|
if (op.parameters.length === 0) delete op.parameters;
|
|
214
282
|
if (route.validation?.body && [
|
|
215
283
|
"post",
|
|
@@ -232,9 +300,18 @@ function buildOpenAPISpec(options = {}) {
|
|
|
232
300
|
}
|
|
233
301
|
const fileUpload = Reflect.getMetadata(METADATA.FILE_UPLOAD, controllerClass, route.handlerName);
|
|
234
302
|
if (fileUpload) {
|
|
303
|
+
const fieldName = fileUpload.fieldName ?? "file";
|
|
235
304
|
const properties = {};
|
|
236
|
-
if (fileUpload.
|
|
237
|
-
properties[
|
|
305
|
+
if (fileUpload.mode === "array") {
|
|
306
|
+
properties[fieldName] = {
|
|
307
|
+
type: "array",
|
|
308
|
+
items: {
|
|
309
|
+
type: "string",
|
|
310
|
+
format: "binary"
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
} else if (fileUpload.mode !== "none") {
|
|
314
|
+
properties[fieldName] = {
|
|
238
315
|
type: "string",
|
|
239
316
|
format: "binary"
|
|
240
317
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schema-parser.ts","../src/decorators.ts","../src/openapi-builder.ts","../src/swagger.adapter.ts","../src/ui.ts"],"sourcesContent":["/**\n * Interface for converting validation library schemas to JSON Schema.\n *\n * KickJS ships with a Zod parser by default. To use a different validation\n * library (Yup, Joi, Valibot, ArkType, etc.), implement this interface and\n * pass it to the SwaggerAdapter.\n *\n * @example\n * ```ts\n * import Joi from 'joi'\n * import joiToJson from 'joi-to-json'\n *\n * const joiParser: SchemaParser = {\n * name: 'joi',\n * supports: (schema) => Joi.isSchema(schema),\n * toJsonSchema: (schema) => joiToJson(schema),\n * }\n *\n * new SwaggerAdapter({ schemaParser: joiParser })\n * ```\n */\nexport interface SchemaParser {\n /** Human-readable name for logging/debugging */\n readonly name: string\n\n /**\n * Return true if this parser can handle the given schema object.\n * Called before `toJsonSchema` to allow graceful fallback.\n */\n supports(schema: unknown): boolean\n\n /**\n * Convert a validation schema to a JSON Schema object.\n * Should return a plain object conforming to JSON Schema draft-07 or later.\n * Must not include the top-level `$schema` key — the builder adds it.\n */\n toJsonSchema(schema: unknown): Record<string, unknown>\n}\n\n/**\n * Default schema parser for Zod v4+.\n * Uses Zod's built-in `.toJSONSchema()` instance method.\n */\nexport const zodSchemaParser: SchemaParser = {\n name: 'zod',\n\n supports(schema: unknown): boolean {\n return (\n schema != null &&\n typeof schema === 'object' &&\n typeof (schema as any).safeParse === 'function' &&\n typeof (schema as any).toJSONSchema === 'function'\n )\n },\n\n toJsonSchema(schema: unknown): Record<string, unknown> {\n const { $schema: _, ...rest } = (schema as any).toJSONSchema() as Record<string, unknown>\n return rest\n },\n}\n","import 'reflect-metadata'\n\nconst SWAGGER_KEYS = {\n OPERATION: Symbol('kick:swagger:operation'),\n RESPONSES: Symbol('kick:swagger:responses'),\n TAGS: Symbol('kick:swagger:tags'),\n BEARER_AUTH: Symbol('kick:swagger:bearer'),\n EXCLUDE: Symbol('kick:swagger:exclude'),\n}\n\nexport { SWAGGER_KEYS }\n\nexport interface ApiOperationOptions {\n summary?: string\n description?: string\n operationId?: string\n deprecated?: boolean\n}\n\nexport interface ApiResponseOptions {\n status: number\n description?: string\n schema?: any\n /** Schema name in components/schemas (e.g., 'UserResponse', 'ErrorBody'). Auto-generated from handler name if omitted. */\n name?: string\n}\n\n/** Attach operation metadata to a route handler */\nexport function ApiOperation(options: ApiOperationOptions): MethodDecorator {\n return (target, propertyKey) => {\n Reflect.defineMetadata(SWAGGER_KEYS.OPERATION, options, target.constructor, propertyKey)\n }\n}\n\n/** Document a response status. Can be stacked multiple times. */\nexport function ApiResponse(options: ApiResponseOptions): MethodDecorator {\n return (target, propertyKey) => {\n const existing: ApiResponseOptions[] =\n Reflect.getMetadata(SWAGGER_KEYS.RESPONSES, target.constructor, propertyKey) || []\n Reflect.defineMetadata(\n SWAGGER_KEYS.RESPONSES,\n [...existing, options],\n target.constructor,\n propertyKey,\n )\n }\n}\n\n/** Apply OpenAPI tags at class or method level */\nexport function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.TAGS, tags, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.TAGS, tags, target)\n }\n }\n}\n\n/** Mark endpoint as requiring Bearer token auth */\nexport function ApiBearerAuth(name = 'BearerAuth'): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.BEARER_AUTH, name, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.BEARER_AUTH, name, target)\n }\n }\n}\n\n/** Exclude a controller or method from the OpenAPI spec */\nexport function ApiExclude(): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.EXCLUDE, true, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.EXCLUDE, true, target)\n }\n }\n}\n","import 'reflect-metadata'\nimport { METADATA, type RouteDefinition } from '@forinda/kickjs-core'\nimport { SWAGGER_KEYS, type ApiOperationOptions, type ApiResponseOptions } from './decorators'\nimport { zodSchemaParser, type SchemaParser } from './schema-parser'\n\nexport interface OpenAPIInfo {\n title: string\n version: string\n description?: string\n}\n\nexport interface SwaggerOptions {\n info?: Partial<OpenAPIInfo>\n servers?: { url: string; description?: string }[]\n bearerAuth?: boolean\n /**\n * Pluggable schema parser for converting validation schemas to JSON Schema.\n * Defaults to `zodSchemaParser` which handles Zod v4+ schemas.\n *\n * Override this to use Yup, Joi, Valibot, ArkType, or any other library.\n *\n * @example\n * ```ts\n * new SwaggerAdapter({\n * schemaParser: myYupParser,\n * })\n * ```\n */\n schemaParser?: SchemaParser\n}\n\ninterface RegisteredRoute {\n controllerClass: any\n mountPath: string\n}\n\nconst registeredRoutes: RegisteredRoute[] = []\n\n/** Register a controller for OpenAPI introspection (called by Application during route mounting) */\nexport function registerControllerForDocs(controllerClass: any, mountPath: string): void {\n registeredRoutes.push({ controllerClass, mountPath })\n}\n\n/** Clear all registered routes (for HMR) */\nexport function clearRegisteredRoutes(): void {\n registeredRoutes.length = 0\n}\n\n/** Build a full OpenAPI 3.0.3 spec from registered controllers and their decorators */\nexport function buildOpenAPISpec(options: SwaggerOptions = {}): any {\n const parser = options.schemaParser ?? zodSchemaParser\n\n /** Convert a validation schema to JSON Schema using the configured parser */\n const toJsonSchema = (schema: unknown): Record<string, unknown> | null => {\n try {\n if (!parser.supports(schema)) return null\n return parser.toJsonSchema(schema)\n } catch {\n return null\n }\n }\n\n const componentSchemas: Record<string, any> = {}\n let schemaCounter = 0\n\n /**\n * Register a schema in components.schemas and return a $ref pointer.\n * If the schema has a title/label, use that as the name. Otherwise generate one.\n */\n const registerSchema = (jsonSchema: Record<string, unknown>, hint?: string): any => {\n // Try to extract a name from the schema\n let name = (jsonSchema.title as string) || (jsonSchema.label as string) || hint || ''\n if (!name) {\n name = `Schema${++schemaCounter}`\n }\n // Sanitize name for OpenAPI (remove spaces, special chars)\n name = name.replace(/[^a-zA-Z0-9]/g, '')\n\n // Avoid duplicates — if already registered with same name, reuse\n if (!componentSchemas[name]) {\n const clean = { ...jsonSchema }\n delete clean.title\n delete clean.label\n delete clean.$schema\n componentSchemas[name] = clean\n }\n return { $ref: `#/components/schemas/${name}` }\n }\n\n const spec: any = {\n openapi: '3.0.3',\n info: {\n title: options.info?.title || 'API',\n version: options.info?.version || '1.0.0',\n ...(options.info?.description ? { description: options.info.description } : {}),\n },\n paths: {},\n components: { schemas: {}, securitySchemes: {} },\n tags: [],\n }\n\n if (options.servers) {\n spec.servers = options.servers\n }\n\n const allTags = new Set<string>()\n const securitySchemes: Record<string, any> = {}\n\n for (const { controllerClass, mountPath } of registeredRoutes) {\n // Skip excluded controllers\n if (Reflect.getMetadata(SWAGGER_KEYS.EXCLUDE, controllerClass)) continue\n\n const routes: RouteDefinition[] = Reflect.getMetadata(METADATA.ROUTES, controllerClass) || []\n const classTags: string[] = Reflect.getMetadata(SWAGGER_KEYS.TAGS, controllerClass) || []\n const classAuth: string | undefined = Reflect.getMetadata(\n SWAGGER_KEYS.BEARER_AUTH,\n controllerClass,\n )\n const controllerPath = Reflect.getMetadata(METADATA.CONTROLLER_PATH, controllerClass) || '/'\n\n for (const route of routes) {\n // Skip excluded methods\n if (Reflect.getMetadata(SWAGGER_KEYS.EXCLUDE, controllerClass, route.handlerName)) continue\n\n // Build the full path\n let routePath = route.path === '/' ? '' : route.path\n let fullPath = mountPath + (controllerPath === '/' ? '' : controllerPath) + routePath\n if (!fullPath) fullPath = '/'\n\n // Convert Express :param to OpenAPI {param}\n const openApiPath = fullPath.replace(/:([a-zA-Z_]+)/g, '{$1}')\n const method = route.method.toLowerCase()\n\n // Gather metadata\n const operation: ApiOperationOptions =\n Reflect.getMetadata(SWAGGER_KEYS.OPERATION, controllerClass, route.handlerName) || {}\n const responses: ApiResponseOptions[] =\n Reflect.getMetadata(SWAGGER_KEYS.RESPONSES, controllerClass, route.handlerName) || []\n const methodTags: string[] =\n Reflect.getMetadata(SWAGGER_KEYS.TAGS, controllerClass, route.handlerName) || []\n const methodAuth: string | undefined = Reflect.getMetadata(\n SWAGGER_KEYS.BEARER_AUTH,\n controllerClass,\n route.handlerName,\n )\n\n // Tags — method level overrides class level\n const tags = methodTags.length > 0 ? methodTags : classTags\n tags.forEach((t) => allTags.add(t))\n\n // Build operation object\n const op: any = {\n ...(tags.length > 0 ? { tags } : {}),\n ...(operation.summary ? { summary: operation.summary } : {}),\n ...(operation.description ? { description: operation.description } : {}),\n ...(operation.operationId ? { operationId: operation.operationId } : {}),\n ...(operation.deprecated ? { deprecated: true } : {}),\n parameters: [],\n responses: {},\n }\n\n // Path parameters\n const paramMatches = fullPath.match(/:([a-zA-Z_]+)/g) || []\n for (const match of paramMatches) {\n const paramName = match.slice(1)\n let schema: any = { type: 'string' }\n\n // Try to get type from params validation schema\n if (route.validation?.params) {\n const jsonSchema = toJsonSchema(route.validation.params)\n if (jsonSchema?.properties && typeof jsonSchema.properties === 'object') {\n const props = jsonSchema.properties as Record<string, any>\n if (props[paramName]) {\n schema = props[paramName]\n }\n }\n }\n\n op.parameters.push({\n name: paramName,\n in: 'path',\n required: true,\n schema,\n })\n }\n\n // Query parameters\n if (route.validation?.query) {\n const jsonSchema = toJsonSchema(route.validation.query)\n if (jsonSchema?.properties && typeof jsonSchema.properties === 'object') {\n const required = Array.isArray(jsonSchema.required) ? jsonSchema.required : []\n for (const [name, propSchema] of Object.entries(\n jsonSchema.properties as Record<string, any>,\n )) {\n op.parameters.push({\n name,\n in: 'query',\n required: required.includes(name),\n schema: propSchema,\n })\n }\n }\n }\n\n // Remove empty parameters array\n if (op.parameters.length === 0) delete op.parameters\n\n // Request body\n if (route.validation?.body && ['post', 'put', 'patch'].includes(method)) {\n const bodySchema = toJsonSchema(route.validation.body)\n if (bodySchema) {\n const bodyName = route.validation.name || `${route.handlerName}Body`\n const ref = registerSchema(bodySchema, bodyName)\n op.requestBody = {\n required: true,\n content: { 'application/json': { schema: ref } },\n }\n }\n }\n\n // File upload detection\n const fileUpload = Reflect.getMetadata(\n METADATA.FILE_UPLOAD,\n controllerClass,\n route.handlerName,\n )\n if (fileUpload) {\n const properties: any = {}\n if (fileUpload.fieldName) {\n properties[fileUpload.fieldName] = {\n type: 'string',\n format: 'binary',\n }\n }\n op.requestBody = {\n required: true,\n content: {\n 'multipart/form-data': {\n schema: { type: 'object', properties },\n },\n },\n }\n }\n\n // Responses\n if (responses.length > 0) {\n for (const resp of responses) {\n op.responses[String(resp.status)] = {\n description: resp.description || '',\n ...(resp.schema\n ? (() => {\n const converted =\n typeof resp.schema === 'function' || typeof resp.schema === 'object'\n ? toJsonSchema(resp.schema)\n : null\n const schemaName = resp.name || `${route.handlerName}Response${resp.status}`\n const finalSchema = converted\n ? registerSchema(converted, schemaName)\n : typeof resp.schema === 'object'\n ? resp.schema\n : undefined\n return finalSchema\n ? { content: { 'application/json': { schema: finalSchema } } }\n : {}\n })()\n : {}),\n }\n }\n } else {\n // Auto-generate default responses\n const defaultStatus = method === 'post' ? '201' : method === 'delete' ? '204' : '200'\n op.responses[defaultStatus] = { description: 'Successful operation' }\n\n if (route.validation?.body) {\n op.responses['422'] = { description: 'Validation error' }\n }\n }\n\n // Security\n const authName = methodAuth || classAuth\n if (authName) {\n op.security = [{ [authName]: [] }]\n securitySchemes[authName] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n }\n }\n\n // Mount\n if (!spec.paths[openApiPath]) spec.paths[openApiPath] = {}\n spec.paths[openApiPath][method] = op\n }\n }\n\n // Finalize\n spec.tags = Array.from(allTags).map((name) => ({ name }))\n spec.components.securitySchemes = securitySchemes\n\n if (options.bearerAuth) {\n if (!securitySchemes.BearerAuth) {\n spec.components.securitySchemes.BearerAuth = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n }\n }\n spec.security = [{ BearerAuth: [] }]\n }\n\n // Merge collected schemas into components\n spec.components.schemas = componentSchemas\n\n // Clean up empty components\n if (Object.keys(spec.components.schemas).length === 0) delete spec.components.schemas\n if (Object.keys(spec.components.securitySchemes).length === 0)\n delete spec.components.securitySchemes\n if (Object.keys(spec.components).length === 0) delete spec.components\n\n return spec\n}\n","import { Router } from 'express'\nimport type { Express } from 'express'\nimport { Logger, type AppAdapter, type Container } from '@forinda/kickjs-core'\nimport {\n buildOpenAPISpec,\n registerControllerForDocs,\n clearRegisteredRoutes,\n type SwaggerOptions,\n} from './openapi-builder'\nimport { swaggerUIHtml, redocHtml } from './ui'\n\nconst log = Logger.for('SwaggerAdapter')\n\nexport interface SwaggerAdapterOptions extends SwaggerOptions {\n /** Path to serve Swagger UI (default: '/docs') */\n docsPath?: string\n /** Path to serve ReDoc (default: '/redoc') */\n redocPath?: string\n /** Path to serve the raw JSON spec (default: '/openapi.json') */\n specPath?: string\n /** Other adapters to discover (e.g., WsAdapter for WebSocket server URLs) */\n adapters?: any[]\n}\n\n/**\n * Swagger adapter — auto-generates OpenAPI spec from decorators and serves docs.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * adapters: [\n * new SwaggerAdapter({\n * info: { title: 'My API', version: '1.0.0' },\n * }),\n * ],\n * })\n * ```\n *\n * Endpoints:\n * GET /docs — Swagger UI\n * GET /redoc — ReDoc\n * GET /openapi.json — Raw OpenAPI 3.0.3 spec\n */\nexport class SwaggerAdapter implements AppAdapter {\n name = 'SwaggerAdapter'\n\n constructor(private options: SwaggerAdapterOptions = {}) {}\n\n /** Auto-detect server URLs from the running HTTP server and peer adapters */\n afterStart(server: any, _container: Container): void {\n const addr = server?.address?.()\n if (!addr || typeof addr !== 'object') return\n\n const host = addr.address === '::' || addr.address === '0.0.0.0' ? 'localhost' : addr.address\n\n // Auto-add HTTP server URL if none configured\n if (!this.options.servers || this.options.servers.length === 0) {\n this.options.servers = [{ url: `http://${host}:${addr.port}`, description: 'HTTP server' }]\n }\n\n // Auto-add WebSocket server URLs from WsAdapter\n const wsAdapter = this.options.adapters?.find(\n (a) => a.name === 'WsAdapter' && typeof a.getStats === 'function',\n )\n if (wsAdapter) {\n const stats = wsAdapter.getStats()\n for (const namespace of Object.keys(stats.namespaces || {})) {\n this.options.servers!.push({\n url: `ws://${host}:${addr.port}${namespace}`,\n description: `WebSocket: ${namespace}`,\n })\n }\n }\n }\n\n /** Collect controller metadata as routes are mounted */\n onRouteMount(controllerClass: any, mountPath: string): void {\n registerControllerForDocs(controllerClass, mountPath)\n }\n\n beforeMount(app: Express, _container: Container): void {\n // Clear previous registrations (supports HMR rebuild)\n clearRegisteredRoutes()\n const docsPath = this.options.docsPath ?? '/docs'\n const redocPath = this.options.redocPath ?? '/redoc'\n const specPath = this.options.specPath ?? '/openapi.json'\n\n // Use a sub-router with relaxed CSP so CDN scripts load\n const docsRouter = Router()\n\n docsRouter.use((_req, res, next) => {\n res.setHeader(\n 'Content-Security-Policy',\n [\n \"default-src 'self'\",\n \"script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.redoc.ly https://cdn.jsdelivr.net\",\n \"style-src 'self' 'unsafe-inline' https://unpkg.com https://fonts.googleapis.com\",\n \"font-src 'self' https://fonts.gstatic.com\",\n \"img-src 'self' data: https://unpkg.com\",\n \"connect-src 'self'\",\n ].join('; '),\n )\n next()\n })\n\n // Spec endpoint (JSON)\n docsRouter.get(specPath, (_req, res) => {\n const spec = buildOpenAPISpec(this.options)\n res.json(spec)\n })\n\n // Swagger UI\n docsRouter.get(docsPath, (_req, res) => {\n res.type('html').send(swaggerUIHtml(specPath, this.options.info?.title))\n })\n\n // ReDoc\n docsRouter.get(redocPath, (_req, res) => {\n res.type('html').send(redocHtml(specPath, this.options.info?.title))\n })\n\n app.use(docsRouter)\n\n log.info(`Swagger UI: ${docsPath}`)\n log.info(`ReDoc: ${redocPath}`)\n log.info(`OpenAPI spec: ${specPath}`)\n }\n}\n\n// Re-export for use by Application when mounting module routes\nexport { registerControllerForDocs, clearRegisteredRoutes }\n","/** Escape a string for safe HTML attribute/content interpolation */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\n/** Generate Swagger UI HTML that loads from CDN */\nexport function swaggerUIHtml(specUrl: string, title = 'API Docs'): string {\n const safeTitle = escapeHtml(title)\n const safeUrl = JSON.stringify(specUrl).replace(/</g, '\\\\u003c')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${safeTitle}</title>\n <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\">\n</head>\n<body>\n <div id=\"swagger-ui\"></div>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js\"></script>\n <script>\n SwaggerUIBundle({\n url: ${safeUrl},\n dom_id: '#swagger-ui',\n deepLinking: true,\n presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],\n plugins: [SwaggerUIBundle.plugins.DownloadUrl],\n layout: 'StandaloneLayout',\n });\n </script>\n</body>\n</html>`\n}\n\n/** Generate ReDoc HTML that loads from CDN */\nexport function redocHtml(specUrl: string, title = 'API Docs'): string {\n const safeTitle = escapeHtml(title)\n const safeUrl = escapeHtml(specUrl)\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${safeTitle}</title>\n</head>\n<body>\n <redoc spec-url=\"${safeUrl}\"></redoc>\n <script src=\"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js\"></script>\n</body>\n</html>`\n}\n"],"mappings":"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","names":["zodSchemaParser","name","supports","schema","safeParse","toJSONSchema","toJsonSchema","$schema","_","rest","SWAGGER_KEYS","OPERATION","Symbol","RESPONSES","TAGS","BEARER_AUTH","EXCLUDE","ApiOperation","options","target","propertyKey","Reflect","defineMetadata","SWAGGER_KEYS","OPERATION","ApiResponse","existing","getMetadata","RESPONSES","ApiTags","tags","TAGS","ApiBearerAuth","name","BEARER_AUTH","ApiExclude","EXCLUDE","METADATA","registeredRoutes","registerControllerForDocs","controllerClass","mountPath","push","clearRegisteredRoutes","length","buildOpenAPISpec","options","parser","schemaParser","zodSchemaParser","toJsonSchema","schema","supports","componentSchemas","schemaCounter","registerSchema","jsonSchema","hint","name","title","label","replace","clean","$schema","$ref","spec","openapi","info","version","description","paths","components","schemas","securitySchemes","tags","servers","allTags","Set","Reflect","getMetadata","SWAGGER_KEYS","EXCLUDE","routes","METADATA","ROUTES","classTags","TAGS","classAuth","BEARER_AUTH","controllerPath","CONTROLLER_PATH","route","handlerName","routePath","path","fullPath","openApiPath","method","toLowerCase","operation","OPERATION","responses","RESPONSES","methodTags","methodAuth","forEach","t","add","op","summary","operationId","deprecated","parameters","paramMatches","match","paramName","slice","type","validation","params","properties","props","in","required","query","Array","isArray","propSchema","Object","entries","includes","body","bodySchema","bodyName","ref","requestBody","content","fileUpload","FILE_UPLOAD","fieldName","format","resp","String","status","converted","schemaName","finalSchema","undefined","defaultStatus","authName","security","scheme","bearerFormat","from","map","bearerAuth","BearerAuth","keys","Router","Logger","escapeHtml","str","replace","swaggerUIHtml","specUrl","title","safeTitle","safeUrl","JSON","stringify","redocHtml","log","Logger","for","SwaggerAdapter","name","options","afterStart","server","_container","addr","address","host","servers","length","url","port","description","wsAdapter","adapters","find","a","getStats","stats","namespace","Object","keys","namespaces","push","onRouteMount","controllerClass","mountPath","registerControllerForDocs","beforeMount","app","clearRegisteredRoutes","docsPath","redocPath","specPath","docsRouter","Router","use","_req","res","next","setHeader","join","get","spec","buildOpenAPISpec","json","type","send","swaggerUIHtml","info","title","redocHtml"]}
|
|
1
|
+
{"version":3,"sources":["../src/schema-parser.ts","../src/decorators.ts","../src/openapi-builder.ts","../src/swagger.adapter.ts","../src/ui.ts"],"sourcesContent":["/**\n * Interface for converting validation library schemas to JSON Schema.\n *\n * KickJS ships with a Zod parser by default. To use a different validation\n * library (Yup, Joi, Valibot, ArkType, etc.), implement this interface and\n * pass it to the SwaggerAdapter.\n *\n * @example\n * ```ts\n * import Joi from 'joi'\n * import joiToJson from 'joi-to-json'\n *\n * const joiParser: SchemaParser = {\n * name: 'joi',\n * supports: (schema) => Joi.isSchema(schema),\n * toJsonSchema: (schema) => joiToJson(schema),\n * }\n *\n * new SwaggerAdapter({ schemaParser: joiParser })\n * ```\n */\nexport interface SchemaParser {\n /** Human-readable name for logging/debugging */\n readonly name: string\n\n /**\n * Return true if this parser can handle the given schema object.\n * Called before `toJsonSchema` to allow graceful fallback.\n */\n supports(schema: unknown): boolean\n\n /**\n * Convert a validation schema to a JSON Schema object.\n * Should return a plain object conforming to JSON Schema draft-07 or later.\n * Must not include the top-level `$schema` key — the builder adds it.\n */\n toJsonSchema(schema: unknown): Record<string, unknown>\n}\n\n/**\n * Default schema parser for Zod v4+.\n * Uses Zod's built-in `.toJSONSchema()` instance method.\n */\nexport const zodSchemaParser: SchemaParser = {\n name: 'zod',\n\n supports(schema: unknown): boolean {\n return (\n schema != null &&\n typeof schema === 'object' &&\n typeof (schema as any).safeParse === 'function' &&\n typeof (schema as any).toJSONSchema === 'function'\n )\n },\n\n toJsonSchema(schema: unknown): Record<string, unknown> {\n const { $schema: _, ...rest } = (schema as any).toJSONSchema() as Record<string, unknown>\n return rest\n },\n}\n","import 'reflect-metadata'\n\nconst SWAGGER_KEYS = {\n OPERATION: Symbol('kick:swagger:operation'),\n RESPONSES: Symbol('kick:swagger:responses'),\n TAGS: Symbol('kick:swagger:tags'),\n BEARER_AUTH: Symbol('kick:swagger:bearer'),\n EXCLUDE: Symbol('kick:swagger:exclude'),\n}\n\nexport { SWAGGER_KEYS }\n\nexport interface ApiOperationOptions {\n summary?: string\n description?: string\n operationId?: string\n deprecated?: boolean\n}\n\nexport interface ApiResponseOptions {\n status: number\n description?: string\n schema?: any\n /** Schema name in components/schemas (e.g., 'UserResponse', 'ErrorBody'). Auto-generated from handler name if omitted. */\n name?: string\n}\n\n/** Attach operation metadata to a route handler */\nexport function ApiOperation(options: ApiOperationOptions): MethodDecorator {\n return (target, propertyKey) => {\n Reflect.defineMetadata(SWAGGER_KEYS.OPERATION, options, target.constructor, propertyKey)\n }\n}\n\n/** Document a response status. Can be stacked multiple times. */\nexport function ApiResponse(options: ApiResponseOptions): MethodDecorator {\n return (target, propertyKey) => {\n const existing: ApiResponseOptions[] =\n Reflect.getMetadata(SWAGGER_KEYS.RESPONSES, target.constructor, propertyKey) || []\n Reflect.defineMetadata(\n SWAGGER_KEYS.RESPONSES,\n [...existing, options],\n target.constructor,\n propertyKey,\n )\n }\n}\n\n/** Apply OpenAPI tags at class or method level */\nexport function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.TAGS, tags, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.TAGS, tags, target)\n }\n }\n}\n\n/** Mark endpoint as requiring Bearer token auth */\nexport function ApiBearerAuth(name = 'BearerAuth'): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.BEARER_AUTH, name, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.BEARER_AUTH, name, target)\n }\n }\n}\n\n/** Exclude a controller or method from the OpenAPI spec */\nexport function ApiExclude(): ClassDecorator & MethodDecorator {\n return (target: any, propertyKey?: string | symbol) => {\n if (propertyKey) {\n Reflect.defineMetadata(SWAGGER_KEYS.EXCLUDE, true, target.constructor, propertyKey)\n } else {\n Reflect.defineMetadata(SWAGGER_KEYS.EXCLUDE, true, target)\n }\n }\n}\n","import 'reflect-metadata'\nimport { METADATA, type RouteDefinition } from '@forinda/kickjs-core'\nimport { SWAGGER_KEYS, type ApiOperationOptions, type ApiResponseOptions } from './decorators'\nimport { zodSchemaParser, type SchemaParser } from './schema-parser'\n\nexport interface OpenAPIInfo {\n title: string\n version: string\n description?: string\n}\n\nexport interface SwaggerOptions {\n info?: Partial<OpenAPIInfo>\n servers?: { url: string; description?: string }[]\n bearerAuth?: boolean\n /**\n * Pluggable schema parser for converting validation schemas to JSON Schema.\n * Defaults to `zodSchemaParser` which handles Zod v4+ schemas.\n *\n * Override this to use Yup, Joi, Valibot, ArkType, or any other library.\n *\n * @example\n * ```ts\n * new SwaggerAdapter({\n * schemaParser: myYupParser,\n * })\n * ```\n */\n schemaParser?: SchemaParser\n}\n\ninterface RegisteredRoute {\n controllerClass: any\n mountPath: string\n}\n\nconst registeredRoutes: RegisteredRoute[] = []\n\n/** Register a controller for OpenAPI introspection (called by Application during route mounting) */\nexport function registerControllerForDocs(controllerClass: any, mountPath: string): void {\n registeredRoutes.push({ controllerClass, mountPath })\n}\n\n/** Clear all registered routes (for HMR) */\nexport function clearRegisteredRoutes(): void {\n registeredRoutes.length = 0\n}\n\n/** Build a full OpenAPI 3.0.3 spec from registered controllers and their decorators */\nexport function buildOpenAPISpec(options: SwaggerOptions = {}): any {\n const parser = options.schemaParser ?? zodSchemaParser\n\n /** Convert a validation schema to JSON Schema using the configured parser */\n const toJsonSchema = (schema: unknown): Record<string, unknown> | null => {\n try {\n if (!parser.supports(schema)) return null\n return parser.toJsonSchema(schema)\n } catch {\n return null\n }\n }\n\n const componentSchemas: Record<string, any> = {}\n let schemaCounter = 0\n\n /**\n * Register a schema in components.schemas and return a $ref pointer.\n * If the schema has a title/label, use that as the name. Otherwise generate one.\n */\n const registerSchema = (jsonSchema: Record<string, unknown>, hint?: string): any => {\n // Try to extract a name from the schema\n let name = (jsonSchema.title as string) || (jsonSchema.label as string) || hint || ''\n if (!name) {\n name = `Schema${++schemaCounter}`\n }\n // Sanitize name for OpenAPI (remove spaces, special chars)\n name = name.replace(/[^a-zA-Z0-9]/g, '')\n\n // Avoid duplicates — if already registered with same name, reuse\n if (!componentSchemas[name]) {\n const clean = { ...jsonSchema }\n delete clean.title\n delete clean.label\n delete clean.$schema\n componentSchemas[name] = clean\n }\n return { $ref: `#/components/schemas/${name}` }\n }\n\n const spec: any = {\n openapi: '3.0.3',\n info: {\n title: options.info?.title || 'API',\n version: options.info?.version || '1.0.0',\n ...(options.info?.description ? { description: options.info.description } : {}),\n },\n paths: {},\n components: { schemas: {}, securitySchemes: {} },\n tags: [],\n }\n\n if (options.servers) {\n spec.servers = options.servers\n }\n\n const allTags = new Set<string>()\n const securitySchemes: Record<string, any> = {}\n\n for (const { controllerClass, mountPath } of registeredRoutes) {\n // Skip excluded controllers\n if (Reflect.getMetadata(SWAGGER_KEYS.EXCLUDE, controllerClass)) continue\n\n const routes: RouteDefinition[] = Reflect.getMetadata(METADATA.ROUTES, controllerClass) || []\n const classTags: string[] = Reflect.getMetadata(SWAGGER_KEYS.TAGS, controllerClass) || []\n const classAuth: string | undefined = Reflect.getMetadata(\n SWAGGER_KEYS.BEARER_AUTH,\n controllerClass,\n )\n const controllerPath = Reflect.getMetadata(METADATA.CONTROLLER_PATH, controllerClass) || '/'\n\n for (const route of routes) {\n // Skip excluded methods\n if (Reflect.getMetadata(SWAGGER_KEYS.EXCLUDE, controllerClass, route.handlerName)) continue\n\n // Build the full path\n let routePath = route.path === '/' ? '' : route.path\n let fullPath = mountPath + (controllerPath === '/' ? '' : controllerPath) + routePath\n if (!fullPath) fullPath = '/'\n\n // Convert Express :param to OpenAPI {param}\n const openApiPath = fullPath.replace(/:([a-zA-Z_]+)/g, '{$1}')\n const method = route.method.toLowerCase()\n\n // Gather metadata\n const operation: ApiOperationOptions =\n Reflect.getMetadata(SWAGGER_KEYS.OPERATION, controllerClass, route.handlerName) || {}\n const responses: ApiResponseOptions[] =\n Reflect.getMetadata(SWAGGER_KEYS.RESPONSES, controllerClass, route.handlerName) || []\n const methodTags: string[] =\n Reflect.getMetadata(SWAGGER_KEYS.TAGS, controllerClass, route.handlerName) || []\n const methodAuth: string | undefined = Reflect.getMetadata(\n SWAGGER_KEYS.BEARER_AUTH,\n controllerClass,\n route.handlerName,\n )\n\n // Tags — method level overrides class level\n const tags = methodTags.length > 0 ? methodTags : classTags\n tags.forEach((t) => allTags.add(t))\n\n // Build operation object\n const op: any = {\n ...(tags.length > 0 ? { tags } : {}),\n ...(operation.summary ? { summary: operation.summary } : {}),\n ...(operation.description ? { description: operation.description } : {}),\n ...(operation.operationId ? { operationId: operation.operationId } : {}),\n ...(operation.deprecated ? { deprecated: true } : {}),\n parameters: [],\n responses: {},\n }\n\n // Path parameters\n const paramMatches = fullPath.match(/:([a-zA-Z_]+)/g) || []\n for (const match of paramMatches) {\n const paramName = match.slice(1)\n let schema: any = { type: 'string' }\n\n // Try to get type from params validation schema\n if (route.validation?.params) {\n const jsonSchema = toJsonSchema(route.validation.params)\n if (jsonSchema?.properties && typeof jsonSchema.properties === 'object') {\n const props = jsonSchema.properties as Record<string, any>\n if (props[paramName]) {\n schema = props[paramName]\n }\n }\n }\n\n op.parameters.push({\n name: paramName,\n in: 'path',\n required: true,\n schema,\n })\n }\n\n // Query parameters\n if (route.validation?.query) {\n const jsonSchema = toJsonSchema(route.validation.query)\n if (jsonSchema?.properties && typeof jsonSchema.properties === 'object') {\n const required = Array.isArray(jsonSchema.required) ? jsonSchema.required : []\n for (const [name, propSchema] of Object.entries(\n jsonSchema.properties as Record<string, any>,\n )) {\n op.parameters.push({\n name,\n in: 'query',\n required: required.includes(name),\n schema: propSchema,\n })\n }\n }\n }\n\n // @ApiQueryParams decorator — document filterable/sortable/searchable fields\n const queryParamsConfig = Reflect.getMetadata(\n METADATA.QUERY_PARAMS,\n controllerClass,\n route.handlerName,\n )\n if (queryParamsConfig) {\n if (queryParamsConfig.filterable?.length) {\n op.parameters.push({\n name: 'filter',\n in: 'query',\n required: false,\n description: `Filter fields: ${queryParamsConfig.filterable.join(', ')}. Format: \\`field:operator:value\\`. Operators: eq, neq, gt, gte, lt, lte, contains, starts, ends, in, between`,\n schema: { type: 'array', items: { type: 'string' } },\n style: 'form',\n explode: true,\n })\n }\n if (queryParamsConfig.sortable?.length) {\n op.parameters.push({\n name: 'sort',\n in: 'query',\n required: false,\n description: `Sort fields: ${queryParamsConfig.sortable.join(', ')}. Format: \\`field:asc\\` or \\`field:desc\\``,\n schema: { type: 'array', items: { type: 'string' } },\n style: 'form',\n explode: true,\n })\n }\n if (queryParamsConfig.searchable?.length) {\n op.parameters.push({\n name: 'q',\n in: 'query',\n required: false,\n description: `Search across: ${queryParamsConfig.searchable.join(', ')}`,\n schema: { type: 'string' },\n })\n }\n op.parameters.push(\n {\n name: 'page',\n in: 'query',\n required: false,\n description: 'Page number (default: 1)',\n schema: { type: 'integer', minimum: 1, default: 1 },\n },\n {\n name: 'limit',\n in: 'query',\n required: false,\n description: 'Items per page (default: 20, max: 100)',\n schema: { type: 'integer', minimum: 1, maximum: 100, default: 20 },\n },\n )\n }\n\n // Remove empty parameters array\n if (op.parameters.length === 0) delete op.parameters\n\n // Request body\n if (route.validation?.body && ['post', 'put', 'patch'].includes(method)) {\n const bodySchema = toJsonSchema(route.validation.body)\n if (bodySchema) {\n const bodyName = route.validation.name || `${route.handlerName}Body`\n const ref = registerSchema(bodySchema, bodyName)\n op.requestBody = {\n required: true,\n content: { 'application/json': { schema: ref } },\n }\n }\n }\n\n // File upload detection\n const fileUpload = Reflect.getMetadata(\n METADATA.FILE_UPLOAD,\n controllerClass,\n route.handlerName,\n )\n if (fileUpload) {\n const fieldName = fileUpload.fieldName ?? 'file'\n const properties: any = {}\n\n if (fileUpload.mode === 'array') {\n properties[fieldName] = {\n type: 'array',\n items: { type: 'string', format: 'binary' },\n }\n } else if (fileUpload.mode !== 'none') {\n properties[fieldName] = {\n type: 'string',\n format: 'binary',\n }\n }\n\n op.requestBody = {\n required: true,\n content: {\n 'multipart/form-data': {\n schema: { type: 'object', properties },\n },\n },\n }\n }\n\n // Responses\n if (responses.length > 0) {\n for (const resp of responses) {\n op.responses[String(resp.status)] = {\n description: resp.description || '',\n ...(resp.schema\n ? (() => {\n const converted =\n typeof resp.schema === 'function' || typeof resp.schema === 'object'\n ? toJsonSchema(resp.schema)\n : null\n const schemaName = resp.name || `${route.handlerName}Response${resp.status}`\n const finalSchema = converted\n ? registerSchema(converted, schemaName)\n : typeof resp.schema === 'object'\n ? resp.schema\n : undefined\n return finalSchema\n ? { content: { 'application/json': { schema: finalSchema } } }\n : {}\n })()\n : {}),\n }\n }\n } else {\n // Auto-generate default responses\n const defaultStatus = method === 'post' ? '201' : method === 'delete' ? '204' : '200'\n op.responses[defaultStatus] = { description: 'Successful operation' }\n\n if (route.validation?.body) {\n op.responses['422'] = { description: 'Validation error' }\n }\n }\n\n // Security\n const authName = methodAuth || classAuth\n if (authName) {\n op.security = [{ [authName]: [] }]\n securitySchemes[authName] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n }\n }\n\n // Mount\n if (!spec.paths[openApiPath]) spec.paths[openApiPath] = {}\n spec.paths[openApiPath][method] = op\n }\n }\n\n // Finalize\n spec.tags = Array.from(allTags).map((name) => ({ name }))\n spec.components.securitySchemes = securitySchemes\n\n if (options.bearerAuth) {\n if (!securitySchemes.BearerAuth) {\n spec.components.securitySchemes.BearerAuth = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n }\n }\n spec.security = [{ BearerAuth: [] }]\n }\n\n // Merge collected schemas into components\n spec.components.schemas = componentSchemas\n\n // Clean up empty components\n if (Object.keys(spec.components.schemas).length === 0) delete spec.components.schemas\n if (Object.keys(spec.components.securitySchemes).length === 0)\n delete spec.components.securitySchemes\n if (Object.keys(spec.components).length === 0) delete spec.components\n\n return spec\n}\n","import { Router } from 'express'\nimport type { Express } from 'express'\nimport { Logger, type AppAdapter, type Container } from '@forinda/kickjs-core'\nimport {\n buildOpenAPISpec,\n registerControllerForDocs,\n clearRegisteredRoutes,\n type SwaggerOptions,\n} from './openapi-builder'\nimport { swaggerUIHtml, redocHtml } from './ui'\n\nconst log = Logger.for('SwaggerAdapter')\n\nexport interface SwaggerAdapterOptions extends SwaggerOptions {\n /** Path to serve Swagger UI (default: '/docs') */\n docsPath?: string\n /** Path to serve ReDoc (default: '/redoc') */\n redocPath?: string\n /** Path to serve the raw JSON spec (default: '/openapi.json') */\n specPath?: string\n /** Other adapters to discover (e.g., WsAdapter for WebSocket server URLs) */\n adapters?: any[]\n}\n\n/**\n * Swagger adapter — auto-generates OpenAPI spec from decorators and serves docs.\n *\n * @example\n * ```ts\n * bootstrap({\n * modules,\n * adapters: [\n * new SwaggerAdapter({\n * info: { title: 'My API', version: '1.0.0' },\n * }),\n * ],\n * })\n * ```\n *\n * Endpoints:\n * GET /docs — Swagger UI\n * GET /redoc — ReDoc\n * GET /openapi.json — Raw OpenAPI 3.0.3 spec\n */\nexport class SwaggerAdapter implements AppAdapter {\n name = 'SwaggerAdapter'\n\n constructor(private options: SwaggerAdapterOptions = {}) {}\n\n /** Auto-detect server URLs from the running HTTP server and peer adapters */\n afterStart(server: any, _container: Container): void {\n const addr = server?.address?.()\n if (!addr || typeof addr !== 'object') return\n\n const host = addr.address === '::' || addr.address === '0.0.0.0' ? 'localhost' : addr.address\n\n // Auto-add HTTP server URL if none configured\n if (!this.options.servers || this.options.servers.length === 0) {\n this.options.servers = [{ url: `http://${host}:${addr.port}`, description: 'HTTP server' }]\n }\n\n // Auto-add WebSocket server URLs from WsAdapter\n const wsAdapter = this.options.adapters?.find(\n (a) => a.name === 'WsAdapter' && typeof a.getStats === 'function',\n )\n if (wsAdapter) {\n const stats = wsAdapter.getStats()\n for (const namespace of Object.keys(stats.namespaces || {})) {\n this.options.servers!.push({\n url: `ws://${host}:${addr.port}${namespace}`,\n description: `WebSocket: ${namespace}`,\n })\n }\n }\n }\n\n /** Collect controller metadata as routes are mounted */\n onRouteMount(controllerClass: any, mountPath: string): void {\n registerControllerForDocs(controllerClass, mountPath)\n }\n\n beforeMount(app: Express, _container: Container): void {\n // Clear previous registrations (supports HMR rebuild)\n clearRegisteredRoutes()\n const docsPath = this.options.docsPath ?? '/docs'\n const redocPath = this.options.redocPath ?? '/redoc'\n const specPath = this.options.specPath ?? '/openapi.json'\n\n // Use a sub-router with relaxed CSP so CDN scripts load\n const docsRouter = Router()\n\n docsRouter.use((_req, res, next) => {\n res.setHeader(\n 'Content-Security-Policy',\n [\n \"default-src 'self'\",\n \"script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.redoc.ly https://cdn.jsdelivr.net\",\n \"style-src 'self' 'unsafe-inline' https://unpkg.com https://fonts.googleapis.com\",\n \"font-src 'self' https://fonts.gstatic.com\",\n \"img-src 'self' data: https://unpkg.com\",\n \"connect-src 'self'\",\n ].join('; '),\n )\n next()\n })\n\n // Spec endpoint (JSON)\n docsRouter.get(specPath, (_req, res) => {\n const spec = buildOpenAPISpec(this.options)\n res.json(spec)\n })\n\n // Swagger UI\n docsRouter.get(docsPath, (_req, res) => {\n res.type('html').send(swaggerUIHtml(specPath, this.options.info?.title))\n })\n\n // ReDoc\n docsRouter.get(redocPath, (_req, res) => {\n res.type('html').send(redocHtml(specPath, this.options.info?.title))\n })\n\n app.use(docsRouter)\n\n log.info(`Swagger UI: ${docsPath}`)\n log.info(`ReDoc: ${redocPath}`)\n log.info(`OpenAPI spec: ${specPath}`)\n }\n}\n\n// Re-export for use by Application when mounting module routes\nexport { registerControllerForDocs, clearRegisteredRoutes }\n","/** Escape a string for safe HTML attribute/content interpolation */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\n/** Generate Swagger UI HTML that loads from CDN */\nexport function swaggerUIHtml(specUrl: string, title = 'API Docs'): string {\n const safeTitle = escapeHtml(title)\n const safeUrl = JSON.stringify(specUrl).replace(/</g, '\\\\u003c')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${safeTitle}</title>\n <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui.css\">\n</head>\n<body>\n <div id=\"swagger-ui\"></div>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js\"></script>\n <script src=\"https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js\"></script>\n <script>\n SwaggerUIBundle({\n url: ${safeUrl},\n dom_id: '#swagger-ui',\n deepLinking: true,\n presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],\n plugins: [SwaggerUIBundle.plugins.DownloadUrl],\n layout: 'StandaloneLayout',\n });\n </script>\n</body>\n</html>`\n}\n\n/** Generate ReDoc HTML that loads from CDN */\nexport function redocHtml(specUrl: string, title = 'API Docs'): string {\n const safeTitle = escapeHtml(title)\n const safeUrl = escapeHtml(specUrl)\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${safeTitle}</title>\n</head>\n<body>\n <redoc spec-url=\"${safeUrl}\"></redoc>\n <script src=\"https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js\"></script>\n</body>\n</html>`\n}\n"],"mappings":"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","names":["zodSchemaParser","name","supports","schema","safeParse","toJSONSchema","toJsonSchema","$schema","_","rest","SWAGGER_KEYS","OPERATION","Symbol","RESPONSES","TAGS","BEARER_AUTH","EXCLUDE","ApiOperation","options","target","propertyKey","Reflect","defineMetadata","SWAGGER_KEYS","OPERATION","ApiResponse","existing","getMetadata","RESPONSES","ApiTags","tags","TAGS","ApiBearerAuth","name","BEARER_AUTH","ApiExclude","EXCLUDE","METADATA","registeredRoutes","registerControllerForDocs","controllerClass","mountPath","push","clearRegisteredRoutes","length","buildOpenAPISpec","options","parser","schemaParser","zodSchemaParser","toJsonSchema","schema","supports","componentSchemas","schemaCounter","registerSchema","jsonSchema","hint","name","title","label","replace","clean","$schema","$ref","spec","openapi","info","version","description","paths","components","schemas","securitySchemes","tags","servers","allTags","Set","Reflect","getMetadata","SWAGGER_KEYS","EXCLUDE","routes","METADATA","ROUTES","classTags","TAGS","classAuth","BEARER_AUTH","controllerPath","CONTROLLER_PATH","route","handlerName","routePath","path","fullPath","openApiPath","method","toLowerCase","operation","OPERATION","responses","RESPONSES","methodTags","methodAuth","forEach","t","add","op","summary","operationId","deprecated","parameters","paramMatches","match","paramName","slice","type","validation","params","properties","props","in","required","query","Array","isArray","propSchema","Object","entries","includes","queryParamsConfig","QUERY_PARAMS","filterable","join","items","style","explode","sortable","searchable","minimum","default","maximum","body","bodySchema","bodyName","ref","requestBody","content","fileUpload","FILE_UPLOAD","fieldName","mode","format","resp","String","status","converted","schemaName","finalSchema","undefined","defaultStatus","authName","security","scheme","bearerFormat","from","map","bearerAuth","BearerAuth","keys","Router","Logger","escapeHtml","str","replace","swaggerUIHtml","specUrl","title","safeTitle","safeUrl","JSON","stringify","redocHtml","log","Logger","for","SwaggerAdapter","name","options","afterStart","server","_container","addr","address","host","servers","length","url","port","description","wsAdapter","adapters","find","a","getStats","stats","namespace","Object","keys","namespaces","push","onRouteMount","controllerClass","mountPath","registerControllerForDocs","beforeMount","app","clearRegisteredRoutes","docsPath","redocPath","specPath","docsRouter","Router","use","_req","res","next","setHeader","join","get","spec","buildOpenAPISpec","json","type","send","swaggerUIHtml","info","title","redocHtml"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forinda/kickjs-swagger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "OpenAPI spec generation from decorators, Swagger UI and ReDoc serving for KickJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kickjs",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"reflect-metadata": "^0.2.2",
|
|
43
|
-
"@forinda/kickjs-core": "0.
|
|
43
|
+
"@forinda/kickjs-core": "0.6.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"express": "^5.1.0",
|