import type { ParsedOperation, ParsedWebhook, SchemaField, } from "./parseOpenApi.js"; /** * Generate the MDX content for request body and response fields. */ export function generateFields({ operation, }: { operation: ParsedOperation; }): string { const sections: string[] = []; // Generate request body parameters if (operation.requestBody && operation.requestBody.length > 0) { sections.push("### Body Parameters\n"); sections.push( generateParamFields({ fields: operation.requestBody, indent: 0 }), ); } // Generate response fields (use 200 or 201 response) const responseStatusCode = operation.responses?.["200"] ? "200" : operation.responses?.["201"] ? "201" : null; const responseFields = responseStatusCode ? operation.responses?.[responseStatusCode] : null; if (responseFields && responseFields.length > 0) { sections.push("\n### Response\n"); sections.push( generateResponseFields({ fields: responseFields, indent: 0 }), ); } // Generate DynamicResponseExample with the actual example from OpenAPI spec // This example is already in snake_case - the component will convert to camelCase when needed const responseExample = responseStatusCode ? operation.responseExamples?.[responseStatusCode] : null; if (responseExample && typeof responseExample === "object") { sections.push( generateResponseExampleMarkdown({ json: responseExample, statusCode: responseStatusCode!, }), ); } return sections.join("\n"); } /** * Generate a ResponseExample with markdown code block. * Uses Mintlify's ResponseExample component which pins content to the sidebar. */ function generateResponseExampleMarkdown({ json, statusCode, }: { json: unknown; statusCode: string; }): string { // Format the JSON with proper indentation const jsonString = JSON.stringify(json, null, 2); // Generate markdown ResponseExample block // The triple backticks create a code block inside ResponseExample return ` \`\`\`json ${statusCode} ${jsonString} \`\`\` `; } /** * Generate DynamicParamField components for request body fields. */ function generateParamFields({ fields, indent, }: { fields: SchemaField[]; indent: number; }): string { const indentStr = " ".repeat(indent); const lines: string[] = []; for (const field of fields) { const props = buildFieldProps({ name: field.name, type: field.type, required: field.required, enumValues: field.enumValues, }); const description = escapeDescription(field.description); const hasChildren = field.children && field.children.length > 0; if (hasChildren) { // Field with nested children lines.push(`${indentStr}`); if (description) { lines.push(`${indentStr} ${description}`); } lines.push(`${indentStr} `); lines.push( generateParamFields({ fields: field.children!, indent: indent + 2 }), ); lines.push(`${indentStr} `); lines.push(`${indentStr}\n`); } else { // Simple field if (description) { lines.push(`${indentStr}`); lines.push(`${indentStr} ${description}`); lines.push(`${indentStr}\n`); } else { lines.push(`${indentStr}\n`); } } } return lines.join("\n"); } /** * Generate DynamicResponseField components for response fields. */ function generateResponseFields({ fields, indent, }: { fields: SchemaField[]; indent: number; }): string { const indentStr = " ".repeat(indent); const lines: string[] = []; for (const field of fields) { const props = buildResponseFieldProps({ name: field.name, type: field.type, enumValues: field.enumValues, }); const description = escapeDescription(field.description); const hasChildren = field.children && field.children.length > 0; if (hasChildren) { // Field with nested children lines.push(`${indentStr}`); if (description) { lines.push(`${indentStr} ${description}`); } lines.push(`${indentStr} `); lines.push( generateResponseFields({ fields: field.children!, indent: indent + 2 }), ); lines.push(`${indentStr} `); lines.push(`${indentStr}\n`); } else { // Simple field if (description) { lines.push(`${indentStr}`); lines.push(`${indentStr} ${description}`); lines.push(`${indentStr}\n`); } else { lines.push(`${indentStr}\n`); } } } return lines.join("\n"); } /** * Build the props string for a DynamicParamField component. */ function buildFieldProps({ name, type, required, enumValues, }: { name: string; type: string; required: boolean; enumValues?: string[]; }): string { const props: string[] = [ `body="${name}"`, `type="${formatType(type, enumValues)}"`, ]; if (required) { props.push("required"); } return props.join(" "); } /** * Build the props string for a DynamicResponseField component. */ function buildResponseFieldProps({ name, type, enumValues, }: { name: string; type: string; enumValues?: string[]; }): string { return `name="${name}" type="${formatType(type, enumValues)}"`; } /** * Format the type string, including enum values if present. * Shows all enum values inline for better documentation clarity. */ function formatType(type: string, enumValues?: string[]): string { if (enumValues && enumValues.length > 0) { const isArray = type.endsWith("[]"); const values = enumValues.map((v) => `'${v}'`).join(" | "); return isArray ? `(${values})[]` : values; } return type; } /** * Generate MDX content for a webhook event. * Uses plain `ParamField` with snake_case names since webhook payloads * are raw JSON (not SDK-transformed). */ export function generateWebhookFields({ webhook, }: { webhook: ParsedWebhook; }): string { const sections: string[] = []; if (webhook.dataFields && webhook.dataFields.length > 0) { sections.push("### Payload Fields\n"); sections.push( generateStaticParamFields({ fields: webhook.dataFields, indent: 0 }), ); } return sections.join("\n"); } /** * Generate ParamField components with static snake_case names. */ function generateStaticParamFields({ fields, indent, }: { fields: SchemaField[]; indent: number; }): string { const indentStr = " ".repeat(indent); const lines: string[] = []; for (const field of fields) { const typeStr = formatType(field.type, field.enumValues); const requiredAttr = field.required ? " required" : ""; const description = escapeDescription(field.description); const hasChildren = field.children && field.children.length > 0; if (hasChildren) { lines.push( `${indentStr}`, ); if (description) { lines.push(`${indentStr} ${description}`); } lines.push(`${indentStr} `); lines.push( generateStaticParamFields({ fields: field.children!, indent: indent + 2, }), ); lines.push(`${indentStr} `); lines.push(`${indentStr}\n`); } else if (description) { lines.push( `${indentStr}`, ); lines.push(`${indentStr} ${description}`); lines.push(`${indentStr}\n`); } else { lines.push( `${indentStr}\n`, ); } } return lines.join("\n"); } /** * Escape special characters in description for MDX. */ function escapeDescription(description?: string): string { if (!description) return ""; return ( description // Escape curly braces for JSX .replace(/\{/g, "\\{") .replace(/\}/g, "\\}") // Remove markdown code blocks that might cause issues .replace(/```[\s\S]*?```/g, "") // Normalize whitespace .replace(/\s+/g, " ") .trim() ); }