@crawlora-org/sdk 1.2.0-sdk.18 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlora-org/sdk",
3
- "version": "1.2.0-sdk.18",
3
+ "version": "1.3.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,7 +1,7 @@
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.18";
4
+ export const VERSION = "1.3.0-sdk.1";
5
5
  const DEFAULT_USER_AGENT = `crawlora-js-sdk/${VERSION}`;
6
6
 
7
7
  export class CrawloraError extends Error {
@@ -18,6 +18,35 @@ export class CrawloraError extends Error {
18
18
  }
19
19
  }
20
20
 
21
+ // Response status 4xx: the request was rejected by the API (bad params, auth,
22
+ // not found). Usually not retryable.
23
+ export class CrawloraClientError extends CrawloraError {
24
+ constructor(message, options) {
25
+ super(message, options);
26
+ this.name = "CrawloraClientError";
27
+ }
28
+ }
29
+
30
+ // Response status 5xx: the API failed to handle a valid request. Retryable.
31
+ export class CrawloraServerError extends CrawloraError {
32
+ constructor(message, options) {
33
+ super(message, options);
34
+ this.name = "CrawloraServerError";
35
+ }
36
+ }
37
+
38
+ // Transport failure, timeout, or abort before a response was received.
39
+ export class CrawloraNetworkError extends CrawloraError {
40
+ constructor(message, options) {
41
+ super(message, options);
42
+ this.name = "CrawloraNetworkError";
43
+ }
44
+ }
45
+
46
+ function apiErrorClass(status) {
47
+ return status >= 500 ? CrawloraServerError : CrawloraClientError;
48
+ }
49
+
21
50
  export class CrawloraClient {
22
51
  constructor(options = {}) {
23
52
  this.apiKey = options.apiKey || "";
@@ -98,12 +127,12 @@ export class CrawloraClient {
98
127
  });
99
128
  } catch (error) {
100
129
  if (options.signal?.aborted) {
101
- throw new CrawloraError("Crawlora request aborted", { cause: error, retryable: false });
130
+ throw new CrawloraNetworkError("Crawlora request aborted", { cause: error, retryable: false });
102
131
  }
103
132
  if (timedOut) {
104
- throw new CrawloraError("Crawlora request timed out", { cause: error, retryable: false });
133
+ throw new CrawloraNetworkError("Crawlora request timed out", { cause: error, retryable: false });
105
134
  }
106
- throw new CrawloraError("Crawlora transport error", { cause: error });
135
+ throw new CrawloraNetworkError("Crawlora transport error", { cause: error });
107
136
  } finally {
108
137
  if (timeout) clearTimeout(timeout);
109
138
  }
