diff options
Diffstat (limited to 'packages/tool-web-search/src/validate.ts')
| -rw-r--r-- | packages/tool-web-search/src/validate.ts | 304 |
1 files changed, 152 insertions, 152 deletions
diff --git a/packages/tool-web-search/src/validate.ts b/packages/tool-web-search/src/validate.ts index 56bd356..019fd5a 100644 --- a/packages/tool-web-search/src/validate.ts +++ b/packages/tool-web-search/src/validate.ts @@ -17,32 +17,32 @@ export const CRAWL_DEFAULT_LIMIT = 3; export const MAX_LIMIT = 10; interface BaseArgs { - readonly format: Format; + readonly format: Format; } export interface SearchArgs extends BaseArgs { - readonly mode: "search"; - readonly query: string; - readonly limit: number; - readonly scrape: boolean; - readonly lang?: string; - readonly country?: string; + readonly mode: "search"; + readonly query: string; + readonly limit: number; + readonly scrape: boolean; + readonly lang?: string; + readonly country?: string; } export interface ScrapeArgs extends BaseArgs { - readonly mode: "scrape"; - readonly url: string; + readonly mode: "scrape"; + readonly url: string; } export interface CrawlArgs extends BaseArgs { - readonly mode: "crawl"; - readonly url: string; - readonly limit: number; + readonly mode: "crawl"; + readonly url: string; + readonly limit: number; } export interface MapArgs extends BaseArgs { - readonly mode: "map"; - readonly url: string; + readonly mode: "map"; + readonly url: string; } export type ValidatedArgs = SearchArgs | ScrapeArgs | CrawlArgs | MapArgs; @@ -52,60 +52,60 @@ export type ValidationError = { readonly error: string }; type Result<T> = { readonly value: T } | ValidationError; function resolveFormat(raw: unknown): Result<Format> { - if (raw === undefined || raw === null) { - return { value: "markdown" }; - } - if (typeof raw === "string" && (FORMATS as readonly string[]).includes(raw)) { - return { value: raw as Format }; - } - return { - error: `Error: Invalid format "${String(raw)}" (must be one of: markdown, text, html).`, - }; + if (raw === undefined || raw === null) { + return { value: "markdown" }; + } + if (typeof raw === "string" && (FORMATS as readonly string[]).includes(raw)) { + return { value: raw as Format }; + } + return { + error: `Error: Invalid format "${String(raw)}" (must be one of: markdown, text, html).`, + }; } function resolveMode(raw: unknown, query: unknown, url: unknown): Result<Mode> { - if (raw === undefined || raw === null) { - const hasQuery = typeof query === "string" && query.trim().length > 0; - const hasUrl = typeof url === "string" && url.trim().length > 0; - return { value: hasQuery ? "search" : hasUrl ? "scrape" : "search" }; - } - if (typeof raw === "string" && (MODES as readonly string[]).includes(raw)) { - return { value: raw as Mode }; - } - return { - error: `Error: Invalid mode "${String(raw)}" (must be one of: search, scrape, crawl, map).`, - }; + if (raw === undefined || raw === null) { + const hasQuery = typeof query === "string" && query.trim().length > 0; + const hasUrl = typeof url === "string" && url.trim().length > 0; + return { value: hasQuery ? "search" : hasUrl ? "scrape" : "search" }; + } + if (typeof raw === "string" && (MODES as readonly string[]).includes(raw)) { + return { value: raw as Mode }; + } + return { + error: `Error: Invalid mode "${String(raw)}" (must be one of: search, scrape, crawl, map).`, + }; } function optionalString(raw: unknown, name: string): Result<string | undefined> { - if (raw === undefined || raw === null) { - return { value: undefined }; - } - if (typeof raw === "string") { - return { value: raw }; - } - return { error: `Error: "${name}" must be a string.` }; + if (raw === undefined || raw === null) { + return { value: undefined }; + } + if (typeof raw === "string") { + return { value: raw }; + } + return { error: `Error: "${name}" must be a string.` }; } function resolveLimit(raw: unknown, defaultLimit: number): Result<number> { - if (raw === undefined || raw === null) { - return { value: defaultLimit }; - } - const n = Number(raw); - if (!Number.isFinite(n) || n < 1) { - return { error: 'Error: "limit" must be a positive number.' }; - } - return { value: Math.min(Math.floor(n), MAX_LIMIT) }; + if (raw === undefined || raw === null) { + return { value: defaultLimit }; + } + const n = Number(raw); + if (!Number.isFinite(n) || n < 1) { + return { error: 'Error: "limit" must be a positive number.' }; + } + return { value: Math.min(Math.floor(n), MAX_LIMIT) }; } function resolveBoolean(raw: unknown, name: string): Result<boolean> { - if (raw === undefined || raw === null) { - return { value: false }; - } - if (typeof raw === "boolean") { - return { value: raw }; - } - return { error: `Error: "${name}" must be a boolean.` }; + if (raw === undefined || raw === null) { + return { value: false }; + } + if (typeof raw === "boolean") { + return { value: raw }; + } + return { error: `Error: "${name}" must be a boolean.` }; } /** @@ -113,100 +113,100 @@ function resolveBoolean(raw: unknown, name: string): Result<boolean> { * Returns `{ error }` for invalid input — the tool surfaces it verbatim. */ export function validateArgs(args: unknown): ValidatedArgs | ValidationError { - if (args === null || args === undefined || typeof args !== "object") { - return { error: "Error: Arguments must be an object." }; - } - const obj = args as Record<string, unknown>; - - const format = resolveFormat(obj.format); - if ("error" in format) { - return format; - } - - const mode = resolveMode(obj.mode, obj.query, obj.url); - if ("error" in mode) { - return mode; - } - - const query = optionalString(obj.query, "query"); - if ("error" in query) { - return query; - } - - const url = optionalString(obj.url, "url"); - if ("error" in url) { - return url; - } - - switch (mode.value) { - case "search": { - if (query.value === undefined || query.value.trim().length === 0) { - return { error: "Error: query is required for search mode." }; - } - const limit = resolveLimit(obj.limit, SEARCH_DEFAULT_LIMIT); - if ("error" in limit) { - return limit; - } - const scrape = resolveBoolean(obj.scrape, "scrape"); - if ("error" in scrape) { - return scrape; - } - const lang = optionalString(obj.lang, "lang"); - if ("error" in lang) { - return lang; - } - const country = optionalString(obj.country, "country"); - if ("error" in country) { - return country; - } - const result: SearchArgs = { - mode: "search", - query: query.value, - limit: limit.value, - scrape: scrape.value, - format: format.value, - ...(lang.value !== undefined ? { lang: lang.value } : {}), - ...(country.value !== undefined ? { country: country.value } : {}), - }; - return result; - } - case "scrape": { - if (url.value === undefined || url.value.trim().length === 0) { - return { error: "Error: url is required for scrape mode." }; - } - const result: ScrapeArgs = { - mode: "scrape", - url: url.value, - format: format.value, - }; - return result; - } - case "crawl": { - if (url.value === undefined || url.value.trim().length === 0) { - return { error: "Error: url is required for crawl mode." }; - } - const limit = resolveLimit(obj.limit, CRAWL_DEFAULT_LIMIT); - if ("error" in limit) { - return limit; - } - const result: CrawlArgs = { - mode: "crawl", - url: url.value, - limit: limit.value, - format: format.value, - }; - return result; - } - case "map": { - if (url.value === undefined || url.value.trim().length === 0) { - return { error: "Error: url is required for map mode." }; - } - const result: MapArgs = { - mode: "map", - url: url.value, - format: format.value, - }; - return result; - } - } + if (args === null || args === undefined || typeof args !== "object") { + return { error: "Error: Arguments must be an object." }; + } + const obj = args as Record<string, unknown>; + + const format = resolveFormat(obj.format); + if ("error" in format) { + return format; + } + + const mode = resolveMode(obj.mode, obj.query, obj.url); + if ("error" in mode) { + return mode; + } + + const query = optionalString(obj.query, "query"); + if ("error" in query) { + return query; + } + + const url = optionalString(obj.url, "url"); + if ("error" in url) { + return url; + } + + switch (mode.value) { + case "search": { + if (query.value === undefined || query.value.trim().length === 0) { + return { error: "Error: query is required for search mode." }; + } + const limit = resolveLimit(obj.limit, SEARCH_DEFAULT_LIMIT); + if ("error" in limit) { + return limit; + } + const scrape = resolveBoolean(obj.scrape, "scrape"); + if ("error" in scrape) { + return scrape; + } + const lang = optionalString(obj.lang, "lang"); + if ("error" in lang) { + return lang; + } + const country = optionalString(obj.country, "country"); + if ("error" in country) { + return country; + } + const result: SearchArgs = { + mode: "search", + query: query.value, + limit: limit.value, + scrape: scrape.value, + format: format.value, + ...(lang.value !== undefined ? { lang: lang.value } : {}), + ...(country.value !== undefined ? { country: country.value } : {}), + }; + return result; + } + case "scrape": { + if (url.value === undefined || url.value.trim().length === 0) { + return { error: "Error: url is required for scrape mode." }; + } + const result: ScrapeArgs = { + mode: "scrape", + url: url.value, + format: format.value, + }; + return result; + } + case "crawl": { + if (url.value === undefined || url.value.trim().length === 0) { + return { error: "Error: url is required for crawl mode." }; + } + const limit = resolveLimit(obj.limit, CRAWL_DEFAULT_LIMIT); + if ("error" in limit) { + return limit; + } + const result: CrawlArgs = { + mode: "crawl", + url: url.value, + limit: limit.value, + format: format.value, + }; + return result; + } + case "map": { + if (url.value === undefined || url.value.trim().length === 0) { + return { error: "Error: url is required for map mode." }; + } + const result: MapArgs = { + mode: "map", + url: url.value, + format: format.value, + }; + return result; + } + } } |
