37 lines
1.5 KiB
TypeScript
37 lines
1.5 KiB
TypeScript
import type { Result } from "../../types/result";
|
|
import { z } from "zod";
|
|
import { Err, Ok } from "../../types/result";
|
|
|
|
const scrapeResponseSchema = z.object({
|
|
success: z.boolean().optional(),
|
|
error: z.union([z.string(), z.object({ message: z.string() })]).optional(),
|
|
message: z.string().optional(),
|
|
data: z.unknown().optional(),
|
|
});
|
|
|
|
export const readFirecrawlResponse = async (response: Response): Promise<Result<string, string>> => {
|
|
const context = `Firecrawl scrape (HTTP ${response.status})`;
|
|
let body: unknown;
|
|
try {
|
|
body = await response.json();
|
|
} catch {
|
|
return Err(`${context}: received a non-JSON or unreadable response.`);
|
|
}
|
|
|
|
const parsed = scrapeResponseSchema.safeParse(body);
|
|
if (!parsed.success) {
|
|
return Err(`${context}: received an unexpected response format.`);
|
|
}
|
|
|
|
const payload = parsed.data;
|
|
const providerError = typeof payload.error === "string" ? payload.error : payload.error?.message;
|
|
if (!response.ok || payload.success === false || providerError) {
|
|
return Err(`${context}: ${providerError || payload.message || response.statusText || "Scrape failed without an error message."}`);
|
|
}
|
|
const content = z.object({ markdown: z.string() }).safeParse(payload.data);
|
|
if (!content.success || content.data.markdown.trim() === "") {
|
|
return Err(`${context}: no Markdown content was returned for this URL.`);
|
|
}
|
|
return Ok(content.data.markdown);
|
|
};
|