@beeperbot/sdk 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.js ADDED
@@ -0,0 +1,3202 @@
1
+ import { z } from 'zod';
2
+ import { randomUUID } from 'crypto';
3
+
4
+ // src/client/http.ts
5
+
6
+ // src/errors/codes.ts
7
+ var ErrorCodes = {
8
+ // Quote-related errors
9
+ QUOTE_EXPIRED: "QUOTE_EXPIRED",
10
+ QUOTE_NOT_FOUND: "QUOTE_NOT_FOUND",
11
+ QUOTE_INVALID: "QUOTE_INVALID",
12
+ // Recipient-related errors
13
+ INVALID_RECIPIENT: "INVALID_RECIPIENT",
14
+ RECIPIENT_NOT_FOUND: "RECIPIENT_NOT_FOUND",
15
+ // Filter-related errors
16
+ INVALID_FILTER: "INVALID_FILTER",
17
+ FILTER_SYNTAX_ERROR: "FILTER_SYNTAX_ERROR",
18
+ // Budget-related errors
19
+ BUDGET_TOO_LOW: "BUDGET_TOO_LOW",
20
+ BUDGET_EXCEEDED: "BUDGET_EXCEEDED",
21
+ // Payment-related errors
22
+ PAYMENT_NOT_FOUND: "PAYMENT_NOT_FOUND",
23
+ PAYMENT_INSUFFICIENT: "PAYMENT_INSUFFICIENT",
24
+ PAYMENT_FAILED: "PAYMENT_FAILED",
25
+ // Execution-related errors
26
+ EXECUTION_ALREADY_TRIGGERED: "EXECUTION_ALREADY_TRIGGERED",
27
+ EXECUTION_FAILED: "EXECUTION_FAILED",
28
+ EXECUTION_PENDING: "EXECUTION_PENDING",
29
+ // Rate limiting
30
+ RATE_LIMITED: "RATE_LIMITED",
31
+ // Authentication/Authorization
32
+ UNAUTHORIZED: "UNAUTHORIZED",
33
+ FORBIDDEN: "FORBIDDEN",
34
+ API_KEY_INVALID: "API_KEY_INVALID",
35
+ // Network errors
36
+ NETWORK_ERROR: "NETWORK_ERROR",
37
+ TIMEOUT: "TIMEOUT",
38
+ CONNECTION_REFUSED: "CONNECTION_REFUSED",
39
+ // Validation errors
40
+ VALIDATION_ERROR: "VALIDATION_ERROR",
41
+ SCHEMA_VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED",
42
+ // Server errors
43
+ INTERNAL_ERROR: "INTERNAL_ERROR",
44
+ SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
45
+ // Draft errors
46
+ DRAFT_NOT_FOUND: "DRAFT_NOT_FOUND",
47
+ DRAFT_INVALID: "DRAFT_INVALID",
48
+ // Generic
49
+ UNKNOWN_ERROR: "UNKNOWN_ERROR"
50
+ };
51
+ var RETRYABLE_ERROR_CODES = [
52
+ ErrorCodes.RATE_LIMITED,
53
+ ErrorCodes.NETWORK_ERROR,
54
+ ErrorCodes.TIMEOUT,
55
+ ErrorCodes.CONNECTION_REFUSED,
56
+ ErrorCodes.SERVICE_UNAVAILABLE,
57
+ ErrorCodes.INTERNAL_ERROR
58
+ ];
59
+ function isRetryableCode(code) {
60
+ return RETRYABLE_ERROR_CODES.includes(code);
61
+ }
62
+ var HTTP_STATUS_TO_ERROR_CODE = {
63
+ 400: ErrorCodes.VALIDATION_ERROR,
64
+ 401: ErrorCodes.UNAUTHORIZED,
65
+ 403: ErrorCodes.FORBIDDEN,
66
+ 404: ErrorCodes.UNKNOWN_ERROR,
67
+ // Specific 404s are mapped differently
68
+ 408: ErrorCodes.TIMEOUT,
69
+ 429: ErrorCodes.RATE_LIMITED,
70
+ 500: ErrorCodes.INTERNAL_ERROR,
71
+ 502: ErrorCodes.SERVICE_UNAVAILABLE,
72
+ 503: ErrorCodes.SERVICE_UNAVAILABLE,
73
+ 504: ErrorCodes.TIMEOUT
74
+ };
75
+
76
+ // src/errors/BeeperError.ts
77
+ var BeeperError = class _BeeperError extends Error {
78
+ /** Error code identifying the type of error */
79
+ code;
80
+ /** Whether this error can be retried */
81
+ retryable;
82
+ /** Additional context about the error */
83
+ context;
84
+ /** The original error that caused this error */
85
+ cause;
86
+ constructor(options) {
87
+ super(options.message);
88
+ this.name = "BeeperError";
89
+ this.code = options.code;
90
+ this.cause = options.cause;
91
+ this.context = options.context ?? {};
92
+ this.retryable = options.retryable ?? isRetryableCode(options.code);
93
+ if (Error.captureStackTrace) {
94
+ Error.captureStackTrace(this, _BeeperError);
95
+ }
96
+ }
97
+ /**
98
+ * Create a string representation of the error
99
+ */
100
+ toString() {
101
+ let str = `BeeperError [${this.code}]: ${this.message}`;
102
+ if (this.context.requestId) {
103
+ str += ` (Request ID: ${this.context.requestId})`;
104
+ }
105
+ return str;
106
+ }
107
+ /**
108
+ * Convert to a plain object for logging/serialization
109
+ */
110
+ toJSON() {
111
+ return {
112
+ name: this.name,
113
+ code: this.code,
114
+ message: this.message,
115
+ retryable: this.retryable,
116
+ context: this.context,
117
+ stack: this.stack
118
+ };
119
+ }
120
+ /**
121
+ * Create a network error
122
+ */
123
+ static networkError(message, cause) {
124
+ const options = {
125
+ code: ErrorCodes.NETWORK_ERROR,
126
+ message,
127
+ retryable: true
128
+ };
129
+ if (cause !== void 0) {
130
+ options.cause = cause;
131
+ }
132
+ return new _BeeperError(options);
133
+ }
134
+ /**
135
+ * Create a timeout error
136
+ */
137
+ static timeout(endpoint, timeoutMs) {
138
+ return new _BeeperError({
139
+ code: ErrorCodes.TIMEOUT,
140
+ message: `Request to ${endpoint} timed out after ${timeoutMs}ms`,
141
+ context: { endpoint },
142
+ retryable: true
143
+ });
144
+ }
145
+ /**
146
+ * Create an unauthorized error
147
+ */
148
+ static unauthorized(message = "Invalid or missing API key") {
149
+ return new _BeeperError({
150
+ code: ErrorCodes.UNAUTHORIZED,
151
+ message,
152
+ retryable: false
153
+ });
154
+ }
155
+ /**
156
+ * Create a rate limited error
157
+ */
158
+ static rateLimited(retryAfter) {
159
+ const context = {};
160
+ if (retryAfter !== void 0) {
161
+ context.retryAfter = retryAfter;
162
+ }
163
+ return new _BeeperError({
164
+ code: ErrorCodes.RATE_LIMITED,
165
+ message: retryAfter ? `Rate limited. Retry after ${retryAfter} seconds` : "Rate limited. Please try again later",
166
+ context,
167
+ retryable: true
168
+ });
169
+ }
170
+ /**
171
+ * Create a validation error
172
+ */
173
+ static validation(message, details) {
174
+ const context = {};
175
+ if (details !== void 0) {
176
+ context.details = details;
177
+ }
178
+ return new _BeeperError({
179
+ code: ErrorCodes.VALIDATION_ERROR,
180
+ message,
181
+ context,
182
+ retryable: false
183
+ });
184
+ }
185
+ /**
186
+ * Create a quote expired error
187
+ */
188
+ static quoteExpired(quoteId) {
189
+ return new _BeeperError({
190
+ code: ErrorCodes.QUOTE_EXPIRED,
191
+ message: `Quote ${quoteId} has expired`,
192
+ context: { details: { quoteId } },
193
+ retryable: false
194
+ });
195
+ }
196
+ /**
197
+ * Create from an HTTP response
198
+ */
199
+ static fromHttpResponse(statusCode, body, requestId) {
200
+ const rawBody = body;
201
+ const errorData = rawBody?.error ?? rawBody;
202
+ const code = errorData?.code ?? ErrorCodes.UNKNOWN_ERROR;
203
+ const message = errorData?.message ?? `HTTP ${statusCode} error`;
204
+ const context = { statusCode };
205
+ if (requestId !== void 0) {
206
+ context.requestId = requestId;
207
+ }
208
+ if (errorData?.details !== void 0) {
209
+ context.details = errorData.details;
210
+ }
211
+ return new _BeeperError({
212
+ code,
213
+ message,
214
+ context
215
+ });
216
+ }
217
+ };
218
+
219
+ // src/constants.ts
220
+ var API_BASE_URLS = {
221
+ production: "https://beep.works/api/v1/sdk",
222
+ staging: "https://staging.beep.works/api/v1/sdk",
223
+ development: "http://localhost:3000/api/v1/sdk"
224
+ };
225
+ var TIMEOUTS = {
226
+ /** Default request timeout */
227
+ DEFAULT: 3e4,
228
+ /** Timeout for quote requests */
229
+ QUOTE: 1e4,
230
+ /** Timeout for execute requests (longer due to blockchain) */
231
+ EXECUTE: 6e4,
232
+ /** Timeout for health checks */
233
+ HEALTH: 5e3
234
+ };
235
+ var ENDPOINTS = {
236
+ HEALTH: "/send/health",
237
+ QUOTES: "/send/quotes",
238
+ KEYS: "/keys"
239
+ };
240
+ var HEADERS = {
241
+ IDEMPOTENCY_KEY: "Idempotency-Key",
242
+ AUTHORIZATION: "Authorization",
243
+ REQUEST_ID: "X-Request-Id",
244
+ CONTENT_TYPE: "Content-Type"
245
+ };
246
+ var RETRY_CONFIG = {
247
+ /** Maximum number of retry attempts */
248
+ MAX_RETRIES: 3,
249
+ /** Initial delay before first retry (1 second) */
250
+ INITIAL_DELAY_MS: 1e3,
251
+ /** Maximum delay between retries (30 seconds) */
252
+ MAX_DELAY_MS: 3e4,
253
+ /** Multiplier for exponential backoff */
254
+ BACKOFF_MULTIPLIER: 2,
255
+ /** Jitter factor to add randomness (0-1) */
256
+ JITTER_FACTOR: 0.1
257
+ };
258
+ var QUOTE_EXPIRATION_SECONDS = 300;
259
+ var SDK_VERSION = "0.1.0";
260
+
261
+ // src/client/http.ts
262
+ function createHttpConfig(options) {
263
+ const baseUrl = options.baseUrl ?? API_BASE_URLS[options.environment ?? "production"];
264
+ return {
265
+ apiKey: options.apiKey,
266
+ baseUrl,
267
+ timeout: options.timeout ?? TIMEOUTS.DEFAULT,
268
+ fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
269
+ debug: options.debug ?? false
270
+ };
271
+ }
272
+ var HttpClient = class {
273
+ config;
274
+ constructor(config) {
275
+ this.config = config;
276
+ }
277
+ /**
278
+ * Check if an HTTP status code is retryable
279
+ * - 5xx server errors
280
+ * - 429 rate limit errors
281
+ * Does NOT retry 4xx errors (except 429)
282
+ */
283
+ isRetryableStatus(status) {
284
+ if (status === 429) {
285
+ return true;
286
+ }
287
+ if (status >= 500 && status < 600) {
288
+ return true;
289
+ }
290
+ return false;
291
+ }
292
+ /**
293
+ * Check if an error is retryable (network errors, timeouts)
294
+ */
295
+ isRetryableError(error) {
296
+ if (error.name === "AbortError") {
297
+ return true;
298
+ }
299
+ if (error instanceof TypeError) {
300
+ return true;
301
+ }
302
+ if (error.message.includes("fetch failed") || error.message.includes("network") || error.message.includes("ECONNREFUSED") || error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT")) {
303
+ return true;
304
+ }
305
+ return false;
306
+ }
307
+ /**
308
+ * Calculate delay with exponential backoff and jitter
309
+ */
310
+ calculateDelay(attempt, retryConfig) {
311
+ const exponentialDelay = retryConfig.initialDelayMs * Math.pow(retryConfig.backoffMultiplier, attempt);
312
+ const cappedDelay = Math.min(exponentialDelay, retryConfig.maxDelayMs);
313
+ const jitter = cappedDelay * retryConfig.jitterFactor * Math.random();
314
+ return Math.floor(cappedDelay + jitter);
315
+ }
316
+ /**
317
+ * Sleep for a given number of milliseconds
318
+ */
319
+ sleep(ms) {
320
+ return new Promise((resolve) => setTimeout(resolve, ms));
321
+ }
322
+ /**
323
+ * Get default retry configuration
324
+ */
325
+ getRetryConfig(options) {
326
+ if (options === false) {
327
+ return null;
328
+ }
329
+ return {
330
+ maxRetries: options?.maxRetries ?? RETRY_CONFIG.MAX_RETRIES,
331
+ initialDelayMs: options?.initialDelayMs ?? RETRY_CONFIG.INITIAL_DELAY_MS,
332
+ maxDelayMs: options?.maxDelayMs ?? RETRY_CONFIG.MAX_DELAY_MS,
333
+ backoffMultiplier: options?.backoffMultiplier ?? RETRY_CONFIG.BACKOFF_MULTIPLIER,
334
+ jitterFactor: options?.jitterFactor ?? RETRY_CONFIG.JITTER_FACTOR
335
+ };
336
+ }
337
+ /**
338
+ * Make an HTTP request with validation and automatic retry
339
+ */
340
+ async request(options, responseSchema) {
341
+ let url = `${this.config.baseUrl}${options.endpoint}`;
342
+ if (options.params) {
343
+ const searchParams = new URLSearchParams();
344
+ for (const [key, value] of Object.entries(options.params)) {
345
+ if (value !== void 0) {
346
+ searchParams.append(key, String(value));
347
+ }
348
+ }
349
+ const queryString = searchParams.toString();
350
+ if (queryString) {
351
+ url += `?${queryString}`;
352
+ }
353
+ }
354
+ const timeout = options.timeout ?? this.config.timeout;
355
+ const retryConfig = this.getRetryConfig(options.retry);
356
+ const startTime = Date.now();
357
+ let attempt = 0;
358
+ const maxAttempts = retryConfig ? retryConfig.maxRetries + 1 : 1;
359
+ while (attempt < maxAttempts) {
360
+ const controller = new AbortController();
361
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
362
+ const signal = options.signal ? this.mergeSignals(options.signal, controller.signal) : controller.signal;
363
+ try {
364
+ const headers = this.buildHeaders(options);
365
+ const body = options.body ? JSON.stringify(options.body) : null;
366
+ if (this.config.debug) {
367
+ console.log(`[Beeper SDK] Request:`, {
368
+ method: options.method,
369
+ url,
370
+ headers: this.sanitizeHeaders(headers),
371
+ body: options.body,
372
+ attempt: attempt + 1,
373
+ maxAttempts
374
+ });
375
+ }
376
+ const requestInit = {
377
+ method: options.method,
378
+ headers,
379
+ signal
380
+ };
381
+ if (body !== null) {
382
+ requestInit.body = body;
383
+ }
384
+ const response = await this.config.fetch(url, requestInit);
385
+ clearTimeout(timeoutId);
386
+ const requestId = response.headers.get(HEADERS.REQUEST_ID) ?? void 0;
387
+ if (this.config.debug) {
388
+ console.log(`[Beeper SDK] Response:`, {
389
+ url,
390
+ status: response.status,
391
+ statusText: response.statusText,
392
+ requestId,
393
+ attempt: attempt + 1,
394
+ timeMs: Date.now() - startTime
395
+ });
396
+ }
397
+ if (!response.ok) {
398
+ const errorBody = await this.safeParseJson(response);
399
+ if (retryConfig && this.isRetryableStatus(response.status) && attempt < retryConfig.maxRetries) {
400
+ const delay = this.calculateDelay(attempt, retryConfig);
401
+ if (this.config.debug) {
402
+ console.log(`[Beeper SDK] Retrying:`, {
403
+ url,
404
+ status: response.status,
405
+ attempt: attempt + 1,
406
+ maxRetries: retryConfig.maxRetries,
407
+ delayMs: delay
408
+ });
409
+ }
410
+ await this.sleep(delay);
411
+ attempt++;
412
+ continue;
413
+ }
414
+ throw BeeperError.fromHttpResponse(
415
+ response.status,
416
+ errorBody,
417
+ requestId
418
+ );
419
+ }
420
+ const data = await this.safeParseJson(response);
421
+ if (responseSchema) {
422
+ const result = responseSchema.safeParse(data);
423
+ if (!result.success) {
424
+ throw BeeperError.validation("Invalid response from server", {
425
+ zodErrors: result.error.errors
426
+ });
427
+ }
428
+ return {
429
+ data: result.data,
430
+ status: response.status,
431
+ headers: response.headers,
432
+ requestId,
433
+ retryCount: attempt,
434
+ totalTimeMs: Date.now() - startTime
435
+ };
436
+ }
437
+ return {
438
+ data,
439
+ status: response.status,
440
+ headers: response.headers,
441
+ requestId,
442
+ retryCount: attempt,
443
+ totalTimeMs: Date.now() - startTime
444
+ };
445
+ } catch (error) {
446
+ clearTimeout(timeoutId);
447
+ const isErrorInstance = error instanceof Error;
448
+ const shouldRetry = retryConfig && isErrorInstance && this.isRetryableError(error) && attempt < retryConfig.maxRetries;
449
+ if (shouldRetry) {
450
+ const delay = this.calculateDelay(attempt, retryConfig);
451
+ if (this.config.debug) {
452
+ console.log(`[Beeper SDK] Retrying after error:`, {
453
+ url,
454
+ error: isErrorInstance ? error.message : "Unknown error",
455
+ attempt: attempt + 1,
456
+ maxRetries: retryConfig.maxRetries,
457
+ delayMs: delay
458
+ });
459
+ }
460
+ await this.sleep(delay);
461
+ attempt++;
462
+ continue;
463
+ }
464
+ if (isErrorInstance && error.name === "AbortError") {
465
+ throw BeeperError.timeout(options.endpoint, timeout);
466
+ }
467
+ if (error instanceof TypeError) {
468
+ throw BeeperError.networkError(
469
+ `Network error: ${error.message}`,
470
+ error
471
+ );
472
+ }
473
+ if (error instanceof BeeperError) {
474
+ throw error;
475
+ }
476
+ const errorOptions = {
477
+ code: ErrorCodes.UNKNOWN_ERROR,
478
+ message: isErrorInstance ? error.message : "Unknown error"
479
+ };
480
+ if (isErrorInstance) {
481
+ errorOptions.cause = error;
482
+ }
483
+ throw new BeeperError(errorOptions);
484
+ }
485
+ }
486
+ throw BeeperError.networkError(
487
+ `Max retries (${retryConfig?.maxRetries ?? 0}) exceeded for ${options.endpoint}`
488
+ );
489
+ }
490
+ /**
491
+ * GET request helper
492
+ */
493
+ async get(endpoint, options, schema) {
494
+ return this.request({ ...options, method: "GET", endpoint }, schema);
495
+ }
496
+ /**
497
+ * POST request helper
498
+ */
499
+ async post(endpoint, body, options, schema) {
500
+ return this.request(
501
+ { ...options, method: "POST", endpoint, body },
502
+ schema
503
+ );
504
+ }
505
+ /**
506
+ * PUT request helper
507
+ */
508
+ async put(endpoint, body, options, schema) {
509
+ return this.request(
510
+ { ...options, method: "PUT", endpoint, body },
511
+ schema
512
+ );
513
+ }
514
+ /**
515
+ * PATCH request helper
516
+ */
517
+ async patch(endpoint, body, options, schema) {
518
+ return this.request(
519
+ { ...options, method: "PATCH", endpoint, body },
520
+ schema
521
+ );
522
+ }
523
+ /**
524
+ * DELETE request helper
525
+ */
526
+ async delete(endpoint, options, schema) {
527
+ return this.request({ ...options, method: "DELETE", endpoint }, schema);
528
+ }
529
+ /**
530
+ * Build request headers
531
+ */
532
+ buildHeaders(options) {
533
+ const headers = {
534
+ [HEADERS.CONTENT_TYPE]: "application/json",
535
+ [HEADERS.AUTHORIZATION]: `Bearer ${this.config.apiKey}`,
536
+ "User-Agent": `beeper-sdk/${SDK_VERSION}`,
537
+ ...options.headers
538
+ };
539
+ if (options.idempotencyKey) {
540
+ headers[HEADERS.IDEMPOTENCY_KEY] = options.idempotencyKey;
541
+ }
542
+ return headers;
543
+ }
544
+ /**
545
+ * Sanitize headers for logging (redact sensitive values)
546
+ */
547
+ sanitizeHeaders(headers) {
548
+ const sensitiveKeys = [
549
+ "authorization",
550
+ "x-api-key",
551
+ "cookie",
552
+ "set-cookie",
553
+ "x-auth-token"
554
+ ];
555
+ const sanitized = {};
556
+ for (const [key, value] of Object.entries(headers)) {
557
+ if (sensitiveKeys.includes(key.toLowerCase())) {
558
+ sanitized[key] = "[REDACTED]";
559
+ } else {
560
+ sanitized[key] = value;
561
+ }
562
+ }
563
+ return sanitized;
564
+ }
565
+ /**
566
+ * Safely parse JSON response
567
+ */
568
+ async safeParseJson(response) {
569
+ const text = await response.text();
570
+ if (!text) {
571
+ return null;
572
+ }
573
+ try {
574
+ return JSON.parse(text);
575
+ } catch {
576
+ return text;
577
+ }
578
+ }
579
+ /**
580
+ * Merge two abort signals
581
+ */
582
+ mergeSignals(signal1, signal2) {
583
+ const controller = new AbortController();
584
+ const abort = () => controller.abort();
585
+ signal1.addEventListener("abort", abort);
586
+ signal2.addEventListener("abort", abort);
587
+ if (signal1.aborted || signal2.aborted) {
588
+ controller.abort();
589
+ }
590
+ return controller.signal;
591
+ }
592
+ };
593
+ function generateIdempotencyKey() {
594
+ const timestamp = Date.now().toString(36);
595
+ const random = Math.random().toString(36).substring(2, 15);
596
+ return `${timestamp}-${random}`;
597
+ }
598
+
599
+ // src/client/filters.schema.ts
600
+ var FILTER_SCHEMA = [
601
+ {
602
+ name: "Platform & Activity",
603
+ description: "Filter by platform and user activity",
604
+ filters: [
605
+ {
606
+ name: "platform",
607
+ description: "Target users on a specific platform",
608
+ type: "string",
609
+ enum: ["all", "farcaster", "twitter"],
610
+ example: "farcaster"
611
+ },
612
+ {
613
+ name: "activeInLastDays",
614
+ description: "Users who have been active within the last N days",
615
+ type: "number",
616
+ min: 1,
617
+ max: 365,
618
+ example: 30
619
+ },
620
+ {
621
+ name: "minCastCount",
622
+ description: "Users with at least N posts/casts",
623
+ type: "number",
624
+ min: 0,
625
+ example: 10
626
+ }
627
+ ]
628
+ },
629
+ {
630
+ name: "Reputation & Quality",
631
+ description: "Filter by user reputation scores",
632
+ filters: [
633
+ {
634
+ name: "neynarScoreMin",
635
+ description: "Minimum Neynar reputation score (0-1, higher = more reputable)",
636
+ type: "number",
637
+ min: 0,
638
+ max: 1,
639
+ example: 0.5
640
+ },
641
+ {
642
+ name: "neynarScoreMax",
643
+ description: "Maximum Neynar reputation score",
644
+ type: "number",
645
+ min: 0,
646
+ max: 1,
647
+ example: 0.9
648
+ },
649
+ {
650
+ name: "quotientScoreMin",
651
+ description: "Minimum Quotient score (0-1, engagement quality)",
652
+ type: "number",
653
+ min: 0,
654
+ max: 1,
655
+ example: 0.5
656
+ },
657
+ {
658
+ name: "spamLabel",
659
+ description: "Filter by spam classification",
660
+ type: "string",
661
+ enum: ["not_spam_only", "spam_only", "all"],
662
+ example: "not_spam_only"
663
+ },
664
+ {
665
+ name: "verifiedOnly",
666
+ description: "Only include verified users",
667
+ type: "boolean",
668
+ example: true
669
+ }
670
+ ]
671
+ },
672
+ {
673
+ name: "Social Graph",
674
+ description: "Filter by follower counts and relationships",
675
+ filters: [
676
+ {
677
+ name: "minFollowers",
678
+ description: "Minimum follower count",
679
+ type: "number",
680
+ min: 0,
681
+ example: 1e3
682
+ },
683
+ {
684
+ name: "maxFollowers",
685
+ description: "Maximum follower count",
686
+ type: "number",
687
+ min: 0,
688
+ example: 1e5
689
+ },
690
+ {
691
+ name: "followersOf",
692
+ description: "Users who follow a specific FID (Farcaster ID)",
693
+ type: "number",
694
+ example: 3
695
+ },
696
+ {
697
+ name: "mutualsWith",
698
+ description: "Users who have mutual follows with a specific FID",
699
+ type: "number",
700
+ example: 3
701
+ }
702
+ ]
703
+ },
704
+ {
705
+ name: "Token Filters",
706
+ description: "Filter by token preferences or holdings. IMPORTANT: Signal tokens and token holders are different concepts.",
707
+ filters: [
708
+ {
709
+ name: "signalTokens",
710
+ description: 'Users who have SET specific tokens on their profile as interests/preferences. Does NOT require actually holding the token - it is a social signal like "I am interested in this community". Use this to find community members by affinity.',
711
+ type: "array",
712
+ items: { name: "tokenAddress", type: "string", description: "Token contract address", example: "0x..." },
713
+ example: ["0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed"]
714
+ },
715
+ {
716
+ name: "tokenHolders",
717
+ description: "Users who actually HOLD specific tokens in their wallet (verified onchain). Use this to find real token holders with actual balances. Requires wallet verification.",
718
+ type: "array",
719
+ items: {
720
+ name: "tokenConfig",
721
+ type: "object",
722
+ description: "Token holder filter config",
723
+ example: { tokenAddress: "0x...", chainId: 8453 },
724
+ properties: {
725
+ tokenAddress: { name: "tokenAddress", type: "string", description: "Token contract address", example: "0x..." },
726
+ chainId: { name: "chainId", type: "number", description: "Chain ID (1=Ethereum, 8453=Base)", example: 8453 },
727
+ minBalance: { name: "minBalance", type: "string", description: "Minimum balance in wei (e.g., 1000000000000000000 = 1 token with 18 decimals)", example: "1000000000000000000" }
728
+ }
729
+ },
730
+ example: [{ tokenAddress: "0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed", chainId: 8453 }]
731
+ },
732
+ {
733
+ name: "hasBaseWallet",
734
+ description: "Users with a wallet on Base chain",
735
+ type: "boolean",
736
+ example: true
737
+ },
738
+ {
739
+ name: "hasVerifiedWallet",
740
+ description: "Users with a verified wallet address",
741
+ type: "boolean",
742
+ example: true
743
+ }
744
+ ]
745
+ },
746
+ {
747
+ name: "Economics",
748
+ description: "Filter by attention price and battery",
749
+ filters: [
750
+ {
751
+ name: "maxAttentionPriceUsd",
752
+ description: "Maximum attention price in USD",
753
+ type: "number",
754
+ min: 0,
755
+ example: 0.5
756
+ },
757
+ {
758
+ name: "minBatteryPercentage",
759
+ description: "Minimum battery percentage (0-100)",
760
+ type: "number",
761
+ min: 0,
762
+ max: 100,
763
+ example: 50
764
+ },
765
+ {
766
+ name: "hasRechargedInLastDays",
767
+ description: "Users who recharged battery within N days",
768
+ type: "number",
769
+ min: 1,
770
+ example: 7
771
+ },
772
+ {
773
+ name: "excludePingedToday",
774
+ description: "Exclude users who were already pinged today",
775
+ type: "boolean",
776
+ example: true
777
+ }
778
+ ]
779
+ },
780
+ {
781
+ name: "Geography",
782
+ description: "Filter by location and timezone",
783
+ filters: [
784
+ {
785
+ name: "countries",
786
+ description: "ISO 3166-1 alpha-2 country codes",
787
+ type: "array",
788
+ items: { name: "countryCode", type: "string", description: "Country code", example: "US" },
789
+ example: ["US", "CA", "GB"]
790
+ },
791
+ {
792
+ name: "timezones",
793
+ description: "Filter by UTC timezone offset",
794
+ type: "array",
795
+ items: {
796
+ name: "timezone",
797
+ type: "object",
798
+ description: "Timezone configuration",
799
+ example: { offset: -5, range: 2 },
800
+ properties: {
801
+ offset: { name: "offset", type: "number", description: "UTC offset in hours", example: -5 },
802
+ range: { name: "range", type: "number", description: "Range in hours for fuzzy matching", example: 2 }
803
+ }
804
+ },
805
+ example: [{ offset: -5, range: 2 }]
806
+ }
807
+ ]
808
+ },
809
+ {
810
+ name: "User Selection",
811
+ description: "Target specific users",
812
+ filters: [
813
+ {
814
+ name: "fids",
815
+ description: "Specific Farcaster IDs to target",
816
+ type: "array",
817
+ items: { name: "fid", type: "number", description: "Farcaster ID", example: 3 },
818
+ example: [3, 1234, 5678]
819
+ },
820
+ {
821
+ name: "userIds",
822
+ description: "Specific user IDs to target",
823
+ type: "array",
824
+ items: { name: "userId", type: "string", description: "User ID", example: "abc123" },
825
+ example: ["abc123", "def456"]
826
+ }
827
+ ]
828
+ },
829
+ {
830
+ name: "Sorting",
831
+ description: "Order results",
832
+ filters: [
833
+ {
834
+ name: "orderBy",
835
+ description: "Sort order for results",
836
+ type: "string",
837
+ enum: [
838
+ "attention_price_asc",
839
+ "attention_price_desc",
840
+ "neynar_score_desc",
841
+ "followers_desc",
842
+ "followers_asc",
843
+ "recent_activity",
844
+ "battery_desc",
845
+ "random"
846
+ ],
847
+ example: "attention_price_asc"
848
+ }
849
+ ]
850
+ }
851
+ ];
852
+ function getAllFilterNames() {
853
+ return FILTER_SCHEMA.flatMap((cat) => cat.filters.map((f) => f.name));
854
+ }
855
+ function getFilterSchema(name) {
856
+ for (const category of FILTER_SCHEMA) {
857
+ const filter = category.filters.find((f) => f.name === name);
858
+ if (filter) return filter;
859
+ }
860
+ return void 0;
861
+ }
862
+ function generateFilterDocumentation() {
863
+ let doc = "# Available Filters\n\n";
864
+ for (const category of FILTER_SCHEMA) {
865
+ doc += `## ${category.name}
866
+ `;
867
+ doc += `${category.description}
868
+
869
+ `;
870
+ for (const filter of category.filters) {
871
+ doc += `### \`${filter.name}\`
872
+ `;
873
+ doc += `${filter.description}
874
+ `;
875
+ doc += `- Type: \`${filter.type}\`
876
+ `;
877
+ if (filter.enum) {
878
+ doc += `- Values: ${filter.enum.map((v) => `\`${v}\``).join(", ")}
879
+ `;
880
+ }
881
+ if (filter.min !== void 0) doc += `- Min: ${filter.min}
882
+ `;
883
+ if (filter.max !== void 0) doc += `- Max: ${filter.max}
884
+ `;
885
+ doc += `- Example: \`${JSON.stringify(filter.example)}\`
886
+
887
+ `;
888
+ }
889
+ }
890
+ return doc;
891
+ }
892
+
893
+ // src/client/AgentClient.ts
894
+ var ENDPOINTS2 = {
895
+ LOOKUP: "/agent/lookup",
896
+ INTENT: "/agent/intent",
897
+ PRICE: "/agent/price",
898
+ ESTIMATE: "/agent/estimate",
899
+ PREVIEW: "/agent/preview",
900
+ BULK_INTENT: "/agent/bulk-intent"
901
+ };
902
+ var AgentClient = class {
903
+ http;
904
+ // @ts-expect-error Reserved for future use
905
+ _debug;
906
+ constructor(config) {
907
+ if (!config.apiKey) {
908
+ throw BeeperError.validation("API key is required");
909
+ }
910
+ const httpConfig = createHttpConfig({
911
+ apiKey: config.apiKey,
912
+ ...config.environment && { environment: config.environment },
913
+ ...config.baseUrl && { baseUrl: config.baseUrl },
914
+ ...config.timeoutMs && { timeout: config.timeoutMs },
915
+ ...config.fetch && { fetch: config.fetch },
916
+ ...config.debug && { debug: config.debug }
917
+ });
918
+ this.http = new HttpClient(httpConfig);
919
+ this._debug = config.debug ?? false;
920
+ }
921
+ /**
922
+ * Look up a user by username, FID, or wallet address
923
+ *
924
+ * @param identifier - Username (e.g., "dwr.eth"), FID (e.g., 3), or address
925
+ * @returns User information including wallet address and attention price
926
+ *
927
+ * @example
928
+ * ```typescript
929
+ * const user = await agent.lookup('dwr.eth');
930
+ * console.log(user.walletAddress); // "0x1234..."
931
+ * console.log(user.attentionPriceUsd); // "0.05"
932
+ * ```
933
+ */
934
+ async lookup(identifier) {
935
+ const params = typeof identifier === "number" ? { fid: identifier } : { q: identifier };
936
+ const response = await this.http.get(
937
+ ENDPOINTS2.LOOKUP,
938
+ { params }
939
+ );
940
+ return response.data.data;
941
+ }
942
+ /**
943
+ * Get the attention price for a user
944
+ *
945
+ * @param identifier - Username, FID, or address
946
+ * @returns Current attention price information
947
+ *
948
+ * @example
949
+ * ```typescript
950
+ * const price = await agent.getPrice('dwr.eth');
951
+ * console.log(`Attention price: ${price.priceUsd}`);
952
+ * ```
953
+ */
954
+ async getPrice(identifier) {
955
+ const params = typeof identifier === "number" ? { fid: identifier } : { q: identifier };
956
+ const response = await this.http.get(
957
+ ENDPOINTS2.PRICE,
958
+ { params }
959
+ );
960
+ return response.data.data;
961
+ }
962
+ /**
963
+ * Create a payment intent
964
+ *
965
+ * This does NOT execute the payment. It returns the information
966
+ * needed for the user to execute the payment from any wallet.
967
+ *
968
+ * @param input - Payment intent parameters
969
+ * @returns Payment intent with recipient address and amount
970
+ *
971
+ * @example
972
+ * ```typescript
973
+ * const intent = await agent.createIntent({
974
+ * to: 'dwr.eth',
975
+ * amount: '$5',
976
+ * message: 'Thanks!',
977
+ * });
978
+ *
979
+ * // Tell the user:
980
+ * console.log(intent.instruction);
981
+ * // "Send 5 USDC to 0x1234... on Base"
982
+ * ```
983
+ */
984
+ async createIntent(input) {
985
+ const amountStr = input.amount.replace(/^\$/, "").trim();
986
+ const amountNum = parseFloat(amountStr);
987
+ if (isNaN(amountNum) || amountNum <= 0) {
988
+ throw BeeperError.validation("Amount must be a positive number");
989
+ }
990
+ const response = await this.http.post(
991
+ ENDPOINTS2.INTENT,
992
+ {
993
+ to: input.to,
994
+ amountUsd: amountStr,
995
+ message: input.message,
996
+ chainId: input.chainId ?? 8453,
997
+ // Default to Base
998
+ tokenSymbol: input.tokenSymbol ?? "USDC"
999
+ }
1000
+ );
1001
+ return response.data.data;
1002
+ }
1003
+ /**
1004
+ * Convenience method to create a human-readable payment instruction
1005
+ *
1006
+ * @param input - Payment intent parameters
1007
+ * @returns Human-readable instruction string
1008
+ *
1009
+ * @example
1010
+ * ```typescript
1011
+ * const instruction = await agent.getPaymentInstruction({
1012
+ * to: 'dwr.eth',
1013
+ * amount: '$5',
1014
+ * });
1015
+ * // "Send 5 USDC to 0x1a2b3c... on Base (to @dwr)"
1016
+ * ```
1017
+ */
1018
+ async getPaymentInstruction(input) {
1019
+ const intent = await this.createIntent(input);
1020
+ return intent.instruction;
1021
+ }
1022
+ /**
1023
+ * Estimate how many recipients match filters within a budget
1024
+ *
1025
+ * @param input - Filters and budget
1026
+ * @returns Estimate with recipient count, costs, and budget analysis
1027
+ *
1028
+ * @example
1029
+ * ```typescript
1030
+ * const estimate = await agent.estimate({
1031
+ * filters: {
1032
+ * platform: 'farcaster',
1033
+ * minFollowers: 1000,
1034
+ * signalTokens: ['0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed'],
1035
+ * },
1036
+ * budget: '$100',
1037
+ * });
1038
+ * console.log(`Can reach ${estimate.recipientCount} people`);
1039
+ * console.log(`Total cost: ${estimate.totalCostUsd}`);
1040
+ * ```
1041
+ */
1042
+ async estimate(input) {
1043
+ const budgetStr = input.budget.replace(/^\$/, "").trim();
1044
+ const budgetNum = parseFloat(budgetStr);
1045
+ if (isNaN(budgetNum) || budgetNum <= 0) {
1046
+ throw BeeperError.validation("Budget must be a positive number");
1047
+ }
1048
+ const response = await this.http.post(
1049
+ ENDPOINTS2.ESTIMATE,
1050
+ {
1051
+ filters: input.filters,
1052
+ budgetUsd: budgetStr,
1053
+ message: input.message
1054
+ }
1055
+ );
1056
+ return response.data.data;
1057
+ }
1058
+ /**
1059
+ * Preview sample users matching filters
1060
+ *
1061
+ * @param input - Filters and limit
1062
+ * @returns Sample users and total count
1063
+ *
1064
+ * @example
1065
+ * ```typescript
1066
+ * const preview = await agent.preview({
1067
+ * filters: { minFollowers: 1000 },
1068
+ * limit: 5,
1069
+ * });
1070
+ * console.log(`Found ${preview.totalCount} users`);
1071
+ * preview.users.forEach(u => {
1072
+ * console.log(`@${u.username} - ${u.priceUsd}`);
1073
+ * });
1074
+ * ```
1075
+ */
1076
+ async preview(input) {
1077
+ const limit = Math.min(input.limit ?? 10, 20);
1078
+ const response = await this.http.post(
1079
+ ENDPOINTS2.PREVIEW,
1080
+ {
1081
+ filters: input.filters,
1082
+ limit
1083
+ }
1084
+ );
1085
+ return response.data.data;
1086
+ }
1087
+ /**
1088
+ * Create bulk payment intents for multiple recipients
1089
+ *
1090
+ * @param input - Filters, budget, and message
1091
+ * @returns Bulk intent with all payment instructions
1092
+ *
1093
+ * @example
1094
+ * ```typescript
1095
+ * const bulk = await agent.createBulkIntent({
1096
+ * filters: { signalTokens: ['0x...'] },
1097
+ * budget: '$100',
1098
+ * message: 'GM holders!',
1099
+ * });
1100
+ * console.log(bulk.summary);
1101
+ * // "Send to 50 recipients for total 45.00 USDC on Base"
1102
+ * ```
1103
+ */
1104
+ async createBulkIntent(input) {
1105
+ const budgetStr = input.budget.replace(/^\$/, "").trim();
1106
+ const budgetNum = parseFloat(budgetStr);
1107
+ if (isNaN(budgetNum) || budgetNum <= 0) {
1108
+ throw BeeperError.validation("Budget must be a positive number");
1109
+ }
1110
+ const response = await this.http.post(
1111
+ ENDPOINTS2.BULK_INTENT,
1112
+ {
1113
+ filters: input.filters,
1114
+ budgetUsd: budgetStr,
1115
+ message: input.message,
1116
+ chainId: input.chainId ?? 8453
1117
+ }
1118
+ );
1119
+ return response.data.data;
1120
+ }
1121
+ /**
1122
+ * Get filter schema documentation for LLMs
1123
+ *
1124
+ * Returns structured information about all available filters
1125
+ * that can be used in system prompts or tool descriptions.
1126
+ *
1127
+ * @returns Filter schema categories
1128
+ *
1129
+ * @example
1130
+ * ```typescript
1131
+ * const schema = agent.describeFilters();
1132
+ * // Use in LLM prompt:
1133
+ * // "Available filters: " + JSON.stringify(schema)
1134
+ * ```
1135
+ */
1136
+ describeFilters() {
1137
+ return FILTER_SCHEMA;
1138
+ }
1139
+ /**
1140
+ * Get filter documentation as markdown
1141
+ *
1142
+ * Useful for including in LLM system prompts.
1143
+ *
1144
+ * @returns Markdown documentation of all filters
1145
+ *
1146
+ * @example
1147
+ * ```typescript
1148
+ * const docs = agent.getFilterDocumentation();
1149
+ * // Include in system prompt for LLM
1150
+ * ```
1151
+ */
1152
+ getFilterDocumentation() {
1153
+ return generateFilterDocumentation();
1154
+ }
1155
+ };
1156
+
1157
+ // src/client/auth.ts
1158
+ var API_KEY_PREFIXES = {
1159
+ LIVE: "bpk_live_",
1160
+ TEST: "bpk_test_"
1161
+ };
1162
+ function isValidApiKeyFormat(apiKey) {
1163
+ if (!apiKey || typeof apiKey !== "string") {
1164
+ return false;
1165
+ }
1166
+ const hasValidPrefix = apiKey.startsWith(API_KEY_PREFIXES.LIVE) || apiKey.startsWith(API_KEY_PREFIXES.TEST);
1167
+ if (!hasValidPrefix) {
1168
+ return false;
1169
+ }
1170
+ const minLength = API_KEY_PREFIXES.LIVE.length + 20;
1171
+ return apiKey.length >= minLength;
1172
+ }
1173
+ function getApiKeyEnvironment(apiKey) {
1174
+ if (!apiKey || typeof apiKey !== "string") {
1175
+ return null;
1176
+ }
1177
+ if (apiKey.startsWith(API_KEY_PREFIXES.LIVE)) {
1178
+ return "production";
1179
+ }
1180
+ if (apiKey.startsWith(API_KEY_PREFIXES.TEST)) {
1181
+ return "test";
1182
+ }
1183
+ return null;
1184
+ }
1185
+ function createAuthorizationHeader(apiKey) {
1186
+ return `Bearer ${apiKey}`;
1187
+ }
1188
+ function maskApiKey(apiKey) {
1189
+ if (!apiKey || apiKey.length < 12) {
1190
+ return "***";
1191
+ }
1192
+ const prefix = apiKey.startsWith(API_KEY_PREFIXES.LIVE) ? API_KEY_PREFIXES.LIVE : apiKey.startsWith(API_KEY_PREFIXES.TEST) ? API_KEY_PREFIXES.TEST : "";
1193
+ const suffix = apiKey.slice(-4);
1194
+ return `${prefix}***${suffix}`;
1195
+ }
1196
+
1197
+ // src/client/BeeperClient.ts
1198
+ var QuoteSchema = z.object({
1199
+ id: z.string(),
1200
+ status: z.enum([
1201
+ "pending",
1202
+ "deposit_confirmed",
1203
+ "executing",
1204
+ "completed",
1205
+ "expired",
1206
+ "failed"
1207
+ ]),
1208
+ recipientCount: z.number(),
1209
+ totalAmount: z.string(),
1210
+ protocolFee: z.string(),
1211
+ depositAmount: z.string(),
1212
+ depositAddress: z.string(),
1213
+ depositChainId: z.number(),
1214
+ depositTokenAddress: z.string(),
1215
+ expiresAt: z.string(),
1216
+ input: z.object({
1217
+ filter: z.record(z.unknown()),
1218
+ tokenAddress: z.string(),
1219
+ chainId: z.number(),
1220
+ amountPerRecipient: z.string(),
1221
+ budgetCap: z.string(),
1222
+ memo: z.string().optional(),
1223
+ metadata: z.record(z.unknown()).optional()
1224
+ }),
1225
+ createdAt: z.string(),
1226
+ updatedAt: z.string()
1227
+ });
1228
+ var AttentionQuoteSchema = z.object({
1229
+ id: z.string(),
1230
+ status: z.enum([
1231
+ "pending",
1232
+ "deposit_confirmed",
1233
+ "executing",
1234
+ "completed",
1235
+ "expired",
1236
+ "failed"
1237
+ ]),
1238
+ recipientCount: z.number(),
1239
+ expiresAt: z.string(),
1240
+ input: z.record(z.unknown()),
1241
+ createdAt: z.string(),
1242
+ updatedAt: z.string()
1243
+ }).passthrough();
1244
+ var ConfirmResultSchema = z.object({
1245
+ quoteId: z.string(),
1246
+ status: z.enum(["confirmed", "pending_verification"]),
1247
+ detectedAmount: z.string(),
1248
+ sufficient: z.boolean(),
1249
+ blockNumber: z.number(),
1250
+ confirmedAt: z.string()
1251
+ });
1252
+ var ExecuteResultSchema = z.object({
1253
+ quoteId: z.string(),
1254
+ status: z.enum(["executing", "queued"]),
1255
+ estimatedCompletionAt: z.string(),
1256
+ batchId: z.string()
1257
+ });
1258
+ var ReceiptSchema = z.object({
1259
+ quoteId: z.string(),
1260
+ status: z.enum(["completed", "partial", "failed"]),
1261
+ successCount: z.number(),
1262
+ failureCount: z.number(),
1263
+ totalSent: z.string(),
1264
+ refundAmount: z.string(),
1265
+ refundTxHash: z.string().optional(),
1266
+ transactions: z.array(
1267
+ z.object({
1268
+ recipient: z.string(),
1269
+ amount: z.string(),
1270
+ txHash: z.string().nullable(),
1271
+ status: z.enum(["success", "failed"]),
1272
+ error: z.string().optional()
1273
+ })
1274
+ ),
1275
+ completedAt: z.string()
1276
+ });
1277
+ var HealthSchema = z.object({
1278
+ status: z.enum(["healthy", "degraded", "unhealthy"]),
1279
+ version: z.string(),
1280
+ timestamp: z.string(),
1281
+ services: z.object({
1282
+ database: z.enum(["up", "down"]),
1283
+ queue: z.enum(["up", "down"]),
1284
+ oracle: z.enum(["up", "down"])
1285
+ })
1286
+ });
1287
+ var ENDPOINTS3 = {
1288
+ QUOTES: "/api/v1/sdk/send/quotes",
1289
+ HEALTH: "/api/v1/sdk/send/health"
1290
+ };
1291
+ var BeeperClient = class {
1292
+ http;
1293
+ constructor(config) {
1294
+ this.validateConfig(config);
1295
+ const httpConfig = createHttpConfig({
1296
+ apiKey: config.apiKey,
1297
+ ...config.environment !== void 0 && { environment: config.environment },
1298
+ ...config.baseUrl !== void 0 && { baseUrl: config.baseUrl },
1299
+ ...config.timeoutMs !== void 0 && { timeout: config.timeoutMs },
1300
+ ...config.fetch !== void 0 && { fetch: config.fetch },
1301
+ ...config.debug !== void 0 && { debug: config.debug }
1302
+ });
1303
+ this.http = new HttpClient(httpConfig);
1304
+ }
1305
+ /**
1306
+ * Validates client configuration
1307
+ */
1308
+ validateConfig(config) {
1309
+ if (!config.apiKey) {
1310
+ throw BeeperError.validation("API key is required");
1311
+ }
1312
+ if (!isValidApiKeyFormat(config.apiKey)) {
1313
+ throw BeeperError.validation(
1314
+ "Invalid API key format. Key must start with bpk_live_ or bpk_test_"
1315
+ );
1316
+ }
1317
+ }
1318
+ /**
1319
+ * Creates a local draft. Does NOT make a network request.
1320
+ * @param input - The draft input parameters
1321
+ * @returns A draft object with a generated ID
1322
+ */
1323
+ createDraft(input) {
1324
+ this.validateDraftInput(input);
1325
+ return {
1326
+ id: `draft_${generateIdempotencyKey()}`,
1327
+ input,
1328
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1329
+ };
1330
+ }
1331
+ /**
1332
+ * Validates draft input parameters
1333
+ */
1334
+ validateDraftInput(input) {
1335
+ if (!input.filter || typeof input.filter !== "object") {
1336
+ throw BeeperError.validation("Filter is required and must be an object");
1337
+ }
1338
+ if (!input.tokenAddress || !/^0x[a-fA-F0-9]{40}$/.test(input.tokenAddress)) {
1339
+ throw BeeperError.validation("Invalid token address format");
1340
+ }
1341
+ if (!input.chainId || typeof input.chainId !== "number") {
1342
+ throw BeeperError.validation("Chain ID is required and must be a number");
1343
+ }
1344
+ if (!input.amountPerRecipient || !/^[0-9]+$/.test(input.amountPerRecipient)) {
1345
+ throw BeeperError.validation(
1346
+ "Amount per recipient must be a positive integer string"
1347
+ );
1348
+ }
1349
+ if (!input.budgetCap || !/^[0-9]+$/.test(input.budgetCap)) {
1350
+ throw BeeperError.validation("Budget cap must be a positive integer string");
1351
+ }
1352
+ if (input.memo !== void 0 && input.memo.length > 256) {
1353
+ throw BeeperError.validation("Memo must be 256 characters or less");
1354
+ }
1355
+ }
1356
+ /**
1357
+ * Submits draft to server and receives a priced quote
1358
+ * @param draft - The draft to quote
1359
+ * @param opts - Optional quote options
1360
+ * @returns A quote with server-computed pricing
1361
+ */
1362
+ async createQuote(draft, opts) {
1363
+ const body = {
1364
+ ...draft.input,
1365
+ ttlSeconds: opts?.ttlSeconds ?? 300
1366
+ };
1367
+ const response = await this.http.post(
1368
+ ENDPOINTS3.QUOTES,
1369
+ body,
1370
+ {},
1371
+ QuoteSchema
1372
+ );
1373
+ return response.data;
1374
+ }
1375
+ /**
1376
+ * Creates an attention marketplace quote (message-based send)
1377
+ * @param input - Attention quote input with message, recipients, and budget
1378
+ * @param opts - Optional quote options
1379
+ * @returns An attention quote with server-computed pricing
1380
+ *
1381
+ * @example
1382
+ * ```typescript
1383
+ * const quote = await client.createAttentionQuote({
1384
+ * message: "Check out our new feature!",
1385
+ * filter: FilterBuilder.and([
1386
+ * FilterBuilder.platform('farcaster'),
1387
+ * FilterBuilder.activeInLastDays(7),
1388
+ * ]).toJSON(),
1389
+ * budgetUSD: "50.00",
1390
+ * rewardType: 'guaranteed',
1391
+ * });
1392
+ * ```
1393
+ */
1394
+ async createAttentionQuote(input, opts) {
1395
+ if (!input.message || input.message.length === 0) {
1396
+ throw BeeperError.validation("Message is required");
1397
+ }
1398
+ if (input.message.length > 1e3) {
1399
+ throw BeeperError.validation("Message must be 1000 characters or less");
1400
+ }
1401
+ if (!input.recipientFids?.length && !input.filter) {
1402
+ throw BeeperError.validation("Must provide either recipientFids or filter");
1403
+ }
1404
+ if (input.recipientFids && input.recipientFids.length > 1e3) {
1405
+ throw BeeperError.validation("recipientFids cannot exceed 1000");
1406
+ }
1407
+ if (!input.budgetUSD || !/^[0-9]+\.?[0-9]*$/.test(input.budgetUSD)) {
1408
+ throw BeeperError.validation("budgetUSD must be a valid USD amount");
1409
+ }
1410
+ if (parseFloat(input.budgetUSD) <= 0) {
1411
+ throw BeeperError.validation("budgetUSD must be positive");
1412
+ }
1413
+ const body = {
1414
+ message: input.message,
1415
+ recipientFids: input.recipientFids,
1416
+ filter: input.filter,
1417
+ budgetUSD: input.budgetUSD,
1418
+ rewardType: input.rewardType ?? "guaranteed",
1419
+ memo: input.memo,
1420
+ metadata: input.metadata,
1421
+ ttlSeconds: opts?.ttlSeconds ?? 300
1422
+ };
1423
+ const response = await this.http.post(
1424
+ ENDPOINTS3.QUOTES,
1425
+ body,
1426
+ {},
1427
+ AttentionQuoteSchema
1428
+ );
1429
+ return response.data;
1430
+ }
1431
+ /**
1432
+ * Retrieves an existing quote by ID
1433
+ * @param quoteId - The quote ID
1434
+ * @returns The quote
1435
+ */
1436
+ async getQuote(quoteId) {
1437
+ const response = await this.http.get(
1438
+ `${ENDPOINTS3.QUOTES}/${quoteId}`,
1439
+ {},
1440
+ QuoteSchema
1441
+ );
1442
+ return response.data;
1443
+ }
1444
+ /**
1445
+ * Confirms that deposit has been made
1446
+ * @param quoteId - The quote ID
1447
+ * @param params - Confirmation parameters including txHash and idempotencyKey
1448
+ * @returns Confirmation result
1449
+ */
1450
+ async confirmDeposit(quoteId, params) {
1451
+ if (!params.idempotencyKey) {
1452
+ throw BeeperError.validation("Idempotency key is required for confirmDeposit");
1453
+ }
1454
+ if (!params.txHash || !/^0x[a-fA-F0-9]{64}$/.test(params.txHash)) {
1455
+ throw BeeperError.validation("Invalid transaction hash format");
1456
+ }
1457
+ const response = await this.http.post(
1458
+ `${ENDPOINTS3.QUOTES}/${quoteId}/confirm`,
1459
+ { txHash: params.txHash },
1460
+ { idempotencyKey: params.idempotencyKey },
1461
+ ConfirmResultSchema
1462
+ );
1463
+ return response.data;
1464
+ }
1465
+ /**
1466
+ * Triggers execution of the send
1467
+ * @param quoteId - The quote ID
1468
+ * @param params - Execution parameters including idempotencyKey
1469
+ * @returns Execution result
1470
+ */
1471
+ async executeSend(quoteId, params) {
1472
+ if (!params.idempotencyKey) {
1473
+ throw BeeperError.validation("Idempotency key is required for executeSend");
1474
+ }
1475
+ const response = await this.http.post(
1476
+ `${ENDPOINTS3.QUOTES}/${quoteId}/execute`,
1477
+ {},
1478
+ { idempotencyKey: params.idempotencyKey },
1479
+ ExecuteResultSchema
1480
+ );
1481
+ return response.data;
1482
+ }
1483
+ /**
1484
+ * Gets the receipt for a completed (or failed) execution
1485
+ * @param quoteId - The quote ID
1486
+ * @returns The receipt
1487
+ */
1488
+ async getReceiptByQuoteId(quoteId) {
1489
+ const response = await this.http.get(
1490
+ `${ENDPOINTS3.QUOTES}/${quoteId}/receipt`,
1491
+ {},
1492
+ ReceiptSchema
1493
+ );
1494
+ return response.data;
1495
+ }
1496
+ /**
1497
+ * Polls until execution completes, then returns receipt
1498
+ * @param quoteId - The quote ID
1499
+ * @param opts - Polling options
1500
+ * @returns The receipt when execution completes
1501
+ */
1502
+ async pollUntilCompleteByQuoteId(quoteId, opts) {
1503
+ const maxAttempts = opts?.maxAttempts ?? 60;
1504
+ const intervalMs = opts?.intervalMs ?? 2e3;
1505
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1506
+ const quote = await this.getQuote(quoteId);
1507
+ if (opts?.onPoll) {
1508
+ opts.onPoll(quote);
1509
+ }
1510
+ if (quote.status === "completed" || quote.status === "failed") {
1511
+ return this.getReceiptByQuoteId(quoteId);
1512
+ }
1513
+ if (quote.status === "expired") {
1514
+ throw new BeeperError({
1515
+ code: ErrorCodes.QUOTE_EXPIRED,
1516
+ message: `Quote ${quoteId} expired while polling`,
1517
+ retryable: false
1518
+ });
1519
+ }
1520
+ if (attempt < maxAttempts - 1) {
1521
+ await this.sleep(intervalMs);
1522
+ }
1523
+ }
1524
+ throw new BeeperError({
1525
+ code: ErrorCodes.TIMEOUT,
1526
+ message: `Polling timed out after ${maxAttempts} attempts`,
1527
+ retryable: false
1528
+ });
1529
+ }
1530
+ /**
1531
+ * Checks API health status
1532
+ * @returns Health status
1533
+ */
1534
+ async health() {
1535
+ const response = await this.http.get(
1536
+ ENDPOINTS3.HEALTH,
1537
+ {},
1538
+ HealthSchema
1539
+ );
1540
+ return response.data;
1541
+ }
1542
+ /**
1543
+ * Sleep helper for polling
1544
+ */
1545
+ sleep(ms) {
1546
+ return new Promise((resolve) => setTimeout(resolve, ms));
1547
+ }
1548
+ };
1549
+ var BeeperClient_default = BeeperClient;
1550
+ var LegacyFilterOperatorSchema = z.enum([
1551
+ "eq",
1552
+ "ne",
1553
+ "gt",
1554
+ "gte",
1555
+ "lt",
1556
+ "lte",
1557
+ "in",
1558
+ "nin",
1559
+ "contains",
1560
+ "startsWith",
1561
+ "endsWith"
1562
+ ]);
1563
+ var LegacyFilterValueSchema = z.union([
1564
+ z.string(),
1565
+ z.number(),
1566
+ z.boolean(),
1567
+ z.array(z.string()),
1568
+ z.array(z.number())
1569
+ ]);
1570
+ var LegacyFieldComparisonSchema = z.object({
1571
+ field: z.string().min(1),
1572
+ op: LegacyFilterOperatorSchema,
1573
+ value: LegacyFilterValueSchema
1574
+ });
1575
+ var LegacyRecipientFilterDSLSchema = z.lazy(
1576
+ () => z.union([
1577
+ // Match all
1578
+ z.object({ all: z.literal(true) }),
1579
+ // Logical AND
1580
+ z.object({ and: z.array(LegacyRecipientFilterDSLSchema).min(1) }),
1581
+ // Logical OR
1582
+ z.object({ or: z.array(LegacyRecipientFilterDSLSchema).min(1) }),
1583
+ // Logical NOT
1584
+ z.object({ not: LegacyRecipientFilterDSLSchema }),
1585
+ // Field comparison
1586
+ LegacyFieldComparisonSchema
1587
+ ])
1588
+ );
1589
+ var FilterOperatorSchema = LegacyFilterOperatorSchema;
1590
+ var FilterValueSchema = LegacyFilterValueSchema;
1591
+ var FieldComparisonSchema = LegacyFieldComparisonSchema;
1592
+ var PlatformFilterSchema = z.object({
1593
+ platform: z.enum(["all", "farcaster", "twitter"])
1594
+ });
1595
+ var SpecificUsersFilterSchema = z.object({
1596
+ specificIds: z.array(z.string()).optional(),
1597
+ specificFids: z.array(z.number().int()).optional(),
1598
+ specificUsernames: z.array(z.string()).optional(),
1599
+ specificUsersMode: z.enum(["exclusive", "additive"]).optional()
1600
+ });
1601
+ var ExcludeUsersFilterSchema = z.object({
1602
+ excludeFids: z.array(z.number().int()).optional(),
1603
+ excludeUsernames: z.array(z.string()).optional()
1604
+ });
1605
+ var MinFollowersFilterSchema = z.object({
1606
+ minFollowers: z.number().int().min(0)
1607
+ });
1608
+ var MaxFollowersFilterSchema = z.object({
1609
+ maxFollowers: z.number().int().min(0)
1610
+ });
1611
+ var MinFollowingFilterSchema = z.object({
1612
+ minFollowing: z.number().int().min(0)
1613
+ });
1614
+ var MaxFollowingFilterSchema = z.object({
1615
+ maxFollowing: z.number().int().min(0)
1616
+ });
1617
+ var FollowersOfFilterSchema = z.object({
1618
+ followersOf: z.number().int()
1619
+ });
1620
+ var FollowingOfFilterSchema = z.object({
1621
+ followingOf: z.number().int()
1622
+ });
1623
+ var MutualsWithFilterSchema = z.object({
1624
+ mutualsWith: z.number().int()
1625
+ });
1626
+ var SignalTokenFilterSchema = z.object({
1627
+ signalTokens: z.array(
1628
+ z.object({
1629
+ tokenAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid token address")
1630
+ })
1631
+ )
1632
+ });
1633
+ var TimezoneFilterSchema = z.object({
1634
+ timezones: z.array(
1635
+ z.object({
1636
+ offset: z.number(),
1637
+ // UTC offset in hours (e.g., -5, +9, +5.5)
1638
+ range: z.number().min(0).max(12).optional()
1639
+ // Range in hours (+/-)
1640
+ })
1641
+ )
1642
+ });
1643
+ var CountryFilterSchema = z.object({
1644
+ countries: z.array(
1645
+ z.object({
1646
+ code: z.string().length(2)
1647
+ // ISO 3166-1 alpha-2 country code
1648
+ })
1649
+ )
1650
+ });
1651
+ var RolesFilterSchema = z.object({
1652
+ roles: z.array(z.string())
1653
+ });
1654
+ var SocialFilterSchema = z.object({
1655
+ specificIds: z.array(z.string()).optional(),
1656
+ specificFids: z.array(z.number().int()).optional(),
1657
+ specificUsernames: z.array(z.string()).optional(),
1658
+ specificUsersMode: z.enum(["exclusive", "additive"]).optional(),
1659
+ excludeFids: z.array(z.number().int()).optional(),
1660
+ excludeUsernames: z.array(z.string()).optional(),
1661
+ followersOf: z.number().int().optional(),
1662
+ followingOf: z.number().int().optional(),
1663
+ mutualsWith: z.number().int().optional(),
1664
+ minFollowers: z.number().int().min(0).optional(),
1665
+ maxFollowers: z.number().int().min(0).optional(),
1666
+ minFollowing: z.number().int().min(0).optional(),
1667
+ maxFollowing: z.number().int().min(0).optional(),
1668
+ signalTokens: z.array(
1669
+ z.object({
1670
+ tokenAddress: z.string()
1671
+ })
1672
+ ).optional(),
1673
+ timezones: z.array(
1674
+ z.object({
1675
+ offset: z.number(),
1676
+ range: z.number().min(0).max(12).optional()
1677
+ })
1678
+ ).optional(),
1679
+ countries: z.array(
1680
+ z.object({
1681
+ code: z.string()
1682
+ })
1683
+ ).optional(),
1684
+ roles: z.array(z.string()).optional()
1685
+ });
1686
+ var ActiveInLastDaysFilterSchema = z.object({
1687
+ activeInLastDays: z.number().int().min(1).max(365)
1688
+ });
1689
+ var NeynarScoreMinFilterSchema = z.object({
1690
+ neynarScoreMin: z.number().min(0).max(1)
1691
+ });
1692
+ var NeynarScoreMaxFilterSchema = z.object({
1693
+ neynarScoreMax: z.number().min(0).max(1)
1694
+ });
1695
+ var QuotientScoreMinFilterSchema = z.object({
1696
+ quotientScoreMin: z.number().min(0).max(1)
1697
+ });
1698
+ var QuotientScoreMaxFilterSchema = z.object({
1699
+ quotientScoreMax: z.number().min(0).max(1)
1700
+ });
1701
+ var SpamLabelFilterSchema = z.object({
1702
+ spamLabel: z.enum(["not_spam_only", "spam_only", "all"])
1703
+ });
1704
+ var ProSubscriptionFilterSchema = z.object({
1705
+ proSubscriptionRequired: z.boolean()
1706
+ });
1707
+ var MinProTenureDaysFilterSchema = z.object({
1708
+ minProTenureDays: z.number().int().min(0)
1709
+ });
1710
+ var MinTenureDaysFilterSchema = z.object({
1711
+ minTenureDays: z.number().int().min(0)
1712
+ });
1713
+ var VerifiedOnlyFilterSchema = z.object({
1714
+ verifiedOnly: z.boolean()
1715
+ });
1716
+ var ReputationFilterSchema = z.object({
1717
+ neynarScoreMin: z.number().min(0).max(1).optional(),
1718
+ neynarScoreMax: z.number().min(0).max(1).optional(),
1719
+ quotientScoreMin: z.number().min(0).max(1).optional(),
1720
+ quotientScoreMax: z.number().min(0).max(1).optional(),
1721
+ spamLabel: z.enum(["not_spam_only", "spam_only", "all"]).optional(),
1722
+ proSubscriptionRequired: z.boolean().optional(),
1723
+ minProTenureDays: z.number().int().min(0).optional(),
1724
+ minTenureDays: z.number().int().min(0).optional(),
1725
+ verifiedOnly: z.boolean().optional()
1726
+ });
1727
+ var MaxAttentionPriceFilterSchema = z.object({
1728
+ maxAttentionPriceUsd: z.number().positive()
1729
+ });
1730
+ var MinAttentionPriceFilterSchema = z.object({
1731
+ minAttentionPriceUsd: z.number().min(0)
1732
+ });
1733
+ var MinFidFilterSchema = z.object({
1734
+ minFid: z.number().int().min(1)
1735
+ });
1736
+ var MaxFidFilterSchema = z.object({
1737
+ maxFid: z.number().int().min(1)
1738
+ });
1739
+ var MinBatteryPercentageFilterSchema = z.object({
1740
+ minBatteryPercentage: z.number().min(0).max(100)
1741
+ });
1742
+ var ExcludePingedTodayFilterSchema = z.object({
1743
+ excludePingedToday: z.boolean()
1744
+ });
1745
+ var HasRechargedInLastDaysFilterSchema = z.object({
1746
+ hasRechargedInLastDays: z.number().int().min(1)
1747
+ });
1748
+ var RequireLotteryOptInFilterSchema = z.object({
1749
+ requireLotteryOptIn: z.boolean()
1750
+ });
1751
+ var RequireQuizOptInFilterSchema = z.object({
1752
+ requireQuizOptIn: z.boolean()
1753
+ });
1754
+ var MinCastCountFilterSchema = z.object({
1755
+ minCastCount: z.number().int().min(0)
1756
+ });
1757
+ var MinClickThroughRateFilterSchema = z.object({
1758
+ minClickThroughRate: z.number().min(0).max(1)
1759
+ });
1760
+ var IsWaitlistedFilterSchema = z.object({
1761
+ isWaitlisted: z.boolean()
1762
+ });
1763
+ var HasTierFilterSchema = z.object({
1764
+ hasTier: z.boolean()
1765
+ });
1766
+ var BeeperEconomicsFilterSchema = z.object({
1767
+ maxAttentionPriceUsd: z.number().min(0).optional(),
1768
+ minAttentionPriceUsd: z.number().min(0).optional(),
1769
+ minFid: z.number().int().min(1).optional(),
1770
+ maxFid: z.number().int().min(1).optional(),
1771
+ minBatteryPercentage: z.number().min(0).max(100).optional(),
1772
+ excludePingedToday: z.boolean().optional(),
1773
+ hasRechargedInLastDays: z.number().int().min(1).optional(),
1774
+ activeInLastDays: z.number().int().min(0).optional(),
1775
+ requireLotteryOptIn: z.boolean().optional(),
1776
+ requireQuizOptIn: z.boolean().optional(),
1777
+ minCastCount: z.number().int().min(0).optional(),
1778
+ minClickThroughRate: z.number().min(0).max(1).optional(),
1779
+ isWaitlisted: z.boolean().optional(),
1780
+ hasTier: z.boolean().optional()
1781
+ });
1782
+ var TokenHolderFilterSchema = z.object({
1783
+ tokenHolder: z.object({
1784
+ chain: z.enum(["base", "ethereum"]),
1785
+ contractAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid contract address"),
1786
+ tokenStandard: z.enum(["ERC20", "ERC721", "ERC1155"]),
1787
+ tokenId: z.string().optional(),
1788
+ // Required for ERC1155, optional for ERC721
1789
+ minBalance: z.string().optional(),
1790
+ symbol: z.string().optional(),
1791
+ name: z.string().optional()
1792
+ })
1793
+ });
1794
+ var CachedTokenHolderFilterSchema = z.object({
1795
+ cachedTokenHolder: z.object({
1796
+ chain: z.enum(["base", "ethereum"]),
1797
+ contractAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid contract address"),
1798
+ tokenStandard: z.enum(["ERC20", "ERC721", "ERC1155"]).default("ERC20"),
1799
+ tokenId: z.string().optional(),
1800
+ tokenSymbol: z.string().optional(),
1801
+ tokenName: z.string().optional(),
1802
+ minBalance: z.union([z.string(), z.number()]).optional()
1803
+ })
1804
+ });
1805
+ var HasBaseWalletFilterSchema = z.object({
1806
+ hasBaseWallet: z.boolean()
1807
+ });
1808
+ var HasVerifiedWalletFilterSchema = z.object({
1809
+ hasVerifiedWallet: z.boolean()
1810
+ });
1811
+ var TokenHolderDiscoverySchema = z.object({
1812
+ chain: z.enum(["base", "ethereum"]),
1813
+ contractAddress: z.string(),
1814
+ tokenStandard: z.enum(["ERC20", "ERC721", "ERC1155"]),
1815
+ tokenId: z.string().optional(),
1816
+ minBalance: z.union([z.string(), z.number()]).optional(),
1817
+ symbol: z.string().optional(),
1818
+ name: z.string().optional()
1819
+ });
1820
+ var CachedTokenHolderSchema = z.object({
1821
+ chain: z.enum(["base", "ethereum"]),
1822
+ contractAddress: z.string(),
1823
+ tokenStandard: z.enum(["ERC20", "ERC721", "ERC1155"]).default("ERC20"),
1824
+ tokenId: z.string().optional(),
1825
+ tokenSymbol: z.string().optional(),
1826
+ tokenName: z.string().optional(),
1827
+ minBalance: z.union([z.string(), z.number()]).optional()
1828
+ });
1829
+ var OnchainFilterSchema = z.object({
1830
+ hasBaseWallet: z.boolean().optional(),
1831
+ tokenHolders: z.array(TokenHolderDiscoverySchema).optional(),
1832
+ cachedTokenHolders: z.array(CachedTokenHolderSchema).optional(),
1833
+ hasVerifiedWallet: z.boolean().optional()
1834
+ });
1835
+ var OrderBySchema = z.enum([
1836
+ "attention_price_asc",
1837
+ "attention_price_desc",
1838
+ "neynar_score_desc",
1839
+ "followers_desc",
1840
+ "followers_asc",
1841
+ "recent_activity",
1842
+ "battery_desc",
1843
+ "random"
1844
+ ]);
1845
+ var RecipientFilterSchema = z.object({
1846
+ // Platform filter
1847
+ platform: z.enum(["all", "farcaster", "twitter"]).optional(),
1848
+ // Social targeting
1849
+ social: SocialFilterSchema.optional(),
1850
+ // Reputation filters
1851
+ reputation: ReputationFilterSchema.optional(),
1852
+ // Onchain filters
1853
+ onchain: OnchainFilterSchema.optional(),
1854
+ // Beeper economics filters
1855
+ beeperEconomics: BeeperEconomicsFilterSchema.optional(),
1856
+ // Sorting and limits
1857
+ orderBy: OrderBySchema.optional(),
1858
+ limit: z.number().int().min(1).optional()
1859
+ });
1860
+ var FilterExpressionSchema = z.lazy(
1861
+ () => z.union([
1862
+ // Match all users
1863
+ z.object({ all: z.literal(true) }),
1864
+ // Logical AND
1865
+ z.object({ and: z.array(FilterExpressionSchema).min(1) }),
1866
+ // Logical OR
1867
+ z.object({ or: z.array(FilterExpressionSchema).min(1) }),
1868
+ // Logical NOT
1869
+ z.object({ not: FilterExpressionSchema }),
1870
+ // Platform filter
1871
+ PlatformFilterSchema,
1872
+ // Activity filter
1873
+ ActiveInLastDaysFilterSchema,
1874
+ // Reputation filters
1875
+ NeynarScoreMinFilterSchema,
1876
+ NeynarScoreMaxFilterSchema,
1877
+ QuotientScoreMinFilterSchema,
1878
+ QuotientScoreMaxFilterSchema,
1879
+ SpamLabelFilterSchema,
1880
+ ProSubscriptionFilterSchema,
1881
+ MinProTenureDaysFilterSchema,
1882
+ MinTenureDaysFilterSchema,
1883
+ VerifiedOnlyFilterSchema,
1884
+ // Social filters
1885
+ MinFollowersFilterSchema,
1886
+ MaxFollowersFilterSchema,
1887
+ MinFollowingFilterSchema,
1888
+ MaxFollowingFilterSchema,
1889
+ FollowersOfFilterSchema,
1890
+ FollowingOfFilterSchema,
1891
+ MutualsWithFilterSchema,
1892
+ // Economics filters
1893
+ MaxAttentionPriceFilterSchema,
1894
+ MinAttentionPriceFilterSchema,
1895
+ MinFidFilterSchema,
1896
+ MaxFidFilterSchema,
1897
+ MinBatteryPercentageFilterSchema,
1898
+ ExcludePingedTodayFilterSchema,
1899
+ HasRechargedInLastDaysFilterSchema,
1900
+ RequireLotteryOptInFilterSchema,
1901
+ RequireQuizOptInFilterSchema,
1902
+ MinCastCountFilterSchema,
1903
+ MinClickThroughRateFilterSchema,
1904
+ IsWaitlistedFilterSchema,
1905
+ HasTierFilterSchema,
1906
+ // Onchain filters
1907
+ TokenHolderFilterSchema,
1908
+ CachedTokenHolderFilterSchema,
1909
+ HasBaseWalletFilterSchema,
1910
+ HasVerifiedWalletFilterSchema,
1911
+ // Full recipient filter (structured DSL)
1912
+ RecipientFilterSchema,
1913
+ // Legacy field comparison (for backward compatibility)
1914
+ LegacyFieldComparisonSchema
1915
+ ])
1916
+ );
1917
+ var RecipientFilterDSLSchema = FilterExpressionSchema;
1918
+ function validateFilter(filter) {
1919
+ const result = FilterExpressionSchema.safeParse(filter);
1920
+ return result.success;
1921
+ }
1922
+ function parseFilter(filter) {
1923
+ return FilterExpressionSchema.parse(filter);
1924
+ }
1925
+ function safeParseFilter(filter) {
1926
+ return FilterExpressionSchema.safeParse(filter);
1927
+ }
1928
+ function validateRecipientFilter(filter) {
1929
+ const result = RecipientFilterSchema.safeParse(filter);
1930
+ return result.success;
1931
+ }
1932
+ function parseRecipientFilter(filter) {
1933
+ return RecipientFilterSchema.parse(filter);
1934
+ }
1935
+ function safeParseRecipientFilter(filter) {
1936
+ return RecipientFilterSchema.safeParse(filter);
1937
+ }
1938
+ function validateFilterHasTargeting(filter) {
1939
+ const hasSocial = filter.social?.specificFids?.length || filter.social?.specificUsernames?.length || filter.social?.followersOf || filter.social?.followingOf || filter.social?.mutualsWith;
1940
+ if (!hasSocial) {
1941
+ const hasOtherFilters = filter.reputation || filter.onchain || filter.beeperEconomics;
1942
+ return !!hasOtherFilters;
1943
+ }
1944
+ return true;
1945
+ }
1946
+ function describeFilters(filter) {
1947
+ const descriptions = [];
1948
+ if (filter.platform && filter.platform !== "all") {
1949
+ descriptions.push(`Platform: ${filter.platform}`);
1950
+ }
1951
+ if (filter.social?.specificFids?.length) {
1952
+ descriptions.push(`${filter.social.specificFids.length} specific users`);
1953
+ }
1954
+ if (filter.social?.followersOf) {
1955
+ descriptions.push(`Followers of FID ${filter.social.followersOf}`);
1956
+ }
1957
+ if (filter.social?.mutualsWith) {
1958
+ descriptions.push(`Mutual followers with FID ${filter.social.mutualsWith}`);
1959
+ }
1960
+ if (filter.social?.minFollowers) {
1961
+ descriptions.push(`Min ${filter.social.minFollowers} followers`);
1962
+ }
1963
+ if (filter.social?.signalTokens?.length) {
1964
+ descriptions.push(
1965
+ `Signal token holders: ${filter.social.signalTokens.length} token(s)`
1966
+ );
1967
+ }
1968
+ if (filter.social?.timezones && filter.social.timezones.length > 0) {
1969
+ descriptions.push(`${filter.social.timezones.length} timezone(s)`);
1970
+ }
1971
+ if (filter.social?.countries && filter.social.countries.length > 0) {
1972
+ descriptions.push(`${filter.social.countries.length} country/countries`);
1973
+ }
1974
+ if (filter.social?.roles && filter.social.roles.length > 0) {
1975
+ descriptions.push(`${filter.social.roles.length} role(s)`);
1976
+ }
1977
+ if (filter.reputation?.neynarScoreMin) {
1978
+ descriptions.push(`Neynar score >= ${filter.reputation.neynarScoreMin}`);
1979
+ }
1980
+ if (filter.reputation?.quotientScoreMin) {
1981
+ descriptions.push(`Quotient score >= ${filter.reputation.quotientScoreMin}`);
1982
+ }
1983
+ if (filter.reputation?.spamLabel === "not_spam_only") {
1984
+ descriptions.push("Non-spam only");
1985
+ }
1986
+ if (filter.reputation?.proSubscriptionRequired) {
1987
+ descriptions.push("Pro subscribers");
1988
+ }
1989
+ if (filter.onchain?.hasBaseWallet) {
1990
+ descriptions.push("Base wallet holders");
1991
+ }
1992
+ if (filter.onchain?.tokenHolders?.length) {
1993
+ descriptions.push(
1994
+ `Token holders: ${filter.onchain.tokenHolders.length} token(s)`
1995
+ );
1996
+ }
1997
+ if (filter.onchain?.cachedTokenHolders?.length) {
1998
+ const tokenNames = filter.onchain.cachedTokenHolders.map((t) => t.tokenSymbol || t.contractAddress.slice(0, 10)).join(", ");
1999
+ descriptions.push(`Holders of: ${tokenNames}`);
2000
+ }
2001
+ if (filter.beeperEconomics?.maxAttentionPriceUsd) {
2002
+ descriptions.push(
2003
+ `Max $${filter.beeperEconomics.maxAttentionPriceUsd}/msg`
2004
+ );
2005
+ }
2006
+ if (filter.beeperEconomics?.activeInLastDays) {
2007
+ descriptions.push(
2008
+ `Active in last ${filter.beeperEconomics.activeInLastDays} days`
2009
+ );
2010
+ }
2011
+ if (filter.beeperEconomics?.hasRechargedInLastDays) {
2012
+ descriptions.push(
2013
+ `Recharged in last ${filter.beeperEconomics.hasRechargedInLastDays} days`
2014
+ );
2015
+ }
2016
+ if (filter.beeperEconomics?.minBatteryPercentage) {
2017
+ descriptions.push(`Min ${filter.beeperEconomics.minBatteryPercentage}% battery`);
2018
+ }
2019
+ return descriptions;
2020
+ }
2021
+
2022
+ // src/schemas/draft.schema.ts
2023
+ var NetworkSchema = z.enum([
2024
+ "ethereum",
2025
+ "polygon",
2026
+ "base",
2027
+ "arbitrum",
2028
+ "optimism"
2029
+ ]);
2030
+ var TokenTypeSchema = z.enum(["USDC", "USDT", "ETH", "MATIC"]);
2031
+ var DistributionStrategySchema = z.enum([
2032
+ "equal",
2033
+ "weighted",
2034
+ "proportional"
2035
+ ]);
2036
+ var DraftStatusSchema = z.enum(["draft", "quoted", "executed"]);
2037
+ var DraftInputSchema = z.object({
2038
+ name: z.string().min(1).max(255),
2039
+ amount: z.string().regex(/^\d+(\.\d+)?$/, "Amount must be a valid decimal string"),
2040
+ token: TokenTypeSchema,
2041
+ network: NetworkSchema,
2042
+ filter: RecipientFilterDSLSchema,
2043
+ strategy: DistributionStrategySchema,
2044
+ weights: z.record(z.string(), z.number().min(0)).optional(),
2045
+ metadata: z.record(z.string(), z.unknown()).optional()
2046
+ });
2047
+ var DraftSchema = z.object({
2048
+ id: z.string().uuid(),
2049
+ name: z.string(),
2050
+ amount: z.string(),
2051
+ token: TokenTypeSchema,
2052
+ network: NetworkSchema,
2053
+ filter: RecipientFilterDSLSchema,
2054
+ strategy: DistributionStrategySchema,
2055
+ weights: z.record(z.string(), z.number()).optional(),
2056
+ metadata: z.record(z.string(), z.unknown()).optional(),
2057
+ createdAt: z.string().datetime(),
2058
+ updatedAt: z.string().datetime(),
2059
+ status: DraftStatusSchema
2060
+ });
2061
+ var DraftUpdateSchema = DraftInputSchema.partial();
2062
+ function validateDraftInput(input) {
2063
+ return DraftInputSchema.safeParse(input);
2064
+ }
2065
+ function parseDraftInput(input) {
2066
+ return DraftInputSchema.parse(input);
2067
+ }
2068
+ function validateDraft(draft) {
2069
+ return DraftSchema.safeParse(draft);
2070
+ }
2071
+ function parseDraft(draft) {
2072
+ return DraftSchema.parse(draft);
2073
+ }
2074
+
2075
+ // src/send/draft.ts
2076
+ function createDraft(input) {
2077
+ const validated = validateDraftInput2(input);
2078
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2079
+ const draft = {
2080
+ id: randomUUID(),
2081
+ name: validated.name,
2082
+ amount: validated.amount,
2083
+ token: validated.token,
2084
+ network: validated.network,
2085
+ filter: validated.filter,
2086
+ strategy: validated.strategy,
2087
+ createdAt: now,
2088
+ updatedAt: now,
2089
+ status: "draft"
2090
+ };
2091
+ if (validated.weights !== void 0) {
2092
+ draft.weights = validated.weights;
2093
+ }
2094
+ if (validated.metadata !== void 0) {
2095
+ draft.metadata = validated.metadata;
2096
+ }
2097
+ return draft;
2098
+ }
2099
+ function validateDraftInput2(input) {
2100
+ const result = DraftInputSchema.safeParse(input);
2101
+ if (!result.success) {
2102
+ const issues = result.error.issues.map((issue) => ({
2103
+ path: issue.path.join("."),
2104
+ message: issue.message
2105
+ }));
2106
+ throw new BeeperError({
2107
+ code: ErrorCodes.VALIDATION_ERROR,
2108
+ message: "Invalid draft input",
2109
+ context: {
2110
+ details: { issues }
2111
+ },
2112
+ retryable: false
2113
+ });
2114
+ }
2115
+ return result.data;
2116
+ }
2117
+ function updateDraft(draft, updates) {
2118
+ const mergedInput = {
2119
+ name: updates.name ?? draft.name,
2120
+ amount: updates.amount ?? draft.amount,
2121
+ token: updates.token ?? draft.token,
2122
+ network: updates.network ?? draft.network,
2123
+ filter: updates.filter ?? draft.filter,
2124
+ strategy: updates.strategy ?? draft.strategy
2125
+ };
2126
+ if (updates.weights !== void 0) {
2127
+ mergedInput.weights = updates.weights;
2128
+ } else if (draft.weights !== void 0) {
2129
+ mergedInput.weights = draft.weights;
2130
+ }
2131
+ if (updates.metadata !== void 0) {
2132
+ mergedInput.metadata = updates.metadata;
2133
+ } else if (draft.metadata !== void 0) {
2134
+ mergedInput.metadata = draft.metadata;
2135
+ }
2136
+ const validated = validateDraftInput2(mergedInput);
2137
+ const updatedDraft = {
2138
+ ...draft,
2139
+ name: validated.name,
2140
+ amount: validated.amount,
2141
+ token: validated.token,
2142
+ network: validated.network,
2143
+ filter: validated.filter,
2144
+ strategy: validated.strategy,
2145
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2146
+ };
2147
+ if (validated.weights !== void 0) {
2148
+ updatedDraft.weights = validated.weights;
2149
+ }
2150
+ if (validated.metadata !== void 0) {
2151
+ updatedDraft.metadata = validated.metadata;
2152
+ }
2153
+ return updatedDraft;
2154
+ }
2155
+ function isReadyForQuote(draft) {
2156
+ return draft.status === "draft" && draft.name.length > 0 && parseFloat(draft.amount) > 0;
2157
+ }
2158
+ var ApiQuoteResponseSchema = z.object({
2159
+ id: z.string().regex(/^qt_[a-zA-Z0-9]+$/),
2160
+ status: z.enum([
2161
+ "pending",
2162
+ "deposit_confirmed",
2163
+ "executing",
2164
+ "completed",
2165
+ "expired",
2166
+ "failed"
2167
+ ]),
2168
+ recipientCount: z.number().int().min(0),
2169
+ totalAmount: z.string(),
2170
+ protocolFee: z.string(),
2171
+ depositAmount: z.string(),
2172
+ depositAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
2173
+ depositChainId: z.number().int(),
2174
+ depositTokenAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
2175
+ expiresAt: z.string().datetime(),
2176
+ input: z.record(z.unknown()),
2177
+ createdAt: z.string().datetime(),
2178
+ updatedAt: z.string().datetime()
2179
+ });
2180
+ async function createQuote(httpClient, draft, opts) {
2181
+ const body = {
2182
+ filter: draft.filter,
2183
+ tokenAddress: getTokenAddress(draft.token, draft.network),
2184
+ chainId: getChainId(draft.network),
2185
+ amountPerRecipient: draft.amount,
2186
+ budgetCap: opts?.amount ?? draft.amount,
2187
+ memo: draft.name,
2188
+ metadata: draft.metadata,
2189
+ ttlSeconds: 300
2190
+ };
2191
+ const response = await httpClient.post(
2192
+ "/api/v1/sdk/send/quotes",
2193
+ body,
2194
+ {},
2195
+ ApiQuoteResponseSchema
2196
+ );
2197
+ return response.data;
2198
+ }
2199
+ async function getQuote(httpClient, quoteId) {
2200
+ if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
2201
+ throw new BeeperError({
2202
+ code: ErrorCodes.VALIDATION_ERROR,
2203
+ message: "Invalid quote ID format. Expected format: qt_xxxxx",
2204
+ retryable: false
2205
+ });
2206
+ }
2207
+ const response = await httpClient.get(
2208
+ `/api/v1/sdk/send/quotes/${quoteId}`,
2209
+ {},
2210
+ ApiQuoteResponseSchema
2211
+ );
2212
+ return response.data;
2213
+ }
2214
+ function isQuoteExpired(quote) {
2215
+ return quote.status === "expired" || new Date(quote.expiresAt) < /* @__PURE__ */ new Date();
2216
+ }
2217
+ function isReadyForDeposit(quote) {
2218
+ return quote.status === "pending" && !isQuoteExpired(quote);
2219
+ }
2220
+ function getChainId(network) {
2221
+ const chainIds = {
2222
+ ethereum: 1,
2223
+ base: 8453,
2224
+ arbitrum: 42161,
2225
+ polygon: 137,
2226
+ optimism: 10
2227
+ };
2228
+ return chainIds[network] ?? 1;
2229
+ }
2230
+ function getTokenAddress(token, network) {
2231
+ const addresses = {
2232
+ USDC: {
2233
+ ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
2234
+ base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2235
+ arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
2236
+ polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
2237
+ optimism: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
2238
+ },
2239
+ USDT: {
2240
+ ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
2241
+ base: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
2242
+ arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
2243
+ polygon: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
2244
+ optimism: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"
2245
+ }
2246
+ };
2247
+ return addresses[token]?.[network] ?? "0x0000000000000000000000000000000000000000";
2248
+ }
2249
+ var ConfirmDepositResponseSchema = z.object({
2250
+ quoteId: z.string(),
2251
+ status: z.enum(["confirmed", "pending_verification"]),
2252
+ detectedAmount: z.string(),
2253
+ sufficient: z.boolean(),
2254
+ blockNumber: z.number().int(),
2255
+ confirmedAt: z.string().datetime()
2256
+ });
2257
+ async function confirmDeposit(httpClient, quoteId, params) {
2258
+ validateQuoteId(quoteId);
2259
+ validateTxHash(params.txHash);
2260
+ if (!params.idempotencyKey || params.idempotencyKey.length === 0) {
2261
+ throw new BeeperError({
2262
+ code: ErrorCodes.VALIDATION_ERROR,
2263
+ message: "Idempotency key is required for deposit confirmation",
2264
+ retryable: false
2265
+ });
2266
+ }
2267
+ const response = await httpClient.post(
2268
+ `/api/v1/sdk/send/quotes/${quoteId}/confirm`,
2269
+ { txHash: params.txHash },
2270
+ { idempotencyKey: params.idempotencyKey },
2271
+ ConfirmDepositResponseSchema
2272
+ );
2273
+ return response.data;
2274
+ }
2275
+ function isDepositSufficient(result) {
2276
+ return result.status === "confirmed" && result.sufficient;
2277
+ }
2278
+ function validateTxHash(txHash) {
2279
+ const txHashRegex = /^0x[a-fA-F0-9]{64}$/;
2280
+ if (!txHashRegex.test(txHash)) {
2281
+ throw new BeeperError({
2282
+ code: ErrorCodes.VALIDATION_ERROR,
2283
+ message: "Invalid transaction hash format. Expected 0x followed by 64 hex characters.",
2284
+ context: {
2285
+ details: {
2286
+ provided: txHash,
2287
+ expectedFormat: "0x + 64 hex characters"
2288
+ }
2289
+ },
2290
+ retryable: false
2291
+ });
2292
+ }
2293
+ }
2294
+ function validateQuoteId(quoteId) {
2295
+ if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
2296
+ throw new BeeperError({
2297
+ code: ErrorCodes.VALIDATION_ERROR,
2298
+ message: "Invalid quote ID format. Expected format: qt_xxxxx",
2299
+ retryable: false
2300
+ });
2301
+ }
2302
+ }
2303
+ function generateDepositIdempotencyKey(quoteId) {
2304
+ return `confirm-${quoteId}`;
2305
+ }
2306
+ var ExecuteSendResponseSchema = z.object({
2307
+ quoteId: z.string(),
2308
+ status: z.enum(["executing", "queued"]),
2309
+ estimatedCompletionAt: z.string().datetime(),
2310
+ batchId: z.string()
2311
+ });
2312
+ async function executeSend(httpClient, quoteId, params) {
2313
+ validateQuoteId2(quoteId);
2314
+ if (!params.idempotencyKey || params.idempotencyKey.length === 0) {
2315
+ throw new BeeperError({
2316
+ code: ErrorCodes.VALIDATION_ERROR,
2317
+ message: "Idempotency key is required for execution",
2318
+ retryable: false
2319
+ });
2320
+ }
2321
+ const response = await httpClient.post(
2322
+ `/api/v1/sdk/send/quotes/${quoteId}/execute`,
2323
+ {},
2324
+ // Empty body as per HTTP contract
2325
+ { idempotencyKey: params.idempotencyKey },
2326
+ ExecuteSendResponseSchema
2327
+ );
2328
+ return response.data;
2329
+ }
2330
+ function isExecuting(result) {
2331
+ return result.status === "executing" || result.status === "queued";
2332
+ }
2333
+ function getEstimatedTimeRemaining(result) {
2334
+ const estimatedCompletion = new Date(result.estimatedCompletionAt);
2335
+ const now = /* @__PURE__ */ new Date();
2336
+ return Math.max(0, estimatedCompletion.getTime() - now.getTime());
2337
+ }
2338
+ function validateQuoteId2(quoteId) {
2339
+ if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
2340
+ throw new BeeperError({
2341
+ code: ErrorCodes.VALIDATION_ERROR,
2342
+ message: "Invalid quote ID format. Expected format: qt_xxxxx",
2343
+ retryable: false
2344
+ });
2345
+ }
2346
+ }
2347
+ function generateExecuteIdempotencyKey(quoteId) {
2348
+ return `execute-${quoteId}`;
2349
+ }
2350
+ var ReceiptTransactionSchema = z.object({
2351
+ recipient: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
2352
+ amount: z.string(),
2353
+ txHash: z.string().nullable(),
2354
+ status: z.enum(["success", "failed"]),
2355
+ error: z.string().nullable().optional()
2356
+ });
2357
+ var ApiReceiptResponseSchema = z.object({
2358
+ quoteId: z.string(),
2359
+ status: z.enum(["completed", "partial", "failed"]),
2360
+ successCount: z.number().int().min(0),
2361
+ failureCount: z.number().int().min(0),
2362
+ totalSent: z.string(),
2363
+ refundAmount: z.string(),
2364
+ refundTxHash: z.string().nullable().optional(),
2365
+ transactions: z.array(ReceiptTransactionSchema),
2366
+ completedAt: z.string().datetime()
2367
+ });
2368
+ var DEFAULT_MAX_ATTEMPTS = 60;
2369
+ var DEFAULT_INTERVAL_MS = 5e3;
2370
+ async function getReceipt(httpClient, quoteId) {
2371
+ if (!quoteId.match(/^qt_[a-zA-Z0-9]+$/)) {
2372
+ throw new BeeperError({
2373
+ code: ErrorCodes.VALIDATION_ERROR,
2374
+ message: "Invalid quote ID format. Expected format: qt_xxxxx",
2375
+ retryable: false
2376
+ });
2377
+ }
2378
+ const response = await httpClient.get(
2379
+ `/api/v1/sdk/send/quotes/${quoteId}/receipt`,
2380
+ {},
2381
+ ApiReceiptResponseSchema
2382
+ );
2383
+ return response.data;
2384
+ }
2385
+ async function pollUntilComplete(httpClient, quoteId, opts) {
2386
+ const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
2387
+ const intervalMs = opts?.intervalMs ?? DEFAULT_INTERVAL_MS;
2388
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
2389
+ if (opts?.signal?.aborted) {
2390
+ throw new BeeperError({
2391
+ code: ErrorCodes.TIMEOUT,
2392
+ message: "Polling was aborted",
2393
+ retryable: false
2394
+ });
2395
+ }
2396
+ try {
2397
+ const receipt = await getReceipt(httpClient, quoteId);
2398
+ if (isComplete(receipt)) return receipt;
2399
+ } catch (error) {
2400
+ if (!(error instanceof BeeperError && error.context.statusCode === 428)) {
2401
+ throw error;
2402
+ }
2403
+ }
2404
+ if (attempt < maxAttempts - 1) {
2405
+ await sleep(intervalMs, opts?.signal);
2406
+ }
2407
+ }
2408
+ throw new BeeperError({
2409
+ code: ErrorCodes.QUOTE_EXPIRED,
2410
+ message: `Polling timed out after ${maxAttempts} attempts`,
2411
+ context: { details: { quoteId, maxAttempts } },
2412
+ retryable: false
2413
+ });
2414
+ }
2415
+ function isComplete(receipt) {
2416
+ return ["completed", "partial", "failed"].includes(receipt.status);
2417
+ }
2418
+ function isSuccess(receipt) {
2419
+ return receipt.status === "completed" && receipt.failureCount === 0;
2420
+ }
2421
+ function getSuccessRate(receipt) {
2422
+ const total = receipt.successCount + receipt.failureCount;
2423
+ return total === 0 ? 0 : receipt.successCount / total * 100;
2424
+ }
2425
+ function getFailedTransactions(receipt) {
2426
+ return receipt.transactions.filter((tx) => tx.status === "failed");
2427
+ }
2428
+ function sleep(ms, signal) {
2429
+ return new Promise((resolve, reject) => {
2430
+ const timeoutId = setTimeout(resolve, ms);
2431
+ signal?.addEventListener("abort", () => {
2432
+ clearTimeout(timeoutId);
2433
+ reject(new Error("Aborted"));
2434
+ });
2435
+ });
2436
+ }
2437
+
2438
+ // src/client/FilterBuilder.ts
2439
+ var FilterExpression = class {
2440
+ expression;
2441
+ constructor(expression) {
2442
+ this.expression = expression;
2443
+ }
2444
+ /**
2445
+ * Convert the filter expression to JSON format for API requests
2446
+ */
2447
+ toJSON() {
2448
+ return this.expression;
2449
+ }
2450
+ /**
2451
+ * Combine this filter with another using AND logic
2452
+ */
2453
+ and(other) {
2454
+ return FilterBuilder.and([this, other]);
2455
+ }
2456
+ /**
2457
+ * Combine this filter with another using OR logic
2458
+ */
2459
+ or(other) {
2460
+ return FilterBuilder.or([this, other]);
2461
+ }
2462
+ /**
2463
+ * Negate this filter
2464
+ */
2465
+ not() {
2466
+ return FilterBuilder.not(this);
2467
+ }
2468
+ };
2469
+ var FilterBuilder = class {
2470
+ // ===========================================================================
2471
+ // Logical Combinators
2472
+ // ===========================================================================
2473
+ /**
2474
+ * Combine multiple filters with AND logic (all must match)
2475
+ * @param filters - Array of filter expressions to combine
2476
+ */
2477
+ static and(filters) {
2478
+ if (filters.length === 0) {
2479
+ throw new Error("and() requires at least one filter");
2480
+ }
2481
+ if (filters.length === 1) {
2482
+ return filters[0];
2483
+ }
2484
+ return new FilterExpression({
2485
+ and: filters.map((f) => f.toJSON())
2486
+ });
2487
+ }
2488
+ /**
2489
+ * Combine multiple filters with OR logic (any must match)
2490
+ * @param filters - Array of filter expressions to combine
2491
+ */
2492
+ static or(filters) {
2493
+ if (filters.length === 0) {
2494
+ throw new Error("or() requires at least one filter");
2495
+ }
2496
+ if (filters.length === 1) {
2497
+ return filters[0];
2498
+ }
2499
+ return new FilterExpression({
2500
+ or: filters.map((f) => f.toJSON())
2501
+ });
2502
+ }
2503
+ /**
2504
+ * Negate a filter (matches users who do NOT match the filter)
2505
+ * @param filter - Filter expression to negate
2506
+ */
2507
+ static not(filter) {
2508
+ return new FilterExpression({
2509
+ not: filter.toJSON()
2510
+ });
2511
+ }
2512
+ // ===========================================================================
2513
+ // Platform Filter
2514
+ // ===========================================================================
2515
+ /**
2516
+ * Filter by platform
2517
+ * @param p - Platform to target: 'all', 'farcaster', or 'twitter'
2518
+ */
2519
+ static platform(p) {
2520
+ return new FilterExpression({ platform: p });
2521
+ }
2522
+ // ===========================================================================
2523
+ // Activity Filters
2524
+ // ===========================================================================
2525
+ /**
2526
+ * Filter users who have been active within the last N days
2527
+ * @param days - Number of days (1-365)
2528
+ */
2529
+ static activeInLastDays(days) {
2530
+ if (days < 1 || days > 365) {
2531
+ throw new Error("activeInLastDays must be between 1 and 365");
2532
+ }
2533
+ return new FilterExpression({ activeInLastDays: days });
2534
+ }
2535
+ // ===========================================================================
2536
+ // Reputation Filters (Neynar Score)
2537
+ // ===========================================================================
2538
+ /**
2539
+ * Filter users with Neynar score >= minimum
2540
+ * @param score - Minimum score (0-1)
2541
+ */
2542
+ static neynarScoreMin(score) {
2543
+ if (score < 0 || score > 1) {
2544
+ throw new Error("neynarScoreMin must be between 0 and 1");
2545
+ }
2546
+ return new FilterExpression({ neynarScoreMin: score });
2547
+ }
2548
+ /**
2549
+ * Filter users with Neynar score <= maximum
2550
+ * @param score - Maximum score (0-1)
2551
+ */
2552
+ static neynarScoreMax(score) {
2553
+ if (score < 0 || score > 1) {
2554
+ throw new Error("neynarScoreMax must be between 0 and 1");
2555
+ }
2556
+ return new FilterExpression({ neynarScoreMax: score });
2557
+ }
2558
+ /**
2559
+ * Filter users with Neynar score in a range
2560
+ * @param min - Minimum score (0-1, optional)
2561
+ * @param max - Maximum score (0-1, optional)
2562
+ */
2563
+ static neynarScoreRange(min, max) {
2564
+ if (min !== void 0 && (min < 0 || min > 1)) {
2565
+ throw new Error("neynarScoreRange min must be between 0 and 1");
2566
+ }
2567
+ if (max !== void 0 && (max < 0 || max > 1)) {
2568
+ throw new Error("neynarScoreRange max must be between 0 and 1");
2569
+ }
2570
+ const range = {};
2571
+ if (min !== void 0) range.min = min;
2572
+ if (max !== void 0) range.max = max;
2573
+ return new FilterExpression({ neynarScoreRange: range });
2574
+ }
2575
+ /**
2576
+ * Filter by spam label
2577
+ * @param label - Spam label filter: 'not_spam_only', 'spam_only', or 'all'
2578
+ */
2579
+ static spamLabel(label) {
2580
+ return new FilterExpression({ spamLabel: label });
2581
+ }
2582
+ /**
2583
+ * Filter to exclude spam users (convenience method)
2584
+ * @deprecated Use spamLabel('not_spam_only') instead
2585
+ */
2586
+ static excludeSpam() {
2587
+ return new FilterExpression({ spamLabel: "not_spam_only" });
2588
+ }
2589
+ // ===========================================================================
2590
+ // Social Graph Filters (Followers)
2591
+ // ===========================================================================
2592
+ /**
2593
+ * Filter users with at least N followers
2594
+ * @param count - Minimum follower count
2595
+ */
2596
+ static minFollowers(count) {
2597
+ if (count < 0) {
2598
+ throw new Error("minFollowers must be >= 0");
2599
+ }
2600
+ return new FilterExpression({ minFollowers: count });
2601
+ }
2602
+ /**
2603
+ * Filter users with at most N followers
2604
+ * @param count - Maximum follower count
2605
+ */
2606
+ static maxFollowers(count) {
2607
+ if (count < 0) {
2608
+ throw new Error("maxFollowers must be >= 0");
2609
+ }
2610
+ return new FilterExpression({ maxFollowers: count });
2611
+ }
2612
+ /**
2613
+ * Filter users with follower count in a range
2614
+ * @param min - Minimum follower count (optional)
2615
+ * @param max - Maximum follower count (optional)
2616
+ */
2617
+ static followerRange(min, max) {
2618
+ if (min !== void 0 && min < 0) {
2619
+ throw new Error("followerRange min must be >= 0");
2620
+ }
2621
+ if (max !== void 0 && max < 0) {
2622
+ throw new Error("followerRange max must be >= 0");
2623
+ }
2624
+ const range = {};
2625
+ if (min !== void 0) range.min = min;
2626
+ if (max !== void 0) range.max = max;
2627
+ return new FilterExpression({ followerRange: range });
2628
+ }
2629
+ /**
2630
+ * Filter users who follow a specific FID
2631
+ * @param fid - Farcaster ID to check followers of
2632
+ */
2633
+ static followersOf(fid) {
2634
+ if (fid < 1) {
2635
+ throw new Error("followersOf FID must be >= 1");
2636
+ }
2637
+ return new FilterExpression({ followersOf: fid });
2638
+ }
2639
+ /**
2640
+ * Filter users who have mutual follows with a specific FID
2641
+ * @param fid - Farcaster ID to check mutual follows with
2642
+ */
2643
+ static mutualsWith(fid) {
2644
+ if (fid < 1) {
2645
+ throw new Error("mutualsWith FID must be >= 1");
2646
+ }
2647
+ return new FilterExpression({ mutualsWith: fid });
2648
+ }
2649
+ // ===========================================================================
2650
+ // Economics Filters
2651
+ // ===========================================================================
2652
+ /**
2653
+ * Filter users with attention price <= maximum USD
2654
+ * @param usd - Maximum attention price in USD
2655
+ */
2656
+ static maxAttentionPriceUsd(usd) {
2657
+ if (usd < 0) {
2658
+ throw new Error("maxAttentionPriceUsd must be >= 0");
2659
+ }
2660
+ return new FilterExpression({ maxAttentionPriceUsd: usd });
2661
+ }
2662
+ // ===========================================================================
2663
+ // Onchain Filters (Token Holdings)
2664
+ // ===========================================================================
2665
+ /**
2666
+ * Filter users who hold a specific token
2667
+ * @param opts - Token holder options (tokenAddress, chainId, optional minBalance)
2668
+ */
2669
+ static tokenHolder(opts) {
2670
+ if (!/^0x[a-fA-F0-9]{40}$/.test(opts.tokenAddress)) {
2671
+ throw new Error("tokenAddress must be a valid Ethereum address");
2672
+ }
2673
+ if (opts.minBalance !== void 0) {
2674
+ if (!/^[0-9]+$/.test(opts.minBalance)) {
2675
+ throw new Error("minBalance must be a numeric string (wei)");
2676
+ }
2677
+ return new FilterExpression({
2678
+ walletMinBalance: {
2679
+ tokenAddress: opts.tokenAddress,
2680
+ chainId: opts.chainId,
2681
+ minBalance: opts.minBalance
2682
+ }
2683
+ });
2684
+ }
2685
+ return new FilterExpression({
2686
+ walletHasToken: {
2687
+ tokenAddress: opts.tokenAddress,
2688
+ chainId: opts.chainId
2689
+ }
2690
+ });
2691
+ }
2692
+ /**
2693
+ * Filter users who hold any of the specified tokens (multiple token filter)
2694
+ * @param opts - Array of token holder options (max 10)
2695
+ */
2696
+ static tokenHolders(opts) {
2697
+ if (opts.length === 0) {
2698
+ throw new Error("tokenHolders requires at least one token option");
2699
+ }
2700
+ if (opts.length > 10) {
2701
+ throw new Error("tokenHolders cannot exceed 10 tokens");
2702
+ }
2703
+ for (const opt of opts) {
2704
+ if (!/^0x[a-fA-F0-9]{40}$/.test(opt.tokenAddress)) {
2705
+ throw new Error("tokenAddress must be a valid Ethereum address");
2706
+ }
2707
+ if (opt.minBalance !== void 0 && !/^[0-9]+$/.test(opt.minBalance)) {
2708
+ throw new Error("minBalance must be a numeric string (wei)");
2709
+ }
2710
+ }
2711
+ return new FilterExpression({
2712
+ tokenHolders: opts.map((o) => ({
2713
+ tokenAddress: o.tokenAddress,
2714
+ chainId: o.chainId,
2715
+ ...o.minBalance !== void 0 ? { minBalance: o.minBalance } : {}
2716
+ }))
2717
+ });
2718
+ }
2719
+ /**
2720
+ * Filter users who hold any of the specified tokens (uses pre-synced cache for faster queries)
2721
+ * @param opts - Array of token holder options (max 10)
2722
+ */
2723
+ static cachedTokenHolders(opts) {
2724
+ if (opts.length === 0) {
2725
+ throw new Error("cachedTokenHolders requires at least one token option");
2726
+ }
2727
+ if (opts.length > 10) {
2728
+ throw new Error("cachedTokenHolders cannot exceed 10 tokens");
2729
+ }
2730
+ for (const opt of opts) {
2731
+ if (!/^0x[a-fA-F0-9]{40}$/.test(opt.tokenAddress)) {
2732
+ throw new Error("tokenAddress must be a valid Ethereum address");
2733
+ }
2734
+ if (opt.minBalance !== void 0 && !/^[0-9]+$/.test(opt.minBalance)) {
2735
+ throw new Error("minBalance must be a numeric string (wei)");
2736
+ }
2737
+ }
2738
+ return new FilterExpression({
2739
+ cachedTokenHolders: opts.map((o) => ({
2740
+ tokenAddress: o.tokenAddress,
2741
+ chainId: o.chainId,
2742
+ ...o.minBalance !== void 0 ? { minBalance: o.minBalance } : {}
2743
+ }))
2744
+ });
2745
+ }
2746
+ /**
2747
+ * Filter users who have configured specific signal tokens
2748
+ * @param tokenAddresses - Array of token contract addresses (max 20)
2749
+ */
2750
+ static signalTokens(tokenAddresses) {
2751
+ if (tokenAddresses.length === 0) {
2752
+ throw new Error("signalTokens requires at least one token address");
2753
+ }
2754
+ if (tokenAddresses.length > 20) {
2755
+ throw new Error("signalTokens cannot exceed 20 tokens");
2756
+ }
2757
+ for (const addr of tokenAddresses) {
2758
+ if (!/^0x[a-fA-F0-9]{40}$/.test(addr)) {
2759
+ throw new Error("tokenAddress must be a valid Ethereum address");
2760
+ }
2761
+ }
2762
+ return new FilterExpression({ signalTokens: tokenAddresses });
2763
+ }
2764
+ /**
2765
+ * Filter users with a wallet on a specific chain
2766
+ * @param chain - Blockchain chain
2767
+ */
2768
+ static walletChain(chain) {
2769
+ return new FilterExpression({ walletChain: chain });
2770
+ }
2771
+ /**
2772
+ * Filter users who have a Base chain wallet
2773
+ */
2774
+ static hasBaseWallet() {
2775
+ return new FilterExpression({ hasBaseWallet: true });
2776
+ }
2777
+ /**
2778
+ * Filter users who have a verified wallet
2779
+ */
2780
+ static hasVerifiedWallet() {
2781
+ return new FilterExpression({ hasVerifiedWallet: true });
2782
+ }
2783
+ // ===========================================================================
2784
+ // Verification Filters
2785
+ // ===========================================================================
2786
+ /**
2787
+ * Filter to only verified users
2788
+ */
2789
+ static verifiedOnly() {
2790
+ return new FilterExpression({ verifiedOnly: true });
2791
+ }
2792
+ /**
2793
+ * Filter to only users with Pro subscription
2794
+ */
2795
+ static proSubscriptionRequired() {
2796
+ return new FilterExpression({ proSubscriptionRequired: true });
2797
+ }
2798
+ /**
2799
+ * Filter users with minimum account tenure
2800
+ * @param days - Minimum number of days since account creation
2801
+ */
2802
+ static minTenureDays(days) {
2803
+ if (days < 0) {
2804
+ throw new Error("minTenureDays must be >= 0");
2805
+ }
2806
+ return new FilterExpression({ minTenureDays: days });
2807
+ }
2808
+ // ===========================================================================
2809
+ // User ID Filters
2810
+ // ===========================================================================
2811
+ /**
2812
+ * Filter by specific user ID
2813
+ * @param id - User ID
2814
+ */
2815
+ static userId(id) {
2816
+ return new FilterExpression({ userId: id });
2817
+ }
2818
+ /**
2819
+ * Filter by multiple user IDs
2820
+ * @param ids - Array of user IDs (max 1000)
2821
+ */
2822
+ static userIds(ids) {
2823
+ if (ids.length === 0) {
2824
+ throw new Error("userIds requires at least one ID");
2825
+ }
2826
+ if (ids.length > 1e3) {
2827
+ throw new Error("userIds cannot exceed 1000 IDs");
2828
+ }
2829
+ return new FilterExpression({ userIds: ids });
2830
+ }
2831
+ /**
2832
+ * Filter by specific Farcaster ID
2833
+ * @param fid - Farcaster ID
2834
+ */
2835
+ static fid(fid) {
2836
+ return new FilterExpression({ fid });
2837
+ }
2838
+ /**
2839
+ * Filter by multiple Farcaster IDs
2840
+ * @param fids - Array of Farcaster IDs (max 1000)
2841
+ */
2842
+ static fids(fids) {
2843
+ if (fids.length === 0) {
2844
+ throw new Error("fids requires at least one FID");
2845
+ }
2846
+ if (fids.length > 1e3) {
2847
+ throw new Error("fids cannot exceed 1000 FIDs");
2848
+ }
2849
+ return new FilterExpression({ fids });
2850
+ }
2851
+ // ===========================================================================
2852
+ // Tag Filters
2853
+ // ===========================================================================
2854
+ /**
2855
+ * Filter users with a specific tag
2856
+ * @param tag - Tag name (max 64 chars)
2857
+ */
2858
+ static hasTag(tag) {
2859
+ if (tag.length > 64) {
2860
+ throw new Error("Tag must be 64 characters or less");
2861
+ }
2862
+ return new FilterExpression({ hasTag: tag });
2863
+ }
2864
+ /**
2865
+ * Filter users with any of the specified tags
2866
+ * @param tags - Array of tag names (1-20 tags)
2867
+ */
2868
+ static hasAnyTag(tags) {
2869
+ if (tags.length === 0 || tags.length > 20) {
2870
+ throw new Error("hasAnyTag requires 1-20 tags");
2871
+ }
2872
+ return new FilterExpression({ hasAnyTag: tags });
2873
+ }
2874
+ /**
2875
+ * Filter users with all of the specified tags
2876
+ * @param tags - Array of tag names (1-20 tags)
2877
+ */
2878
+ static hasAllTags(tags) {
2879
+ if (tags.length === 0 || tags.length > 20) {
2880
+ throw new Error("hasAllTags requires 1-20 tags");
2881
+ }
2882
+ return new FilterExpression({ hasAllTags: tags });
2883
+ }
2884
+ // ===========================================================================
2885
+ // Time-based Filters
2886
+ // ===========================================================================
2887
+ /**
2888
+ * Filter users created after a specific date
2889
+ * @param date - Date or ISO 8601 string
2890
+ */
2891
+ static createdAfter(date) {
2892
+ const isoString = date instanceof Date ? date.toISOString() : date;
2893
+ return new FilterExpression({ createdAfter: isoString });
2894
+ }
2895
+ /**
2896
+ * Filter users created before a specific date
2897
+ * @param date - Date or ISO 8601 string
2898
+ */
2899
+ static createdBefore(date) {
2900
+ const isoString = date instanceof Date ? date.toISOString() : date;
2901
+ return new FilterExpression({ createdBefore: isoString });
2902
+ }
2903
+ /**
2904
+ * Filter users who were last active after a specific date
2905
+ * @param date - Date or ISO 8601 string
2906
+ */
2907
+ static lastActiveAfter(date) {
2908
+ const isoString = date instanceof Date ? date.toISOString() : date;
2909
+ return new FilterExpression({ lastActiveAfter: isoString });
2910
+ }
2911
+ // ===========================================================================
2912
+ // Reward/Quest Filters
2913
+ // ===========================================================================
2914
+ /**
2915
+ * Filter users who completed a specific reward
2916
+ * @param rewardId - Reward ID
2917
+ */
2918
+ static completedReward(rewardId) {
2919
+ return new FilterExpression({ completedReward: rewardId });
2920
+ }
2921
+ /**
2922
+ * Filter users who have NOT completed a specific reward
2923
+ * @param rewardId - Reward ID
2924
+ */
2925
+ static notCompletedReward(rewardId) {
2926
+ return new FilterExpression({ notCompletedReward: rewardId });
2927
+ }
2928
+ /**
2929
+ * Filter users who completed a specific quest
2930
+ * @param questId - Quest ID
2931
+ */
2932
+ static completedQuest(questId) {
2933
+ return new FilterExpression({ completedQuest: questId });
2934
+ }
2935
+ /**
2936
+ * Filter users who have a specific achievement
2937
+ * @param achievementId - Achievement ID
2938
+ */
2939
+ static hasAchievement(achievementId) {
2940
+ return new FilterExpression({ hasAchievement: achievementId });
2941
+ }
2942
+ // ===========================================================================
2943
+ // Geography Filters
2944
+ // ===========================================================================
2945
+ /**
2946
+ * Filter users by timezone
2947
+ * @param zones - Array of timezone options with UTC offset and optional range (max 24)
2948
+ */
2949
+ static timezones(zones) {
2950
+ if (zones.length === 0) {
2951
+ throw new Error("timezones requires at least one timezone");
2952
+ }
2953
+ if (zones.length > 24) {
2954
+ throw new Error("timezones cannot exceed 24 entries");
2955
+ }
2956
+ for (const zone of zones) {
2957
+ if (zone.offset < -12 || zone.offset > 14) {
2958
+ throw new Error("timezone offset must be between -12 and 14");
2959
+ }
2960
+ if (zone.range !== void 0 && (zone.range < 0 || zone.range > 12)) {
2961
+ throw new Error("timezone range must be between 0 and 12");
2962
+ }
2963
+ }
2964
+ return new FilterExpression({ timezones: zones });
2965
+ }
2966
+ /**
2967
+ * Filter users by country (ISO 3166-1 alpha-2 codes)
2968
+ * @param codes - Array of ISO 3166-1 alpha-2 country codes (e.g., ['US', 'CA', 'GB'])
2969
+ */
2970
+ static countries(codes) {
2971
+ if (codes.length === 0) {
2972
+ throw new Error("countries requires at least one country code");
2973
+ }
2974
+ if (codes.length > 50) {
2975
+ throw new Error("countries cannot exceed 50 country codes");
2976
+ }
2977
+ for (const code of codes) {
2978
+ if (!/^[A-Z]{2}$/.test(code)) {
2979
+ throw new Error("Country codes must be ISO 3166-1 alpha-2 format (e.g., US, CA, GB)");
2980
+ }
2981
+ }
2982
+ return new FilterExpression({ countries: codes.map((code) => ({ code })) });
2983
+ }
2984
+ // ===========================================================================
2985
+ // Engagement & Quality Filters
2986
+ // ===========================================================================
2987
+ /**
2988
+ * Filter users with quotient score >= minimum
2989
+ * @param score - Minimum quotient score (0-1, where 0.5=Casual, 0.75=Influential, 0.9=Exceptional)
2990
+ */
2991
+ static quotientScoreMin(score) {
2992
+ if (score < 0 || score > 1) {
2993
+ throw new Error("quotientScoreMin must be between 0 and 1");
2994
+ }
2995
+ return new FilterExpression({ quotientScoreMin: score });
2996
+ }
2997
+ /**
2998
+ * Filter users with quotient score <= maximum
2999
+ * @param score - Maximum quotient score (0-1)
3000
+ */
3001
+ static quotientScoreMax(score) {
3002
+ if (score < 0 || score > 1) {
3003
+ throw new Error("quotientScoreMax must be between 0 and 1");
3004
+ }
3005
+ return new FilterExpression({ quotientScoreMax: score });
3006
+ }
3007
+ /**
3008
+ * Filter users with quotient score in a range
3009
+ * @param min - Minimum score (0-1, optional)
3010
+ * @param max - Maximum score (0-1, optional)
3011
+ */
3012
+ static quotientScoreRange(min, max) {
3013
+ if (min !== void 0 && (min < 0 || min > 1)) {
3014
+ throw new Error("quotientScoreRange min must be between 0 and 1");
3015
+ }
3016
+ if (max !== void 0 && (max < 0 || max > 1)) {
3017
+ throw new Error("quotientScoreRange max must be between 0 and 1");
3018
+ }
3019
+ const range = {};
3020
+ if (min !== void 0) range.min = min;
3021
+ if (max !== void 0) range.max = max;
3022
+ return new FilterExpression({ quotientScoreRange: range });
3023
+ }
3024
+ /**
3025
+ * Filter users with minimum battery percentage
3026
+ * @param pct - Minimum battery percentage (0-100)
3027
+ */
3028
+ static minBatteryPercentage(pct) {
3029
+ if (pct < 0 || pct > 100) {
3030
+ throw new Error("minBatteryPercentage must be between 0 and 100");
3031
+ }
3032
+ return new FilterExpression({ minBatteryPercentage: pct });
3033
+ }
3034
+ /**
3035
+ * Filter users who have recharged their battery within the last N days
3036
+ * @param days - Number of days
3037
+ */
3038
+ static hasRechargedInLastDays(days) {
3039
+ if (days < 1) {
3040
+ throw new Error("hasRechargedInLastDays must be >= 1");
3041
+ }
3042
+ return new FilterExpression({ hasRechargedInLastDays: days });
3043
+ }
3044
+ /**
3045
+ * Exclude users who have already been pinged today
3046
+ */
3047
+ static excludePingedToday() {
3048
+ return new FilterExpression({ excludePingedToday: true });
3049
+ }
3050
+ // ===========================================================================
3051
+ // Opt-In Filters
3052
+ // ===========================================================================
3053
+ /**
3054
+ * Filter to only users who have opted into lottery rewards
3055
+ */
3056
+ static requireLotteryOptIn() {
3057
+ return new FilterExpression({ requireLotteryOptIn: true });
3058
+ }
3059
+ /**
3060
+ * Filter to only users who have opted into quiz rewards
3061
+ */
3062
+ static requireQuizOptIn() {
3063
+ return new FilterExpression({ requireQuizOptIn: true });
3064
+ }
3065
+ // ===========================================================================
3066
+ // Activity Count Filters
3067
+ // ===========================================================================
3068
+ /**
3069
+ * Filter users with minimum cast count
3070
+ * @param count - Minimum number of casts
3071
+ */
3072
+ static minCastCount(count) {
3073
+ if (count < 0) {
3074
+ throw new Error("minCastCount must be >= 0");
3075
+ }
3076
+ return new FilterExpression({ minCastCount: count });
3077
+ }
3078
+ // ===========================================================================
3079
+ // Ordering & Pagination
3080
+ // ===========================================================================
3081
+ /**
3082
+ * Set the order for results
3083
+ * @param order - Order option for sorting results
3084
+ */
3085
+ static orderBy(order) {
3086
+ return new FilterExpression({ orderBy: order });
3087
+ }
3088
+ /**
3089
+ * Limit the number of results
3090
+ * @param n - Maximum number of results
3091
+ * @deprecated Limit is controlled by budgetCap, not this filter. This method will be removed.
3092
+ */
3093
+ static limit(n) {
3094
+ console.warn("FilterBuilder.limit() is deprecated. Use budgetCap to control result count.");
3095
+ if (n < 1 || n > 1e4) {
3096
+ throw new Error("limit must be between 1 and 10000");
3097
+ }
3098
+ return new FilterExpression({ limit: n });
3099
+ }
3100
+ };
3101
+ var GasTierSchema = z.enum(["slow", "standard", "fast"]);
3102
+ var QuoteOptionsSchema = z.object({
3103
+ draftId: z.string().uuid(),
3104
+ amount: z.string().regex(/^\d+(\.\d+)?$/).optional(),
3105
+ gasTier: GasTierSchema.optional(),
3106
+ slippageTolerance: z.number().min(0).max(1).optional()
3107
+ });
3108
+ var QuoteRecipientSchema = z.object({
3109
+ address: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address"),
3110
+ amount: z.string(),
3111
+ share: z.number().min(0).max(1)
3112
+ });
3113
+ var QuoteSchema2 = z.object({
3114
+ id: z.string().uuid(),
3115
+ draftId: z.string().uuid(),
3116
+ totalAmount: z.string(),
3117
+ estimatedGas: z.string(),
3118
+ gasPrice: z.string(),
3119
+ networkFee: z.string(),
3120
+ platformFee: z.string(),
3121
+ totalCost: z.string(),
3122
+ token: TokenTypeSchema,
3123
+ network: NetworkSchema,
3124
+ recipients: z.array(QuoteRecipientSchema),
3125
+ recipientCount: z.number().int().positive(),
3126
+ expiresAt: z.string().datetime(),
3127
+ createdAt: z.string().datetime(),
3128
+ isValid: z.boolean()
3129
+ });
3130
+ var ConfirmResultSchema2 = z.object({
3131
+ valid: z.boolean(),
3132
+ invalidReason: z.string().optional(),
3133
+ currentGasEstimate: z.string(),
3134
+ priceChange: z.string(),
3135
+ canExecute: z.boolean()
3136
+ });
3137
+ var ExecuteStatusSchema = z.enum(["pending", "confirmed", "failed"]);
3138
+ var ExecuteResultSchema2 = z.object({
3139
+ id: z.string().uuid(),
3140
+ quoteId: z.string().uuid(),
3141
+ transactionHash: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
3142
+ status: ExecuteStatusSchema,
3143
+ blockNumber: z.number().int().positive().optional(),
3144
+ initiatedAt: z.string().datetime(),
3145
+ confirmedAt: z.string().datetime().optional(),
3146
+ confirmations: z.number().int().min(0),
3147
+ receiptId: z.string().uuid().optional()
3148
+ });
3149
+ function validateQuoteOptions(options) {
3150
+ return QuoteOptionsSchema.safeParse(options);
3151
+ }
3152
+ function parseQuoteOptions(options) {
3153
+ return QuoteOptionsSchema.parse(options);
3154
+ }
3155
+ function validateQuote(quote) {
3156
+ return QuoteSchema2.safeParse(quote);
3157
+ }
3158
+ function parseQuote(quote) {
3159
+ return QuoteSchema2.parse(quote);
3160
+ }
3161
+ function validateExecuteResult(result) {
3162
+ return ExecuteResultSchema2.safeParse(result);
3163
+ }
3164
+ function parseExecuteResult(result) {
3165
+ return ExecuteResultSchema2.parse(result);
3166
+ }
3167
+ var TransferStatusSchema = z.enum(["success", "failed"]);
3168
+ var ReceiptTransferSchema = z.object({
3169
+ recipient: z.string().regex(/^0x[a-fA-F0-9]{40}$/),
3170
+ amount: z.string(),
3171
+ status: TransferStatusSchema,
3172
+ logIndex: z.number().int().min(0)
3173
+ });
3174
+ var ReceiptSchema2 = z.object({
3175
+ id: z.string().uuid(),
3176
+ executionId: z.string().uuid(),
3177
+ quoteId: z.string().uuid(),
3178
+ draftId: z.string().uuid(),
3179
+ transactionHash: z.string().regex(/^0x[a-fA-F0-9]{64}$/),
3180
+ blockNumber: z.number().int().positive(),
3181
+ blockTimestamp: z.string().datetime(),
3182
+ network: NetworkSchema,
3183
+ token: TokenTypeSchema,
3184
+ totalAmount: z.string(),
3185
+ gasUsed: z.string(),
3186
+ gasPrice: z.string(),
3187
+ gasCost: z.string(),
3188
+ transfers: z.array(ReceiptTransferSchema),
3189
+ successfulTransfers: z.number().int().min(0),
3190
+ failedTransfers: z.number().int().min(0),
3191
+ createdAt: z.string().datetime()
3192
+ });
3193
+ function validateReceipt(receipt) {
3194
+ return ReceiptSchema2.safeParse(receipt);
3195
+ }
3196
+ function parseReceipt(receipt) {
3197
+ return ReceiptSchema2.parse(receipt);
3198
+ }
3199
+
3200
+ export { API_BASE_URLS, API_KEY_PREFIXES, ActiveInLastDaysFilterSchema, AgentClient, ApiQuoteResponseSchema, ApiReceiptResponseSchema, BeeperClient, BeeperEconomicsFilterSchema, BeeperError, CachedTokenHolderFilterSchema, CachedTokenHolderSchema, ConfirmDepositResponseSchema, ConfirmResultSchema2 as ConfirmResultSchema, CountryFilterSchema, DistributionStrategySchema, DraftInputSchema, DraftSchema, DraftStatusSchema, DraftUpdateSchema, ENDPOINTS, ErrorCodes, ExcludePingedTodayFilterSchema, ExcludeUsersFilterSchema, ExecuteResultSchema2 as ExecuteResultSchema, ExecuteSendResponseSchema, ExecuteStatusSchema, FILTER_SCHEMA, FieldComparisonSchema, FilterBuilder, FilterExpression, FilterExpressionSchema, FilterOperatorSchema, FilterValueSchema, FollowersOfFilterSchema, FollowingOfFilterSchema, GasTierSchema, HEADERS, HTTP_STATUS_TO_ERROR_CODE, HasBaseWalletFilterSchema, HasRechargedInLastDaysFilterSchema, HasTierFilterSchema, HasVerifiedWalletFilterSchema, HttpClient, IsWaitlistedFilterSchema, LegacyFieldComparisonSchema, LegacyFilterOperatorSchema, LegacyFilterValueSchema, LegacyRecipientFilterDSLSchema, MaxAttentionPriceFilterSchema, MaxFidFilterSchema, MaxFollowersFilterSchema, MaxFollowingFilterSchema, MinAttentionPriceFilterSchema, MinBatteryPercentageFilterSchema, MinCastCountFilterSchema, MinClickThroughRateFilterSchema, MinFidFilterSchema, MinFollowersFilterSchema, MinFollowingFilterSchema, MinProTenureDaysFilterSchema, MinTenureDaysFilterSchema, MutualsWithFilterSchema, NetworkSchema, NeynarScoreMaxFilterSchema, NeynarScoreMinFilterSchema, OnchainFilterSchema, OrderBySchema, PlatformFilterSchema, ProSubscriptionFilterSchema, QUOTE_EXPIRATION_SECONDS, QuoteOptionsSchema, QuoteRecipientSchema, QuoteSchema2 as QuoteSchema, QuotientScoreMaxFilterSchema, QuotientScoreMinFilterSchema, RETRYABLE_ERROR_CODES, RETRY_CONFIG, ReceiptSchema2 as ReceiptSchema, ReceiptTransactionSchema, ReceiptTransferSchema, RecipientFilterDSLSchema, RecipientFilterSchema, ReputationFilterSchema, RequireLotteryOptInFilterSchema, RequireQuizOptInFilterSchema, RolesFilterSchema, SDK_VERSION, SignalTokenFilterSchema, SocialFilterSchema, SpamLabelFilterSchema, SpecificUsersFilterSchema, TIMEOUTS, TimezoneFilterSchema, TokenHolderDiscoverySchema, TokenHolderFilterSchema, TokenTypeSchema, TransferStatusSchema, VerifiedOnlyFilterSchema, createAuthorizationHeader, createHttpConfig, BeeperClient_default as default, describeFilters, generateDepositIdempotencyKey, generateExecuteIdempotencyKey, generateFilterDocumentation, generateIdempotencyKey, getAllFilterNames, getApiKeyEnvironment, getFilterSchema, isRetryableCode, isValidApiKeyFormat, maskApiKey, parseDraft, parseDraftInput, parseExecuteResult, parseFilter, parseQuote, parseQuoteOptions, parseReceipt, parseRecipientFilter, safeParseFilter, safeParseRecipientFilter, confirmDeposit as sendConfirmDeposit, createDraft as sendCreateDraft, createQuote as sendCreateQuote, executeSend as sendExecuteSend, getEstimatedTimeRemaining as sendGetEstimatedTimeRemaining, getFailedTransactions as sendGetFailedTransactions, getQuote as sendGetQuote, getReceipt as sendGetReceipt, getSuccessRate as sendGetSuccessRate, isComplete as sendIsComplete, isDepositSufficient as sendIsDepositSufficient, isExecuting as sendIsExecuting, isQuoteExpired as sendIsQuoteExpired, isReadyForDeposit as sendIsReadyForDeposit, isReadyForQuote as sendIsReadyForQuote, isSuccess as sendIsSuccess, pollUntilComplete as sendPollUntilComplete, updateDraft as sendUpdateDraft, validateDraftInput2 as sendValidateDraftInput, validateDraft, validateDraftInput, validateExecuteResult, validateFilter, validateFilterHasTargeting, validateQuote, validateQuoteOptions, validateReceipt, validateRecipientFilter };
3201
+ //# sourceMappingURL=index.js.map
3202
+ //# sourceMappingURL=index.js.map