@crawlora-org/sdk 1.2.0-sdk.19 → 1.4.0-sdk.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.4.0-sdk.1
4
+
5
+ - Configurable retries: `maxRetryDelay`, `retryStatuses`, and an `isRetryable`
6
+ predicate; added an `onRetry` hook, opt-in `requestId` (x-request-id, also on
7
+ `error.requestId`), and a `logger` event sink.
8
+ - Pagination: cursor/token mode (`cursorParam` + `nextCursor`) and a
9
+ `paginateItems` per-item async iterator; operation metadata now exposes
10
+ `paginatable` and `cursorParams`.
11
+ - Streaming: `responseType: "stream"` returns the raw `Response` on success.
12
+ - Config: `CRAWLORA_API_KEY` / `CRAWLORA_BASE_URL` environment fallback.
13
+
14
+ ## v1.3.0-sdk.1
15
+
16
+ - Added `CrawloraClientError`, `CrawloraServerError`, and `CrawloraNetworkError`
17
+ subclasses of `CrawloraError` so callers can branch on 4xx vs 5xx vs transport
18
+ failures.
19
+ - Added `client.paginate(operationId, params, options?)`, an async iterator that
20
+ advances the page/offset query parameter and stops on an empty page.
21
+ - Added the generated `OperationIds` map for typo-safe dynamic operation ids,
22
+ e.g. `client.request(OperationIds.BingSearch, { q: "coffee" })`.
23
+ - The generator now shares a single language-neutral core with the Go and Python
24
+ SDKs; generated output is unchanged.
25
+
3
26
  ## v1.2.0-sdk.19
4
27
 
5
28
  - Regenerated the public SDK contract with the promoted Shopify endpoint family.
package/README.md CHANGED
@@ -142,6 +142,23 @@ The error includes `status`, optional API `code`, parsed `body`, response
142
142
  headers, capped at 30 seconds. Externally aborted requests fail with
143
143
  `Crawlora request aborted` and are not retried.
144
144
 
145
+ `CrawloraError` has three subclasses so you can branch on the failure kind:
146
+ `CrawloraClientError` (4xx, request rejected), `CrawloraServerError` (5xx), and
147
+ `CrawloraNetworkError` (transport failure, timeout, or abort before a response).
148
+
149
+ ## Pagination
150
+
151
+ `client.paginate` is an async iterator that advances the page/offset query
152
+ parameter and stops when a page returns no data:
153
+
154
+ ```js
155
+ for await (const page of crawlora.paginate("ebay-seller-feedback", { seller: "acme" })) {
156
+ for (const review of page.data) console.log(review);
157
+ }
158
+ ```
159
+
160
+ Override detection with `{ pageParam, start, step, maxPages }`.
161
+
145
162
  ## Examples
146
163
 
147
164
  Runnable examples live under `examples/` and skip cleanly when required
package/docs/recipes.md CHANGED
@@ -74,6 +74,51 @@ try {
74
74
  }
