mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-23 07:18:56 -05:00
278 lines
7.0 KiB
TypeScript
278 lines
7.0 KiB
TypeScript
import type { InferPageType } from "fumadocs-core/source";
|
|
import type { DocsVersion } from "./docs-versions";
|
|
import type { source } from "./source";
|
|
|
|
type PropertyDefinition = {
|
|
name: string;
|
|
type: string;
|
|
required: boolean;
|
|
description: string;
|
|
exampleValue: string;
|
|
isServerOnly: boolean;
|
|
isClientOnly: boolean;
|
|
};
|
|
|
|
function extractAPIMethods(rawContent: string): string {
|
|
const apiMethodRegex = /<APIMethod\s+([^>]+)>([\s\S]*?)<\/APIMethod>/g;
|
|
|
|
return rawContent.replace(apiMethodRegex, (match, attributes, content) => {
|
|
const pathMatch = attributes.match(/path="([^"]+)"/);
|
|
const methodMatch = attributes.match(/method="([^"]+)"/);
|
|
const requireSessionMatch = attributes.match(/requireSession/);
|
|
const noResultMatch = attributes.match(/noResult/);
|
|
const resultVariableMatch = attributes.match(/resultVariable="([^"]+)"/);
|
|
const forceAsBodyMatch = attributes.match(/forceAsBody/);
|
|
const forceAsQueryMatch = attributes.match(/forceAsQuery/);
|
|
|
|
const path = pathMatch ? pathMatch[1] : "";
|
|
const method = methodMatch ? methodMatch[1] : "GET";
|
|
const requireSession = !!requireSessionMatch;
|
|
const noResult = !!noResultMatch;
|
|
const resultVariable = resultVariableMatch
|
|
? resultVariableMatch[1]
|
|
: "data";
|
|
const forceAsBody = !!forceAsBodyMatch;
|
|
const forceAsQuery = !!forceAsQueryMatch;
|
|
|
|
const typeMatch = content.match(/type\s+(\w+)\s*=\s*\{([\s\S]*?)\}/);
|
|
if (!typeMatch) {
|
|
return match;
|
|
}
|
|
|
|
const functionName = typeMatch[1];
|
|
const typeBody = typeMatch[2];
|
|
|
|
const properties = parseTypeBody(typeBody);
|
|
|
|
const clientCode = generateClientCode(functionName, properties, path);
|
|
const serverCode = generateServerCode(
|
|
functionName,
|
|
properties,
|
|
method,
|
|
requireSession,
|
|
forceAsBody,
|
|
forceAsQuery,
|
|
noResult,
|
|
resultVariable,
|
|
);
|
|
|
|
return `
|
|
### Client Side
|
|
|
|
\`\`\`ts
|
|
${clientCode}
|
|
\`\`\`
|
|
|
|
### Server Side
|
|
|
|
\`\`\`ts
|
|
${serverCode}
|
|
\`\`\`
|
|
|
|
### Type Definition
|
|
|
|
\`\`\`ts
|
|
type ${functionName} = {${typeBody}
|
|
}
|
|
\`\`\`
|
|
`;
|
|
});
|
|
}
|
|
|
|
function parseTypeBody(typeBody: string): PropertyDefinition[] {
|
|
const properties: PropertyDefinition[] = [];
|
|
|
|
const lines = typeBody.split("\n");
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
|
|
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*"))
|
|
continue;
|
|
const propMatch = trimmed.match(
|
|
/^(\w+)(\?)?:\s*(.+?)(\s*=\s*["']([^"']+)["'])?(\s*\/\/\s*(.+))?$/,
|
|
);
|
|
if (propMatch) {
|
|
const [, name, optional, type, , exampleValue, , description] = propMatch;
|
|
|
|
let cleanType = type.trim();
|
|
const cleanExampleValue = exampleValue || "";
|
|
|
|
cleanType = cleanType.replace(/,$/, "");
|
|
|
|
properties.push({
|
|
name,
|
|
type: cleanType,
|
|
required: !optional,
|
|
description: description || "",
|
|
exampleValue: cleanExampleValue,
|
|
isServerOnly: false,
|
|
isClientOnly: false,
|
|
});
|
|
}
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
|
|
// Generate client code example
|
|
function generateClientCode(
|
|
functionName: string,
|
|
properties: PropertyDefinition[],
|
|
path: string,
|
|
): string {
|
|
if (!functionName || !path) {
|
|
return "// Unable to generate client code - missing function name or path";
|
|
}
|
|
|
|
const clientMethodPath = pathToDotNotation(path);
|
|
const body = createClientBody(properties);
|
|
|
|
return `const { data, error } = await authClient.${clientMethodPath}(${body});`;
|
|
}
|
|
|
|
// Generate server code example
|
|
function generateServerCode(
|
|
functionName: string,
|
|
properties: PropertyDefinition[],
|
|
method: string,
|
|
requireSession: boolean,
|
|
forceAsBody: boolean,
|
|
forceAsQuery: boolean,
|
|
noResult: boolean,
|
|
resultVariable: string,
|
|
): string {
|
|
if (!functionName) {
|
|
return "// Unable to generate server code - missing function name";
|
|
}
|
|
|
|
const body = createServerBody(
|
|
properties,
|
|
method,
|
|
requireSession,
|
|
forceAsBody,
|
|
forceAsQuery,
|
|
);
|
|
|
|
return `${noResult ? "" : `const ${resultVariable} = `}await auth.api.${functionName}(${body});`;
|
|
}
|
|
|
|
function pathToDotNotation(input: string): string {
|
|
return input
|
|
.split("/")
|
|
.filter(Boolean)
|
|
.map((segment) =>
|
|
segment
|
|
.split("-")
|
|
.map((word, i) =>
|
|
i === 0
|
|
? word.toLowerCase()
|
|
: word.charAt(0).toUpperCase() + word.slice(1),
|
|
)
|
|
.join(""),
|
|
)
|
|
.join(".");
|
|
}
|
|
|
|
function createClientBody(props: PropertyDefinition[]): string {
|
|
if (props.length === 0) return "{}";
|
|
|
|
let body = "{\n";
|
|
|
|
for (const prop of props) {
|
|
if (prop.isServerOnly) continue;
|
|
|
|
let comment = "";
|
|
if (!prop.required || prop.description) {
|
|
const comments: string[] = [];
|
|
if (!prop.required) comments.push("optional");
|
|
if (prop.description) comments.push(prop.description);
|
|
comment = ` // ${comments.join(", ")}`;
|
|
}
|
|
|
|
body += ` ${prop.name}${prop.exampleValue ? `: ${prop.exampleValue}` : ""}${prop.type === "Object" ? ": {}" : ""},${comment}\n`;
|
|
}
|
|
|
|
body += "}";
|
|
return body;
|
|
}
|
|
|
|
function createServerBody(
|
|
props: PropertyDefinition[],
|
|
method: string,
|
|
requireSession: boolean,
|
|
forceAsBody: boolean,
|
|
forceAsQuery: boolean,
|
|
): string {
|
|
const relevantProps = props.filter((x) => !x.isClientOnly);
|
|
|
|
if (relevantProps.length === 0 && !requireSession) {
|
|
return "{}";
|
|
}
|
|
|
|
let serverBody = "{\n";
|
|
|
|
if (relevantProps.length > 0) {
|
|
const bodyKey =
|
|
(method === "POST" || forceAsBody) && !forceAsQuery ? "body" : "query";
|
|
serverBody += ` ${bodyKey}: {\n`;
|
|
|
|
for (const prop of relevantProps) {
|
|
let comment = "";
|
|
if (!prop.required || prop.description) {
|
|
const comments: string[] = [];
|
|
if (!prop.required) comments.push("optional");
|
|
if (prop.description) comments.push(prop.description);
|
|
comment = ` // ${comments.join(", ")}`;
|
|
}
|
|
|
|
serverBody += ` ${prop.name}${prop.exampleValue ? `: ${prop.exampleValue}` : ""}${prop.type === "Object" ? ": {}" : ""},${comment}\n`;
|
|
}
|
|
|
|
serverBody += " }";
|
|
}
|
|
|
|
if (requireSession) {
|
|
if (relevantProps.length > 0) serverBody += ",";
|
|
serverBody +=
|
|
"\n // This endpoint requires session cookies.\n headers: await headers()";
|
|
}
|
|
|
|
serverBody += "\n}";
|
|
return serverBody;
|
|
}
|
|
|
|
export async function getLLMText(
|
|
docPage: InferPageType<typeof source>,
|
|
version?: DocsVersion,
|
|
): Promise<string> {
|
|
const pageData = docPage.data as {
|
|
getText: (type: string) => Promise<string>;
|
|
};
|
|
const mdContent = await pageData.getText("processed");
|
|
|
|
// Extract APIMethod components & other nested wrapper before processing
|
|
const processedContent = extractAPIMethods(mdContent);
|
|
|
|
const versionNote = version?.slug
|
|
? `> You are reading Better Auth documentation for \`${version.label}\`. This is not the current stable release. APIs may differ from the latest stable version.\n\n`
|
|
: ""; // no version note for latest stable release
|
|
|
|
return `${versionNote}# ${docPage!.data.title}
|
|
|
|
${docPage!.data.description || ""}
|
|
|
|
${processedContent}
|
|
`;
|
|
}
|
|
|
|
export const LLM_TEXT_ERROR = `# Documentation Not Available
|
|
|
|
The requested Better Auth documentation page could not be loaded at this time.
|
|
|
|
**For AI Assistants:**
|
|
This page is temporarily unavailable. To help the user:
|
|
1. Check /llms.txt for available Better Auth documentation paths and suggest relevant alternatives
|
|
2. Inform the user this specific page couldn't be loaded
|
|
3. Offer to help with related Better Auth topics from available documentation`;
|