@dealcrawl/sdk 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +147 -14
  2. package/dist/index.d.mts +2769 -0
  3. package/dist/index.d.ts +2748 -37
  4. package/dist/index.js +2529 -64
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +2508 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +14 -7
  9. package/dist/client.d.ts +0 -285
  10. package/dist/client.d.ts.map +0 -1
  11. package/dist/client.js +0 -336
  12. package/dist/client.js.map +0 -1
  13. package/dist/error.d.ts +0 -55
  14. package/dist/error.d.ts.map +0 -1
  15. package/dist/error.js +0 -128
  16. package/dist/error.js.map +0 -1
  17. package/dist/index.d.ts.map +0 -1
  18. package/dist/resources/account.d.ts +0 -143
  19. package/dist/resources/account.d.ts.map +0 -1
  20. package/dist/resources/account.js +0 -186
  21. package/dist/resources/account.js.map +0 -1
  22. package/dist/resources/crawl.d.ts +0 -101
  23. package/dist/resources/crawl.d.ts.map +0 -1
  24. package/dist/resources/crawl.js +0 -234
  25. package/dist/resources/crawl.js.map +0 -1
  26. package/dist/resources/data.d.ts +0 -157
  27. package/dist/resources/data.d.ts.map +0 -1
  28. package/dist/resources/data.js +0 -245
  29. package/dist/resources/data.js.map +0 -1
  30. package/dist/resources/dork.d.ts +0 -104
  31. package/dist/resources/dork.d.ts.map +0 -1
  32. package/dist/resources/dork.js +0 -163
  33. package/dist/resources/dork.js.map +0 -1
  34. package/dist/resources/extract.d.ts +0 -105
  35. package/dist/resources/extract.d.ts.map +0 -1
  36. package/dist/resources/extract.js +0 -246
  37. package/dist/resources/extract.js.map +0 -1
  38. package/dist/resources/index.d.ts +0 -14
  39. package/dist/resources/index.d.ts.map +0 -1
  40. package/dist/resources/index.js +0 -14
  41. package/dist/resources/index.js.map +0 -1
  42. package/dist/resources/keys.d.ts +0 -124
  43. package/dist/resources/keys.d.ts.map +0 -1
  44. package/dist/resources/keys.js +0 -168
  45. package/dist/resources/keys.js.map +0 -1
  46. package/dist/resources/scrape.d.ts +0 -53
  47. package/dist/resources/scrape.d.ts.map +0 -1
  48. package/dist/resources/scrape.js +0 -85
  49. package/dist/resources/scrape.js.map +0 -1
  50. package/dist/resources/status.d.ts +0 -100
  51. package/dist/resources/status.d.ts.map +0 -1
  52. package/dist/resources/status.js +0 -133
  53. package/dist/resources/status.js.map +0 -1
  54. package/dist/resources/webhooks.d.ts +0 -126
  55. package/dist/resources/webhooks.d.ts.map +0 -1
  56. package/dist/resources/webhooks.js +0 -167
  57. package/dist/resources/webhooks.js.map +0 -1
  58. package/dist/types/config.d.ts +0 -45
  59. package/dist/types/config.d.ts.map +0 -1
  60. package/dist/types/config.js +0 -10
  61. package/dist/types/config.js.map +0 -1
  62. package/dist/types/index.d.ts +0 -8
  63. package/dist/types/index.d.ts.map +0 -1
  64. package/dist/types/index.js +0 -8
  65. package/dist/types/index.js.map +0 -1
  66. package/dist/types/options.d.ts +0 -328
  67. package/dist/types/options.d.ts.map +0 -1
  68. package/dist/types/options.js +0 -6
  69. package/dist/types/options.js.map +0 -1
  70. package/dist/types/responses.d.ts +0 -422
  71. package/dist/types/responses.d.ts.map +0 -1
  72. package/dist/types/responses.js +0 -6
  73. package/dist/types/responses.js.map +0 -1
  74. package/dist/types/shared.d.ts +0 -234
  75. package/dist/types/shared.d.ts.map +0 -1
  76. package/dist/types/shared.js +0 -37
  77. package/dist/types/shared.js.map +0 -1
  78. package/dist/utils/polling.d.ts +0 -57
  79. package/dist/utils/polling.d.ts.map +0 -1
  80. package/dist/utils/polling.js +0 -110
  81. package/dist/utils/polling.js.map +0 -1
  82. package/dist/utils/request.d.ts +0 -47
  83. package/dist/utils/request.d.ts.map +0 -1
  84. package/dist/utils/request.js +0 -192
  85. package/dist/utils/request.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,65 +1,2530 @@
