@dealcrawl/sdk 2.1.0 → 2.1.3

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