209 lines
7.4 KiB
TypeScript
209 lines
7.4 KiB
TypeScript
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");
|
|
}
|
|
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 ((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);
|
|
}
|
|
|