75
75
  ```
76
76
 
77
+ ## Custom Retries And Observability
78
+
79
+ ```js
80
+ const crawlora = new CrawloraClient({
81
+ retries: 3,
82
+ maxRetryDelay: 10000,
83
+ retryStatuses: [429, 503], // or:
84
+ isRetryable: (status, error) => status >= 500,
85
+ onRetry: (attempt, error, delay) => console.warn(`retry ${attempt} after ${delay}ms`, error.status),
86
+ requestId: true, // sets x-request-id; available as error.requestId
87
+ logger: (event) => console.debug(event)
88
+ });
89
+ ```
90
+
91
+ Branch on `CrawloraClientError` (4xx), `CrawloraServerError` (5xx), and
92
+ `CrawloraNetworkError` (transport).
93
+
94
+ ## Pagination
95
+
96
+ ```js
97
+ // page/offset (auto-detected)
98
+ for await (const page of crawlora.paginate("ebay-seller-feedback", { seller: "acme" })) { /* ... */ }
99
+
100
+ // per-item iteration
101
+ for await (const item of crawlora.paginateItems("ebay-seller-feedback", { seller: "acme" })) { /* ... */ }
102
+
103
+ // cursor/token pagination
104
+ for await (const page of crawlora.paginate("producthunt-leaderboard", {}, {
105
+ cursorParam: "cursor",
106
+ nextCursor: (p) => p.next_cursor
107
+ })) { /* ... */ }
108
+ ```
109
+
110
+ ## Streaming Responses
111
+
112
+ ```js
113
+ const res = await crawlora.request("bing-search", { q: "coffee" }, { responseType: "stream" });
114
+ for await (const chunk of res.body) { /* ... */ }
115
+ ```
116
+
117
+ ## Environment Variables
118
+
119
+ `CRAWLORA_API_KEY` and `CRAWLORA_BASE_URL` are used when not set explicitly
120
+ (precedence: option > env > default).
121
+
77
122
  ## Optional Live Smoke Tests
78
123
 
79
124
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlora-org/sdk",
3
- "version": "1.2.0-sdk.19",
3
+ "version": "1.4.0-sdk.1",
4
4
  "description": "JavaScript and TypeScript SDK for the public Crawlora API.",
5
5
  "keywords": [
6
6
  "crawlora",
package/src/client.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { operations, groups } from "./operations.js";
2
2
 
3
3
  const DEFAULT_BASE_URL = "https://api.crawlora.net/api/v1";
4
- export const VERSION = "1.2.0-sdk.19";
4
+ export const VERSION = "1.4.0-sdk.1";
5
5
  const DEFAULT_USER_AGENT = `crawlora-js-sdk/${VERSION}`;
6
6
 
7
7
  export class CrawloraError extends Error {
8
- constructor(message, { status = 0, code, body, headers = {}, response, cause, retryable } = {}) {
8
+ constructor(message, { status = 0, code, body, headers = {}, response, cause, retryable, requestId } = {}) {
9
9
  super(message);
10
10
  this.name = "CrawloraError";
11
11
  this.status = status;
@@ -15,17 +15,54 @@ export class CrawloraError extends Error {
15
15
  this.response = response;
16
16
  this.cause = cause;
17
17
  this.retryable = retryable;
18
+ this.requestId = requestId;
18
19
  }
19
20
  }
20
21
 
22
+ // Response status 4xx: the request was rejected by the API (bad params, auth,
23
+ // not found). Usually not retryable.
24
+ export class CrawloraClientError extends CrawloraError {
25
+ constructor(message, options) {
26
+ super(message, options);
27
+ this.name = "CrawloraClientError";
28
+ }
29
+ }
30
+
31
+ // Response status 5xx: the API failed to handle a valid request. Retryable.
32
+ export class CrawloraServerError extends CrawloraError {
33
+ constructor(message, options) {
34
+ super(message, options);
35
+ this.name = "CrawloraServerError";
36
+ }
37
+ }
38
+
39
+ // Transport failure, timeout, or abort before a response was received.
40
+ export class CrawloraNetworkError extends CrawloraError {
41
+ constructor(message, options) {
42
+ super(message, options);
43
+ this.name = "CrawloraNetworkError";
44
+ }
45
+ }
46
+
47
+ function apiErrorClass(status) {
48
+ return status >= 500 ? CrawloraServerError : CrawloraClientError;
49
+ }
50
+
21
51
  export class CrawloraClient {
22
52
  constructor(options = {}) {
23
- this.apiKey = options.apiKey || "";
53
+ // Precedence: explicit option > environment variable > default.
54
+ this.apiKey = options.apiKey || envVar("CRAWLORA_API_KEY") || "";
24
55
  this.jwtToken = options.jwtToken || "";
25
- this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
56
+ this.baseUrl = (options.baseUrl || envVar("CRAWLORA_BASE_URL") || DEFAULT_BASE_URL).replace(/\/+$/, "");
26
57
  this.timeout = options.timeout ?? 30000;
27
58
  this.retries = normalizeNonNegativeInteger(options.retries ?? 0);
28
59
  this.retryDelay = normalizeNonNegativeNumber(options.retryDelay ?? 250);
60
+ this.maxRetryDelay = normalizeNonNegativeNumber(options.maxRetryDelay ?? 30000);
61
+ this.retryStatuses = options.retryStatuses ? new Set(options.retryStatuses) : null;
62
+ this.isRetryable = typeof options.isRetryable === "function" ? options.isRetryable : null;
63
+ this.onRetry = typeof options.onRetry === "function" ? options.onRetry : null;
64
+ this.requestId = options.requestId === true;
65
+ this.logger = typeof options.logger === "function" ? options.logger : null;
29
66
  this.headers = { ...(options.headers || {}) };
30
67
  this.userAgent = options.userAgent === false ? "" : (options.userAgent || DEFAULT_USER_AGENT);
31
68
  this.fetch = options.fetch || globalThis.fetch;
@@ -55,21 +92,40 @@ export class CrawloraClient {
55
92
  params = params ?? {};
56
93
  options = options ?? {};
57
94
  const responseType = validateResponseType(options.responseType || "auto");
95
+ this.#log({ event: "request", operation: operationId });
58
96
 
59
97
  let attempt = 0;
60
98
  for (;;) {
61
99
  try {
62
100
  return await this.#send(operation, params, options, responseType);
63
101
  } catch (error) {
64
- if (!(error instanceof CrawloraError) || error.retryable === false || attempt >= this.retries || !shouldRetry(error.status)) {
102
+ if (!(error instanceof CrawloraError) || error.retryable === false || attempt >= this.retries || !this.#isRetryable(error.status, error)) {
65
103
  throw error;
66
104
  }
67
105
  attempt++;
68
- await sleep(retryDelayForError(error, this.retryDelay, attempt), options.signal);
106
+ const delay = this.#retryDelayFor(error, attempt);
107
+ this.#log({ event: "retry", operation: operationId, attempt, status: error.status, delay });
108
+ if (this.onRetry) this.onRetry(attempt, error, delay);
109
+ await sleep(delay, options.signal);
69
110
  }
70
111
  }
71
112
  }
72
113
 
114
+ #isRetryable(status, error) {
115
+ if (this.isRetryable) return !!this.isRetryable(status, error);
116
+ if (this.retryStatuses) return status === 0 || this.retryStatuses.has(status);
117
+ return shouldRetry(status);
118
+ }
119
+
120
+ #retryDelayFor(error, attempt) {
121
+ const retryAfter = parseRetryAfter(error.headers, this.maxRetryDelay);
122
+ return retryAfter ?? retryDelay(this.retryDelay, attempt);
123
+ }
124
+
125
+ #log(event) {
126
+ if (this.logger) this.logger(event);
127
+ }
128
+
73
129
  async #send(operation, params, options, responseType) {
74
130
  const { url, body, bodyHeaders } = buildRequest(operation, this.baseUrl, params);
75
131
  const headers = mergeHeaders(
@@ -79,6 +135,7 @@ export class CrawloraClient {
79
135
  bodyHeaders,
80
136
  options.headers
81
137
  );
138
+ const requestId = this.requestId ? ensureRequestId(headers) : (headerValue(headers, "x-request-id") || undefined);
82
139
 
83
140
  const controller = new AbortController();
84
141
  const timeoutMs = options.timeout ?? this.timeout;
@@ -98,31 +155,117 @@ export class CrawloraClient {
98
155
  });
99
156
  } catch (error) {
100
157
  if (options.signal?.aborted) {
101
- throw new CrawloraError("Crawlora request aborted", { cause: error, retryable: false });
158
+ throw new CrawloraNetworkError("Crawlora request aborted", { cause: error, retryable: false, requestId });
102
159
  }
103
160
  if (timedOut) {
104
- throw new CrawloraError("Crawlora request timed out", { cause: error, retryable: false });
161
+ throw new CrawloraNetworkError("Crawlora request timed out", { cause: error, retryable: false, requestId });
105
162
  }
106
- throw new CrawloraError("Crawlora transport error", { cause: error });
163
+ throw new CrawloraNetworkError("Crawlora transport error", { cause: error, requestId });
107
164
  } finally {
108
165
  if (timeout) clearTimeout(timeout);
109
166
  }
110
167
 
111
168
  const headersObject = responseHeaders(response);
112
- const parsed = await parseResponse(response, responseType, headersObject);
169
+ // Streaming success returns the raw Response; the caller reads response.body.
170
+ if (responseType === "stream" && response.ok) {
171
+ return response;
172
+ }
173
+ const parsed = await parseResponse(response, responseType === "stream" ? "auto" : responseType, headersObject, requestId);
113
174
  if (!response.ok) {
114
175
  const code = parsed && typeof parsed === "object" ? parsed.code : undefined;
115
176
  const message = parsed && typeof parsed === "object" && parsed.msg ? parsed.msg : response.statusText;
116
- throw new CrawloraError(message || `Crawlora request failed with status ${response.status}`, {
177
+ const ApiError = apiErrorClass(response.status);
178
+ throw new ApiError(message || `Crawlora request failed with status ${response.status}`, {
117
179
  status: response.status,
118
180
  code,
119
181
  body: parsed,
120
182
  headers: headersObject,
121
- response
183
+ response,
184
+ requestId
122
185
  });
123
186
  }
124
187
  return parsed;
125
188
  }
189
+
190
+ // Async iterator over pages of a paginated operation. Numeric mode advances
191
+ // the page/offset query parameter and stops on an empty page; cursor mode
192
+ // (cursorParam + nextCursor) sends the cursor and stops when nextCursor is falsy.
193
+ // for await (const page of client.paginate("ebay-seller-feedback", { seller })) { ... }
194
+ async *paginate(operationId, params = {}, options = {}) {
195
+ const operation = operations[operationId];
196
+ if (!operation) {
197
+ throw new TypeError(`Unknown Crawlora operation: ${operationId}`);
198
+ }
199
+ const maxPages = options.maxPages ?? Infinity;
200
+
201
+ if (options.cursorParam || options.nextCursor) {
202
+ if (!(options.cursorParam && options.nextCursor)) {
203
+ throw new TypeError("cursor pagination requires both cursorParam and nextCursor");
204
+ }
205
+ if (!operation.queryParams.some((parameter) => parameter.name === options.cursorParam)) {
206
+ throw new TypeError(`cursorParam ${options.cursorParam} is not a query parameter of ${operationId}`);
207
+ }
208
+ let cursor = options.start;
209
+ for (let i = 0; i < maxPages; i++) {
210
+ const pageParams = { ...params };
211
+ if (cursor !== undefined && cursor !== null) pageParams[options.cursorParam] = cursor;
212
+ const response = await this.request(operationId, pageParams, options);
213
+ yield response;
214
+ cursor = options.nextCursor(response);
215
+ if (!cursor) break;
216
+ }
217
+ return;
218
+ }
219
+
220
+ const pageParam = options.pageParam || detectPageParam(operation);
221
+ if (!pageParam) {
222
+ throw new TypeError(`Operation ${operationId} has no page or offset query parameter to paginate`);
223
+ }
224
+ const step = options.step ?? 1;
225
+ let pageValue = options.start ?? (pageParam === "offset" ? 0 : 1);
226
+ for (let i = 0; i < maxPages; i++) {
227
+ const response = await this.request(operationId, { ...params, [pageParam]: pageValue }, options);
228
+ yield response;
229
+ if (pageIsEmpty(response)) break;
230
+ pageValue += step;
231
+ }
232
+ }
233
+
234
+ // Async iterator over individual items across pages. `items` extracts the list
235
+ // from a page (default: the Crawlora `data` array).
236
+ async *paginateItems(operationId, params = {}, options = {}) {
237
+ const extract = options.items || defaultItems;
238
+ for await (const page of this.paginate(operationId, params, options)) {
239
+ for (const item of extract(page)) yield item;
240
+ }
241
+ }
242
+ }
243
+
244
+ const PAGE_PARAM_NAMES = ["page", "offset"];
245
+
246
+ function defaultItems(response) {
247
+ if (response && typeof response === "object" && Array.isArray(response.data)) return response.data;
248
+ if (Array.isArray(response)) return response;
249
+ return [];
250
+ }
251
+
252
+ function detectPageParam(operation) {
253
+ for (const name of PAGE_PARAM_NAMES) {
254
+ if (operation.queryParams.some((parameter) => parameter.name === name)) return name;
255
+ }
256
+ return undefined;
257
+ }
258
+
259
+ function pageIsEmpty(response) {
260
+ if (response === undefined || response === null) return true;
261
+ let data = response;
262
+ if (typeof response === "object" && !Array.isArray(response) && "data" in response) {
263
+ data = response.data;
264
+ }
265
+ if (data === undefined || data === null) return true;
266
+ if (Array.isArray(data)) return data.length === 0;
267
+ if (typeof data === "object") return Object.keys(data).length === 0;
268
+ return !data;
126
269
  }
127
270
 
128
271
  function buildRequest(operation, baseUrl, params) {
@@ -242,13 +385,13 @@ function composeSignal(controller, signal) {
242
385
  }
243
386
 
244
387
  function validateResponseType(responseType) {
245
- if (responseType === "auto" || responseType === "json" || responseType === "text") {
388
+ if (responseType === "auto" || responseType === "json" || responseType === "text" || responseType === "stream") {
246
389
  return responseType;
247
390
  }
248
- throw new TypeError("Invalid responseType: expected one of auto, json, text");
391
+ throw new TypeError("Invalid responseType: expected one of auto, json, text, stream");
249
392
  }
250
393
 
251
- async function parseResponse(response, responseType, headers) {
394
+ async function parseResponse(response, responseType, headers, requestId) {
252
395
  if (responseType === "text") return response.text();
253
396
  const contentType = response.headers.get("content-type") || "";
254
397
  if (responseType === "json" || contentType.toLowerCase().includes("application/json")) {
@@ -261,7 +404,8 @@ async function parseResponse(response, responseType, headers) {
261
404
  body: text,
262
405
  headers,
263
406
  response,
264
- cause: error
407
+ cause: error,
408
+ requestId
265
409
  });
266
410
  }
267
411
  }
@@ -279,25 +423,33 @@ function retryDelay(baseDelay, attempt) {
279
423
  return delay + jitter;
280
424
  }
281
425
 
282
- function retryDelayForError(error, baseDelay, attempt) {
283
- return parseRetryAfter(error.headers) ?? retryDelay(baseDelay, attempt);
284
- }
285
-
286
- function parseRetryAfter(headers) {
426
+ function parseRetryAfter(headers, cap = 30000) {
287
427
  const value = headerValue(headers, "retry-after");
288
428
  if (!value) return undefined;
289
429
  const seconds = Number(value);
290
430
  if (Number.isFinite(seconds) && seconds > 0) {
291
- return Math.min(seconds * 1000, 30000);
431
+ return Math.min(seconds * 1000, cap);
292
432
  }
293
433
  const date = Date.parse(value);
294
434
  if (Number.isFinite(date)) {
295
435
  const delay = date - Date.now();
296
- if (delay > 0) return Math.min(delay, 30000);
436
+ if (delay > 0) return Math.min(delay, cap);
297
437
  }
298
438
  return undefined;
299
439
  }
300
440
 
441
+ function envVar(name) {
442
+ return (typeof process !== "undefined" && process.env && process.env[name]) || undefined;
443
+ }
444
+
445
+ function ensureRequestId(headers) {
446
+ const existing = headerValue(headers, "x-request-id");
447
+ if (existing) return existing;
448
+ const id = (typeof crypto !== "undefined" && crypto.randomUUID) ? crypto.randomUUID() : `req-${Date.now()}-${Math.random().toString(16).slice(2)}`;
449
+ headers["x-request-id"] = id;
450
+ return id;
451
+ }
452
+
301
453
  function headerValue(headers, name) {
302
454
  for (const [key, value] of Object.entries(headers || {})) {
303
455
  if (key.toLowerCase() === name.toLowerCase()) return value;
package/src/index.d.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import type {
2
2
  CrawloraGeneratedGroups,
3
3
  OperationId,
4
+ OperationParamsMap,
4
5
  OperationRequestArgs,
5
6
  OperationResponseMap
6
7
  } from "./types.js";
7
8
 
8
9
  export type CrawloraParams = Record<string, unknown>;
9
10
 
11
+ export type CrawloraLogEvent = { event: string; [key: string]: unknown };
12
+
10
13
  export interface CrawloraClientOptions {
11
14
  apiKey?: string;
12
15
  jwtToken?: string;
@@ -14,6 +17,18 @@ export interface CrawloraClientOptions {
14
17
  timeout?: number;
15
18
  retries?: number;
16
19
  retryDelay?: number;
20
+ /** Cap on backoff and Retry-After delays in ms (default 30000). */
21
+ maxRetryDelay?: number;
22
+ /** Override the retryable HTTP status set (network status 0 stays retryable). */
23
+ retryStatuses?: Iterable<number>;
24
+ /** Full predicate; supersedes retryStatuses when set. */
25
+ isRetryable?: (status: number, error: CrawloraError) => boolean;
26
+ /** Called before each retry sleep with (attempt, error, delayMs). */
27
+ onRetry?: (attempt: number, error: CrawloraError, delay: number) => void;
28
+ /** Generate an x-request-id header when absent. */
29
+ requestId?: boolean;
30
+ /** Structured event sink (request/retry); never logs on its own. */
31
+ logger?: (event: CrawloraLogEvent) => void;
17
32
  headers?: Record<string, string>;
18
33
  userAgent?: string | false;
19
34
  fetch?: typeof globalThis.fetch;
@@ -21,7 +36,7 @@ export interface CrawloraClientOptions {
21
36
 
22
37
  export interface CrawloraRequestOptions {
23
38
  headers?: Record<string, string>;
24
- responseType?: "auto" | "json" | "text";
39
+ responseType?: "auto" | "json" | "text" | "stream";
25
40
  timeout?: number;
26
41
  signal?: AbortSignal;
27
42
  }
@@ -43,6 +58,8 @@ export interface OperationDefinition {
43
58
  consumes: string[];
44
59
  produces: string[];
45
60
  security: string[];
61
+ paginatable?: boolean;
62
+ cursorParams?: string[];
46
63
  }
47
64
 
48
65
  export class CrawloraError extends Error {
@@ -53,6 +70,34 @@ export class CrawloraError extends Error {
53
70
  response?: Response;
54
71
  cause?: unknown;
55
72
  retryable?: boolean;
73
+ requestId?: string;
74
+ }
75
+
76
+ // Thrown for 4xx API responses (request rejected by the API).
77
+ export class CrawloraClientError extends CrawloraError {}
78
+ // Thrown for 5xx API responses (API failed to handle a valid request).
79
+ export class CrawloraServerError extends CrawloraError {}
80
+ // Thrown for transport failures, timeouts, and aborts before a response.
81
+ export class CrawloraNetworkError extends CrawloraError {}
82
+
83
+ export interface CrawloraPaginateOptions extends CrawloraRequestOptions {
84
+ // Numeric query parameter to advance. Auto-detected as "page" or "offset".
85
+ pageParam?: string;
86
+ // Cursor/token query parameter (cursor mode). Requires nextCursor.
87
+ cursorParam?: string;
88
+ // Extracts the next cursor from a page; iteration stops when it returns falsy.
89
+ nextCursor?: (page: unknown) => unknown;
90
+ // First page value (numeric) or initial cursor value.
91
+ start?: unknown;
92
+ // Amount added to the page parameter after each page. Defaults to 1.
93
+ step?: number;
94
+ // Maximum number of pages to fetch. Defaults to unbounded.
95
+ maxPages?: number;
96
+ }
97
+
98
+ export interface CrawloraPaginateItemsOptions extends CrawloraPaginateOptions {
99
+ // Extracts the item list from a page (default: the `data` array).
100
+ items?: (page: unknown) => Iterable<unknown>;
56
101
  }
57
102
 
58
103
  export class CrawloraClient {
@@ -65,6 +110,16 @@ export class CrawloraClient {
65
110
  operationId: I,
66
111
  ...args: OperationRequestArgs<I>
67
112
  ): Promise<OperationResponseMap[I]>;
113
+ paginate<I extends OperationId>(
114
+ operationId: I,
115
+ params?: OperationParamsMap[I],
116
+ options?: CrawloraPaginateOptions
117
+ ): AsyncGenerator<OperationResponseMap[I], void, unknown>;
118
+ paginateItems<I extends OperationId>(
119
+ operationId: I,
120
+ params?: OperationParamsMap[I],
121
+ options?: CrawloraPaginateItemsOptions
122
+ ): AsyncGenerator<unknown, void, unknown>;
68
123
  [group: string]: unknown;
69
124
  }
70
125
 
@@ -73,5 +128,6 @@ export interface CrawloraClient extends CrawloraGeneratedGroups {}
73
128
  export const operations: Record<string, OperationDefinition>;
74
129
  export const groups: Record<string, Record<string, string>>;
75
130
  export const operationCount: number;
131
+ export const OperationIds: Readonly<Record<string, OperationId>>;
76
132
  export const VERSION: string;
77
133
  export * from "./types.js";
package/src/index.js CHANGED
@@ -1,2 +1,9 @@
1
- export { CrawloraClient, CrawloraError, VERSION } from "./client.js";
2
- export { operations, groups, operationCount } from "./operations.js";
1
+ export {
2
+ CrawloraClient,
3
+ CrawloraError,
4
+ CrawloraClientError,
5
+ CrawloraServerError,
6
+ CrawloraNetworkError,
7
+ VERSION
8
+ } from "./client.js";
9
+ export { operations, groups, operationCount, OperationIds } from "./operations.js";