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