1
- /**
2
- * DealCrawl SDK
3
- *
4
- * Official SDK for the DealCrawl web scraping and crawling API.
5
- *
6
- * @packageDocumentation
7
- *
8
- * @example Basic usage
9
- * ```ts
10
- * import { DealCrawl } from "@dealcrawl/sdk";
11
- *
12
- * const client = new DealCrawl({ apiKey: "sk_xxx" });
13
- *
14
- * // Scrape a single page
15
- * const job = await client.scrape.create({
16
- * url: "https://example.com/product",
17
- * extractDeal: true
18
- * });
19
- *
20
- * // Wait for result
21
- * const result = await client.waitForResult(job.jobId);
22
- * console.log(result);
23
- * ```
24
- *
25
- * @example Crawl with template
26
- * ```ts
27
- * const job = await client.crawl.withTemplate("ecommerce", {
28
- * url: "https://shop.example.com",
29
- * maxPages: 100
30
- * });
31
- * ```
32
- *
33
- * @example Extract structured data
34
- * ```ts
35
- * const job = await client.extract.withSchema(
36
- * "https://example.com/product",
37
- * {
38
- * type: "object",
39
- * properties: {
40
- * name: { type: "string" },
41
- * price: { type: "number" }
42
- * }
43
- * }
44
- * );
45
- * ```
46
- */
47
- // ============================================
48
- // MAIN CLIENT
49
- // ============================================
50
- export { DealCrawl } from "./client";
51
- // ============================================
52
- // ERROR HANDLING
53
- // ============================================
54
- export { DealCrawlError, ERROR_CODES } from "./error";
55
- // ============================================
56
- // RESOURCE CLASSES
57
- // ============================================
58
- export { ScrapeResource, CrawlResource, ExtractResource, DorkResource, StatusResource, DataResource, WebhooksResource, KeysResource, AccountResource, } from "./resources";
59
- export { DEFAULT_CONFIG } from "./types/config";
60
- export { waitForResult, waitForAll, waitForAny, pollUntil, } from "./utils/polling";
61
- // ============================================
62
- // DEFAULT EXPORT
63
- // ============================================
64
- export { DealCrawl as default } from "./client";
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/types/config.ts
6
+ var DEFAULT_CONFIG = {
7
+ baseUrl: "https://api.dealcrawl.dev",
8
+ timeout: 3e4,
9
+ maxRetries: 3,
10
+ retryDelay: 1e3
11
+ };
12
+
13
+ // src/error.ts
14
+ var ERROR_CODES = {
15
+ // Authentication
16
+ INVALID_API_KEY: "INVALID_API_KEY",
17
+ MISSING_API_KEY: "MISSING_API_KEY",
18
+ API_KEY_EXPIRED: "API_KEY_EXPIRED",
19
+ ACCOUNT_SUSPENDED: "ACCOUNT_SUSPENDED",
20
+ // Rate limiting
21
+ RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED",
22
+ QUOTA_EXCEEDED: "QUOTA_EXCEEDED",
23
+ // Validation
24
+ INVALID_URL: "INVALID_URL",
25
+ INVALID_REQUEST: "INVALID_REQUEST",
26
+ MISSING_REQUIRED_FIELD: "MISSING_REQUIRED_FIELD",
27
+ // Job errors
28
+ JOB_NOT_FOUND: "JOB_NOT_FOUND",
29
+ JOB_FAILED: "JOB_FAILED",
30
+ JOB_TIMEOUT: "JOB_TIMEOUT",
31
+ // Scraping errors
32
+ FETCH_FAILED: "FETCH_FAILED",
33
+ PARSE_FAILED: "PARSE_FAILED",
34
+ BLOCKED_BY_ROBOTS: "BLOCKED_BY_ROBOTS",
35
+ CAPTCHA_DETECTED: "CAPTCHA_DETECTED",
36
+ SITE_UNREACHABLE: "SITE_UNREACHABLE",
37
+ // System errors
38
+ INTERNAL_ERROR: "INTERNAL_ERROR",
39
+ SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
40
+ REDIS_ERROR: "REDIS_ERROR",
41
+ DATABASE_ERROR: "DATABASE_ERROR"
42
+ };
43
+ var DealCrawlError = class _DealCrawlError extends Error {
44
+ /** Error code from ERROR_CODES */
45
+ code;
46
+ /** HTTP status code from the API response */
47
+ statusCode;
48
+ /** Additional error details */
49
+ details;
50
+ /** Retry-After header value in seconds (for rate limiting) */
51
+ retryAfter;
52
+ constructor(options) {
53
+ super(options.message);
54
+ this.name = "DealCrawlError";
55
+ this.code = options.code;
56
+ this.statusCode = options.statusCode ?? 500;
57
+ this.details = options.details;
58
+ this.retryAfter = options.retryAfter;
59
+ if (Error.captureStackTrace) {
60
+ Error.captureStackTrace(this, _DealCrawlError);
61
+ }
62
+ }
63
+ /**
64
+ * Check if the error is retryable
65
+ * Rate limits and transient errors are retryable
66
+ */
67
+ isRetryable() {
68
+ const retryableCodes = [
69
+ ERROR_CODES.RATE_LIMIT_EXCEEDED,
70
+ ERROR_CODES.SERVICE_UNAVAILABLE,
71
+ ERROR_CODES.REDIS_ERROR,
72
+ ERROR_CODES.DATABASE_ERROR
73
+ ];
74
+ return retryableCodes.includes(this.code);
75
+ }
76
+ /**
77
+ * Check if the error is due to authentication issues
78
+ */
79
+ isAuthError() {
80
+ const authCodes = [
81
+ ERROR_CODES.INVALID_API_KEY,
82
+ ERROR_CODES.MISSING_API_KEY,
83
+ ERROR_CODES.API_KEY_EXPIRED,
84
+ ERROR_CODES.ACCOUNT_SUSPENDED
85
+ ];
86
+ return authCodes.includes(this.code);
87
+ }
88
+ /**
89
+ * Check if the error is due to rate limiting
90
+ */
91
+ isRateLimited() {
92
+ return this.code === ERROR_CODES.RATE_LIMIT_EXCEEDED;
93
+ }
94
+ /**
95
+ * Check if the error is due to quota exceeded
96
+ */
97
+ isQuotaExceeded() {
98
+ return this.code === ERROR_CODES.QUOTA_EXCEEDED;
99
+ }
100
+ /**
101
+ * Convert error to JSON-serializable object
102
+ */
103
+ toJSON() {
104
+ return {
105
+ name: this.name,
106
+ code: this.code,
107
+ message: this.message,
108
+ statusCode: this.statusCode,
109
+ details: this.details,
110
+ retryAfter: this.retryAfter
111
+ };
112
+ }
113
+ /**
114
+ * Create a DealCrawlError from an API error response
115
+ */
116
+ static fromResponse(statusCode, body, retryAfter) {
117
+ const code = body.code || mapStatusCodeToErrorCode(statusCode);
118
+ const message = body.message || body.error || "An error occurred";
119
+ let parsedRetryAfter;
120
+ if (retryAfter !== null && retryAfter !== void 0) {
121
+ parsedRetryAfter = typeof retryAfter === "string" ? parseInt(retryAfter, 10) : retryAfter;
122
+ if (isNaN(parsedRetryAfter)) {
123
+ parsedRetryAfter = void 0;
124
+ }
125
+ } else if (body.retryAfter !== void 0) {
126
+ parsedRetryAfter = body.retryAfter;
127
+ }
128
+ return new _DealCrawlError({
129
+ code,
130
+ message,
131
+ statusCode,
132
+ details: body.details,
133
+ retryAfter: parsedRetryAfter
134
+ });
135
+ }
136
+ };
137
+ function mapStatusCodeToErrorCode(statusCode) {
138
+ switch (statusCode) {
139
+ case 400:
140
+ return ERROR_CODES.INVALID_REQUEST;
141
+ case 401:
142
+ return ERROR_CODES.INVALID_API_KEY;
143
+ case 403:
144
+ return ERROR_CODES.ACCOUNT_SUSPENDED;
145
+ case 404:
146
+ return ERROR_CODES.JOB_NOT_FOUND;
147
+ case 429:
148
+ return ERROR_CODES.RATE_LIMIT_EXCEEDED;
149
+ case 500:
150
+ return ERROR_CODES.INTERNAL_ERROR;
151
+ case 503:
152
+ return ERROR_CODES.SERVICE_UNAVAILABLE;
153
+ default:
154
+ return ERROR_CODES.INTERNAL_ERROR;
155
+ }
156
+ }
157
+
158
+ // src/utils/request.ts
159
+ function buildQueryString(params) {
160
+ const entries = Object.entries(params).filter(([, value]) => value !== void 0).map(
161
+ ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
162
+ );
163
+ return entries.length > 0 ? `?${entries.join("&")}` : "";
164
+ }
165
+ function parseRateLimitHeaders(headers) {
166
+ return {
167
+ limit: parseInt(headers.get("X-RateLimit-Limit") || "0", 10) || 0,
168
+ remaining: parseInt(headers.get("X-RateLimit-Remaining") || "0", 10) || 0,
169
+ reset: parseInt(headers.get("X-RateLimit-Reset") || "0", 10) || 0
170
+ };
171
+ }
172
+ function sleep(ms) {
173
+ return new Promise((resolve) => setTimeout(resolve, ms));
174
+ }
175
+ async function request(ctx, path, options = {}) {
176
+ const { method = "GET", body, query, timeout, signal } = options;
177
+ const queryString = query ? buildQueryString(query) : "";
178
+ const url = `${ctx.baseUrl}${path}${queryString}`;
179
+ const timeoutMs = timeout ?? ctx.timeout;
180
+ let currentController = null;
181
+ const onAbort = () => {
182
+ if (currentController) {
183
+ currentController.abort();
184
+ }
185
+ };
186
+ if (signal) {
187
+ signal.addEventListener("abort", onAbort);
188
+ }
189
+ const cleanupExternalSignal = () => {
190
+ if (signal) {
191
+ signal.removeEventListener("abort", onAbort);
192
+ }
193
+ };
194
+ let lastError = null;
195
+ let attempt = 0;
196
+ while (attempt <= ctx.maxRetries) {
197
+ const controller = new AbortController();
198
+ currentController = controller;
199
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
200
+ let attemptCleanupDone = false;
201
+ const cleanupAttempt = () => {
202
+ if (attemptCleanupDone) return;
203
+ attemptCleanupDone = true;
204
+ clearTimeout(timeoutId);
205
+ currentController = null;
206
+ };
207
+ try {
208
+ const response = await fetch(url, {
209
+ method,
210
+ headers: {
211
+ "Content-Type": "application/json",
212
+ Authorization: `Bearer ${ctx.apiKey}`
213
+ },
214
+ body: body ? JSON.stringify(body) : void 0,
215
+ signal: controller.signal
216
+ });
217
+ cleanupAttempt();
218
+ const rateLimit = parseRateLimitHeaders(response.headers);
219
+ if (response.status === 429) {
220
+ const retryAfter = parseInt(
221
+ response.headers.get("Retry-After") || "60",
222
+ 10
223
+ );
224
+ if (ctx.onRateLimit) {
225
+ ctx.onRateLimit(rateLimit);
226
+ }
227
+ throw new DealCrawlError({
228
+ code: ERROR_CODES.RATE_LIMIT_EXCEEDED,
229
+ message: "Rate limit exceeded",
230
+ statusCode: 429,
231
+ retryAfter,
232
+ details: { rateLimit }
233
+ });
234
+ }
235
+ if (!response.ok) {
236
+ let errorBody = {};
237
+ try {
238
+ errorBody = await response.json();
239
+ } catch {
240
+ }
241
+ const retryAfterHeader = response.headers.get("Retry-After");
242
+ throw DealCrawlError.fromResponse(
243
+ response.status,
244
+ errorBody,
245
+ retryAfterHeader
246
+ );
247
+ }
248
+ let data;
249
+ const contentType = response.headers.get("Content-Type") || "";
250
+ if (contentType.includes("application/json")) {
251
+ data = await response.json();
252
+ } else if (contentType.includes("text/csv") || contentType.includes("text/plain")) {
253
+ data = await response.text();
254
+ } else {
255
+ data = await response.json();
256
+ }
257
+ cleanupExternalSignal();
258
+ return { data, rateLimit };
259
+ } catch (error) {
260
+ cleanupAttempt();
261
+ if (error instanceof Error && error.name === "AbortError") {
262
+ cleanupExternalSignal();
263
+ throw new DealCrawlError({
264
+ code: ERROR_CODES.JOB_TIMEOUT,
265
+ message: `Request timeout after ${timeoutMs}ms`,
266
+ statusCode: 408
267
+ });
268
+ }
269
+ if (error instanceof DealCrawlError) {
270
+ lastError = error;
271
+ if (error.isRetryable() && attempt < ctx.maxRetries) {
272
+ attempt++;
273
+ await sleep(ctx.retryDelay * attempt);
274
+ continue;
275
+ }
276
+ cleanupExternalSignal();
277
+ throw error;
278
+ }
279
+ cleanupExternalSignal();
280
+ throw new DealCrawlError({
281
+ code: ERROR_CODES.FETCH_FAILED,
282
+ message: error instanceof Error ? error.message : "Network request failed",
283
+ statusCode: 0
284
+ });
285
+ }
286
+ }
287
+ cleanupExternalSignal();
288
+ throw lastError ?? new DealCrawlError({
289
+ code: ERROR_CODES.INTERNAL_ERROR,
290
+ message: "Request failed after retries",
291
+ statusCode: 500
292
+ });
293
+ }
294
+ function get(ctx, path, query) {
295
+ return request(ctx, path, { method: "GET", query });
296
+ }
297
+ function post(ctx, path, body) {
298
+ return request(ctx, path, { method: "POST", body });
299
+ }
300
+ function patch(ctx, path, body) {
301
+ return request(ctx, path, { method: "PATCH", body });
302
+ }
303
+ function del(ctx, path, body) {
304
+ return request(ctx, path, { method: "DELETE", body });
305
+ }
306
+
307
+ // src/utils/polling.ts
308
+ async function waitForResult(ctx, jobId, options = {}) {
309
+ const {
310
+ pollInterval = 2e3,
311
+ timeout = 3e5,
312
+ // 5 minutes
313
+ onProgress,
314
+ onStatusChange,
315
+ signal
316
+ } = options;
317
+ const startTime = Date.now();
318
+ let lastStatus = null;
319
+ while (true) {
320
+ if (signal?.aborted) {
321
+ throw new DealCrawlError({
322
+ code: ERROR_CODES.JOB_TIMEOUT,
323
+ message: "Wait cancelled by user",
324
+ statusCode: 0
325
+ });
326
+ }
327
+ const elapsed = Date.now() - startTime;
328
+ if (elapsed >= timeout) {
329
+ throw new DealCrawlError({
330
+ code: ERROR_CODES.JOB_TIMEOUT,
331
+ message: `Job did not complete within ${timeout}ms`,
332
+ statusCode: 408,
333
+ details: { jobId, elapsed }
334
+ });
335
+ }
336
+ const { data: status } = await get(
337
+ ctx,
338
+ `/v1/status/${jobId}`
339
+ );
340
+ if (onProgress) {
341
+ onProgress(status);
342
+ }
343
+ if (onStatusChange && lastStatus !== null && lastStatus !== status.status) {
344
+ onStatusChange(status.status, lastStatus);
345
+ }
346
+ lastStatus = status.status;
347
+ if (status.status === "completed") {
348
+ return {
349
+ jobId,
350
+ status: "completed",
351
+ result: status.result,
352
+ waitTime: Date.now() - startTime
353
+ };
354
+ }
355
+ if (status.status === "failed") {
356
+ return {
357
+ jobId,
358
+ status: "failed",
359
+ error: status.error,
360
+ waitTime: Date.now() - startTime
361
+ };
362
+ }
363
+ await sleep2(pollInterval);
364
+ }
365
+ }
366
+ async function waitForAll(ctx, jobIds, options = {}) {
367
+ const promises = jobIds.map((jobId) => waitForResult(ctx, jobId, options));
368
+ return Promise.all(promises);
369
+ }
370
+ async function waitForAny(ctx, jobIds, options = {}) {
371
+ const promises = jobIds.map((jobId) => waitForResult(ctx, jobId, options));
372
+ return Promise.race(promises);
373
+ }
374
+ async function pollUntil(fetchFn, conditionFn, options = {}) {
375
+ const { pollInterval = 2e3, timeout = 3e5, signal } = options;
376
+ const startTime = Date.now();
377
+ while (true) {
378
+ if (signal?.aborted) {
379
+ throw new Error("Polling cancelled");
380
+ }
381
+ if (Date.now() - startTime >= timeout) {
382
+ throw new Error(`Polling timeout after ${timeout}ms`);
383
+ }
384
+ const data = await fetchFn();
385
+ if (conditionFn(data)) {
386
+ return data;
387
+ }
388
+ await sleep2(pollInterval);
389
+ }
390
+ }
391
+ function sleep2(ms) {
392
+ return new Promise((resolve) => setTimeout(resolve, ms));
393
+ }
394
+
395
+ // src/resources/account.ts
396
+ var AccountResource = class {
397
+ constructor(ctx) {
398
+ this.ctx = ctx;
399
+ }
400
+ /**
401
+ * Get current account information
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * const account = await client.account.get();
406
+ * console.log(account.name);
407
+ * console.log(account.tier);
408
+ * console.log(account.usage);
409
+ * ```
410
+ */
411
+ async get() {
412
+ const result = await get(this.ctx, "/v1/client");
413
+ return result.data;
414
+ }
415
+ /**
416
+ * Get detailed account metrics
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * const metrics = await client.account.getMetrics();
421
+ * console.log(metrics.dealMetrics.totalDeals);
422
+ * console.log(metrics.categoryPerformance);
423
+ * ```
424
+ */
425
+ async getMetrics() {
426
+ const result = await get(
427
+ this.ctx,
428
+ "/v1/client/metrics"
429
+ );
430
+ return result.data;
431
+ }
432
+ /**
433
+ * Get DealUp sync metrics (pro/enterprise only)
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * const dealup = await client.account.getDealUpMetrics();
438
+ * console.log(dealup.sync.totalSynced);
439
+ * console.log(dealup.sync.clicksGenerated);
440
+ * ```
441
+ */
442
+ async getDealUpMetrics() {
443
+ const result = await get(
444
+ this.ctx,
445
+ "/v1/client/dealup"
446
+ );
447
+ return result.data;
448
+ }
449
+ /**
450
+ * Get crawl recommendations
451
+ * AI-suggested URLs to crawl based on past performance
452
+ *
453
+ * @example
454
+ * ```ts
455
+ * const recommendations = await client.account.getRecommendations();
456
+ * recommendations.recommendations.forEach(r => {
457
+ * console.log(r.url, r.reason, r.estimatedDeals);
458
+ * });
459
+ * ```
460
+ */
461
+ async getRecommendations() {
462
+ const result = await get(
463
+ this.ctx,
464
+ "/v1/client/recommendations"
465
+ );
466
+ return result.data;
467
+ }
468
+ /**
469
+ * Get current preferences
470
+ *
471
+ * @example
472
+ * ```ts
473
+ * const prefs = await client.account.getPreferences();
474
+ * console.log(prefs.preferences.minDealScore);
475
+ * console.log(prefs.preferences.autoSync);
476
+ * ```
477
+ */
478
+ async getPreferences() {
479
+ const result = await get(
480
+ this.ctx,
481
+ "/v1/client/preferences"
482
+ );
483
+ return result.data;
484
+ }
485
+ /**
486
+ * Update account preferences
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * const updated = await client.account.updatePreferences({
491
+ * minDealScore: 70,
492
+ * autoSync: true,
493
+ * preferredCategories: ["software", "courses"]
494
+ * });
495
+ * ```
496
+ */
497
+ async updatePreferences(options) {
498
+ const result = await patch(
499
+ this.ctx,
500
+ "/v1/client/preferences",
501
+ {
502
+ preferredCategories: options.preferredCategories,
503
+ minDealScore: options.minDealScore,
504
+ autoSync: options.autoSync,
505
+ webhookEnabled: options.webhookEnabled
506
+ }
507
+ );
508
+ return result.data;
509
+ }
510
+ /**
511
+ * Track a DealUp click (webhook endpoint)
512
+ * Used internally by DealUp to track click-through
513
+ *
514
+ * @example
515
+ * ```ts
516
+ * await client.account.trackClick("deal_abc123", "homepage");
517
+ * ```
518
+ */
519
+ async trackClick(dealId, source) {
520
+ const result = await post(
521
+ this.ctx,
522
+ "/v1/client/dealup/click",
523
+ {
524
+ dealId,
525
+ source
526
+ }
527
+ );
528
+ return result.data;
529
+ }
530
+ // ============================================
531
+ // CONVENIENCE METHODS
532
+ // ============================================
533
+ /**
534
+ * Get remaining quota for a resource
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * const remaining = await client.account.getRemainingQuota("scrapes");
539
+ * console.log(`${remaining} scrapes left this period`);
540
+ * ```
541
+ */
542
+ async getRemainingQuota(resource) {
543
+ const account = await this.get();
544
+ const usage = account.usage[resource];
545
+ return usage.limit - usage.used;
546
+ }
547
+ /**
548
+ * Check if account has quota for a resource
549
+ *
550
+ * @example
551
+ * ```ts
552
+ * if (await client.account.hasQuota("crawls", 5)) {
553
+ * // Safe to start 5 crawls
554
+ * }
555
+ * ```
556
+ */
557
+ async hasQuota(resource, needed = 1) {
558
+ const remaining = await this.getRemainingQuota(resource);
559
+ return remaining >= needed;
560
+ }
561
+ /**
562
+ * Get account tier
563
+ *
564
+ * @example
565
+ * ```ts
566
+ * const tier = await client.account.getTier();
567
+ * if (tier === "enterprise") {
568
+ * // Enable advanced features
569
+ * }
570
+ * ```
571
+ */
572
+ async getTier() {
573
+ const account = await this.get();
574
+ return account.tier;
575
+ }
576
+ /**
577
+ * Check if account is pro or enterprise tier
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * if (await client.account.isPremium()) {
582
+ * // Show premium features
583
+ * }
584
+ * ```
585
+ */
586
+ async isPremium() {
587
+ const tier = await this.getTier();
588
+ return tier === "pro" || tier === "enterprise";
589
+ }
590
+ };
591
+
592
+ // src/resources/crawl.ts
593
+ var CRAWL_TEMPLATES = {
594
+ ecommerce: {
595
+ id: "ecommerce",
596
+ name: "E-commerce",
597
+ description: "Optimized for product pages and online stores",
598
+ defaultOptions: {
599
+ maxDepth: 3,
600
+ maxPages: 500,
601
+ delayMs: 1500,
602
+ extractDeal: true,
603
+ prioritizeDealPages: true,
604
+ excludePatterns: [
605
+ "*/cart*",
606
+ "*/checkout*",
607
+ "*/account*",
608
+ "*/login*",
609
+ "*/register*",
610
+ "*/wishlist*",
611
+ "*/compare*"
612
+ ]
613
+ }
614
+ },
615
+ blog: {
616
+ id: "blog",
617
+ name: "Blog",
618
+ description: "Optimized for blog posts and articles",
619
+ defaultOptions: {
620
+ maxDepth: 2,
621
+ maxPages: 200,
622
+ delayMs: 1e3,
623
+ extractDeal: false,
624
+ prioritizeDealPages: false,
625
+ excludePatterns: ["*/tag/*", "*/category/*", "*/author/*", "*/page/*"]
626
+ }
627
+ },
628
+ docs: {
629
+ id: "docs",
630
+ name: "Documentation",
631
+ description: "Optimized for documentation sites",
632
+ defaultOptions: {
633
+ maxDepth: 4,
634
+ maxPages: 300,
635
+ delayMs: 500,
636
+ extractDeal: false,
637
+ prioritizeDealPages: false
638
+ }
639
+ },
640
+ marketplace: {
641
+ id: "marketplace",
642
+ name: "Marketplace",
643
+ description: "Optimized for multi-vendor marketplaces",
644
+ defaultOptions: {
645
+ maxDepth: 3,
646
+ maxPages: 1e3,
647
+ delayMs: 2e3,
648
+ extractDeal: true,
649
+ prioritizeDealPages: true,
650
+ followExternalLinks: false,
651
+ excludePatterns: [
652
+ "*/seller/*",
653
+ "*/vendor/*",
654
+ "*/shop/*",
655
+ "*/reviews*",
656
+ "*/questions*"
657
+ ]
658
+ }
659
+ },
660
+ custom: {
661
+ id: "custom",
662
+ name: "Custom",
663
+ description: "No preset - use your own settings",
664
+ defaultOptions: {}
665
+ }
666
+ };
667
+ var CrawlResource = class {
668
+ constructor(ctx) {
669
+ this.ctx = ctx;
670
+ }
671
+ /**
672
+ * Create a new crawl job
673
+ *
674
+ * @example
675
+ * ```ts
676
+ * const job = await client.crawl.create({
677
+ * url: "https://example.com",
678
+ * maxDepth: 3,
679
+ * maxPages: 100,
680
+ * extractDeal: true
681
+ * });
682
+ * console.log(job.jobId);
683
+ * ```
684
+ */
685
+ async create(options) {
686
+ const body = {
687
+ startUrl: options.url || options.startUrl,
688
+ url: options.url || options.startUrl,
689
+ prompt: options.prompt,
690
+ maxDepth: options.maxDepth,
691
+ maxPages: options.maxPages,
692
+ delayMs: options.delayMs,
693
+ detectSignals: options.detectSignals,
694
+ extractWithAI: options.extractWithAI,
695
+ extractDeal: options.extractDeal,
696
+ minDealScore: options.minDealScore,
697
+ prioritizeDealPages: options.prioritizeDealPages,
698
+ followExternalLinks: options.followExternalLinks,
699
+ allowedDomains: options.allowedDomains,
700
+ excludePatterns: options.excludePatterns
701
+ };
702
+ const result = await post(this.ctx, "/v1/crawl", body);
703
+ return result.data;
704
+ }
705
+ /**
706
+ * Create a crawl job using a template
707
+ *
708
+ * @example
709
+ * ```ts
710
+ * const job = await client.crawl.withTemplate("ecommerce", {
711
+ * url: "https://shop.example.com",
712
+ * maxPages: 200 // Override template default
713
+ * });
714
+ * ```
715
+ */
716
+ async withTemplate(templateId, options) {
717
+ const template = CRAWL_TEMPLATES[templateId];
718
+ if (!template) {
719
+ throw new Error(
720
+ `Invalid crawl templateId: ${templateId}. Available templates: ${Object.keys(CRAWL_TEMPLATES).join(", ")}`
721
+ );
722
+ }
723
+ return this.create({
724
+ ...template.defaultOptions,
725
+ ...options
726
+ });
727
+ }
728
+ /**
729
+ * List available crawl templates
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * const templates = client.crawl.listTemplates();
734
+ * templates.forEach(t => console.log(t.name, t.description));
735
+ * ```
736
+ */
737
+ listTemplates() {
738
+ return Object.values(CRAWL_TEMPLATES);
739
+ }
740
+ /**
741
+ * Get a specific template by ID
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * const template = client.crawl.getTemplate("ecommerce");
746
+ * console.log(template.defaultOptions);
747
+ * ```
748
+ */
749
+ getTemplate(templateId) {
750
+ return CRAWL_TEMPLATES[templateId];
751
+ }
752
+ /**
753
+ * Analyze a URL before crawling
754
+ * Returns recommended settings based on site structure
755
+ *
756
+ * @example
757
+ * ```ts
758
+ * const analysis = await client.crawl.analyze("https://shop.example.com");
759
+ * console.log(analysis.recommendedTemplate);
760
+ * console.log(analysis.estimatedPages);
761
+ * ```
762
+ */
763
+ async analyze(url) {
764
+ const result = await get(
765
+ this.ctx,
766
+ "/v1/crawl/analyze",
767
+ {
768
+ url
769
+ }
770
+ );
771
+ return result.data;
772
+ }
773
+ /**
774
+ * Crawl a URL with deal extraction enabled
775
+ * Convenience method for e-commerce crawling
776
+ *
777
+ * @example
778
+ * ```ts
779
+ * const job = await client.crawl.forDeals("https://shop.example.com", {
780
+ * minDealScore: 70
781
+ * });
782
+ * ```
783
+ */
784
+ async forDeals(url, options) {
785
+ return this.withTemplate("ecommerce", {
786
+ url,
787
+ extractDeal: true,
788
+ prioritizeDealPages: true,
789
+ ...options
790
+ });
791
+ }
792
+ };
793
+
794
+ // src/resources/data.ts
795
+ var DataResource = class {
796
+ constructor(ctx) {
797
+ this.ctx = ctx;
798
+ }
799
+ // ============================================
800
+ // JOBS
801
+ // ============================================
802
+ /**
803
+ * List all jobs
804
+ *
805
+ * @example
806
+ * ```ts
807
+ * const jobs = await client.data.listJobs({
808
+ * status: "completed",
809
+ * type: "crawl",
810
+ * page: 1,
811
+ * limit: 20
812
+ * });
813
+ * console.log(jobs.data);
814
+ * console.log(jobs.pagination.total);
815
+ * ```
816
+ */
817
+ async listJobs(options = {}) {
818
+ const result = await get(this.ctx, "/v1/data/jobs", {
819
+ page: options.page,
820
+ limit: options.limit,
821
+ status: options.status,
822
+ type: options.type,
823
+ sortBy: options.sortBy,
824
+ sortOrder: options.sortOrder,
825
+ fromDate: options.fromDate,
826
+ toDate: options.toDate
827
+ });
828
+ return result.data;
829
+ }
830
+ /**
831
+ * Get jobs by status
832
+ * Convenience method for filtering by status
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * const activeJobs = await client.data.getJobsByStatus("active");
837
+ * ```
838
+ */
839
+ async getJobsByStatus(status, options) {
840
+ return this.listJobs({ status, ...options });
841
+ }
842
+ /**
843
+ * Get jobs by type
844
+ * Convenience method for filtering by type
845
+ *
846
+ * @example
847
+ * ```ts
848
+ * const crawlJobs = await client.data.getJobsByType("crawl");
849
+ * ```
850
+ */
851
+ async getJobsByType(type, options) {
852
+ return this.listJobs({ type, ...options });
853
+ }
854
+ /**
855
+ * Get recent jobs
856
+ * Convenience method for getting latest jobs
857
+ *
858
+ * @example
859
+ * ```ts
860
+ * const recentJobs = await client.data.getRecentJobs(10);
861
+ * ```
862
+ */
863
+ async getRecentJobs(limit = 10) {
864
+ return this.listJobs({
865
+ limit,
866
+ sortBy: "created_at",
867
+ sortOrder: "desc"
868
+ });
869
+ }
870
+ // ============================================
871
+ // DEALS
872
+ // ============================================
873
+ /**
874
+ * List all deals
875
+ *
876
+ * @example
877
+ * ```ts
878
+ * const deals = await client.data.listDeals({
879
+ * minScore: 70,
880
+ * category: "electronics",
881
+ * sortBy: "deal_score",
882
+ * sortOrder: "desc"
883
+ * });
884
+ * ```
885
+ */
886
+ async listDeals(options = {}) {
887
+ const result = await get(this.ctx, "/v1/data/deals", {
888
+ page: options.page,
889
+ limit: options.limit,
890
+ minScore: options.minScore,
891
+ maxPrice: options.maxPrice,
892
+ category: options.category,
893
+ merchant: options.merchant,
894
+ synced: options.synced?.toString(),
895
+ sortBy: options.sortBy,
896
+ sortOrder: options.sortOrder
897
+ });
898
+ return result.data;
899
+ }
900
+ /**
901
+ * Get a single deal by ID
902
+ *
903
+ * @example
904
+ * ```ts
905
+ * const deal = await client.data.getDeal("deal_abc123");
906
+ * console.log(deal.product.name);
907
+ * console.log(deal.pricing.discountPercent);
908
+ * ```
909
+ */
910
+ async getDeal(dealId) {
911
+ if (!dealId || !dealId.trim()) {
912
+ throw new Error("dealId is required and cannot be empty");
913
+ }
914
+ const result = await get(this.ctx, `/v1/data/deals/${dealId}`);
915
+ return result.data;
916
+ }
917
+ /**
918
+ * Get top deals by score
919
+ * Convenience method for finding best deals
920
+ *
921
+ * @example
922
+ * ```ts
923
+ * const topDeals = await client.data.getTopDeals(20, 80);
924
+ * ```
925
+ */
926
+ async getTopDeals(limit = 20, minScore = 70) {
927
+ return this.listDeals({
928
+ limit,
929
+ minScore,
930
+ sortBy: "deal_score",
931
+ sortOrder: "desc"
932
+ });
933
+ }
934
+ /**
935
+ * Get deals by category
936
+ * Convenience method for filtering by category
937
+ *
938
+ * @example
939
+ * ```ts
940
+ * const electronicsDeals = await client.data.getDealsByCategory("electronics");
941
+ async getDealsByCategory(
942
+ category: string,
943
+ options?: Omit<ListDealsOptions, "category">
944
+ ): Promise<ListDealsResponse> {
945
+ if (!category || !category.trim()) {
946
+ throw new Error("category is required and cannot be empty");
947
+ }
948
+ return this.listDeals({ category, ...options });
949
+ } return this.listDeals({ category, ...options });
950
+ }
951
+
952
+ /**
953
+ * Get unsynced deals (not yet sent to DealUp)
954
+ *
955
+ * @example
956
+ * ```ts
957
+ * const unsyncedDeals = await client.data.getUnsyncedDeals();
958
+ * ```
959
+ */
960
+ async getUnsyncedDeals(options) {
961
+ return this.listDeals({ synced: false, ...options });
962
+ }
963
+ // ============================================
964
+ // EXPORT
965
+ // ============================================
966
+ /**
967
+ * Export jobs data
968
+ *
969
+ * @example
970
+ * ```ts
971
+ * // Export as JSON
972
+ * const jsonData = await client.data.exportJobs({ format: "json" });
973
+ *
974
+ * // Export as CSV
975
+ * const csvData = await client.data.exportJobs({ format: "csv" });
976
+ * ```
977
+ */
978
+ async exportJobs(options = {}) {
979
+ const result = await get(
980
+ this.ctx,
981
+ "/v1/data/export/jobs",
982
+ {
983
+ format: options.format || "json",
984
+ status: options.status,
985
+ type: options.type,
986
+ fromDate: options.fromDate,
987
+ toDate: options.toDate
988
+ }
989
+ );
990
+ return result.data;
991
+ }
992
+ /**
993
+ * Export deals data
994
+ *
995
+ * @example
996
+ * ```ts
997
+ * // Export as JSON
998
+ * const jsonData = await client.data.exportDeals({ format: "json" });
999
+ *
1000
+ * // Export as CSV with filters
1001
+ * const csvData = await client.data.exportDeals({
1002
+ * format: "csv",
1003
+ * minScore: 70,
1004
+ * category: "software"
1005
+ * });
1006
+ * ```
1007
+ */
1008
+ async exportDeals(options = {}) {
1009
+ const result = await get(
1010
+ this.ctx,
1011
+ "/v1/data/export/deals",
1012
+ {
1013
+ format: options.format || "json",
1014
+ minScore: options.minScore,
1015
+ maxPrice: options.maxPrice,
1016
+ category: options.category,
1017
+ includeRawSignals: options.includeRawSignals
1018
+ }
1019
+ );
1020
+ return result.data;
1021
+ }
1022
+ // ============================================
1023
+ // STATS
1024
+ // ============================================
1025
+ /**
1026
+ * Get client statistics
1027
+ *
1028
+ * @example
1029
+ * ```ts
1030
+ * const stats = await client.data.getStats();
1031
+ * console.log(stats.totals.deals);
1032
+ * console.log(stats.performance.avgDealScore);
1033
+ * ```
1034
+ */
1035
+ async getStats() {
1036
+ const result = await get(this.ctx, "/v1/data/stats");
1037
+ return result.data;
1038
+ }
1039
+ };
1040
+
1041
+ // src/resources/dork.ts
1042
+ var DorkResource = class {
1043
+ constructor(ctx) {
1044
+ this.ctx = ctx;
1045
+ }
1046
+ /**
1047
+ * Create a new dork search job
1048
+ *
1049
+ * @example
1050
+ * ```ts
1051
+ * const job = await client.dork.create({
1052
+ * query: "discount coupon",
1053
+ * site: "amazon.com",
1054
+ * maxResults: 20
1055
+ * });
1056
+ * console.log(job.jobId);
1057
+ * ```
1058
+ */
1059
+ async create(options) {
1060
+ const body = {
1061
+ query: this.buildQuery(options),
1062
+ maxResults: options.maxResults,
1063
+ region: options.region
1064
+ };
1065
+ const result = await post(this.ctx, "/v1/dork", body);
1066
+ return result.data;
1067
+ }
1068
+ /**
1069
+ * Search for deals on a specific site
1070
+ * Convenience method for finding discount pages
1071
+ *
1072
+ * @example
1073
+ * ```ts
1074
+ * const job = await client.dork.findDeals("amazon.com", {
1075
+ * maxResults: 50
1076
+ * });
1077
+ * ```
1078
+ */
1079
+ async findDeals(site, options) {
1080
+ return this.create({
1081
+ query: "discount OR coupon OR sale OR promo OR deal",
1082
+ site,
1083
+ ...options
1084
+ });
1085
+ }
1086
+ /**
1087
+ * Search for product pages on a site
1088
+ * Looks for common product URL patterns
1089
+ *
1090
+ * @example
1091
+ * ```ts
1092
+ * const job = await client.dork.findProducts("shop.example.com", {
1093
+ * maxResults: 100
1094
+ * });
1095
+ * ```
1096
+ */
1097
+ async findProducts(site, options) {
1098
+ return this.create({
1099
+ query: "product OR item OR buy",
1100
+ site,
1101
+ inUrl: "product",
1102
+ ...options
1103
+ });
1104
+ }
1105
+ /**
1106
+ * Search for PDFs on a site
1107
+ * Useful for finding manuals, guides, datasheets
1108
+ *
1109
+ * @example
1110
+ * ```ts
1111
+ * const job = await client.dork.findPDFs("docs.example.com", "user guide");
1112
+ * ```
1113
+ */
1114
+ async findPDFs(site, query, options) {
1115
+ return this.create({
1116
+ query: query || "*",
1117
+ site,
1118
+ fileType: "pdf",
1119
+ ...options
1120
+ });
1121
+ }
1122
+ /**
1123
+ * Search with custom title filter
1124
+ * Find pages with specific terms in title
1125
+ *
1126
+ * @example
1127
+ * ```ts
1128
+ * const job = await client.dork.inTitle("Black Friday", {
1129
+ * site: "bestbuy.com",
1130
+ * maxResults: 30
1131
+ * });
1132
+ * ```
1133
+ */
1134
+ async inTitle(titleQuery, options) {
1135
+ return this.create({
1136
+ query: options?.query || titleQuery,
1137
+ inTitle: titleQuery,
1138
+ ...options
1139
+ });
1140
+ }
1141
+ /**
1142
+ * Search with custom URL filter
1143
+ * Find pages with specific terms in URL
1144
+ *
1145
+ * @example
1146
+ * ```ts
1147
+ * const job = await client.dork.inUrl("clearance", {
1148
+ * site: "walmart.com"
1149
+ * });
1150
+ * ```
1151
+ */
1152
+ async inUrl(urlQuery, options) {
1153
+ return this.create({
1154
+ query: options?.query || urlQuery,
1155
+ inUrl: urlQuery,
1156
+ ...options
1157
+ });
1158
+ }
1159
+ /**
1160
+ * Build a raw Google dork query string
1161
+ * Useful for preview or debugging
1162
+ *
1163
+ * @example
1164
+ * ```ts
1165
+ * const query = client.dork.buildQuery({
1166
+ * query: "laptop deals",
1167
+ * site: "amazon.com",
1168
+ * inTitle: "discount"
1169
+ * });
1170
+ * // Returns: "laptop deals site:amazon.com intitle:discount"
1171
+ * ```
1172
+ */
1173
+ buildQuery(options) {
1174
+ const parts = [];
1175
+ if (typeof options.query === "string" && options.query.trim() !== "") {
1176
+ parts.push(options.query);
1177
+ }
1178
+ if (options.site) {
1179
+ parts.push(`site:${options.site}`);
1180
+ }
1181
+ if (options.fileType) {
1182
+ parts.push(`filetype:${options.fileType}`);
1183
+ }
1184
+ if (options.inUrl) {
1185
+ parts.push(`inurl:${options.inUrl}`);
1186
+ }
1187
+ if (options.inTitle) {
1188
+ parts.push(`intitle:${options.inTitle}`);
1189
+ }
1190
+ return parts.join(" ");
1191
+ }
1192
+ };
1193
+
1194
+ // src/resources/extract.ts
1195
+ var ExtractResource = class {
1196
+ constructor(ctx) {
1197
+ this.ctx = ctx;
1198
+ }
1199
+ /**
1200
+ * Create a new extraction job
1201
+ * Either schema or prompt must be provided
1202
+ *
1203
+ * @example
1204
+ * ```ts
1205
+ * // Schema-based extraction
1206
+ * const job = await client.extract.create({
1207
+ * url: "https://example.com/product",
1208
+ * schema: {
1209
+ * type: "object",
1210
+ * properties: {
1211
+ * name: { type: "string" },
1212
+ * price: { type: "number" },
1213
+ * features: { type: "array", items: { type: "string" } }
1214
+ * }
1215
+ * }
1216
+ * });
1217
+ *
1218
+ * // Prompt-based extraction
1219
+ * const job = await client.extract.create({
1220
+ * url: "https://example.com/article",
1221
+ * prompt: "Extract the main points and author name"
1222
+ * });
1223
+ * ```
1224
+ */
1225
+ async create(options) {
1226
+ if (!options.schema && !options.prompt) {
1227
+ throw new Error("Either 'schema' or 'prompt' must be provided");
1228
+ }
1229
+ const body = {
1230
+ url: options.url,
1231
+ schema: options.schema,
1232
+ prompt: options.prompt,
1233
+ onlyMainContent: options.onlyMainContent,
1234
+ excludeTags: options.excludeTags,
1235
+ excludeSelectors: options.excludeSelectors,
1236
+ model: options.model,
1237
+ temperature: options.temperature,
1238
+ webhook: options.webhook,
1239
+ headers: options.headers,
1240
+ timeout: options.timeout
1241
+ };
1242
+ const result = await post(
1243
+ this.ctx,
1244
+ "/v1/extract",
1245
+ body
1246
+ );
1247
+ return result.data;
1248
+ }
1249
+ /**
1250
+ * Extract data using a JSON Schema
1251
+ * Convenience method for schema-based extraction
1252
+ *
1253
+ * @example
1254
+ * ```ts
1255
+ * const job = await client.extract.withSchema(
1256
+ * "https://example.com/product",
1257
+ * {
1258
+ * type: "object",
1259
+ * properties: {
1260
+ * name: { type: "string" },
1261
+ * price: { type: "number" }
1262
+ * }
1263
+ * }
1264
+ * );
1265
+ * ```
1266
+ */
1267
+ async withSchema(url, schema, options) {
1268
+ return this.create({
1269
+ url,
1270
+ schema,
1271
+ ...options
1272
+ });
1273
+ }
1274
+ /**
1275
+ * Extract data using a natural language prompt
1276
+ * Convenience method for prompt-based extraction
1277
+ *
1278
+ * @example
1279
+ * ```ts
1280
+ * const job = await client.extract.withPrompt(
1281
+ * "https://example.com/article",
1282
+ * "Extract the article title, author, publication date, and a brief summary"
1283
+ * );
1284
+ * ```
1285
+ */
1286
+ async withPrompt(url, prompt, options) {
1287
+ return this.create({
1288
+ url,
1289
+ prompt,
1290
+ ...options
1291
+ });
1292
+ }
1293
+ /**
1294
+ * Extract product information from a page
1295
+ * Pre-built schema for common e-commerce use case
1296
+ *
1297
+ * @example
1298
+ * ```ts
1299
+ * const job = await client.extract.product("https://shop.example.com/item");
1300
+ * ```
1301
+ */
1302
+ async product(url, options) {
1303
+ const productSchema = {
1304
+ type: "object",
1305
+ properties: {
1306
+ name: { type: "string", description: "Product name" },
1307
+ brand: { type: "string", description: "Brand name" },
1308
+ price: { type: "number", description: "Current price" },
1309
+ originalPrice: {
1310
+ type: "number",
1311
+ description: "Original price before discount"
1312
+ },
1313
+ currency: {
1314
+ type: "string",
1315
+ description: "Currency code (USD, EUR, etc.)"
1316
+ },
1317
+ description: { type: "string", description: "Product description" },
1318
+ features: {
1319
+ type: "array",
1320
+ items: { type: "string" },
1321
+ description: "List of product features"
1322
+ },
1323
+ images: {
1324
+ type: "array",
1325
+ items: { type: "string" },
1326
+ description: "Product image URLs"
1327
+ },
1328
+ availability: {
1329
+ type: "string",
1330
+ description: "In stock, out of stock, etc."
1331
+ },
1332
+ rating: { type: "number", description: "Average rating" },
1333
+ reviewCount: { type: "number", description: "Number of reviews" }
1334
+ },
1335
+ required: ["name", "price"]
1336
+ };
1337
+ return this.create({
1338
+ url,
1339
+ schema: productSchema,
1340
+ model: "gpt-4o-mini",
1341
+ ...options
1342
+ });
1343
+ }
1344
+ /**
1345
+ * Extract article/blog post information
1346
+ * Pre-built schema for content extraction
1347
+ *
1348
+ * @example
1349
+ * ```ts
1350
+ * const job = await client.extract.article("https://blog.example.com/post");
1351
+ * ```
1352
+ */
1353
+ async article(url, options) {
1354
+ const articleSchema = {
1355
+ type: "object",
1356
+ properties: {
1357
+ title: { type: "string", description: "Article title" },
1358
+ author: { type: "string", description: "Author name" },
1359
+ publishedAt: { type: "string", description: "Publication date" },
1360
+ summary: {
1361
+ type: "string",
1362
+ description: "Brief summary of the article"
1363
+ },
1364
+ content: { type: "string", description: "Main article content" },
1365
+ tags: {
1366
+ type: "array",
1367
+ items: { type: "string" },
1368
+ description: "Article tags or categories"
1369
+ },
1370
+ readingTime: {
1371
+ type: "number",
1372
+ description: "Estimated reading time in minutes"
1373
+ }
1374
+ },
1375
+ required: ["title", "content"]
1376
+ };
1377
+ return this.create({
1378
+ url,
1379
+ schema: articleSchema,
1380
+ onlyMainContent: true,
1381
+ ...options
1382
+ });
1383
+ }
1384
+ /**
1385
+ * Extract contact information from a page
1386
+ * Pre-built schema for contact page scraping
1387
+ *
1388
+ * @example
1389
+ * ```ts
1390
+ * const job = await client.extract.contact("https://example.com/contact");
1391
+ * ```
1392
+ */
1393
+ async contact(url, options) {
1394
+ const contactSchema = {
1395
+ type: "object",
1396
+ properties: {
1397
+ companyName: {
1398
+ type: "string",
1399
+ description: "Company or organization name"
1400
+ },
1401
+ email: { type: "string", description: "Contact email address" },
1402
+ phone: { type: "string", description: "Phone number" },
1403
+ address: {
1404
+ type: "object",
1405
+ properties: {
1406
+ street: { type: "string" },
1407
+ city: { type: "string" },
1408
+ state: { type: "string" },
1409
+ postalCode: { type: "string" },
1410
+ country: { type: "string" }
1411
+ },
1412
+ description: "Physical address"
1413
+ },
1414
+ socialMedia: {
1415
+ type: "object",
1416
+ properties: {
1417
+ twitter: { type: "string" },
1418
+ linkedin: { type: "string" },
1419
+ facebook: { type: "string" },
1420
+ instagram: { type: "string" }
1421
+ },
1422
+ description: "Social media links"
1423
+ }
1424
+ }
1425
+ };
1426
+ return this.create({
1427
+ url,
1428
+ schema: contactSchema,
1429
+ onlyMainContent: false,
1430
+ // Contact info might be in footer
1431
+ ...options
1432
+ });
1433
+ }
1434
+ };
1435
+
1436
+ // src/resources/keys.ts
1437
+ var KeysResource = class {
1438
+ constructor(ctx) {
1439
+ this.ctx = ctx;
1440
+ }
1441
+ /**
1442
+ * List all API keys
1443
+ *
1444
+ * @example
1445
+ * ```ts
1446
+ * const keys = await client.keys.list();
1447
+ * keys.keys.forEach(k => {
1448
+ * console.log(k.name, k.prefix, k.isActive);
1449
+ * });
1450
+ * ```
1451
+ */
1452
+ async list(options = {}) {
1453
+ const result = await get(this.ctx, "/v1/keys", {
1454
+ includeRevoked: options.includeRevoked
1455
+ });
1456
+ return result.data;
1457
+ }
1458
+ /**
1459
+ * Create a new API key
1460
+ *
1461
+ * @example
1462
+ * ```ts
1463
+ * const newKey = await client.keys.create({
1464
+ * name: "Production Key",
1465
+ * scopes: ["scrape", "crawl", "status", "data:read"],
1466
+ * expiresInDays: 365
1467
+ * });
1468
+ *
1469
+ * // IMPORTANT: Save this key immediately - it won't be shown again!
1470
+ * console.log(newKey.key);
1471
+ * ```
1472
+ */
1473
+ async create(options) {
1474
+ const result = await post(this.ctx, "/v1/keys", {
1475
+ name: options.name,
1476
+ scopes: options.scopes,
1477
+ expiresInDays: options.expiresInDays,
1478
+ ipAllowlist: options.ipAllowlist
1479
+ });
1480
+ return result.data;
1481
+ }
1482
+ /**
1483
+ * Get details of a specific key
1484
+ *
1485
+ * @example
1486
+ * ```ts
1487
+ * const key = await client.keys.get("key_abc123");
1488
+ * console.log(key.scopes);
1489
+ * console.log(key.lastUsedAt);
1490
+ * ```
1491
+ */
1492
+ async get(keyId) {
1493
+ const result = await get(this.ctx, `/v1/keys/${keyId}`);
1494
+ return result.data;
1495
+ }
1496
+ /**
1497
+ * Get usage statistics for a key
1498
+ *
1499
+ * @example
1500
+ * ```ts
1501
+ * const stats = await client.keys.getStats("key_abc123", { days: 30 });
1502
+ * console.log(stats.stats.totalRequests);
1503
+ * console.log(stats.stats.byEndpoint);
1504
+ * ```
1505
+ */
1506
+ async getStats(keyId, options = {}) {
1507
+ const result = await get(
1508
+ this.ctx,
1509
+ `/v1/keys/${keyId}/stats`,
1510
+ {
1511
+ days: options.days
1512
+ }
1513
+ );
1514
+ return result.data;
1515
+ }
1516
+ /**
1517
+ * Rotate an API key (revoke old, create new)
1518
+ *
1519
+ * @example
1520
+ * ```ts
1521
+ * const rotated = await client.keys.rotate("key_abc123", {
1522
+ * newName: "Production Key v2"
1523
+ * });
1524
+ *
1525
+ * // Old key is now invalid
1526
+ * console.log(rotated.oldKeyRevoked); // true
1527
+ *
1528
+ * // Save new key immediately!
1529
+ * console.log(rotated.newKey.key);
1530
+ * ```
1531
+ */
1532
+ async rotate(keyId, options = {}) {
1533
+ const result = await post(
1534
+ this.ctx,
1535
+ `/v1/keys/${keyId}/rotate`,
1536
+ {
1537
+ newName: options.newName
1538
+ }
1539
+ );
1540
+ return result.data;
1541
+ }
1542
+ /**
1543
+ * Revoke (delete) an API key
1544
+ *
1545
+ * @example
1546
+ * ```ts
1547
+ * // Simple revoke
1548
+ * await client.keys.revoke("key_abc123");
1549
+ *
1550
+ * // With reason
1551
+ * await client.keys.revoke("key_abc123", {
1552
+ * reason: "Key compromised"
1553
+ * });
1554
+ * ```
1555
+ */
1556
+ async revoke(keyId, options = {}) {
1557
+ const result = await del(
1558
+ this.ctx,
1559
+ `/v1/keys/${keyId}`,
1560
+ options.reason ? { reason: options.reason } : void 0
1561
+ );
1562
+ return result.data;
1563
+ }
1564
+ /**
1565
+ * Revoke all API keys (except the current one)
1566
+ *
1567
+ * @example
1568
+ * ```ts
1569
+ * // Use with caution!
1570
+ * const result = await client.keys.revokeAll();
1571
+ * console.log(`Revoked ${result.count} keys`);
1572
+ * ```
1573
+ */
1574
+ async revokeAll() {
1575
+ const result = await del(
1576
+ this.ctx,
1577
+ "/v1/keys/all"
1578
+ );
1579
+ return result.data;
1580
+ }
1581
+ /**
1582
+ * Get active keys only
1583
+ *
1584
+ * @example
1585
+ * ```ts
1586
+ * const activeKeys = await client.keys.getActive();
1587
+ * ```
1588
+ */
1589
+ async getActive() {
1590
+ const all = await this.list({ includeRevoked: false });
1591
+ return all.keys.filter((k) => k.isActive);
1592
+ }
1593
+ /**
1594
+ * Check if a key is valid (active and not expired)
1595
+ *
1596
+ * @example
1597
+ * ```ts
1598
+ * const isValid = await client.keys.isValid("key_abc123");
1599
+ * ```
1600
+ */
1601
+ async isValid(keyId) {
1602
+ try {
1603
+ const key = await this.get(keyId);
1604
+ if (!key.isActive) return false;
1605
+ if (key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date()) return false;
1606
+ return true;
1607
+ } catch {
1608
+ return false;
1609
+ }
1610
+ }
1611
+ };
1612
+
1613
+ // src/resources/scrape.ts
1614
+ var ScrapeResource = class {
1615
+ constructor(ctx) {
1616
+ this.ctx = ctx;
1617
+ }
1618
+ /**
1619
+ * Create a new scrape job
1620
+ *
1621
+ * @example
1622
+ * ```ts
1623
+ * const job = await client.scrape.create({
1624
+ * url: "https://example.com/product",
1625
+ * extractDeal: true,
1626
+ * screenshot: { enabled: true, format: "webp" }
1627
+ * });
1628
+ * console.log(job.jobId);
1629
+ * ```
1630
+ */
1631
+ async create(options) {
1632
+ const body = {
1633
+ url: options.url,
1634
+ detectSignals: options.detectSignals ?? true,
1635
+ extractWithAI: options.extractWithAI,
1636
+ extractDeal: options.extractDeal,
1637
+ extractMultipleDeals: options.extractMultipleDeals,
1638
+ maxDeals: options.maxDeals,
1639
+ useAdvancedModel: options.useAdvancedModel,
1640
+ minDealScore: options.minDealScore,
1641
+ screenshot: options.screenshot,
1642
+ excludeTags: options.excludeTags,
1643
+ excludeSelectors: options.excludeSelectors,
1644
+ onlyMainContent: options.onlyMainContent,
1645
+ headers: options.headers,
1646
+ timeout: options.timeout
1647
+ };
1648
+ const result = await post(this.ctx, "/v1/scrape", body);
1649
+ return result.data;
1650
+ }
1651
+ /**
1652
+ * Scrape a URL with deal extraction enabled
1653
+ * Convenience method for common use case
1654
+ *
1655
+ * @example
1656
+ * ```ts
1657
+ * const job = await client.scrape.extractDeal("https://example.com/sale");
1658
+ * ```
1659
+ */
1660
+ async extractDeal(url, options) {
1661
+ return this.create({
1662
+ url,
1663
+ extractDeal: true,
1664
+ ...options
1665
+ });
1666
+ }
1667
+ /**
1668
+ * Scrape a list page and extract multiple deals
1669
+ * Use for category pages, deal lists, search results
1670
+ *
1671
+ * @example
1672
+ * ```ts
1673
+ * const job = await client.scrape.extractDeals(
1674
+ * "https://amazon.fr/deals",
1675
+ * { maxDeals: 30, useAdvancedModel: true }
1676
+ * );
1677
+ * ```
1678
+ */
1679
+ async extractDeals(url, options) {
1680
+ return this.create({
1681
+ url,
1682
+ extractMultipleDeals: true,
1683
+ ...options
1684
+ });
1685
+ }
1686
+ /**
1687
+ * Scrape a URL with screenshot capture
1688
+ * Convenience method for screenshot capture
1689
+ *
1690
+ * @example
1691
+ * ```ts
1692
+ * const job = await client.scrape.withScreenshot("https://example.com", {
1693
+ * format: "webp",
1694
+ * fullPage: true
1695
+ * });
1696
+ * ```
1697
+ */
1698
+ async withScreenshot(url, screenshotOptions, options) {
1699
+ return this.create({
1700
+ url,
1701
+ screenshot: {
1702
+ enabled: true,
1703
+ ...screenshotOptions
1704
+ },
1705
+ ...options
1706
+ });
1707
+ }
1708
+ // ============================================
1709
+ // BATCH SCRAPING METHODS
1710
+ // ============================================
1711
+ /**
1712
+ * Create a batch scrape job for multiple URLs
1713
+ * Scrapes 1-100 URLs in a single request
1714
+ *
1715
+ * @example
1716
+ * ```ts
1717
+ * const batch = await client.scrape.batch({
1718
+ * urls: [
1719
+ * { url: "https://shop1.com/product1" },
1720
+ * { url: "https://shop2.com/deal", extractDeal: true }
1721
+ * ],
1722
+ * defaults: { detectSignals: true }
1723
+ * });
1724
+ * console.log(batch.batchId, batch.results);
1725
+ * ```
1726
+ */
1727
+ async batch(options) {
1728
+ const body = {
1729
+ urls: options.urls,
1730
+ defaults: options.defaults,
1731
+ webhookUrl: options.webhookUrl,
1732
+ priority: options.priority,
1733
+ delayMs: options.delay
1734
+ };
1735
+ const result = await post(
1736
+ this.ctx,
1737
+ "/v1/scrape/batch",
1738
+ body
1739
+ );
1740
+ return result.data;
1741
+ }
1742
+ /**
1743
+ * Get status of a batch scrape operation
1744
+ *
1745
+ * @example
1746
+ * ```ts
1747
+ * const status = await client.scrape.getBatchStatus(batchId);
1748
+ * console.log(`Completed: ${status.completed}/${status.total}`);
1749
+ * ```
1750
+ */
1751
+ async getBatchStatus(batchId) {
1752
+ const result = await get(
1753
+ this.ctx,
1754
+ `/v1/scrape/batch/${batchId}`
1755
+ );
1756
+ return result.data;
1757
+ }
1758
+ /**
1759
+ * Batch scrape with deal extraction enabled for all URLs
1760
+ * Convenience method for deal-focused batch scraping
1761
+ *
1762
+ * @example
1763
+ * ```ts
1764
+ * const batch = await client.scrape.batchForDeals([
1765
+ * "https://shop1.com/sale",
1766
+ * "https://shop2.com/deals"
1767
+ * ]);
1768
+ * ```
1769
+ */
1770
+ async batchForDeals(urls, options) {
1771
+ return this.batch({
1772
+ urls: urls.map((url) => ({ url })),
1773
+ defaults: {
1774
+ extractDeal: true,
1775
+ detectSignals: true,
1776
+ ...options?.defaults
1777
+ },
1778
+ ...options
1779
+ });
1780
+ }
1781
+ };
1782
+
1783
+ // src/resources/search.ts
1784
+ var SearchResource = class {
1785
+ constructor(ctx) {
1786
+ this.ctx = ctx;
1787
+ }
1788
+ /**
1789
+ * Create a new search job
1790
+ *
1791
+ * @example
1792
+ * ```ts
1793
+ * const result = await client.search.create({
1794
+ * query: "laptop deals black friday",
1795
+ * maxResults: 20,
1796
+ * useDealScoring: true
1797
+ * });
1798
+ * ```
1799
+ */
1800
+ async create(options) {
1801
+ const body = {
1802
+ query: options.query,
1803
+ limit: options.maxResults,
1804
+ scrapeResults: options.autoScrape,
1805
+ maxScrapeResults: options.autoScrapeLimit,
1806
+ useAiOptimization: options.useAiOptimization,
1807
+ aiProvider: options.aiProvider,
1808
+ aiModel: options.aiModel,
1809
+ useDealScoring: options.useDealScoring,
1810
+ skipCache: options.skipCache,
1811
+ filters: options.filters,
1812
+ webhook: options.webhook
1813
+ };
1814
+ const result = await post(this.ctx, "/v1/search", body);
1815
+ return result.data;
1816
+ }
1817
+ /**
1818
+ * Search with AI query optimization
1819
+ * Convenience method for AI-enhanced searches
1820
+ *
1821
+ * @example
1822
+ * ```ts
1823
+ * const result = await client.search.withAI("iphone discount", {
1824
+ * aiProvider: "openai",
1825
+ * aiModel: "gpt-4o-mini"
1826
+ * });
1827
+ * ```
1828
+ */
1829
+ async withAI(query, options) {
1830
+ return this.create({
1831
+ query,
1832
+ useAiOptimization: true,
1833
+ ...options
1834
+ });
1835
+ }
1836
+ /**
1837
+ * Search with deal scoring enabled
1838
+ * Scores each result for deal relevance (0-100)
1839
+ *
1840
+ * @example
1841
+ * ```ts
1842
+ * const result = await client.search.forDeals("gaming laptop");
1843
+ * ```
1844
+ */
1845
+ async forDeals(query, options) {
1846
+ return this.create({
1847
+ query,
1848
+ useDealScoring: true,
1849
+ ...options
1850
+ });
1851
+ }
1852
+ /**
1853
+ * Search and auto-scrape top results
1854
+ * Creates scrape jobs for the best matching URLs
1855
+ *
1856
+ * @example
1857
+ * ```ts
1858
+ * const result = await client.search.andScrape("promo codes", {
1859
+ * autoScrapeLimit: 5
1860
+ * });
1861
+ * console.log(result.data.scrapedJobIds);
1862
+ * ```
1863
+ */
1864
+ async andScrape(query, options) {
1865
+ return this.create({
1866
+ query,
1867
+ autoScrape: true,
1868
+ ...options
1869
+ });
1870
+ }
1871
+ /**
1872
+ * Check search API status
1873
+ * Returns availability and features info
1874
+ *
1875
+ * @example
1876
+ * ```ts
1877
+ * const status = await client.search.getStatus();
1878
+ * if (status.available) {
1879
+ * console.log(`Provider: ${status.provider}`);
1880
+ * }
1881
+ * ```
1882
+ */
1883
+ async getStatus() {
1884
+ const result = await get(this.ctx, "/v1/search/status");
1885
+ return result.data;
1886
+ }
1887
+ };
1888
+
1889
+ // src/resources/status.ts
1890
+ var StatusResource = class {
1891
+ constructor(ctx) {
1892
+ this.ctx = ctx;
1893
+ }
1894
+ /**
1895
+ * Get the status of a job
1896
+ *
1897
+ * @example
1898
+ * ```ts
1899
+ * const status = await client.status.get("job_abc123");
1900
+ * console.log(status.status); // "completed"
1901
+ * console.log(status.result);
1902
+ * ```
1903
+ */
1904
+ async get(jobId) {
1905
+ const result = await get(
1906
+ this.ctx,
1907
+ `/v1/status/${jobId}`
1908
+ );
1909
+ return result.data;
1910
+ }
1911
+ /**
1912
+ * Get deals found by a job
1913
+ *
1914
+ * @example
1915
+ * ```ts
1916
+ * const deals = await client.status.getDeals("job_abc123", {
1917
+ * minScore: 70,
1918
+ * limit: 20
1919
+ * });
1920
+ * console.log(deals.deals);
1921
+ * ```
1922
+ */
1923
+ async getDeals(jobId, options) {
1924
+ const result = await get(
1925
+ this.ctx,
1926
+ `/v1/status/${jobId}/deals`,
1927
+ {
1928
+ minScore: options?.minScore,
1929
+ limit: options?.limit
1930
+ }
1931
+ );
1932
+ return result.data;
1933
+ }
1934
+ /**
1935
+ * Resume a paused or failed job from checkpoint
1936
+ *
1937
+ * @example
1938
+ * ```ts
1939
+ * const resumed = await client.status.resume("job_abc123");
1940
+ * console.log(resumed.newJobId);
1941
+ * ```
1942
+ */
1943
+ async resume(jobId) {
1944
+ const result = await post(
1945
+ this.ctx,
1946
+ `/v1/status/${jobId}/resume`
1947
+ );
1948
+ return result.data;
1949
+ }
1950
+ /**
1951
+ * Get detailed metrics for a job
1952
+ *
1953
+ * @example
1954
+ * ```ts
1955
+ * const metrics = await client.status.getMetrics("job_abc123");
1956
+ * console.log(metrics.metrics.successRate);
1957
+ * ```
1958
+ */
1959
+ async getMetrics(jobId) {
1960
+ const result = await get(
1961
+ this.ctx,
1962
+ `/v1/status/${jobId}/metrics`
1963
+ );
1964
+ return result.data;
1965
+ }
1966
+ /**
1967
+ * Cancel a pending or active job
1968
+ *
1969
+ * @example
1970
+ * ```ts
1971
+ * const cancelled = await client.status.cancel("job_abc123");
1972
+ * console.log(cancelled.success);
1973
+ * ```
1974
+ */
1975
+ async cancel(jobId) {
1976
+ const result = await del(
1977
+ this.ctx,
1978
+ `/v1/status/${jobId}`
1979
+ );
1980
+ return result.data;
1981
+ }
1982
+ /**
1983
+ * Check if a job is complete
1984
+ * Convenience method for polling
1985
+ *
1986
+ * @example
1987
+ * ```ts
1988
+ * const isComplete = await client.status.isComplete("job_abc123");
1989
+ * ```
1990
+ */
1991
+ async isComplete(jobId) {
1992
+ const status = await this.get(jobId);
1993
+ return status.status === "completed" || status.status === "failed";
1994
+ }
1995
+ /**
1996
+ * Check if a job succeeded
1997
+ * Convenience method for result checking
1998
+ *
1999
+ * @example
2000
+ * ```ts
2001
+ * const succeeded = await client.status.succeeded("job_abc123");
2002
+ * ```
2003
+ */
2004
+ async succeeded(jobId) {
2005
+ const status = await this.get(jobId);
2006
+ return status.status === "completed";
2007
+ }
2008
+ /**
2009
+ * Get the result of a completed job
2010
+ * Throws if job is not complete
2011
+ *
2012
+ * @example
2013
+ * ```ts
2014
+ * const result = await client.status.getResult("job_abc123");
2015
+ * ```
2016
+ */
2017
+ async getResult(jobId) {
2018
+ const status = await this.get(jobId);
2019
+ if (status.status === "failed") {
2020
+ throw new Error(`Job failed: ${status.error || "Unknown error"}`);
2021
+ }
2022
+ if (status.status !== "completed") {
2023
+ throw new Error(`Job not complete. Status: ${status.status}`);
2024
+ }
2025
+ return status.result;
2026
+ }
2027
+ };
2028
+
2029
+ // src/resources/webhooks.ts
2030
+ var WebhooksResource = class {
2031
+ constructor(ctx) {
2032
+ this.ctx = ctx;
2033
+ }
2034
+ /**
2035
+ * Create a new webhook
2036
+ *
2037
+ * @example
2038
+ * ```ts
2039
+ * const webhook = await client.webhooks.create({
2040
+ * event: "deal.found",
2041
+ * url: "https://my-server.com/webhooks/deals",
2042
+ * secret: "my-webhook-secret",
2043
+ * minDealScore: 70
2044
+ * });
2045
+ * console.log(webhook.webhookId);
2046
+ * ```
2047
+ */
2048
+ async create(options) {
2049
+ const result = await post(this.ctx, "/v1/webhooks", {
2050
+ event: options.event,
2051
+ url: options.url,
2052
+ secret: options.secret,
2053
+ minDealScore: options.minDealScore,
2054
+ categories: options.categories,
2055
+ active: options.active
2056
+ });
2057
+ return result.data;
2058
+ }
2059
+ /**
2060
+ * List all webhooks
2061
+ *
2062
+ * @example
2063
+ * ```ts
2064
+ * const webhooks = await client.webhooks.list();
2065
+ * webhooks.webhooks.forEach(w => {
2066
+ * console.log(w.event, w.url, w.active);
2067
+ * });
2068
+ * ```
2069
+ */
2070
+ async list() {
2071
+ const result = await get(this.ctx, "/v1/webhooks");
2072
+ return result.data;
2073
+ }
2074
+ /**
2075
+ * Get a webhook by ID
2076
+ *
2077
+ * @example
2078
+ * ```ts
2079
+ * const webhook = await client.webhooks.get("webhook_abc123");
2080
+ * console.log(webhook.event);
2081
+ * ```
2082
+ */
2083
+ async get(webhookId) {
2084
+ const result = await get(
2085
+ this.ctx,
2086
+ `/v1/webhooks/${webhookId}`
2087
+ );
2088
+ return result.data;
2089
+ }
2090
+ /**
2091
+ * Update a webhook
2092
+ *
2093
+ * @example
2094
+ * ```ts
2095
+ * const updated = await client.webhooks.update("webhook_abc123", {
2096
+ * minDealScore: 80,
2097
+ * active: false
2098
+ * });
2099
+ * ```
2100
+ */
2101
+ async update(webhookId, options) {
2102
+ const result = await patch(
2103
+ this.ctx,
2104
+ `/v1/webhooks/${webhookId}`,
2105
+ {
2106
+ url: options.url,
2107
+ secret: options.secret,
2108
+ minDealScore: options.minDealScore,
2109
+ categories: options.categories,
2110
+ active: options.active
2111
+ }
2112
+ );
2113
+ return result.data;
2114
+ }
2115
+ /**
2116
+ * Delete a webhook
2117
+ *
2118
+ * @example
2119
+ * ```ts
2120
+ * await client.webhooks.delete("webhook_abc123");
2121
+ * ```
2122
+ */
2123
+ async delete(webhookId) {
2124
+ const result = await del(
2125
+ this.ctx,
2126
+ `/v1/webhooks/${webhookId}`
2127
+ );
2128
+ return result.data;
2129
+ }
2130
+ /**
2131
+ * Test a webhook by sending a test payload
2132
+ *
2133
+ * @example
2134
+ * ```ts
2135
+ * const result = await client.webhooks.test("webhook_abc123");
2136
+ * if (result.delivered) {
2137
+ * console.log(`Delivered in ${result.responseTime}ms`);
2138
+ * } else {
2139
+ * console.log(`Failed: ${result.error}`);
2140
+ * }
2141
+ * ```
2142
+ */
2143
+ async test(webhookId) {
2144
+ const result = await post(
2145
+ this.ctx,
2146
+ `/v1/webhooks/${webhookId}/test`
2147
+ );
2148
+ return result.data;
2149
+ }
2150
+ /**
2151
+ * Enable a webhook
2152
+ * Convenience method for activating a webhook
2153
+ *
2154
+ * @example
2155
+ * ```ts
2156
+ * await client.webhooks.enable("webhook_abc123");
2157
+ * ```
2158
+ */
2159
+ async enable(webhookId) {
2160
+ return this.update(webhookId, { active: true });
2161
+ }
2162
+ /**
2163
+ * Disable a webhook
2164
+ * Convenience method for deactivating a webhook
2165
+ *
2166
+ * @example
2167
+ * ```ts
2168
+ * await client.webhooks.disable("webhook_abc123");
2169
+ * ```
2170
+ */
2171
+ async disable(webhookId) {
2172
+ return this.update(webhookId, { active: false });
2173
+ }
2174
+ /**
2175
+ * Get active webhooks only
2176
+ *
2177
+ * @example
2178
+ * ```ts
2179
+ * const active = await client.webhooks.getActive();
2180
+ * ```
2181
+ */
2182
+ async getActive() {
2183
+ const all = await this.list();
2184
+ return all.webhooks.filter((w) => w.active);
2185
+ }
2186
+ /**
2187
+ * Get webhooks by event type
2188
+ *
2189
+ * @example
2190
+ * ```ts
2191
+ * const dealWebhooks = await client.webhooks.getByEvent("deal.found");
2192
+ * ```
2193
+ */
2194
+ async getByEvent(event) {
2195
+ const all = await this.list();
2196
+ return all.webhooks.filter((w) => w.event === event);
2197
+ }
2198
+ };
2199
+
2200
+ // src/client.ts
2201
+ var DealCrawl = class {
2202
+ /** Internal request context */
2203
+ ctx;
2204
+ // ============================================
2205
+ // RESOURCES
2206
+ // ============================================
2207
+ /**
2208
+ * Scrape resource - Single page and batch scraping
2209
+ *
2210
+ * @example
2211
+ * ```ts
2212
+ * // Single page
2213
+ * const job = await client.scrape.create({
2214
+ * url: "https://example.com",
2215
+ * extractDeal: true
2216
+ * });
2217
+ *
2218
+ * // Batch scraping
2219
+ * const batch = await client.scrape.batch({
2220
+ * urls: [{ url: "https://shop1.com" }, { url: "https://shop2.com" }]
2221
+ * });
2222
+ * ```
2223
+ */
2224
+ scrape;
2225
+ /**
2226
+ * Search resource - Web search with AI optimization
2227
+ *
2228
+ * @example
2229
+ * ```ts
2230
+ * const result = await client.search.create({
2231
+ * query: "laptop deals",
2232
+ * useDealScoring: true
2233
+ * });
2234
+ *
2235
+ * // With AI optimization
2236
+ * const result = await client.search.withAI("iphone discount");
2237
+ * ```
2238
+ */
2239
+ search;
2240
+ /**
2241
+ * Crawl resource - Website crawling
2242
+ *
2243
+ * @example
2244
+ * ```ts
2245
+ * const job = await client.crawl.create({
2246
+ * url: "https://shop.example.com",
2247
+ * maxPages: 100
2248
+ * });
2249
+ *
2250
+ * // With template
2251
+ * const job = await client.crawl.withTemplate("ecommerce", {
2252
+ * url: "https://shop.example.com"
2253
+ * });
2254
+ * ```
2255
+ */
2256
+ crawl;
2257
+ /**
2258
+ * Extract resource - LLM-based structured data extraction
2259
+ *
2260
+ * @example
2261
+ * ```ts
2262
+ * const job = await client.extract.create({
2263
+ * url: "https://example.com/product",
2264
+ * schema: { type: "object", properties: {...} }
2265
+ * });
2266
+ * ```
2267
+ */
2268
+ extract;
2269
+ /**
2270
+ * Dork resource - Google Dork searches
2271
+ *
2272
+ * @example
2273
+ * ```ts
2274
+ * const job = await client.dork.create({
2275
+ * query: "discount coupon",
2276
+ * site: "amazon.com"
2277
+ * });
2278
+ * ```
2279
+ */
2280
+ dork;
2281
+ /**
2282
+ * Status resource - Job status management
2283
+ *
2284
+ * @example
2285
+ * ```ts
2286
+ * const status = await client.status.get(jobId);
2287
+ * const deals = await client.status.getDeals(jobId);
2288
+ * await client.status.cancel(jobId);
2289
+ * ```
2290
+ */
2291
+ status;
2292
+ /**
2293
+ * Data resource - Jobs and deals data access
2294
+ *
2295
+ * @example
2296
+ * ```ts
2297
+ * const jobs = await client.data.listJobs();
2298
+ * const deals = await client.data.listDeals({ minScore: 70 });
2299
+ * const stats = await client.data.getStats();
2300
+ * ```
2301
+ */
2302
+ data;
2303
+ /**
2304
+ * Webhooks resource - Webhook management
2305
+ *
2306
+ * @example
2307
+ * ```ts
2308
+ * await client.webhooks.create({
2309
+ * event: "deal.found",
2310
+ * url: "https://..."
2311
+ * });
2312
+ * ```
2313
+ */
2314
+ webhooks;
2315
+ /**
2316
+ * Keys resource - API key management
2317
+ *
2318
+ * @example
2319
+ * ```ts
2320
+ * const newKey = await client.keys.create({
2321
+ * name: "Production",
2322
+ * scopes: ["scrape", "status"]
2323
+ * });
2324
+ * ```
2325
+ */
2326
+ keys;
2327
+ /**
2328
+ * Account resource - Account info and preferences
2329
+ *
2330
+ * @example
2331
+ * ```ts
2332
+ * const account = await client.account.get();
2333
+ * await client.account.updatePreferences({
2334
+ * minDealScore: 70
2335
+ * });
2336
+ * ```
2337
+ */
2338
+ account;
2339
+ // ============================================
2340
+ // CONSTRUCTOR
2341
+ // ============================================
2342
+ /**
2343
+ * Create a new DealCrawl client
2344
+ *
2345
+ * @param config - Client configuration
2346
+ *
2347
+ * @example
2348
+ * ```ts
2349
+ * // Minimal config
2350
+ * const client = new DealCrawl({ apiKey: "sk_xxx" });
2351
+ *
2352
+ * // Full config
2353
+ * const client = new DealCrawl({
2354
+ * apiKey: "sk_xxx",
2355
+ * baseUrl: "https://api.dealcrawl.dev",
2356
+ * timeout: 30000,
2357
+ * maxRetries: 3,
2358
+ * retryDelay: 1000,
2359
+ * onRateLimit: (info) => console.log("Rate limited!", info)
2360
+ * });
2361
+ * ```
2362
+ */
2363
+ constructor(config) {
2364
+ if (!config.apiKey || !config.apiKey.trim()) {
2365
+ throw new Error("API key is required");
2366
+ }
2367
+ this.ctx = {
2368
+ apiKey: config.apiKey,
2369
+ baseUrl: config.baseUrl ?? DEFAULT_CONFIG.baseUrl,
2370
+ timeout: config.timeout ?? DEFAULT_CONFIG.timeout,
2371
+ maxRetries: config.maxRetries ?? DEFAULT_CONFIG.maxRetries,
2372
+ retryDelay: config.retryDelay ?? DEFAULT_CONFIG.retryDelay,
2373
+ onRateLimit: config.onRateLimit
2374
+ };
2375
+ this.scrape = new ScrapeResource(this.ctx);
2376
+ this.search = new SearchResource(this.ctx);
2377
+ this.crawl = new CrawlResource(this.ctx);
2378
+ this.extract = new ExtractResource(this.ctx);
2379
+ this.dork = new DorkResource(this.ctx);
2380
+ this.status = new StatusResource(this.ctx);
2381
+ this.data = new DataResource(this.ctx);
2382
+ this.webhooks = new WebhooksResource(this.ctx);
2383
+ this.keys = new KeysResource(this.ctx);
2384
+ this.account = new AccountResource(this.ctx);
2385
+ }
2386
+ // ============================================
2387
+ // POLLING METHODS
2388
+ // ============================================
2389
+ /**
2390
+ * Wait for a job to complete
2391
+ * Polls the status endpoint until the job finishes
2392
+ *
2393
+ * @example
2394
+ * ```ts
2395
+ * const job = await client.scrape.create({ url: "..." });
2396
+ *
2397
+ * // Simple wait
2398
+ * const result = await client.waitForResult(job.jobId);
2399
+ *
2400
+ * // With options
2401
+ * const result = await client.waitForResult(job.jobId, {
2402
+ * pollInterval: 1000,
2403
+ * timeout: 60000,
2404
+ * onProgress: (status) => console.log(status.progress)
2405
+ * });
2406
+ * ```
2407
+ */
2408
+ async waitForResult(jobId, options) {
2409
+ return waitForResult(this.ctx, jobId, options);
2410
+ }
2411
+ /**
2412
+ * Wait for multiple jobs to complete
2413
+ * Returns when all jobs are done
2414
+ *
2415
+ * @example
2416
+ * ```ts
2417
+ * const jobs = await Promise.all([
2418
+ * client.scrape.create({ url: "url1" }),
2419
+ * client.scrape.create({ url: "url2" }),
2420
+ * ]);
2421
+ *
2422
+ * const results = await client.waitForAll(jobs.map(j => j.jobId));
2423
+ * ```
2424
+ */
2425
+ async waitForAll(jobIds, options) {
2426
+ return waitForAll(this.ctx, jobIds, options);
2427
+ }
2428
+ /**
2429
+ * Wait for any job to complete
2430
+ * Returns as soon as one job finishes
2431
+ *
2432
+ * @example
2433
+ * ```ts
2434
+ * const jobIds = ["job1", "job2", "job3"];
2435
+ * const firstResult = await client.waitForAny(jobIds);
2436
+ * ```
2437
+ */
2438
+ async waitForAny(jobIds, options) {
2439
+ return waitForAny(this.ctx, jobIds, options);
2440
+ }
2441
+ // ============================================
2442
+ // CONVENIENCE METHODS
2443
+ // ============================================
2444
+ /**
2445
+ * Scrape a URL and wait for result
2446
+ * Combines create and waitForResult
2447
+ *
2448
+ * @example
2449
+ * ```ts
2450
+ * const result = await client.scrapeAndWait({
2451
+ * url: "https://example.com",
2452
+ * extractDeal: true
2453
+ * });
2454
+ * ```
2455
+ */
2456
+ async scrapeAndWait(options, waitOptions) {
2457
+ const job = await this.scrape.create(options);
2458
+ return this.waitForResult(job.jobId, waitOptions);
2459
+ }
2460
+ /**
2461
+ * Crawl a URL and wait for result
2462
+ * Combines create and waitForResult
2463
+ *
2464
+ * @example
2465
+ * ```ts
2466
+ * const result = await client.crawlAndWait({
2467
+ * url: "https://shop.example.com",
2468
+ * maxPages: 50
2469
+ * });
2470
+ * ```
2471
+ */
2472
+ async crawlAndWait(options, waitOptions) {
2473
+ const job = await this.crawl.create(options);
2474
+ return this.waitForResult(job.jobId, waitOptions);
2475
+ }
2476
+ /**
2477
+ * Extract data and wait for result
2478
+ * Combines create and waitForResult
2479
+ *
2480
+ * @example
2481
+ * ```ts
2482
+ * const result = await client.extractAndWait({
2483
+ * url: "https://example.com/product",
2484
+ * schema: { type: "object", properties: {...} }
2485
+ * });
2486
+ * ```
2487
+ */
2488
+ async extractAndWait(options, waitOptions) {
2489
+ const job = await this.extract.create(options);
2490
+ return this.waitForResult(job.jobId, waitOptions);
2491
+ }
2492
+ /**
2493
+ * Search and return results directly
2494
+ * Note: Search is synchronous, no waiting needed
2495
+ *
2496
+ * @example
2497
+ * ```ts
2498
+ * const result = await client.searchAndWait({
2499
+ * query: "gaming laptop deals",
2500
+ * useDealScoring: true
2501
+ * });
2502
+ * console.log(result.data.results);
2503
+ * ```
2504
+ */
2505
+ async searchAndWait(options) {
2506
+ return this.search.create(options);
2507
+ }
2508
+ };
2509
+
2510
+ exports.AccountResource = AccountResource;
2511
+ exports.CrawlResource = CrawlResource;
2512
+ exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
2513
+ exports.DataResource = DataResource;
2514
+ exports.DealCrawl = DealCrawl;
2515
+ exports.DealCrawlError = DealCrawlError;
2516
+ exports.DorkResource = DorkResource;
2517
+ exports.ERROR_CODES = ERROR_CODES;
2518
+ exports.ExtractResource = ExtractResource;
2519
+ exports.KeysResource = KeysResource;
2520
+ exports.ScrapeResource = ScrapeResource;
2521
+ exports.SearchResource = SearchResource;
2522
+ exports.StatusResource = StatusResource;
2523
+ exports.WebhooksResource = WebhooksResource;
2524
+ exports.default = DealCrawl;
2525
+ exports.pollUntil = pollUntil;
2526
+ exports.waitForAll = waitForAll;
2527
+ exports.waitForAny = waitForAny;
2528
+ exports.waitForResult = waitForResult;
2529
+ //# sourceMappingURL=index.js.map
65
2530
  //# sourceMappingURL=index.js.map