{"version":3,"file":"index.mjs","sources":["../src/constants.ts","../src/ui/utils.ts","../src/utils/object.ts","../src/utils/url.ts","../src/ui/handler.ts","../src/ui/module.ts","../src/generator/module.ts"],"sourcesContent":["import path from 'node:path';\n\nexport const ASSETS_PATH = path.resolve(__dirname, '..', 'assets');\n","import { URL, pathToFileURL } from 'node:url';\n\nexport function isFileURL(input: string) : boolean {\n    let url: URL;\n\n    try {\n        url = new URL(input);\n    } catch (e) {\n        url = pathToFileURL(input);\n    }\n\n    return url.protocol === 'file:';\n}\n","export function isObject(item: unknown) : item is Record<string, any> {\n    return (\n        !!item &&\n        typeof item === 'object' &&\n        !Array.isArray(item)\n    );\n}\n","const TRAILING_SLASH_RE = /\\/$|\\/\\?/;\n\nexport function hasTrailingSlash(input = '', queryParams = false): boolean {\n    if (!queryParams) {\n        return input.endsWith('/');\n    }\n\n    return TRAILING_SLASH_RE.test(input);\n}\n\nexport function withoutTrailingSlash(input = '', queryParams = false): string {\n    if (!queryParams) {\n        return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/';\n    }\n\n    if (!hasTrailingSlash(input, true)) {\n        return input || '/';\n    }\n\n    const [s0, ...s] = input.split('?');\n\n    return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '');\n}\n\nexport function withTrailingSlash(input = '', queryParams = false): string {\n    if (!queryParams) {\n        return input.endsWith('/') ? input : (`${input}/`);\n    }\n\n    if (hasTrailingSlash(input, true)) {\n        return input || '/';\n    }\n\n    const [s0, ...s] = input.split('?');\n    return `${s0}/${s.length ? `?${s.join('?')}` : ''}`;\n}\n\nexport function hasLeadingSlash(input = ''): boolean {\n    return input.startsWith('/');\n}\n\nexport function withoutLeadingSlash(input = ''): string {\n    return (hasLeadingSlash(input) ? input.substring(1) : input) || '/';\n}\n\nexport function withLeadingSlash(input = ''): string {\n    return hasLeadingSlash(input) ? input : `/${input}`;\n}\n\nexport function cleanDoubleSlashes(input = ''): string {\n    return input.split('://')\n        .map((str) => str.replace(/\\/{2,}/g, '/'))\n        .join('://');\n}\n","import {\n    coreHandler,\n    send,\n    useRequestMountPath, useRequestPath,\n} from 'routup';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { URL } from 'node:url';\nimport type { Spec } from 'swagger-ui-dist';\nimport { createHandlerFn } from '@routup/assets';\nimport { ASSETS_PATH } from '../constants';\nimport type { UIOptions } from './type';\nimport { isFileURL } from './utils';\nimport {\n    cleanDoubleSlashes,\n    isObject,\n    withLeadingSlash,\n    withTrailingSlash,\n    withoutLeadingSlash,\n} from '../utils';\n\n/* istanbul ignore next */\nconst stringify = (obj: Record<string, any>) => {\n    const placeholder = '____FUNCTION_PLACEHOLDER____';\n    const fns : CallableFunction[] = [];\n\n    let json = JSON.stringify(obj, (key, value) => {\n        if (typeof value === 'function') {\n            fns.push(value);\n            return placeholder;\n        }\n\n        return value;\n    }, 2);\n\n    json = json.replace(new RegExp(`\"${placeholder}\"`, 'g'), (_) => fns.shift() as any);\n\n    return `var options = ${json};`;\n};\n\n/**\n * Create a swagger handler by swagger document or web url.\n *\n * @param document\n * @param options\n */\nexport function createUIHandler(\n    document: Spec | string,\n    options: UIOptions = {},\n) {\n    const handler = createHandlerFn(path.dirname(require.resolve('swagger-ui-dist')), {\n        extensions: [],\n    });\n\n    if (isObject(document)) {\n        options.spec = document;\n    } else if (isFileURL(document)) {\n        const documentFile = fs.readFileSync(document, { encoding: 'utf-8' });\n        options.spec = JSON.parse(documentFile);\n    } else {\n        options.url = document;\n    }\n\n    let template : string | undefined;\n    const templateRaw = fs.readFileSync(path.join(ASSETS_PATH, 'template.tpl'), {\n        encoding: 'utf-8',\n    });\n\n    const compileTemplate = (context: {url?: string, mountPath: string, path: string }) : void => {\n        let href = '/';\n        if (context.url) {\n            let pathName : string;\n            if (context.url.startsWith('http')) {\n                pathName = new URL(context.url).pathname;\n            } else {\n                pathName = context.url;\n            }\n\n            const mountPathIndex = pathName.indexOf(context.path);\n            if (mountPathIndex !== -1) {\n                href = pathName.substring(0, mountPathIndex + context.path.length);\n            } else {\n                href = pathName;\n            }\n\n            if (options.baseURL) {\n                href = new URL(withoutLeadingSlash(href), options.baseURL).href;\n            } else if (options.basePath) {\n                href = withLeadingSlash(cleanDoubleSlashes(`${options.basePath}/${href}`));\n            }\n\n            href = withTrailingSlash(href);\n        } else if (options.baseURL) {\n            href = withTrailingSlash(options.baseURL);\n        } else if (options.basePath) {\n            href = withTrailingSlash(withLeadingSlash(options.basePath));\n        }\n\n        template = templateRaw\n            .replace('<% title %>', 'Swagger UI')\n            .replace('<% swaggerOptions %>', stringify(options))\n            .replace('<% baseHref %>', href);\n    };\n\n    return coreHandler((req, res, next) => {\n        /* istanbul ignore next */\n        if (typeof req.url === 'undefined') {\n            return next();\n        }\n\n        if (req.url.includes('/package.json')) {\n            res.statusCode = 404;\n\n            return send(res);\n        }\n\n        return handler(req, res, async () => {\n            if (typeof template === 'undefined') {\n                compileTemplate({\n                    url: req.url,\n                    mountPath: useRequestMountPath(req),\n                    path: useRequestPath(req),\n                });\n            }\n\n            return send(res, template);\n        });\n    });\n}\n","import type { Plugin } from 'routup';\nimport type { Spec } from 'swagger-ui-dist';\nimport { createUIHandler } from './handler';\nimport type { UIOptions } from './type';\n\nexport function swaggerUI(\n    document: Spec | string,\n    options: UIOptions = {},\n) : Plugin {\n    return {\n        name: 'swaggerUI',\n        install: (router) => {\n            router.use(createUIHandler(document, options));\n        },\n    };\n}\n","import { Version, generate as _generate, isMetadata } from '@trapi/swagger';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { createMerger } from 'smob';\nimport type { GeneratorContext, GeneratorOutput } from './type';\n\nexport async function generate<V extends `${Version}`>(\n    context: GeneratorContext<V>,\n): Promise<GeneratorOutput<V>> {\n    if (context.options.metadata) {\n        if (!isMetadata(context.options.metadata)) {\n            if (!context.options.metadata.entryPoint) {\n                context.options.metadata.entryPoint = [\n                    { pattern: '**/*.ts', cwd: path.join(process.cwd(), 'src') },\n                ];\n            }\n\n            if (!context.options.metadata.preset) {\n                context.options.metadata.preset = '@routup/swagger-preset';\n            }\n        }\n    } else {\n        context.options.metadata = {\n            ignore: ['**/node_modules/**'],\n            preset: '@routup/swagger-preset',\n            entryPoint: [\n                { pattern: '**/*.ts', cwd: path.join(process.cwd(), 'src') },\n            ],\n        };\n    }\n\n    const merge = createMerger({\n        array: true,\n        arrayDistinct: true,\n    });\n\n    context.options = merge({\n        name: 'API Documentation',\n        description: 'Explore the REST Endpoints of the API.',\n        consumes: ['application/json'],\n        produces: ['application/json'],\n    }, context.options || {});\n\n    return await _generate({\n        version: context.version || Version.V3,\n        options: context.options,\n        tsConfig: context.tsconfig,\n    }) as GeneratorOutput<V>;\n}\n"],"names":["url","fns","json","extensions","options","pathName","href","template","res","mountPath","path","install","context","cwd","version","tsConfig"],"mappings":";;;;;;;;;;;;;;;;;;AAEO;;ACAA;;;AAICA;AACJ;AACIA;AACJ;;AAGJ;;ACZO;;AAMP;;ACNA;AAEO;AACH;;AAEA;;AAGJ;AAgBO;AACH;;AAEA;;AAGI;AACJ;AAEA;AACA;AACJ;AAEO;;AAEP;AAEO;;AAEP;AAEO;AACH;AACJ;AAEO;AACH;AAGJ;;AChCA;AAEI;AACA;AAEA;;AAEQC;;AAEJ;;;AAKJC;AAEA;AACJ;AAEA;;;;;AAKC;;AAMOC;AACJ;AAEA;AACIC;;AAEA;;AAAmE;AACnEA;;AAEAA;AACJ;;;;AAKA;AAEA;AACI;;;AAGI;AACIC;;AAEAA;AACJ;AAEA;;;;;AAKA;;AAGIC;;;AAGJ;AAEAA;;;;;AAKJ;AAEAC;AAIJ;;AAGI;;AAGA;AAEA;AACIC;AAEA;AACJ;;;;AAKYR;AACAS;AACAC;AACJ;AACJ;AAEA;AACJ;AACJ;AACJ;;AC3HO;;;AAMCC;;AAEA;AACJ;AACJ;;ACTO;AAGH;AACI;AACI;AACIC;AACI;;AAAsBC;AAAqC;AAC9D;AACL;AAEA;AACID;AACJ;AACJ;;;;AAGa;AAAqB;;;AAG1B;;AAAsBC;AAAqC;AAC9D;AACL;AACJ;AAEA;;;AAGA;;;;;AAKe;AAAmB;;AACnB;AAAmB;;AAGlC;AACIC;AACAV;AACAW;AACJ;AACJ;;"}