@drip-sdk/node 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -494
- package/dist/core.cjs +3 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +762 -0
- package/dist/core.d.ts +760 -0
- package/dist/core.js +3 -0
- package/dist/core.js.map +1 -0
- package/dist/express.cjs +2 -2
- package/dist/express.cjs.map +1 -1
- package/dist/express.d.cts +3 -3
- package/dist/express.d.ts +3 -3
- package/dist/express.js +2 -2
- package/dist/express.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1266 -66
- package/dist/index.d.ts +1266 -66
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +3 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +290 -0
- package/dist/langchain.d.ts +290 -0
- package/dist/langchain.js +3 -0
- package/dist/langchain.js.map +1 -0
- package/dist/middleware.cjs +2 -2
- package/dist/middleware.cjs.map +1 -1
- package/dist/middleware.d.cts +3 -3
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.js +2 -2
- package/dist/middleware.js.map +1 -1
- package/dist/next.cjs +2 -2
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +3 -3
- package/dist/next.d.ts +3 -3
- package/dist/next.js +2 -2
- package/dist/next.js.map +1 -1
- package/dist/{types-D8mMON4v.d.ts → types-B2qwDadD.d.ts} +1 -1
- package/dist/{types-92iVqLtE.d.cts → types-Bo8SiUdl.d.cts} +1 -1
- package/package.json +23 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamMeter - Accumulate usage locally and charge once at the end.
|
|
3
|
+
*
|
|
4
|
+
* Perfect for LLM token streaming and other high-frequency metering scenarios
|
|
5
|
+
* where you want to avoid making an API call for every small increment.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const meter = drip.createStreamMeter({
|
|
10
|
+
* customerId: 'cust_abc123',
|
|
11
|
+
* meter: 'tokens',
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* for await (const chunk of llmStream) {
|
|
15
|
+
* meter.add(chunk.tokens);
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* const result = await meter.flush();
|
|
19
|
+
* console.log(`Charged ${result.charge.amountUsdc} for ${result.quantity} tokens`);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Options for creating a StreamMeter.
|
|
25
|
+
*/
|
|
26
|
+
interface StreamMeterOptions {
|
|
27
|
+
/**
|
|
28
|
+
* The Drip customer ID to charge.
|
|
29
|
+
*/
|
|
30
|
+
customerId: string;
|
|
31
|
+
/**
|
|
32
|
+
* The usage meter/type to record against.
|
|
33
|
+
* Must match a meter configured in your pricing plan.
|
|
34
|
+
*/
|
|
35
|
+
meter: string;
|
|
36
|
+
/**
|
|
37
|
+
* Unique key to prevent duplicate charges.
|
|
38
|
+
* If not provided, one will be generated.
|
|
39
|
+
*/
|
|
40
|
+
idempotencyKey?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Additional metadata to attach to the charge.
|
|
43
|
+
*/
|
|
44
|
+
metadata?: Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Auto-flush when accumulated quantity reaches this threshold.
|
|
47
|
+
* Useful for long-running streams where you want periodic charges.
|
|
48
|
+
*/
|
|
49
|
+
flushThreshold?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Callback invoked on each add() call.
|
|
52
|
+
* Useful for logging or progress tracking.
|
|
53
|
+
*/
|
|
54
|
+
onAdd?: (quantity: number, total: number) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Callback invoked after each successful flush.
|
|
57
|
+
*/
|
|
58
|
+
onFlush?: (result: StreamMeterFlushResult) => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Result of flushing a StreamMeter.
|
|
62
|
+
*/
|
|
63
|
+
interface StreamMeterFlushResult {
|
|
64
|
+
/** Whether the flush was successful */
|
|
65
|
+
success: boolean;
|
|
66
|
+
/** The quantity that was charged */
|
|
67
|
+
quantity: number;
|
|
68
|
+
/** The charge result from the API (if quantity > 0) */
|
|
69
|
+
charge: ChargeResult['charge'] | null;
|
|
70
|
+
/** Whether this was an idempotent replay */
|
|
71
|
+
isReplay: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Internal charge function type (injected by Drip class).
|
|
75
|
+
*/
|
|
76
|
+
type ChargeFn = (params: ChargeParams) => Promise<ChargeResult>;
|
|
77
|
+
/**
|
|
78
|
+
* StreamMeter accumulates usage locally and charges once when flushed.
|
|
79
|
+
*
|
|
80
|
+
* This is ideal for:
|
|
81
|
+
* - LLM token streaming (charge once at end of stream)
|
|
82
|
+
* - High-frequency events (batch small increments)
|
|
83
|
+
* - Partial failure handling (charge for what was delivered)
|
|
84
|
+
*/
|
|
85
|
+
declare class StreamMeter {
|
|
86
|
+
private _total;
|
|
87
|
+
private _flushed;
|
|
88
|
+
private _flushCount;
|
|
89
|
+
private readonly _chargeFn;
|
|
90
|
+
private readonly _options;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a new StreamMeter.
|
|
93
|
+
*
|
|
94
|
+
* @param chargeFn - The charge function from Drip client
|
|
95
|
+
* @param options - StreamMeter configuration
|
|
96
|
+
*/
|
|
97
|
+
constructor(chargeFn: ChargeFn, options: StreamMeterOptions);
|
|
98
|
+
/**
|
|
99
|
+
* Current accumulated quantity (not yet charged).
|
|
100
|
+
*/
|
|
101
|
+
get total(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Whether this meter has been flushed at least once.
|
|
104
|
+
*/
|
|
105
|
+
get isFlushed(): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Number of times this meter has been flushed.
|
|
108
|
+
*/
|
|
109
|
+
get flushCount(): number;
|
|
110
|
+
/**
|
|
111
|
+
* Add quantity to the accumulated total.
|
|
112
|
+
*
|
|
113
|
+
* If a flushThreshold is set and the total exceeds it,
|
|
114
|
+
* this will automatically trigger a flush.
|
|
115
|
+
*
|
|
116
|
+
* @param quantity - Amount to add (must be positive)
|
|
117
|
+
* @returns Promise that resolves when add completes (may trigger auto-flush)
|
|
118
|
+
*/
|
|
119
|
+
add(quantity: number): Promise<StreamMeterFlushResult | null>;
|
|
120
|
+
/**
|
|
121
|
+
* Synchronously add quantity without auto-flush.
|
|
122
|
+
* Use this for maximum performance when you don't need threshold-based flushing.
|
|
123
|
+
*
|
|
124
|
+
* @param quantity - Amount to add (must be positive)
|
|
125
|
+
*/
|
|
126
|
+
addSync(quantity: number): void;
|
|
127
|
+
/**
|
|
128
|
+
* Flush accumulated usage and charge the customer.
|
|
129
|
+
*
|
|
130
|
+
* If total is 0, returns a success result with no charge.
|
|
131
|
+
* After flush, the meter resets to 0 and can be reused.
|
|
132
|
+
*
|
|
133
|
+
* @returns The flush result including charge details
|
|
134
|
+
*/
|
|
135
|
+
flush(): Promise<StreamMeterFlushResult>;
|
|
136
|
+
/**
|
|
137
|
+
* Reset the meter without charging.
|
|
138
|
+
* Use this to discard accumulated usage (e.g., on error before delivery).
|
|
139
|
+
*/
|
|
140
|
+
reset(): void;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Production-grade resilience patterns for the Drip SDK.
|
|
145
|
+
*
|
|
146
|
+
* This module provides:
|
|
147
|
+
* - Rate limiting (token bucket algorithm)
|
|
148
|
+
* - Retry with exponential backoff
|
|
149
|
+
* - Circuit breaker pattern
|
|
150
|
+
* - Request metrics and observability
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Configuration for rate limiting.
|
|
154
|
+
*/
|
|
155
|
+
interface RateLimiterConfig {
|
|
156
|
+
/**
|
|
157
|
+
* Maximum requests per second.
|
|
158
|
+
* @default 100
|
|
159
|
+
*/
|
|
160
|
+
requestsPerSecond: number;
|
|
161
|
+
/**
|
|
162
|
+
* Maximum burst size (bucket capacity).
|
|
163
|
+
* @default 200
|
|
164
|
+
*/
|
|
165
|
+
burstSize: number;
|
|
166
|
+
/**
|
|
167
|
+
* Whether rate limiting is enabled.
|
|
168
|
+
* @default true
|
|
169
|
+
*/
|
|
170
|
+
enabled: boolean;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Thread-safe token bucket rate limiter.
|
|
174
|
+
*
|
|
175
|
+
* Allows bursting up to `burstSize` requests, then limits to
|
|
176
|
+
* `requestsPerSecond` sustained rate.
|
|
177
|
+
*/
|
|
178
|
+
declare class RateLimiter {
|
|
179
|
+
private readonly config;
|
|
180
|
+
private tokens;
|
|
181
|
+
private lastRefill;
|
|
182
|
+
constructor(config?: Partial<RateLimiterConfig>);
|
|
183
|
+
/**
|
|
184
|
+
* Refill tokens based on elapsed time.
|
|
185
|
+
*/
|
|
186
|
+
private refill;
|
|
187
|
+
/**
|
|
188
|
+
* Acquire a token, blocking if necessary.
|
|
189
|
+
*
|
|
190
|
+
* @param timeout - Maximum time to wait for a token in ms (undefined = wait forever)
|
|
191
|
+
* @returns Promise that resolves to true if token acquired, false if timeout
|
|
192
|
+
*/
|
|
193
|
+
acquire(timeout?: number): Promise<boolean>;
|
|
194
|
+
/**
|
|
195
|
+
* Try to acquire a token without waiting.
|
|
196
|
+
*
|
|
197
|
+
* @returns true if token acquired, false otherwise
|
|
198
|
+
*/
|
|
199
|
+
tryAcquire(): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Get current number of available tokens.
|
|
202
|
+
*/
|
|
203
|
+
get availableTokens(): number;
|
|
204
|
+
private sleep;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Configuration for retry behavior.
|
|
208
|
+
*/
|
|
209
|
+
interface RetryConfig {
|
|
210
|
+
/**
|
|
211
|
+
* Maximum number of retry attempts.
|
|
212
|
+
* @default 3
|
|
213
|
+
*/
|
|
214
|
+
maxRetries: number;
|
|
215
|
+
/**
|
|
216
|
+
* Base delay in milliseconds.
|
|
217
|
+
* @default 100
|
|
218
|
+
*/
|
|
219
|
+
baseDelayMs: number;
|
|
220
|
+
/**
|
|
221
|
+
* Maximum delay in milliseconds.
|
|
222
|
+
* @default 10000
|
|
223
|
+
*/
|
|
224
|
+
maxDelayMs: number;
|
|
225
|
+
/**
|
|
226
|
+
* Exponential backoff base.
|
|
227
|
+
* @default 2
|
|
228
|
+
*/
|
|
229
|
+
exponentialBase: number;
|
|
230
|
+
/**
|
|
231
|
+
* Random jitter factor (0-1).
|
|
232
|
+
* @default 0.1
|
|
233
|
+
*/
|
|
234
|
+
jitter: number;
|
|
235
|
+
/**
|
|
236
|
+
* HTTP status codes that should trigger retry.
|
|
237
|
+
* @default [429, 500, 502, 503, 504]
|
|
238
|
+
*/
|
|
239
|
+
retryableStatusCodes: number[];
|
|
240
|
+
/**
|
|
241
|
+
* Whether retry is enabled.
|
|
242
|
+
* @default true
|
|
243
|
+
*/
|
|
244
|
+
enabled: boolean;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Error thrown when all retry attempts have been exhausted.
|
|
248
|
+
*/
|
|
249
|
+
declare class RetryExhaustedError extends Error {
|
|
250
|
+
readonly attempts: number;
|
|
251
|
+
readonly lastError: Error;
|
|
252
|
+
constructor(attempts: number, lastError: Error);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Calculate backoff delay for a given attempt.
|
|
256
|
+
*/
|
|
257
|
+
declare function calculateBackoff(attempt: number, config: RetryConfig): number;
|
|
258
|
+
/**
|
|
259
|
+
* Check if an error is retryable based on configuration.
|
|
260
|
+
*/
|
|
261
|
+
declare function isRetryableError(error: unknown, config: RetryConfig): boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Circuit breaker states.
|
|
264
|
+
*/
|
|
265
|
+
type CircuitState = 'closed' | 'open' | 'half_open';
|
|
266
|
+
/**
|
|
267
|
+
* Configuration for circuit breaker.
|
|
268
|
+
*/
|
|
269
|
+
interface CircuitBreakerConfig {
|
|
270
|
+
/**
|
|
271
|
+
* Number of failures before opening circuit.
|
|
272
|
+
* @default 5
|
|
273
|
+
*/
|
|
274
|
+
failureThreshold: number;
|
|
275
|
+
/**
|
|
276
|
+
* Number of successes in half-open to close circuit.
|
|
277
|
+
* @default 2
|
|
278
|
+
*/
|
|
279
|
+
successThreshold: number;
|
|
280
|
+
/**
|
|
281
|
+
* Milliseconds to wait before transitioning from open to half-open.
|
|
282
|
+
* @default 30000
|
|
283
|
+
*/
|
|
284
|
+
timeoutMs: number;
|
|
285
|
+
/**
|
|
286
|
+
* Whether circuit breaker is enabled.
|
|
287
|
+
* @default true
|
|
288
|
+
*/
|
|
289
|
+
enabled: boolean;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Error thrown when circuit breaker is open.
|
|
293
|
+
*/
|
|
294
|
+
declare class CircuitBreakerOpenError extends Error {
|
|
295
|
+
readonly circuitName: string;
|
|
296
|
+
readonly timeUntilRetryMs: number;
|
|
297
|
+
constructor(circuitName: string, timeUntilRetryMs: number);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Circuit breaker pattern implementation.
|
|
301
|
+
*
|
|
302
|
+
* Prevents cascading failures by failing fast when a service is unhealthy.
|
|
303
|
+
*/
|
|
304
|
+
declare class CircuitBreaker {
|
|
305
|
+
readonly name: string;
|
|
306
|
+
private readonly config;
|
|
307
|
+
private state;
|
|
308
|
+
private failureCount;
|
|
309
|
+
private successCount;
|
|
310
|
+
private lastFailureTime;
|
|
311
|
+
constructor(name: string, config?: Partial<CircuitBreakerConfig>);
|
|
312
|
+
/**
|
|
313
|
+
* Get current circuit state.
|
|
314
|
+
*/
|
|
315
|
+
getState(): CircuitState;
|
|
316
|
+
/**
|
|
317
|
+
* Check if state should transition based on timeout.
|
|
318
|
+
*/
|
|
319
|
+
private checkStateTransition;
|
|
320
|
+
/**
|
|
321
|
+
* Record a successful call.
|
|
322
|
+
*/
|
|
323
|
+
recordSuccess(): void;
|
|
324
|
+
/**
|
|
325
|
+
* Record a failed call.
|
|
326
|
+
*/
|
|
327
|
+
recordFailure(): void;
|
|
328
|
+
/**
|
|
329
|
+
* Check if a request should be allowed.
|
|
330
|
+
*/
|
|
331
|
+
allowRequest(): boolean;
|
|
332
|
+
/**
|
|
333
|
+
* Get milliseconds until circuit transitions to half-open.
|
|
334
|
+
*/
|
|
335
|
+
getTimeUntilRetry(): number;
|
|
336
|
+
/**
|
|
337
|
+
* Reset the circuit breaker to initial state.
|
|
338
|
+
*/
|
|
339
|
+
reset(): void;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Metrics for a single request.
|
|
343
|
+
*/
|
|
344
|
+
interface RequestMetrics {
|
|
345
|
+
method: string;
|
|
346
|
+
endpoint: string;
|
|
347
|
+
statusCode: number | null;
|
|
348
|
+
durationMs: number;
|
|
349
|
+
success: boolean;
|
|
350
|
+
timestamp: number;
|
|
351
|
+
error?: string;
|
|
352
|
+
retryCount: number;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Aggregated metrics summary.
|
|
356
|
+
*/
|
|
357
|
+
interface MetricsSummary {
|
|
358
|
+
windowSize: number;
|
|
359
|
+
totalRequests: number;
|
|
360
|
+
totalSuccesses: number;
|
|
361
|
+
totalFailures: number;
|
|
362
|
+
successRate: number;
|
|
363
|
+
avgLatencyMs: number;
|
|
364
|
+
minLatencyMs: number;
|
|
365
|
+
maxLatencyMs: number;
|
|
366
|
+
p50LatencyMs: number;
|
|
367
|
+
p95LatencyMs: number;
|
|
368
|
+
p99LatencyMs: number;
|
|
369
|
+
requestsByEndpoint: Record<string, number>;
|
|
370
|
+
errorsByType: Record<string, number>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Collects and aggregates request metrics.
|
|
374
|
+
*
|
|
375
|
+
* Thread-safe metrics collection with windowed aggregation.
|
|
376
|
+
*/
|
|
377
|
+
declare class MetricsCollector {
|
|
378
|
+
private readonly windowSize;
|
|
379
|
+
private readonly metrics;
|
|
380
|
+
private totalRequests;
|
|
381
|
+
private totalSuccesses;
|
|
382
|
+
private totalFailures;
|
|
383
|
+
constructor(windowSize?: number);
|
|
384
|
+
/**
|
|
385
|
+
* Record a request's metrics.
|
|
386
|
+
*/
|
|
387
|
+
record(metrics: RequestMetrics): void;
|
|
388
|
+
/**
|
|
389
|
+
* Get aggregated metrics summary.
|
|
390
|
+
*/
|
|
391
|
+
getSummary(): MetricsSummary;
|
|
392
|
+
/**
|
|
393
|
+
* Reset all metrics.
|
|
394
|
+
*/
|
|
395
|
+
reset(): void;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Combined configuration for all resilience features.
|
|
399
|
+
*/
|
|
400
|
+
interface ResilienceConfig {
|
|
401
|
+
rateLimiter: RateLimiterConfig;
|
|
402
|
+
retry: RetryConfig;
|
|
403
|
+
circuitBreaker: CircuitBreakerConfig;
|
|
404
|
+
collectMetrics: boolean;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Create default production configuration.
|
|
408
|
+
*/
|
|
409
|
+
declare function createDefaultResilienceConfig(): ResilienceConfig;
|
|
410
|
+
/**
|
|
411
|
+
* Create configuration with all features disabled.
|
|
412
|
+
*/
|
|
413
|
+
declare function createDisabledResilienceConfig(): ResilienceConfig;
|
|
414
|
+
/**
|
|
415
|
+
* Create configuration optimized for high throughput.
|
|
416
|
+
*/
|
|
417
|
+
declare function createHighThroughputResilienceConfig(): ResilienceConfig;
|
|
418
|
+
/**
|
|
419
|
+
* Health status of resilience components.
|
|
420
|
+
*/
|
|
421
|
+
interface ResilienceHealth {
|
|
422
|
+
circuitBreaker: {
|
|
423
|
+
state: CircuitState;
|
|
424
|
+
timeUntilRetryMs: number;
|
|
425
|
+
};
|
|
426
|
+
rateLimiter: {
|
|
427
|
+
availableTokens: number;
|
|
428
|
+
requestsPerSecond: number;
|
|
429
|
+
};
|
|
430
|
+
metrics: MetricsSummary | null;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Manages all resilience features for the SDK.
|
|
434
|
+
*
|
|
435
|
+
* Provides a unified interface for rate limiting, retry, circuit breaker,
|
|
436
|
+
* and metrics collection.
|
|
437
|
+
*/
|
|
438
|
+
declare class ResilienceManager {
|
|
439
|
+
readonly config: ResilienceConfig;
|
|
440
|
+
readonly rateLimiter: RateLimiter;
|
|
441
|
+
readonly circuitBreaker: CircuitBreaker;
|
|
442
|
+
readonly metrics: MetricsCollector | null;
|
|
443
|
+
constructor(config?: Partial<ResilienceConfig>);
|
|
444
|
+
/**
|
|
445
|
+
* Execute a function with all resilience features.
|
|
446
|
+
*
|
|
447
|
+
* @param fn - The function to execute
|
|
448
|
+
* @param method - HTTP method for metrics
|
|
449
|
+
* @param endpoint - Endpoint for metrics
|
|
450
|
+
* @returns Result of the function
|
|
451
|
+
*/
|
|
452
|
+
execute<T>(fn: () => Promise<T>, method?: string, endpoint?: string): Promise<T>;
|
|
453
|
+
/**
|
|
454
|
+
* Get current metrics summary.
|
|
455
|
+
*/
|
|
456
|
+
getMetrics(): MetricsSummary | null;
|
|
457
|
+
/**
|
|
458
|
+
* Get health status of all resilience components.
|
|
459
|
+
*/
|
|
460
|
+
getHealth(): ResilienceHealth;
|
|
461
|
+
private sleep;
|
|
462
|
+
}
|
|
463
|
+
|
|
1
464
|
/**
|
|
2
465
|
* Drip SDK - Usage-based billing for Node.js
|
|
3
466
|
*
|
|
@@ -7,18 +470,56 @@
|
|
|
7
470
|
*
|
|
8
471
|
* @packageDocumentation
|
|
9
472
|
*/
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Retry options for API calls.
|
|
476
|
+
*/
|
|
477
|
+
interface RetryOptions {
|
|
478
|
+
/**
|
|
479
|
+
* Maximum number of retry attempts.
|
|
480
|
+
* @default 3
|
|
481
|
+
*/
|
|
482
|
+
maxAttempts?: number;
|
|
483
|
+
/**
|
|
484
|
+
* Base delay between retries in milliseconds (exponential backoff).
|
|
485
|
+
* @default 100
|
|
486
|
+
*/
|
|
487
|
+
baseDelayMs?: number;
|
|
488
|
+
/**
|
|
489
|
+
* Maximum delay between retries in milliseconds.
|
|
490
|
+
* @default 5000
|
|
491
|
+
*/
|
|
492
|
+
maxDelayMs?: number;
|
|
493
|
+
/**
|
|
494
|
+
* Custom function to determine if an error is retryable.
|
|
495
|
+
* By default, retries on network errors and 5xx status codes.
|
|
496
|
+
*/
|
|
497
|
+
isRetryable?: (error: unknown) => boolean;
|
|
498
|
+
}
|
|
10
499
|
/**
|
|
11
500
|
* Configuration options for the Drip SDK client.
|
|
501
|
+
*
|
|
502
|
+
* All fields are optional - the SDK will read from environment variables:
|
|
503
|
+
* - `DRIP_API_KEY` - Your Drip API key
|
|
504
|
+
* - `DRIP_BASE_URL` - Override API base URL (optional)
|
|
12
505
|
*/
|
|
13
506
|
interface DripConfig {
|
|
14
507
|
/**
|
|
15
508
|
* Your Drip API key. Obtain this from the Drip dashboard.
|
|
16
|
-
*
|
|
509
|
+
* Falls back to `DRIP_API_KEY` environment variable if not provided.
|
|
510
|
+
*
|
|
511
|
+
* Supports both key types:
|
|
512
|
+
* - **Secret keys** (`sk_live_...` / `sk_test_...`): Full access to all endpoints
|
|
513
|
+
* - **Public keys** (`pk_live_...` / `pk_test_...`): Safe for client-side use.
|
|
514
|
+
* Can access usage, customers, charges, sessions, analytics, etc.
|
|
515
|
+
* Cannot access webhook management, API key management, or feature flags.
|
|
516
|
+
*
|
|
517
|
+
* @example "sk_live_abc123..." or "pk_live_abc123..."
|
|
17
518
|
*/
|
|
18
|
-
apiKey
|
|
519
|
+
apiKey?: string;
|
|
19
520
|
/**
|
|
20
521
|
* Base URL for the Drip API. Defaults to production API.
|
|
21
|
-
*
|
|
522
|
+
* Falls back to `DRIP_BASE_URL` environment variable if not provided.
|
|
22
523
|
* @default "https://api.drip.dev/v1"
|
|
23
524
|
*/
|
|
24
525
|
baseUrl?: string;
|
|
@@ -27,6 +528,36 @@ interface DripConfig {
|
|
|
27
528
|
* @default 30000
|
|
28
529
|
*/
|
|
29
530
|
timeout?: number;
|
|
531
|
+
/**
|
|
532
|
+
* Enable production resilience features (rate limiting, retry with backoff,
|
|
533
|
+
* circuit breaker, metrics).
|
|
534
|
+
*
|
|
535
|
+
* - `true`: Use default production settings (100 req/s, 3 retries)
|
|
536
|
+
* - `'high-throughput'`: Optimized for high throughput (1000 req/s, 2 retries)
|
|
537
|
+
* - `ResilienceConfig`: Custom configuration object
|
|
538
|
+
* - `undefined`/`false`: Disabled (default for backward compatibility)
|
|
539
|
+
*
|
|
540
|
+
* @example
|
|
541
|
+
* ```typescript
|
|
542
|
+
* // Enable with defaults
|
|
543
|
+
* const drip = new Drip({ apiKey: '...', resilience: true });
|
|
544
|
+
*
|
|
545
|
+
* // High throughput mode
|
|
546
|
+
* const drip = new Drip({ apiKey: '...', resilience: 'high-throughput' });
|
|
547
|
+
*
|
|
548
|
+
* // Custom config
|
|
549
|
+
* const drip = new Drip({
|
|
550
|
+
* apiKey: '...',
|
|
551
|
+
* resilience: {
|
|
552
|
+
* rateLimiter: { requestsPerSecond: 500, burstSize: 1000, enabled: true },
|
|
553
|
+
* retry: { maxRetries: 5, enabled: true },
|
|
554
|
+
* circuitBreaker: { failureThreshold: 10, enabled: true },
|
|
555
|
+
* collectMetrics: true,
|
|
556
|
+
* },
|
|
557
|
+
* });
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
resilience?: boolean | 'high-throughput' | Partial<ResilienceConfig>;
|
|
30
561
|
}
|
|
31
562
|
/**
|
|
32
563
|
* Parameters for creating a new customer.
|
|
@@ -53,8 +584,8 @@ interface CreateCustomerParams {
|
|
|
53
584
|
interface Customer {
|
|
54
585
|
/** Unique customer ID in Drip */
|
|
55
586
|
id: string;
|
|
56
|
-
/** Your business ID */
|
|
57
|
-
businessId
|
|
587
|
+
/** Your business ID (optional - may not be returned by all endpoints) */
|
|
588
|
+
businessId?: string;
|
|
58
589
|
/** Your external customer ID (if provided) */
|
|
59
590
|
externalCustomerId: string | null;
|
|
60
591
|
/** Customer's on-chain address */
|
|
@@ -95,12 +626,16 @@ interface ListCustomersResponse {
|
|
|
95
626
|
interface BalanceResult {
|
|
96
627
|
/** Customer ID */
|
|
97
628
|
customerId: string;
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
/** Balance in
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
|
|
629
|
+
/** On-chain address */
|
|
630
|
+
onchainAddress: string;
|
|
631
|
+
/** Balance in USDC (6 decimals) - matches backend field name */
|
|
632
|
+
balanceUsdc: string;
|
|
633
|
+
/** Pending charges in USDC */
|
|
634
|
+
pendingChargesUsdc: string;
|
|
635
|
+
/** Available USDC (balance minus pending) */
|
|
636
|
+
availableUsdc: string;
|
|
637
|
+
/** ISO timestamp of last balance sync */
|
|
638
|
+
lastSyncedAt: string | null;
|
|
104
639
|
}
|
|
105
640
|
/**
|
|
106
641
|
* Parameters for recording usage and charging a customer.
|
|
@@ -122,8 +657,8 @@ interface ChargeParams {
|
|
|
122
657
|
*/
|
|
123
658
|
quantity: number;
|
|
124
659
|
/**
|
|
125
|
-
* Unique key to prevent duplicate charges.
|
|
126
|
-
*
|
|
660
|
+
* Unique key to prevent duplicate charges and map each call to a single event.
|
|
661
|
+
* Auto-generated if not provided. Retrying with the same key returns the original charge.
|
|
127
662
|
* @example "req_abc123"
|
|
128
663
|
*/
|
|
129
664
|
idempotencyKey?: string;
|
|
@@ -160,6 +695,60 @@ interface ChargeResult {
|
|
|
160
695
|
* Possible charge statuses.
|
|
161
696
|
*/
|
|
162
697
|
type ChargeStatus = 'PENDING' | 'SUBMITTED' | 'CONFIRMED' | 'FAILED' | 'REFUNDED';
|
|
698
|
+
/**
|
|
699
|
+
* Parameters for tracking usage without billing.
|
|
700
|
+
* Use this for internal visibility, pilots, or pre-billing tracking.
|
|
701
|
+
*/
|
|
702
|
+
interface TrackUsageParams {
|
|
703
|
+
/**
|
|
704
|
+
* The Drip customer ID to track usage for.
|
|
705
|
+
* @example "cust_abc123"
|
|
706
|
+
*/
|
|
707
|
+
customerId: string;
|
|
708
|
+
/**
|
|
709
|
+
* The meter/usage type (e.g., 'api_calls', 'tokens').
|
|
710
|
+
*/
|
|
711
|
+
meter: string;
|
|
712
|
+
/**
|
|
713
|
+
* The quantity of usage to record.
|
|
714
|
+
*/
|
|
715
|
+
quantity: number;
|
|
716
|
+
/**
|
|
717
|
+
* Unique key to prevent duplicate records.
|
|
718
|
+
*/
|
|
719
|
+
idempotencyKey?: string;
|
|
720
|
+
/**
|
|
721
|
+
* Human-readable unit label (e.g., 'tokens', 'requests').
|
|
722
|
+
*/
|
|
723
|
+
units?: string;
|
|
724
|
+
/**
|
|
725
|
+
* Human-readable description of this usage event.
|
|
726
|
+
*/
|
|
727
|
+
description?: string;
|
|
728
|
+
/**
|
|
729
|
+
* Additional metadata to attach to this usage event.
|
|
730
|
+
*/
|
|
731
|
+
metadata?: Record<string, unknown>;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Result of tracking usage (no billing).
|
|
735
|
+
*/
|
|
736
|
+
interface TrackUsageResult {
|
|
737
|
+
/** Whether the usage was recorded */
|
|
738
|
+
success: boolean;
|
|
739
|
+
/** The usage event ID */
|
|
740
|
+
usageEventId: string;
|
|
741
|
+
/** Customer ID */
|
|
742
|
+
customerId: string;
|
|
743
|
+
/** Usage type that was recorded */
|
|
744
|
+
usageType: string;
|
|
745
|
+
/** Quantity recorded */
|
|
746
|
+
quantity: number;
|
|
747
|
+
/** Whether this customer is internal-only */
|
|
748
|
+
isInternal: boolean;
|
|
749
|
+
/** Confirmation message */
|
|
750
|
+
message: string;
|
|
751
|
+
}
|
|
163
752
|
/**
|
|
164
753
|
* A detailed charge record.
|
|
165
754
|
*/
|
|
@@ -367,7 +956,7 @@ interface CreateWorkflowParams {
|
|
|
367
956
|
/** URL-safe identifier (lowercase alphanumeric with underscores/hyphens) */
|
|
368
957
|
slug: string;
|
|
369
958
|
/** Type of workflow */
|
|
370
|
-
productSurface?: 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';
|
|
959
|
+
productSurface?: 'API' | 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';
|
|
371
960
|
/** Optional description */
|
|
372
961
|
description?: string;
|
|
373
962
|
/** Additional metadata */
|
|
@@ -496,6 +1085,96 @@ interface ListMetersResponse {
|
|
|
496
1085
|
/** Total count */
|
|
497
1086
|
count: number;
|
|
498
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Custom pricing map for cost estimation.
|
|
1090
|
+
* Maps usage type to unit price (e.g., { "api_call": "0.005", "token": "0.0001" })
|
|
1091
|
+
*/
|
|
1092
|
+
type CustomPricing = Record<string, string>;
|
|
1093
|
+
/**
|
|
1094
|
+
* Parameters for estimating costs from historical usage events.
|
|
1095
|
+
*/
|
|
1096
|
+
interface EstimateFromUsageParams {
|
|
1097
|
+
/** Filter to a specific customer (optional) */
|
|
1098
|
+
customerId?: string;
|
|
1099
|
+
/** Start of the period to estimate */
|
|
1100
|
+
periodStart: Date | string;
|
|
1101
|
+
/** End of the period to estimate */
|
|
1102
|
+
periodEnd: Date | string;
|
|
1103
|
+
/** Default price for usage types without pricing plans */
|
|
1104
|
+
defaultUnitPrice?: string;
|
|
1105
|
+
/** Include events that already have charges (default: true) */
|
|
1106
|
+
includeChargedEvents?: boolean;
|
|
1107
|
+
/** Filter to specific usage types */
|
|
1108
|
+
usageTypes?: string[];
|
|
1109
|
+
/** Custom pricing overrides (takes precedence over DB pricing) */
|
|
1110
|
+
customPricing?: CustomPricing;
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* A usage item for hypothetical cost estimation.
|
|
1114
|
+
*/
|
|
1115
|
+
interface HypotheticalUsageItem {
|
|
1116
|
+
/** The usage type (e.g., "api_call", "token") */
|
|
1117
|
+
usageType: string;
|
|
1118
|
+
/** The quantity of usage */
|
|
1119
|
+
quantity: number;
|
|
1120
|
+
/** Override unit price for this specific item */
|
|
1121
|
+
unitPriceOverride?: string;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Parameters for estimating costs from hypothetical usage.
|
|
1125
|
+
*/
|
|
1126
|
+
interface EstimateFromHypotheticalParams {
|
|
1127
|
+
/** List of usage items to estimate */
|
|
1128
|
+
items: HypotheticalUsageItem[];
|
|
1129
|
+
/** Default price for usage types without pricing plans */
|
|
1130
|
+
defaultUnitPrice?: string;
|
|
1131
|
+
/** Custom pricing overrides (takes precedence over DB pricing) */
|
|
1132
|
+
customPricing?: CustomPricing;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* A line item in the cost estimate.
|
|
1136
|
+
*/
|
|
1137
|
+
interface CostEstimateLineItem {
|
|
1138
|
+
/** The usage type */
|
|
1139
|
+
usageType: string;
|
|
1140
|
+
/** Total quantity */
|
|
1141
|
+
quantity: string;
|
|
1142
|
+
/** Unit price used */
|
|
1143
|
+
unitPrice: string;
|
|
1144
|
+
/** Estimated cost in USDC */
|
|
1145
|
+
estimatedCostUsdc: string;
|
|
1146
|
+
/** Number of events (for usage-based estimates) */
|
|
1147
|
+
eventCount?: number;
|
|
1148
|
+
/** Whether a pricing plan was found for this usage type */
|
|
1149
|
+
hasPricingPlan: boolean;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Response from cost estimation.
|
|
1153
|
+
*/
|
|
1154
|
+
interface CostEstimateResponse {
|
|
1155
|
+
/** Business ID (optional - may not be returned by all endpoints) */
|
|
1156
|
+
businessId?: string;
|
|
1157
|
+
/** Customer ID (if filtered) */
|
|
1158
|
+
customerId?: string;
|
|
1159
|
+
/** Period start (for usage-based estimates) */
|
|
1160
|
+
periodStart?: string;
|
|
1161
|
+
/** Period end (for usage-based estimates) */
|
|
1162
|
+
periodEnd?: string;
|
|
1163
|
+
/** Breakdown by usage type */
|
|
1164
|
+
lineItems: CostEstimateLineItem[];
|
|
1165
|
+
/** Subtotal in USDC */
|
|
1166
|
+
subtotalUsdc: string;
|
|
1167
|
+
/** Total estimated cost in USDC */
|
|
1168
|
+
estimatedTotalUsdc: string;
|
|
1169
|
+
/** Currency (always USDC) */
|
|
1170
|
+
currency: 'USDC';
|
|
1171
|
+
/** Indicates this is an estimate, not a charge */
|
|
1172
|
+
isEstimate: true;
|
|
1173
|
+
/** When the estimate was generated */
|
|
1174
|
+
generatedAt: string;
|
|
1175
|
+
/** Notes about the estimate (e.g., missing pricing plans, custom pricing applied) */
|
|
1176
|
+
notes: string[];
|
|
1177
|
+
}
|
|
499
1178
|
/**
|
|
500
1179
|
* A single event to record in a run.
|
|
501
1180
|
*/
|
|
@@ -563,48 +1242,146 @@ interface RecordRunResult {
|
|
|
563
1242
|
/** Human-readable summary */
|
|
564
1243
|
summary: string;
|
|
565
1244
|
}
|
|
566
|
-
/**
|
|
567
|
-
* Full run timeline response.
|
|
568
|
-
*/
|
|
569
|
-
interface RunTimeline {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
endedAt: string | null;
|
|
579
|
-
durationMs: number | null;
|
|
580
|
-
errorMessage: string | null;
|
|
581
|
-
errorCode: string | null;
|
|
582
|
-
correlationId: string | null;
|
|
583
|
-
metadata: Record<string, unknown> | null;
|
|
584
|
-
};
|
|
585
|
-
timeline: Array<{
|
|
1245
|
+
/**
|
|
1246
|
+
* Full run timeline response from GET /runs/:id/timeline.
|
|
1247
|
+
*/
|
|
1248
|
+
interface RunTimeline {
|
|
1249
|
+
runId: string;
|
|
1250
|
+
workflowId: string | null;
|
|
1251
|
+
customerId: string;
|
|
1252
|
+
status: RunStatus;
|
|
1253
|
+
startedAt: string | null;
|
|
1254
|
+
endedAt: string | null;
|
|
1255
|
+
durationMs: number | null;
|
|
1256
|
+
events: Array<{
|
|
586
1257
|
id: string;
|
|
587
1258
|
eventType: string;
|
|
588
|
-
|
|
589
|
-
|
|
1259
|
+
actionName: string | null;
|
|
1260
|
+
outcome: 'SUCCESS' | 'FAILED' | 'PENDING' | 'TIMEOUT' | 'RETRYING';
|
|
1261
|
+
explanation: string | null;
|
|
590
1262
|
description: string | null;
|
|
591
|
-
costUnits: number | null;
|
|
592
1263
|
timestamp: string;
|
|
593
|
-
|
|
1264
|
+
durationMs: number | null;
|
|
594
1265
|
parentEventId: string | null;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
1266
|
+
retryOfEventId: string | null;
|
|
1267
|
+
attemptNumber: number;
|
|
1268
|
+
retriedByEventId: string | null;
|
|
1269
|
+
costUsdc: string | null;
|
|
1270
|
+
isRetry: boolean;
|
|
1271
|
+
retryChain: {
|
|
1272
|
+
totalAttempts: number;
|
|
1273
|
+
finalOutcome: string;
|
|
1274
|
+
events: string[];
|
|
599
1275
|
} | null;
|
|
1276
|
+
metadata: {
|
|
1277
|
+
usageType: string;
|
|
1278
|
+
quantity: number;
|
|
1279
|
+
units: string | null;
|
|
1280
|
+
} | null;
|
|
1281
|
+
}>;
|
|
1282
|
+
anomalies: Array<{
|
|
1283
|
+
id: string;
|
|
1284
|
+
type: string;
|
|
1285
|
+
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
1286
|
+
title: string;
|
|
1287
|
+
explanation: string;
|
|
1288
|
+
relatedEventIds: string[];
|
|
1289
|
+
detectedAt: string;
|
|
1290
|
+
status: 'OPEN' | 'INVESTIGATING' | 'RESOLVED' | 'FALSE_POSITIVE' | 'IGNORED';
|
|
600
1291
|
}>;
|
|
1292
|
+
summary: {
|
|
1293
|
+
totalEvents: number;
|
|
1294
|
+
byType: Record<string, number>;
|
|
1295
|
+
byOutcome: Record<string, number>;
|
|
1296
|
+
retriedEvents: number;
|
|
1297
|
+
failedEvents: number;
|
|
1298
|
+
totalCostUsdc: string | null;
|
|
1299
|
+
};
|
|
1300
|
+
hasMore: boolean;
|
|
1301
|
+
nextCursor: string | null;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Run details response from GET /runs/:id.
|
|
1305
|
+
*/
|
|
1306
|
+
interface RunDetails {
|
|
1307
|
+
id: string;
|
|
1308
|
+
customerId: string;
|
|
1309
|
+
customerName: string | null;
|
|
1310
|
+
workflowId: string;
|
|
1311
|
+
workflowName: string;
|
|
1312
|
+
status: RunStatus;
|
|
1313
|
+
startedAt: string | null;
|
|
1314
|
+
endedAt: string | null;
|
|
1315
|
+
durationMs: number | null;
|
|
1316
|
+
errorMessage: string | null;
|
|
1317
|
+
errorCode: string | null;
|
|
1318
|
+
correlationId: string | null;
|
|
1319
|
+
metadata: Record<string, unknown> | null;
|
|
601
1320
|
totals: {
|
|
602
1321
|
eventCount: number;
|
|
603
1322
|
totalQuantity: string;
|
|
604
1323
|
totalCostUnits: string;
|
|
605
|
-
totalChargedUsdc: string;
|
|
606
1324
|
};
|
|
607
|
-
|
|
1325
|
+
_links: {
|
|
1326
|
+
timeline: string;
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Parameters for wrapping an external API call with usage tracking.
|
|
1331
|
+
* This ensures usage is recorded even if there's a crash/failure after the API call.
|
|
1332
|
+
*/
|
|
1333
|
+
interface WrapApiCallParams<T> {
|
|
1334
|
+
/**
|
|
1335
|
+
* The Drip customer ID to charge.
|
|
1336
|
+
*/
|
|
1337
|
+
customerId: string;
|
|
1338
|
+
/**
|
|
1339
|
+
* The usage meter/type to record against.
|
|
1340
|
+
* Must match a meter configured in your pricing plan.
|
|
1341
|
+
*/
|
|
1342
|
+
meter: string;
|
|
1343
|
+
/**
|
|
1344
|
+
* The async function that makes the external API call.
|
|
1345
|
+
* This is the call you want to track (e.g., OpenAI, Anthropic, etc.)
|
|
1346
|
+
*/
|
|
1347
|
+
call: () => Promise<T>;
|
|
1348
|
+
/**
|
|
1349
|
+
* Function to extract the usage quantity from the API call result.
|
|
1350
|
+
* @example (result) => result.usage.total_tokens
|
|
1351
|
+
*/
|
|
1352
|
+
extractUsage: (result: T) => number;
|
|
1353
|
+
/**
|
|
1354
|
+
* Custom idempotency key prefix.
|
|
1355
|
+
* If not provided, a unique key is generated.
|
|
1356
|
+
* The key ensures retries don't double-charge.
|
|
1357
|
+
*/
|
|
1358
|
+
idempotencyKey?: string;
|
|
1359
|
+
/**
|
|
1360
|
+
* Additional metadata to attach to this usage event.
|
|
1361
|
+
*/
|
|
1362
|
+
metadata?: Record<string, unknown>;
|
|
1363
|
+
/**
|
|
1364
|
+
* Retry configuration for the Drip charge call.
|
|
1365
|
+
* The external API call is NOT retried (only called once).
|
|
1366
|
+
*/
|
|
1367
|
+
retryOptions?: RetryOptions;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Result of a wrapped API call.
|
|
1371
|
+
*/
|
|
1372
|
+
interface WrapApiCallResult<T> {
|
|
1373
|
+
/**
|
|
1374
|
+
* The result from the external API call.
|
|
1375
|
+
*/
|
|
1376
|
+
result: T;
|
|
1377
|
+
/**
|
|
1378
|
+
* The charge result from Drip.
|
|
1379
|
+
*/
|
|
1380
|
+
charge: ChargeResult;
|
|
1381
|
+
/**
|
|
1382
|
+
* The idempotency key used (useful for debugging).
|
|
1383
|
+
*/
|
|
1384
|
+
idempotencyKey: string;
|
|
608
1385
|
}
|
|
609
1386
|
/**
|
|
610
1387
|
* Error thrown by Drip SDK operations.
|
|
@@ -625,7 +1402,7 @@ declare class DripError extends Error {
|
|
|
625
1402
|
*
|
|
626
1403
|
* @example
|
|
627
1404
|
* ```typescript
|
|
628
|
-
* import { Drip } from '@drip-
|
|
1405
|
+
* import { Drip } from '@drip-sdk/node';
|
|
629
1406
|
*
|
|
630
1407
|
* const drip = new Drip({
|
|
631
1408
|
* apiKey: process.env.DRIP_API_KEY!,
|
|
@@ -651,6 +1428,15 @@ declare class Drip {
|
|
|
651
1428
|
private readonly apiKey;
|
|
652
1429
|
private readonly baseUrl;
|
|
653
1430
|
private readonly timeout;
|
|
1431
|
+
private readonly resilience;
|
|
1432
|
+
/**
|
|
1433
|
+
* The type of API key being used.
|
|
1434
|
+
*
|
|
1435
|
+
* - `'secret'` — Full access (sk_live_... / sk_test_...)
|
|
1436
|
+
* - `'public'` — Client-safe, restricted access (pk_live_... / pk_test_...)
|
|
1437
|
+
* - `'unknown'` — Key format not recognized (legacy or custom)
|
|
1438
|
+
*/
|
|
1439
|
+
readonly keyType: 'secret' | 'public' | 'unknown';
|
|
654
1440
|
/**
|
|
655
1441
|
* Creates a new Drip SDK client.
|
|
656
1442
|
*
|
|
@@ -659,17 +1445,100 @@ declare class Drip {
|
|
|
659
1445
|
*
|
|
660
1446
|
* @example
|
|
661
1447
|
* ```typescript
|
|
1448
|
+
* // Basic usage
|
|
1449
|
+
* const drip = new Drip({
|
|
1450
|
+
* apiKey: 'sk_live_...',
|
|
1451
|
+
* });
|
|
1452
|
+
*
|
|
1453
|
+
* // With production resilience (recommended)
|
|
1454
|
+
* const drip = new Drip({
|
|
1455
|
+
* apiKey: 'sk_live_...',
|
|
1456
|
+
* resilience: true,
|
|
1457
|
+
* });
|
|
1458
|
+
*
|
|
1459
|
+
* // High throughput mode
|
|
662
1460
|
* const drip = new Drip({
|
|
663
|
-
* apiKey: '
|
|
1461
|
+
* apiKey: 'sk_live_...',
|
|
1462
|
+
* resilience: 'high-throughput',
|
|
664
1463
|
* });
|
|
665
1464
|
* ```
|
|
666
1465
|
*/
|
|
667
|
-
constructor(config
|
|
1466
|
+
constructor(config?: DripConfig);
|
|
1467
|
+
/**
|
|
1468
|
+
* Asserts that the SDK was initialized with a secret key (sk_).
|
|
1469
|
+
* Throws a clear error if a public key is being used for a secret-key-only operation.
|
|
1470
|
+
* @internal
|
|
1471
|
+
*/
|
|
1472
|
+
private assertSecretKey;
|
|
668
1473
|
/**
|
|
669
1474
|
* Makes an authenticated request to the Drip API.
|
|
670
1475
|
* @internal
|
|
671
1476
|
*/
|
|
672
1477
|
private request;
|
|
1478
|
+
/**
|
|
1479
|
+
* Execute the actual HTTP request (internal).
|
|
1480
|
+
* @internal
|
|
1481
|
+
*/
|
|
1482
|
+
private rawRequest;
|
|
1483
|
+
/**
|
|
1484
|
+
* Pings the Drip API to check connectivity and measure latency.
|
|
1485
|
+
*
|
|
1486
|
+
* @returns Health status with latency information
|
|
1487
|
+
* @throws {DripError} If the request fails or times out
|
|
1488
|
+
*
|
|
1489
|
+
* @example
|
|
1490
|
+
* ```typescript
|
|
1491
|
+
* const health = await drip.ping();
|
|
1492
|
+
* if (health.ok) {
|
|
1493
|
+
* console.log(`API healthy, latency: ${health.latencyMs}ms`);
|
|
1494
|
+
* }
|
|
1495
|
+
* ```
|
|
1496
|
+
*/
|
|
1497
|
+
ping(): Promise<{
|
|
1498
|
+
ok: boolean;
|
|
1499
|
+
status: string;
|
|
1500
|
+
latencyMs: number;
|
|
1501
|
+
timestamp: number;
|
|
1502
|
+
}>;
|
|
1503
|
+
/**
|
|
1504
|
+
* Get SDK metrics (requires resilience to be enabled).
|
|
1505
|
+
*
|
|
1506
|
+
* Returns aggregated metrics including success rates, latencies, and errors.
|
|
1507
|
+
*
|
|
1508
|
+
* @returns Metrics summary or null if resilience is not enabled
|
|
1509
|
+
*
|
|
1510
|
+
* @example
|
|
1511
|
+
* ```typescript
|
|
1512
|
+
* const drip = new Drip({ apiKey: '...', resilience: true });
|
|
1513
|
+
* // ... make some requests ...
|
|
1514
|
+
*
|
|
1515
|
+
* const metrics = drip.getMetrics();
|
|
1516
|
+
* if (metrics) {
|
|
1517
|
+
* console.log(`Success rate: ${metrics.successRate.toFixed(1)}%`);
|
|
1518
|
+
* console.log(`P95 latency: ${metrics.p95LatencyMs.toFixed(0)}ms`);
|
|
1519
|
+
* }
|
|
1520
|
+
* ```
|
|
1521
|
+
*/
|
|
1522
|
+
getMetrics(): MetricsSummary | null;
|
|
1523
|
+
/**
|
|
1524
|
+
* Get SDK health status (requires resilience to be enabled).
|
|
1525
|
+
*
|
|
1526
|
+
* Returns health status including circuit breaker state and rate limiter status.
|
|
1527
|
+
*
|
|
1528
|
+
* @returns Health status or null if resilience is not enabled
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* ```typescript
|
|
1532
|
+
* const drip = new Drip({ apiKey: '...', resilience: true });
|
|
1533
|
+
*
|
|
1534
|
+
* const health = drip.getHealth();
|
|
1535
|
+
* if (health) {
|
|
1536
|
+
* console.log(`Circuit: ${health.circuitBreaker.state}`);
|
|
1537
|
+
* console.log(`Available tokens: ${health.rateLimiter.availableTokens}`);
|
|
1538
|
+
* }
|
|
1539
|
+
* ```
|
|
1540
|
+
*/
|
|
1541
|
+
getHealth(): ResilienceHealth | null;
|
|
673
1542
|
/**
|
|
674
1543
|
* Creates a new customer in your Drip account.
|
|
675
1544
|
*
|
|
@@ -761,6 +1630,113 @@ declare class Drip {
|
|
|
761
1630
|
* ```
|
|
762
1631
|
*/
|
|
763
1632
|
charge(params: ChargeParams): Promise<ChargeResult>;
|
|
1633
|
+
/**
|
|
1634
|
+
* Wraps an external API call with guaranteed usage recording.
|
|
1635
|
+
*
|
|
1636
|
+
* **This solves the crash-before-record problem:**
|
|
1637
|
+
* ```typescript
|
|
1638
|
+
* // DANGEROUS - usage lost if crash between lines 1 and 2:
|
|
1639
|
+
* const response = await openai.chat.completions.create({...}); // line 1
|
|
1640
|
+
* await drip.charge({ tokens: response.usage.total_tokens }); // line 2
|
|
1641
|
+
*
|
|
1642
|
+
* // SAFE - wrapApiCall guarantees recording with retry:
|
|
1643
|
+
* const { result } = await drip.wrapApiCall({
|
|
1644
|
+
* call: () => openai.chat.completions.create({...}),
|
|
1645
|
+
* extractUsage: (r) => r.usage.total_tokens,
|
|
1646
|
+
* ...
|
|
1647
|
+
* });
|
|
1648
|
+
* ```
|
|
1649
|
+
*
|
|
1650
|
+
* How it works:
|
|
1651
|
+
* 1. Generates idempotency key BEFORE the API call
|
|
1652
|
+
* 2. Makes the external API call (once, no retry)
|
|
1653
|
+
* 3. Records usage in Drip with retry + idempotency
|
|
1654
|
+
* 4. If recording fails transiently, retries are safe (no double-charge)
|
|
1655
|
+
*
|
|
1656
|
+
* @param params - Wrap parameters including the call and usage extractor
|
|
1657
|
+
* @returns The API result and charge details
|
|
1658
|
+
* @throws {DripError} If the Drip charge fails after retries
|
|
1659
|
+
* @throws {Error} If the external API call fails
|
|
1660
|
+
*
|
|
1661
|
+
* @example
|
|
1662
|
+
* ```typescript
|
|
1663
|
+
* // OpenAI example
|
|
1664
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1665
|
+
* customerId: 'cust_abc123',
|
|
1666
|
+
* meter: 'tokens',
|
|
1667
|
+
* call: () => openai.chat.completions.create({
|
|
1668
|
+
* model: 'gpt-4',
|
|
1669
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1670
|
+
* }),
|
|
1671
|
+
* extractUsage: (r) => r.usage?.total_tokens ?? 0,
|
|
1672
|
+
* });
|
|
1673
|
+
*
|
|
1674
|
+
* console.log(result.choices[0].message.content);
|
|
1675
|
+
* console.log(`Charged: ${charge.charge.amountUsdc} USDC`);
|
|
1676
|
+
* ```
|
|
1677
|
+
*
|
|
1678
|
+
* @example
|
|
1679
|
+
* ```typescript
|
|
1680
|
+
* // Anthropic example
|
|
1681
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1682
|
+
* customerId: 'cust_abc123',
|
|
1683
|
+
* meter: 'tokens',
|
|
1684
|
+
* call: () => anthropic.messages.create({
|
|
1685
|
+
* model: 'claude-3-opus-20240229',
|
|
1686
|
+
* max_tokens: 1024,
|
|
1687
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1688
|
+
* }),
|
|
1689
|
+
* extractUsage: (r) => r.usage.input_tokens + r.usage.output_tokens,
|
|
1690
|
+
* });
|
|
1691
|
+
* ```
|
|
1692
|
+
*
|
|
1693
|
+
* @example
|
|
1694
|
+
* ```typescript
|
|
1695
|
+
* // With custom retry options
|
|
1696
|
+
* const { result } = await drip.wrapApiCall({
|
|
1697
|
+
* customerId: 'cust_abc123',
|
|
1698
|
+
* meter: 'api_calls',
|
|
1699
|
+
* call: () => fetch('https://api.example.com/expensive'),
|
|
1700
|
+
* extractUsage: () => 1, // Fixed cost per call
|
|
1701
|
+
* retryOptions: {
|
|
1702
|
+
* maxAttempts: 5,
|
|
1703
|
+
* baseDelayMs: 200,
|
|
1704
|
+
* },
|
|
1705
|
+
* });
|
|
1706
|
+
* ```
|
|
1707
|
+
*/
|
|
1708
|
+
wrapApiCall<T>(params: WrapApiCallParams<T>): Promise<WrapApiCallResult<T>>;
|
|
1709
|
+
/**
|
|
1710
|
+
* Records usage for internal visibility WITHOUT billing.
|
|
1711
|
+
*
|
|
1712
|
+
* Use this for:
|
|
1713
|
+
* - Tracking internal team usage without charging
|
|
1714
|
+
* - Pilot programs where you want visibility before billing
|
|
1715
|
+
* - Pre-billing tracking before customer has on-chain wallet
|
|
1716
|
+
*
|
|
1717
|
+
* This does NOT:
|
|
1718
|
+
* - Create a Charge record
|
|
1719
|
+
* - Require customer balance
|
|
1720
|
+
* - Require blockchain/wallet setup
|
|
1721
|
+
*
|
|
1722
|
+
* For billing, use `charge()` instead.
|
|
1723
|
+
*
|
|
1724
|
+
* @param params - The usage tracking parameters
|
|
1725
|
+
* @returns The tracked usage event
|
|
1726
|
+
*
|
|
1727
|
+
* @example
|
|
1728
|
+
* ```typescript
|
|
1729
|
+
* const result = await drip.trackUsage({
|
|
1730
|
+
* customerId: 'cust_abc123',
|
|
1731
|
+
* meter: 'api_calls',
|
|
1732
|
+
* quantity: 100,
|
|
1733
|
+
* description: 'API calls during trial period',
|
|
1734
|
+
* });
|
|
1735
|
+
*
|
|
1736
|
+
* console.log(`Tracked: ${result.usageEventId}`);
|
|
1737
|
+
* ```
|
|
1738
|
+
*/
|
|
1739
|
+
trackUsage(params: TrackUsageParams): Promise<TrackUsageResult>;
|
|
764
1740
|
/**
|
|
765
1741
|
* Retrieves a specific charge by ID.
|
|
766
1742
|
*
|
|
@@ -965,8 +1941,8 @@ declare class Drip {
|
|
|
965
1941
|
* @example
|
|
966
1942
|
* ```typescript
|
|
967
1943
|
* const workflow = await drip.createWorkflow({
|
|
968
|
-
* name: '
|
|
969
|
-
* slug: '
|
|
1944
|
+
* name: 'Document Processing',
|
|
1945
|
+
* slug: 'doc_processing',
|
|
970
1946
|
* productSurface: 'AGENT',
|
|
971
1947
|
* });
|
|
972
1948
|
* ```
|
|
@@ -1031,33 +2007,53 @@ declare class Drip {
|
|
|
1031
2007
|
totalCostUnits: string | null;
|
|
1032
2008
|
}>;
|
|
1033
2009
|
/**
|
|
1034
|
-
* Gets
|
|
2010
|
+
* Gets run details with summary totals.
|
|
2011
|
+
*
|
|
2012
|
+
* For full event history with retry chains and anomalies, use `getRunTimeline()`.
|
|
2013
|
+
*
|
|
2014
|
+
* @param runId - The run ID
|
|
2015
|
+
* @returns Run details with totals
|
|
2016
|
+
*
|
|
2017
|
+
* @example
|
|
2018
|
+
* ```typescript
|
|
2019
|
+
* const run = await drip.getRun('run_abc123');
|
|
2020
|
+
* console.log(`Status: ${run.status}, Events: ${run.totals.eventCount}`);
|
|
2021
|
+
* ```
|
|
2022
|
+
*/
|
|
2023
|
+
getRun(runId: string): Promise<RunDetails>;
|
|
2024
|
+
/**
|
|
2025
|
+
* Gets a run's full timeline with events, anomalies, and analytics.
|
|
1035
2026
|
*
|
|
1036
2027
|
* This is the key endpoint for debugging "what happened" in an execution.
|
|
1037
2028
|
*
|
|
1038
2029
|
* @param runId - The run ID
|
|
1039
|
-
* @
|
|
2030
|
+
* @param options - Pagination and filtering options
|
|
2031
|
+
* @returns Full timeline with events, anomalies, and summary
|
|
1040
2032
|
*
|
|
1041
2033
|
* @example
|
|
1042
2034
|
* ```typescript
|
|
1043
|
-
* const
|
|
2035
|
+
* const timeline = await drip.getRunTimeline('run_abc123');
|
|
1044
2036
|
*
|
|
1045
|
-
* console.log(`Status: ${
|
|
1046
|
-
* console.log(`
|
|
1047
|
-
* console.log(`Total cost: ${totals.totalCostUnits}`);
|
|
2037
|
+
* console.log(`Status: ${timeline.status}`);
|
|
2038
|
+
* console.log(`Events: ${timeline.summary.totalEvents}`);
|
|
1048
2039
|
*
|
|
1049
|
-
* for (const event of timeline) {
|
|
1050
|
-
* console.log(`${event.eventType}: ${event.
|
|
2040
|
+
* for (const event of timeline.events) {
|
|
2041
|
+
* console.log(`${event.eventType}: ${event.outcome}`);
|
|
1051
2042
|
* }
|
|
1052
2043
|
* ```
|
|
1053
2044
|
*/
|
|
1054
|
-
getRunTimeline(runId: string
|
|
2045
|
+
getRunTimeline(runId: string, options?: {
|
|
2046
|
+
limit?: number;
|
|
2047
|
+
cursor?: string;
|
|
2048
|
+
includeAnomalies?: boolean;
|
|
2049
|
+
collapseRetries?: boolean;
|
|
2050
|
+
}): Promise<RunTimeline>;
|
|
1055
2051
|
/**
|
|
1056
2052
|
* Emits an event to a run.
|
|
1057
2053
|
*
|
|
1058
|
-
*
|
|
2054
|
+
* Each event is assigned a unique idempotency key (auto-generated if not provided).
|
|
2055
|
+
* This maps each inference or API call to a single trackable event.
|
|
1059
2056
|
* Use `Drip.generateIdempotencyKey()` for deterministic key generation.
|
|
1060
|
-
* If `idempotencyKey` is omitted, repeated calls may create duplicate events.
|
|
1061
2057
|
*
|
|
1062
2058
|
* @param params - Event parameters
|
|
1063
2059
|
* @returns The created event
|
|
@@ -1098,10 +2094,13 @@ declare class Drip {
|
|
|
1098
2094
|
success: boolean;
|
|
1099
2095
|
created: number;
|
|
1100
2096
|
duplicates: number;
|
|
2097
|
+
skipped: number;
|
|
1101
2098
|
events: Array<{
|
|
1102
2099
|
id: string;
|
|
1103
2100
|
eventType: string;
|
|
1104
2101
|
isDuplicate: boolean;
|
|
2102
|
+
skipped?: boolean;
|
|
2103
|
+
reason?: string;
|
|
1105
2104
|
}>;
|
|
1106
2105
|
}>;
|
|
1107
2106
|
/**
|
|
@@ -1130,6 +2129,82 @@ declare class Drip {
|
|
|
1130
2129
|
* ```
|
|
1131
2130
|
*/
|
|
1132
2131
|
listMeters(): Promise<ListMetersResponse>;
|
|
2132
|
+
/**
|
|
2133
|
+
* Estimates costs from historical usage events.
|
|
2134
|
+
*
|
|
2135
|
+
* Use this to preview what existing usage would cost before creating charges,
|
|
2136
|
+
* or to run "what-if" scenarios with custom pricing.
|
|
2137
|
+
*
|
|
2138
|
+
* @param params - Parameters for the estimate
|
|
2139
|
+
* @returns Cost estimate with line item breakdown
|
|
2140
|
+
*
|
|
2141
|
+
* @example
|
|
2142
|
+
* ```typescript
|
|
2143
|
+
* // Estimate costs for last month's usage
|
|
2144
|
+
* const estimate = await drip.estimateFromUsage({
|
|
2145
|
+
* periodStart: new Date('2024-01-01'),
|
|
2146
|
+
* periodEnd: new Date('2024-01-31'),
|
|
2147
|
+
* });
|
|
2148
|
+
*
|
|
2149
|
+
* console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);
|
|
2150
|
+
* ```
|
|
2151
|
+
*
|
|
2152
|
+
* @example
|
|
2153
|
+
* ```typescript
|
|
2154
|
+
* // "What-if" scenario with custom pricing
|
|
2155
|
+
* const estimate = await drip.estimateFromUsage({
|
|
2156
|
+
* periodStart: new Date('2024-01-01'),
|
|
2157
|
+
* periodEnd: new Date('2024-01-31'),
|
|
2158
|
+
* customPricing: {
|
|
2159
|
+
* 'api_call': '0.005', // What if we charged $0.005 per call?
|
|
2160
|
+
* 'token': '0.0001', // What if we charged $0.0001 per token?
|
|
2161
|
+
* },
|
|
2162
|
+
* });
|
|
2163
|
+
* ```
|
|
2164
|
+
*/
|
|
2165
|
+
estimateFromUsage(params: EstimateFromUsageParams): Promise<CostEstimateResponse>;
|
|
2166
|
+
/**
|
|
2167
|
+
* Estimates costs from hypothetical usage.
|
|
2168
|
+
*
|
|
2169
|
+
* Use this for "what-if" scenarios, budget planning, or to preview
|
|
2170
|
+
* costs before usage occurs.
|
|
2171
|
+
*
|
|
2172
|
+
* @param params - Parameters for the estimate
|
|
2173
|
+
* @returns Cost estimate with line item breakdown
|
|
2174
|
+
*
|
|
2175
|
+
* @example
|
|
2176
|
+
* ```typescript
|
|
2177
|
+
* // Estimate what 10,000 API calls and 1M tokens would cost
|
|
2178
|
+
* const estimate = await drip.estimateFromHypothetical({
|
|
2179
|
+
* items: [
|
|
2180
|
+
* { usageType: 'api_call', quantity: 10000 },
|
|
2181
|
+
* { usageType: 'token', quantity: 1000000 },
|
|
2182
|
+
* ],
|
|
2183
|
+
* });
|
|
2184
|
+
*
|
|
2185
|
+
* console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);
|
|
2186
|
+
* for (const item of estimate.lineItems) {
|
|
2187
|
+
* console.log(` ${item.usageType}: ${item.quantity} × $${item.unitPrice} = $${item.estimatedCostUsdc}`);
|
|
2188
|
+
* }
|
|
2189
|
+
* ```
|
|
2190
|
+
*
|
|
2191
|
+
* @example
|
|
2192
|
+
* ```typescript
|
|
2193
|
+
* // Compare different pricing scenarios
|
|
2194
|
+
* const currentPricing = await drip.estimateFromHypothetical({
|
|
2195
|
+
* items: [{ usageType: 'api_call', quantity: 100000 }],
|
|
2196
|
+
* });
|
|
2197
|
+
*
|
|
2198
|
+
* const newPricing = await drip.estimateFromHypothetical({
|
|
2199
|
+
* items: [{ usageType: 'api_call', quantity: 100000 }],
|
|
2200
|
+
* customPricing: { 'api_call': '0.0005' }, // 50% discount
|
|
2201
|
+
* });
|
|
2202
|
+
*
|
|
2203
|
+
* console.log(`Current: $${currentPricing.estimatedTotalUsdc}`);
|
|
2204
|
+
* console.log(`With 50% discount: $${newPricing.estimatedTotalUsdc}`);
|
|
2205
|
+
* ```
|
|
2206
|
+
*/
|
|
2207
|
+
estimateFromHypothetical(params: EstimateFromHypotheticalParams): Promise<CostEstimateResponse>;
|
|
1133
2208
|
/**
|
|
1134
2209
|
* Records a complete agent run in a single call.
|
|
1135
2210
|
*
|
|
@@ -1150,7 +2225,7 @@ declare class Drip {
|
|
|
1150
2225
|
* // Record a complete agent run in one call
|
|
1151
2226
|
* const result = await drip.recordRun({
|
|
1152
2227
|
* customerId: 'cust_123',
|
|
1153
|
-
* workflow: '
|
|
2228
|
+
* workflow: 'doc_processing', // Auto-creates if doesn't exist
|
|
1154
2229
|
* events: [
|
|
1155
2230
|
* { eventType: 'agent.start', description: 'Started processing' },
|
|
1156
2231
|
* { eventType: 'tool.ocr', quantity: 3, units: 'pages', costUnits: 0.15 },
|
|
@@ -1169,7 +2244,7 @@ declare class Drip {
|
|
|
1169
2244
|
* // Record a failed run with error details
|
|
1170
2245
|
* const result = await drip.recordRun({
|
|
1171
2246
|
* customerId: 'cust_123',
|
|
1172
|
-
* workflow: '
|
|
2247
|
+
* workflow: 'doc_processing',
|
|
1173
2248
|
* events: [
|
|
1174
2249
|
* { eventType: 'agent.start', description: 'Started processing' },
|
|
1175
2250
|
* { eventType: 'tool.ocr', quantity: 1, units: 'pages' },
|
|
@@ -1214,9 +2289,39 @@ declare class Drip {
|
|
|
1214
2289
|
sequence?: number;
|
|
1215
2290
|
}): string;
|
|
1216
2291
|
/**
|
|
1217
|
-
* Verifies a webhook signature.
|
|
2292
|
+
* Verifies a webhook signature using HMAC-SHA256.
|
|
1218
2293
|
*
|
|
1219
2294
|
* Call this when receiving webhook events to ensure they're authentic.
|
|
2295
|
+
* This is an async method that uses the Web Crypto API for secure verification.
|
|
2296
|
+
*
|
|
2297
|
+
* @param payload - The raw request body (string)
|
|
2298
|
+
* @param signature - The x-drip-signature header value
|
|
2299
|
+
* @param secret - Your webhook secret
|
|
2300
|
+
* @returns Promise resolving to whether the signature is valid
|
|
2301
|
+
*
|
|
2302
|
+
* @example
|
|
2303
|
+
* ```typescript
|
|
2304
|
+
* app.post('/webhooks/drip', async (req, res) => {
|
|
2305
|
+
* const isValid = await Drip.verifyWebhookSignature(
|
|
2306
|
+
* req.rawBody,
|
|
2307
|
+
* req.headers['x-drip-signature'],
|
|
2308
|
+
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
2309
|
+
* );
|
|
2310
|
+
*
|
|
2311
|
+
* if (!isValid) {
|
|
2312
|
+
* return res.status(401).send('Invalid signature');
|
|
2313
|
+
* }
|
|
2314
|
+
*
|
|
2315
|
+
* // Process the webhook...
|
|
2316
|
+
* });
|
|
2317
|
+
* ```
|
|
2318
|
+
*/
|
|
2319
|
+
static verifyWebhookSignature(payload: string, signature: string, secret: string, tolerance?: number): Promise<boolean>;
|
|
2320
|
+
/**
|
|
2321
|
+
* Synchronously verifies a webhook signature using HMAC-SHA256.
|
|
2322
|
+
*
|
|
2323
|
+
* This method uses Node.js crypto module and is only available in Node.js environments.
|
|
2324
|
+
* For edge runtimes or browsers, use the async `verifyWebhookSignature` method instead.
|
|
1220
2325
|
*
|
|
1221
2326
|
* @param payload - The raw request body (string)
|
|
1222
2327
|
* @param signature - The x-drip-signature header value
|
|
@@ -1226,7 +2331,7 @@ declare class Drip {
|
|
|
1226
2331
|
* @example
|
|
1227
2332
|
* ```typescript
|
|
1228
2333
|
* app.post('/webhooks/drip', (req, res) => {
|
|
1229
|
-
* const isValid = Drip.
|
|
2334
|
+
* const isValid = Drip.verifyWebhookSignatureSync(
|
|
1230
2335
|
* req.rawBody,
|
|
1231
2336
|
* req.headers['x-drip-signature'],
|
|
1232
2337
|
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
@@ -1240,7 +2345,102 @@ declare class Drip {
|
|
|
1240
2345
|
* });
|
|
1241
2346
|
* ```
|
|
1242
2347
|
*/
|
|
1243
|
-
static
|
|
2348
|
+
static verifyWebhookSignatureSync(payload: string, signature: string, secret: string, tolerance?: number): boolean;
|
|
2349
|
+
/**
|
|
2350
|
+
* Generates a webhook signature for testing purposes.
|
|
2351
|
+
*
|
|
2352
|
+
* This method creates a signature in the same format the Drip backend uses,
|
|
2353
|
+
* allowing you to test your webhook handling code locally.
|
|
2354
|
+
*
|
|
2355
|
+
* @param payload - The webhook payload (JSON string)
|
|
2356
|
+
* @param secret - The webhook secret
|
|
2357
|
+
* @param timestamp - Optional timestamp (defaults to current time)
|
|
2358
|
+
* @returns Signature in format: t=timestamp,v1=hexsignature
|
|
2359
|
+
*
|
|
2360
|
+
* @example
|
|
2361
|
+
* ```typescript
|
|
2362
|
+
* const payload = JSON.stringify({ type: 'charge.succeeded', data: {...} });
|
|
2363
|
+
* const signature = Drip.generateWebhookSignature(payload, 'whsec_test123');
|
|
2364
|
+
*
|
|
2365
|
+
* // Use in tests:
|
|
2366
|
+
* const isValid = Drip.verifyWebhookSignatureSync(payload, signature, 'whsec_test123');
|
|
2367
|
+
* console.log(isValid); // true
|
|
2368
|
+
* ```
|
|
2369
|
+
*/
|
|
2370
|
+
static generateWebhookSignature(payload: string, secret: string, timestamp?: number): string;
|
|
2371
|
+
/**
|
|
2372
|
+
* Creates a StreamMeter for accumulating usage and charging once.
|
|
2373
|
+
*
|
|
2374
|
+
* Perfect for LLM token streaming where you want to:
|
|
2375
|
+
* - Accumulate tokens locally (no API call per token)
|
|
2376
|
+
* - Charge once at the end of the stream
|
|
2377
|
+
* - Handle partial failures (charge for what was delivered)
|
|
2378
|
+
*
|
|
2379
|
+
* @param options - StreamMeter configuration
|
|
2380
|
+
* @returns A new StreamMeter instance
|
|
2381
|
+
*
|
|
2382
|
+
* @example
|
|
2383
|
+
* ```typescript
|
|
2384
|
+
* const meter = drip.createStreamMeter({
|
|
2385
|
+
* customerId: 'cust_abc123',
|
|
2386
|
+
* meter: 'tokens',
|
|
2387
|
+
* });
|
|
2388
|
+
*
|
|
2389
|
+
* // Accumulate tokens as they stream
|
|
2390
|
+
* for await (const chunk of llmStream) {
|
|
2391
|
+
* meter.addSync(chunk.tokens);
|
|
2392
|
+
* yield chunk;
|
|
2393
|
+
* }
|
|
2394
|
+
*
|
|
2395
|
+
* // Single charge at end
|
|
2396
|
+
* const result = await meter.flush();
|
|
2397
|
+
* console.log(`Charged ${result.charge?.amountUsdc} for ${result.quantity} tokens`);
|
|
2398
|
+
* ```
|
|
2399
|
+
*
|
|
2400
|
+
* @example
|
|
2401
|
+
* ```typescript
|
|
2402
|
+
* // With auto-flush threshold
|
|
2403
|
+
* const meter = drip.createStreamMeter({
|
|
2404
|
+
* customerId: 'cust_abc123',
|
|
2405
|
+
* meter: 'tokens',
|
|
2406
|
+
* flushThreshold: 10000, // Charge every 10k tokens
|
|
2407
|
+
* });
|
|
2408
|
+
*
|
|
2409
|
+
* for await (const chunk of longStream) {
|
|
2410
|
+
* await meter.add(chunk.tokens); // May auto-flush
|
|
2411
|
+
* }
|
|
2412
|
+
*
|
|
2413
|
+
* await meter.flush(); // Final flush for remaining tokens
|
|
2414
|
+
* ```
|
|
2415
|
+
*/
|
|
2416
|
+
createStreamMeter(options: StreamMeterOptions): StreamMeter;
|
|
1244
2417
|
}
|
|
1245
2418
|
|
|
1246
|
-
|
|
2419
|
+
/**
|
|
2420
|
+
* Pre-initialized Drip client singleton.
|
|
2421
|
+
*
|
|
2422
|
+
* Uses lazy initialization - only creates the client when first accessed.
|
|
2423
|
+
* Reads `DRIP_API_KEY` from environment variables.
|
|
2424
|
+
*
|
|
2425
|
+
* @example
|
|
2426
|
+
* ```typescript
|
|
2427
|
+
* import { drip } from '@drip-sdk/node';
|
|
2428
|
+
*
|
|
2429
|
+
* // Track usage with one line
|
|
2430
|
+
* await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
|
|
2431
|
+
*
|
|
2432
|
+
* // Charge with one line
|
|
2433
|
+
* await drip.charge({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
|
|
2434
|
+
*
|
|
2435
|
+
* // Record a run
|
|
2436
|
+
* await drip.recordRun({
|
|
2437
|
+
* customerId: 'cust_123',
|
|
2438
|
+
* workflow: 'agent-run',
|
|
2439
|
+
* events: [{ eventType: 'llm.call', quantity: 1000, units: 'tokens' }],
|
|
2440
|
+
* status: 'COMPLETED',
|
|
2441
|
+
* });
|
|
2442
|
+
* ```
|
|
2443
|
+
*/
|
|
2444
|
+
declare const drip: Drip;
|
|
2445
|
+
|
|
2446
|
+
export { type BalanceResult, type Charge, type ChargeParams, type ChargeResult, type ChargeStatus, type CheckoutParams, type CheckoutResult, CircuitBreaker, type CircuitBreakerConfig, CircuitBreakerOpenError, type CircuitState, type CostEstimateLineItem, type CostEstimateResponse, type CreateCustomerParams, type CreateWebhookParams, type CreateWebhookResponse, type CreateWorkflowParams, type CustomPricing, type Customer, type DeleteWebhookResponse, Drip, type DripConfig, DripError, type EmitEventParams, type EndRunParams, type EstimateFromHypotheticalParams, type EstimateFromUsageParams, type EventResult, type HypotheticalUsageItem, type ListChargesOptions, type ListChargesResponse, type ListCustomersOptions, type ListCustomersResponse, type ListMetersResponse, type ListWebhooksResponse, type Meter, MetricsCollector, type MetricsSummary, RateLimiter, type RateLimiterConfig, type RecordRunEvent, type RecordRunParams, type RecordRunResult, type RequestMetrics, type ResilienceConfig, type ResilienceHealth, ResilienceManager, type RetryConfig, RetryExhaustedError, type RetryOptions, type RunDetails, type RunResult, type RunStatus, type RunTimeline, type StartRunParams, StreamMeter, type StreamMeterFlushResult, type StreamMeterOptions, type TrackUsageParams, type TrackUsageResult, type Webhook, type WebhookEventType, type Workflow, type WrapApiCallParams, type WrapApiCallResult, calculateBackoff, createDefaultResilienceConfig, createDisabledResilienceConfig, createHighThroughputResilienceConfig, Drip as default, drip, isRetryableError };
|