scrapebadger 0.1.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1414 @@
1
+ // src/internal/exceptions.ts
2
+ var ScrapeBadgerError = class _ScrapeBadgerError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "ScrapeBadgerError";
6
+ Object.setPrototypeOf(this, _ScrapeBadgerError.prototype);
7
+ }
8
+ };
9
+ var AuthenticationError = class _AuthenticationError extends ScrapeBadgerError {
10
+ constructor(message = "Authentication failed. Check your API key.") {
11
+ super(message);
12
+ this.name = "AuthenticationError";
13
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
14
+ }
15
+ };
16
+ var RateLimitError = class _RateLimitError extends ScrapeBadgerError {
17
+ /** Unix timestamp when the rate limit resets */
18
+ retryAfter;
19
+ /** Maximum requests per minute for this tier */
20
+ limit;
21
+ /** Remaining requests in the current window */
22
+ remaining;
23
+ constructor(message = "Rate limit exceeded.", options) {
24
+ super(message);
25
+ this.name = "RateLimitError";
26
+ this.retryAfter = options?.retryAfter;
27
+ this.limit = options?.limit;
28
+ this.remaining = options?.remaining;
29
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
30
+ }
31
+ };
32
+ var NotFoundError = class _NotFoundError extends ScrapeBadgerError {
33
+ /** The resource type that was not found */
34
+ resourceType;
35
+ /** The resource ID that was not found */
36
+ resourceId;
37
+ constructor(message = "Resource not found.", resourceType, resourceId) {
38
+ super(message);
39
+ this.name = "NotFoundError";
40
+ this.resourceType = resourceType;
41
+ this.resourceId = resourceId;
42
+ Object.setPrototypeOf(this, _NotFoundError.prototype);
43
+ }
44
+ };
45
+ var ValidationError = class _ValidationError extends ScrapeBadgerError {
46
+ /** Validation errors by field */
47
+ errors;
48
+ constructor(message = "Validation error.", errors) {
49
+ super(message);
50
+ this.name = "ValidationError";
51
+ this.errors = errors;
52
+ Object.setPrototypeOf(this, _ValidationError.prototype);
53
+ }
54
+ };
55
+ var ServerError = class _ServerError extends ScrapeBadgerError {
56
+ /** HTTP status code */
57
+ statusCode;
58
+ constructor(message = "Internal server error.", statusCode = 500) {
59
+ super(message);
60
+ this.name = "ServerError";
61
+ this.statusCode = statusCode;
62
+ Object.setPrototypeOf(this, _ServerError.prototype);
63
+ }
64
+ };
65
+ var TimeoutError = class _TimeoutError extends ScrapeBadgerError {
66
+ /** Timeout duration in milliseconds */
67
+ timeout;
68
+ constructor(message = "Request timed out.", timeout) {
69
+ super(message);
70
+ this.name = "TimeoutError";
71
+ this.timeout = timeout;
72
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
73
+ }
74
+ };
75
+ var InsufficientCreditsError = class _InsufficientCreditsError extends ScrapeBadgerError {
76
+ /** Current credit balance */
77
+ creditsBalance;
78
+ constructor(message = "Insufficient credits.", creditsBalance) {
79
+ super(message);
80
+ this.name = "InsufficientCreditsError";
81
+ this.creditsBalance = creditsBalance;
82
+ Object.setPrototypeOf(this, _InsufficientCreditsError.prototype);
83
+ }
84
+ };
85
+ var AccountRestrictedError = class _AccountRestrictedError extends ScrapeBadgerError {
86
+ /** Reason for the restriction */
87
+ reason;
88
+ constructor(message = "Account restricted.", reason) {
89
+ super(message);
90
+ this.name = "AccountRestrictedError";
91
+ this.reason = reason;
92
+ Object.setPrototypeOf(this, _AccountRestrictedError.prototype);
93
+ }
94
+ };
95
+
96
+ // src/internal/client.ts
97
+ var BaseClient = class {
98
+ config;
99
+ constructor(config) {
100
+ this.config = config;
101
+ }
102
+ /**
103
+ * Make an HTTP request to the API.
104
+ */
105
+ async request(path, options = {}) {
106
+ const { method = "GET", params, body, headers = {} } = options;
107
+ const url = new URL(path, this.config.baseUrl);
108
+ if (params) {
109
+ for (const [key, value] of Object.entries(params)) {
110
+ if (value !== void 0) {
111
+ url.searchParams.set(key, String(value));
112
+ }
113
+ }
114
+ }
115
+ const requestHeaders = {
116
+ "Content-Type": "application/json",
117
+ Accept: "application/json",
118
+ "X-API-Key": this.config.apiKey,
119
+ "User-Agent": "scrapebadger-node/0.1.0",
120
+ ...headers
121
+ };
122
+ const fetchOptions = {
123
+ method,
124
+ headers: requestHeaders
125
+ };
126
+ if (body && method !== "GET") {
127
+ fetchOptions.body = JSON.stringify(body);
128
+ }
129
+ return this.executeWithRetry(url.toString(), fetchOptions);
130
+ }
131
+ /**
132
+ * Execute request with exponential backoff retry logic.
133
+ */
134
+ async executeWithRetry(url, options) {
135
+ let lastError;
136
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
137
+ try {
138
+ const response = await this.fetchWithTimeout(url, options);
139
+ return await this.handleResponse(response);
140
+ } catch (error) {
141
+ lastError = error;
142
+ if (error instanceof ScrapeBadgerError && !(error instanceof RateLimitError)) {
143
+ if (error instanceof AuthenticationError || error instanceof NotFoundError || error instanceof ValidationError || error instanceof InsufficientCreditsError || error instanceof AccountRestrictedError) {
144
+ throw error;
145
+ }
146
+ }
147
+ if (attempt === this.config.maxRetries) {
148
+ break;
149
+ }
150
+ const delay = this.config.retryDelay * Math.pow(2, attempt);
151
+ if (error instanceof RateLimitError && error.retryAfter) {
152
+ const retryDelay = (error.retryAfter - Date.now() / 1e3) * 1e3;
153
+ if (retryDelay > 0 && retryDelay < 6e4) {
154
+ await this.sleep(retryDelay);
155
+ continue;
156
+ }
157
+ }
158
+ await this.sleep(delay);
159
+ }
160
+ }
161
+ throw lastError ?? new ScrapeBadgerError("Request failed after retries");
162
+ }
163
+ /**
164
+ * Fetch with timeout support.
165
+ */
166
+ async fetchWithTimeout(url, options) {
167
+ const controller = new AbortController();
168
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
169
+ try {
170
+ const response = await fetch(url, {
171
+ ...options,
172
+ signal: controller.signal
173
+ });
174
+ return response;
175
+ } catch (error) {
176
+ if (error instanceof Error && error.name === "AbortError") {
177
+ throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`, this.config.timeout);
178
+ }
179
+ throw error;
180
+ } finally {
181
+ clearTimeout(timeoutId);
182
+ }
183
+ }
184
+ /**
185
+ * Handle HTTP response and convert errors.
186
+ */
187
+ async handleResponse(response) {
188
+ let data;
189
+ const contentType = response.headers.get("content-type");
190
+ if (contentType?.includes("application/json")) {
191
+ data = await response.json();
192
+ } else {
193
+ const text = await response.text();
194
+ data = { detail: text };
195
+ }
196
+ if (response.ok) {
197
+ return data;
198
+ }
199
+ const errorData = data;
200
+ const message = errorData.detail ?? errorData.message ?? "Request failed";
201
+ switch (response.status) {
202
+ case 401:
203
+ throw new AuthenticationError(message);
204
+ case 402:
205
+ throw new InsufficientCreditsError(message, errorData.credits_balance);
206
+ case 403:
207
+ if (message.toLowerCase().includes("restricted")) {
208
+ throw new AccountRestrictedError(message, errorData.reason);
209
+ }
210
+ throw new AuthenticationError(message);
211
+ case 404:
212
+ throw new NotFoundError(message);
213
+ case 422:
214
+ throw new ValidationError(message, errorData.errors);
215
+ case 429:
216
+ throw new RateLimitError(message, {
217
+ retryAfter: errorData.reset_at,
218
+ limit: errorData.limit,
219
+ remaining: errorData.remaining
220
+ });
221
+ default:
222
+ if (response.status >= 500) {
223
+ throw new ServerError(message, response.status);
224
+ }
225
+ throw new ScrapeBadgerError(message);
226
+ }
227
+ }
228
+ /**
229
+ * Sleep for a given duration.
230
+ */
231
+ sleep(ms) {
232
+ return new Promise((resolve) => setTimeout(resolve, ms));
233
+ }
234
+ };
235
+
236
+ // src/internal/config.ts
237
+ var DEFAULT_BASE_URL = "https://api.scrapebadger.com";
238
+ var DEFAULT_TIMEOUT = 3e4;
239
+ var DEFAULT_MAX_RETRIES = 3;
240
+ var DEFAULT_RETRY_DELAY = 1e3;
241
+ function resolveConfig(config) {
242
+ if (!config.apiKey) {
243
+ throw new Error("API key is required");
244
+ }
245
+ return {
246
+ apiKey: config.apiKey,
247
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
248
+ timeout: config.timeout ?? DEFAULT_TIMEOUT,
249
+ maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
250
+ retryDelay: config.retryDelay ?? DEFAULT_RETRY_DELAY
251
+ };
252
+ }
253
+ function getApiKeyFromEnv() {
254
+ if (typeof process !== "undefined" && process.env) {
255
+ return process.env.SCRAPEBADGER_API_KEY;
256
+ }
257
+ return void 0;
258
+ }
259
+
260
+ // src/internal/pagination.ts
261
+ function createPaginatedResponse(data, cursor) {
262
+ return {
263
+ data,
264
+ nextCursor: cursor,
265
+ hasMore: !!cursor
266
+ };
267
+ }
268
+ async function* paginate(fetchPage, options = {}) {
269
+ const { maxItems } = options;
270
+ let cursor;
271
+ let totalYielded = 0;
272
+ do {
273
+ const response = await fetchPage(cursor);
274
+ for (const item of response.data) {
275
+ yield item;
276
+ totalYielded++;
277
+ if (maxItems !== void 0 && totalYielded >= maxItems) {
278
+ return;
279
+ }
280
+ }
281
+ cursor = response.nextCursor;
282
+ } while (cursor);
283
+ }
284
+ async function collectAll(generator) {
285
+ const items = [];
286
+ for await (const item of generator) {
287
+ items.push(item);
288
+ }
289
+ return items;
290
+ }
291
+
292
+ // src/twitter/tweets.ts
293
+ var TweetsClient = class {
294
+ client;
295
+ constructor(client) {
296
+ this.client = client;
297
+ }
298
+ /**
299
+ * Get a single tweet by ID.
300
+ *
301
+ * @param tweetId - The tweet ID to fetch.
302
+ * @returns The tweet data.
303
+ * @throws NotFoundError - If the tweet doesn't exist.
304
+ * @throws AuthenticationError - If the API key is invalid.
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * const tweet = await client.twitter.tweets.getById("1234567890");
309
+ * console.log(`@${tweet.username}: ${tweet.text}`);
310
+ * ```
311
+ */
312
+ async getById(tweetId) {
313
+ return this.client.request(`/v1/twitter/tweets/tweet/${tweetId}`);
314
+ }
315
+ /**
316
+ * Get multiple tweets by their IDs.
317
+ *
318
+ * @param tweetIds - List of tweet IDs to fetch.
319
+ * @returns Paginated response containing the tweets.
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * const tweets = await client.twitter.tweets.getByIds([
324
+ * "1234567890",
325
+ * "0987654321"
326
+ * ]);
327
+ * for (const tweet of tweets.data) {
328
+ * console.log(tweet.text);
329
+ * }
330
+ * ```
331
+ */
332
+ async getByIds(tweetIds) {
333
+ const tweetsParam = tweetIds.join(",");
334
+ const response = await this.client.request(
335
+ "/v1/twitter/tweets/",
336
+ { params: { tweets: tweetsParam } }
337
+ );
338
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
339
+ }
340
+ /**
341
+ * Get replies to a tweet.
342
+ *
343
+ * @param tweetId - The tweet ID to get replies for.
344
+ * @param options - Pagination options.
345
+ * @returns Paginated response containing reply tweets.
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const replies = await client.twitter.tweets.getReplies("1234567890");
350
+ * for (const reply of replies.data) {
351
+ * console.log(`@${reply.username}: ${reply.text}`);
352
+ * }
353
+ *
354
+ * // Get next page
355
+ * if (replies.hasMore) {
356
+ * const more = await client.twitter.tweets.getReplies("1234567890", {
357
+ * cursor: replies.nextCursor
358
+ * });
359
+ * }
360
+ * ```
361
+ */
362
+ async getReplies(tweetId, options = {}) {
363
+ const response = await this.client.request(
364
+ `/v1/twitter/tweets/tweet/${tweetId}/replies`,
365
+ { params: { cursor: options.cursor } }
366
+ );
367
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
368
+ }
369
+ /**
370
+ * Get users who retweeted a tweet.
371
+ *
372
+ * @param tweetId - The tweet ID to get retweeters for.
373
+ * @param options - Pagination options.
374
+ * @returns Paginated response containing users who retweeted.
375
+ *
376
+ * @example
377
+ * ```typescript
378
+ * const retweeters = await client.twitter.tweets.getRetweeters("1234567890");
379
+ * for (const user of retweeters.data) {
380
+ * console.log(`@${user.username} retweeted`);
381
+ * }
382
+ * ```
383
+ */
384
+ async getRetweeters(tweetId, options = {}) {
385
+ const response = await this.client.request(
386
+ `/v1/twitter/tweets/tweet/${tweetId}/retweeters`,
387
+ { params: { cursor: options.cursor } }
388
+ );
389
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
390
+ }
391
+ /**
392
+ * Get users who liked/favorited a tweet.
393
+ *
394
+ * @param tweetId - The tweet ID to get favoriters for.
395
+ * @param options - Pagination options with optional count.
396
+ * @returns Paginated response containing users who liked.
397
+ *
398
+ * @example
399
+ * ```typescript
400
+ * const likers = await client.twitter.tweets.getFavoriters("1234567890");
401
+ * console.log(`${likers.data.length} users liked this tweet`);
402
+ * ```
403
+ */
404
+ async getFavoriters(tweetId, options = {}) {
405
+ const response = await this.client.request(
406
+ `/v1/twitter/tweets/tweet/${tweetId}/favoriters`,
407
+ { params: { count: options.count ?? 40, cursor: options.cursor } }
408
+ );
409
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
410
+ }
411
+ /**
412
+ * Get tweets similar to a given tweet.
413
+ *
414
+ * @param tweetId - The tweet ID to find similar tweets for.
415
+ * @returns Paginated response containing similar tweets.
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * const similar = await client.twitter.tweets.getSimilar("1234567890");
420
+ * for (const tweet of similar.data) {
421
+ * console.log(`Similar: ${tweet.text.slice(0, 100)}...`);
422
+ * }
423
+ * ```
424
+ */
425
+ async getSimilar(tweetId) {
426
+ const response = await this.client.request(
427
+ `/v1/twitter/tweets/tweet/${tweetId}/similar`
428
+ );
429
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
430
+ }
431
+ /**
432
+ * Search for tweets.
433
+ *
434
+ * @param query - Search query string. Supports Twitter advanced search operators.
435
+ * @param options - Search options including query type and pagination.
436
+ * @returns Paginated response containing matching tweets.
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * // Basic search
441
+ * const results = await client.twitter.tweets.search("python programming");
442
+ *
443
+ * // Latest tweets only
444
+ * const latest = await client.twitter.tweets.search("python", {
445
+ * queryType: "Latest"
446
+ * });
447
+ *
448
+ * // Advanced search operators
449
+ * const fromUser = await client.twitter.tweets.search("from:elonmusk lang:en");
450
+ * ```
451
+ */
452
+ async search(query, options = {}) {
453
+ const response = await this.client.request(
454
+ "/v1/twitter/tweets/advanced_search",
455
+ {
456
+ params: {
457
+ query,
458
+ query_type: options.queryType ?? "Top",
459
+ cursor: options.cursor
460
+ }
461
+ }
462
+ );
463
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
464
+ }
465
+ /**
466
+ * Iterate through all search results with automatic pagination.
467
+ *
468
+ * This is a convenience method that automatically handles pagination,
469
+ * yielding tweets one at a time.
470
+ *
471
+ * @param query - Search query string.
472
+ * @param options - Search and iteration options.
473
+ * @yields Tweet objects matching the search query.
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // Get up to 1000 tweets
478
+ * for await (const tweet of client.twitter.tweets.searchAll("python", {
479
+ * maxItems: 1000
480
+ * })) {
481
+ * console.log(tweet.text);
482
+ * }
483
+ *
484
+ * // Collect into an array
485
+ * import { collectAll } from "scrapebadger";
486
+ * const tweets = await collectAll(
487
+ * client.twitter.tweets.searchAll("python", { maxItems: 100 })
488
+ * );
489
+ * ```
490
+ */
491
+ async *searchAll(query, options = {}) {
492
+ const fetchPage = async (cursor) => {
493
+ return this.search(query, { ...options, cursor });
494
+ };
495
+ yield* paginate(fetchPage, options);
496
+ }
497
+ /**
498
+ * Get tweets from a user's timeline.
499
+ *
500
+ * @param username - Twitter username (without @).
501
+ * @param options - Pagination options.
502
+ * @returns Paginated response containing the user's tweets.
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * const tweets = await client.twitter.tweets.getUserTweets("elonmusk");
507
+ * for (const tweet of tweets.data) {
508
+ * console.log(`${tweet.created_at}: ${tweet.text.slice(0, 100)}...`);
509
+ * }
510
+ * ```
511
+ */
512
+ async getUserTweets(username, options = {}) {
513
+ const response = await this.client.request(
514
+ `/v1/twitter/users/${username}/latest_tweets`,
515
+ { params: { cursor: options.cursor } }
516
+ );
517
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
518
+ }
519
+ /**
520
+ * Iterate through all tweets from a user with automatic pagination.
521
+ *
522
+ * @param username - Twitter username (without @).
523
+ * @param options - Iteration options.
524
+ * @yields Tweet objects from the user's timeline.
525
+ *
526
+ * @example
527
+ * ```typescript
528
+ * for await (const tweet of client.twitter.tweets.getUserTweetsAll("elonmusk", {
529
+ * maxItems: 500
530
+ * })) {
531
+ * console.log(tweet.text);
532
+ * }
533
+ * ```
534
+ */
535
+ async *getUserTweetsAll(username, options = {}) {
536
+ const fetchPage = async (cursor) => {
537
+ return this.getUserTweets(username, { ...options, cursor });
538
+ };
539
+ yield* paginate(fetchPage, options);
540
+ }
541
+ };
542
+
543
+ // src/twitter/users.ts
544
+ var UsersClient = class {
545
+ client;
546
+ constructor(client) {
547
+ this.client = client;
548
+ }
549
+ /**
550
+ * Get a user by their numeric ID.
551
+ *
552
+ * @param userId - The user's numeric ID.
553
+ * @returns The user profile.
554
+ * @throws NotFoundError - If the user doesn't exist.
555
+ *
556
+ * @example
557
+ * ```typescript
558
+ * const user = await client.twitter.users.getById("44196397");
559
+ * console.log(`@${user.username}`);
560
+ * ```
561
+ */
562
+ async getById(userId) {
563
+ return this.client.request(`/v1/twitter/users/${userId}/by_id`);
564
+ }
565
+ /**
566
+ * Get a user by their username.
567
+ *
568
+ * @param username - The user's username (without @).
569
+ * @returns The user profile.
570
+ * @throws NotFoundError - If the user doesn't exist.
571
+ *
572
+ * @example
573
+ * ```typescript
574
+ * const user = await client.twitter.users.getByUsername("elonmusk");
575
+ * console.log(`${user.name} has ${user.followers_count.toLocaleString()} followers`);
576
+ * ```
577
+ */
578
+ async getByUsername(username) {
579
+ return this.client.request(`/v1/twitter/users/${username}/by_username`);
580
+ }
581
+ /**
582
+ * Get extended "About" information for a user.
583
+ *
584
+ * Returns additional metadata including account location,
585
+ * username change history, and verification details.
586
+ *
587
+ * @param username - The user's username (without @).
588
+ * @returns Extended user information.
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * const about = await client.twitter.users.getAbout("elonmusk");
593
+ * console.log(`Account based in: ${about.account_based_in}`);
594
+ * console.log(`Username changes: ${about.username_changes}`);
595
+ * ```
596
+ */
597
+ async getAbout(username) {
598
+ return this.client.request(`/v1/twitter/users/${username}/about`);
599
+ }
600
+ /**
601
+ * Get a user's followers.
602
+ *
603
+ * @param username - The user's username (without @).
604
+ * @param options - Pagination options.
605
+ * @returns Paginated response containing follower users.
606
+ *
607
+ * @example
608
+ * ```typescript
609
+ * const followers = await client.twitter.users.getFollowers("elonmusk");
610
+ * for (const user of followers.data) {
611
+ * console.log(`@${user.username}`);
612
+ * }
613
+ *
614
+ * // Get next page
615
+ * if (followers.hasMore) {
616
+ * const more = await client.twitter.users.getFollowers("elonmusk", {
617
+ * cursor: followers.nextCursor
618
+ * });
619
+ * }
620
+ * ```
621
+ */
622
+ async getFollowers(username, options = {}) {
623
+ const response = await this.client.request(
624
+ `/v1/twitter/users/${username}/followers`,
625
+ { params: { cursor: options.cursor } }
626
+ );
627
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
628
+ }
629
+ /**
630
+ * Iterate through all followers with automatic pagination.
631
+ *
632
+ * @param username - The user's username (without @).
633
+ * @param options - Iteration options.
634
+ * @yields User objects for each follower.
635
+ *
636
+ * @example
637
+ * ```typescript
638
+ * for await (const follower of client.twitter.users.getFollowersAll("elonmusk", {
639
+ * maxItems: 1000
640
+ * })) {
641
+ * console.log(follower.username);
642
+ * }
643
+ * ```
644
+ */
645
+ async *getFollowersAll(username, options = {}) {
646
+ const fetchPage = async (cursor) => {
647
+ return this.getFollowers(username, { ...options, cursor });
648
+ };
649
+ yield* paginate(fetchPage, options);
650
+ }
651
+ /**
652
+ * Get users that a user is following.
653
+ *
654
+ * @param username - The user's username (without @).
655
+ * @param options - Pagination options.
656
+ * @returns Paginated response containing followed users.
657
+ *
658
+ * @example
659
+ * ```typescript
660
+ * const following = await client.twitter.users.getFollowing("elonmusk");
661
+ * for (const user of following.data) {
662
+ * console.log(`Follows @${user.username}`);
663
+ * }
664
+ * ```
665
+ */
666
+ async getFollowing(username, options = {}) {
667
+ const response = await this.client.request(
668
+ `/v1/twitter/users/${username}/followings`,
669
+ { params: { cursor: options.cursor } }
670
+ );
671
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
672
+ }
673
+ /**
674
+ * Iterate through all following with automatic pagination.
675
+ *
676
+ * @param username - The user's username (without @).
677
+ * @param options - Iteration options.
678
+ * @yields User objects for each followed account.
679
+ */
680
+ async *getFollowingAll(username, options = {}) {
681
+ const fetchPage = async (cursor) => {
682
+ return this.getFollowing(username, { ...options, cursor });
683
+ };
684
+ yield* paginate(fetchPage, options);
685
+ }
686
+ /**
687
+ * Get a user's most recent followers.
688
+ *
689
+ * @param username - The user's username (without @).
690
+ * @param options - Pagination options with optional count.
691
+ * @returns Paginated response containing recent followers.
692
+ */
693
+ async getLatestFollowers(username, options = {}) {
694
+ const response = await this.client.request(
695
+ `/v1/twitter/users/${username}/latest_followers`,
696
+ { params: { count: options.count ?? 200, cursor: options.cursor } }
697
+ );
698
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
699
+ }
700
+ /**
701
+ * Get accounts a user most recently followed.
702
+ *
703
+ * @param username - The user's username (without @).
704
+ * @param options - Pagination options with optional count.
705
+ * @returns Paginated response containing recently followed users.
706
+ */
707
+ async getLatestFollowing(username, options = {}) {
708
+ const response = await this.client.request(
709
+ `/v1/twitter/users/${username}/latest_following`,
710
+ { params: { count: options.count ?? 200, cursor: options.cursor } }
711
+ );
712
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
713
+ }
714
+ /**
715
+ * Get follower IDs for a user.
716
+ *
717
+ * More efficient than getFollowers when you only need IDs.
718
+ *
719
+ * @param username - The user's username (without @).
720
+ * @param options - Pagination options with optional count.
721
+ * @returns UserIds containing list of follower IDs.
722
+ *
723
+ * @example
724
+ * ```typescript
725
+ * const ids = await client.twitter.users.getFollowerIds("elonmusk");
726
+ * console.log(`Found ${ids.ids.length.toLocaleString()} follower IDs`);
727
+ * ```
728
+ */
729
+ async getFollowerIds(username, options = {}) {
730
+ const response = await this.client.request(
731
+ `/v1/twitter/users/${username}/follower_ids`,
732
+ { params: { count: options.count ?? 5e3, cursor: options.cursor } }
733
+ );
734
+ return {
735
+ ids: response.data?.ids ?? [],
736
+ next_cursor: response.data?.next_cursor
737
+ };
738
+ }
739
+ /**
740
+ * Get following IDs for a user.
741
+ *
742
+ * More efficient than getFollowing when you only need IDs.
743
+ *
744
+ * @param username - The user's username (without @).
745
+ * @param options - Pagination options with optional count.
746
+ * @returns UserIds containing list of following IDs.
747
+ */
748
+ async getFollowingIds(username, options = {}) {
749
+ const response = await this.client.request(
750
+ `/v1/twitter/users/${username}/following_ids`,
751
+ { params: { count: options.count ?? 5e3, cursor: options.cursor } }
752
+ );
753
+ return {
754
+ ids: response.data?.ids ?? [],
755
+ next_cursor: response.data?.next_cursor
756
+ };
757
+ }
758
+ /**
759
+ * Get verified followers for a user.
760
+ *
761
+ * @param userId - The user's numeric ID.
762
+ * @param options - Pagination options with optional count.
763
+ * @returns Paginated response containing verified followers.
764
+ */
765
+ async getVerifiedFollowers(userId, options = {}) {
766
+ const response = await this.client.request(
767
+ `/v1/twitter/users/${userId}/verified_followers`,
768
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
769
+ );
770
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
771
+ }
772
+ /**
773
+ * Get followers that the authenticated user also follows.
774
+ *
775
+ * @param userId - The user's numeric ID.
776
+ * @param options - Pagination options with optional count.
777
+ * @returns Paginated response containing mutual connections.
778
+ */
779
+ async getFollowersYouKnow(userId, options = {}) {
780
+ const response = await this.client.request(
781
+ `/v1/twitter/users/${userId}/followers_you_know`,
782
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
783
+ );
784
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
785
+ }
786
+ /**
787
+ * Get premium accounts that a user subscribes to.
788
+ *
789
+ * @param userId - The user's numeric ID.
790
+ * @param options - Pagination options with optional count.
791
+ * @returns Paginated response containing subscribed accounts.
792
+ */
793
+ async getSubscriptions(userId, options = {}) {
794
+ const response = await this.client.request(
795
+ `/v1/twitter/users/${userId}/subscriptions`,
796
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
797
+ );
798
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
799
+ }
800
+ /**
801
+ * Get a user's highlighted tweets.
802
+ *
803
+ * @param userId - The user's numeric ID.
804
+ * @param options - Pagination options with optional count.
805
+ * @returns Paginated response containing highlighted tweets.
806
+ */
807
+ async getHighlights(userId, options = {}) {
808
+ const response = await this.client.request(
809
+ `/v1/twitter/users/${userId}/highlights`,
810
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
811
+ );
812
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
813
+ }
814
+ /**
815
+ * Search for users.
816
+ *
817
+ * @param query - Search query string.
818
+ * @param options - Pagination options.
819
+ * @returns Paginated response containing matching users.
820
+ *
821
+ * @example
822
+ * ```typescript
823
+ * const results = await client.twitter.users.search("python developer");
824
+ * for (const user of results.data) {
825
+ * console.log(`@${user.username}: ${user.description}`);
826
+ * }
827
+ * ```
828
+ */
829
+ async search(query, options = {}) {
830
+ const response = await this.client.request(
831
+ "/v1/twitter/users/search_users",
832
+ { params: { query, cursor: options.cursor } }
833
+ );
834
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
835
+ }
836
+ /**
837
+ * Iterate through all search results with automatic pagination.
838
+ *
839
+ * @param query - Search query string.
840
+ * @param options - Iteration options.
841
+ * @yields User objects matching the search query.
842
+ */
843
+ async *searchAll(query, options = {}) {
844
+ const fetchPage = async (cursor) => {
845
+ return this.search(query, { ...options, cursor });
846
+ };
847
+ yield* paginate(fetchPage, options);
848
+ }
849
+ };
850
+
851
+ // src/twitter/lists.ts
852
+ var ListsClient = class {
853
+ client;
854
+ constructor(client) {
855
+ this.client = client;
856
+ }
857
+ /**
858
+ * Get details for a specific list.
859
+ *
860
+ * @param listId - The list ID.
861
+ * @returns The list details.
862
+ * @throws NotFoundError - If the list doesn't exist.
863
+ *
864
+ * @example
865
+ * ```typescript
866
+ * const list = await client.twitter.lists.getDetail("123456");
867
+ * console.log(`${list.name} by @${list.username}`);
868
+ * console.log(`${list.member_count} members, ${list.subscriber_count} subscribers`);
869
+ * ```
870
+ */
871
+ async getDetail(listId) {
872
+ return this.client.request(`/v1/twitter/lists/${listId}/detail`);
873
+ }
874
+ /**
875
+ * Get tweets from a list's timeline.
876
+ *
877
+ * @param listId - The list ID.
878
+ * @param options - Pagination options.
879
+ * @returns Paginated response containing tweets from list members.
880
+ *
881
+ * @example
882
+ * ```typescript
883
+ * const tweets = await client.twitter.lists.getTweets("123456");
884
+ * for (const tweet of tweets.data) {
885
+ * console.log(`@${tweet.username}: ${tweet.text.slice(0, 100)}...`);
886
+ * }
887
+ * ```
888
+ */
889
+ async getTweets(listId, options = {}) {
890
+ const response = await this.client.request(
891
+ `/v1/twitter/lists/${listId}/tweets`,
892
+ { params: { cursor: options.cursor } }
893
+ );
894
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
895
+ }
896
+ /**
897
+ * Iterate through all list tweets with automatic pagination.
898
+ *
899
+ * @param listId - The list ID.
900
+ * @param options - Iteration options.
901
+ * @yields Tweet objects from the list timeline.
902
+ */
903
+ async *getTweetsAll(listId, options = {}) {
904
+ const fetchPage = async (cursor) => {
905
+ return this.getTweets(listId, { ...options, cursor });
906
+ };
907
+ yield* paginate(fetchPage, options);
908
+ }
909
+ /**
910
+ * Get members of a list.
911
+ *
912
+ * @param listId - The list ID.
913
+ * @param options - Pagination options.
914
+ * @returns Paginated response containing list members.
915
+ *
916
+ * @example
917
+ * ```typescript
918
+ * const members = await client.twitter.lists.getMembers("123456");
919
+ * for (const user of members.data) {
920
+ * console.log(`@${user.username}`);
921
+ * }
922
+ * ```
923
+ */
924
+ async getMembers(listId, options = {}) {
925
+ const response = await this.client.request(
926
+ `/v1/twitter/lists/${listId}/members`,
927
+ { params: { cursor: options.cursor } }
928
+ );
929
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
930
+ }
931
+ /**
932
+ * Iterate through all list members with automatic pagination.
933
+ *
934
+ * @param listId - The list ID.
935
+ * @param options - Iteration options.
936
+ * @yields User objects for each list member.
937
+ */
938
+ async *getMembersAll(listId, options = {}) {
939
+ const fetchPage = async (cursor) => {
940
+ return this.getMembers(listId, { ...options, cursor });
941
+ };
942
+ yield* paginate(fetchPage, options);
943
+ }
944
+ /**
945
+ * Get subscribers of a list.
946
+ *
947
+ * @param listId - The list ID.
948
+ * @param options - Pagination options with optional count.
949
+ * @returns Paginated response containing list subscribers.
950
+ */
951
+ async getSubscribers(listId, options = {}) {
952
+ const response = await this.client.request(
953
+ `/v1/twitter/lists/${listId}/subscribers`,
954
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
955
+ );
956
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
957
+ }
958
+ /**
959
+ * Search for lists.
960
+ *
961
+ * @param query - Search query string.
962
+ * @param options - Pagination options with optional count.
963
+ * @returns Paginated response containing matching lists.
964
+ *
965
+ * @example
966
+ * ```typescript
967
+ * const results = await client.twitter.lists.search("tech leaders");
968
+ * for (const list of results.data) {
969
+ * console.log(`${list.name}: ${list.member_count} members`);
970
+ * }
971
+ * ```
972
+ */
973
+ async search(query, options = {}) {
974
+ const response = await this.client.request(
975
+ "/v1/twitter/lists/search",
976
+ { params: { query, count: options.count ?? 20, cursor: options.cursor } }
977
+ );
978
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
979
+ }
980
+ /**
981
+ * Get lists owned by the authenticated user.
982
+ *
983
+ * @param options - Pagination options with optional count.
984
+ * @returns Paginated response containing the user's lists.
985
+ */
986
+ async getMyLists(options = {}) {
987
+ const response = await this.client.request(
988
+ "/v1/twitter/lists/my_lists",
989
+ { params: { count: options.count ?? 100, cursor: options.cursor } }
990
+ );
991
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
992
+ }
993
+ };
994
+
995
+ // src/twitter/communities.ts
996
+ var CommunitiesClient = class {
997
+ client;
998
+ constructor(client) {
999
+ this.client = client;
1000
+ }
1001
+ /**
1002
+ * Get details for a specific community.
1003
+ *
1004
+ * @param communityId - The community ID.
1005
+ * @returns The community details including rules and admin info.
1006
+ * @throws NotFoundError - If the community doesn't exist.
1007
+ *
1008
+ * @example
1009
+ * ```typescript
1010
+ * const community = await client.twitter.communities.getDetail("123456");
1011
+ * console.log(`${community.name}`);
1012
+ * console.log(`Members: ${community.member_count?.toLocaleString()}`);
1013
+ * console.log(`Join policy: ${community.join_policy}`);
1014
+ *
1015
+ * if (community.rules) {
1016
+ * console.log("Rules:");
1017
+ * for (const rule of community.rules) {
1018
+ * console.log(` - ${rule.name}`);
1019
+ * }
1020
+ * }
1021
+ * ```
1022
+ */
1023
+ async getDetail(communityId) {
1024
+ return this.client.request(`/v1/twitter/communities/${communityId}`);
1025
+ }
1026
+ /**
1027
+ * Get tweets from a community.
1028
+ *
1029
+ * @param communityId - The community ID.
1030
+ * @param options - Options including tweet type, count, and pagination.
1031
+ * @returns Paginated response containing community tweets.
1032
+ *
1033
+ * @example
1034
+ * ```typescript
1035
+ * // Get top tweets
1036
+ * const tweets = await client.twitter.communities.getTweets("123456");
1037
+ *
1038
+ * // Get latest tweets
1039
+ * const latest = await client.twitter.communities.getTweets("123456", {
1040
+ * tweetType: "Latest"
1041
+ * });
1042
+ * ```
1043
+ */
1044
+ async getTweets(communityId, options = {}) {
1045
+ const response = await this.client.request(
1046
+ `/v1/twitter/communities/${communityId}/tweets`,
1047
+ {
1048
+ params: {
1049
+ tweet_type: options.tweetType ?? "Top",
1050
+ count: options.count ?? 40,
1051
+ cursor: options.cursor
1052
+ }
1053
+ }
1054
+ );
1055
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
1056
+ }
1057
+ /**
1058
+ * Iterate through all community tweets with automatic pagination.
1059
+ *
1060
+ * @param communityId - The community ID.
1061
+ * @param options - Options including tweet type and iteration limits.
1062
+ * @yields Tweet objects from the community.
1063
+ */
1064
+ async *getTweetsAll(communityId, options = {}) {
1065
+ const fetchPage = async (cursor) => {
1066
+ return this.getTweets(communityId, { ...options, cursor });
1067
+ };
1068
+ yield* paginate(fetchPage, options);
1069
+ }
1070
+ /**
1071
+ * Get members of a community.
1072
+ *
1073
+ * @param communityId - The community ID.
1074
+ * @param options - Pagination options with optional count.
1075
+ * @returns Paginated response containing community members.
1076
+ *
1077
+ * @example
1078
+ * ```typescript
1079
+ * const members = await client.twitter.communities.getMembers("123456");
1080
+ * for (const member of members.data) {
1081
+ * console.log(`@${member.user.username} (${member.role})`);
1082
+ * }
1083
+ * ```
1084
+ */
1085
+ async getMembers(communityId, options = {}) {
1086
+ const response = await this.client.request(`/v1/twitter/communities/${communityId}/members`, {
1087
+ params: { count: options.count ?? 20, cursor: options.cursor }
1088
+ });
1089
+ const data = (response.data ?? []).map((item) => {
1090
+ if ("user" in item && item.user) {
1091
+ return item;
1092
+ }
1093
+ const userItem = item;
1094
+ return {
1095
+ user: item,
1096
+ role: userItem.role,
1097
+ joined_at: userItem.joined_at
1098
+ };
1099
+ });
1100
+ return createPaginatedResponse(data, response.next_cursor);
1101
+ }
1102
+ /**
1103
+ * Get moderators of a community.
1104
+ *
1105
+ * @param communityId - The community ID.
1106
+ * @param options - Pagination options with optional count.
1107
+ * @returns Paginated response containing community moderators.
1108
+ */
1109
+ async getModerators(communityId, options = {}) {
1110
+ const response = await this.client.request(`/v1/twitter/communities/${communityId}/moderators`, {
1111
+ params: { count: options.count ?? 20, cursor: options.cursor }
1112
+ });
1113
+ const data = (response.data ?? []).map((item) => {
1114
+ if ("user" in item && item.user) {
1115
+ return item;
1116
+ }
1117
+ const userItem = item;
1118
+ return {
1119
+ user: item,
1120
+ role: "moderator",
1121
+ joined_at: userItem.joined_at
1122
+ };
1123
+ });
1124
+ return createPaginatedResponse(data, response.next_cursor);
1125
+ }
1126
+ /**
1127
+ * Search for communities.
1128
+ *
1129
+ * @param query - Search query string.
1130
+ * @param options - Pagination options.
1131
+ * @returns Paginated response containing matching communities.
1132
+ *
1133
+ * @example
1134
+ * ```typescript
1135
+ * const results = await client.twitter.communities.search("python developers");
1136
+ * for (const community of results.data) {
1137
+ * console.log(`${community.name}: ${community.member_count} members`);
1138
+ * }
1139
+ * ```
1140
+ */
1141
+ async search(query, options = {}) {
1142
+ const response = await this.client.request(
1143
+ "/v1/twitter/communities/search",
1144
+ { params: { query, cursor: options.cursor } }
1145
+ );
1146
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
1147
+ }
1148
+ /**
1149
+ * Search for tweets within a community.
1150
+ *
1151
+ * @param communityId - The community ID.
1152
+ * @param query - Search query string.
1153
+ * @param options - Pagination options with optional count.
1154
+ * @returns Paginated response containing matching tweets.
1155
+ */
1156
+ async searchTweets(communityId, query, options = {}) {
1157
+ const response = await this.client.request(
1158
+ `/v1/twitter/communities/${communityId}/search_tweets`,
1159
+ { params: { query, count: options.count ?? 20, cursor: options.cursor } }
1160
+ );
1161
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
1162
+ }
1163
+ /**
1164
+ * Get the community timeline (tweets from communities you're in).
1165
+ *
1166
+ * @param options - Pagination options with optional count.
1167
+ * @returns Paginated response containing community timeline tweets.
1168
+ */
1169
+ async getTimeline(options = {}) {
1170
+ const response = await this.client.request(
1171
+ "/v1/twitter/communities/timeline",
1172
+ { params: { count: options.count ?? 20, cursor: options.cursor } }
1173
+ );
1174
+ return createPaginatedResponse(response.data ?? [], response.next_cursor);
1175
+ }
1176
+ };
1177
+
1178
+ // src/twitter/trends.ts
1179
+ var TrendsClient = class {
1180
+ client;
1181
+ constructor(client) {
1182
+ this.client = client;
1183
+ }
1184
+ /**
1185
+ * Get trending topics.
1186
+ *
1187
+ * @param options - Options for filtering trends.
1188
+ * @returns Paginated response containing trending topics.
1189
+ *
1190
+ * @example
1191
+ * ```typescript
1192
+ * // Get general trending topics
1193
+ * const trends = await client.twitter.trends.getTrends();
1194
+ *
1195
+ * // Get news trends
1196
+ * const newsTrends = await client.twitter.trends.getTrends({
1197
+ * category: "news"
1198
+ * });
1199
+ *
1200
+ * for (const trend of trends.data) {
1201
+ * const count = trend.tweet_count?.toLocaleString() || "N/A";
1202
+ * console.log(`${trend.name}: ${count} tweets`);
1203
+ * }
1204
+ * ```
1205
+ */
1206
+ async getTrends(options = {}) {
1207
+ const response = await this.client.request("/v1/twitter/trends/", {
1208
+ params: {
1209
+ category: options.category ?? "trending",
1210
+ count: options.count ?? 20
1211
+ }
1212
+ });
1213
+ return createPaginatedResponse(response.data ?? []);
1214
+ }
1215
+ /**
1216
+ * Get trends for a specific location.
1217
+ *
1218
+ * @param woeid - Where On Earth ID for the location.
1219
+ * Common WOEIDs:
1220
+ * - 1: Worldwide
1221
+ * - 23424977: United States
1222
+ * - 23424975: United Kingdom
1223
+ * - 23424856: Japan
1224
+ * - 23424829: Germany
1225
+ * @returns PlaceTrends containing location info and trends.
1226
+ * @throws NotFoundError - If the WOEID is invalid.
1227
+ *
1228
+ * @example
1229
+ * ```typescript
1230
+ * // Get US trends
1231
+ * const usTrends = await client.twitter.trends.getPlaceTrends(23424977);
1232
+ * console.log(`Trends in ${usTrends.name}:`);
1233
+ * for (const trend of usTrends.trends) {
1234
+ * console.log(` - ${trend.name}`);
1235
+ * }
1236
+ *
1237
+ * // Get worldwide trends
1238
+ * const globalTrends = await client.twitter.trends.getPlaceTrends(1);
1239
+ * ```
1240
+ */
1241
+ async getPlaceTrends(woeid) {
1242
+ return this.client.request(`/v1/twitter/trends/place/${woeid}`);
1243
+ }
1244
+ /**
1245
+ * Get all locations where trends are available.
1246
+ *
1247
+ * @returns Paginated response containing available trend locations.
1248
+ *
1249
+ * @example
1250
+ * ```typescript
1251
+ * const locations = await client.twitter.trends.getAvailableLocations();
1252
+ *
1253
+ * // Filter by country
1254
+ * const usLocations = locations.data.filter(
1255
+ * loc => loc.country_code === "US"
1256
+ * );
1257
+ * console.log(`Found ${usLocations.length} US locations`);
1258
+ *
1259
+ * // Get countries only
1260
+ * const countries = locations.data.filter(
1261
+ * loc => loc.place_type === "Country"
1262
+ * );
1263
+ * ```
1264
+ */
1265
+ async getAvailableLocations() {
1266
+ const response = await this.client.request(
1267
+ "/v1/twitter/trends/locations"
1268
+ );
1269
+ return createPaginatedResponse(response.data ?? []);
1270
+ }
1271
+ };
1272
+
1273
+ // src/twitter/geo.ts
1274
+ var GeoClient = class {
1275
+ client;
1276
+ constructor(client) {
1277
+ this.client = client;
1278
+ }
1279
+ /**
1280
+ * Get details for a specific place.
1281
+ *
1282
+ * @param placeId - The Twitter place ID.
1283
+ * @returns The place details.
1284
+ * @throws NotFoundError - If the place doesn't exist.
1285
+ *
1286
+ * @example
1287
+ * ```typescript
1288
+ * const place = await client.twitter.geo.getDetail("5a110d312052166f");
1289
+ * console.log(`${place.full_name}`);
1290
+ * console.log(`Type: ${place.place_type}`);
1291
+ * console.log(`Country: ${place.country}`);
1292
+ * ```
1293
+ */
1294
+ async getDetail(placeId) {
1295
+ return this.client.request(`/v1/twitter/geo/places/${placeId}`);
1296
+ }
1297
+ /**
1298
+ * Search for geographic places.
1299
+ *
1300
+ * At least one of lat/long, query, or ip must be provided.
1301
+ *
1302
+ * @param options - Search options.
1303
+ * @returns Paginated response containing matching places.
1304
+ * @throws ValidationError - If no search parameters are provided.
1305
+ *
1306
+ * @example
1307
+ * ```typescript
1308
+ * // Search by name
1309
+ * const places = await client.twitter.geo.search({ query: "San Francisco" });
1310
+ * for (const place of places.data) {
1311
+ * console.log(`${place.full_name} (${place.place_type})`);
1312
+ * }
1313
+ *
1314
+ * // Search by coordinates
1315
+ * const nearby = await client.twitter.geo.search({
1316
+ * lat: 37.7749,
1317
+ * long: -122.4194,
1318
+ * granularity: "city"
1319
+ * });
1320
+ *
1321
+ * // Search by IP
1322
+ * const ipPlaces = await client.twitter.geo.search({ ip: "8.8.8.8" });
1323
+ * ```
1324
+ */
1325
+ async search(options = {}) {
1326
+ const response = await this.client.request("/v1/twitter/geo/search", {
1327
+ params: {
1328
+ lat: options.lat,
1329
+ long: options.long,
1330
+ query: options.query,
1331
+ ip: options.ip,
1332
+ granularity: options.granularity,
1333
+ max_results: options.maxResults
1334
+ }
1335
+ });
1336
+ return createPaginatedResponse(response.data ?? []);
1337
+ }
1338
+ };
1339
+
1340
+ // src/twitter/client.ts
1341
+ var TwitterClient = class {
1342
+ /** Client for tweet operations */
1343
+ tweets;
1344
+ /** Client for user operations */
1345
+ users;
1346
+ /** Client for list operations */
1347
+ lists;
1348
+ /** Client for community operations */
1349
+ communities;
1350
+ /** Client for trends operations */
1351
+ trends;
1352
+ /** Client for geo/places operations */
1353
+ geo;
1354
+ /**
1355
+ * Create a new Twitter client.
1356
+ *
1357
+ * @param client - The base HTTP client for making requests.
1358
+ */
1359
+ constructor(client) {
1360
+ this.tweets = new TweetsClient(client);
1361
+ this.users = new UsersClient(client);
1362
+ this.lists = new ListsClient(client);
1363
+ this.communities = new CommunitiesClient(client);
1364
+ this.trends = new TrendsClient(client);
1365
+ this.geo = new GeoClient(client);
1366
+ }
1367
+ };
1368
+
1369
+ // src/client.ts
1370
+ var ScrapeBadger = class {
1371
+ baseClient;
1372
+ /** Twitter API client */
1373
+ twitter;
1374
+ /**
1375
+ * Create a new ScrapeBadger client.
1376
+ *
1377
+ * @param config - Configuration options. If apiKey is not provided,
1378
+ * it will be read from the SCRAPEBADGER_API_KEY environment variable.
1379
+ * @throws Error if no API key is provided or found in environment.
1380
+ *
1381
+ * @example
1382
+ * ```typescript
1383
+ * // With explicit API key
1384
+ * const client = new ScrapeBadger({ apiKey: "your-api-key" });
1385
+ *
1386
+ * // With custom options
1387
+ * const client = new ScrapeBadger({
1388
+ * apiKey: "your-api-key",
1389
+ * baseUrl: "https://custom.api.com",
1390
+ * timeout: 60000,
1391
+ * maxRetries: 5
1392
+ * });
1393
+ *
1394
+ * // Using environment variable
1395
+ * // Set SCRAPEBADGER_API_KEY=your-api-key
1396
+ * const client = new ScrapeBadger();
1397
+ * ```
1398
+ */
1399
+ constructor(config = {}) {
1400
+ const apiKey = config.apiKey ?? getApiKeyFromEnv();
1401
+ if (!apiKey) {
1402
+ throw new Error(
1403
+ "API key is required. Pass it in the config or set SCRAPEBADGER_API_KEY environment variable."
1404
+ );
1405
+ }
1406
+ const resolvedConfig = resolveConfig({ ...config, apiKey });
1407
+ this.baseClient = new BaseClient(resolvedConfig);
1408
+ this.twitter = new TwitterClient(this.baseClient);
1409
+ }
1410
+ };
1411
+
1412
+ export { AccountRestrictedError, AuthenticationError, CommunitiesClient, GeoClient, InsufficientCreditsError, ListsClient, NotFoundError, RateLimitError, ScrapeBadger, ScrapeBadgerError, ServerError, TimeoutError, TrendsClient, TweetsClient, TwitterClient, UsersClient, ValidationError, collectAll };
1413
+ //# sourceMappingURL=index.mjs.map
1414
+ //# sourceMappingURL=index.mjs.map