serpkite 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -87,6 +87,7 @@ vertical-specific extras and `meta` (`request_id`, `credits_used`, `cached`, `la
87
87
  | `news(params)` | `POST /v1/news` | `SearchParams` | `NewsResponse` |
88
88
  | `maps(params)` | `POST /v1/maps` | `SearchParams` (+ `ll`: `"@lat,lng,14z"`) | `PlacesResponse` |
89
89
  | `places(params)` | `POST /v1/places` | `SearchParams` | `PlacesResponse` |
90
+ | `place(params)` | `POST /v1/place` | `PlaceParams` (`place_id` \| `cid` \| `fid` \| `oid` (Yandex Maps), `country`, `language`) | `PlacesResponse` (one place: `weekly_hours`, `photos`, `timezone`, …; empty when the id is unknown) |
90
91
  | `reviews(params)` | `POST /v1/reviews` | `ReviewsParams` (`place_id` \| `cid` \| `fid`, `sort`, `page_token`, `num` ≤ 50) | `ReviewsResponse` (+ `next_page_token`) |
91
92
  | `shopping(params)` | `POST /v1/shopping` | `SearchParams` | `ShoppingResponse` |
92
93
  | `scholar(params)` | `POST /v1/scholar` | `SearchParams` | `ScholarResponse` |
@@ -128,8 +129,12 @@ const lean = await sk.search({ q: "espresso", fields: "results.title,results.lin
128
129
  // Token-lean JSON for agents
129
130
  const compact = await sk.search({ q: "espresso", format: "compact" });
130
131
 
131
- // Reviews, paged
132
- let page = await sk.reviews({ place_id: "ChIJN1t_tDeuEmsRUsoyG83frY4", sort: "newest" });
132
+ // One place in full: structured hours, phones, time zone, photos
133
+ const { results } = await sk.place({ place_id: "ChIJN1t_tDeuEmsRUsoyG83frY4" });
134
+ console.log(results[0]?.timezone, results[0]?.weekly_hours?.monday); // [{ open: "09:00", close: "17:00" }]
135
+
136
+ // Reviews, paged (the first 10 of a place)
137
+ let page = await sk.reviews({ place_id: "ChIJN1t_tDeuEmsRUsoyG83frY4", num: 5 });
133
138
  while (page.next_page_token) {
134
139
  page = await sk.reviews({ place_id: "ChIJN1t_tDeuEmsRUsoyG83frY4", page_token: page.next_page_token });
135
140
  }
@@ -377,8 +382,9 @@ are never retried. `batches.create`, `crawl`, `monitors.create` and `monitors.ru
377
382
  Types are generated from the OpenAPI contract (`backend/api/serp-api.yaml`) with
378
383
  `openapi-typescript`. Friendly aliases are exported: `SearchParams`, `SearchResponse`,
379
384
  `OrganicResult`, `AnswerBox`, `NewsResponse`, `NewsResult`, `ImagesResponse`, `PlacesResponse`,
380
- `ReviewsResponse`, `ShoppingResponse`, `ScholarResponse`, `PatentsResponse`,
381
- `AutocompleteResponse`, `WebpageResponse`, `RankResponse`,
385
+ `PlaceParams`, `PlaceResult`, `WeeklyHours`, `HoursInterval`, `PlacePhoto`, `ReviewsResponse`,
386
+ `ShoppingResponse`, `ScholarResponse`, `PatentsResponse`, `AutocompleteResponse`, `WebpageResponse`,
387
+ `RankResponse`,
382
388
  `Account`, `Batch`, `BatchCreateParams`, `Meta`, `Engine`, `Provider`, `RouteStep`,
383
389
  `CompactResponse`, `MapResponse`, `ExtractResponse`, `CrawlParams`, `CrawlTask`, `CrawlResult`,
384
390
  `TaskCreated`, `Monitor`, `MonitorRun`, `MonitorPageChange`, `TaskCompletedEvent`,
package/dist/index.cjs CHANGED
@@ -50,7 +50,7 @@ function errorFromResponse(status, headers, body) {
50
50
  }
51
51
 
52
52
  // src/client.ts
53
- var VERSION = "0.4.0";
53
+ var VERSION = "0.6.0";
54
54
  var DEFAULT_BASE_URL = "https://api.serpkite.com";
55
55
  var TASK_DONE = /* @__PURE__ */ new Set(["completed", "failed", "canceled"]);
56
56
  var SerpKite = class {
@@ -98,10 +98,20 @@ var SerpKite = class {
98
98
  videos = this.#vertical("/v1/videos");
99
99
  /** Google News. */
100
100
  news = this.#vertical("/v1/news");
101
- /** Google Maps search (places with coordinates). Accepts `ll` ("@lat,lng,14z"). */
101
+ /**
102
+ * Google Maps search (places with coordinates). Accepts `ll` ("@lat,lng,14z"). With
103
+ * `engine: "yandex"` (where enabled) the places come from Yandex Maps and carry an `oid`.
104
+ */
102
105
  maps = this.#vertical("/v1/maps");
103
106
  /** Google local results. */
104
107
  places = this.#vertical("/v1/places");
108
+ /**
109
+ * Details of one place by `place_id`, `cid` or `fid`, or by a Yandex Maps `oid` (implies
110
+ * `engine: "yandex"`): address, country, time zone, hours
111
+ * (`opening_hours` text and structured `weekly_hours`), phones, description and photos.
112
+ * `results` holds the place, or is empty (not charged) when no place has that id. 1 credit.
113
+ */
114
+ place = this.#vertical("/v1/place");
105
115
  /** Reviews of a place by `place_id`, `cid` or `fid`. Page with `page_token` = `next_page_token`. */
106
116
  reviews = this.#vertical("/v1/reviews");
107
117
  /** Google Shopping. */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/util.ts","../src/webhooks.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Every failure thrown by the SDK. API errors carry the fields of the\n * `{\"error\":{\"code\",\"message\",\"request_id\"}}` envelope; network failures and\n * timeouts use `status: 0` with code `connection_error` or `timeout`.\n */\nexport class SerpKiteError extends Error {\n /** HTTP status, or 0 when no response was received. */\n readonly status: number;\n /** Machine-readable code, e.g. `unauthorized`, `insufficient_credits`, `rate_limited`. */\n readonly code: string;\n /** `X-Request-Id` of the failed request, when the API returned one. */\n readonly requestId: string | undefined;\n /** Response headers, when the API responded. */\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n code: string,\n message: string,\n requestId?: string,\n headers?: Headers,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"SerpKiteError\";\n this.status = status;\n this.code = code;\n this.requestId = requestId || undefined;\n this.headers = headers;\n }\n}\n\nconst fallbackCodes: Record<number, string> = {\n 400: \"invalid_request\",\n 401: \"unauthorized\",\n 402: \"insufficient_credits\",\n 403: \"forbidden\",\n 404: \"not_found\",\n 429: \"rate_limited\",\n // The API sends upstream failures as 503 with a JSON body; a bare 502/504\n // comes from a proxy (Cloudflare replaces origin 502/504 bodies).\n 502: \"upstream_error\",\n 503: \"unavailable\",\n 504: \"upstream_timeout\",\n};\n\n/** Builds a SerpKiteError from a non-2xx response body (JSON envelope or anything else). */\nexport function errorFromResponse(status: number, headers: Headers, body: string): SerpKiteError {\n let code = fallbackCodes[status] ?? (status >= 500 ? \"server_error\" : \"http_error\");\n let message = body.trim().slice(0, 500) || `HTTP ${status}`;\n let requestId = headers.get(\"x-request-id\") ?? undefined;\n try {\n const parsed = JSON.parse(body) as {\n error?: { code?: unknown; message?: unknown; request_id?: unknown };\n };\n const e = parsed?.error;\n if (e && typeof e === \"object\") {\n if (typeof e.code === \"string\" && e.code) code = e.code;\n if (typeof e.message === \"string\" && e.message) message = e.message;\n if (typeof e.request_id === \"string\" && e.request_id) requestId = e.request_id;\n }\n } catch {\n // Not JSON: keep the text body as the message.\n }\n return new SerpKiteError(status, code, message, requestId, headers);\n}\n","import { errorFromResponse, SerpKiteError } from \"./errors\";\nimport type {\n Account,\n AutocompleteResponse,\n Batch,\n BatchCreateEntry,\n BatchCreateParams,\n BatchCreateResponse,\n CompactResponse,\n CrawlParams,\n CrawlTask,\n ExtractParams,\n ExtractResponse,\n ImagesResponse,\n MapParams,\n MapResponse,\n Monitor,\n MonitorCreateParams,\n MonitorList,\n MonitorRun,\n MonitorRunList,\n MonitorRunsParams,\n MonitorUpdateParams,\n NewsResponse,\n PatentsResponse,\n PlacesResponse,\n RankParams,\n RankResponse,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n TaskCancelResponse,\n TaskCreated,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.4.0\";\nexport const DEFAULT_BASE_URL = \"https://api.serpkite.com\";\n\nexport interface SerpKiteOptions {\n /** API key (`skt_live_…`). Defaults to the `SERPKITE_API_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `SERPKITE_BASE_URL` or https://api.serpkite.com. */\n baseUrl?: string;\n /** Per-attempt timeout in milliseconds. Default 60000. */\n timeoutMs?: number;\n /** Retries after the first attempt on 429, 5xx and network errors. Default 2. */\n maxRetries?: number;\n /** Base delay of the exponential backoff in milliseconds. Default 500. */\n retryDelayMs?: number;\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n}\n\n/** Billing and tracing details from the response headers. */\nexport interface ResponseInfo {\n status: number;\n headers: Headers;\n requestId: string | undefined;\n creditsUsed: number | undefined;\n creditsRemaining: number | undefined;\n costUsd: number | undefined;\n /** `HIT` when served from cache (`max_age`), else `MISS`. */\n cache: \"HIT\" | \"MISS\" | undefined;\n latencyMs: number | undefined;\n tokensEstimate: number | undefined;\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n /** Overrides the client's per-attempt timeout. */\n timeoutMs?: number;\n /** Overrides the client's retry count. */\n maxRetries?: number;\n headers?: Record<string, string>;\n /** Called with the final response's headers (credits used/remaining, cache, request id). */\n onResponse?: (info: ResponseInfo) => void;\n}\n\nexport interface BatchCreateOptions extends RequestOptions {\n /**\n * Sent as `Idempotency-Key` (1-255 printable ASCII characters, e.g. a UUID).\n * For 24 hours a retry with the same key and body returns the first response\n * instead of queueing and billing the jobs again.\n */\n idempotencyKey?: string;\n}\n\nexport interface WaitOptions {\n /**\n * Give up after this many milliseconds. Default 600000 (10 minutes) for batches and\n * 2100000 (35 minutes) for `waitForCrawl`: a crawl runs for up to 30 minutes.\n */\n timeoutMs?: number;\n /** First delay between polls. Default 1000. Grows 1.5× per poll. */\n pollIntervalMs?: number;\n /** Upper bound of the delay between polls. Default 10000. */\n maxPollIntervalMs?: number;\n signal?: AbortSignal;\n}\n\n/** A vertical whose return type follows `format`: `\"markdown\"` → string, `\"compact\"` → CompactResponse. */\nexport interface VerticalMethod<P, R> {\n (params: P & { format: \"markdown\" }, options?: RequestOptions): Promise<string>;\n (params: P & { format: \"compact\" }, options?: RequestOptions): Promise<CompactResponse>;\n (params: P & { format?: \"json\" }, options?: RequestOptions): Promise<R>;\n (params: P, options?: RequestOptions): Promise<R | CompactResponse | string>;\n}\n\ntype Method = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\n/** Task states after which `waitForCrawl` returns. */\nconst TASK_DONE = new Set([\"completed\", \"failed\", \"canceled\"]);\n\ninterface Call {\n method: Method;\n path: string;\n body?: unknown;\n options?: RequestOptions;\n /** Which failures are safe to retry. `all` = 429, 5xx and network errors. */\n retry?: \"all\" | \"rate_limit\";\n}\n\nexport class SerpKite {\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly maxRetries: number;\n readonly retryDelayMs: number;\n readonly #apiKey: string;\n readonly #fetch: typeof fetch;\n readonly #headers: Record<string, string>;\n\n constructor(options: SerpKiteOptions = {}) {\n const apiKey = options.apiKey ?? env(\"SERPKITE_API_KEY\");\n if (!apiKey) {\n throw new SerpKiteError(\n 0,\n \"missing_api_key\",\n \"No API key: pass { apiKey } or set the SERPKITE_API_KEY environment variable\",\n );\n }\n this.#apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? env(\"SERPKITE_BASE_URL\") ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n \"\",\n );\n this.timeoutMs = options.timeoutMs ?? 60_000;\n this.maxRetries = Math.max(0, options.maxRetries ?? 2);\n this.retryDelayMs = options.retryDelayMs ?? 500;\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new SerpKiteError(\n 0,\n \"missing_fetch\",\n \"No global fetch: pass { fetch } (Node 18+ has one)\",\n );\n }\n this.#fetch = f;\n this.#headers = { ...options.headers };\n }\n\n // ── Verticals ─────────────────────────────────────────────────────────\n\n /** Google web search: organic results, answer box, knowledge graph, people also ask… */\n readonly search = this.#vertical<SearchParams, SearchResponse>(\"/v1/search\");\n /** Google Images. */\n readonly images = this.#vertical<SearchParams, ImagesResponse>(\"/v1/images\");\n /** Google Videos. */\n readonly videos = this.#vertical<SearchParams, VideosResponse>(\"/v1/videos\");\n /** Google News. */\n readonly news = this.#vertical<SearchParams, NewsResponse>(\"/v1/news\");\n /** Google Maps search (places with coordinates). Accepts `ll` (\"@lat,lng,14z\"). */\n readonly maps = this.#vertical<SearchParams, PlacesResponse>(\"/v1/maps\");\n /** Google local results. */\n readonly places = this.#vertical<SearchParams, PlacesResponse>(\"/v1/places\");\n /** Reviews of a place by `place_id`, `cid` or `fid`. Page with `page_token` = `next_page_token`. */\n readonly reviews = this.#vertical<ReviewsParams, ReviewsResponse>(\"/v1/reviews\");\n /** Google Shopping. */\n readonly shopping = this.#vertical<SearchParams, ShoppingResponse>(\"/v1/shopping\");\n /** Google Scholar. */\n readonly scholar = this.#vertical<SearchParams, ScholarResponse>(\"/v1/scholar\");\n /** Google Patents. */\n readonly patents = this.#vertical<SearchParams, PatentsResponse>(\"/v1/patents\");\n /** Google autocomplete suggestions. */\n readonly autocomplete = this.#vertical<SearchParams, AutocompleteResponse>(\"/v1/autocomplete\");\n /** Fetch any public URL as clean Markdown plus metadata. */\n readonly webpage = this.#vertical<WebpageParams, WebpageResponse>(\"/v1/webpage\");\n\n /** Position of `domain` for keyword `q` in the top `num` results (default 100). */\n rank(params: RankParams, options?: RequestOptions): Promise<RankResponse> {\n return this.request({ method: \"POST\", path: \"/v1/rank\", body: params, options });\n }\n\n /**\n * Read up to 20 URLs (HTML or PDF) as Markdown, text or HTML in one call, with optional\n * query-ranked `highlights` (BM25), `include_links` and `include_images`. 1 credit per URL\n * that comes back (0.5 from cache); URLs that fail are listed in `failed` and cost nothing.\n */\n extract(params: ExtractParams, options?: RequestOptions): Promise<ExtractResponse> {\n // Billed per page even when the response is lost: retried only on 429,\n // and the HTTP timeout covers the server's deadline (timeout, default 50 s) + 15 s.\n const wait = ((params.timeout ?? 50) + 15) * 1000;\n return this.request({\n method: \"POST\",\n path: \"/v1/extract\",\n body: params,\n options: {\n ...options,\n timeoutMs: options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, wait) : 0),\n },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * The URLs of a site from its sitemaps (XML, RSS/Atom, gzip, plain text) and start page,\n * canonicalised and deduplicated, optionally ranked by `search`. `include_paths` /\n * `exclude_paths` (regular expressions on the URL path) and `ignore_query_parameters`\n * narrow the list. 1 credit; free when nothing is found.\n */\n map(params: MapParams, options?: RequestOptions): Promise<MapResponse> {\n return this.request({ method: \"POST\", path: \"/v1/map\", body: params, options });\n }\n\n /** Balance, limits and this month's usage for the key's account. */\n account(options?: RequestOptions): Promise<Account> {\n return this.request({ method: \"GET\", path: \"/v1/account\", options });\n }\n\n // ── Async tasks: crawl ────────────────────────────────────────────────\n\n /**\n * Starts an async crawl of one site and returns the queued task (`202`). 1 credit per page\n * read (0.5 from cache); `limit` credits (up to 1000 pages, `max_depth` up to 10) are\n * reserved up front and the rest refunded. `sitemap` (`include` default, `only`, `skip`)\n * seeds the crawl with sitemap URLs; `query` reads the most relevant pages first. robots.txt\n * and Crawl-delay are honoured; `result.stats.stopped` says why the crawl ended (`done`,\n * `limit`, `time_limit`, `size_limit`, `too_many_failures`, `canceled`).\n * Only retried on 429, so a lost response never starts (and reserves) a second crawl.\n */\n crawl(params: CrawlParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/crawl\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a crawl (results are kept for 24 hours). */\n getCrawl(id: string, options?: RequestOptions): Promise<CrawlTask> {\n return this.request({ method: \"GET\", path: `/v1/crawl/${encodeURIComponent(id)}`, options });\n }\n\n /** Cancels a crawl: refunded if still queued, else stops at the next checkpoint. */\n cancelCrawl(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/crawl/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the crawl is `completed`, `failed` or `canceled` and returns it (a failed\n * task is returned, not thrown; check `status` and `error`). Accepts an id or the task\n * returned by `crawl`.\n */\n async waitForCrawl(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<CrawlTask> {\n return this.#waitTask(taskId(idOrTask), (id) => this.getCrawl(id, { signal: options.signal }), {\n ...options,\n timeoutMs: options.timeoutMs ?? 2_100_000,\n });\n }\n\n // ── Monitors ──────────────────────────────────────────────────────────\n\n readonly monitors = {\n /**\n * Saves a `search` or `news` request (`q`) that runs on a schedule (hourly to monthly) and\n * POSTs results it hasn't seen before to `webhook_url` (`monitor.results`). With\n * `endpoint: \"webpage\"` and a `url` it checks that page each interval (1 credit per check)\n * and reports it (`MonitorPageChange`) when its content changes. `metadata` (your JSON\n * object, at most 2 KB) is echoed on the monitor and in its webhooks. Without a\n * `webhook_url`, read new results from `monitors.runs`. `active: false`\n * creates it paused. A monitor pauses itself after 10 failed runs in a row\n * (`last_status: \"paused\"`); `update(id, { active: true })` resumes it. Each run costs\n * what its search costs. Only retried on 429.\n */\n create: (params: MonitorCreateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: \"/v1/monitors\",\n body: params,\n options,\n retry: \"rate_limit\",\n }),\n\n /** The account's monitors. */\n list: (options?: RequestOptions): Promise<MonitorList> =>\n this.request({ method: \"GET\", path: \"/v1/monitors\", options }),\n\n get: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({ method: \"GET\", path: `/v1/monitors/${encodeURIComponent(id)}`, options }),\n\n /**\n * Changes `name`, `interval` / `interval_seconds`, `webhook_url` (`\"\"` removes it),\n * `active` (`true` resumes a paused monitor), `metadata` (an object replaces it, `null`\n * clears it) or the saved request (`endpoint`, `q`, `url`, `country`, `num`, domains,\n * `engine`…). Switching `endpoint` between a search and `webpage` drops the other kind's\n * fields. A changed search reports every result as new on the next run.\n */\n update: (id: string, params: MonitorUpdateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"PATCH\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n body: params,\n options,\n }),\n\n delete: async (id: string, options?: RequestOptions): Promise<void> => {\n await this.request({\n method: \"DELETE\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n options,\n });\n },\n\n /** Runs an active monitor at the next scheduler poll (within about 30 seconds).\n * Paused monitors return 409; update(id, { active: true }) resumes and schedules them. */\n run: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: `/v1/monitors/${encodeURIComponent(id)}/run`,\n options,\n retry: \"rate_limit\",\n }),\n\n /**\n * One page of the monitor's run history, newest first (`limit` 1-100, default 20). Each\n * run carries its new results for 24 hours. Pass `next_before` as `before` for the next\n * page; it is null on the last one.\n */\n runs: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): Promise<MonitorRunList> =>\n this.request({\n method: \"GET\",\n path: `/v1/monitors/${encodeURIComponent(id)}/runs${query(params)}`,\n options,\n }),\n\n /**\n * Iterates over every run of the monitor, newest first, fetching pages of `limit` as\n * needed (starting at `before` when given).\n *\n * ```ts\n * for await (const run of sk.monitors.iterRuns(\"m1\")) console.log(run.new_results);\n * ```\n */\n iterRuns: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): AsyncIterable<MonitorRun> => this.#iterRuns(id, params, options),\n };\n\n // ── Batches ───────────────────────────────────────────────────────────\n\n readonly batches = {\n /**\n * Queue 1-100 requests for one endpoint at half price. Returns one entry per\n * request, in order: a queued `Batch` or an error object. Without an\n * `idempotencyKey` it is only retried on 429, so a lost response never queues\n * (and bills) the same requests twice. With one, 5xx and network errors are\n * retried too: the server replays the first response for the same key and body.\n */\n create: (\n params: BatchCreateParams,\n options: BatchCreateOptions = {},\n ): Promise<BatchCreateResponse> => {\n const { idempotencyKey, ...rest } = options;\n return this.request({\n method: \"POST\",\n path: \"/v1/batches\",\n body: params,\n options: idempotencyKey\n ? { ...rest, headers: { ...rest.headers, \"Idempotency-Key\": idempotencyKey } }\n : rest,\n retry: idempotencyKey ? \"all\" : \"rate_limit\",\n });\n },\n\n /** Poll one batch job. */\n get: (id: string, options?: RequestOptions): Promise<Batch> =>\n this.request({ method: \"GET\", path: `/v1/batches/${encodeURIComponent(id)}`, options }),\n\n /**\n * Poll until the job is `done` or `failed` and return it (a failed job is\n * returned, not thrown; check `status` and `error`). Accepts an id or an\n * entry from `create`; an entry that failed to queue throws its error.\n */\n wait: (idOrEntry: string | BatchCreateEntry | undefined, options: WaitOptions = {}) =>\n this.#wait(idOrEntry, options),\n };\n\n // ── Plumbing ──────────────────────────────────────────────────────────\n\n async *#iterRuns(\n id: string,\n params: MonitorRunsParams,\n options?: RequestOptions,\n ): AsyncGenerator<MonitorRun> {\n let before = params.before;\n for (;;) {\n const page = await this.monitors.runs(id, { ...params, before }, options);\n yield* page.results;\n if (!page.next_before || page.results.length === 0) return;\n before = page.next_before;\n }\n }\n\n async #waitTask<T extends { status: string }>(\n id: string,\n get: (id: string) => Promise<T>,\n o: WaitOptions,\n ): Promise<T> {\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const task = await get(id);\n if (TASK_DONE.has(task.status)) return task;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `task ${id} still ${task.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n #vertical<P, R>(path: string): VerticalMethod<P, R> {\n return ((params: P, options?: RequestOptions) =>\n this.request({ method: \"POST\", path, body: params, options })) as VerticalMethod<P, R>;\n }\n\n async #wait(idOrEntry: string | BatchCreateEntry | undefined, o: WaitOptions): Promise<Batch> {\n let id: string | undefined;\n if (typeof idOrEntry === \"string\") {\n id = idOrEntry;\n } else if (idOrEntry && \"id\" in idOrEntry && typeof idOrEntry.id === \"string\") {\n id = idOrEntry.id;\n } else if (idOrEntry && \"error\" in idOrEntry && idOrEntry.error && !(\"id\" in idOrEntry)) {\n const e = idOrEntry.error as { code?: string; message?: string; request_id?: string };\n throw new SerpKiteError(\n 400,\n e.code ?? \"invalid_request\",\n e.message ?? \"batch entry was not queued\",\n e.request_id,\n );\n }\n if (!id) {\n throw new SerpKiteError(0, \"invalid_request\", \"batches.wait needs a batch id\");\n }\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const batch = await this.batches.get(id, { signal: o.signal });\n if (batch.status === \"done\" || batch.status === \"failed\") return batch;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `batch ${id} still ${batch.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n /** Low-level request: JSON body in, parsed JSON (or text for non-JSON responses) out. */\n async request<T>(call: Call): Promise<T> {\n const { method, path, body, options = {} } = call;\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"application/json, text/markdown;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n // Browsers would turn a custom User-Agent into a CORS preflight header.\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n const init: RequestInit = {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n };\n\n for (let attempt = 0; ; attempt++) {\n const canRetry = attempt < maxRetries;\n let res: Response;\n const { signal, cleanup, timedOut } = withTimeout(options.signal, timeoutMs);\n try {\n res = await this.#fetch(this.baseUrl + path, { ...init, signal });\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const msg = timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n throw new SerpKiteError(\n 0,\n code,\n \"failed to read the response body\",\n undefined,\n res.headers,\n {\n cause: err,\n },\n );\n }\n cleanup();\n\n if (!res.ok) {\n const retryable = res.status === 429 || (res.status >= 500 && call.retry !== \"rate_limit\");\n if (canRetry && retryable) {\n await sleep(this.#retryDelay(attempt, res.headers), options.signal);\n continue;\n }\n throw errorFromResponse(res.status, res.headers, text);\n }\n\n options.onResponse?.(responseInfo(res));\n const type = res.headers.get(\"content-type\") ?? \"\";\n if (type.includes(\"json\")) {\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n res.headers.get(\"x-request-id\") ?? undefined,\n res.headers,\n { cause: err },\n );\n }\n }\n return text as T;\n }\n }\n\n #backoff(attempt: number): number {\n const base = Math.min(this.retryDelayMs * 2 ** attempt, 8_000);\n return base / 2 + Math.random() * (base / 2);\n }\n\n #retryDelay(attempt: number, headers: Headers): number {\n const after = parseRetryAfter(headers.get(\"retry-after\"));\n return after === undefined ? this.#backoff(attempt) : Math.min(after, 60_000);\n }\n}\n\n// ── helpers ─────────────────────────────────────────────────────────────\n\n/** Builds `?a=1&b=2` from the defined values (empty string when there are none). */\nfunction query(params: Record<string, string | number | boolean | undefined>): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) q.set(k, String(v));\n }\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\nfunction taskId(idOrTask: string | { id: string }): string {\n const id = typeof idOrTask === \"string\" ? idOrTask : idOrTask?.id;\n if (!id) throw new SerpKiteError(0, \"invalid_request\", \"a task id is required\");\n return id;\n}\n\n/** Parses Retry-After (seconds or an HTTP date) into milliseconds. */\nexport function parseRetryAfter(v: string | null): number | undefined {\n if (!v) return undefined;\n const secs = Number(v);\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000);\n const at = Date.parse(v);\n return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());\n}\n\nfunction responseInfo(res: Response): ResponseInfo {\n const h = res.headers;\n const num = (name: string) => {\n const v = h.get(name);\n if (v === null || v === \"\") return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n const cache = h.get(\"x-cache\")?.toUpperCase();\n return {\n status: res.status,\n headers: h,\n requestId: h.get(\"x-request-id\") ?? undefined,\n creditsUsed: num(\"x-credits-used\"),\n creditsRemaining: num(\"x-credits-remaining\"),\n costUsd: num(\"x-cost-usd\"),\n cache: cache === \"HIT\" || cache === \"MISS\" ? cache : undefined,\n latencyMs: num(\"x-latency-ms\"),\n tokensEstimate: num(\"x-tokens-estimate\"),\n };\n}\n\nfunction withTimeout(outer: AbortSignal | undefined, ms: number) {\n const ctrl = new AbortController();\n let timedOut = false;\n const onAbort = () => ctrl.abort(outer?.reason);\n if (outer?.aborted) ctrl.abort(outer.reason);\n else outer?.addEventListener(\"abort\", onAbort, { once: true });\n const timer =\n ms > 0 && Number.isFinite(ms)\n ? setTimeout(() => {\n timedOut = true;\n ctrl.abort(new SerpKiteError(0, \"timeout\", `request timed out after ${ms} ms`));\n }, ms)\n : undefined;\n return {\n signal: ctrl.signal,\n timedOut: () => timedOut,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n outer?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(signal?.reason);\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\ntype EnvHost = {\n process?: { env?: Record<string, string | undefined> };\n Deno?: { env?: { get?: (name: string) => string | undefined } };\n document?: unknown;\n};\n\nfunction env(name: string): string | undefined {\n const g = globalThis as EnvHost;\n try {\n const v = g.process?.env?.[name];\n if (v) return v;\n } catch {\n // process.env may be a throwing proxy on some edge runtimes.\n }\n try {\n return g.Deno?.env?.get?.(name) || undefined;\n } catch {\n // Deno without --allow-env.\n return undefined;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof (globalThis as EnvHost).document !== \"undefined\";\n}\n","import type { BatchCreateEntry, BatchCreateError } from \"./types\";\n\n/** True when a `batches.create` entry was rejected instead of queued. */\nexport function isBatchError(entry: BatchCreateEntry): entry is BatchCreateError {\n return !(\"id\" in entry && typeof entry.id === \"string\") && \"error\" in entry && !!entry.error;\n}\n","import { SerpKiteError } from \"./errors\";\nimport type { WebhookEvent } from \"./types\";\n\n/** Options for {@link verifyWebhook}. */\nexport interface VerifyWebhookOptions {\n /** Reject deliveries whose timestamp is further than this from now. Default 300. */\n toleranceSeconds?: number;\n /** Current time in Unix seconds (tests). */\n now?: number;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n const rec = headers as Record<string, string | string[] | undefined>;\n const key = Object.keys(rec).find((k) => k.toLowerCase() === name.toLowerCase());\n const v = key === undefined ? undefined : rec[key];\n return Array.isArray(v) ? v[0] : v;\n}\n\n/**\n * Checks a webhook delivery (any event: `batch.completed`, `crawl.completed`,\n * `monitor.results`): `X-SerpKite-Signature` must be\n * `v1=<hex HMAC-SHA256(secret, \"<X-SerpKite-Timestamp>.<raw body>\")>` and the\n * timestamp recent. Pass the raw request body exactly as received (not\n * re-serialised JSON). Uses Web Crypto (Node 18+, Deno, Bun, browsers).\n *\n * ```ts\n * const ok = await verifyWebhook(process.env.SERPKITE_WEBHOOK_SECRET!, rawBody, req.headers);\n * ```\n */\nexport async function verifyWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<boolean> {\n const signature = header(headers, \"x-serpkite-signature\");\n const ts = header(headers, \"x-serpkite-timestamp\");\n if (!secret || !signature?.startsWith(\"v1=\") || !ts || !/^\\d+$/.test(ts)) return false;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - Number(ts)) > (options.toleranceSeconds ?? 300)) return false;\n\n const enc = new TextEncoder();\n const body = typeof payload === \"string\" ? enc.encode(payload) : payload;\n const prefix = enc.encode(`${ts}.`);\n const msg = new Uint8Array(prefix.length + body.length);\n msg.set(prefix);\n msg.set(body, prefix.length);\n const key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, msg));\n const want = Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n const got = signature.slice(3).toLowerCase();\n if (got.length !== want.length) return false;\n let diff = 0;\n for (let i = 0; i < want.length; i++) diff |= want.charCodeAt(i) ^ got.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Verifies a delivery (see {@link verifyWebhook}) and returns it typed by its\n * `X-SerpKite-Event` header. `deliveryId` is `X-SerpKite-Delivery` (for\n * `monitor.results` it equals `data.run_id`): use it to deduplicate retries. A\n * `crawl.completed` result over 4 MB arrives as `result: null` with\n * `result_omitted: true`; fetch it from `data.poll_url`. Throws a\n * `SerpKiteError` with code `invalid_signature` when the signature or timestamp\n * doesn't check out.\n *\n * ```ts\n * const ev = await parseWebhook(secret, rawBody, req.headers);\n * if (ev.type === \"monitor.results\") console.log(ev.data.new_results.length);\n * ```\n */\nexport async function parseWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n if (!(await verifyWebhook(secret, payload, headers, options))) {\n throw new SerpKiteError(400, \"invalid_signature\", \"webhook signature or timestamp is invalid\");\n }\n const text = typeof payload === \"string\" ? payload : new TextDecoder().decode(payload);\n let data: { event?: unknown };\n try {\n data = JSON.parse(text);\n } catch (err) {\n throw new SerpKiteError(\n 400,\n \"invalid_payload\",\n \"webhook body is not JSON\",\n undefined,\n undefined,\n {\n cause: err,\n },\n );\n }\n const type =\n header(headers, \"x-serpkite-event\") ??\n (typeof data?.event === \"string\" ? data.event : \"batch.completed\");\n return { type, deliveryId: header(headers, \"x-serpkite-delivery\"), data } as WebhookEvent;\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/util.ts","../src/webhooks.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Every failure thrown by the SDK. API errors carry the fields of the\n * `{\"error\":{\"code\",\"message\",\"request_id\"}}` envelope; network failures and\n * timeouts use `status: 0` with code `connection_error` or `timeout`.\n */\nexport class SerpKiteError extends Error {\n /** HTTP status, or 0 when no response was received. */\n readonly status: number;\n /** Machine-readable code, e.g. `unauthorized`, `insufficient_credits`, `rate_limited`. */\n readonly code: string;\n /** `X-Request-Id` of the failed request, when the API returned one. */\n readonly requestId: string | undefined;\n /** Response headers, when the API responded. */\n readonly headers: Headers | undefined;\n\n constructor(\n status: number,\n code: string,\n message: string,\n requestId?: string,\n headers?: Headers,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"SerpKiteError\";\n this.status = status;\n this.code = code;\n this.requestId = requestId || undefined;\n this.headers = headers;\n }\n}\n\nconst fallbackCodes: Record<number, string> = {\n 400: \"invalid_request\",\n 401: \"unauthorized\",\n 402: \"insufficient_credits\",\n 403: \"forbidden\",\n 404: \"not_found\",\n 429: \"rate_limited\",\n // The API sends upstream failures as 503 with a JSON body; a bare 502/504\n // comes from a proxy (Cloudflare replaces origin 502/504 bodies).\n 502: \"upstream_error\",\n 503: \"unavailable\",\n 504: \"upstream_timeout\",\n};\n\n/** Builds a SerpKiteError from a non-2xx response body (JSON envelope or anything else). */\nexport function errorFromResponse(status: number, headers: Headers, body: string): SerpKiteError {\n let code = fallbackCodes[status] ?? (status >= 500 ? \"server_error\" : \"http_error\");\n let message = body.trim().slice(0, 500) || `HTTP ${status}`;\n let requestId = headers.get(\"x-request-id\") ?? undefined;\n try {\n const parsed = JSON.parse(body) as {\n error?: { code?: unknown; message?: unknown; request_id?: unknown };\n };\n const e = parsed?.error;\n if (e && typeof e === \"object\") {\n if (typeof e.code === \"string\" && e.code) code = e.code;\n if (typeof e.message === \"string\" && e.message) message = e.message;\n if (typeof e.request_id === \"string\" && e.request_id) requestId = e.request_id;\n }\n } catch {\n // Not JSON: keep the text body as the message.\n }\n return new SerpKiteError(status, code, message, requestId, headers);\n}\n","import { errorFromResponse, SerpKiteError } from \"./errors\";\nimport type {\n Account,\n AutocompleteResponse,\n Batch,\n BatchCreateEntry,\n BatchCreateParams,\n BatchCreateResponse,\n CompactResponse,\n CrawlParams,\n CrawlTask,\n ExtractParams,\n ExtractResponse,\n ImagesResponse,\n MapParams,\n MapResponse,\n Monitor,\n MonitorCreateParams,\n MonitorList,\n MonitorRun,\n MonitorRunList,\n MonitorRunsParams,\n MonitorUpdateParams,\n NewsResponse,\n PatentsResponse,\n PlaceParams,\n PlacesResponse,\n RankParams,\n RankResponse,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n TaskCancelResponse,\n TaskCreated,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.6.0\";\nexport const DEFAULT_BASE_URL = \"https://api.serpkite.com\";\n\nexport interface SerpKiteOptions {\n /** API key (`skt_live_…`). Defaults to the `SERPKITE_API_KEY` environment variable. */\n apiKey?: string;\n /** Defaults to `SERPKITE_BASE_URL` or https://api.serpkite.com. */\n baseUrl?: string;\n /** Per-attempt timeout in milliseconds. Default 60000. */\n timeoutMs?: number;\n /** Retries after the first attempt on 429, 5xx and network errors. Default 2. */\n maxRetries?: number;\n /** Base delay of the exponential backoff in milliseconds. Default 500. */\n retryDelayMs?: number;\n /** Custom fetch implementation. Defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Extra headers sent with every request. */\n headers?: Record<string, string>;\n}\n\n/** Billing and tracing details from the response headers. */\nexport interface ResponseInfo {\n status: number;\n headers: Headers;\n requestId: string | undefined;\n creditsUsed: number | undefined;\n creditsRemaining: number | undefined;\n costUsd: number | undefined;\n /** `HIT` when served from cache (`max_age`), else `MISS`. */\n cache: \"HIT\" | \"MISS\" | undefined;\n latencyMs: number | undefined;\n tokensEstimate: number | undefined;\n}\n\nexport interface RequestOptions {\n signal?: AbortSignal;\n /** Overrides the client's per-attempt timeout. */\n timeoutMs?: number;\n /** Overrides the client's retry count. */\n maxRetries?: number;\n headers?: Record<string, string>;\n /** Called with the final response's headers (credits used/remaining, cache, request id). */\n onResponse?: (info: ResponseInfo) => void;\n}\n\nexport interface BatchCreateOptions extends RequestOptions {\n /**\n * Sent as `Idempotency-Key` (1-255 printable ASCII characters, e.g. a UUID).\n * For 24 hours a retry with the same key and body returns the first response\n * instead of queueing and billing the jobs again.\n */\n idempotencyKey?: string;\n}\n\nexport interface WaitOptions {\n /**\n * Give up after this many milliseconds. Default 600000 (10 minutes) for batches and\n * 2100000 (35 minutes) for `waitForCrawl`: a crawl runs for up to 30 minutes.\n */\n timeoutMs?: number;\n /** First delay between polls. Default 1000. Grows 1.5× per poll. */\n pollIntervalMs?: number;\n /** Upper bound of the delay between polls. Default 10000. */\n maxPollIntervalMs?: number;\n signal?: AbortSignal;\n}\n\n/** A vertical whose return type follows `format`: `\"markdown\"` → string, `\"compact\"` → CompactResponse. */\nexport interface VerticalMethod<P, R> {\n (params: P & { format: \"markdown\" }, options?: RequestOptions): Promise<string>;\n (params: P & { format: \"compact\" }, options?: RequestOptions): Promise<CompactResponse>;\n (params: P & { format?: \"json\" }, options?: RequestOptions): Promise<R>;\n (params: P, options?: RequestOptions): Promise<R | CompactResponse | string>;\n}\n\ntype Method = \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\n/** Task states after which `waitForCrawl` returns. */\nconst TASK_DONE = new Set([\"completed\", \"failed\", \"canceled\"]);\n\ninterface Call {\n method: Method;\n path: string;\n body?: unknown;\n options?: RequestOptions;\n /** Which failures are safe to retry. `all` = 429, 5xx and network errors. */\n retry?: \"all\" | \"rate_limit\";\n}\n\nexport class SerpKite {\n readonly baseUrl: string;\n readonly timeoutMs: number;\n readonly maxRetries: number;\n readonly retryDelayMs: number;\n readonly #apiKey: string;\n readonly #fetch: typeof fetch;\n readonly #headers: Record<string, string>;\n\n constructor(options: SerpKiteOptions = {}) {\n const apiKey = options.apiKey ?? env(\"SERPKITE_API_KEY\");\n if (!apiKey) {\n throw new SerpKiteError(\n 0,\n \"missing_api_key\",\n \"No API key: pass { apiKey } or set the SERPKITE_API_KEY environment variable\",\n );\n }\n this.#apiKey = apiKey;\n this.baseUrl = (options.baseUrl ?? env(\"SERPKITE_BASE_URL\") ?? DEFAULT_BASE_URL).replace(\n /\\/+$/,\n \"\",\n );\n this.timeoutMs = options.timeoutMs ?? 60_000;\n this.maxRetries = Math.max(0, options.maxRetries ?? 2);\n this.retryDelayMs = options.retryDelayMs ?? 500;\n const f = options.fetch ?? globalThis.fetch;\n if (typeof f !== \"function\") {\n throw new SerpKiteError(\n 0,\n \"missing_fetch\",\n \"No global fetch: pass { fetch } (Node 18+ has one)\",\n );\n }\n this.#fetch = f;\n this.#headers = { ...options.headers };\n }\n\n // ── Verticals ─────────────────────────────────────────────────────────\n\n /** Google web search: organic results, answer box, knowledge graph, people also ask… */\n readonly search = this.#vertical<SearchParams, SearchResponse>(\"/v1/search\");\n /** Google Images. */\n readonly images = this.#vertical<SearchParams, ImagesResponse>(\"/v1/images\");\n /** Google Videos. */\n readonly videos = this.#vertical<SearchParams, VideosResponse>(\"/v1/videos\");\n /** Google News. */\n readonly news = this.#vertical<SearchParams, NewsResponse>(\"/v1/news\");\n /**\n * Google Maps search (places with coordinates). Accepts `ll` (\"@lat,lng,14z\"). With\n * `engine: \"yandex\"` (where enabled) the places come from Yandex Maps and carry an `oid`.\n */\n readonly maps = this.#vertical<SearchParams, PlacesResponse>(\"/v1/maps\");\n /** Google local results. */\n readonly places = this.#vertical<SearchParams, PlacesResponse>(\"/v1/places\");\n /**\n * Details of one place by `place_id`, `cid` or `fid`, or by a Yandex Maps `oid` (implies\n * `engine: \"yandex\"`): address, country, time zone, hours\n * (`opening_hours` text and structured `weekly_hours`), phones, description and photos.\n * `results` holds the place, or is empty (not charged) when no place has that id. 1 credit.\n */\n readonly place = this.#vertical<PlaceParams, PlacesResponse>(\"/v1/place\");\n /** Reviews of a place by `place_id`, `cid` or `fid`. Page with `page_token` = `next_page_token`. */\n readonly reviews = this.#vertical<ReviewsParams, ReviewsResponse>(\"/v1/reviews\");\n /** Google Shopping. */\n readonly shopping = this.#vertical<SearchParams, ShoppingResponse>(\"/v1/shopping\");\n /** Google Scholar. */\n readonly scholar = this.#vertical<SearchParams, ScholarResponse>(\"/v1/scholar\");\n /** Google Patents. */\n readonly patents = this.#vertical<SearchParams, PatentsResponse>(\"/v1/patents\");\n /** Google autocomplete suggestions. */\n readonly autocomplete = this.#vertical<SearchParams, AutocompleteResponse>(\"/v1/autocomplete\");\n /** Fetch any public URL as clean Markdown plus metadata. */\n readonly webpage = this.#vertical<WebpageParams, WebpageResponse>(\"/v1/webpage\");\n\n /** Position of `domain` for keyword `q` in the top `num` results (default 100). */\n rank(params: RankParams, options?: RequestOptions): Promise<RankResponse> {\n return this.request({ method: \"POST\", path: \"/v1/rank\", body: params, options });\n }\n\n /**\n * Read up to 20 URLs (HTML or PDF) as Markdown, text or HTML in one call, with optional\n * query-ranked `highlights` (BM25), `include_links` and `include_images`. 1 credit per URL\n * that comes back (0.5 from cache); URLs that fail are listed in `failed` and cost nothing.\n */\n extract(params: ExtractParams, options?: RequestOptions): Promise<ExtractResponse> {\n // Billed per page even when the response is lost: retried only on 429,\n // and the HTTP timeout covers the server's deadline (timeout, default 50 s) + 15 s.\n const wait = ((params.timeout ?? 50) + 15) * 1000;\n return this.request({\n method: \"POST\",\n path: \"/v1/extract\",\n body: params,\n options: {\n ...options,\n timeoutMs: options?.timeoutMs ?? (this.timeoutMs > 0 ? Math.max(this.timeoutMs, wait) : 0),\n },\n retry: \"rate_limit\",\n });\n }\n\n /**\n * The URLs of a site from its sitemaps (XML, RSS/Atom, gzip, plain text) and start page,\n * canonicalised and deduplicated, optionally ranked by `search`. `include_paths` /\n * `exclude_paths` (regular expressions on the URL path) and `ignore_query_parameters`\n * narrow the list. 1 credit; free when nothing is found.\n */\n map(params: MapParams, options?: RequestOptions): Promise<MapResponse> {\n return this.request({ method: \"POST\", path: \"/v1/map\", body: params, options });\n }\n\n /** Balance, limits and this month's usage for the key's account. */\n account(options?: RequestOptions): Promise<Account> {\n return this.request({ method: \"GET\", path: \"/v1/account\", options });\n }\n\n // ── Async tasks: crawl ────────────────────────────────────────────────\n\n /**\n * Starts an async crawl of one site and returns the queued task (`202`). 1 credit per page\n * read (0.5 from cache); `limit` credits (up to 1000 pages, `max_depth` up to 10) are\n * reserved up front and the rest refunded. `sitemap` (`include` default, `only`, `skip`)\n * seeds the crawl with sitemap URLs; `query` reads the most relevant pages first. robots.txt\n * and Crawl-delay are honoured; `result.stats.stopped` says why the crawl ended (`done`,\n * `limit`, `time_limit`, `size_limit`, `too_many_failures`, `canceled`).\n * Only retried on 429, so a lost response never starts (and reserves) a second crawl.\n */\n crawl(params: CrawlParams, options?: RequestOptions): Promise<TaskCreated> {\n return this.request({\n method: \"POST\",\n path: \"/v1/crawl\",\n body: params,\n options,\n retry: \"rate_limit\",\n });\n }\n\n /** Polls a crawl (results are kept for 24 hours). */\n getCrawl(id: string, options?: RequestOptions): Promise<CrawlTask> {\n return this.request({ method: \"GET\", path: `/v1/crawl/${encodeURIComponent(id)}`, options });\n }\n\n /** Cancels a crawl: refunded if still queued, else stops at the next checkpoint. */\n cancelCrawl(id: string, options?: RequestOptions): Promise<TaskCancelResponse> {\n return this.request({\n method: \"DELETE\",\n path: `/v1/crawl/${encodeURIComponent(id)}`,\n options,\n });\n }\n\n /**\n * Polls until the crawl is `completed`, `failed` or `canceled` and returns it (a failed\n * task is returned, not thrown; check `status` and `error`). Accepts an id or the task\n * returned by `crawl`.\n */\n async waitForCrawl(\n idOrTask: string | { id: string },\n options: WaitOptions = {},\n ): Promise<CrawlTask> {\n return this.#waitTask(taskId(idOrTask), (id) => this.getCrawl(id, { signal: options.signal }), {\n ...options,\n timeoutMs: options.timeoutMs ?? 2_100_000,\n });\n }\n\n // ── Monitors ──────────────────────────────────────────────────────────\n\n readonly monitors = {\n /**\n * Saves a `search` or `news` request (`q`) that runs on a schedule (hourly to monthly) and\n * POSTs results it hasn't seen before to `webhook_url` (`monitor.results`). With\n * `endpoint: \"webpage\"` and a `url` it checks that page each interval (1 credit per check)\n * and reports it (`MonitorPageChange`) when its content changes. `metadata` (your JSON\n * object, at most 2 KB) is echoed on the monitor and in its webhooks. Without a\n * `webhook_url`, read new results from `monitors.runs`. `active: false`\n * creates it paused. A monitor pauses itself after 10 failed runs in a row\n * (`last_status: \"paused\"`); `update(id, { active: true })` resumes it. Each run costs\n * what its search costs. Only retried on 429.\n */\n create: (params: MonitorCreateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: \"/v1/monitors\",\n body: params,\n options,\n retry: \"rate_limit\",\n }),\n\n /** The account's monitors. */\n list: (options?: RequestOptions): Promise<MonitorList> =>\n this.request({ method: \"GET\", path: \"/v1/monitors\", options }),\n\n get: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({ method: \"GET\", path: `/v1/monitors/${encodeURIComponent(id)}`, options }),\n\n /**\n * Changes `name`, `interval` / `interval_seconds`, `webhook_url` (`\"\"` removes it),\n * `active` (`true` resumes a paused monitor), `metadata` (an object replaces it, `null`\n * clears it) or the saved request (`endpoint`, `q`, `url`, `country`, `num`, domains,\n * `engine`…). Switching `endpoint` between a search and `webpage` drops the other kind's\n * fields. A changed search reports every result as new on the next run.\n */\n update: (id: string, params: MonitorUpdateParams, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"PATCH\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n body: params,\n options,\n }),\n\n delete: async (id: string, options?: RequestOptions): Promise<void> => {\n await this.request({\n method: \"DELETE\",\n path: `/v1/monitors/${encodeURIComponent(id)}`,\n options,\n });\n },\n\n /** Runs an active monitor at the next scheduler poll (within about 30 seconds).\n * Paused monitors return 409; update(id, { active: true }) resumes and schedules them. */\n run: (id: string, options?: RequestOptions): Promise<Monitor> =>\n this.request({\n method: \"POST\",\n path: `/v1/monitors/${encodeURIComponent(id)}/run`,\n options,\n retry: \"rate_limit\",\n }),\n\n /**\n * One page of the monitor's run history, newest first (`limit` 1-100, default 20). Each\n * run carries its new results for 24 hours. Pass `next_before` as `before` for the next\n * page; it is null on the last one.\n */\n runs: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): Promise<MonitorRunList> =>\n this.request({\n method: \"GET\",\n path: `/v1/monitors/${encodeURIComponent(id)}/runs${query(params)}`,\n options,\n }),\n\n /**\n * Iterates over every run of the monitor, newest first, fetching pages of `limit` as\n * needed (starting at `before` when given).\n *\n * ```ts\n * for await (const run of sk.monitors.iterRuns(\"m1\")) console.log(run.new_results);\n * ```\n */\n iterRuns: (\n id: string,\n params: MonitorRunsParams = {},\n options?: RequestOptions,\n ): AsyncIterable<MonitorRun> => this.#iterRuns(id, params, options),\n };\n\n // ── Batches ───────────────────────────────────────────────────────────\n\n readonly batches = {\n /**\n * Queue 1-100 requests for one endpoint at half price. Returns one entry per\n * request, in order: a queued `Batch` or an error object. Without an\n * `idempotencyKey` it is only retried on 429, so a lost response never queues\n * (and bills) the same requests twice. With one, 5xx and network errors are\n * retried too: the server replays the first response for the same key and body.\n */\n create: (\n params: BatchCreateParams,\n options: BatchCreateOptions = {},\n ): Promise<BatchCreateResponse> => {\n const { idempotencyKey, ...rest } = options;\n return this.request({\n method: \"POST\",\n path: \"/v1/batches\",\n body: params,\n options: idempotencyKey\n ? { ...rest, headers: { ...rest.headers, \"Idempotency-Key\": idempotencyKey } }\n : rest,\n retry: idempotencyKey ? \"all\" : \"rate_limit\",\n });\n },\n\n /** Poll one batch job. */\n get: (id: string, options?: RequestOptions): Promise<Batch> =>\n this.request({ method: \"GET\", path: `/v1/batches/${encodeURIComponent(id)}`, options }),\n\n /**\n * Poll until the job is `done` or `failed` and return it (a failed job is\n * returned, not thrown; check `status` and `error`). Accepts an id or an\n * entry from `create`; an entry that failed to queue throws its error.\n */\n wait: (idOrEntry: string | BatchCreateEntry | undefined, options: WaitOptions = {}) =>\n this.#wait(idOrEntry, options),\n };\n\n // ── Plumbing ──────────────────────────────────────────────────────────\n\n async *#iterRuns(\n id: string,\n params: MonitorRunsParams,\n options?: RequestOptions,\n ): AsyncGenerator<MonitorRun> {\n let before = params.before;\n for (;;) {\n const page = await this.monitors.runs(id, { ...params, before }, options);\n yield* page.results;\n if (!page.next_before || page.results.length === 0) return;\n before = page.next_before;\n }\n }\n\n async #waitTask<T extends { status: string }>(\n id: string,\n get: (id: string) => Promise<T>,\n o: WaitOptions,\n ): Promise<T> {\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const task = await get(id);\n if (TASK_DONE.has(task.status)) return task;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `task ${id} still ${task.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n #vertical<P, R>(path: string): VerticalMethod<P, R> {\n return ((params: P, options?: RequestOptions) =>\n this.request({ method: \"POST\", path, body: params, options })) as VerticalMethod<P, R>;\n }\n\n async #wait(idOrEntry: string | BatchCreateEntry | undefined, o: WaitOptions): Promise<Batch> {\n let id: string | undefined;\n if (typeof idOrEntry === \"string\") {\n id = idOrEntry;\n } else if (idOrEntry && \"id\" in idOrEntry && typeof idOrEntry.id === \"string\") {\n id = idOrEntry.id;\n } else if (idOrEntry && \"error\" in idOrEntry && idOrEntry.error && !(\"id\" in idOrEntry)) {\n const e = idOrEntry.error as { code?: string; message?: string; request_id?: string };\n throw new SerpKiteError(\n 400,\n e.code ?? \"invalid_request\",\n e.message ?? \"batch entry was not queued\",\n e.request_id,\n );\n }\n if (!id) {\n throw new SerpKiteError(0, \"invalid_request\", \"batches.wait needs a batch id\");\n }\n const timeoutMs = o.timeoutMs ?? 600_000;\n const maxInterval = o.maxPollIntervalMs ?? 10_000;\n let interval = o.pollIntervalMs ?? 1_000;\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const batch = await this.batches.get(id, { signal: o.signal });\n if (batch.status === \"done\" || batch.status === \"failed\") return batch;\n const left = deadline - Date.now();\n if (left <= 0) {\n throw new SerpKiteError(\n 0,\n \"timeout\",\n `batch ${id} still ${batch.status} after ${timeoutMs} ms`,\n );\n }\n await sleep(Math.min(interval, left), o.signal);\n interval = Math.min(interval * 1.5, maxInterval);\n }\n }\n\n /** Low-level request: JSON body in, parsed JSON (or text for non-JSON responses) out. */\n async request<T>(call: Call): Promise<T> {\n const { method, path, body, options = {} } = call;\n const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);\n const timeoutMs = options.timeoutMs ?? this.timeoutMs;\n const headers: Record<string, string> = {\n Accept: \"application/json, text/markdown;q=0.9\",\n Authorization: `Bearer ${this.#apiKey}`,\n ...this.#headers,\n ...options.headers,\n };\n if (body !== undefined) headers[\"Content-Type\"] = \"application/json\";\n // Browsers would turn a custom User-Agent into a CORS preflight header.\n if (!isBrowser()) headers[\"User-Agent\"] = `serpkite-typescript/${VERSION}`;\n const init: RequestInit = {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n };\n\n for (let attempt = 0; ; attempt++) {\n const canRetry = attempt < maxRetries;\n let res: Response;\n const { signal, cleanup, timedOut } = withTimeout(options.signal, timeoutMs);\n try {\n res = await this.#fetch(this.baseUrl + path, { ...init, signal });\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const msg = timedOut()\n ? `request timed out after ${timeoutMs} ms`\n : `request failed: ${err instanceof Error ? err.message : String(err)}`;\n throw new SerpKiteError(0, code, msg, undefined, undefined, { cause: err });\n }\n\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n cleanup();\n if (options.signal?.aborted) throw err;\n if (canRetry && call.retry !== \"rate_limit\") {\n await sleep(this.#backoff(attempt), options.signal);\n continue;\n }\n const code = timedOut() ? \"timeout\" : \"connection_error\";\n throw new SerpKiteError(\n 0,\n code,\n \"failed to read the response body\",\n undefined,\n res.headers,\n {\n cause: err,\n },\n );\n }\n cleanup();\n\n if (!res.ok) {\n const retryable = res.status === 429 || (res.status >= 500 && call.retry !== \"rate_limit\");\n if (canRetry && retryable) {\n await sleep(this.#retryDelay(attempt, res.headers), options.signal);\n continue;\n }\n throw errorFromResponse(res.status, res.headers, text);\n }\n\n options.onResponse?.(responseInfo(res));\n const type = res.headers.get(\"content-type\") ?? \"\";\n if (type.includes(\"json\")) {\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n throw new SerpKiteError(\n res.status,\n \"invalid_response\",\n \"response is not valid JSON\",\n res.headers.get(\"x-request-id\") ?? undefined,\n res.headers,\n { cause: err },\n );\n }\n }\n return text as T;\n }\n }\n\n #backoff(attempt: number): number {\n const base = Math.min(this.retryDelayMs * 2 ** attempt, 8_000);\n return base / 2 + Math.random() * (base / 2);\n }\n\n #retryDelay(attempt: number, headers: Headers): number {\n const after = parseRetryAfter(headers.get(\"retry-after\"));\n return after === undefined ? this.#backoff(attempt) : Math.min(after, 60_000);\n }\n}\n\n// ── helpers ─────────────────────────────────────────────────────────────\n\n/** Builds `?a=1&b=2` from the defined values (empty string when there are none). */\nfunction query(params: Record<string, string | number | boolean | undefined>): string {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) q.set(k, String(v));\n }\n const s = q.toString();\n return s ? `?${s}` : \"\";\n}\n\nfunction taskId(idOrTask: string | { id: string }): string {\n const id = typeof idOrTask === \"string\" ? idOrTask : idOrTask?.id;\n if (!id) throw new SerpKiteError(0, \"invalid_request\", \"a task id is required\");\n return id;\n}\n\n/** Parses Retry-After (seconds or an HTTP date) into milliseconds. */\nexport function parseRetryAfter(v: string | null): number | undefined {\n if (!v) return undefined;\n const secs = Number(v);\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000);\n const at = Date.parse(v);\n return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());\n}\n\nfunction responseInfo(res: Response): ResponseInfo {\n const h = res.headers;\n const num = (name: string) => {\n const v = h.get(name);\n if (v === null || v === \"\") return undefined;\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n };\n const cache = h.get(\"x-cache\")?.toUpperCase();\n return {\n status: res.status,\n headers: h,\n requestId: h.get(\"x-request-id\") ?? undefined,\n creditsUsed: num(\"x-credits-used\"),\n creditsRemaining: num(\"x-credits-remaining\"),\n costUsd: num(\"x-cost-usd\"),\n cache: cache === \"HIT\" || cache === \"MISS\" ? cache : undefined,\n latencyMs: num(\"x-latency-ms\"),\n tokensEstimate: num(\"x-tokens-estimate\"),\n };\n}\n\nfunction withTimeout(outer: AbortSignal | undefined, ms: number) {\n const ctrl = new AbortController();\n let timedOut = false;\n const onAbort = () => ctrl.abort(outer?.reason);\n if (outer?.aborted) ctrl.abort(outer.reason);\n else outer?.addEventListener(\"abort\", onAbort, { once: true });\n const timer =\n ms > 0 && Number.isFinite(ms)\n ? setTimeout(() => {\n timedOut = true;\n ctrl.abort(new SerpKiteError(0, \"timeout\", `request timed out after ${ms} ms`));\n }, ms)\n : undefined;\n return {\n signal: ctrl.signal,\n timedOut: () => timedOut,\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n outer?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(signal.reason);\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(signal?.reason);\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\ntype EnvHost = {\n process?: { env?: Record<string, string | undefined> };\n Deno?: { env?: { get?: (name: string) => string | undefined } };\n document?: unknown;\n};\n\nfunction env(name: string): string | undefined {\n const g = globalThis as EnvHost;\n try {\n const v = g.process?.env?.[name];\n if (v) return v;\n } catch {\n // process.env may be a throwing proxy on some edge runtimes.\n }\n try {\n return g.Deno?.env?.get?.(name) || undefined;\n } catch {\n // Deno without --allow-env.\n return undefined;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof (globalThis as EnvHost).document !== \"undefined\";\n}\n","import type { BatchCreateEntry, BatchCreateError } from \"./types\";\n\n/** True when a `batches.create` entry was rejected instead of queued. */\nexport function isBatchError(entry: BatchCreateEntry): entry is BatchCreateError {\n return !(\"id\" in entry && typeof entry.id === \"string\") && \"error\" in entry && !!entry.error;\n}\n","import { SerpKiteError } from \"./errors\";\nimport type { WebhookEvent } from \"./types\";\n\n/** Options for {@link verifyWebhook}. */\nexport interface VerifyWebhookOptions {\n /** Reject deliveries whose timestamp is further than this from now. Default 300. */\n toleranceSeconds?: number;\n /** Current time in Unix seconds (tests). */\n now?: number;\n}\n\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\n\nfunction header(headers: HeaderSource, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n const rec = headers as Record<string, string | string[] | undefined>;\n const key = Object.keys(rec).find((k) => k.toLowerCase() === name.toLowerCase());\n const v = key === undefined ? undefined : rec[key];\n return Array.isArray(v) ? v[0] : v;\n}\n\n/**\n * Checks a webhook delivery (any event: `batch.completed`, `crawl.completed`,\n * `monitor.results`): `X-SerpKite-Signature` must be\n * `v1=<hex HMAC-SHA256(secret, \"<X-SerpKite-Timestamp>.<raw body>\")>` and the\n * timestamp recent. Pass the raw request body exactly as received (not\n * re-serialised JSON). Uses Web Crypto (Node 18+, Deno, Bun, browsers).\n *\n * ```ts\n * const ok = await verifyWebhook(process.env.SERPKITE_WEBHOOK_SECRET!, rawBody, req.headers);\n * ```\n */\nexport async function verifyWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<boolean> {\n const signature = header(headers, \"x-serpkite-signature\");\n const ts = header(headers, \"x-serpkite-timestamp\");\n if (!secret || !signature?.startsWith(\"v1=\") || !ts || !/^\\d+$/.test(ts)) return false;\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - Number(ts)) > (options.toleranceSeconds ?? 300)) return false;\n\n const enc = new TextEncoder();\n const body = typeof payload === \"string\" ? enc.encode(payload) : payload;\n const prefix = enc.encode(`${ts}.`);\n const msg = new Uint8Array(prefix.length + body.length);\n msg.set(prefix);\n msg.set(body, prefix.length);\n const key = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const mac = new Uint8Array(await crypto.subtle.sign(\"HMAC\", key, msg));\n const want = Array.from(mac, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n const got = signature.slice(3).toLowerCase();\n if (got.length !== want.length) return false;\n let diff = 0;\n for (let i = 0; i < want.length; i++) diff |= want.charCodeAt(i) ^ got.charCodeAt(i);\n return diff === 0;\n}\n\n/**\n * Verifies a delivery (see {@link verifyWebhook}) and returns it typed by its\n * `X-SerpKite-Event` header. `deliveryId` is `X-SerpKite-Delivery` (for\n * `monitor.results` it equals `data.run_id`): use it to deduplicate retries. A\n * `crawl.completed` result over 4 MB arrives as `result: null` with\n * `result_omitted: true`; fetch it from `data.poll_url`. Throws a\n * `SerpKiteError` with code `invalid_signature` when the signature or timestamp\n * doesn't check out.\n *\n * ```ts\n * const ev = await parseWebhook(secret, rawBody, req.headers);\n * if (ev.type === \"monitor.results\") console.log(ev.data.new_results.length);\n * ```\n */\nexport async function parseWebhook(\n secret: string,\n payload: string | Uint8Array,\n headers: HeaderSource,\n options: VerifyWebhookOptions = {},\n): Promise<WebhookEvent> {\n if (!(await verifyWebhook(secret, payload, headers, options))) {\n throw new SerpKiteError(400, \"invalid_signature\", \"webhook signature or timestamp is invalid\");\n }\n const text = typeof payload === \"string\" ? payload : new TextDecoder().decode(payload);\n let data: { event?: unknown };\n try {\n data = JSON.parse(text);\n } catch (err) {\n throw new SerpKiteError(\n 400,\n \"invalid_payload\",\n \"webhook body is not JSON\",\n undefined,\n undefined,\n {\n cause: err,\n },\n );\n }\n const type =\n header(headers, \"x-serpkite-event\") ??\n (typeof data?.event === \"string\" ? data.event : \"batch.completed\");\n return { type, deliveryId: header(headers, \"x-serpkite-delivery\"), data } as WebhookEvent;\n}\n"]}