@openserp/sdk 0.1.1 → 0.1.3

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
@@ -4,9 +4,37 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@openserp/sdk.svg)](https://www.npmjs.com/package/@openserp/sdk)
5
5
  [![license](https://img.shields.io/npm/l/@openserp/sdk.svg)](https://github.com/karust/openserp/blob/master/LICENSE)
6
6
 
7
+ ```bash
8
+ npm install @openserp/sdk
9
+ ```
10
+
11
+ Cloud:
12
+
13
+ ```ts
14
+ import { OpenSERP } from "@openserp/sdk";
15
+
16
+ const client = new OpenSERP({ apiKey: process.env.OPENSERP_KEY });
17
+ const { results } = await client.search({ engine: "google", text: "openserp" });
18
+
19
+ console.log(results[0]?.title, results[0]?.url);
20
+ ```
21
+
22
+ Self-hosted:
23
+
24
+ ```ts
25
+ import { OpenSERP } from "@openserp/sdk";
26
+
27
+ const client = new OpenSERP({ baseUrl: "http://localhost:7000" });
28
+ const { results } = await client.search({ engine: "bing", text: "openserp" });
29
+
30
+ console.log(results[0]?.title, results[0]?.url);
31
+ ```
32
+
7
33
  Universal TypeScript / JavaScript SDK for the **OpenSERP** multi-engine SERP API — Google, Bing, Yandex, Baidu, DuckDuckGo, and Ecosia results in a single call. Works against the self-hosted [open source server](https://github.com/karust/openserp) and against [OpenSERP Cloud](https://openserp.org/cloud) with the same code.
8
34
 
9
- Use it for AI grounding, RAG pipelines, LLM tool use, SEO monitoring, competitor analysis, and search-powered automations. Open-source alternative to SerpAPI and DataForSEO.
35
+ Use it for AI grounding, RAG pipelines, LLM tool use, agent tool use, LangChain / LlamaIndex integrations, SEO rank tracking, competitor analysis, and search-powered automations. Open-source alternative to SerpAPI, DataForSEO, ScrapingBee, Bright Data SERP, Oxylabs SERP, and Zenserp.
36
+
37
+ > Also available for Python: [`openserp`](https://pypi.org/project/openserp/) ([source](https://github.com/karust/openserp/tree/master/integrations/sdk-python)).
10
38
 
11
39
  > **Alpha — the API may change before `1.0.0`.** Pin a version in production.
12
40
 
@@ -22,7 +50,6 @@ Use it for AI grounding, RAG pipelines, LLM tool use, SEO monitoring, competitor
22
50
  - [Telemetry](#telemetry)
23
51
  - [Error handling](#error-handling)
24
52
  - [Retry hook](#retry-hook)
25
- - [Edge runtimes](#edge-runtimes)
26
53
  - [Use cases](#use-cases)
27
54
  - [Development](#development)
28
55
 
@@ -162,11 +189,7 @@ await client.enginesStatus();
162
189
  await client.enginesCapabilities();
163
190
  ```
164
191
 
165
- The backend is inferred from `baseUrl` and `apiKey`. Override explicitly when needed:
166
-
167
- ```ts
168
- client.config.backend = "oss";
169
- ```
192
+ The backend is inferred from `baseUrl` and `apiKey`. Pass `backend: "oss"` or `backend: "cloud"` to the constructor to override.
170
193
 
171
194
  ## Telemetry
172
195
 
@@ -204,10 +227,19 @@ try {
204
227
  The SDK does not apply a retry policy. Provide a hook when you want one:
205
228
 
206
229
  ```ts
230
+ import { OpenSERP, SERPError } from "@openserp/sdk";
231
+
232
+ const RETRYABLE = new Set([408, 429, 500, 502, 503]);
233
+
207
234
  const client = new OpenSERP({
208
- baseUrl: "http://localhost:7000",
209
- retry: (err, attempt) => {
210
- return err instanceof SERPError && err.status >= 500 && attempt < 2;
235
+ apiKey: process.env.OPENSERP_KEY,
236
+ retry: async (err, attempt) => {
237
+ if (attempt >= 2 || !(err instanceof SERPError) || !RETRYABLE.has(err.status)) {
238
+ return false;
239
+ }
240
+ const wait = Math.min(2 ** attempt * 250, 8_000) + Math.random() * 250;
241
+ await new Promise((r) => setTimeout(r, wait));
242
+ return true;
211
243
  },
212
244
  });
213
245
  ```
@@ -218,20 +250,18 @@ const client = new OpenSERP({
218
250
  - **LLM tool use** — expose `client.search` as a tool to your agent.
219
251
  - **SEO monitoring** — daily rank tracking across multiple engines and regions, export to Sheets or Notion.
220
252
  - **Competitor analysis** — weekly diff of top-10 results for a keyword set.
221
- - **Automations**
222
253
  - **Data pipelines** — stream SERPs to ClickHouse, BigQuery, or a DataFrame for NLP on snippets.
223
254
 
224
255
  ## Development
225
256
 
226
257
  ```bash
227
- corepack enable
228
- corepack pnpm install
229
- corepack pnpm test
230
- corepack pnpm build
258
+ pnpm install
259
+ pnpm test
260
+ pnpm build
231
261
  ```
232
262
 
233
263
  Regenerate OSS API types after changing `openserp/docs/openapi.yaml`:
234
264
 
235
265
  ```bash
236
- corepack pnpm generate:types
266
+ pnpm generate:types
237
267
  ```
package/dist/index.cjs CHANGED
@@ -147,6 +147,80 @@ function isErrorResponse(body) {
147
147
  return typeof body === "object" && body !== null && "error" in body;
148
148
  }
149
149
 
150
+ // src/debug.ts
151
+ var NO_OP = {
152
+ level: "off",
153
+ emit() {
154
+ }
155
+ };
156
+ function resolveDebug(config) {
157
+ const setting = config.debug ?? envDebug();
158
+ if (!setting) {
159
+ return NO_OP;
160
+ }
161
+ if (typeof setting === "function") {
162
+ return { level: "verbose", emit: guard(setting) };
163
+ }
164
+ const level = setting === "verbose" ? "verbose" : "info";
165
+ return { level, emit: consoleSink };
166
+ }
167
+ function envDebug() {
168
+ const value = typeof process !== "undefined" ? process.env?.OPENSERP_DEBUG : void 0;
169
+ if (!value) {
170
+ return void 0;
171
+ }
172
+ const normalized = value.trim().toLowerCase();
173
+ if (normalized === "verbose") {
174
+ return "verbose";
175
+ }
176
+ if (normalized === "1" || normalized === "true") {
177
+ return true;
178
+ }
179
+ return void 0;
180
+ }
181
+ function guard(sink) {
182
+ return (event) => {
183
+ try {
184
+ sink(event);
185
+ } catch {
186
+ }
187
+ };
188
+ }
189
+ var consoleSink = (event) => {
190
+ switch (event.type) {
191
+ case "request":
192
+ console.error(`[openserp] \u2192 ${event.method} ${event.url}`);
193
+ if (event.headers) {
194
+ console.error("[openserp] headers:", event.headers);
195
+ }
196
+ break;
197
+ case "response": {
198
+ const engine = event.engineUsed ? ` engine=${event.engineUsed}` : "";
199
+ const reqId = event.requestId ? ` request-id=${event.requestId}` : "";
200
+ console.error(
201
+ `[openserp] \u2190 ${event.status} ${event.method} ${event.url} (${event.durationMs}ms)${engine}${reqId}`
202
+ );
203
+ if (event.headers) {
204
+ console.error("[openserp] headers:", event.headers);
205
+ }
206
+ break;
207
+ }
208
+ case "error":
209
+ console.error(
210
+ `[openserp] \u2715 ${event.method} ${event.url} (${event.durationMs}ms):`,
211
+ event.error
212
+ );
213
+ break;
214
+ }
215
+ };
216
+ function dumpHeaders(headers) {
217
+ const out = {};
218
+ for (const [key, value] of headers) {
219
+ out[key] = key.toLowerCase() === "authorization" ? "Bearer ***" : value;
220
+ }
221
+ return out;
222
+ }
223
+
150
224
  // src/request.ts
151
225
  async function request(context, options) {
152
226
  let attempt = 0;
@@ -176,13 +250,24 @@ async function requestOnce({ baseUrl, config, setLastResponse }, options) {
176
250
  for (const [key, value] of new Headers(options.headers)) {
177
251
  headers.set(key, value);
178
252
  }
253
+ const debug = resolveDebug(config);
254
+ const method = options.method ?? "GET";
255
+ if (debug.level !== "off") {
256
+ debug.emit({
257
+ type: "request",
258
+ method,
259
+ url: url.toString(),
260
+ ...debug.level === "verbose" ? { headers: dumpHeaders(headers) } : {}
261
+ });
262
+ }
179
263
  const timeoutMs = config.timeoutMs ?? 3e4;
180
264
  const controller = new AbortController();
181
265
  const timer = setTimeout(() => controller.abort(), timeoutMs);
266
+ const startedAt = Date.now();
182
267
  let response;
183
268
  try {
184
269
  const init = {
185
- method: options.method ?? "GET",
270
+ method,
186
271
  headers,
187
272
  signal: controller.signal
188
273
  };
@@ -191,14 +276,34 @@ async function requestOnce({ baseUrl, config, setLastResponse }, options) {
191
276
  }
192
277
  response = await fetchImpl(url, init);
193
278
  } catch (err) {
194
- if (isAbortError(err)) {
195
- throw new TimeoutError(timeoutMs, err);
279
+ const error = isAbortError(err) ? new TimeoutError(timeoutMs, err) : err;
280
+ if (debug.level !== "off") {
281
+ debug.emit({
282
+ type: "error",
283
+ method,
284
+ url: url.toString(),
285
+ durationMs: Date.now() - startedAt,
286
+ error
287
+ });
196
288
  }
197
- throw err;
289
+ throw error;
198
290
  } finally {
199
291
  clearTimeout(timer);
200
292
  }
201
- setLastResponse(readLastResponse(response));
293
+ const last = readLastResponse(response);
294
+ setLastResponse(last);
295
+ if (debug.level !== "off") {
296
+ debug.emit({
297
+ type: "response",
298
+ method,
299
+ url: url.toString(),
300
+ status: response.status,
301
+ engineUsed: last.engineUsed,
302
+ requestId: last.requestId,
303
+ durationMs: Date.now() - startedAt,
304
+ ...debug.level === "verbose" ? { headers: dumpHeaders(response.headers) } : {}
305
+ });
306
+ }
202
307
  const body = await readBody(response, options.format);
203
308
  if (!response.ok) {
204
309
  throw errorFromResponse(response.status, body, response.headers.get("x-request-id") ?? void 0);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/backend.ts","../src/errors.ts","../src/request.ts","../src/client.ts"],"sourcesContent":["export { OpenSERP } from \"./client\";\nexport {\n CLOUD_BASE_URL,\n OSS_BASE_URL,\n inferBackend,\n normalizeBaseUrl,\n resolveBaseUrl,\n} from \"./backend\";\nexport {\n CaptchaError,\n CloudOnlyError,\n OssOnlyError,\n RateLimitError,\n SERPError,\n TimeoutError,\n} from \"./errors\";\nexport type {\n Backend,\n CacheStats,\n CircuitBreakerStatsResponse,\n Engine,\n ErrorResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaMode,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ProxyStats,\n ReadinessStatus,\n ResponseFormat,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n} from \"./types/public\";\nexport type {\n CloudAccount,\n CreditInfo,\n EngineCapability,\n EngineStatus,\n EnginesCapabilities,\n EnginesStatus,\n ModeCapability,\n OverallStatus,\n Price,\n Pricing,\n} from \"./types/cloud\";\n","import type { Backend, OpenSERPConfig } from \"./types/public\";\n\nexport const OSS_BASE_URL = \"http://localhost:7000\";\nexport const CLOUD_BASE_URL = \"https://api.openserp.org/v1\";\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function resolveBaseUrl(config: OpenSERPConfig): string {\n if (config.baseUrl) {\n return normalizeBaseUrl(config.baseUrl);\n }\n\n if (config.apiKey) {\n return CLOUD_BASE_URL;\n }\n\n return OSS_BASE_URL;\n}\n\nexport function inferBackend(config: Pick<OpenSERPConfig, \"apiKey\" | \"baseUrl\" | \"backend\">): Backend {\n if (config.backend) {\n return config.backend;\n }\n\n if (config.baseUrl) {\n try {\n if (new URL(config.baseUrl).hostname === \"api.openserp.org\") {\n return \"cloud\";\n }\n } catch {\n if (config.baseUrl.includes(\"api.openserp.org\")) {\n return \"cloud\";\n }\n }\n }\n\n if (config.apiKey) {\n return \"cloud\";\n }\n\n return \"oss\";\n}\n","import type { ErrorResponse } from \"./types/public\";\n\nexport class SERPError extends Error {\n readonly status: number;\n readonly code?: string | undefined;\n readonly reason?: string | undefined;\n readonly requestId?: string | undefined;\n readonly meta?: Record<string, unknown> | undefined;\n readonly response?: ErrorResponse | unknown | undefined;\n\n constructor(message: string, options: {\n status?: number | undefined;\n code?: string | undefined;\n reason?: string | undefined;\n requestId?: string | undefined;\n meta?: Record<string, unknown> | undefined;\n response?: ErrorResponse | unknown | undefined;\n cause?: unknown | undefined;\n } = {}) {\n super(message, { cause: options.cause });\n this.name = \"SERPError\";\n this.status = options.status ?? 0;\n this.code = options.code;\n this.reason = options.reason;\n this.requestId = options.requestId;\n this.meta = options.meta;\n this.response = options.response;\n }\n}\n\nexport class RateLimitError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"RateLimitError\";\n }\n}\n\nexport class CaptchaError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"CaptchaError\";\n }\n}\n\nexport class CloudOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against OpenSERP Cloud. Configure apiKey/baseUrl for https://api.openserp.org/v1 or set client.config.backend = \"cloud\".`);\n this.name = \"CloudOnlyError\";\n }\n}\n\nexport class OssOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against a self-hosted OpenSERP server. Configure baseUrl for your OSS server or set client.config.backend = \"oss\".`);\n this.name = \"OssOnlyError\";\n }\n}\n\nexport class TimeoutError extends SERPError {\n constructor(timeoutMs: number, cause?: unknown) {\n super(`OpenSERP request timed out after ${timeoutMs}ms`, {\n code: \"request_timeout\",\n cause,\n });\n this.name = \"TimeoutError\";\n }\n}\n\nexport function errorFromResponse(status: number, body: unknown, requestId?: string): SERPError {\n const data = isErrorResponse(body) ? body : undefined;\n const code = data?.error;\n const message = data?.message ?? `OpenSERP request failed with status ${status}`;\n const options = {\n status,\n code,\n reason: data?.reason,\n requestId: data?.request_id ?? requestId,\n meta: data?.meta as Record<string, unknown> | undefined,\n response: body,\n };\n\n if (status === 429 || code === \"rate_limited\") {\n return new RateLimitError(message, options);\n }\n\n if (code === \"captcha_detected\") {\n return new CaptchaError(message, options);\n }\n\n return new SERPError(message, options);\n}\n\nfunction isErrorResponse(body: unknown): body is ErrorResponse {\n return typeof body === \"object\" && body !== null && \"error\" in body;\n}\n","import { TimeoutError, errorFromResponse } from \"./errors\";\nimport type { LastResponse, OpenSERPConfig, ResponseFormat } from \"./types/public\";\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined\n | null;\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | undefined;\n path: string;\n query?: Record<string, QueryValue> | undefined;\n headers?: HeadersInit | undefined;\n body?: BodyInit | null | undefined;\n format?: ResponseFormat | undefined;\n}\n\nexport interface RequestContext {\n baseUrl: string;\n config: OpenSERPConfig;\n setLastResponse(response: LastResponse): void;\n}\n\nexport async function request<T>(\n context: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n let attempt = 0;\n\n for (;;) {\n try {\n return await requestOnce<T>(context, options);\n } catch (err) {\n const shouldRetry = await context.config.retry?.(err, attempt);\n if (!shouldRetry) {\n throw err;\n }\n attempt += 1;\n }\n }\n}\n\nasync function requestOnce<T>(\n { baseUrl, config, setLastResponse }: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new TypeError(\"OpenSERP SDK requires a fetch implementation.\");\n }\n\n const url = new URL(`${baseUrl}${options.path}`);\n appendQuery(url, options.query);\n\n const headers = new Headers(config.headers);\n if (config.apiKey) {\n headers.set(\"Authorization\", `Bearer ${config.apiKey}`);\n }\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n\n const timeoutMs = config.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let response: Response;\n try {\n const init: RequestInit = {\n method: options.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (options.body !== undefined) {\n init.body = options.body;\n }\n\n response = await fetchImpl(url, init);\n } catch (err) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs, err);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n\n setLastResponse(readLastResponse(response));\n\n const body = await readBody(response, options.format);\n if (!response.ok) {\n throw errorFromResponse(response.status, body, response.headers.get(\"x-request-id\") ?? undefined);\n }\n\n return body as T;\n}\n\nfunction appendQuery(url: URL, query: Record<string, QueryValue> | undefined): void {\n if (!query) {\n return;\n }\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n const encoded = Array.isArray(value) ? value.join(\",\") : String(value);\n url.searchParams.set(key, encoded);\n }\n}\n\nasync function readBody(response: Response, format?: ResponseFormat): Promise<unknown> {\n if (response.status === 204) {\n return undefined;\n }\n\n if (format && format !== \"json\") {\n return response.text();\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return response.json();\n }\n\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction readLastResponse(response: Response): LastResponse {\n const credits = {\n used: numberHeader(response.headers, \"x-credits-used\"),\n remaining: numberHeader(response.headers, \"x-credits-remaining\"),\n };\n const hasCredits = credits.used !== undefined || credits.remaining !== undefined;\n\n return {\n status: response.status,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n credits: hasCredits ? credits : undefined,\n engineUsed: response.headers.get(\"x-engine-used\") ?? undefined,\n fallbackEngine: response.headers.get(\"x-fallback-engine\") ?? undefined,\n cache: response.headers.get(\"x-cache\") ?? undefined,\n proxyMode: response.headers.get(\"x-proxy-mode\") ?? undefined,\n proxyTag: response.headers.get(\"x-proxy-tag\") ?? undefined,\n proxyUsed: response.headers.get(\"x-proxy-used\") ?? undefined,\n networkBytes: numberHeader(response.headers, \"x-network-bytes\"),\n browserProfileId: response.headers.get(\"x-browser-profile-id\") ?? undefined,\n headers: response.headers,\n };\n}\n\nfunction numberHeader(headers: Headers, name: string): number | undefined {\n const value = headers.get(name);\n if (value === null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === \"AbortError\";\n}\n","import { inferBackend, resolveBaseUrl } from \"./backend\";\nimport { CloudOnlyError, OssOnlyError } from \"./errors\";\nimport { request, type QueryValue } from \"./request\";\nimport type {\n CacheStats,\n CircuitBreakerStatsResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ReadinessStatus,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n ProxyStats,\n} from \"./types/public\";\nimport type {\n CloudAccount,\n EnginesCapabilities,\n EnginesStatus,\n Pricing,\n} from \"./types/cloud\";\n\nexport class OpenSERP {\n readonly config: OpenSERPConfig;\n lastResponse?: LastResponse;\n\n constructor(config: OpenSERPConfig = {}) {\n this.config = { ...config };\n }\n\n get backend() {\n return inferBackend(this.config);\n }\n\n get baseUrl() {\n return resolveBaseUrl(this.config);\n }\n\n search(params: SearchParams): Promise<SearchEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<SearchEnvelope | string>(`/${engine}/search`, query, format);\n }\n\n image(params: ImageParams): Promise<ImageEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<ImageEnvelope | string>(`/${engine}/image`, query, format);\n }\n\n megaSearch(params: MegaSearchParams): Promise<MegaSearchEnvelope | string> {\n const { format, ...query } = params;\n return this.get<MegaSearchEnvelope | string>(\"/mega/search\", query, format);\n }\n\n fastSearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"fast\" });\n }\n\n anySearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"any\" });\n }\n\n megaImage(params: MegaImageParams): Promise<ImageEnvelope | string> {\n const { format, ...query } = params;\n return this.get<ImageEnvelope | string>(\"/mega/image\", query, format);\n }\n\n fastImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"fast\" });\n }\n\n anyImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"any\" });\n }\n\n parseGoogle(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseGoogle\");\n return this.parse(\"/google/parse\", params);\n }\n\n parseBing(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseBing\");\n return this.parse(\"/bing/parse\", params);\n }\n\n health(): Promise<HealthStatus> {\n this.assertOss(\"health\");\n return this.get<HealthStatus>(\"/health\");\n }\n\n ready(): Promise<ReadinessStatus> {\n this.assertOss(\"ready\");\n return this.get<ReadinessStatus>(\"/ready\");\n }\n\n stats(): Promise<StatsResponse> {\n this.assertOss(\"stats\");\n return this.get<StatsResponse>(\"/stats\");\n }\n\n cacheStats(): Promise<CacheStats> {\n this.assertOss(\"cacheStats\");\n return this.get<CacheStats>(\"/stats/cache\");\n }\n\n proxyStats(): Promise<ProxyStats> {\n this.assertOss(\"proxyStats\");\n return this.get<ProxyStats>(\"/stats/proxy\");\n }\n\n circuitBreakerStats(): Promise<CircuitBreakerStatsResponse> {\n this.assertOss(\"circuitBreakerStats\");\n return this.get<CircuitBreakerStatsResponse>(\"/stats/cb\");\n }\n\n engines(): Promise<MegaEnginesResponse> {\n this.assertOss(\"engines\");\n return this.get<MegaEnginesResponse>(\"/mega/engines\");\n }\n\n me(): Promise<CloudAccount> {\n this.assertCloud(\"me\");\n return this.get<CloudAccount>(\"/me\");\n }\n\n pricing(): Promise<Pricing> {\n this.assertCloud(\"pricing\");\n return this.get<Pricing>(\"/pricing\");\n }\n\n enginesStatus(): Promise<EnginesStatus> {\n this.assertCloud(\"enginesStatus\");\n return this.get<EnginesStatus>(\"/engines/status\");\n }\n\n enginesCapabilities(): Promise<EnginesCapabilities> {\n this.assertCloud(\"enginesCapabilities\");\n return this.get<EnginesCapabilities>(\"/engines/capabilities\");\n }\n\n private parse(path: string, params: ParseParams): Promise<SearchEnvelope | string> {\n return request<SearchEnvelope | string>(this.requestContext(), {\n method: \"POST\",\n path,\n query: params.format ? { format: params.format } : undefined,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n },\n body: params.html,\n format: params.format,\n });\n }\n\n private get<T>(\n path: string,\n query?: Record<string, QueryValue>,\n format?: SearchParams[\"format\"],\n ): Promise<T> {\n const { query: cleanQuery, headers } = splitQueryAndHeaders({\n ...query,\n format,\n });\n\n return request<T>(this.requestContext(), {\n path,\n query: cleanQuery,\n headers,\n format,\n });\n }\n\n private requestContext() {\n return {\n baseUrl: this.baseUrl,\n config: this.config,\n setLastResponse: (response: LastResponse) => {\n this.lastResponse = response;\n },\n };\n }\n\n private assertCloud(method: string): void {\n if (this.backend !== \"cloud\") {\n throw new CloudOnlyError(method);\n }\n }\n\n private assertOss(method: string): void {\n if (this.backend !== \"oss\") {\n throw new OssOnlyError(method);\n }\n }\n}\n\nfunction splitQueryAndHeaders(query: Record<string, QueryValue>): {\n query: Record<string, QueryValue>;\n headers: HeadersInit;\n} {\n const {\n useProxy,\n proxyUrl,\n proxyCountry,\n proxyClass,\n proxyProvider,\n proxySessionId,\n tenant,\n ...cleanQuery\n } = query;\n const headers: Record<string, string> = {};\n\n addHeader(headers, \"X-Use-Proxy\", useProxy);\n addHeader(headers, \"X-Proxy-URL\", proxyUrl);\n addHeader(headers, \"X-Proxy-Country\", proxyCountry);\n addHeader(headers, \"X-Proxy-Class\", proxyClass);\n addHeader(headers, \"X-Proxy-Provider\", proxyProvider);\n addHeader(headers, \"X-Proxy-Session-ID\", proxySessionId);\n addHeader(headers, \"X-Tenant\", tenant);\n\n return { query: cleanQuery, headers };\n}\n\nfunction addHeader(headers: Record<string, string>, name: string, value: QueryValue): void {\n if (value === undefined || value === null) {\n return;\n }\n headers[name] = Array.isArray(value) ? value.join(\",\") : String(value);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/backend.ts","../src/errors.ts","../src/debug.ts","../src/request.ts","../src/client.ts"],"sourcesContent":["export { OpenSERP } from \"./client\";\nexport {\n CLOUD_BASE_URL,\n OSS_BASE_URL,\n inferBackend,\n normalizeBaseUrl,\n resolveBaseUrl,\n} from \"./backend\";\nexport {\n CaptchaError,\n CloudOnlyError,\n OssOnlyError,\n RateLimitError,\n SERPError,\n TimeoutError,\n} from \"./errors\";\nexport type {\n Backend,\n CacheStats,\n CircuitBreakerStatsResponse,\n DebugEvent,\n Engine,\n ErrorResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaMode,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ProxyStats,\n ReadinessStatus,\n ResponseFormat,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n} from \"./types/public\";\nexport type {\n CloudAccount,\n CreditInfo,\n EngineCapability,\n EngineStatus,\n EnginesCapabilities,\n EnginesStatus,\n ModeCapability,\n OverallStatus,\n Price,\n Pricing,\n} from \"./types/cloud\";\n","import type { Backend, OpenSERPConfig } from \"./types/public\";\n\nexport const OSS_BASE_URL = \"http://localhost:7000\";\nexport const CLOUD_BASE_URL = \"https://api.openserp.org/v1\";\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function resolveBaseUrl(config: OpenSERPConfig): string {\n if (config.baseUrl) {\n return normalizeBaseUrl(config.baseUrl);\n }\n\n if (config.apiKey) {\n return CLOUD_BASE_URL;\n }\n\n return OSS_BASE_URL;\n}\n\nexport function inferBackend(config: Pick<OpenSERPConfig, \"apiKey\" | \"baseUrl\" | \"backend\">): Backend {\n if (config.backend) {\n return config.backend;\n }\n\n if (config.baseUrl) {\n try {\n if (new URL(config.baseUrl).hostname === \"api.openserp.org\") {\n return \"cloud\";\n }\n } catch {\n if (config.baseUrl.includes(\"api.openserp.org\")) {\n return \"cloud\";\n }\n }\n }\n\n if (config.apiKey) {\n return \"cloud\";\n }\n\n return \"oss\";\n}\n","import type { ErrorResponse } from \"./types/public\";\n\nexport class SERPError extends Error {\n readonly status: number;\n readonly code?: string | undefined;\n readonly reason?: string | undefined;\n readonly requestId?: string | undefined;\n readonly meta?: Record<string, unknown> | undefined;\n readonly response?: ErrorResponse | unknown | undefined;\n\n constructor(message: string, options: {\n status?: number | undefined;\n code?: string | undefined;\n reason?: string | undefined;\n requestId?: string | undefined;\n meta?: Record<string, unknown> | undefined;\n response?: ErrorResponse | unknown | undefined;\n cause?: unknown | undefined;\n } = {}) {\n super(message, { cause: options.cause });\n this.name = \"SERPError\";\n this.status = options.status ?? 0;\n this.code = options.code;\n this.reason = options.reason;\n this.requestId = options.requestId;\n this.meta = options.meta;\n this.response = options.response;\n }\n}\n\nexport class RateLimitError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"RateLimitError\";\n }\n}\n\nexport class CaptchaError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"CaptchaError\";\n }\n}\n\nexport class CloudOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against OpenSERP Cloud. Configure apiKey/baseUrl for https://api.openserp.org/v1 or set client.config.backend = \"cloud\".`);\n this.name = \"CloudOnlyError\";\n }\n}\n\nexport class OssOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against a self-hosted OpenSERP server. Configure baseUrl for your OSS server or set client.config.backend = \"oss\".`);\n this.name = \"OssOnlyError\";\n }\n}\n\nexport class TimeoutError extends SERPError {\n constructor(timeoutMs: number, cause?: unknown) {\n super(`OpenSERP request timed out after ${timeoutMs}ms`, {\n code: \"request_timeout\",\n cause,\n });\n this.name = \"TimeoutError\";\n }\n}\n\nexport function errorFromResponse(status: number, body: unknown, requestId?: string): SERPError {\n const data = isErrorResponse(body) ? body : undefined;\n const code = data?.error;\n const message = data?.message ?? `OpenSERP request failed with status ${status}`;\n const options = {\n status,\n code,\n reason: data?.reason,\n requestId: data?.request_id ?? requestId,\n meta: data?.meta as Record<string, unknown> | undefined,\n response: body,\n };\n\n if (status === 429 || code === \"rate_limited\") {\n return new RateLimitError(message, options);\n }\n\n if (code === \"captcha_detected\") {\n return new CaptchaError(message, options);\n }\n\n return new SERPError(message, options);\n}\n\nfunction isErrorResponse(body: unknown): body is ErrorResponse {\n return typeof body === \"object\" && body !== null && \"error\" in body;\n}\n","import type { DebugEvent, OpenSERPConfig } from \"./types/public\";\n\nexport type DebugLevel = \"off\" | \"info\" | \"verbose\";\n\ntype DebugSink = (event: DebugEvent) => void;\n\n/** Resolved debug configuration: the active level and where events go. */\nexport interface DebugReporter {\n level: DebugLevel;\n emit(event: DebugEvent): void;\n}\n\nconst NO_OP: DebugReporter = {\n level: \"off\",\n emit() {},\n};\n\n/**\n * Resolves the effective debug behaviour from config (falling back to the\n * OPENSERP_DEBUG env var). Returns a no-op reporter when debugging is off so\n * callers can emit unconditionally without a hot-path branch of their own.\n */\nexport function resolveDebug(config: OpenSERPConfig): DebugReporter {\n const setting = config.debug ?? envDebug();\n\n if (!setting) {\n return NO_OP;\n }\n\n if (typeof setting === \"function\") {\n // A custom sink always sees verbose detail; it decides what to keep.\n return { level: \"verbose\", emit: guard(setting) };\n }\n\n const level: DebugLevel = setting === \"verbose\" ? \"verbose\" : \"info\";\n return { level, emit: consoleSink };\n}\n\nfunction envDebug(): boolean | \"verbose\" | undefined {\n // Guarded so the SDK still works in browsers / runtimes without `process`.\n const value =\n typeof process !== \"undefined\" ? process.env?.OPENSERP_DEBUG : undefined;\n if (!value) {\n return undefined;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"verbose\") {\n return \"verbose\";\n }\n if (normalized === \"1\" || normalized === \"true\") {\n return true;\n }\n return undefined;\n}\n\n/** Wraps a user-provided sink so a thrown logger never breaks a request. */\nfunction guard(sink: DebugSink): DebugSink {\n return (event) => {\n try {\n sink(event);\n } catch {\n // A broken debug logger must not take down the actual request.\n }\n };\n}\n\nconst consoleSink: DebugSink = (event) => {\n switch (event.type) {\n case \"request\":\n console.error(`[openserp] → ${event.method} ${event.url}`);\n if (event.headers) {\n console.error(\"[openserp] headers:\", event.headers);\n }\n break;\n case \"response\": {\n const engine = event.engineUsed ? ` engine=${event.engineUsed}` : \"\";\n const reqId = event.requestId ? ` request-id=${event.requestId}` : \"\";\n console.error(\n `[openserp] ← ${event.status} ${event.method} ${event.url} (${event.durationMs}ms)${engine}${reqId}`,\n );\n if (event.headers) {\n console.error(\"[openserp] headers:\", event.headers);\n }\n break;\n }\n case \"error\":\n console.error(\n `[openserp] ✕ ${event.method} ${event.url} (${event.durationMs}ms):`,\n event.error,\n );\n break;\n }\n};\n\n/** Snapshots headers into a plain object, redacting Authorization. */\nexport function dumpHeaders(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of headers) {\n out[key] = key.toLowerCase() === \"authorization\" ? \"Bearer ***\" : value;\n }\n return out;\n}\n","import { dumpHeaders, resolveDebug } from \"./debug\";\nimport { TimeoutError, errorFromResponse } from \"./errors\";\nimport type { LastResponse, OpenSERPConfig, ResponseFormat } from \"./types/public\";\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined\n | null;\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | undefined;\n path: string;\n query?: Record<string, QueryValue> | undefined;\n headers?: HeadersInit | undefined;\n body?: BodyInit | null | undefined;\n format?: ResponseFormat | undefined;\n}\n\nexport interface RequestContext {\n baseUrl: string;\n config: OpenSERPConfig;\n setLastResponse(response: LastResponse): void;\n}\n\nexport async function request<T>(\n context: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n let attempt = 0;\n\n for (;;) {\n try {\n return await requestOnce<T>(context, options);\n } catch (err) {\n const shouldRetry = await context.config.retry?.(err, attempt);\n if (!shouldRetry) {\n throw err;\n }\n attempt += 1;\n }\n }\n}\n\nasync function requestOnce<T>(\n { baseUrl, config, setLastResponse }: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new TypeError(\"OpenSERP SDK requires a fetch implementation.\");\n }\n\n const url = new URL(`${baseUrl}${options.path}`);\n appendQuery(url, options.query);\n\n const headers = new Headers(config.headers);\n if (config.apiKey) {\n headers.set(\"Authorization\", `Bearer ${config.apiKey}`);\n }\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n\n const debug = resolveDebug(config);\n const method = options.method ?? \"GET\";\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"request\",\n method,\n url: url.toString(),\n ...(debug.level === \"verbose\" ? { headers: dumpHeaders(headers) } : {}),\n });\n }\n\n const timeoutMs = config.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const startedAt = Date.now();\n\n let response: Response;\n try {\n const init: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n };\n if (options.body !== undefined) {\n init.body = options.body;\n }\n\n response = await fetchImpl(url, init);\n } catch (err) {\n const error = isAbortError(err) ? new TimeoutError(timeoutMs, err) : err;\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"error\",\n method,\n url: url.toString(),\n durationMs: Date.now() - startedAt,\n error,\n });\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n\n const last = readLastResponse(response);\n setLastResponse(last);\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"response\",\n method,\n url: url.toString(),\n status: response.status,\n engineUsed: last.engineUsed,\n requestId: last.requestId,\n durationMs: Date.now() - startedAt,\n ...(debug.level === \"verbose\" ? { headers: dumpHeaders(response.headers) } : {}),\n });\n }\n\n const body = await readBody(response, options.format);\n if (!response.ok) {\n throw errorFromResponse(response.status, body, response.headers.get(\"x-request-id\") ?? undefined);\n }\n\n return body as T;\n}\n\nfunction appendQuery(url: URL, query: Record<string, QueryValue> | undefined): void {\n if (!query) {\n return;\n }\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n const encoded = Array.isArray(value) ? value.join(\",\") : String(value);\n url.searchParams.set(key, encoded);\n }\n}\n\nasync function readBody(response: Response, format?: ResponseFormat): Promise<unknown> {\n if (response.status === 204) {\n return undefined;\n }\n\n if (format && format !== \"json\") {\n return response.text();\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return response.json();\n }\n\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction readLastResponse(response: Response): LastResponse {\n const credits = {\n used: numberHeader(response.headers, \"x-credits-used\"),\n remaining: numberHeader(response.headers, \"x-credits-remaining\"),\n };\n const hasCredits = credits.used !== undefined || credits.remaining !== undefined;\n\n return {\n status: response.status,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n credits: hasCredits ? credits : undefined,\n engineUsed: response.headers.get(\"x-engine-used\") ?? undefined,\n fallbackEngine: response.headers.get(\"x-fallback-engine\") ?? undefined,\n cache: response.headers.get(\"x-cache\") ?? undefined,\n proxyMode: response.headers.get(\"x-proxy-mode\") ?? undefined,\n proxyTag: response.headers.get(\"x-proxy-tag\") ?? undefined,\n proxyUsed: response.headers.get(\"x-proxy-used\") ?? undefined,\n networkBytes: numberHeader(response.headers, \"x-network-bytes\"),\n browserProfileId: response.headers.get(\"x-browser-profile-id\") ?? undefined,\n headers: response.headers,\n };\n}\n\nfunction numberHeader(headers: Headers, name: string): number | undefined {\n const value = headers.get(name);\n if (value === null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === \"AbortError\";\n}\n","import { inferBackend, resolveBaseUrl } from \"./backend\";\nimport { CloudOnlyError, OssOnlyError } from \"./errors\";\nimport { request, type QueryValue } from \"./request\";\nimport type {\n CacheStats,\n CircuitBreakerStatsResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ReadinessStatus,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n ProxyStats,\n} from \"./types/public\";\nimport type {\n CloudAccount,\n EnginesCapabilities,\n EnginesStatus,\n Pricing,\n} from \"./types/cloud\";\n\nexport class OpenSERP {\n readonly config: OpenSERPConfig;\n lastResponse?: LastResponse;\n\n constructor(config: OpenSERPConfig = {}) {\n this.config = { ...config };\n }\n\n get backend() {\n return inferBackend(this.config);\n }\n\n get baseUrl() {\n return resolveBaseUrl(this.config);\n }\n\n search(params: SearchParams): Promise<SearchEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<SearchEnvelope | string>(`/${engine}/search`, query, format);\n }\n\n image(params: ImageParams): Promise<ImageEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<ImageEnvelope | string>(`/${engine}/image`, query, format);\n }\n\n megaSearch(params: MegaSearchParams): Promise<MegaSearchEnvelope | string> {\n const { format, ...query } = params;\n return this.get<MegaSearchEnvelope | string>(\"/mega/search\", query, format);\n }\n\n fastSearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"fast\" });\n }\n\n anySearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"any\" });\n }\n\n megaImage(params: MegaImageParams): Promise<ImageEnvelope | string> {\n const { format, ...query } = params;\n return this.get<ImageEnvelope | string>(\"/mega/image\", query, format);\n }\n\n fastImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"fast\" });\n }\n\n anyImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"any\" });\n }\n\n parseGoogle(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseGoogle\");\n return this.parse(\"/google/parse\", params);\n }\n\n parseBing(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseBing\");\n return this.parse(\"/bing/parse\", params);\n }\n\n health(): Promise<HealthStatus> {\n this.assertOss(\"health\");\n return this.get<HealthStatus>(\"/health\");\n }\n\n ready(): Promise<ReadinessStatus> {\n this.assertOss(\"ready\");\n return this.get<ReadinessStatus>(\"/ready\");\n }\n\n stats(): Promise<StatsResponse> {\n this.assertOss(\"stats\");\n return this.get<StatsResponse>(\"/stats\");\n }\n\n cacheStats(): Promise<CacheStats> {\n this.assertOss(\"cacheStats\");\n return this.get<CacheStats>(\"/stats/cache\");\n }\n\n proxyStats(): Promise<ProxyStats> {\n this.assertOss(\"proxyStats\");\n return this.get<ProxyStats>(\"/stats/proxy\");\n }\n\n circuitBreakerStats(): Promise<CircuitBreakerStatsResponse> {\n this.assertOss(\"circuitBreakerStats\");\n return this.get<CircuitBreakerStatsResponse>(\"/stats/cb\");\n }\n\n engines(): Promise<MegaEnginesResponse> {\n this.assertOss(\"engines\");\n return this.get<MegaEnginesResponse>(\"/mega/engines\");\n }\n\n me(): Promise<CloudAccount> {\n this.assertCloud(\"me\");\n return this.get<CloudAccount>(\"/me\");\n }\n\n pricing(): Promise<Pricing> {\n this.assertCloud(\"pricing\");\n return this.get<Pricing>(\"/pricing\");\n }\n\n enginesStatus(): Promise<EnginesStatus> {\n this.assertCloud(\"enginesStatus\");\n return this.get<EnginesStatus>(\"/engines/status\");\n }\n\n enginesCapabilities(): Promise<EnginesCapabilities> {\n this.assertCloud(\"enginesCapabilities\");\n return this.get<EnginesCapabilities>(\"/engines/capabilities\");\n }\n\n private parse(path: string, params: ParseParams): Promise<SearchEnvelope | string> {\n return request<SearchEnvelope | string>(this.requestContext(), {\n method: \"POST\",\n path,\n query: params.format ? { format: params.format } : undefined,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n },\n body: params.html,\n format: params.format,\n });\n }\n\n private get<T>(\n path: string,\n query?: Record<string, QueryValue>,\n format?: SearchParams[\"format\"],\n ): Promise<T> {\n const { query: cleanQuery, headers } = splitQueryAndHeaders({\n ...query,\n format,\n });\n\n return request<T>(this.requestContext(), {\n path,\n query: cleanQuery,\n headers,\n format,\n });\n }\n\n private requestContext() {\n return {\n baseUrl: this.baseUrl,\n config: this.config,\n setLastResponse: (response: LastResponse) => {\n this.lastResponse = response;\n },\n };\n }\n\n private assertCloud(method: string): void {\n if (this.backend !== \"cloud\") {\n throw new CloudOnlyError(method);\n }\n }\n\n private assertOss(method: string): void {\n if (this.backend !== \"oss\") {\n throw new OssOnlyError(method);\n }\n }\n}\n\nfunction splitQueryAndHeaders(query: Record<string, QueryValue>): {\n query: Record<string, QueryValue>;\n headers: HeadersInit;\n} {\n const {\n useProxy,\n proxyUrl,\n proxyCountry,\n proxyClass,\n proxyProvider,\n proxySessionId,\n tenant,\n ...cleanQuery\n } = query;\n const headers: Record<string, string> = {};\n\n addHeader(headers, \"X-Use-Proxy\", useProxy);\n addHeader(headers, \"X-Proxy-URL\", proxyUrl);\n addHeader(headers, \"X-Proxy-Country\", proxyCountry);\n addHeader(headers, \"X-Proxy-Class\", proxyClass);\n addHeader(headers, \"X-Proxy-Provider\", proxyProvider);\n addHeader(headers, \"X-Proxy-Session-ID\", proxySessionId);\n addHeader(headers, \"X-Tenant\", tenant);\n\n return { query: cleanQuery, headers };\n}\n\nfunction addHeader(headers: Record<string, string>, name: string, value: QueryValue): void {\n if (value === undefined || value === null) {\n return;\n }\n headers[name] = Array.isArray(value) ? value.join(\",\") : String(value);\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -77,7 +77,44 @@ interface OpenSERPConfig {
77
77
  fetch?: typeof fetch | undefined;
78
78
  headers?: HeadersInit | undefined;
79
79
  retry?: ((err: unknown, attempt: number) => boolean | Promise<boolean>) | undefined;
80
- }
80
+ /**
81
+ * Logs each request and response to help debug what the SDK actually sends.
82
+ * - `true` / `"info"`: logs method, URL (with query string), and response
83
+ * status / engine-used / request-id via `console.error`.
84
+ * - `"verbose"`: additionally logs request and response headers
85
+ * (the `Authorization` header is redacted).
86
+ * - A function: receives a structured {@link DebugEvent} so you can route it
87
+ * to your own logger instead of the console.
88
+ *
89
+ * Defaults to off. The `OPENSERP_DEBUG` env var (`1`/`true`/`verbose`) enables
90
+ * it when this option is left unset and `process` is available.
91
+ */
92
+ debug?: boolean | "info" | "verbose" | ((event: DebugEvent) => void) | undefined;
93
+ }
94
+ /** A single request/response lifecycle event emitted when debug is enabled. */
95
+ type DebugEvent = {
96
+ type: "request";
97
+ method: string;
98
+ url: string;
99
+ /** Present only at "verbose" level. Authorization is redacted. */
100
+ headers?: Record<string, string>;
101
+ } | {
102
+ type: "response";
103
+ method: string;
104
+ url: string;
105
+ status: number;
106
+ engineUsed?: string | undefined;
107
+ requestId?: string | undefined;
108
+ durationMs: number;
109
+ /** Present only at "verbose" level. */
110
+ headers?: Record<string, string>;
111
+ } | {
112
+ type: "error";
113
+ method: string;
114
+ url: string;
115
+ durationMs: number;
116
+ error: unknown;
117
+ };
81
118
  interface LastResponse {
82
119
  status: number;
83
120
  requestId?: string | undefined;
@@ -199,4 +236,4 @@ declare class TimeoutError extends SERPError {
199
236
  constructor(timeoutMs: number, cause?: unknown);
200
237
  }
201
238
 
202
- export { type Backend, CLOUD_BASE_URL, type CacheStats, CaptchaError, type CircuitBreakerStatsResponse, type CloudAccount, CloudOnlyError, type CreditInfo, type Engine, type EngineCapability, type EngineStatus, type EnginesCapabilities, type EnginesStatus, type ErrorResponse, type HealthStatus, type ImageEnvelope, type ImageParams, type LastResponse, type MegaEnginesResponse, type MegaImageParams, type MegaMode, type MegaSearchEnvelope, type MegaSearchParams, type ModeCapability, OSS_BASE_URL, OpenSERP, type OpenSERPConfig, OssOnlyError, type OverallStatus, type ParseParams, type Price, type Pricing, type ProxyStats, RateLimitError, type ReadinessStatus, type ResponseFormat, SERPError, type SearchEnvelope, type SearchParams, type StatsResponse, TimeoutError, inferBackend, normalizeBaseUrl, resolveBaseUrl };
239
+ export { type Backend, CLOUD_BASE_URL, type CacheStats, CaptchaError, type CircuitBreakerStatsResponse, type CloudAccount, CloudOnlyError, type CreditInfo, type DebugEvent, type Engine, type EngineCapability, type EngineStatus, type EnginesCapabilities, type EnginesStatus, type ErrorResponse, type HealthStatus, type ImageEnvelope, type ImageParams, type LastResponse, type MegaEnginesResponse, type MegaImageParams, type MegaMode, type MegaSearchEnvelope, type MegaSearchParams, type ModeCapability, OSS_BASE_URL, OpenSERP, type OpenSERPConfig, OssOnlyError, type OverallStatus, type ParseParams, type Price, type Pricing, type ProxyStats, RateLimitError, type ReadinessStatus, type ResponseFormat, SERPError, type SearchEnvelope, type SearchParams, type StatsResponse, TimeoutError, inferBackend, normalizeBaseUrl, resolveBaseUrl };
package/dist/index.d.ts CHANGED
@@ -77,7 +77,44 @@ interface OpenSERPConfig {
77
77
  fetch?: typeof fetch | undefined;
78
78
  headers?: HeadersInit | undefined;
79
79
  retry?: ((err: unknown, attempt: number) => boolean | Promise<boolean>) | undefined;
80
- }
80
+ /**
81
+ * Logs each request and response to help debug what the SDK actually sends.
82
+ * - `true` / `"info"`: logs method, URL (with query string), and response
83
+ * status / engine-used / request-id via `console.error`.
84
+ * - `"verbose"`: additionally logs request and response headers
85
+ * (the `Authorization` header is redacted).
86
+ * - A function: receives a structured {@link DebugEvent} so you can route it
87
+ * to your own logger instead of the console.
88
+ *
89
+ * Defaults to off. The `OPENSERP_DEBUG` env var (`1`/`true`/`verbose`) enables
90
+ * it when this option is left unset and `process` is available.
91
+ */
92
+ debug?: boolean | "info" | "verbose" | ((event: DebugEvent) => void) | undefined;
93
+ }
94
+ /** A single request/response lifecycle event emitted when debug is enabled. */
95
+ type DebugEvent = {
96
+ type: "request";
97
+ method: string;
98
+ url: string;
99
+ /** Present only at "verbose" level. Authorization is redacted. */
100
+ headers?: Record<string, string>;
101
+ } | {
102
+ type: "response";
103
+ method: string;
104
+ url: string;
105
+ status: number;
106
+ engineUsed?: string | undefined;
107
+ requestId?: string | undefined;
108
+ durationMs: number;
109
+ /** Present only at "verbose" level. */
110
+ headers?: Record<string, string>;
111
+ } | {
112
+ type: "error";
113
+ method: string;
114
+ url: string;
115
+ durationMs: number;
116
+ error: unknown;
117
+ };
81
118
  interface LastResponse {
82
119
  status: number;
83
120
  requestId?: string | undefined;
@@ -199,4 +236,4 @@ declare class TimeoutError extends SERPError {
199
236
  constructor(timeoutMs: number, cause?: unknown);
200
237
  }
201
238
 
202
- export { type Backend, CLOUD_BASE_URL, type CacheStats, CaptchaError, type CircuitBreakerStatsResponse, type CloudAccount, CloudOnlyError, type CreditInfo, type Engine, type EngineCapability, type EngineStatus, type EnginesCapabilities, type EnginesStatus, type ErrorResponse, type HealthStatus, type ImageEnvelope, type ImageParams, type LastResponse, type MegaEnginesResponse, type MegaImageParams, type MegaMode, type MegaSearchEnvelope, type MegaSearchParams, type ModeCapability, OSS_BASE_URL, OpenSERP, type OpenSERPConfig, OssOnlyError, type OverallStatus, type ParseParams, type Price, type Pricing, type ProxyStats, RateLimitError, type ReadinessStatus, type ResponseFormat, SERPError, type SearchEnvelope, type SearchParams, type StatsResponse, TimeoutError, inferBackend, normalizeBaseUrl, resolveBaseUrl };
239
+ export { type Backend, CLOUD_BASE_URL, type CacheStats, CaptchaError, type CircuitBreakerStatsResponse, type CloudAccount, CloudOnlyError, type CreditInfo, type DebugEvent, type Engine, type EngineCapability, type EngineStatus, type EnginesCapabilities, type EnginesStatus, type ErrorResponse, type HealthStatus, type ImageEnvelope, type ImageParams, type LastResponse, type MegaEnginesResponse, type MegaImageParams, type MegaMode, type MegaSearchEnvelope, type MegaSearchParams, type ModeCapability, OSS_BASE_URL, OpenSERP, type OpenSERPConfig, OssOnlyError, type OverallStatus, type ParseParams, type Price, type Pricing, type ProxyStats, RateLimitError, type ReadinessStatus, type ResponseFormat, SERPError, type SearchEnvelope, type SearchParams, type StatsResponse, TimeoutError, inferBackend, normalizeBaseUrl, resolveBaseUrl };
package/dist/index.js CHANGED
@@ -110,6 +110,80 @@ function isErrorResponse(body) {
110
110
  return typeof body === "object" && body !== null && "error" in body;
111
111
  }
112
112
 
113
+ // src/debug.ts
114
+ var NO_OP = {
115
+ level: "off",
116
+ emit() {
117
+ }
118
+ };
119
+ function resolveDebug(config) {
120
+ const setting = config.debug ?? envDebug();
121
+ if (!setting) {
122
+ return NO_OP;
123
+ }
124
+ if (typeof setting === "function") {
125
+ return { level: "verbose", emit: guard(setting) };
126
+ }
127
+ const level = setting === "verbose" ? "verbose" : "info";
128
+ return { level, emit: consoleSink };
129
+ }
130
+ function envDebug() {
131
+ const value = typeof process !== "undefined" ? process.env?.OPENSERP_DEBUG : void 0;
132
+ if (!value) {
133
+ return void 0;
134
+ }
135
+ const normalized = value.trim().toLowerCase();
136
+ if (normalized === "verbose") {
137
+ return "verbose";
138
+ }
139
+ if (normalized === "1" || normalized === "true") {
140
+ return true;
141
+ }
142
+ return void 0;
143
+ }
144
+ function guard(sink) {
145
+ return (event) => {
146
+ try {
147
+ sink(event);
148
+ } catch {
149
+ }
150
+ };
151
+ }
152
+ var consoleSink = (event) => {
153
+ switch (event.type) {
154
+ case "request":
155
+ console.error(`[openserp] \u2192 ${event.method} ${event.url}`);
156
+ if (event.headers) {
157
+ console.error("[openserp] headers:", event.headers);
158
+ }
159
+ break;
160
+ case "response": {
161
+ const engine = event.engineUsed ? ` engine=${event.engineUsed}` : "";
162
+ const reqId = event.requestId ? ` request-id=${event.requestId}` : "";
163
+ console.error(
164
+ `[openserp] \u2190 ${event.status} ${event.method} ${event.url} (${event.durationMs}ms)${engine}${reqId}`
165
+ );
166
+ if (event.headers) {
167
+ console.error("[openserp] headers:", event.headers);
168
+ }
169
+ break;
170
+ }
171
+ case "error":
172
+ console.error(
173
+ `[openserp] \u2715 ${event.method} ${event.url} (${event.durationMs}ms):`,
174
+ event.error
175
+ );
176
+ break;
177
+ }
178
+ };
179
+ function dumpHeaders(headers) {
180
+ const out = {};
181
+ for (const [key, value] of headers) {
182
+ out[key] = key.toLowerCase() === "authorization" ? "Bearer ***" : value;
183
+ }
184
+ return out;
185
+ }
186
+
113
187
  // src/request.ts
114
188
  async function request(context, options) {
115
189
  let attempt = 0;
@@ -139,13 +213,24 @@ async function requestOnce({ baseUrl, config, setLastResponse }, options) {
139
213
  for (const [key, value] of new Headers(options.headers)) {
140
214
  headers.set(key, value);
141
215
  }
216
+ const debug = resolveDebug(config);
217
+ const method = options.method ?? "GET";
218
+ if (debug.level !== "off") {
219
+ debug.emit({
220
+ type: "request",
221
+ method,
222
+ url: url.toString(),
223
+ ...debug.level === "verbose" ? { headers: dumpHeaders(headers) } : {}
224
+ });
225
+ }
142
226
  const timeoutMs = config.timeoutMs ?? 3e4;
143
227
  const controller = new AbortController();
144
228
  const timer = setTimeout(() => controller.abort(), timeoutMs);
229
+ const startedAt = Date.now();
145
230
  let response;
146
231
  try {
147
232
  const init = {
148
- method: options.method ?? "GET",
233
+ method,
149
234
  headers,
150
235
  signal: controller.signal
151
236
  };
@@ -154,14 +239,34 @@ async function requestOnce({ baseUrl, config, setLastResponse }, options) {
154
239
  }
155
240
  response = await fetchImpl(url, init);
156
241
  } catch (err) {
157
- if (isAbortError(err)) {
158
- throw new TimeoutError(timeoutMs, err);
242
+ const error = isAbortError(err) ? new TimeoutError(timeoutMs, err) : err;
243
+ if (debug.level !== "off") {
244
+ debug.emit({
245
+ type: "error",
246
+ method,
247
+ url: url.toString(),
248
+ durationMs: Date.now() - startedAt,
249
+ error
250
+ });
159
251
  }
160
- throw err;
252
+ throw error;
161
253
  } finally {
162
254
  clearTimeout(timer);
163
255
  }
164
- setLastResponse(readLastResponse(response));
256
+ const last = readLastResponse(response);
257
+ setLastResponse(last);
258
+ if (debug.level !== "off") {
259
+ debug.emit({
260
+ type: "response",
261
+ method,
262
+ url: url.toString(),
263
+ status: response.status,
264
+ engineUsed: last.engineUsed,
265
+ requestId: last.requestId,
266
+ durationMs: Date.now() - startedAt,
267
+ ...debug.level === "verbose" ? { headers: dumpHeaders(response.headers) } : {}
268
+ });
269
+ }
165
270
  const body = await readBody(response, options.format);
166
271
  if (!response.ok) {
167
272
  throw errorFromResponse(response.status, body, response.headers.get("x-request-id") ?? void 0);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/backend.ts","../src/errors.ts","../src/request.ts","../src/client.ts"],"sourcesContent":["import type { Backend, OpenSERPConfig } from \"./types/public\";\n\nexport const OSS_BASE_URL = \"http://localhost:7000\";\nexport const CLOUD_BASE_URL = \"https://api.openserp.org/v1\";\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function resolveBaseUrl(config: OpenSERPConfig): string {\n if (config.baseUrl) {\n return normalizeBaseUrl(config.baseUrl);\n }\n\n if (config.apiKey) {\n return CLOUD_BASE_URL;\n }\n\n return OSS_BASE_URL;\n}\n\nexport function inferBackend(config: Pick<OpenSERPConfig, \"apiKey\" | \"baseUrl\" | \"backend\">): Backend {\n if (config.backend) {\n return config.backend;\n }\n\n if (config.baseUrl) {\n try {\n if (new URL(config.baseUrl).hostname === \"api.openserp.org\") {\n return \"cloud\";\n }\n } catch {\n if (config.baseUrl.includes(\"api.openserp.org\")) {\n return \"cloud\";\n }\n }\n }\n\n if (config.apiKey) {\n return \"cloud\";\n }\n\n return \"oss\";\n}\n","import type { ErrorResponse } from \"./types/public\";\n\nexport class SERPError extends Error {\n readonly status: number;\n readonly code?: string | undefined;\n readonly reason?: string | undefined;\n readonly requestId?: string | undefined;\n readonly meta?: Record<string, unknown> | undefined;\n readonly response?: ErrorResponse | unknown | undefined;\n\n constructor(message: string, options: {\n status?: number | undefined;\n code?: string | undefined;\n reason?: string | undefined;\n requestId?: string | undefined;\n meta?: Record<string, unknown> | undefined;\n response?: ErrorResponse | unknown | undefined;\n cause?: unknown | undefined;\n } = {}) {\n super(message, { cause: options.cause });\n this.name = \"SERPError\";\n this.status = options.status ?? 0;\n this.code = options.code;\n this.reason = options.reason;\n this.requestId = options.requestId;\n this.meta = options.meta;\n this.response = options.response;\n }\n}\n\nexport class RateLimitError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"RateLimitError\";\n }\n}\n\nexport class CaptchaError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"CaptchaError\";\n }\n}\n\nexport class CloudOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against OpenSERP Cloud. Configure apiKey/baseUrl for https://api.openserp.org/v1 or set client.config.backend = \"cloud\".`);\n this.name = \"CloudOnlyError\";\n }\n}\n\nexport class OssOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against a self-hosted OpenSERP server. Configure baseUrl for your OSS server or set client.config.backend = \"oss\".`);\n this.name = \"OssOnlyError\";\n }\n}\n\nexport class TimeoutError extends SERPError {\n constructor(timeoutMs: number, cause?: unknown) {\n super(`OpenSERP request timed out after ${timeoutMs}ms`, {\n code: \"request_timeout\",\n cause,\n });\n this.name = \"TimeoutError\";\n }\n}\n\nexport function errorFromResponse(status: number, body: unknown, requestId?: string): SERPError {\n const data = isErrorResponse(body) ? body : undefined;\n const code = data?.error;\n const message = data?.message ?? `OpenSERP request failed with status ${status}`;\n const options = {\n status,\n code,\n reason: data?.reason,\n requestId: data?.request_id ?? requestId,\n meta: data?.meta as Record<string, unknown> | undefined,\n response: body,\n };\n\n if (status === 429 || code === \"rate_limited\") {\n return new RateLimitError(message, options);\n }\n\n if (code === \"captcha_detected\") {\n return new CaptchaError(message, options);\n }\n\n return new SERPError(message, options);\n}\n\nfunction isErrorResponse(body: unknown): body is ErrorResponse {\n return typeof body === \"object\" && body !== null && \"error\" in body;\n}\n","import { TimeoutError, errorFromResponse } from \"./errors\";\nimport type { LastResponse, OpenSERPConfig, ResponseFormat } from \"./types/public\";\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined\n | null;\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | undefined;\n path: string;\n query?: Record<string, QueryValue> | undefined;\n headers?: HeadersInit | undefined;\n body?: BodyInit | null | undefined;\n format?: ResponseFormat | undefined;\n}\n\nexport interface RequestContext {\n baseUrl: string;\n config: OpenSERPConfig;\n setLastResponse(response: LastResponse): void;\n}\n\nexport async function request<T>(\n context: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n let attempt = 0;\n\n for (;;) {\n try {\n return await requestOnce<T>(context, options);\n } catch (err) {\n const shouldRetry = await context.config.retry?.(err, attempt);\n if (!shouldRetry) {\n throw err;\n }\n attempt += 1;\n }\n }\n}\n\nasync function requestOnce<T>(\n { baseUrl, config, setLastResponse }: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new TypeError(\"OpenSERP SDK requires a fetch implementation.\");\n }\n\n const url = new URL(`${baseUrl}${options.path}`);\n appendQuery(url, options.query);\n\n const headers = new Headers(config.headers);\n if (config.apiKey) {\n headers.set(\"Authorization\", `Bearer ${config.apiKey}`);\n }\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n\n const timeoutMs = config.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let response: Response;\n try {\n const init: RequestInit = {\n method: options.method ?? \"GET\",\n headers,\n signal: controller.signal,\n };\n if (options.body !== undefined) {\n init.body = options.body;\n }\n\n response = await fetchImpl(url, init);\n } catch (err) {\n if (isAbortError(err)) {\n throw new TimeoutError(timeoutMs, err);\n }\n throw err;\n } finally {\n clearTimeout(timer);\n }\n\n setLastResponse(readLastResponse(response));\n\n const body = await readBody(response, options.format);\n if (!response.ok) {\n throw errorFromResponse(response.status, body, response.headers.get(\"x-request-id\") ?? undefined);\n }\n\n return body as T;\n}\n\nfunction appendQuery(url: URL, query: Record<string, QueryValue> | undefined): void {\n if (!query) {\n return;\n }\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n const encoded = Array.isArray(value) ? value.join(\",\") : String(value);\n url.searchParams.set(key, encoded);\n }\n}\n\nasync function readBody(response: Response, format?: ResponseFormat): Promise<unknown> {\n if (response.status === 204) {\n return undefined;\n }\n\n if (format && format !== \"json\") {\n return response.text();\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return response.json();\n }\n\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction readLastResponse(response: Response): LastResponse {\n const credits = {\n used: numberHeader(response.headers, \"x-credits-used\"),\n remaining: numberHeader(response.headers, \"x-credits-remaining\"),\n };\n const hasCredits = credits.used !== undefined || credits.remaining !== undefined;\n\n return {\n status: response.status,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n credits: hasCredits ? credits : undefined,\n engineUsed: response.headers.get(\"x-engine-used\") ?? undefined,\n fallbackEngine: response.headers.get(\"x-fallback-engine\") ?? undefined,\n cache: response.headers.get(\"x-cache\") ?? undefined,\n proxyMode: response.headers.get(\"x-proxy-mode\") ?? undefined,\n proxyTag: response.headers.get(\"x-proxy-tag\") ?? undefined,\n proxyUsed: response.headers.get(\"x-proxy-used\") ?? undefined,\n networkBytes: numberHeader(response.headers, \"x-network-bytes\"),\n browserProfileId: response.headers.get(\"x-browser-profile-id\") ?? undefined,\n headers: response.headers,\n };\n}\n\nfunction numberHeader(headers: Headers, name: string): number | undefined {\n const value = headers.get(name);\n if (value === null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === \"AbortError\";\n}\n","import { inferBackend, resolveBaseUrl } from \"./backend\";\nimport { CloudOnlyError, OssOnlyError } from \"./errors\";\nimport { request, type QueryValue } from \"./request\";\nimport type {\n CacheStats,\n CircuitBreakerStatsResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ReadinessStatus,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n ProxyStats,\n} from \"./types/public\";\nimport type {\n CloudAccount,\n EnginesCapabilities,\n EnginesStatus,\n Pricing,\n} from \"./types/cloud\";\n\nexport class OpenSERP {\n readonly config: OpenSERPConfig;\n lastResponse?: LastResponse;\n\n constructor(config: OpenSERPConfig = {}) {\n this.config = { ...config };\n }\n\n get backend() {\n return inferBackend(this.config);\n }\n\n get baseUrl() {\n return resolveBaseUrl(this.config);\n }\n\n search(params: SearchParams): Promise<SearchEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<SearchEnvelope | string>(`/${engine}/search`, query, format);\n }\n\n image(params: ImageParams): Promise<ImageEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<ImageEnvelope | string>(`/${engine}/image`, query, format);\n }\n\n megaSearch(params: MegaSearchParams): Promise<MegaSearchEnvelope | string> {\n const { format, ...query } = params;\n return this.get<MegaSearchEnvelope | string>(\"/mega/search\", query, format);\n }\n\n fastSearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"fast\" });\n }\n\n anySearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"any\" });\n }\n\n megaImage(params: MegaImageParams): Promise<ImageEnvelope | string> {\n const { format, ...query } = params;\n return this.get<ImageEnvelope | string>(\"/mega/image\", query, format);\n }\n\n fastImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"fast\" });\n }\n\n anyImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"any\" });\n }\n\n parseGoogle(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseGoogle\");\n return this.parse(\"/google/parse\", params);\n }\n\n parseBing(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseBing\");\n return this.parse(\"/bing/parse\", params);\n }\n\n health(): Promise<HealthStatus> {\n this.assertOss(\"health\");\n return this.get<HealthStatus>(\"/health\");\n }\n\n ready(): Promise<ReadinessStatus> {\n this.assertOss(\"ready\");\n return this.get<ReadinessStatus>(\"/ready\");\n }\n\n stats(): Promise<StatsResponse> {\n this.assertOss(\"stats\");\n return this.get<StatsResponse>(\"/stats\");\n }\n\n cacheStats(): Promise<CacheStats> {\n this.assertOss(\"cacheStats\");\n return this.get<CacheStats>(\"/stats/cache\");\n }\n\n proxyStats(): Promise<ProxyStats> {\n this.assertOss(\"proxyStats\");\n return this.get<ProxyStats>(\"/stats/proxy\");\n }\n\n circuitBreakerStats(): Promise<CircuitBreakerStatsResponse> {\n this.assertOss(\"circuitBreakerStats\");\n return this.get<CircuitBreakerStatsResponse>(\"/stats/cb\");\n }\n\n engines(): Promise<MegaEnginesResponse> {\n this.assertOss(\"engines\");\n return this.get<MegaEnginesResponse>(\"/mega/engines\");\n }\n\n me(): Promise<CloudAccount> {\n this.assertCloud(\"me\");\n return this.get<CloudAccount>(\"/me\");\n }\n\n pricing(): Promise<Pricing> {\n this.assertCloud(\"pricing\");\n return this.get<Pricing>(\"/pricing\");\n }\n\n enginesStatus(): Promise<EnginesStatus> {\n this.assertCloud(\"enginesStatus\");\n return this.get<EnginesStatus>(\"/engines/status\");\n }\n\n enginesCapabilities(): Promise<EnginesCapabilities> {\n this.assertCloud(\"enginesCapabilities\");\n return this.get<EnginesCapabilities>(\"/engines/capabilities\");\n }\n\n private parse(path: string, params: ParseParams): Promise<SearchEnvelope | string> {\n return request<SearchEnvelope | string>(this.requestContext(), {\n method: \"POST\",\n path,\n query: params.format ? { format: params.format } : undefined,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n },\n body: params.html,\n format: params.format,\n });\n }\n\n private get<T>(\n path: string,\n query?: Record<string, QueryValue>,\n format?: SearchParams[\"format\"],\n ): Promise<T> {\n const { query: cleanQuery, headers } = splitQueryAndHeaders({\n ...query,\n format,\n });\n\n return request<T>(this.requestContext(), {\n path,\n query: cleanQuery,\n headers,\n format,\n });\n }\n\n private requestContext() {\n return {\n baseUrl: this.baseUrl,\n config: this.config,\n setLastResponse: (response: LastResponse) => {\n this.lastResponse = response;\n },\n };\n }\n\n private assertCloud(method: string): void {\n if (this.backend !== \"cloud\") {\n throw new CloudOnlyError(method);\n }\n }\n\n private assertOss(method: string): void {\n if (this.backend !== \"oss\") {\n throw new OssOnlyError(method);\n }\n }\n}\n\nfunction splitQueryAndHeaders(query: Record<string, QueryValue>): {\n query: Record<string, QueryValue>;\n headers: HeadersInit;\n} {\n const {\n useProxy,\n proxyUrl,\n proxyCountry,\n proxyClass,\n proxyProvider,\n proxySessionId,\n tenant,\n ...cleanQuery\n } = query;\n const headers: Record<string, string> = {};\n\n addHeader(headers, \"X-Use-Proxy\", useProxy);\n addHeader(headers, \"X-Proxy-URL\", proxyUrl);\n addHeader(headers, \"X-Proxy-Country\", proxyCountry);\n addHeader(headers, \"X-Proxy-Class\", proxyClass);\n addHeader(headers, \"X-Proxy-Provider\", proxyProvider);\n addHeader(headers, \"X-Proxy-Session-ID\", proxySessionId);\n addHeader(headers, \"X-Tenant\", tenant);\n\n return { query: cleanQuery, headers };\n}\n\nfunction addHeader(headers: Record<string, string>, name: string, value: QueryValue): void {\n if (value === undefined || value === null) {\n return;\n }\n headers[name] = Array.isArray(value) ? value.join(\",\") : String(value);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/backend.ts","../src/errors.ts","../src/debug.ts","../src/request.ts","../src/client.ts"],"sourcesContent":["import type { Backend, OpenSERPConfig } from \"./types/public\";\n\nexport const OSS_BASE_URL = \"http://localhost:7000\";\nexport const CLOUD_BASE_URL = \"https://api.openserp.org/v1\";\n\nexport function normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, \"\");\n}\n\nexport function resolveBaseUrl(config: OpenSERPConfig): string {\n if (config.baseUrl) {\n return normalizeBaseUrl(config.baseUrl);\n }\n\n if (config.apiKey) {\n return CLOUD_BASE_URL;\n }\n\n return OSS_BASE_URL;\n}\n\nexport function inferBackend(config: Pick<OpenSERPConfig, \"apiKey\" | \"baseUrl\" | \"backend\">): Backend {\n if (config.backend) {\n return config.backend;\n }\n\n if (config.baseUrl) {\n try {\n if (new URL(config.baseUrl).hostname === \"api.openserp.org\") {\n return \"cloud\";\n }\n } catch {\n if (config.baseUrl.includes(\"api.openserp.org\")) {\n return \"cloud\";\n }\n }\n }\n\n if (config.apiKey) {\n return \"cloud\";\n }\n\n return \"oss\";\n}\n","import type { ErrorResponse } from \"./types/public\";\n\nexport class SERPError extends Error {\n readonly status: number;\n readonly code?: string | undefined;\n readonly reason?: string | undefined;\n readonly requestId?: string | undefined;\n readonly meta?: Record<string, unknown> | undefined;\n readonly response?: ErrorResponse | unknown | undefined;\n\n constructor(message: string, options: {\n status?: number | undefined;\n code?: string | undefined;\n reason?: string | undefined;\n requestId?: string | undefined;\n meta?: Record<string, unknown> | undefined;\n response?: ErrorResponse | unknown | undefined;\n cause?: unknown | undefined;\n } = {}) {\n super(message, { cause: options.cause });\n this.name = \"SERPError\";\n this.status = options.status ?? 0;\n this.code = options.code;\n this.reason = options.reason;\n this.requestId = options.requestId;\n this.meta = options.meta;\n this.response = options.response;\n }\n}\n\nexport class RateLimitError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"RateLimitError\";\n }\n}\n\nexport class CaptchaError extends SERPError {\n constructor(message: string, options: ConstructorParameters<typeof SERPError>[1] = {}) {\n super(message, options);\n this.name = \"CaptchaError\";\n }\n}\n\nexport class CloudOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against OpenSERP Cloud. Configure apiKey/baseUrl for https://api.openserp.org/v1 or set client.config.backend = \"cloud\".`);\n this.name = \"CloudOnlyError\";\n }\n}\n\nexport class OssOnlyError extends SERPError {\n constructor(method: string) {\n super(`${method} is only available against a self-hosted OpenSERP server. Configure baseUrl for your OSS server or set client.config.backend = \"oss\".`);\n this.name = \"OssOnlyError\";\n }\n}\n\nexport class TimeoutError extends SERPError {\n constructor(timeoutMs: number, cause?: unknown) {\n super(`OpenSERP request timed out after ${timeoutMs}ms`, {\n code: \"request_timeout\",\n cause,\n });\n this.name = \"TimeoutError\";\n }\n}\n\nexport function errorFromResponse(status: number, body: unknown, requestId?: string): SERPError {\n const data = isErrorResponse(body) ? body : undefined;\n const code = data?.error;\n const message = data?.message ?? `OpenSERP request failed with status ${status}`;\n const options = {\n status,\n code,\n reason: data?.reason,\n requestId: data?.request_id ?? requestId,\n meta: data?.meta as Record<string, unknown> | undefined,\n response: body,\n };\n\n if (status === 429 || code === \"rate_limited\") {\n return new RateLimitError(message, options);\n }\n\n if (code === \"captcha_detected\") {\n return new CaptchaError(message, options);\n }\n\n return new SERPError(message, options);\n}\n\nfunction isErrorResponse(body: unknown): body is ErrorResponse {\n return typeof body === \"object\" && body !== null && \"error\" in body;\n}\n","import type { DebugEvent, OpenSERPConfig } from \"./types/public\";\n\nexport type DebugLevel = \"off\" | \"info\" | \"verbose\";\n\ntype DebugSink = (event: DebugEvent) => void;\n\n/** Resolved debug configuration: the active level and where events go. */\nexport interface DebugReporter {\n level: DebugLevel;\n emit(event: DebugEvent): void;\n}\n\nconst NO_OP: DebugReporter = {\n level: \"off\",\n emit() {},\n};\n\n/**\n * Resolves the effective debug behaviour from config (falling back to the\n * OPENSERP_DEBUG env var). Returns a no-op reporter when debugging is off so\n * callers can emit unconditionally without a hot-path branch of their own.\n */\nexport function resolveDebug(config: OpenSERPConfig): DebugReporter {\n const setting = config.debug ?? envDebug();\n\n if (!setting) {\n return NO_OP;\n }\n\n if (typeof setting === \"function\") {\n // A custom sink always sees verbose detail; it decides what to keep.\n return { level: \"verbose\", emit: guard(setting) };\n }\n\n const level: DebugLevel = setting === \"verbose\" ? \"verbose\" : \"info\";\n return { level, emit: consoleSink };\n}\n\nfunction envDebug(): boolean | \"verbose\" | undefined {\n // Guarded so the SDK still works in browsers / runtimes without `process`.\n const value =\n typeof process !== \"undefined\" ? process.env?.OPENSERP_DEBUG : undefined;\n if (!value) {\n return undefined;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"verbose\") {\n return \"verbose\";\n }\n if (normalized === \"1\" || normalized === \"true\") {\n return true;\n }\n return undefined;\n}\n\n/** Wraps a user-provided sink so a thrown logger never breaks a request. */\nfunction guard(sink: DebugSink): DebugSink {\n return (event) => {\n try {\n sink(event);\n } catch {\n // A broken debug logger must not take down the actual request.\n }\n };\n}\n\nconst consoleSink: DebugSink = (event) => {\n switch (event.type) {\n case \"request\":\n console.error(`[openserp] → ${event.method} ${event.url}`);\n if (event.headers) {\n console.error(\"[openserp] headers:\", event.headers);\n }\n break;\n case \"response\": {\n const engine = event.engineUsed ? ` engine=${event.engineUsed}` : \"\";\n const reqId = event.requestId ? ` request-id=${event.requestId}` : \"\";\n console.error(\n `[openserp] ← ${event.status} ${event.method} ${event.url} (${event.durationMs}ms)${engine}${reqId}`,\n );\n if (event.headers) {\n console.error(\"[openserp] headers:\", event.headers);\n }\n break;\n }\n case \"error\":\n console.error(\n `[openserp] ✕ ${event.method} ${event.url} (${event.durationMs}ms):`,\n event.error,\n );\n break;\n }\n};\n\n/** Snapshots headers into a plain object, redacting Authorization. */\nexport function dumpHeaders(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of headers) {\n out[key] = key.toLowerCase() === \"authorization\" ? \"Bearer ***\" : value;\n }\n return out;\n}\n","import { dumpHeaders, resolveDebug } from \"./debug\";\nimport { TimeoutError, errorFromResponse } from \"./errors\";\nimport type { LastResponse, OpenSERPConfig, ResponseFormat } from \"./types/public\";\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | readonly string[]\n | undefined\n | null;\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\" | undefined;\n path: string;\n query?: Record<string, QueryValue> | undefined;\n headers?: HeadersInit | undefined;\n body?: BodyInit | null | undefined;\n format?: ResponseFormat | undefined;\n}\n\nexport interface RequestContext {\n baseUrl: string;\n config: OpenSERPConfig;\n setLastResponse(response: LastResponse): void;\n}\n\nexport async function request<T>(\n context: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n let attempt = 0;\n\n for (;;) {\n try {\n return await requestOnce<T>(context, options);\n } catch (err) {\n const shouldRetry = await context.config.retry?.(err, attempt);\n if (!shouldRetry) {\n throw err;\n }\n attempt += 1;\n }\n }\n}\n\nasync function requestOnce<T>(\n { baseUrl, config, setLastResponse }: RequestContext,\n options: RequestOptions,\n): Promise<T> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new TypeError(\"OpenSERP SDK requires a fetch implementation.\");\n }\n\n const url = new URL(`${baseUrl}${options.path}`);\n appendQuery(url, options.query);\n\n const headers = new Headers(config.headers);\n if (config.apiKey) {\n headers.set(\"Authorization\", `Bearer ${config.apiKey}`);\n }\n for (const [key, value] of new Headers(options.headers)) {\n headers.set(key, value);\n }\n\n const debug = resolveDebug(config);\n const method = options.method ?? \"GET\";\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"request\",\n method,\n url: url.toString(),\n ...(debug.level === \"verbose\" ? { headers: dumpHeaders(headers) } : {}),\n });\n }\n\n const timeoutMs = config.timeoutMs ?? 30_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const startedAt = Date.now();\n\n let response: Response;\n try {\n const init: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n };\n if (options.body !== undefined) {\n init.body = options.body;\n }\n\n response = await fetchImpl(url, init);\n } catch (err) {\n const error = isAbortError(err) ? new TimeoutError(timeoutMs, err) : err;\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"error\",\n method,\n url: url.toString(),\n durationMs: Date.now() - startedAt,\n error,\n });\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n\n const last = readLastResponse(response);\n setLastResponse(last);\n if (debug.level !== \"off\") {\n debug.emit({\n type: \"response\",\n method,\n url: url.toString(),\n status: response.status,\n engineUsed: last.engineUsed,\n requestId: last.requestId,\n durationMs: Date.now() - startedAt,\n ...(debug.level === \"verbose\" ? { headers: dumpHeaders(response.headers) } : {}),\n });\n }\n\n const body = await readBody(response, options.format);\n if (!response.ok) {\n throw errorFromResponse(response.status, body, response.headers.get(\"x-request-id\") ?? undefined);\n }\n\n return body as T;\n}\n\nfunction appendQuery(url: URL, query: Record<string, QueryValue> | undefined): void {\n if (!query) {\n return;\n }\n\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) {\n continue;\n }\n const encoded = Array.isArray(value) ? value.join(\",\") : String(value);\n url.searchParams.set(key, encoded);\n }\n}\n\nasync function readBody(response: Response, format?: ResponseFormat): Promise<unknown> {\n if (response.status === 204) {\n return undefined;\n }\n\n if (format && format !== \"json\") {\n return response.text();\n }\n\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return response.json();\n }\n\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction readLastResponse(response: Response): LastResponse {\n const credits = {\n used: numberHeader(response.headers, \"x-credits-used\"),\n remaining: numberHeader(response.headers, \"x-credits-remaining\"),\n };\n const hasCredits = credits.used !== undefined || credits.remaining !== undefined;\n\n return {\n status: response.status,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n credits: hasCredits ? credits : undefined,\n engineUsed: response.headers.get(\"x-engine-used\") ?? undefined,\n fallbackEngine: response.headers.get(\"x-fallback-engine\") ?? undefined,\n cache: response.headers.get(\"x-cache\") ?? undefined,\n proxyMode: response.headers.get(\"x-proxy-mode\") ?? undefined,\n proxyTag: response.headers.get(\"x-proxy-tag\") ?? undefined,\n proxyUsed: response.headers.get(\"x-proxy-used\") ?? undefined,\n networkBytes: numberHeader(response.headers, \"x-network-bytes\"),\n browserProfileId: response.headers.get(\"x-browser-profile-id\") ?? undefined,\n headers: response.headers,\n };\n}\n\nfunction numberHeader(headers: Headers, name: string): number | undefined {\n const value = headers.get(name);\n if (value === null || value === \"\") {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof DOMException && err.name === \"AbortError\";\n}\n","import { inferBackend, resolveBaseUrl } from \"./backend\";\nimport { CloudOnlyError, OssOnlyError } from \"./errors\";\nimport { request, type QueryValue } from \"./request\";\nimport type {\n CacheStats,\n CircuitBreakerStatsResponse,\n HealthStatus,\n ImageEnvelope,\n ImageParams,\n LastResponse,\n MegaEnginesResponse,\n MegaImageParams,\n MegaSearchEnvelope,\n MegaSearchParams,\n OpenSERPConfig,\n ParseParams,\n ReadinessStatus,\n SearchEnvelope,\n SearchParams,\n StatsResponse,\n ProxyStats,\n} from \"./types/public\";\nimport type {\n CloudAccount,\n EnginesCapabilities,\n EnginesStatus,\n Pricing,\n} from \"./types/cloud\";\n\nexport class OpenSERP {\n readonly config: OpenSERPConfig;\n lastResponse?: LastResponse;\n\n constructor(config: OpenSERPConfig = {}) {\n this.config = { ...config };\n }\n\n get backend() {\n return inferBackend(this.config);\n }\n\n get baseUrl() {\n return resolveBaseUrl(this.config);\n }\n\n search(params: SearchParams): Promise<SearchEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<SearchEnvelope | string>(`/${engine}/search`, query, format);\n }\n\n image(params: ImageParams): Promise<ImageEnvelope | string> {\n const { engine, format, ...query } = params;\n return this.get<ImageEnvelope | string>(`/${engine}/image`, query, format);\n }\n\n megaSearch(params: MegaSearchParams): Promise<MegaSearchEnvelope | string> {\n const { format, ...query } = params;\n return this.get<MegaSearchEnvelope | string>(\"/mega/search\", query, format);\n }\n\n fastSearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"fast\" });\n }\n\n anySearch(params: Omit<MegaSearchParams, \"mode\">): Promise<MegaSearchEnvelope | string> {\n return this.megaSearch({ ...params, mode: \"any\" });\n }\n\n megaImage(params: MegaImageParams): Promise<ImageEnvelope | string> {\n const { format, ...query } = params;\n return this.get<ImageEnvelope | string>(\"/mega/image\", query, format);\n }\n\n fastImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"fast\" });\n }\n\n anyImage(params: Omit<MegaImageParams, \"mode\">): Promise<ImageEnvelope | string> {\n return this.megaImage({ ...params, mode: \"any\" });\n }\n\n parseGoogle(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseGoogle\");\n return this.parse(\"/google/parse\", params);\n }\n\n parseBing(params: ParseParams): Promise<SearchEnvelope | string> {\n this.assertOss(\"parseBing\");\n return this.parse(\"/bing/parse\", params);\n }\n\n health(): Promise<HealthStatus> {\n this.assertOss(\"health\");\n return this.get<HealthStatus>(\"/health\");\n }\n\n ready(): Promise<ReadinessStatus> {\n this.assertOss(\"ready\");\n return this.get<ReadinessStatus>(\"/ready\");\n }\n\n stats(): Promise<StatsResponse> {\n this.assertOss(\"stats\");\n return this.get<StatsResponse>(\"/stats\");\n }\n\n cacheStats(): Promise<CacheStats> {\n this.assertOss(\"cacheStats\");\n return this.get<CacheStats>(\"/stats/cache\");\n }\n\n proxyStats(): Promise<ProxyStats> {\n this.assertOss(\"proxyStats\");\n return this.get<ProxyStats>(\"/stats/proxy\");\n }\n\n circuitBreakerStats(): Promise<CircuitBreakerStatsResponse> {\n this.assertOss(\"circuitBreakerStats\");\n return this.get<CircuitBreakerStatsResponse>(\"/stats/cb\");\n }\n\n engines(): Promise<MegaEnginesResponse> {\n this.assertOss(\"engines\");\n return this.get<MegaEnginesResponse>(\"/mega/engines\");\n }\n\n me(): Promise<CloudAccount> {\n this.assertCloud(\"me\");\n return this.get<CloudAccount>(\"/me\");\n }\n\n pricing(): Promise<Pricing> {\n this.assertCloud(\"pricing\");\n return this.get<Pricing>(\"/pricing\");\n }\n\n enginesStatus(): Promise<EnginesStatus> {\n this.assertCloud(\"enginesStatus\");\n return this.get<EnginesStatus>(\"/engines/status\");\n }\n\n enginesCapabilities(): Promise<EnginesCapabilities> {\n this.assertCloud(\"enginesCapabilities\");\n return this.get<EnginesCapabilities>(\"/engines/capabilities\");\n }\n\n private parse(path: string, params: ParseParams): Promise<SearchEnvelope | string> {\n return request<SearchEnvelope | string>(this.requestContext(), {\n method: \"POST\",\n path,\n query: params.format ? { format: params.format } : undefined,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n },\n body: params.html,\n format: params.format,\n });\n }\n\n private get<T>(\n path: string,\n query?: Record<string, QueryValue>,\n format?: SearchParams[\"format\"],\n ): Promise<T> {\n const { query: cleanQuery, headers } = splitQueryAndHeaders({\n ...query,\n format,\n });\n\n return request<T>(this.requestContext(), {\n path,\n query: cleanQuery,\n headers,\n format,\n });\n }\n\n private requestContext() {\n return {\n baseUrl: this.baseUrl,\n config: this.config,\n setLastResponse: (response: LastResponse) => {\n this.lastResponse = response;\n },\n };\n }\n\n private assertCloud(method: string): void {\n if (this.backend !== \"cloud\") {\n throw new CloudOnlyError(method);\n }\n }\n\n private assertOss(method: string): void {\n if (this.backend !== \"oss\") {\n throw new OssOnlyError(method);\n }\n }\n}\n\nfunction splitQueryAndHeaders(query: Record<string, QueryValue>): {\n query: Record<string, QueryValue>;\n headers: HeadersInit;\n} {\n const {\n useProxy,\n proxyUrl,\n proxyCountry,\n proxyClass,\n proxyProvider,\n proxySessionId,\n tenant,\n ...cleanQuery\n } = query;\n const headers: Record<string, string> = {};\n\n addHeader(headers, \"X-Use-Proxy\", useProxy);\n addHeader(headers, \"X-Proxy-URL\", proxyUrl);\n addHeader(headers, \"X-Proxy-Country\", proxyCountry);\n addHeader(headers, \"X-Proxy-Class\", proxyClass);\n addHeader(headers, \"X-Proxy-Provider\", proxyProvider);\n addHeader(headers, \"X-Proxy-Session-ID\", proxySessionId);\n addHeader(headers, \"X-Tenant\", tenant);\n\n return { query: cleanQuery, headers };\n}\n\nfunction addHeader(headers: Record<string, string>, name: string, value: QueryValue): void {\n if (value === undefined || value === null) {\n return;\n }\n headers[name] = Array.isArray(value) ? value.join(\",\") : String(value);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openserp/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "TypeScript/JavaScript SDK for the OpenSERP self-hosted server and OpenSERP Cloud.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,8 +8,7 @@
8
8
  "homepage": "https://openserp.org",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "https://github.com/karust/openserp.git",
12
- "directory": "integrations/sdk-js"
11
+ "url": "git+https://github.com/karust/openserp.git"
13
12
  },
14
13
  "bugs": {
15
14
  "url": "https://github.com/karust/openserp/issues"
@@ -17,15 +16,40 @@
17
16
  "keywords": [
18
17
  "openserp",
19
18
  "serp",
19
+ "serp-api",
20
20
  "search",
21
+ "search-api",
22
+ "google-search",
23
+ "google-search-api",
21
24
  "google",
22
25
  "bing",
23
26
  "yandex",
24
27
  "baidu",
25
28
  "duckduckgo",
26
29
  "ecosia",
30
+ "scraper",
31
+ "scraping",
32
+ "web-scraping",
33
+ "serpapi",
34
+ "serpapi-alternative",
27
35
  "seo",
28
- "ai-grounding"
36
+ "rank-tracking",
37
+ "rank-tracker",
38
+ "keyword-research",
39
+ "ai",
40
+ "ai-grounding",
41
+ "rag",
42
+ "llm",
43
+ "llm-tools",
44
+ "agent",
45
+ "agent-tools",
46
+ "tool-use",
47
+ "openai",
48
+ "anthropic",
49
+ "mcp",
50
+ "typescript",
51
+ "nodejs",
52
+ "edge-runtime"
29
53
  ],
30
54
  "sideEffects": false,
31
55
  "exports": {