UNPKG

@alova/wormhole

Version:

More modern openAPI generating solution for alova.js

161 lines (160 loc) 5.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.openApiHelper = exports.OpenApiHelper = exports.supportedApiMethods = void 0; const type_1 = require("../../type"); const utils_1 = require("../../utils"); /** * 支持的API方法列表 * @see https://github.com/alovajs/alova/blob/main/packages/alova/typings/index.d.ts#L640 */ exports.supportedApiMethods = [ type_1.HttpMethod.GET, type_1.HttpMethod.PUT, type_1.HttpMethod.POST, type_1.HttpMethod.DELETE, type_1.HttpMethod.PATCH, type_1.HttpMethod.HEAD, type_1.HttpMethod.OPTIONS, ]; class OpenApiHelper { constructor() { this.usedRefsCache = new Set(); } load(document) { this.document = document; // reset cache when document changes this.usedRefsCache.clear(); return this; } static load(document) { const ins = new OpenApiHelper(); return ins.load(document); } getApiMethods() { const paths = this.document.paths || []; const apiMethods = []; for (const [url, pathInfo] of Object.entries(paths)) { if (!pathInfo) { continue; } for (const [method, operationObject] of Object.entries(pathInfo)) { if (!exports.supportedApiMethods.includes(method)) { continue; } if (typeof operationObject === 'string' || Array.isArray(operationObject)) { continue; } apiMethods.push({ url, method, operationObject, }); } } return apiMethods; } /** * 将 ApiMethods 数组写回到 openapi document 的 paths 中。 * 仅更新传入的 url+method 对应的 operationObject,保留其它未涉及的内容。 */ saveApiMethods(apiMethods) { this.document.paths = {}; // 构建 url -> { method -> operationObject } 映射,后写入覆盖前者 const grouped = {}; for (const item of apiMethods || []) { if (!item || !item.url || !item.method || !item.operationObject) { continue; } const method = String(item.method).toLowerCase(); if (!exports.supportedApiMethods.includes(method)) { // 跳过不支持的 http 方法 continue; } if (!grouped[item.url]) { grouped[item.url] = {}; } grouped[item.url][method] = item.operationObject; } // 将分组后的方法写回到 document.paths for (const [url, methodsMap] of Object.entries(grouped)) { const pathInfo = (this.document.paths?.[url] || {}); for (const [method, operationObject] of Object.entries(methodsMap)) { pathInfo[method] = operationObject; } if (this.document.paths) { this.document.paths[url] = pathInfo; } } // 更新后清除引用缓存,确保后续计算的使用引用为最新 this.usedRefsCache.clear(); return this; } /** * 判断某个引用是否在文档中被使用 * @param ref ReferenceObject 或其 `$ref` 路径字符串 */ isReferenceUsed(ref) { const refPath = typeof ref === 'string' ? ref : ref.$ref; const usedSet = this.getUsedReferenceSet(); return usedSet.has(refPath); } /** * 预计算并返回已使用的 `$ref` 集合,仅从 paths 可达 */ getUsedReferenceSet() { if (this.usedRefsCache.size > 0) { return this.usedRefsCache; } const used = this.usedRefsCache; const visitedRefs = new Set(); const stack = []; const roots = []; if (this.document.paths) { roots.push(this.document.paths); } stack.push(...roots); while (stack.length) { const node = stack.pop(); if (!node || typeof node !== 'object') { continue; } if ((0, utils_1.isReferenceObject)(node)) { const currentRef = node.$ref; used.add(currentRef); if (!visitedRefs.has(currentRef)) { visitedRefs.add(currentRef); try { const target = (0, utils_1.findBy$ref)(currentRef, this.document); stack.push(target); } catch { // ignore invalid refs } } continue; } if (Array.isArray(node)) { for (const item of node) { stack.push(item); } continue; } for (const key of Object.keys(node)) { stack.push(node[key]); } } return used; } /** * 过滤掉未使用的引用,返回仍被使用的引用列表 */ filterUsedReferences(refs) { const usedSet = this.getUsedReferenceSet(); return (refs || []).filter((ref) => { const refPath = typeof ref === 'string' ? ref : ref.$ref; return usedSet.has(refPath); }); } } exports.OpenApiHelper = OpenApiHelper; exports.openApiHelper = new OpenApiHelper();