@webclaw/sdk
Advanced tools
+5
-2
@@ -356,2 +356,3 @@ /** | ||
| query: string; | ||
| /** @deprecated Research always runs in deep mode; this flag is ignored by the API. */ | ||
| deep?: boolean; | ||
@@ -399,2 +400,3 @@ max_sources?: number; | ||
| elapsed_ms?: number; | ||
| /** @deprecated Research always runs in deep mode; this flag is ignored by the API. */ | ||
| deep?: boolean; | ||
@@ -571,3 +573,3 @@ } | ||
| interval?: number; | ||
| /** Maximum time to wait in ms. Default 600_000 (10 min), 1_200_000 for deep. */ | ||
| /** Maximum time to wait in ms. Default 1_200_000 (20 min) — research always runs in deep mode. */ | ||
| maxWait?: number; | ||
@@ -758,3 +760,4 @@ } | ||
| * Start a research job and poll until completion. | ||
| * Deep research uses a 20-minute timeout by default; normal uses 10 minutes. | ||
| * Every job runs in deep mode server-side, so the default poll timeout | ||
| * is 20 minutes; pass `opts.maxWait` to override. | ||
| * @param params - Research query and depth options. | ||
@@ -761,0 +764,0 @@ * @param opts - Polling interval and max wait override. |
+5
-2
@@ -356,2 +356,3 @@ /** | ||
| query: string; | ||
| /** @deprecated Research always runs in deep mode; this flag is ignored by the API. */ | ||
| deep?: boolean; | ||
@@ -399,2 +400,3 @@ max_sources?: number; | ||
| elapsed_ms?: number; | ||
| /** @deprecated Research always runs in deep mode; this flag is ignored by the API. */ | ||
| deep?: boolean; | ||
@@ -571,3 +573,3 @@ } | ||
| interval?: number; | ||
| /** Maximum time to wait in ms. Default 600_000 (10 min), 1_200_000 for deep. */ | ||
| /** Maximum time to wait in ms. Default 1_200_000 (20 min) — research always runs in deep mode. */ | ||
| maxWait?: number; | ||
@@ -758,3 +760,4 @@ } | ||
| * Start a research job and poll until completion. | ||
| * Deep research uses a 20-minute timeout by default; normal uses 10 minutes. | ||
| * Every job runs in deep mode server-side, so the default poll timeout | ||
| * is 20 minutes; pass `opts.maxWait` to override. | ||
| * @param params - Research query and depth options. | ||
@@ -761,0 +764,0 @@ * @param opts - Polling interval and max wait override. |
+3
-3
@@ -287,3 +287,4 @@ "use strict"; | ||
| * Start a research job and poll until completion. | ||
| * Deep research uses a 20-minute timeout by default; normal uses 10 minutes. | ||
| * Every job runs in deep mode server-side, so the default poll timeout | ||
| * is 20 minutes; pass `opts.maxWait` to override. | ||
| * @param params - Research query and depth options. | ||
@@ -302,4 +303,3 @@ * @param opts - Polling interval and max wait override. | ||
| const interval = opts.interval ?? 2e3; | ||
| const defaultMax = params.deep ? 12e5 : 6e5; | ||
| const maxWait = opts.maxWait ?? defaultMax; | ||
| const maxWait = opts.maxWait ?? 12e5; | ||
| return pollUntilDone( | ||
@@ -306,0 +306,0 @@ () => this.getResearchStatus(start.id), |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export { Webclaw, CrawlJob } from \"./client.js\";\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\n","/**\n * Error hierarchy for the Webclaw SDK.\n * All errors extend WebclawError so callers can catch broadly or narrowly.\n */\n\nexport class WebclawError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly body?: unknown,\n ) {\n super(message);\n this.name = \"WebclawError\";\n }\n}\n\nexport class AuthenticationError extends WebclawError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, 401);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class CreditLimitError extends WebclawError {\n constructor(message = \"Credit limit reached\") {\n super(message, 402);\n this.name = \"CreditLimitError\";\n }\n}\n\nexport class ScopeError extends WebclawError {\n constructor(message = \"API key lacks the required scope\") {\n super(message, 403);\n this.name = \"ScopeError\";\n }\n}\n\nexport class RateLimitError extends WebclawError {\n public readonly retryAfter: number | null;\n\n constructor(retryAfterSeconds: number | null = null) {\n super(\"Rate limit exceeded\", 429);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfterSeconds;\n }\n}\n\nexport class NotFoundError extends WebclawError {\n constructor(message = \"Resource not found\") {\n super(message, 404);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class TimeoutError extends WebclawError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"TimeoutError\";\n }\n}\n","/**\n * Webclaw SDK client. Wraps the webclaw REST API with typed methods,\n * timeout support, and a clean error hierarchy.\n */\n\nimport {\n AuthenticationError,\n CreditLimitError,\n NotFoundError,\n RateLimitError,\n ScopeError,\n TimeoutError,\n WebclawError,\n} from \"./errors.js\";\nimport type {\n BatchRequest,\n BatchResponse,\n BrandRequest,\n BrandResponse,\n CrawlPollOptions,\n CrawlRequest,\n CrawlStartResponse,\n CrawlStatusResponse,\n DiffRequest,\n DiffResponse,\n EndpointsRequest,\n EndpointsResponse,\n ExtractRequest,\n ExtractResponse,\n LeadBatchOptions,\n LeadBatchPollOptions,\n LeadBatchResponse,\n LeadBatchStartResponse,\n LeadOptions,\n LeadResponse,\n MapRequest,\n MapResponse,\n ResearchPollOptions,\n ResearchRequest,\n ResearchResponse,\n ResearchStartResponse,\n ScrapeRequest,\n ScrapeResponse,\n SearchRequest,\n SearchResponse,\n SummarizeRequest,\n SummarizeResponse,\n WatchCreateRequest,\n WatchResponse,\n WebclawConfig,\n ListExtractorsResponse,\n VerticalScrapeResponse,\n CreateXMonitorRequest,\n UpdateXMonitorRequest,\n XMonitor,\n ListXMonitorsResponse,\n XMonitorMutationResponse,\n XMonitorCheckResponse,\n ExportXAudienceRequest,\n ExportXAudienceResponse,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.webclaw.io\";\nconst DEFAULT_TIMEOUT = 30_000;\n\nexport class Webclaw {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: WebclawConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n // -- Public API methods --\n\n /**\n * Scrape a single URL and extract its content.\n * @param params - URL and extraction options (formats, selectors, caching).\n * @returns Extracted content in the requested formats.\n * @throws {WebclawError} On network or API errors.\n */\n async scrape(params: ScrapeRequest): Promise<ScrapeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ScrapeResponse>(\"/v1/scrape\", params);\n }\n\n /**\n * Start an async crawl job that discovers and scrapes pages from a root URL.\n * @param params - Root URL and crawl limits (depth, max pages).\n * @returns A CrawlJob handle for polling or waiting.\n * @throws {WebclawError} On network or API errors.\n */\n async crawl(params: CrawlRequest): Promise<CrawlJob> {\n if (!params.url) throw new Error(\"url is required\");\n // Async start: no per-request timeout. The job is awaited via\n // polling (which keeps its own deadline), not this call.\n const res = await this.post<CrawlStartResponse>(\"/v1/crawl\", params, null);\n return new CrawlJob(res.id, this);\n }\n\n /**\n * Get the current status and partial results of a crawl job.\n * @param id - Crawl job ID returned by {@link crawl}.\n * @returns Current status, page count, and any completed pages.\n * @throws {NotFoundError} If the crawl job does not exist.\n */\n async getCrawlStatus(id: string): Promise<CrawlStatusResponse> {\n return this.get<CrawlStatusResponse>(`/v1/crawl/${encodeURIComponent(id)}`);\n }\n\n /**\n * Discover URLs from a site's sitemap.\n * @param params - The root URL to map.\n * @returns List of discovered URLs and total count.\n */\n async map(params: MapRequest): Promise<MapResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<MapResponse>(\"/v1/map\", params);\n }\n\n /**\n * Discover API endpoints embedded in a page's JavaScript.\n *\n * Scans the page's inline `<script>` bodies plus its `<script src>`\n * bundles for request paths, absolute URLs, GraphQL, and WebSocket\n * endpoints — the API surface that {@link map} (sitemap-based)\n * cannot see. Credit cost: 2.\n *\n * SECURITY: the returned `endpoints`/`hosts` are extracted from\n * attacker-influenced page content and are NOT sanitized by the SDK.\n * Do not feed any returned `value`/`source` into another fetch,\n * shell, eval, or SQL without your own validation. See\n * {@link DiscoveredEndpoint}.\n *\n * @param params - URL plus optional third-party / bundle-count opts.\n * @returns Discovered endpoints, hosts, and scan counters.\n * @throws {WebclawError} On network or API errors (400 if `url` is\n * missing or invalid).\n */\n async endpoints(params: EndpointsRequest): Promise<EndpointsResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<EndpointsResponse>(\"/v1/endpoints\", params);\n }\n\n /**\n * Scrape multiple URLs in parallel.\n * @param params - Array of URLs, optional formats and concurrency limit.\n * @returns Results for each URL (success or per-URL error).\n */\n async batch(params: BatchRequest): Promise<BatchResponse> {\n if (!params.urls?.length) throw new Error(\"urls must be a non-empty array\");\n return this.post<BatchResponse>(\"/v1/batch\", params);\n }\n\n /**\n * Extract structured data from a page using an LLM.\n * @param params - URL plus a JSON schema or natural-language prompt.\n * @returns Extracted data matching the requested schema.\n */\n async extract(params: ExtractRequest): Promise<ExtractResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ExtractResponse>(\"/v1/extract\", params);\n }\n\n /**\n * Enrich a company lead from its website using an LLM.\n *\n * Fetches the URL and returns a structured company profile — name,\n * summary, socials, tech stack, pricing, and contact emails, plus\n * `people`: founders and team members, each with their LinkedIn and X\n * links where found (`people_source` records how they were sourced).\n *\n * Pricing: a flat 100 credits per successful lead.\n *\n * @param url - The company website to enrich.\n * @param options - Cache control (`no_cache`).\n * @returns The enriched lead plus its cache status and billing.\n */\n async lead(url: string, options: LeadOptions = {}): Promise<LeadResponse> {\n if (!url) throw new Error(\"url is required\");\n return this.post<LeadResponse>(\"/v1/lead\", { url, ...options });\n }\n\n /**\n * Start an async batch that enriches up to 25 company leads at once.\n *\n * Each URL is enriched into the same structured profile that\n * {@link lead} returns. The job runs in the background: this call\n * returns immediately with a job id and the number of URLs accepted\n * (the server validates and dedupes `urls`). Poll {@link getLeadBatch}\n * — or block via {@link waitForLeadBatch} — for results.\n *\n * Pricing: a flat 100 credits per *successful* lead; errored URLs are\n * not billed.\n *\n * @param urls - 1..25 company website URLs. Validated/deduped server-side.\n * @param options - Cache control (`no_cache`).\n * @returns The job id, accepted URL count, and per-URL credit cost.\n * @throws {CreditLimitError} On insufficient credits or an inactive plan (402).\n * @throws {WebclawError} If `urls` is empty or has more than 25 entries (400).\n */\n async leadBatch(\n urls: string[],\n options: LeadBatchOptions = {},\n ): Promise<LeadBatchStartResponse> {\n if (!urls?.length) throw new Error(\"urls must be a non-empty array\");\n // Async start: no per-request timeout. Results are awaited via polling\n // ({@link getLeadBatch}/{@link waitForLeadBatch}), which keeps its own deadline.\n return this.post<LeadBatchStartResponse>(\n \"/v1/lead/batch\",\n { urls, ...options },\n null,\n );\n }\n\n /**\n * Get the current status and results of a lead-batch job without waiting.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @returns Current status, counts, billing, and any per-URL results so far.\n * @throws {NotFoundError} If the batch job does not exist or isn't yours (404).\n */\n async getLeadBatch(id: string): Promise<LeadBatchResponse> {\n return this.get<LeadBatchResponse>(\n `/v1/lead/batch/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * Poll a lead-batch job by id until it's `completed` or `failed`.\n * Same ergonomics as {@link waitForCrawl} / {@link waitForResearch}.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @param opts - Polling interval and max wait override.\n * @returns The finished job with all per-URL results and final billing.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForLeadBatch(\n id: string,\n opts: LeadBatchPollOptions = {},\n ): Promise<LeadBatchResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 600_000;\n return pollUntilDone(\n () => this.getLeadBatch(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Generate a concise summary of a page's content.\n * @param params - URL and optional max sentence count.\n * @returns The generated summary text.\n */\n async summarize(params: SummarizeRequest): Promise<SummarizeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<SummarizeResponse>(\"/v1/summarize\", params);\n }\n\n /**\n * Extract brand identity information (name, logo, colors) from a URL.\n * @param params - The URL to analyze.\n * @returns Brand data as a flexible object (shape depends on the site).\n */\n async brand(params: BrandRequest): Promise<BrandResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<BrandResponse>(\"/v1/brand\", params);\n }\n\n /**\n * Perform a web search query, optionally scraping each result page.\n * @param params - Search query, result count, and optional scrape/format options.\n * @returns Search results with optional scraped content per hit.\n */\n async search(params: SearchRequest): Promise<SearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n return this.post<SearchResponse>(\"/v1/search\", params);\n }\n\n /**\n * Detect content changes on a page since a previous snapshot.\n * @param params - URL and optional previous state to diff against.\n * @returns Detected changes between the two states.\n */\n async diff(params: DiffRequest): Promise<DiffResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<DiffResponse>(\"/v1/diff\", params);\n }\n\n /**\n * Start a research job and poll until completion.\n * Deep research uses a 20-minute timeout by default; normal uses 10 minutes.\n * @param params - Research query and depth options.\n * @param opts - Polling interval and max wait override.\n * @returns Completed research report, sources, and findings.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async research(\n params: ResearchRequest,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n // Async start: no per-request timeout. Completion is awaited via\n // polling below, which enforces its own (interval, maxWait) deadline.\n const start = await this.post<ResearchStartResponse>(\n \"/v1/research\",\n params,\n null,\n );\n\n const interval = opts.interval ?? 2_000;\n const defaultMax = params.deep ? 1_200_000 : 600_000;\n const maxWait = opts.maxWait ?? defaultMax;\n\n return pollUntilDone(\n () => this.getResearchStatus(start.id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a research job by id until it's `completed` or `failed`.\n * Mirrors the ergonomics of `sdk-go.WaitForResearch` for callers who\n * already have an id (e.g. persisted from an earlier `/v1/research`\n * call on another process) and want to block-until-done without\n * restarting the job via {@link research}.\n *\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForResearch(\n id: string,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n const interval = opts.interval ?? 2_000;\n // No `deep` hint here since we don't have the original request;\n // pick the longer window so shallow jobs finish comfortably.\n const maxWait = opts.maxWait ?? 1_200_000;\n return pollUntilDone(\n () => this.getResearchStatus(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a crawl job by id until it's `completed` or `failed`.\n * Same semantics as {@link CrawlJob#waitForCompletion} but for callers\n * who have only the id (e.g. persisted or returned from another process).\n * Mirrors `sdk-go.WaitForCompletion`.\n */\n async waitForCrawl(\n id: string,\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n return pollUntilDone(\n () => this.getCrawlStatus(id),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Get the current status of a research job without waiting.\n * @param id - Research job ID returned when starting research.\n * @returns Current status and any partial/complete results.\n * @throws {NotFoundError} If the research job does not exist.\n */\n async getResearchStatus(id: string): Promise<ResearchResponse> {\n return this.get<ResearchResponse>(`/v1/research/${encodeURIComponent(id)}`);\n }\n\n // -- Watch methods --\n\n async watchCreate(params: WatchCreateRequest): Promise<WatchResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<WatchResponse>(\"/v1/watch\", params);\n }\n\n async watchList(limit?: number, offset?: number): Promise<WatchResponse[]> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<WatchResponse[]>(`/v1/watch${qs ? `?${qs}` : \"\"}`);\n }\n\n async watchGet(id: string): Promise<WatchResponse> {\n return this.get<WatchResponse>(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchDelete(id: string): Promise<void> {\n await this.del(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchCheck(id: string): Promise<WatchResponse> {\n return this.post<WatchResponse>(\n `/v1/watch/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n // -- X (Twitter) monitor methods --\n //\n // The X analog of the watch endpoints: poll X (profiles, searches,\n // lists, or replies) and fire a webhook on new matches. Paid-only —\n // the server returns 403 (ScopeError) for free/lapsed accounts.\n // Monitors cost 1 credit per check; audience export costs 1 credit\n // per page fetched. Max 50 monitors per user.\n\n /**\n * Create a monitor that polls X and fires a webhook on new matches.\n * @param params - `kind` + `target` (required) plus poll interval and\n * match filters.\n * @returns The created monitor (core fields only).\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async createXMonitor(params: CreateXMonitorRequest): Promise<XMonitor> {\n if (!params.kind) throw new Error(\"kind is required\");\n if (!params.target) throw new Error(\"target is required\");\n return this.post<XMonitor>(\"/v1/x/monitors\", params);\n }\n\n /**\n * List X monitors.\n * @param limit - Page size, 1..100.\n * @param offset - Page offset, >= 0.\n * @returns `{ monitors }` — an array of full monitor objects.\n */\n async listXMonitors(\n limit?: number,\n offset?: number,\n ): Promise<ListXMonitorsResponse> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<ListXMonitorsResponse>(\n `/v1/x/monitors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Get one X monitor (full object). */\n async getXMonitor(id: string): Promise<XMonitor> {\n return this.get<XMonitor>(`/v1/x/monitors/${encodeURIComponent(id)}`);\n }\n\n /**\n * Update an X monitor. Only the fields you pass are changed.\n * @returns `{ success: true }`.\n */\n async updateXMonitor(\n id: string,\n params: UpdateXMonitorRequest,\n ): Promise<XMonitorMutationResponse> {\n return this.patch<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n params,\n );\n }\n\n /**\n * Delete an X monitor.\n * @returns `{ success: true }`.\n */\n async deleteXMonitor(id: string): Promise<XMonitorMutationResponse> {\n return this.request<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n },\n );\n }\n\n /**\n * Trigger an immediate check of an X monitor. Runs in the background.\n * @returns `{ status: \"checking\" }`.\n */\n async checkXMonitor(id: string): Promise<XMonitorCheckResponse> {\n return this.post<XMonitorCheckResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n /**\n * Export an X account's followers or following — cursor-paginated and\n * metered at 1 credit per page fetched.\n *\n * Provide `handle` OR `user_id`. To walk a full audience, call\n * repeatedly, passing the returned `user_id` and `next_cursor` back in,\n * until `next_cursor` is `null`.\n *\n * @param params - `handle`/`user_id`, direction, cursor, page count.\n * @returns A page of users plus paging + billing metadata.\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async exportXAudience(\n params: ExportXAudienceRequest,\n ): Promise<ExportXAudienceResponse> {\n if (!params.handle && !params.user_id) {\n throw new Error(\"handle or user_id is required\");\n }\n return this.post<ExportXAudienceResponse>(\"/v1/x/audience\", params);\n }\n\n // -- Vertical extractor methods --\n\n /**\n * List all vertical extractors available on the server. Returns the\n * catalog as `{extractors: [{name, label, description, url_patterns}]}`.\n * Useful for building UIs that let users pick an extractor by name.\n *\n * Extractors return typed JSON specific to the target site (title,\n * price, stars, rating, etc.) rather than generic markdown.\n * See {@link scrapeVertical} to run one.\n */\n async listExtractors(): Promise<ListExtractorsResponse> {\n return this.get<ListExtractorsResponse>(\"/v1/extractors\");\n }\n\n /**\n * Run a specific vertical extractor by name.\n *\n * The server picks the parser from the `name` path parameter and\n * runs it on `url`. The response envelope is\n * `{vertical, url, data}` where `data` is an extractor-specific\n * JSON object (its fields vary per site).\n *\n * @param name - Vertical extractor name. Call {@link listExtractors}\n * to discover names. Examples: \"reddit\", \"github_repo\",\n * \"trustpilot_reviews\", \"youtube_video\", \"shopify_product\".\n * @param url - URL to extract. Must match the URL patterns the\n * extractor claims, or the server returns a 400.\n * @throws {WebclawError} On URL mismatch, unknown vertical, or\n * upstream fetch failure.\n */\n async scrapeVertical(\n name: string,\n url: string,\n ): Promise<VerticalScrapeResponse> {\n if (!name) throw new Error(\"name is required\");\n if (!url) throw new Error(\"url is required\");\n return this.post<VerticalScrapeResponse>(\n `/v1/scrape/${encodeURIComponent(name)}`,\n { url },\n );\n }\n\n // -- Internal HTTP layer --\n\n /**\n * @param timeoutMs - Per-request abort deadline. Defaults to the\n * client timeout. Pass `null` to disable the deadline entirely —\n * used for async *start* calls (crawl/research start) which can\n * legitimately take longer than the sync-call budget; their results\n * are then awaited via polling, which keeps its own timeout.\n */\n private async request<T>(\n path: string,\n init: RequestInit,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer =\n timeoutMs === null\n ? null\n : setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: controller.signal });\n } catch (err: unknown) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs ?? this.timeout);\n }\n throw new WebclawError(\n err instanceof Error ? err.message : \"Network request failed\",\n );\n } finally {\n if (timer !== null) clearTimeout(timer);\n }\n\n if (res.ok) {\n // 204, or any other success with an empty body (some endpoints\n // reply 200/202 with no content). Don't try to parse \"\" as JSON.\n const text = await res.text();\n if (text.length === 0) {\n return undefined as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new WebclawError(\"Invalid JSON in response body\", res.status);\n }\n }\n\n const body = await res.text().catch(() => null);\n const parsed = tryParseJson(body);\n const message =\n (parsed && typeof parsed === \"object\" && \"error\" in parsed\n ? String((parsed as { error: string }).error)\n : null) ??\n body ??\n res.statusText;\n\n if (res.status === 401) throw new AuthenticationError(message);\n if (res.status === 402) throw new CreditLimitError(message);\n if (res.status === 403) throw new ScopeError(message);\n if (res.status === 404) throw new NotFoundError(message);\n if (res.status === 429) {\n const retryAfter = parseRetryAfter(res.headers.get(\"retry-after\"));\n throw new RateLimitError(retryAfter);\n }\n\n throw new WebclawError(message, res.status, parsed ?? body);\n }\n\n private post<T>(\n path: string,\n body: unknown,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n return this.request<T>(\n path,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n );\n }\n\n private get<T>(path: string): Promise<T> {\n return this.request<T>(path, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n\n private patch<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>(path, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n }\n\n private del(path: string): Promise<void> {\n return this.request<void>(path, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n}\n\n/**\n * Handle for an in-progress crawl job.\n * Call `.waitForCompletion()` to poll until the crawl finishes.\n */\nexport class CrawlJob {\n constructor(\n public readonly id: string,\n private readonly client: Webclaw,\n ) {}\n\n async getStatus(): Promise<CrawlStatusResponse> {\n return this.client.getCrawlStatus(this.id);\n }\n\n async waitForCompletion(\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n\n return pollUntilDone(\n () => this.getStatus(),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n}\n\n// -- Helpers --\n\n/** Max consecutive transient poll failures (per-poll timeout or 429)\n * tolerated before giving up. The outer deadline still bounds total\n * wall time; this just stops a permanently-broken endpoint from\n * spinning until the (possibly 20-minute) deadline. */\nconst MAX_TRANSIENT_POLL_FAILURES = 5;\n\n/**\n * Polls checkFn until isDone returns true, or the outer timeout is\n * exceeded.\n *\n * A single status poll going over the per-request timeout, or a\n * transient 429, must NOT abort a long-running job (deep research can\n * legitimately take 20 min while each poll is sub-second). Such errors\n * are swallowed and the loop continues until the outer deadline, with\n * a bounded consecutive-failure cap so a persistently broken endpoint\n * still fails fast. On 429 we honour `retry-after` (capped to the time\n * left). Non-transient errors (404, 401, 5xx, network) propagate\n * immediately.\n */\nasync function pollUntilDone<T>(\n checkFn: () => Promise<T>,\n isDone: (result: T) => boolean,\n options: { interval: number; timeout: number },\n): Promise<T> {\n const deadline = Date.now() + options.timeout;\n let transientFailures = 0;\n\n while (true) {\n let waitMs = options.interval;\n try {\n const result = await checkFn();\n transientFailures = 0;\n if (isDone(result)) return result;\n } catch (err: unknown) {\n if (!isTransientPollError(err)) throw err;\n if (++transientFailures > MAX_TRANSIENT_POLL_FAILURES) {\n throw new WebclawError(\n `Polling failed after ${MAX_TRANSIENT_POLL_FAILURES} consecutive transient errors: ${\n err instanceof Error ? err.message : \"unknown error\"\n }`,\n );\n }\n // On 429, back off for the server-advised window if present.\n if (err instanceof RateLimitError && err.retryAfter != null) {\n waitMs = Math.max(waitMs, err.retryAfter * 1_000);\n }\n }\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) throw new WebclawError(\"Polling timed out\");\n await sleep(Math.min(waitMs, remaining));\n }\n}\n\n/** A per-poll timeout or a 429 is transient: the job may still finish,\n * so the poll loop should retry rather than abort. */\nfunction isTransientPollError(err: unknown): boolean {\n return err instanceof TimeoutError || err instanceof RateLimitError;\n}\n\n/** Detect abort errors across runtimes (browser DOMException vs Node 18 plain Error). */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error && err.name === \"AbortError\") return true;\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction tryParseJson(text: string | null): unknown {\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction parseRetryAfter(header: string | null): number | null {\n if (!header) return null;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : null;\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export { Webclaw, CrawlJob } from \"./client.js\";\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\n","/**\n * Error hierarchy for the Webclaw SDK.\n * All errors extend WebclawError so callers can catch broadly or narrowly.\n */\n\nexport class WebclawError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly body?: unknown,\n ) {\n super(message);\n this.name = \"WebclawError\";\n }\n}\n\nexport class AuthenticationError extends WebclawError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, 401);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class CreditLimitError extends WebclawError {\n constructor(message = \"Credit limit reached\") {\n super(message, 402);\n this.name = \"CreditLimitError\";\n }\n}\n\nexport class ScopeError extends WebclawError {\n constructor(message = \"API key lacks the required scope\") {\n super(message, 403);\n this.name = \"ScopeError\";\n }\n}\n\nexport class RateLimitError extends WebclawError {\n public readonly retryAfter: number | null;\n\n constructor(retryAfterSeconds: number | null = null) {\n super(\"Rate limit exceeded\", 429);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfterSeconds;\n }\n}\n\nexport class NotFoundError extends WebclawError {\n constructor(message = \"Resource not found\") {\n super(message, 404);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class TimeoutError extends WebclawError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"TimeoutError\";\n }\n}\n","/**\n * Webclaw SDK client. Wraps the webclaw REST API with typed methods,\n * timeout support, and a clean error hierarchy.\n */\n\nimport {\n AuthenticationError,\n CreditLimitError,\n NotFoundError,\n RateLimitError,\n ScopeError,\n TimeoutError,\n WebclawError,\n} from \"./errors.js\";\nimport type {\n BatchRequest,\n BatchResponse,\n BrandRequest,\n BrandResponse,\n CrawlPollOptions,\n CrawlRequest,\n CrawlStartResponse,\n CrawlStatusResponse,\n DiffRequest,\n DiffResponse,\n EndpointsRequest,\n EndpointsResponse,\n ExtractRequest,\n ExtractResponse,\n LeadBatchOptions,\n LeadBatchPollOptions,\n LeadBatchResponse,\n LeadBatchStartResponse,\n LeadOptions,\n LeadResponse,\n MapRequest,\n MapResponse,\n ResearchPollOptions,\n ResearchRequest,\n ResearchResponse,\n ResearchStartResponse,\n ScrapeRequest,\n ScrapeResponse,\n SearchRequest,\n SearchResponse,\n SummarizeRequest,\n SummarizeResponse,\n WatchCreateRequest,\n WatchResponse,\n WebclawConfig,\n ListExtractorsResponse,\n VerticalScrapeResponse,\n CreateXMonitorRequest,\n UpdateXMonitorRequest,\n XMonitor,\n ListXMonitorsResponse,\n XMonitorMutationResponse,\n XMonitorCheckResponse,\n ExportXAudienceRequest,\n ExportXAudienceResponse,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.webclaw.io\";\nconst DEFAULT_TIMEOUT = 30_000;\n\nexport class Webclaw {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: WebclawConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n // -- Public API methods --\n\n /**\n * Scrape a single URL and extract its content.\n * @param params - URL and extraction options (formats, selectors, caching).\n * @returns Extracted content in the requested formats.\n * @throws {WebclawError} On network or API errors.\n */\n async scrape(params: ScrapeRequest): Promise<ScrapeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ScrapeResponse>(\"/v1/scrape\", params);\n }\n\n /**\n * Start an async crawl job that discovers and scrapes pages from a root URL.\n * @param params - Root URL and crawl limits (depth, max pages).\n * @returns A CrawlJob handle for polling or waiting.\n * @throws {WebclawError} On network or API errors.\n */\n async crawl(params: CrawlRequest): Promise<CrawlJob> {\n if (!params.url) throw new Error(\"url is required\");\n // Async start: no per-request timeout. The job is awaited via\n // polling (which keeps its own deadline), not this call.\n const res = await this.post<CrawlStartResponse>(\"/v1/crawl\", params, null);\n return new CrawlJob(res.id, this);\n }\n\n /**\n * Get the current status and partial results of a crawl job.\n * @param id - Crawl job ID returned by {@link crawl}.\n * @returns Current status, page count, and any completed pages.\n * @throws {NotFoundError} If the crawl job does not exist.\n */\n async getCrawlStatus(id: string): Promise<CrawlStatusResponse> {\n return this.get<CrawlStatusResponse>(`/v1/crawl/${encodeURIComponent(id)}`);\n }\n\n /**\n * Discover URLs from a site's sitemap.\n * @param params - The root URL to map.\n * @returns List of discovered URLs and total count.\n */\n async map(params: MapRequest): Promise<MapResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<MapResponse>(\"/v1/map\", params);\n }\n\n /**\n * Discover API endpoints embedded in a page's JavaScript.\n *\n * Scans the page's inline `<script>` bodies plus its `<script src>`\n * bundles for request paths, absolute URLs, GraphQL, and WebSocket\n * endpoints — the API surface that {@link map} (sitemap-based)\n * cannot see. Credit cost: 2.\n *\n * SECURITY: the returned `endpoints`/`hosts` are extracted from\n * attacker-influenced page content and are NOT sanitized by the SDK.\n * Do not feed any returned `value`/`source` into another fetch,\n * shell, eval, or SQL without your own validation. See\n * {@link DiscoveredEndpoint}.\n *\n * @param params - URL plus optional third-party / bundle-count opts.\n * @returns Discovered endpoints, hosts, and scan counters.\n * @throws {WebclawError} On network or API errors (400 if `url` is\n * missing or invalid).\n */\n async endpoints(params: EndpointsRequest): Promise<EndpointsResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<EndpointsResponse>(\"/v1/endpoints\", params);\n }\n\n /**\n * Scrape multiple URLs in parallel.\n * @param params - Array of URLs, optional formats and concurrency limit.\n * @returns Results for each URL (success or per-URL error).\n */\n async batch(params: BatchRequest): Promise<BatchResponse> {\n if (!params.urls?.length) throw new Error(\"urls must be a non-empty array\");\n return this.post<BatchResponse>(\"/v1/batch\", params);\n }\n\n /**\n * Extract structured data from a page using an LLM.\n * @param params - URL plus a JSON schema or natural-language prompt.\n * @returns Extracted data matching the requested schema.\n */\n async extract(params: ExtractRequest): Promise<ExtractResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ExtractResponse>(\"/v1/extract\", params);\n }\n\n /**\n * Enrich a company lead from its website using an LLM.\n *\n * Fetches the URL and returns a structured company profile — name,\n * summary, socials, tech stack, pricing, and contact emails, plus\n * `people`: founders and team members, each with their LinkedIn and X\n * links where found (`people_source` records how they were sourced).\n *\n * Pricing: a flat 100 credits per successful lead.\n *\n * @param url - The company website to enrich.\n * @param options - Cache control (`no_cache`).\n * @returns The enriched lead plus its cache status and billing.\n */\n async lead(url: string, options: LeadOptions = {}): Promise<LeadResponse> {\n if (!url) throw new Error(\"url is required\");\n return this.post<LeadResponse>(\"/v1/lead\", { url, ...options });\n }\n\n /**\n * Start an async batch that enriches up to 25 company leads at once.\n *\n * Each URL is enriched into the same structured profile that\n * {@link lead} returns. The job runs in the background: this call\n * returns immediately with a job id and the number of URLs accepted\n * (the server validates and dedupes `urls`). Poll {@link getLeadBatch}\n * — or block via {@link waitForLeadBatch} — for results.\n *\n * Pricing: a flat 100 credits per *successful* lead; errored URLs are\n * not billed.\n *\n * @param urls - 1..25 company website URLs. Validated/deduped server-side.\n * @param options - Cache control (`no_cache`).\n * @returns The job id, accepted URL count, and per-URL credit cost.\n * @throws {CreditLimitError} On insufficient credits or an inactive plan (402).\n * @throws {WebclawError} If `urls` is empty or has more than 25 entries (400).\n */\n async leadBatch(\n urls: string[],\n options: LeadBatchOptions = {},\n ): Promise<LeadBatchStartResponse> {\n if (!urls?.length) throw new Error(\"urls must be a non-empty array\");\n // Async start: no per-request timeout. Results are awaited via polling\n // ({@link getLeadBatch}/{@link waitForLeadBatch}), which keeps its own deadline.\n return this.post<LeadBatchStartResponse>(\n \"/v1/lead/batch\",\n { urls, ...options },\n null,\n );\n }\n\n /**\n * Get the current status and results of a lead-batch job without waiting.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @returns Current status, counts, billing, and any per-URL results so far.\n * @throws {NotFoundError} If the batch job does not exist or isn't yours (404).\n */\n async getLeadBatch(id: string): Promise<LeadBatchResponse> {\n return this.get<LeadBatchResponse>(\n `/v1/lead/batch/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * Poll a lead-batch job by id until it's `completed` or `failed`.\n * Same ergonomics as {@link waitForCrawl} / {@link waitForResearch}.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @param opts - Polling interval and max wait override.\n * @returns The finished job with all per-URL results and final billing.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForLeadBatch(\n id: string,\n opts: LeadBatchPollOptions = {},\n ): Promise<LeadBatchResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 600_000;\n return pollUntilDone(\n () => this.getLeadBatch(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Generate a concise summary of a page's content.\n * @param params - URL and optional max sentence count.\n * @returns The generated summary text.\n */\n async summarize(params: SummarizeRequest): Promise<SummarizeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<SummarizeResponse>(\"/v1/summarize\", params);\n }\n\n /**\n * Extract brand identity information (name, logo, colors) from a URL.\n * @param params - The URL to analyze.\n * @returns Brand data as a flexible object (shape depends on the site).\n */\n async brand(params: BrandRequest): Promise<BrandResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<BrandResponse>(\"/v1/brand\", params);\n }\n\n /**\n * Perform a web search query, optionally scraping each result page.\n * @param params - Search query, result count, and optional scrape/format options.\n * @returns Search results with optional scraped content per hit.\n */\n async search(params: SearchRequest): Promise<SearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n return this.post<SearchResponse>(\"/v1/search\", params);\n }\n\n /**\n * Detect content changes on a page since a previous snapshot.\n * @param params - URL and optional previous state to diff against.\n * @returns Detected changes between the two states.\n */\n async diff(params: DiffRequest): Promise<DiffResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<DiffResponse>(\"/v1/diff\", params);\n }\n\n /**\n * Start a research job and poll until completion.\n * Every job runs in deep mode server-side, so the default poll timeout\n * is 20 minutes; pass `opts.maxWait` to override.\n * @param params - Research query and depth options.\n * @param opts - Polling interval and max wait override.\n * @returns Completed research report, sources, and findings.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async research(\n params: ResearchRequest,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n // Async start: no per-request timeout. Completion is awaited via\n // polling below, which enforces its own (interval, maxWait) deadline.\n const start = await this.post<ResearchStartResponse>(\n \"/v1/research\",\n params,\n null,\n );\n\n const interval = opts.interval ?? 2_000;\n // The API runs every research job in deep mode (the deprecated\n // `params.deep` flag is ignored), so always default to the 20-minute\n // window. An explicit opts.maxWait still wins.\n const maxWait = opts.maxWait ?? 1_200_000;\n\n return pollUntilDone(\n () => this.getResearchStatus(start.id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a research job by id until it's `completed` or `failed`.\n * Mirrors the ergonomics of `sdk-go.WaitForResearch` for callers who\n * already have an id (e.g. persisted from an earlier `/v1/research`\n * call on another process) and want to block-until-done without\n * restarting the job via {@link research}.\n *\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForResearch(\n id: string,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n const interval = opts.interval ?? 2_000;\n // Every research job runs in deep mode, so use the same 20-minute\n // default window as {@link research}.\n const maxWait = opts.maxWait ?? 1_200_000;\n return pollUntilDone(\n () => this.getResearchStatus(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a crawl job by id until it's `completed` or `failed`.\n * Same semantics as {@link CrawlJob#waitForCompletion} but for callers\n * who have only the id (e.g. persisted or returned from another process).\n * Mirrors `sdk-go.WaitForCompletion`.\n */\n async waitForCrawl(\n id: string,\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n return pollUntilDone(\n () => this.getCrawlStatus(id),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Get the current status of a research job without waiting.\n * @param id - Research job ID returned when starting research.\n * @returns Current status and any partial/complete results.\n * @throws {NotFoundError} If the research job does not exist.\n */\n async getResearchStatus(id: string): Promise<ResearchResponse> {\n return this.get<ResearchResponse>(`/v1/research/${encodeURIComponent(id)}`);\n }\n\n // -- Watch methods --\n\n async watchCreate(params: WatchCreateRequest): Promise<WatchResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<WatchResponse>(\"/v1/watch\", params);\n }\n\n async watchList(limit?: number, offset?: number): Promise<WatchResponse[]> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<WatchResponse[]>(`/v1/watch${qs ? `?${qs}` : \"\"}`);\n }\n\n async watchGet(id: string): Promise<WatchResponse> {\n return this.get<WatchResponse>(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchDelete(id: string): Promise<void> {\n await this.del(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchCheck(id: string): Promise<WatchResponse> {\n return this.post<WatchResponse>(\n `/v1/watch/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n // -- X (Twitter) monitor methods --\n //\n // The X analog of the watch endpoints: poll X (profiles, searches,\n // lists, or replies) and fire a webhook on new matches. Paid-only —\n // the server returns 403 (ScopeError) for free/lapsed accounts.\n // Monitors cost 1 credit per check; audience export costs 1 credit\n // per page fetched. Max 50 monitors per user.\n\n /**\n * Create a monitor that polls X and fires a webhook on new matches.\n * @param params - `kind` + `target` (required) plus poll interval and\n * match filters.\n * @returns The created monitor (core fields only).\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async createXMonitor(params: CreateXMonitorRequest): Promise<XMonitor> {\n if (!params.kind) throw new Error(\"kind is required\");\n if (!params.target) throw new Error(\"target is required\");\n return this.post<XMonitor>(\"/v1/x/monitors\", params);\n }\n\n /**\n * List X monitors.\n * @param limit - Page size, 1..100.\n * @param offset - Page offset, >= 0.\n * @returns `{ monitors }` — an array of full monitor objects.\n */\n async listXMonitors(\n limit?: number,\n offset?: number,\n ): Promise<ListXMonitorsResponse> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<ListXMonitorsResponse>(\n `/v1/x/monitors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Get one X monitor (full object). */\n async getXMonitor(id: string): Promise<XMonitor> {\n return this.get<XMonitor>(`/v1/x/monitors/${encodeURIComponent(id)}`);\n }\n\n /**\n * Update an X monitor. Only the fields you pass are changed.\n * @returns `{ success: true }`.\n */\n async updateXMonitor(\n id: string,\n params: UpdateXMonitorRequest,\n ): Promise<XMonitorMutationResponse> {\n return this.patch<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n params,\n );\n }\n\n /**\n * Delete an X monitor.\n * @returns `{ success: true }`.\n */\n async deleteXMonitor(id: string): Promise<XMonitorMutationResponse> {\n return this.request<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n },\n );\n }\n\n /**\n * Trigger an immediate check of an X monitor. Runs in the background.\n * @returns `{ status: \"checking\" }`.\n */\n async checkXMonitor(id: string): Promise<XMonitorCheckResponse> {\n return this.post<XMonitorCheckResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n /**\n * Export an X account's followers or following — cursor-paginated and\n * metered at 1 credit per page fetched.\n *\n * Provide `handle` OR `user_id`. To walk a full audience, call\n * repeatedly, passing the returned `user_id` and `next_cursor` back in,\n * until `next_cursor` is `null`.\n *\n * @param params - `handle`/`user_id`, direction, cursor, page count.\n * @returns A page of users plus paging + billing metadata.\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async exportXAudience(\n params: ExportXAudienceRequest,\n ): Promise<ExportXAudienceResponse> {\n if (!params.handle && !params.user_id) {\n throw new Error(\"handle or user_id is required\");\n }\n return this.post<ExportXAudienceResponse>(\"/v1/x/audience\", params);\n }\n\n // -- Vertical extractor methods --\n\n /**\n * List all vertical extractors available on the server. Returns the\n * catalog as `{extractors: [{name, label, description, url_patterns}]}`.\n * Useful for building UIs that let users pick an extractor by name.\n *\n * Extractors return typed JSON specific to the target site (title,\n * price, stars, rating, etc.) rather than generic markdown.\n * See {@link scrapeVertical} to run one.\n */\n async listExtractors(): Promise<ListExtractorsResponse> {\n return this.get<ListExtractorsResponse>(\"/v1/extractors\");\n }\n\n /**\n * Run a specific vertical extractor by name.\n *\n * The server picks the parser from the `name` path parameter and\n * runs it on `url`. The response envelope is\n * `{vertical, url, data}` where `data` is an extractor-specific\n * JSON object (its fields vary per site).\n *\n * @param name - Vertical extractor name. Call {@link listExtractors}\n * to discover names. Examples: \"reddit\", \"github_repo\",\n * \"trustpilot_reviews\", \"youtube_video\", \"shopify_product\".\n * @param url - URL to extract. Must match the URL patterns the\n * extractor claims, or the server returns a 400.\n * @throws {WebclawError} On URL mismatch, unknown vertical, or\n * upstream fetch failure.\n */\n async scrapeVertical(\n name: string,\n url: string,\n ): Promise<VerticalScrapeResponse> {\n if (!name) throw new Error(\"name is required\");\n if (!url) throw new Error(\"url is required\");\n return this.post<VerticalScrapeResponse>(\n `/v1/scrape/${encodeURIComponent(name)}`,\n { url },\n );\n }\n\n // -- Internal HTTP layer --\n\n /**\n * @param timeoutMs - Per-request abort deadline. Defaults to the\n * client timeout. Pass `null` to disable the deadline entirely —\n * used for async *start* calls (crawl/research start) which can\n * legitimately take longer than the sync-call budget; their results\n * are then awaited via polling, which keeps its own timeout.\n */\n private async request<T>(\n path: string,\n init: RequestInit,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer =\n timeoutMs === null\n ? null\n : setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: controller.signal });\n } catch (err: unknown) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs ?? this.timeout);\n }\n throw new WebclawError(\n err instanceof Error ? err.message : \"Network request failed\",\n );\n } finally {\n if (timer !== null) clearTimeout(timer);\n }\n\n if (res.ok) {\n // 204, or any other success with an empty body (some endpoints\n // reply 200/202 with no content). Don't try to parse \"\" as JSON.\n const text = await res.text();\n if (text.length === 0) {\n return undefined as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new WebclawError(\"Invalid JSON in response body\", res.status);\n }\n }\n\n const body = await res.text().catch(() => null);\n const parsed = tryParseJson(body);\n const message =\n (parsed && typeof parsed === \"object\" && \"error\" in parsed\n ? String((parsed as { error: string }).error)\n : null) ??\n body ??\n res.statusText;\n\n if (res.status === 401) throw new AuthenticationError(message);\n if (res.status === 402) throw new CreditLimitError(message);\n if (res.status === 403) throw new ScopeError(message);\n if (res.status === 404) throw new NotFoundError(message);\n if (res.status === 429) {\n const retryAfter = parseRetryAfter(res.headers.get(\"retry-after\"));\n throw new RateLimitError(retryAfter);\n }\n\n throw new WebclawError(message, res.status, parsed ?? body);\n }\n\n private post<T>(\n path: string,\n body: unknown,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n return this.request<T>(\n path,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n );\n }\n\n private get<T>(path: string): Promise<T> {\n return this.request<T>(path, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n\n private patch<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>(path, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n }\n\n private del(path: string): Promise<void> {\n return this.request<void>(path, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n}\n\n/**\n * Handle for an in-progress crawl job.\n * Call `.waitForCompletion()` to poll until the crawl finishes.\n */\nexport class CrawlJob {\n constructor(\n public readonly id: string,\n private readonly client: Webclaw,\n ) {}\n\n async getStatus(): Promise<CrawlStatusResponse> {\n return this.client.getCrawlStatus(this.id);\n }\n\n async waitForCompletion(\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n\n return pollUntilDone(\n () => this.getStatus(),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n}\n\n// -- Helpers --\n\n/** Max consecutive transient poll failures (per-poll timeout or 429)\n * tolerated before giving up. The outer deadline still bounds total\n * wall time; this just stops a permanently-broken endpoint from\n * spinning until the (possibly 20-minute) deadline. */\nconst MAX_TRANSIENT_POLL_FAILURES = 5;\n\n/**\n * Polls checkFn until isDone returns true, or the outer timeout is\n * exceeded.\n *\n * A single status poll going over the per-request timeout, or a\n * transient 429, must NOT abort a long-running job (deep research can\n * legitimately take 20 min while each poll is sub-second). Such errors\n * are swallowed and the loop continues until the outer deadline, with\n * a bounded consecutive-failure cap so a persistently broken endpoint\n * still fails fast. On 429 we honour `retry-after` (capped to the time\n * left). Non-transient errors (404, 401, 5xx, network) propagate\n * immediately.\n */\nasync function pollUntilDone<T>(\n checkFn: () => Promise<T>,\n isDone: (result: T) => boolean,\n options: { interval: number; timeout: number },\n): Promise<T> {\n const deadline = Date.now() + options.timeout;\n let transientFailures = 0;\n\n while (true) {\n let waitMs = options.interval;\n try {\n const result = await checkFn();\n transientFailures = 0;\n if (isDone(result)) return result;\n } catch (err: unknown) {\n if (!isTransientPollError(err)) throw err;\n if (++transientFailures > MAX_TRANSIENT_POLL_FAILURES) {\n throw new WebclawError(\n `Polling failed after ${MAX_TRANSIENT_POLL_FAILURES} consecutive transient errors: ${\n err instanceof Error ? err.message : \"unknown error\"\n }`,\n );\n }\n // On 429, back off for the server-advised window if present.\n if (err instanceof RateLimitError && err.retryAfter != null) {\n waitMs = Math.max(waitMs, err.retryAfter * 1_000);\n }\n }\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) throw new WebclawError(\"Polling timed out\");\n await sleep(Math.min(waitMs, remaining));\n }\n}\n\n/** A per-poll timeout or a 429 is transient: the job may still finish,\n * so the poll loop should retry rather than abort. */\nfunction isTransientPollError(err: unknown): boolean {\n return err instanceof TimeoutError || err instanceof RateLimitError;\n}\n\n/** Detect abort errors across runtimes (browser DOMException vs Node 18 plain Error). */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error && err.name === \"AbortError\") return true;\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction tryParseJson(text: string | null): unknown {\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction parseRetryAfter(header: string | null): number | null {\n if (!header) return null;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : null;\n}\n"],"mappings":"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","names":[]} |
+3
-3
@@ -253,3 +253,4 @@ // src/errors.ts | ||
| * Start a research job and poll until completion. | ||
| * Deep research uses a 20-minute timeout by default; normal uses 10 minutes. | ||
| * Every job runs in deep mode server-side, so the default poll timeout | ||
| * is 20 minutes; pass `opts.maxWait` to override. | ||
| * @param params - Research query and depth options. | ||
@@ -268,4 +269,3 @@ * @param opts - Polling interval and max wait override. | ||
| const interval = opts.interval ?? 2e3; | ||
| const defaultMax = params.deep ? 12e5 : 6e5; | ||
| const maxWait = opts.maxWait ?? defaultMax; | ||
| const maxWait = opts.maxWait ?? 12e5; | ||
| return pollUntilDone( | ||
@@ -272,0 +272,0 @@ () => this.getResearchStatus(start.id), |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["/**\n * Error hierarchy for the Webclaw SDK.\n * All errors extend WebclawError so callers can catch broadly or narrowly.\n */\n\nexport class WebclawError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly body?: unknown,\n ) {\n super(message);\n this.name = \"WebclawError\";\n }\n}\n\nexport class AuthenticationError extends WebclawError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, 401);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class CreditLimitError extends WebclawError {\n constructor(message = \"Credit limit reached\") {\n super(message, 402);\n this.name = \"CreditLimitError\";\n }\n}\n\nexport class ScopeError extends WebclawError {\n constructor(message = \"API key lacks the required scope\") {\n super(message, 403);\n this.name = \"ScopeError\";\n }\n}\n\nexport class RateLimitError extends WebclawError {\n public readonly retryAfter: number | null;\n\n constructor(retryAfterSeconds: number | null = null) {\n super(\"Rate limit exceeded\", 429);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfterSeconds;\n }\n}\n\nexport class NotFoundError extends WebclawError {\n constructor(message = \"Resource not found\") {\n super(message, 404);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class TimeoutError extends WebclawError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"TimeoutError\";\n }\n}\n","/**\n * Webclaw SDK client. Wraps the webclaw REST API with typed methods,\n * timeout support, and a clean error hierarchy.\n */\n\nimport {\n AuthenticationError,\n CreditLimitError,\n NotFoundError,\n RateLimitError,\n ScopeError,\n TimeoutError,\n WebclawError,\n} from \"./errors.js\";\nimport type {\n BatchRequest,\n BatchResponse,\n BrandRequest,\n BrandResponse,\n CrawlPollOptions,\n CrawlRequest,\n CrawlStartResponse,\n CrawlStatusResponse,\n DiffRequest,\n DiffResponse,\n EndpointsRequest,\n EndpointsResponse,\n ExtractRequest,\n ExtractResponse,\n LeadBatchOptions,\n LeadBatchPollOptions,\n LeadBatchResponse,\n LeadBatchStartResponse,\n LeadOptions,\n LeadResponse,\n MapRequest,\n MapResponse,\n ResearchPollOptions,\n ResearchRequest,\n ResearchResponse,\n ResearchStartResponse,\n ScrapeRequest,\n ScrapeResponse,\n SearchRequest,\n SearchResponse,\n SummarizeRequest,\n SummarizeResponse,\n WatchCreateRequest,\n WatchResponse,\n WebclawConfig,\n ListExtractorsResponse,\n VerticalScrapeResponse,\n CreateXMonitorRequest,\n UpdateXMonitorRequest,\n XMonitor,\n ListXMonitorsResponse,\n XMonitorMutationResponse,\n XMonitorCheckResponse,\n ExportXAudienceRequest,\n ExportXAudienceResponse,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.webclaw.io\";\nconst DEFAULT_TIMEOUT = 30_000;\n\nexport class Webclaw {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: WebclawConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n // -- Public API methods --\n\n /**\n * Scrape a single URL and extract its content.\n * @param params - URL and extraction options (formats, selectors, caching).\n * @returns Extracted content in the requested formats.\n * @throws {WebclawError} On network or API errors.\n */\n async scrape(params: ScrapeRequest): Promise<ScrapeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ScrapeResponse>(\"/v1/scrape\", params);\n }\n\n /**\n * Start an async crawl job that discovers and scrapes pages from a root URL.\n * @param params - Root URL and crawl limits (depth, max pages).\n * @returns A CrawlJob handle for polling or waiting.\n * @throws {WebclawError} On network or API errors.\n */\n async crawl(params: CrawlRequest): Promise<CrawlJob> {\n if (!params.url) throw new Error(\"url is required\");\n // Async start: no per-request timeout. The job is awaited via\n // polling (which keeps its own deadline), not this call.\n const res = await this.post<CrawlStartResponse>(\"/v1/crawl\", params, null);\n return new CrawlJob(res.id, this);\n }\n\n /**\n * Get the current status and partial results of a crawl job.\n * @param id - Crawl job ID returned by {@link crawl}.\n * @returns Current status, page count, and any completed pages.\n * @throws {NotFoundError} If the crawl job does not exist.\n */\n async getCrawlStatus(id: string): Promise<CrawlStatusResponse> {\n return this.get<CrawlStatusResponse>(`/v1/crawl/${encodeURIComponent(id)}`);\n }\n\n /**\n * Discover URLs from a site's sitemap.\n * @param params - The root URL to map.\n * @returns List of discovered URLs and total count.\n */\n async map(params: MapRequest): Promise<MapResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<MapResponse>(\"/v1/map\", params);\n }\n\n /**\n * Discover API endpoints embedded in a page's JavaScript.\n *\n * Scans the page's inline `<script>` bodies plus its `<script src>`\n * bundles for request paths, absolute URLs, GraphQL, and WebSocket\n * endpoints — the API surface that {@link map} (sitemap-based)\n * cannot see. Credit cost: 2.\n *\n * SECURITY: the returned `endpoints`/`hosts` are extracted from\n * attacker-influenced page content and are NOT sanitized by the SDK.\n * Do not feed any returned `value`/`source` into another fetch,\n * shell, eval, or SQL without your own validation. See\n * {@link DiscoveredEndpoint}.\n *\n * @param params - URL plus optional third-party / bundle-count opts.\n * @returns Discovered endpoints, hosts, and scan counters.\n * @throws {WebclawError} On network or API errors (400 if `url` is\n * missing or invalid).\n */\n async endpoints(params: EndpointsRequest): Promise<EndpointsResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<EndpointsResponse>(\"/v1/endpoints\", params);\n }\n\n /**\n * Scrape multiple URLs in parallel.\n * @param params - Array of URLs, optional formats and concurrency limit.\n * @returns Results for each URL (success or per-URL error).\n */\n async batch(params: BatchRequest): Promise<BatchResponse> {\n if (!params.urls?.length) throw new Error(\"urls must be a non-empty array\");\n return this.post<BatchResponse>(\"/v1/batch\", params);\n }\n\n /**\n * Extract structured data from a page using an LLM.\n * @param params - URL plus a JSON schema or natural-language prompt.\n * @returns Extracted data matching the requested schema.\n */\n async extract(params: ExtractRequest): Promise<ExtractResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ExtractResponse>(\"/v1/extract\", params);\n }\n\n /**\n * Enrich a company lead from its website using an LLM.\n *\n * Fetches the URL and returns a structured company profile — name,\n * summary, socials, tech stack, pricing, and contact emails, plus\n * `people`: founders and team members, each with their LinkedIn and X\n * links where found (`people_source` records how they were sourced).\n *\n * Pricing: a flat 100 credits per successful lead.\n *\n * @param url - The company website to enrich.\n * @param options - Cache control (`no_cache`).\n * @returns The enriched lead plus its cache status and billing.\n */\n async lead(url: string, options: LeadOptions = {}): Promise<LeadResponse> {\n if (!url) throw new Error(\"url is required\");\n return this.post<LeadResponse>(\"/v1/lead\", { url, ...options });\n }\n\n /**\n * Start an async batch that enriches up to 25 company leads at once.\n *\n * Each URL is enriched into the same structured profile that\n * {@link lead} returns. The job runs in the background: this call\n * returns immediately with a job id and the number of URLs accepted\n * (the server validates and dedupes `urls`). Poll {@link getLeadBatch}\n * — or block via {@link waitForLeadBatch} — for results.\n *\n * Pricing: a flat 100 credits per *successful* lead; errored URLs are\n * not billed.\n *\n * @param urls - 1..25 company website URLs. Validated/deduped server-side.\n * @param options - Cache control (`no_cache`).\n * @returns The job id, accepted URL count, and per-URL credit cost.\n * @throws {CreditLimitError} On insufficient credits or an inactive plan (402).\n * @throws {WebclawError} If `urls` is empty or has more than 25 entries (400).\n */\n async leadBatch(\n urls: string[],\n options: LeadBatchOptions = {},\n ): Promise<LeadBatchStartResponse> {\n if (!urls?.length) throw new Error(\"urls must be a non-empty array\");\n // Async start: no per-request timeout. Results are awaited via polling\n // ({@link getLeadBatch}/{@link waitForLeadBatch}), which keeps its own deadline.\n return this.post<LeadBatchStartResponse>(\n \"/v1/lead/batch\",\n { urls, ...options },\n null,\n );\n }\n\n /**\n * Get the current status and results of a lead-batch job without waiting.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @returns Current status, counts, billing, and any per-URL results so far.\n * @throws {NotFoundError} If the batch job does not exist or isn't yours (404).\n */\n async getLeadBatch(id: string): Promise<LeadBatchResponse> {\n return this.get<LeadBatchResponse>(\n `/v1/lead/batch/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * Poll a lead-batch job by id until it's `completed` or `failed`.\n * Same ergonomics as {@link waitForCrawl} / {@link waitForResearch}.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @param opts - Polling interval and max wait override.\n * @returns The finished job with all per-URL results and final billing.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForLeadBatch(\n id: string,\n opts: LeadBatchPollOptions = {},\n ): Promise<LeadBatchResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 600_000;\n return pollUntilDone(\n () => this.getLeadBatch(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Generate a concise summary of a page's content.\n * @param params - URL and optional max sentence count.\n * @returns The generated summary text.\n */\n async summarize(params: SummarizeRequest): Promise<SummarizeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<SummarizeResponse>(\"/v1/summarize\", params);\n }\n\n /**\n * Extract brand identity information (name, logo, colors) from a URL.\n * @param params - The URL to analyze.\n * @returns Brand data as a flexible object (shape depends on the site).\n */\n async brand(params: BrandRequest): Promise<BrandResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<BrandResponse>(\"/v1/brand\", params);\n }\n\n /**\n * Perform a web search query, optionally scraping each result page.\n * @param params - Search query, result count, and optional scrape/format options.\n * @returns Search results with optional scraped content per hit.\n */\n async search(params: SearchRequest): Promise<SearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n return this.post<SearchResponse>(\"/v1/search\", params);\n }\n\n /**\n * Detect content changes on a page since a previous snapshot.\n * @param params - URL and optional previous state to diff against.\n * @returns Detected changes between the two states.\n */\n async diff(params: DiffRequest): Promise<DiffResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<DiffResponse>(\"/v1/diff\", params);\n }\n\n /**\n * Start a research job and poll until completion.\n * Deep research uses a 20-minute timeout by default; normal uses 10 minutes.\n * @param params - Research query and depth options.\n * @param opts - Polling interval and max wait override.\n * @returns Completed research report, sources, and findings.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async research(\n params: ResearchRequest,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n // Async start: no per-request timeout. Completion is awaited via\n // polling below, which enforces its own (interval, maxWait) deadline.\n const start = await this.post<ResearchStartResponse>(\n \"/v1/research\",\n params,\n null,\n );\n\n const interval = opts.interval ?? 2_000;\n const defaultMax = params.deep ? 1_200_000 : 600_000;\n const maxWait = opts.maxWait ?? defaultMax;\n\n return pollUntilDone(\n () => this.getResearchStatus(start.id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a research job by id until it's `completed` or `failed`.\n * Mirrors the ergonomics of `sdk-go.WaitForResearch` for callers who\n * already have an id (e.g. persisted from an earlier `/v1/research`\n * call on another process) and want to block-until-done without\n * restarting the job via {@link research}.\n *\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForResearch(\n id: string,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n const interval = opts.interval ?? 2_000;\n // No `deep` hint here since we don't have the original request;\n // pick the longer window so shallow jobs finish comfortably.\n const maxWait = opts.maxWait ?? 1_200_000;\n return pollUntilDone(\n () => this.getResearchStatus(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a crawl job by id until it's `completed` or `failed`.\n * Same semantics as {@link CrawlJob#waitForCompletion} but for callers\n * who have only the id (e.g. persisted or returned from another process).\n * Mirrors `sdk-go.WaitForCompletion`.\n */\n async waitForCrawl(\n id: string,\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n return pollUntilDone(\n () => this.getCrawlStatus(id),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Get the current status of a research job without waiting.\n * @param id - Research job ID returned when starting research.\n * @returns Current status and any partial/complete results.\n * @throws {NotFoundError} If the research job does not exist.\n */\n async getResearchStatus(id: string): Promise<ResearchResponse> {\n return this.get<ResearchResponse>(`/v1/research/${encodeURIComponent(id)}`);\n }\n\n // -- Watch methods --\n\n async watchCreate(params: WatchCreateRequest): Promise<WatchResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<WatchResponse>(\"/v1/watch\", params);\n }\n\n async watchList(limit?: number, offset?: number): Promise<WatchResponse[]> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<WatchResponse[]>(`/v1/watch${qs ? `?${qs}` : \"\"}`);\n }\n\n async watchGet(id: string): Promise<WatchResponse> {\n return this.get<WatchResponse>(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchDelete(id: string): Promise<void> {\n await this.del(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchCheck(id: string): Promise<WatchResponse> {\n return this.post<WatchResponse>(\n `/v1/watch/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n // -- X (Twitter) monitor methods --\n //\n // The X analog of the watch endpoints: poll X (profiles, searches,\n // lists, or replies) and fire a webhook on new matches. Paid-only —\n // the server returns 403 (ScopeError) for free/lapsed accounts.\n // Monitors cost 1 credit per check; audience export costs 1 credit\n // per page fetched. Max 50 monitors per user.\n\n /**\n * Create a monitor that polls X and fires a webhook on new matches.\n * @param params - `kind` + `target` (required) plus poll interval and\n * match filters.\n * @returns The created monitor (core fields only).\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async createXMonitor(params: CreateXMonitorRequest): Promise<XMonitor> {\n if (!params.kind) throw new Error(\"kind is required\");\n if (!params.target) throw new Error(\"target is required\");\n return this.post<XMonitor>(\"/v1/x/monitors\", params);\n }\n\n /**\n * List X monitors.\n * @param limit - Page size, 1..100.\n * @param offset - Page offset, >= 0.\n * @returns `{ monitors }` — an array of full monitor objects.\n */\n async listXMonitors(\n limit?: number,\n offset?: number,\n ): Promise<ListXMonitorsResponse> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<ListXMonitorsResponse>(\n `/v1/x/monitors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Get one X monitor (full object). */\n async getXMonitor(id: string): Promise<XMonitor> {\n return this.get<XMonitor>(`/v1/x/monitors/${encodeURIComponent(id)}`);\n }\n\n /**\n * Update an X monitor. Only the fields you pass are changed.\n * @returns `{ success: true }`.\n */\n async updateXMonitor(\n id: string,\n params: UpdateXMonitorRequest,\n ): Promise<XMonitorMutationResponse> {\n return this.patch<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n params,\n );\n }\n\n /**\n * Delete an X monitor.\n * @returns `{ success: true }`.\n */\n async deleteXMonitor(id: string): Promise<XMonitorMutationResponse> {\n return this.request<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n },\n );\n }\n\n /**\n * Trigger an immediate check of an X monitor. Runs in the background.\n * @returns `{ status: \"checking\" }`.\n */\n async checkXMonitor(id: string): Promise<XMonitorCheckResponse> {\n return this.post<XMonitorCheckResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n /**\n * Export an X account's followers or following — cursor-paginated and\n * metered at 1 credit per page fetched.\n *\n * Provide `handle` OR `user_id`. To walk a full audience, call\n * repeatedly, passing the returned `user_id` and `next_cursor` back in,\n * until `next_cursor` is `null`.\n *\n * @param params - `handle`/`user_id`, direction, cursor, page count.\n * @returns A page of users plus paging + billing metadata.\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async exportXAudience(\n params: ExportXAudienceRequest,\n ): Promise<ExportXAudienceResponse> {\n if (!params.handle && !params.user_id) {\n throw new Error(\"handle or user_id is required\");\n }\n return this.post<ExportXAudienceResponse>(\"/v1/x/audience\", params);\n }\n\n // -- Vertical extractor methods --\n\n /**\n * List all vertical extractors available on the server. Returns the\n * catalog as `{extractors: [{name, label, description, url_patterns}]}`.\n * Useful for building UIs that let users pick an extractor by name.\n *\n * Extractors return typed JSON specific to the target site (title,\n * price, stars, rating, etc.) rather than generic markdown.\n * See {@link scrapeVertical} to run one.\n */\n async listExtractors(): Promise<ListExtractorsResponse> {\n return this.get<ListExtractorsResponse>(\"/v1/extractors\");\n }\n\n /**\n * Run a specific vertical extractor by name.\n *\n * The server picks the parser from the `name` path parameter and\n * runs it on `url`. The response envelope is\n * `{vertical, url, data}` where `data` is an extractor-specific\n * JSON object (its fields vary per site).\n *\n * @param name - Vertical extractor name. Call {@link listExtractors}\n * to discover names. Examples: \"reddit\", \"github_repo\",\n * \"trustpilot_reviews\", \"youtube_video\", \"shopify_product\".\n * @param url - URL to extract. Must match the URL patterns the\n * extractor claims, or the server returns a 400.\n * @throws {WebclawError} On URL mismatch, unknown vertical, or\n * upstream fetch failure.\n */\n async scrapeVertical(\n name: string,\n url: string,\n ): Promise<VerticalScrapeResponse> {\n if (!name) throw new Error(\"name is required\");\n if (!url) throw new Error(\"url is required\");\n return this.post<VerticalScrapeResponse>(\n `/v1/scrape/${encodeURIComponent(name)}`,\n { url },\n );\n }\n\n // -- Internal HTTP layer --\n\n /**\n * @param timeoutMs - Per-request abort deadline. Defaults to the\n * client timeout. Pass `null` to disable the deadline entirely —\n * used for async *start* calls (crawl/research start) which can\n * legitimately take longer than the sync-call budget; their results\n * are then awaited via polling, which keeps its own timeout.\n */\n private async request<T>(\n path: string,\n init: RequestInit,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer =\n timeoutMs === null\n ? null\n : setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: controller.signal });\n } catch (err: unknown) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs ?? this.timeout);\n }\n throw new WebclawError(\n err instanceof Error ? err.message : \"Network request failed\",\n );\n } finally {\n if (timer !== null) clearTimeout(timer);\n }\n\n if (res.ok) {\n // 204, or any other success with an empty body (some endpoints\n // reply 200/202 with no content). Don't try to parse \"\" as JSON.\n const text = await res.text();\n if (text.length === 0) {\n return undefined as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new WebclawError(\"Invalid JSON in response body\", res.status);\n }\n }\n\n const body = await res.text().catch(() => null);\n const parsed = tryParseJson(body);\n const message =\n (parsed && typeof parsed === \"object\" && \"error\" in parsed\n ? String((parsed as { error: string }).error)\n : null) ??\n body ??\n res.statusText;\n\n if (res.status === 401) throw new AuthenticationError(message);\n if (res.status === 402) throw new CreditLimitError(message);\n if (res.status === 403) throw new ScopeError(message);\n if (res.status === 404) throw new NotFoundError(message);\n if (res.status === 429) {\n const retryAfter = parseRetryAfter(res.headers.get(\"retry-after\"));\n throw new RateLimitError(retryAfter);\n }\n\n throw new WebclawError(message, res.status, parsed ?? body);\n }\n\n private post<T>(\n path: string,\n body: unknown,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n return this.request<T>(\n path,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n );\n }\n\n private get<T>(path: string): Promise<T> {\n return this.request<T>(path, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n\n private patch<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>(path, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n }\n\n private del(path: string): Promise<void> {\n return this.request<void>(path, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n}\n\n/**\n * Handle for an in-progress crawl job.\n * Call `.waitForCompletion()` to poll until the crawl finishes.\n */\nexport class CrawlJob {\n constructor(\n public readonly id: string,\n private readonly client: Webclaw,\n ) {}\n\n async getStatus(): Promise<CrawlStatusResponse> {\n return this.client.getCrawlStatus(this.id);\n }\n\n async waitForCompletion(\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n\n return pollUntilDone(\n () => this.getStatus(),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n}\n\n// -- Helpers --\n\n/** Max consecutive transient poll failures (per-poll timeout or 429)\n * tolerated before giving up. The outer deadline still bounds total\n * wall time; this just stops a permanently-broken endpoint from\n * spinning until the (possibly 20-minute) deadline. */\nconst MAX_TRANSIENT_POLL_FAILURES = 5;\n\n/**\n * Polls checkFn until isDone returns true, or the outer timeout is\n * exceeded.\n *\n * A single status poll going over the per-request timeout, or a\n * transient 429, must NOT abort a long-running job (deep research can\n * legitimately take 20 min while each poll is sub-second). Such errors\n * are swallowed and the loop continues until the outer deadline, with\n * a bounded consecutive-failure cap so a persistently broken endpoint\n * still fails fast. On 429 we honour `retry-after` (capped to the time\n * left). Non-transient errors (404, 401, 5xx, network) propagate\n * immediately.\n */\nasync function pollUntilDone<T>(\n checkFn: () => Promise<T>,\n isDone: (result: T) => boolean,\n options: { interval: number; timeout: number },\n): Promise<T> {\n const deadline = Date.now() + options.timeout;\n let transientFailures = 0;\n\n while (true) {\n let waitMs = options.interval;\n try {\n const result = await checkFn();\n transientFailures = 0;\n if (isDone(result)) return result;\n } catch (err: unknown) {\n if (!isTransientPollError(err)) throw err;\n if (++transientFailures > MAX_TRANSIENT_POLL_FAILURES) {\n throw new WebclawError(\n `Polling failed after ${MAX_TRANSIENT_POLL_FAILURES} consecutive transient errors: ${\n err instanceof Error ? err.message : \"unknown error\"\n }`,\n );\n }\n // On 429, back off for the server-advised window if present.\n if (err instanceof RateLimitError && err.retryAfter != null) {\n waitMs = Math.max(waitMs, err.retryAfter * 1_000);\n }\n }\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) throw new WebclawError(\"Polling timed out\");\n await sleep(Math.min(waitMs, remaining));\n }\n}\n\n/** A per-poll timeout or a 429 is transient: the job may still finish,\n * so the poll loop should retry rather than abort. */\nfunction isTransientPollError(err: unknown): boolean {\n return err instanceof TimeoutError || err instanceof RateLimitError;\n}\n\n/** Detect abort errors across runtimes (browser DOMException vs Node 18 plain Error). */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error && err.name === \"AbortError\") return true;\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction tryParseJson(text: string | null): unknown {\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction parseRetryAfter(header: string | null): number | null {\n if (!header) return null;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : null;\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["/**\n * Error hierarchy for the Webclaw SDK.\n * All errors extend WebclawError so callers can catch broadly or narrowly.\n */\n\nexport class WebclawError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly body?: unknown,\n ) {\n super(message);\n this.name = \"WebclawError\";\n }\n}\n\nexport class AuthenticationError extends WebclawError {\n constructor(message = \"Invalid or missing API key\") {\n super(message, 401);\n this.name = \"AuthenticationError\";\n }\n}\n\nexport class CreditLimitError extends WebclawError {\n constructor(message = \"Credit limit reached\") {\n super(message, 402);\n this.name = \"CreditLimitError\";\n }\n}\n\nexport class ScopeError extends WebclawError {\n constructor(message = \"API key lacks the required scope\") {\n super(message, 403);\n this.name = \"ScopeError\";\n }\n}\n\nexport class RateLimitError extends WebclawError {\n public readonly retryAfter: number | null;\n\n constructor(retryAfterSeconds: number | null = null) {\n super(\"Rate limit exceeded\", 429);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfterSeconds;\n }\n}\n\nexport class NotFoundError extends WebclawError {\n constructor(message = \"Resource not found\") {\n super(message, 404);\n this.name = \"NotFoundError\";\n }\n}\n\nexport class TimeoutError extends WebclawError {\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"TimeoutError\";\n }\n}\n","/**\n * Webclaw SDK client. Wraps the webclaw REST API with typed methods,\n * timeout support, and a clean error hierarchy.\n */\n\nimport {\n AuthenticationError,\n CreditLimitError,\n NotFoundError,\n RateLimitError,\n ScopeError,\n TimeoutError,\n WebclawError,\n} from \"./errors.js\";\nimport type {\n BatchRequest,\n BatchResponse,\n BrandRequest,\n BrandResponse,\n CrawlPollOptions,\n CrawlRequest,\n CrawlStartResponse,\n CrawlStatusResponse,\n DiffRequest,\n DiffResponse,\n EndpointsRequest,\n EndpointsResponse,\n ExtractRequest,\n ExtractResponse,\n LeadBatchOptions,\n LeadBatchPollOptions,\n LeadBatchResponse,\n LeadBatchStartResponse,\n LeadOptions,\n LeadResponse,\n MapRequest,\n MapResponse,\n ResearchPollOptions,\n ResearchRequest,\n ResearchResponse,\n ResearchStartResponse,\n ScrapeRequest,\n ScrapeResponse,\n SearchRequest,\n SearchResponse,\n SummarizeRequest,\n SummarizeResponse,\n WatchCreateRequest,\n WatchResponse,\n WebclawConfig,\n ListExtractorsResponse,\n VerticalScrapeResponse,\n CreateXMonitorRequest,\n UpdateXMonitorRequest,\n XMonitor,\n ListXMonitorsResponse,\n XMonitorMutationResponse,\n XMonitorCheckResponse,\n ExportXAudienceRequest,\n ExportXAudienceResponse,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.webclaw.io\";\nconst DEFAULT_TIMEOUT = 30_000;\n\nexport class Webclaw {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: WebclawConfig) {\n if (!config.apiKey) throw new Error(\"apiKey is required\");\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n // -- Public API methods --\n\n /**\n * Scrape a single URL and extract its content.\n * @param params - URL and extraction options (formats, selectors, caching).\n * @returns Extracted content in the requested formats.\n * @throws {WebclawError} On network or API errors.\n */\n async scrape(params: ScrapeRequest): Promise<ScrapeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ScrapeResponse>(\"/v1/scrape\", params);\n }\n\n /**\n * Start an async crawl job that discovers and scrapes pages from a root URL.\n * @param params - Root URL and crawl limits (depth, max pages).\n * @returns A CrawlJob handle for polling or waiting.\n * @throws {WebclawError} On network or API errors.\n */\n async crawl(params: CrawlRequest): Promise<CrawlJob> {\n if (!params.url) throw new Error(\"url is required\");\n // Async start: no per-request timeout. The job is awaited via\n // polling (which keeps its own deadline), not this call.\n const res = await this.post<CrawlStartResponse>(\"/v1/crawl\", params, null);\n return new CrawlJob(res.id, this);\n }\n\n /**\n * Get the current status and partial results of a crawl job.\n * @param id - Crawl job ID returned by {@link crawl}.\n * @returns Current status, page count, and any completed pages.\n * @throws {NotFoundError} If the crawl job does not exist.\n */\n async getCrawlStatus(id: string): Promise<CrawlStatusResponse> {\n return this.get<CrawlStatusResponse>(`/v1/crawl/${encodeURIComponent(id)}`);\n }\n\n /**\n * Discover URLs from a site's sitemap.\n * @param params - The root URL to map.\n * @returns List of discovered URLs and total count.\n */\n async map(params: MapRequest): Promise<MapResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<MapResponse>(\"/v1/map\", params);\n }\n\n /**\n * Discover API endpoints embedded in a page's JavaScript.\n *\n * Scans the page's inline `<script>` bodies plus its `<script src>`\n * bundles for request paths, absolute URLs, GraphQL, and WebSocket\n * endpoints — the API surface that {@link map} (sitemap-based)\n * cannot see. Credit cost: 2.\n *\n * SECURITY: the returned `endpoints`/`hosts` are extracted from\n * attacker-influenced page content and are NOT sanitized by the SDK.\n * Do not feed any returned `value`/`source` into another fetch,\n * shell, eval, or SQL without your own validation. See\n * {@link DiscoveredEndpoint}.\n *\n * @param params - URL plus optional third-party / bundle-count opts.\n * @returns Discovered endpoints, hosts, and scan counters.\n * @throws {WebclawError} On network or API errors (400 if `url` is\n * missing or invalid).\n */\n async endpoints(params: EndpointsRequest): Promise<EndpointsResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<EndpointsResponse>(\"/v1/endpoints\", params);\n }\n\n /**\n * Scrape multiple URLs in parallel.\n * @param params - Array of URLs, optional formats and concurrency limit.\n * @returns Results for each URL (success or per-URL error).\n */\n async batch(params: BatchRequest): Promise<BatchResponse> {\n if (!params.urls?.length) throw new Error(\"urls must be a non-empty array\");\n return this.post<BatchResponse>(\"/v1/batch\", params);\n }\n\n /**\n * Extract structured data from a page using an LLM.\n * @param params - URL plus a JSON schema or natural-language prompt.\n * @returns Extracted data matching the requested schema.\n */\n async extract(params: ExtractRequest): Promise<ExtractResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<ExtractResponse>(\"/v1/extract\", params);\n }\n\n /**\n * Enrich a company lead from its website using an LLM.\n *\n * Fetches the URL and returns a structured company profile — name,\n * summary, socials, tech stack, pricing, and contact emails, plus\n * `people`: founders and team members, each with their LinkedIn and X\n * links where found (`people_source` records how they were sourced).\n *\n * Pricing: a flat 100 credits per successful lead.\n *\n * @param url - The company website to enrich.\n * @param options - Cache control (`no_cache`).\n * @returns The enriched lead plus its cache status and billing.\n */\n async lead(url: string, options: LeadOptions = {}): Promise<LeadResponse> {\n if (!url) throw new Error(\"url is required\");\n return this.post<LeadResponse>(\"/v1/lead\", { url, ...options });\n }\n\n /**\n * Start an async batch that enriches up to 25 company leads at once.\n *\n * Each URL is enriched into the same structured profile that\n * {@link lead} returns. The job runs in the background: this call\n * returns immediately with a job id and the number of URLs accepted\n * (the server validates and dedupes `urls`). Poll {@link getLeadBatch}\n * — or block via {@link waitForLeadBatch} — for results.\n *\n * Pricing: a flat 100 credits per *successful* lead; errored URLs are\n * not billed.\n *\n * @param urls - 1..25 company website URLs. Validated/deduped server-side.\n * @param options - Cache control (`no_cache`).\n * @returns The job id, accepted URL count, and per-URL credit cost.\n * @throws {CreditLimitError} On insufficient credits or an inactive plan (402).\n * @throws {WebclawError} If `urls` is empty or has more than 25 entries (400).\n */\n async leadBatch(\n urls: string[],\n options: LeadBatchOptions = {},\n ): Promise<LeadBatchStartResponse> {\n if (!urls?.length) throw new Error(\"urls must be a non-empty array\");\n // Async start: no per-request timeout. Results are awaited via polling\n // ({@link getLeadBatch}/{@link waitForLeadBatch}), which keeps its own deadline.\n return this.post<LeadBatchStartResponse>(\n \"/v1/lead/batch\",\n { urls, ...options },\n null,\n );\n }\n\n /**\n * Get the current status and results of a lead-batch job without waiting.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @returns Current status, counts, billing, and any per-URL results so far.\n * @throws {NotFoundError} If the batch job does not exist or isn't yours (404).\n */\n async getLeadBatch(id: string): Promise<LeadBatchResponse> {\n return this.get<LeadBatchResponse>(\n `/v1/lead/batch/${encodeURIComponent(id)}`,\n );\n }\n\n /**\n * Poll a lead-batch job by id until it's `completed` or `failed`.\n * Same ergonomics as {@link waitForCrawl} / {@link waitForResearch}.\n * @param id - Batch job id returned by {@link leadBatch}.\n * @param opts - Polling interval and max wait override.\n * @returns The finished job with all per-URL results and final billing.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForLeadBatch(\n id: string,\n opts: LeadBatchPollOptions = {},\n ): Promise<LeadBatchResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 600_000;\n return pollUntilDone(\n () => this.getLeadBatch(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Generate a concise summary of a page's content.\n * @param params - URL and optional max sentence count.\n * @returns The generated summary text.\n */\n async summarize(params: SummarizeRequest): Promise<SummarizeResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<SummarizeResponse>(\"/v1/summarize\", params);\n }\n\n /**\n * Extract brand identity information (name, logo, colors) from a URL.\n * @param params - The URL to analyze.\n * @returns Brand data as a flexible object (shape depends on the site).\n */\n async brand(params: BrandRequest): Promise<BrandResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<BrandResponse>(\"/v1/brand\", params);\n }\n\n /**\n * Perform a web search query, optionally scraping each result page.\n * @param params - Search query, result count, and optional scrape/format options.\n * @returns Search results with optional scraped content per hit.\n */\n async search(params: SearchRequest): Promise<SearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n return this.post<SearchResponse>(\"/v1/search\", params);\n }\n\n /**\n * Detect content changes on a page since a previous snapshot.\n * @param params - URL and optional previous state to diff against.\n * @returns Detected changes between the two states.\n */\n async diff(params: DiffRequest): Promise<DiffResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<DiffResponse>(\"/v1/diff\", params);\n }\n\n /**\n * Start a research job and poll until completion.\n * Every job runs in deep mode server-side, so the default poll timeout\n * is 20 minutes; pass `opts.maxWait` to override.\n * @param params - Research query and depth options.\n * @param opts - Polling interval and max wait override.\n * @returns Completed research report, sources, and findings.\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async research(\n params: ResearchRequest,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n if (!params.query) throw new Error(\"query is required\");\n // Async start: no per-request timeout. Completion is awaited via\n // polling below, which enforces its own (interval, maxWait) deadline.\n const start = await this.post<ResearchStartResponse>(\n \"/v1/research\",\n params,\n null,\n );\n\n const interval = opts.interval ?? 2_000;\n // The API runs every research job in deep mode (the deprecated\n // `params.deep` flag is ignored), so always default to the 20-minute\n // window. An explicit opts.maxWait still wins.\n const maxWait = opts.maxWait ?? 1_200_000;\n\n return pollUntilDone(\n () => this.getResearchStatus(start.id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a research job by id until it's `completed` or `failed`.\n * Mirrors the ergonomics of `sdk-go.WaitForResearch` for callers who\n * already have an id (e.g. persisted from an earlier `/v1/research`\n * call on another process) and want to block-until-done without\n * restarting the job via {@link research}.\n *\n * @throws {WebclawError} If polling times out before the job finishes.\n */\n async waitForResearch(\n id: string,\n opts: ResearchPollOptions = {},\n ): Promise<ResearchResponse> {\n const interval = opts.interval ?? 2_000;\n // Every research job runs in deep mode, so use the same 20-minute\n // default window as {@link research}.\n const maxWait = opts.maxWait ?? 1_200_000;\n return pollUntilDone(\n () => this.getResearchStatus(id),\n (r) => r.status === \"completed\" || r.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Poll a crawl job by id until it's `completed` or `failed`.\n * Same semantics as {@link CrawlJob#waitForCompletion} but for callers\n * who have only the id (e.g. persisted or returned from another process).\n * Mirrors `sdk-go.WaitForCompletion`.\n */\n async waitForCrawl(\n id: string,\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n return pollUntilDone(\n () => this.getCrawlStatus(id),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n\n /**\n * Get the current status of a research job without waiting.\n * @param id - Research job ID returned when starting research.\n * @returns Current status and any partial/complete results.\n * @throws {NotFoundError} If the research job does not exist.\n */\n async getResearchStatus(id: string): Promise<ResearchResponse> {\n return this.get<ResearchResponse>(`/v1/research/${encodeURIComponent(id)}`);\n }\n\n // -- Watch methods --\n\n async watchCreate(params: WatchCreateRequest): Promise<WatchResponse> {\n if (!params.url) throw new Error(\"url is required\");\n return this.post<WatchResponse>(\"/v1/watch\", params);\n }\n\n async watchList(limit?: number, offset?: number): Promise<WatchResponse[]> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<WatchResponse[]>(`/v1/watch${qs ? `?${qs}` : \"\"}`);\n }\n\n async watchGet(id: string): Promise<WatchResponse> {\n return this.get<WatchResponse>(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchDelete(id: string): Promise<void> {\n await this.del(`/v1/watch/${encodeURIComponent(id)}`);\n }\n\n async watchCheck(id: string): Promise<WatchResponse> {\n return this.post<WatchResponse>(\n `/v1/watch/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n // -- X (Twitter) monitor methods --\n //\n // The X analog of the watch endpoints: poll X (profiles, searches,\n // lists, or replies) and fire a webhook on new matches. Paid-only —\n // the server returns 403 (ScopeError) for free/lapsed accounts.\n // Monitors cost 1 credit per check; audience export costs 1 credit\n // per page fetched. Max 50 monitors per user.\n\n /**\n * Create a monitor that polls X and fires a webhook on new matches.\n * @param params - `kind` + `target` (required) plus poll interval and\n * match filters.\n * @returns The created monitor (core fields only).\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async createXMonitor(params: CreateXMonitorRequest): Promise<XMonitor> {\n if (!params.kind) throw new Error(\"kind is required\");\n if (!params.target) throw new Error(\"target is required\");\n return this.post<XMonitor>(\"/v1/x/monitors\", params);\n }\n\n /**\n * List X monitors.\n * @param limit - Page size, 1..100.\n * @param offset - Page offset, >= 0.\n * @returns `{ monitors }` — an array of full monitor objects.\n */\n async listXMonitors(\n limit?: number,\n offset?: number,\n ): Promise<ListXMonitorsResponse> {\n const query = new URLSearchParams();\n if (limit !== undefined) query.set(\"limit\", String(limit));\n if (offset !== undefined) query.set(\"offset\", String(offset));\n const qs = query.toString();\n return this.get<ListXMonitorsResponse>(\n `/v1/x/monitors${qs ? `?${qs}` : \"\"}`,\n );\n }\n\n /** Get one X monitor (full object). */\n async getXMonitor(id: string): Promise<XMonitor> {\n return this.get<XMonitor>(`/v1/x/monitors/${encodeURIComponent(id)}`);\n }\n\n /**\n * Update an X monitor. Only the fields you pass are changed.\n * @returns `{ success: true }`.\n */\n async updateXMonitor(\n id: string,\n params: UpdateXMonitorRequest,\n ): Promise<XMonitorMutationResponse> {\n return this.patch<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n params,\n );\n }\n\n /**\n * Delete an X monitor.\n * @returns `{ success: true }`.\n */\n async deleteXMonitor(id: string): Promise<XMonitorMutationResponse> {\n return this.request<XMonitorMutationResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}`,\n {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n },\n );\n }\n\n /**\n * Trigger an immediate check of an X monitor. Runs in the background.\n * @returns `{ status: \"checking\" }`.\n */\n async checkXMonitor(id: string): Promise<XMonitorCheckResponse> {\n return this.post<XMonitorCheckResponse>(\n `/v1/x/monitors/${encodeURIComponent(id)}/check`,\n {},\n );\n }\n\n /**\n * Export an X account's followers or following — cursor-paginated and\n * metered at 1 credit per page fetched.\n *\n * Provide `handle` OR `user_id`. To walk a full audience, call\n * repeatedly, passing the returned `user_id` and `next_cursor` back in,\n * until `next_cursor` is `null`.\n *\n * @param params - `handle`/`user_id`, direction, cursor, page count.\n * @returns A page of users plus paging + billing metadata.\n * @throws {ScopeError} On free/lapsed accounts (403).\n */\n async exportXAudience(\n params: ExportXAudienceRequest,\n ): Promise<ExportXAudienceResponse> {\n if (!params.handle && !params.user_id) {\n throw new Error(\"handle or user_id is required\");\n }\n return this.post<ExportXAudienceResponse>(\"/v1/x/audience\", params);\n }\n\n // -- Vertical extractor methods --\n\n /**\n * List all vertical extractors available on the server. Returns the\n * catalog as `{extractors: [{name, label, description, url_patterns}]}`.\n * Useful for building UIs that let users pick an extractor by name.\n *\n * Extractors return typed JSON specific to the target site (title,\n * price, stars, rating, etc.) rather than generic markdown.\n * See {@link scrapeVertical} to run one.\n */\n async listExtractors(): Promise<ListExtractorsResponse> {\n return this.get<ListExtractorsResponse>(\"/v1/extractors\");\n }\n\n /**\n * Run a specific vertical extractor by name.\n *\n * The server picks the parser from the `name` path parameter and\n * runs it on `url`. The response envelope is\n * `{vertical, url, data}` where `data` is an extractor-specific\n * JSON object (its fields vary per site).\n *\n * @param name - Vertical extractor name. Call {@link listExtractors}\n * to discover names. Examples: \"reddit\", \"github_repo\",\n * \"trustpilot_reviews\", \"youtube_video\", \"shopify_product\".\n * @param url - URL to extract. Must match the URL patterns the\n * extractor claims, or the server returns a 400.\n * @throws {WebclawError} On URL mismatch, unknown vertical, or\n * upstream fetch failure.\n */\n async scrapeVertical(\n name: string,\n url: string,\n ): Promise<VerticalScrapeResponse> {\n if (!name) throw new Error(\"name is required\");\n if (!url) throw new Error(\"url is required\");\n return this.post<VerticalScrapeResponse>(\n `/v1/scrape/${encodeURIComponent(name)}`,\n { url },\n );\n }\n\n // -- Internal HTTP layer --\n\n /**\n * @param timeoutMs - Per-request abort deadline. Defaults to the\n * client timeout. Pass `null` to disable the deadline entirely —\n * used for async *start* calls (crawl/research start) which can\n * legitimately take longer than the sync-call budget; their results\n * are then awaited via polling, which keeps its own timeout.\n */\n private async request<T>(\n path: string,\n init: RequestInit,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const controller = new AbortController();\n const timer =\n timeoutMs === null\n ? null\n : setTimeout(() => controller.abort(), timeoutMs);\n\n let res: Response;\n try {\n res = await fetch(url, { ...init, signal: controller.signal });\n } catch (err: unknown) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs ?? this.timeout);\n }\n throw new WebclawError(\n err instanceof Error ? err.message : \"Network request failed\",\n );\n } finally {\n if (timer !== null) clearTimeout(timer);\n }\n\n if (res.ok) {\n // 204, or any other success with an empty body (some endpoints\n // reply 200/202 with no content). Don't try to parse \"\" as JSON.\n const text = await res.text();\n if (text.length === 0) {\n return undefined as T;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new WebclawError(\"Invalid JSON in response body\", res.status);\n }\n }\n\n const body = await res.text().catch(() => null);\n const parsed = tryParseJson(body);\n const message =\n (parsed && typeof parsed === \"object\" && \"error\" in parsed\n ? String((parsed as { error: string }).error)\n : null) ??\n body ??\n res.statusText;\n\n if (res.status === 401) throw new AuthenticationError(message);\n if (res.status === 402) throw new CreditLimitError(message);\n if (res.status === 403) throw new ScopeError(message);\n if (res.status === 404) throw new NotFoundError(message);\n if (res.status === 429) {\n const retryAfter = parseRetryAfter(res.headers.get(\"retry-after\"));\n throw new RateLimitError(retryAfter);\n }\n\n throw new WebclawError(message, res.status, parsed ?? body);\n }\n\n private post<T>(\n path: string,\n body: unknown,\n timeoutMs: number | null = this.timeout,\n ): Promise<T> {\n return this.request<T>(\n path,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n },\n timeoutMs,\n );\n }\n\n private get<T>(path: string): Promise<T> {\n return this.request<T>(path, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n\n private patch<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>(path, {\n method: \"PATCH\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n }\n\n private del(path: string): Promise<void> {\n return this.request<void>(path, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n }\n}\n\n/**\n * Handle for an in-progress crawl job.\n * Call `.waitForCompletion()` to poll until the crawl finishes.\n */\nexport class CrawlJob {\n constructor(\n public readonly id: string,\n private readonly client: Webclaw,\n ) {}\n\n async getStatus(): Promise<CrawlStatusResponse> {\n return this.client.getCrawlStatus(this.id);\n }\n\n async waitForCompletion(\n opts: CrawlPollOptions = {},\n ): Promise<CrawlStatusResponse> {\n const interval = opts.interval ?? 2_000;\n const maxWait = opts.maxWait ?? 300_000;\n\n return pollUntilDone(\n () => this.getStatus(),\n (s) => s.status === \"completed\" || s.status === \"failed\",\n { interval, timeout: maxWait },\n );\n }\n}\n\n// -- Helpers --\n\n/** Max consecutive transient poll failures (per-poll timeout or 429)\n * tolerated before giving up. The outer deadline still bounds total\n * wall time; this just stops a permanently-broken endpoint from\n * spinning until the (possibly 20-minute) deadline. */\nconst MAX_TRANSIENT_POLL_FAILURES = 5;\n\n/**\n * Polls checkFn until isDone returns true, or the outer timeout is\n * exceeded.\n *\n * A single status poll going over the per-request timeout, or a\n * transient 429, must NOT abort a long-running job (deep research can\n * legitimately take 20 min while each poll is sub-second). Such errors\n * are swallowed and the loop continues until the outer deadline, with\n * a bounded consecutive-failure cap so a persistently broken endpoint\n * still fails fast. On 429 we honour `retry-after` (capped to the time\n * left). Non-transient errors (404, 401, 5xx, network) propagate\n * immediately.\n */\nasync function pollUntilDone<T>(\n checkFn: () => Promise<T>,\n isDone: (result: T) => boolean,\n options: { interval: number; timeout: number },\n): Promise<T> {\n const deadline = Date.now() + options.timeout;\n let transientFailures = 0;\n\n while (true) {\n let waitMs = options.interval;\n try {\n const result = await checkFn();\n transientFailures = 0;\n if (isDone(result)) return result;\n } catch (err: unknown) {\n if (!isTransientPollError(err)) throw err;\n if (++transientFailures > MAX_TRANSIENT_POLL_FAILURES) {\n throw new WebclawError(\n `Polling failed after ${MAX_TRANSIENT_POLL_FAILURES} consecutive transient errors: ${\n err instanceof Error ? err.message : \"unknown error\"\n }`,\n );\n }\n // On 429, back off for the server-advised window if present.\n if (err instanceof RateLimitError && err.retryAfter != null) {\n waitMs = Math.max(waitMs, err.retryAfter * 1_000);\n }\n }\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) throw new WebclawError(\"Polling timed out\");\n await sleep(Math.min(waitMs, remaining));\n }\n}\n\n/** A per-poll timeout or a 429 is transient: the job may still finish,\n * so the poll loop should retry rather than abort. */\nfunction isTransientPollError(err: unknown): boolean {\n return err instanceof TimeoutError || err instanceof RateLimitError;\n}\n\n/** Detect abort errors across runtimes (browser DOMException vs Node 18 plain Error). */\nfunction isAbortError(err: unknown): boolean {\n if (err instanceof DOMException && err.name === \"AbortError\") return true;\n if (err instanceof Error && err.name === \"AbortError\") return true;\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction tryParseJson(text: string | null): unknown {\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction parseRetryAfter(header: string | null): number | null {\n if (!header) return null;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : null;\n}\n"],"mappings":"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","names":[]} |
+1
-1
| { | ||
| "name": "@webclaw/sdk", | ||
| "version": "0.4.0", | ||
| "version": "0.5.0", | ||
| "description": "TypeScript SDK for the Webclaw web extraction API", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
+4
-3
@@ -254,4 +254,6 @@ <p align="center"> | ||
| Start an async deep research job. The SDK automatically polls until the job completes. | ||
| Start an async deep research job. The SDK automatically polls until the job completes (up to 20 minutes by default; override with `maxWait`). | ||
| > **Note:** every research job now runs in deep mode. The `deep` request flag is deprecated and ignored by the API — don't pass it. | ||
| ```typescript | ||
@@ -262,5 +264,4 @@ const result = await client.research( | ||
| max_sources: 15, | ||
| deep: true, | ||
| }, | ||
| { interval: 3_000, maxWait: 600_000 }, | ||
| { interval: 3_000 }, | ||
| ); | ||
@@ -267,0 +268,0 @@ |
212521
0.43%2215
0.14%581
0.17%