serpkite 0.1.1 → 0.3.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
@@ -81,7 +81,7 @@ vertical-specific extras and `meta` (`request_id`, `credits_used`, `cached`, `la
81
81
 
82
82
  | Method | Endpoint | Params | Returns |
83
83
  | --- | --- | --- | --- |
84
- | `search(params)` | `POST /v1/search` | `SearchParams` | `SearchResponse` (results, `ai_overview`, `answer_box`, `knowledge_graph`, `people_also_ask`, `related_searches`, `top_stories`, `places`, `ads`) |
84
+ | `search(params)` | `POST /v1/search` | `SearchParams` | `SearchResponse` (results, `answer_box`, `knowledge_graph`, `people_also_ask`, `related_searches`, `top_stories`, `places`, `ads`) |
85
85
  | `images(params)` | `POST /v1/images` | `SearchParams` | `ImagesResponse` |
86
86
  | `videos(params)` | `POST /v1/videos` | `SearchParams` | `VideosResponse` |
87
87
  | `news(params)` | `POST /v1/news` | `SearchParams` | `NewsResponse` |
@@ -92,8 +92,6 @@ vertical-specific extras and `meta` (`request_id`, `credits_used`, `cached`, `la
92
92
  | `scholar(params)` | `POST /v1/scholar` | `SearchParams` | `ScholarResponse` |
93
93
  | `patents(params)` | `POST /v1/patents` | `SearchParams` | `PatentsResponse` |
94
94
  | `autocomplete(params)` | `POST /v1/autocomplete` | `SearchParams` | `AutocompleteResponse` (`results[].value`) |
95
- | `lens(params)` | `POST /v1/lens` | `LensParams` (`url` of an image) | `LensResponse` |
96
- | `aiMode(params)` | `POST /v1/ai-mode` | `SearchParams` | `AIModeResponse` (`answer`, `markdown`, cited `results`) |
97
95
  | `webpage(params)` | `POST /v1/webpage` | `WebpageParams` (`url`, `include_html`) | `WebpageResponse` (`markdown`, `text`, `metadata`) |
98
96
  | `rank(params)` | `POST /v1/rank` | `RankParams` (`q`, `domain`, `num`: 10\|20\|30\|50\|100) | `RankResponse` (`position` or `null`, `matches`, `checked`) |
99
97
  | `account()` | `GET /v1/account` | none | `Account` (`balance`, `plan`, `rate_limit_rps`, `month`, …) |
@@ -104,8 +102,8 @@ vertical-specific extras and `meta` (`request_id`, `credits_used`, `cached`, `la
104
102
  Common `SearchParams`: `q` (required), `country` (default `us`), `language` (default `en`),
105
103
  `location`, `uule`, `num` (10, or 100 for the depth bundle), `page` (1-10), `time`
106
104
  (`hour`|`day`|`week`|`month`|`year`), `tbs`, `device` (`desktop`|`mobile`), `safe`, `autocorrect`,
107
- `format` (`json`|`markdown`|`compact`), `fields`, `include_content` (0-5), `ai_overview`, `ads`,
108
- `max_age`, `engine`.
105
+ `format` (`json`|`markdown`|`compact`), `fields`, `include_content` (0-5), `ads`, `max_age`,
106
+ `engine`.
109
107
 
110
108
  ### Examples
111
109
 
@@ -118,7 +116,7 @@ const deep = await sk.search({ q: "rust async runtime comparison", include_conte
118
116
  console.log(deep.results[0].content);
119
117
 
120
118
  // Only the fields you need
121
- const lean = await sk.search({ q: "espresso", fields: "results.title,results.link,ai_overview" });
119
+ const lean = await sk.search({ q: "espresso", fields: "results.title,results.link,answer_box" });
122
120
 
123
121
  // Token-lean JSON for agents
124
122
  const compact = await sk.search({ q: "espresso", format: "compact" });
@@ -184,7 +182,7 @@ import { isBatchError, SerpKite } from "serpkite";
184
182
 
185
183
  const sk = new SerpKite();
186
184
  const { batches } = await sk.batches.create({
187
- endpoint: "search", // or images, news, maps, reviews, ai-mode, webpage, …
185
+ endpoint: "search", // or images, news, maps, reviews, webpage, …
188
186
  requests: [{ q: "a" }, { q: "b" }],
189
187
  webhook_url: "https://example.com/hooks/serpkite", // optional
190
188
  });
@@ -255,9 +253,9 @@ are never retried. `batches.create` retries only on `429`, so a lost response ca
255
253
 
256
254
  Types are generated from the OpenAPI contract (`backend/api/serp-api.yaml`) with
257
255
  `openapi-typescript`. Friendly aliases are exported: `SearchParams`, `SearchResponse`,
258
- `OrganicResult`, `AIOverview`, `NewsResponse`, `NewsResult`, `ImagesResponse`, `PlacesResponse`,
256
+ `OrganicResult`, `AnswerBox`, `NewsResponse`, `NewsResult`, `ImagesResponse`, `PlacesResponse`,
259
257
  `ReviewsResponse`, `ShoppingResponse`, `ScholarResponse`, `PatentsResponse`,
260
- `AutocompleteResponse`, `LensResponse`, `AIModeResponse`, `WebpageResponse`, `RankResponse`,
258
+ `AutocompleteResponse`, `WebpageResponse`, `RankResponse`,
261
259
  `Account`, `Batch`, `BatchCreateParams`, `Meta`, `Engine`, `Provider`, `RouteStep`,
262
260
  `CompactResponse` and more. The raw
263
261
  `paths`, `components` and `operations` types are exported as well.
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.1.1";
53
+ var VERSION = "0.3.0";
54
54
  var DEFAULT_BASE_URL = "https://api.serpkite.com";
55
55
  var SerpKite = class {
56
56
  baseUrl;
@@ -89,7 +89,7 @@ var SerpKite = class {
89
89
  this.#headers = { ...options.headers };
90
90
  }
91
91
  // ── Verticals ─────────────────────────────────────────────────────────
92
- /** Google web search: organic results, AI Overview, answer box, knowledge graph, people also ask… */
92
+ /** Google web search: organic results, answer box, knowledge graph, people also ask… */
93
93
  search = this.#vertical("/v1/search");
94
94
  /** Google Images. */
95
95
  images = this.#vertical("/v1/images");
@@ -111,14 +111,8 @@ var SerpKite = class {
111
111
  patents = this.#vertical("/v1/patents");
112
112
  /** Google autocomplete suggestions. */
113
113
  autocomplete = this.#vertical("/v1/autocomplete");
114
- /** Google AI Mode answer with cited sources. */
115
- aiMode = this.#vertical("/v1/ai-mode");
116
114
  /** Fetch any public URL as clean Markdown plus metadata. */
117
115
  webpage = this.#vertical("/v1/webpage");
118
- /** Google Lens visual matches for an image URL. */
119
- lens(params, options) {
120
- return this.request({ method: "POST", path: "/v1/lens", body: params, options });
121
- }
122
116
  /** Position of `domain` for keyword `q` in the top `num` results (default 100). */
123
117
  rank(params, options) {
124
118
  return this.request({ method: "POST", path: "/v1/rank", body: params, options });
@@ -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 AIModeResponse,\n AutocompleteResponse,\n Batch,\n BatchCreateEntry,\n BatchCreateParams,\n BatchCreateResponse,\n CompactResponse,\n ImagesResponse,\n LensParams,\n LensResponse,\n NewsResponse,\n PatentsResponse,\n PlacesResponse,\n RankParams,\n RankResponse,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.1.1\";\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 /** Give up after this many milliseconds. Default 600000 (10 minutes). */\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\";\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, AI Overview, 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 /** Google AI Mode answer with cited sources. */\n readonly aiMode = this.#vertical<SearchParams, AIModeResponse>(\"/v1/ai-mode\");\n /** Fetch any public URL as clean Markdown plus metadata. */\n readonly webpage = this.#vertical<WebpageParams, WebpageResponse>(\"/v1/webpage\");\n\n /** Google Lens visual matches for an image URL. */\n lens(params: LensParams, options?: RequestOptions): Promise<LensResponse> {\n return this.request({ method: \"POST\", path: \"/v1/lens\", body: params, options });\n }\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 /** 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 // ── 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 #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/** 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","/** 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 batch webhook delivery: `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"]}
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 ImagesResponse,\n NewsResponse,\n PatentsResponse,\n PlacesResponse,\n RankParams,\n RankResponse,\n ReviewsParams,\n ReviewsResponse,\n ScholarResponse,\n SearchParams,\n SearchResponse,\n ShoppingResponse,\n VideosResponse,\n WebpageParams,\n WebpageResponse,\n} from \"./types\";\n\nexport const VERSION = \"0.3.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 /** Give up after this many milliseconds. Default 600000 (10 minutes). */\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\";\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 /** 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 // ── 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 #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/** 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","/** 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 batch webhook delivery: `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"]}