@@ -113,7 +142,8 @@ export class CrawloraClient {
113
142
  if (!response.ok) {
114
143
  const code = parsed && typeof parsed === "object" ? parsed.code : undefined;
115
144
  const message = parsed && typeof parsed === "object" && parsed.msg ? parsed.msg : response.statusText;
116
- throw new CrawloraError(message || `Crawlora request failed with status ${response.status}`, {
145
+ const ApiError = apiErrorClass(response.status);
146
+ throw new ApiError(message || `Crawlora request failed with status ${response.status}`, {
117
147
  status: response.status,
118
148
  code,
119
149
  body: parsed,
@@ -123,6 +153,50 @@ export class CrawloraClient {
123
153
  }
124
154
  return parsed;
125
155
  }
156
+
157
+ // Async iterator over pages of a paginated operation. Advances the numeric
158
+ // page/offset query parameter and stops when a page returns no data.
159
+ // for await (const page of client.paginate("ebay-seller-feedback", { seller })) { ... }
160
+ async *paginate(operationId, params = {}, options = {}) {
161
+ const operation = operations[operationId];
162
+ if (!operation) {
163
+ throw new TypeError(`Unknown Crawlora operation: ${operationId}`);
164
+ }
165
+ const pageParam = options.pageParam || detectPageParam(operation);
166
+ if (!pageParam) {
167
+ throw new TypeError(`Operation ${operationId} has no page or offset query parameter to paginate`);
168
+ }
169
+ const step = options.step ?? 1;
170
+ const maxPages = options.maxPages ?? Infinity;
171
+ let pageValue = options.start ?? (pageParam === "offset" ? 0 : 1);
172
+ for (let i = 0; i < maxPages; i++) {
173
+ const response = await this.request(operationId, { ...params, [pageParam]: pageValue }, options);
174
+ yield response;
175
+ if (pageIsEmpty(response)) break;
176
+ pageValue += step;
177
+ }
178
+ }
179
+ }
180
+
181
+ const PAGE_PARAM_NAMES = ["page", "offset"];
182
+
183
+ function detectPageParam(operation) {
184
+ for (const name of PAGE_PARAM_NAMES) {
185
+ if (operation.queryParams.some((parameter) => parameter.name === name)) return name;
186
+ }
187
+ return undefined;
188
+ }
189
+
190
+ function pageIsEmpty(response) {
191
+ if (response === undefined || response === null) return true;
192
+ let data = response;
193
+ if (typeof response === "object" && !Array.isArray(response) && "data" in response) {
194
+ data = response.data;
195
+ }
196
+ if (data === undefined || data === null) return true;
197
+ if (Array.isArray(data)) return data.length === 0;
198
+ if (typeof data === "object") return Object.keys(data).length === 0;
199
+ return !data;
126
200
  }
127
201
 
128
202
  function buildRequest(operation, baseUrl, params) {
package/src/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  CrawloraGeneratedGroups,
3
3
  OperationId,
4
+ OperationParamsMap,
4
5
  OperationRequestArgs,
5
6
  OperationResponseMap
6
7
  } from "./types.js";
@@ -55,6 +56,24 @@ export class CrawloraError extends Error {
55
56
  retryable?: boolean;
56
57
  }
57
58
 
59
+ // Thrown for 4xx API responses (request rejected by the API).
60
+ export class CrawloraClientError extends CrawloraError {}
61
+ // Thrown for 5xx API responses (API failed to handle a valid request).
62
+ export class CrawloraServerError extends CrawloraError {}
63
+ // Thrown for transport failures, timeouts, and aborts before a response.
64
+ export class CrawloraNetworkError extends CrawloraError {}
65
+
66
+ export interface CrawloraPaginateOptions extends CrawloraRequestOptions {
67
+ // Query parameter to advance. Auto-detected as "page" or "offset" when omitted.
68
+ pageParam?: string;
69
+ // First value for the page parameter. Defaults to 1 for "page", 0 for "offset".
70
+ start?: number;
71
+ // Amount added to the page parameter after each page. Defaults to 1.
72
+ step?: number;
73
+ // Maximum number of pages to fetch. Defaults to unbounded.
74
+ maxPages?: number;
75
+ }
76
+
58
77
  export class CrawloraClient {
59
78
  constructor(options?: CrawloraClientOptions);
60
79
  request<I extends OperationId>(
@@ -65,6 +84,11 @@ export class CrawloraClient {
65
84
  operationId: I,
66
85
  ...args: OperationRequestArgs<I>
67
86
  ): Promise<OperationResponseMap[I]>;
87
+ paginate<I extends OperationId>(
88
+ operationId: I,
89
+ params?: OperationParamsMap[I],
90
+ options?: CrawloraPaginateOptions
91
+ ): AsyncGenerator<OperationResponseMap[I], void, unknown>;
68
92
  [group: string]: unknown;
69
93
  }
70
94
 
@@ -73,5 +97,6 @@ export interface CrawloraClient extends CrawloraGeneratedGroups {}
73
97
  export const operations: Record<string, OperationDefinition>;
74
98
  export const groups: Record<string, Record<string, string>>;
75
99
  export const operationCount: number;
100
+ export const OperationIds: Readonly<Record<string, OperationId>>;
76
101
  export const VERSION: string;
77
102
  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";