Sign In

@webclaw/sdk

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@webclaw/sdk - npm Package Compare versions

Comparing version
0.4.0
to
0.5.0
+5
-2
dist/index.d.mts

@@ -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.

@@ -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.

@@ -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":[]}

@@ -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":[]}
{
"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",

@@ -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 @@