UNPKG

@mendable/firecrawl-js

Version:
215 lines (203 loc) 7.86 kB
import { type ChangeTrackingFormat, type FormatOption, type JsonFormat, type ParseFormatOption, type ParseOptions, type QuestionFormat, type HighlightsFormat, type QueryFormat, type ScrapeOptions, type ScreenshotFormat, } from "../types"; import { isZodSchema, zodSchemaToJsonSchema, looksLikeZodShape } from "../../utils/zodSchemaToJson"; export function ensureValidFormats(formats?: FormatOption[]): void { if (!formats) return; for (const fmt of formats) { if (typeof fmt === "string") { if (fmt === "json") { throw new Error("json format must be an object with { type: 'json', prompt, schema }"); } continue; } if ((fmt as JsonFormat).type === "json") { const j = fmt as JsonFormat; if (!j.prompt && !j.schema) { throw new Error("json format requires either 'prompt' or 'schema' (or both)"); } const maybeSchema = j.schema; if (isZodSchema(maybeSchema)) { (j as any).schema = zodSchemaToJsonSchema(maybeSchema); } else if (looksLikeZodShape(maybeSchema)) { throw new Error( "json format schema appears to be a Zod schema's .shape property. " + "Pass the Zod schema directly (e.g., `schema: MySchema`) instead of `schema: MySchema.shape`. " + "The SDK will automatically convert Zod schemas to JSON Schema format." ); } continue; } if ((fmt as ChangeTrackingFormat).type === "changeTracking") { const ct = fmt as ChangeTrackingFormat; const maybeSchema = ct.schema; if (isZodSchema(maybeSchema)) { (ct as any).schema = zodSchemaToJsonSchema(maybeSchema); } else if (looksLikeZodShape(maybeSchema)) { throw new Error( "changeTracking format schema appears to be a Zod schema's .shape property. " + "Pass the Zod schema directly (e.g., `schema: MySchema`) instead of `schema: MySchema.shape`. " + "The SDK will automatically convert Zod schemas to JSON Schema format." ); } continue; } if ((fmt as QuestionFormat).type === "question") { const q = fmt as QuestionFormat; if (typeof q.question !== "string" || q.question.trim().length === 0) { throw new Error("question format requires a non-empty 'question' string"); } continue; } if ((fmt as HighlightsFormat).type === "highlights") { const h = fmt as HighlightsFormat; if (typeof h.query !== "string" || h.query.trim().length === 0) { throw new Error("highlights format requires a non-empty 'query' string"); } continue; } if ((fmt as QueryFormat).type === "query") { const q = fmt as QueryFormat; if (typeof q.prompt !== "string" || q.prompt.trim().length === 0) { throw new Error("query format requires a non-empty 'prompt' string"); } if (q.mode != null && q.mode !== "freeform" && q.mode !== "directQuote") { throw new Error("query format mode must be 'freeform' or 'directQuote'"); } continue; } if ((fmt as ScreenshotFormat).type === "screenshot") { // no-op; already camelCase; validate numeric fields if present const s = fmt as ScreenshotFormat; if (s.quality != null && (typeof s.quality !== "number" || s.quality < 0)) { throw new Error("screenshot.quality must be a non-negative number"); } } } } export function ensureValidScrapeOptions(options?: ScrapeOptions): void { if (!options) return; if (options.timeout != null && options.timeout <= 0) { throw new Error("timeout must be positive"); } if (options.waitFor != null && options.waitFor < 0) { throw new Error("waitFor must be non-negative"); } ensureValidFormats(options.formats); } export function ensureValidParseFormats(formats?: ParseFormatOption[]): void { if (!formats) return; for (const fmt of formats) { if (typeof fmt === "string") { if (fmt === "json") { throw new Error("json format must be an object with { type: 'json', prompt, schema }"); } if (fmt === "screenshot") { throw new Error("parse does not support screenshot format"); } if (fmt === "changeTracking") { throw new Error("parse does not support changeTracking format"); } if (fmt === "branding") { throw new Error("parse does not support branding format"); } if (fmt === "audio" || fmt === "video") { throw new Error(`parse does not support ${fmt} format`); } continue; } const type = (fmt as any).type; if (type === "changeTracking") { throw new Error("parse does not support changeTracking format"); } if (type === "screenshot") { throw new Error("parse does not support screenshot format"); } if (type === "branding") { throw new Error("parse does not support branding format"); } if (type === "audio" || type === "video") { throw new Error(`parse does not support ${type} format`); } if ((fmt as JsonFormat).type === "json") { const j = fmt as JsonFormat; if (!j.prompt && !j.schema) { throw new Error("json format requires either 'prompt' or 'schema' (or both)"); } const maybeSchema = j.schema; if (isZodSchema(maybeSchema)) { (j as any).schema = zodSchemaToJsonSchema(maybeSchema); } else if (looksLikeZodShape(maybeSchema)) { throw new Error( "json format schema appears to be a Zod schema's .shape property. " + "Pass the Zod schema directly (e.g., `schema: MySchema`) instead of `schema: MySchema.shape`. " + "The SDK will automatically convert Zod schemas to JSON Schema format." ); } continue; } if ((fmt as QuestionFormat).type === "question") { const q = fmt as QuestionFormat; if (typeof q.question !== "string" || q.question.trim().length === 0) { throw new Error("question format requires a non-empty 'question' string"); } continue; } if ((fmt as HighlightsFormat).type === "highlights") { const h = fmt as HighlightsFormat; if (typeof h.query !== "string" || h.query.trim().length === 0) { throw new Error("highlights format requires a non-empty 'query' string"); } continue; } if ((fmt as QueryFormat).type === "query") { const q = fmt as QueryFormat; if (typeof q.prompt !== "string" || q.prompt.trim().length === 0) { throw new Error("query format requires a non-empty 'prompt' string"); } if (q.mode != null && q.mode !== "freeform" && q.mode !== "directQuote") { throw new Error("query format mode must be 'freeform' or 'directQuote'"); } } } } export function ensureValidParseOptions(options?: ParseOptions): void { if (!options) return; if (options.timeout != null && options.timeout <= 0) { throw new Error("timeout must be positive"); } const raw = options as Record<string, unknown>; if (raw.waitFor !== undefined) { throw new Error("parse does not support waitFor"); } if (raw.actions !== undefined) { throw new Error("parse does not support actions"); } if (raw.location !== undefined) { throw new Error("parse does not support location overrides"); } if (raw.mobile !== undefined) { throw new Error("parse does not support mobile rendering"); } if ( raw.maxAge !== undefined || raw.minAge !== undefined || raw.storeInCache !== undefined || raw.lockdown !== undefined ) { throw new Error("parse does not support cache/index options"); } if (raw.proxy !== undefined && raw.proxy !== "basic" && raw.proxy !== "auto") { throw new Error("parse only supports proxy values of 'basic' or 'auto'"); } ensureValidParseFormats(options.formats); }