import { applyOverlays, createOverlayRegistry, getOpenApiDocumentFromUrl, isAllowedLocalSchemaUrl, resolveOverlaysForSpec, validate, type OverlayRegistry, } from '@mintlify/common'; import { discoverOverlayFiles, getMintIgnore, resolveFileRefs } from '@mintlify/prebuild'; import { addLog, ErrorLog, SuccessLog, WarningLog } from '@mintlify/previewing'; import { collectOpenApiOverlayConfig, extractOpenApiEntries, type DocsConfig, } from '@mintlify/validation'; import { promises as fs } from 'fs'; import path from 'path'; import { CMD_EXEC_PATH, readLocalOpenApiFile } from './helpers.js'; export const getOpenApiFilenamesFromDocsConfig = (config: Pick): string[] => { return extractOpenApiEntries(config.api?.openapi).map((entry) => entry.source); }; /** * Builds the overlay registry so checks validate specs with overlays applied. * Throws when docs.json configures conflicting overlay lists, matching the build. */ export const buildOverlayRegistryForCheck = async ( localSchema?: boolean ): Promise => { // Discovery failures propagate and fail the check: silently skipping overlays would // validate a different document than the one the build produces. const mintIgnore = await getMintIgnore(CMD_EXEC_PATH); const discovered = await discoverOverlayFiles(CMD_EXEC_PATH, mintIgnore); let rawConfig: unknown; try { rawConfig = JSON.parse(await fs.readFile(path.join(CMD_EXEC_PATH, 'docs.json'), 'utf-8')); } catch { rawConfig = undefined; } if (rawConfig !== undefined) { try { rawConfig = (await resolveFileRefs(rawConfig, CMD_EXEC_PATH)).resolved; } catch { // Broken $refs are reported by regular config validation; collect from the root file. } } const explicit = rawConfig == undefined ? undefined : collectOpenApiOverlayConfig(rawConfig); if (explicit == undefined && discovered.length === 0) return undefined; return createOverlayRegistry({ explicit, discovered, localSchema, loadLocalDocument: (normalizedPath) => readLocalOpenApiFile(normalizedPath), }); }; export const checkOpenApiFile = async ( filename: string, localSchema: boolean, overlayRegistry?: OverlayRegistry ): Promise => { try { const overlays = overlayRegistry ? await resolveOverlaysForSpec(overlayRegistry, filename) : []; if (isAllowedLocalSchemaUrl(filename, localSchema)) { await getOpenApiDocumentFromUrl(filename, { overlays }); addLog(); return true; } if (filename.startsWith('http://') && !localSchema) { addLog( ); addLog(); return true; } const document = await readLocalOpenApiFile(filename); if (!document) { throw new Error( 'failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.' ); } const overlaid: unknown = overlays.length > 0 ? applyOverlays(document, overlays) : document; if (overlaid == undefined || typeof overlaid !== 'object') { throw new Error('applying overlays produced an invalid OpenAPI document.'); } await validate(overlaid); addLog(); return true; } catch (err) { if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') { addLog(); } else { addLog(); } return false; } };