@drip-sdk/node 1.0.2 → 1.0.5
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 +102 -3
- package/dist/express.cjs +2 -2
- package/dist/express.cjs.map +1 -1
- 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 +253 -11
- package/dist/index.d.ts +253 -11
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +2 -2
- package/dist/middleware.cjs.map +1 -1
- 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.js +2 -2
- package/dist/next.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,145 @@
|
|
|
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
|
+
|
|
1
143
|
/**
|
|
2
144
|
* Drip SDK - Usage-based billing for Node.js
|
|
3
145
|
*
|
|
@@ -7,6 +149,7 @@
|
|
|
7
149
|
*
|
|
8
150
|
* @packageDocumentation
|
|
9
151
|
*/
|
|
152
|
+
|
|
10
153
|
/**
|
|
11
154
|
* Configuration options for the Drip SDK client.
|
|
12
155
|
*/
|
|
@@ -95,12 +238,16 @@ interface ListCustomersResponse {
|
|
|
95
238
|
interface BalanceResult {
|
|
96
239
|
/** Customer ID */
|
|
97
240
|
customerId: string;
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
/** Balance in
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
|
|
241
|
+
/** On-chain address */
|
|
242
|
+
onchainAddress: string;
|
|
243
|
+
/** Balance in USDC (6 decimals) - matches backend field name */
|
|
244
|
+
balanceUsdc: string;
|
|
245
|
+
/** Pending charges in USDC */
|
|
246
|
+
pendingChargesUsdc: string;
|
|
247
|
+
/** Available USDC (balance minus pending) */
|
|
248
|
+
availableUsdc: string;
|
|
249
|
+
/** ISO timestamp of last balance sync */
|
|
250
|
+
lastSyncedAt: string | null;
|
|
104
251
|
}
|
|
105
252
|
/**
|
|
106
253
|
* Parameters for recording usage and charging a customer.
|
|
@@ -625,7 +772,7 @@ declare class DripError extends Error {
|
|
|
625
772
|
*
|
|
626
773
|
* @example
|
|
627
774
|
* ```typescript
|
|
628
|
-
* import { Drip } from '@drip-
|
|
775
|
+
* import { Drip } from '@drip-sdk/node';
|
|
629
776
|
*
|
|
630
777
|
* const drip = new Drip({
|
|
631
778
|
* apiKey: process.env.DRIP_API_KEY!,
|
|
@@ -670,6 +817,25 @@ declare class Drip {
|
|
|
670
817
|
* @internal
|
|
671
818
|
*/
|
|
672
819
|
private request;
|
|
820
|
+
/**
|
|
821
|
+
* Pings the Drip API to check connectivity and measure latency.
|
|
822
|
+
*
|
|
823
|
+
* @returns Health status with latency information
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* const health = await drip.ping();
|
|
828
|
+
* if (health.ok) {
|
|
829
|
+
* console.log(`API healthy, latency: ${health.latencyMs}ms`);
|
|
830
|
+
* }
|
|
831
|
+
* ```
|
|
832
|
+
*/
|
|
833
|
+
ping(): Promise<{
|
|
834
|
+
ok: boolean;
|
|
835
|
+
status: string;
|
|
836
|
+
latencyMs: number;
|
|
837
|
+
timestamp: number;
|
|
838
|
+
}>;
|
|
673
839
|
/**
|
|
674
840
|
* Creates a new customer in your Drip account.
|
|
675
841
|
*
|
|
@@ -1214,9 +1380,39 @@ declare class Drip {
|
|
|
1214
1380
|
sequence?: number;
|
|
1215
1381
|
}): string;
|
|
1216
1382
|
/**
|
|
1217
|
-
* Verifies a webhook signature.
|
|
1383
|
+
* Verifies a webhook signature using HMAC-SHA256.
|
|
1218
1384
|
*
|
|
1219
1385
|
* Call this when receiving webhook events to ensure they're authentic.
|
|
1386
|
+
* This is an async method that uses the Web Crypto API for secure verification.
|
|
1387
|
+
*
|
|
1388
|
+
* @param payload - The raw request body (string)
|
|
1389
|
+
* @param signature - The x-drip-signature header value
|
|
1390
|
+
* @param secret - Your webhook secret
|
|
1391
|
+
* @returns Promise resolving to whether the signature is valid
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* ```typescript
|
|
1395
|
+
* app.post('/webhooks/drip', async (req, res) => {
|
|
1396
|
+
* const isValid = await Drip.verifyWebhookSignature(
|
|
1397
|
+
* req.rawBody,
|
|
1398
|
+
* req.headers['x-drip-signature'],
|
|
1399
|
+
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
1400
|
+
* );
|
|
1401
|
+
*
|
|
1402
|
+
* if (!isValid) {
|
|
1403
|
+
* return res.status(401).send('Invalid signature');
|
|
1404
|
+
* }
|
|
1405
|
+
*
|
|
1406
|
+
* // Process the webhook...
|
|
1407
|
+
* });
|
|
1408
|
+
* ```
|
|
1409
|
+
*/
|
|
1410
|
+
static verifyWebhookSignature(payload: string, signature: string, secret: string): Promise<boolean>;
|
|
1411
|
+
/**
|
|
1412
|
+
* Synchronously verifies a webhook signature using HMAC-SHA256.
|
|
1413
|
+
*
|
|
1414
|
+
* This method uses Node.js crypto module and is only available in Node.js environments.
|
|
1415
|
+
* For edge runtimes or browsers, use the async `verifyWebhookSignature` method instead.
|
|
1220
1416
|
*
|
|
1221
1417
|
* @param payload - The raw request body (string)
|
|
1222
1418
|
* @param signature - The x-drip-signature header value
|
|
@@ -1226,7 +1422,7 @@ declare class Drip {
|
|
|
1226
1422
|
* @example
|
|
1227
1423
|
* ```typescript
|
|
1228
1424
|
* app.post('/webhooks/drip', (req, res) => {
|
|
1229
|
-
* const isValid = Drip.
|
|
1425
|
+
* const isValid = Drip.verifyWebhookSignatureSync(
|
|
1230
1426
|
* req.rawBody,
|
|
1231
1427
|
* req.headers['x-drip-signature'],
|
|
1232
1428
|
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
@@ -1240,9 +1436,55 @@ declare class Drip {
|
|
|
1240
1436
|
* });
|
|
1241
1437
|
* ```
|
|
1242
1438
|
*/
|
|
1243
|
-
static
|
|
1439
|
+
static verifyWebhookSignatureSync(payload: string, signature: string, secret: string): boolean;
|
|
1440
|
+
/**
|
|
1441
|
+
* Creates a StreamMeter for accumulating usage and charging once.
|
|
1442
|
+
*
|
|
1443
|
+
* Perfect for LLM token streaming where you want to:
|
|
1444
|
+
* - Accumulate tokens locally (no API call per token)
|
|
1445
|
+
* - Charge once at the end of the stream
|
|
1446
|
+
* - Handle partial failures (charge for what was delivered)
|
|
1447
|
+
*
|
|
1448
|
+
* @param options - StreamMeter configuration
|
|
1449
|
+
* @returns A new StreamMeter instance
|
|
1450
|
+
*
|
|
1451
|
+
* @example
|
|
1452
|
+
* ```typescript
|
|
1453
|
+
* const meter = drip.createStreamMeter({
|
|
1454
|
+
* customerId: 'cust_abc123',
|
|
1455
|
+
* meter: 'tokens',
|
|
1456
|
+
* });
|
|
1457
|
+
*
|
|
1458
|
+
* // Accumulate tokens as they stream
|
|
1459
|
+
* for await (const chunk of llmStream) {
|
|
1460
|
+
* meter.addSync(chunk.tokens);
|
|
1461
|
+
* yield chunk;
|
|
1462
|
+
* }
|
|
1463
|
+
*
|
|
1464
|
+
* // Single charge at end
|
|
1465
|
+
* const result = await meter.flush();
|
|
1466
|
+
* console.log(`Charged ${result.charge?.amountUsdc} for ${result.quantity} tokens`);
|
|
1467
|
+
* ```
|
|
1468
|
+
*
|
|
1469
|
+
* @example
|
|
1470
|
+
* ```typescript
|
|
1471
|
+
* // With auto-flush threshold
|
|
1472
|
+
* const meter = drip.createStreamMeter({
|
|
1473
|
+
* customerId: 'cust_abc123',
|
|
1474
|
+
* meter: 'tokens',
|
|
1475
|
+
* flushThreshold: 10000, // Charge every 10k tokens
|
|
1476
|
+
* });
|
|
1477
|
+
*
|
|
1478
|
+
* for await (const chunk of longStream) {
|
|
1479
|
+
* await meter.add(chunk.tokens); // May auto-flush
|
|
1480
|
+
* }
|
|
1481
|
+
*
|
|
1482
|
+
* await meter.flush(); // Final flush for remaining tokens
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
createStreamMeter(options: StreamMeterOptions): StreamMeter;
|
|
1244
1486
|
}
|
|
1245
1487
|
|
|
1246
1488
|
// @ts-ignore
|
|
1247
1489
|
export = Drip;
|
|
1248
|
-
export { type BalanceResult, type Charge, type ChargeParams, type ChargeResult, type ChargeStatus, type CheckoutParams, type CheckoutResult, type CreateCustomerParams, type CreateWebhookParams, type CreateWebhookResponse, type CreateWorkflowParams, type Customer, type DeleteWebhookResponse, Drip, type DripConfig, DripError, type EmitEventParams, type EndRunParams, type EventResult, type ListChargesOptions, type ListChargesResponse, type ListCustomersOptions, type ListCustomersResponse, type ListMetersResponse, type ListWebhooksResponse, type Meter, type RecordRunEvent, type RecordRunParams, type RecordRunResult, type RunResult, type RunStatus, type RunTimeline, type StartRunParams, type Webhook, type WebhookEventType, type Workflow };
|
|
1490
|
+
export { type BalanceResult, type Charge, type ChargeParams, type ChargeResult, type ChargeStatus, type CheckoutParams, type CheckoutResult, type CreateCustomerParams, type CreateWebhookParams, type CreateWebhookResponse, type CreateWorkflowParams, type Customer, type DeleteWebhookResponse, Drip, type DripConfig, DripError, type EmitEventParams, type EndRunParams, type EventResult, type ListChargesOptions, type ListChargesResponse, type ListCustomersOptions, type ListCustomersResponse, type ListMetersResponse, type ListWebhooksResponse, type Meter, type RecordRunEvent, type RecordRunParams, type RecordRunResult, type RunResult, type RunStatus, type RunTimeline, type StartRunParams, StreamMeter, type StreamMeterFlushResult, type StreamMeterOptions, type Webhook, type WebhookEventType, type Workflow };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,145 @@
|
|
|
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
|
+
|
|
1
143
|
/**
|
|
2
144
|
* Drip SDK - Usage-based billing for Node.js
|
|
3
145
|
*
|
|
@@ -7,6 +149,7 @@
|
|
|
7
149
|
*
|
|
8
150
|
* @packageDocumentation
|
|
9
151
|
*/
|
|
152
|
+
|
|
10
153
|
/**
|
|
11
154
|
* Configuration options for the Drip SDK client.
|
|
12
155
|
*/
|
|
@@ -95,12 +238,16 @@ interface ListCustomersResponse {
|
|
|
95
238
|
interface BalanceResult {
|
|
96
239
|
/** Customer ID */
|
|
97
240
|
customerId: string;
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
/** Balance in
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
|
|
241
|
+
/** On-chain address */
|
|
242
|
+
onchainAddress: string;
|
|
243
|
+
/** Balance in USDC (6 decimals) - matches backend field name */
|
|
244
|
+
balanceUsdc: string;
|
|
245
|
+
/** Pending charges in USDC */
|
|
246
|
+
pendingChargesUsdc: string;
|
|
247
|
+
/** Available USDC (balance minus pending) */
|
|
248
|
+
availableUsdc: string;
|
|
249
|
+
/** ISO timestamp of last balance sync */
|
|
250
|
+
lastSyncedAt: string | null;
|
|
104
251
|
}
|
|
105
252
|
/**
|
|
106
253
|
* Parameters for recording usage and charging a customer.
|
|
@@ -625,7 +772,7 @@ declare class DripError extends Error {
|
|
|
625
772
|
*
|
|
626
773
|
* @example
|
|
627
774
|
* ```typescript
|
|
628
|
-
* import { Drip } from '@drip-
|
|
775
|
+
* import { Drip } from '@drip-sdk/node';
|
|
629
776
|
*
|
|
630
777
|
* const drip = new Drip({
|
|
631
778
|
* apiKey: process.env.DRIP_API_KEY!,
|
|
@@ -670,6 +817,25 @@ declare class Drip {
|
|
|
670
817
|
* @internal
|
|
671
818
|
*/
|
|
672
819
|
private request;
|
|
820
|
+
/**
|
|
821
|
+
* Pings the Drip API to check connectivity and measure latency.
|
|
822
|
+
*
|
|
823
|
+
* @returns Health status with latency information
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* const health = await drip.ping();
|
|
828
|
+
* if (health.ok) {
|
|
829
|
+
* console.log(`API healthy, latency: ${health.latencyMs}ms`);
|
|
830
|
+
* }
|
|
831
|
+
* ```
|
|
832
|
+
*/
|
|
833
|
+
ping(): Promise<{
|
|
834
|
+
ok: boolean;
|
|
835
|
+
status: string;
|
|
836
|
+
latencyMs: number;
|
|
837
|
+
timestamp: number;
|
|
838
|
+
}>;
|
|
673
839
|
/**
|
|
674
840
|
* Creates a new customer in your Drip account.
|
|
675
841
|
*
|
|
@@ -1214,9 +1380,39 @@ declare class Drip {
|
|
|
1214
1380
|
sequence?: number;
|
|
1215
1381
|
}): string;
|
|
1216
1382
|
/**
|
|
1217
|
-
* Verifies a webhook signature.
|
|
1383
|
+
* Verifies a webhook signature using HMAC-SHA256.
|
|
1218
1384
|
*
|
|
1219
1385
|
* Call this when receiving webhook events to ensure they're authentic.
|
|
1386
|
+
* This is an async method that uses the Web Crypto API for secure verification.
|
|
1387
|
+
*
|
|
1388
|
+
* @param payload - The raw request body (string)
|
|
1389
|
+
* @param signature - The x-drip-signature header value
|
|
1390
|
+
* @param secret - Your webhook secret
|
|
1391
|
+
* @returns Promise resolving to whether the signature is valid
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* ```typescript
|
|
1395
|
+
* app.post('/webhooks/drip', async (req, res) => {
|
|
1396
|
+
* const isValid = await Drip.verifyWebhookSignature(
|
|
1397
|
+
* req.rawBody,
|
|
1398
|
+
* req.headers['x-drip-signature'],
|
|
1399
|
+
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
1400
|
+
* );
|
|
1401
|
+
*
|
|
1402
|
+
* if (!isValid) {
|
|
1403
|
+
* return res.status(401).send('Invalid signature');
|
|
1404
|
+
* }
|
|
1405
|
+
*
|
|
1406
|
+
* // Process the webhook...
|
|
1407
|
+
* });
|
|
1408
|
+
* ```
|
|
1409
|
+
*/
|
|
1410
|
+
static verifyWebhookSignature(payload: string, signature: string, secret: string): Promise<boolean>;
|
|
1411
|
+
/**
|
|
1412
|
+
* Synchronously verifies a webhook signature using HMAC-SHA256.
|
|
1413
|
+
*
|
|
1414
|
+
* This method uses Node.js crypto module and is only available in Node.js environments.
|
|
1415
|
+
* For edge runtimes or browsers, use the async `verifyWebhookSignature` method instead.
|
|
1220
1416
|
*
|
|
1221
1417
|
* @param payload - The raw request body (string)
|
|
1222
1418
|
* @param signature - The x-drip-signature header value
|
|
@@ -1226,7 +1422,7 @@ declare class Drip {
|
|
|
1226
1422
|
* @example
|
|
1227
1423
|
* ```typescript
|
|
1228
1424
|
* app.post('/webhooks/drip', (req, res) => {
|
|
1229
|
-
* const isValid = Drip.
|
|
1425
|
+
* const isValid = Drip.verifyWebhookSignatureSync(
|
|
1230
1426
|
* req.rawBody,
|
|
1231
1427
|
* req.headers['x-drip-signature'],
|
|
1232
1428
|
* process.env.DRIP_WEBHOOK_SECRET!,
|
|
@@ -1240,7 +1436,53 @@ declare class Drip {
|
|
|
1240
1436
|
* });
|
|
1241
1437
|
* ```
|
|
1242
1438
|
*/
|
|
1243
|
-
static
|
|
1439
|
+
static verifyWebhookSignatureSync(payload: string, signature: string, secret: string): boolean;
|
|
1440
|
+
/**
|
|
1441
|
+
* Creates a StreamMeter for accumulating usage and charging once.
|
|
1442
|
+
*
|
|
1443
|
+
* Perfect for LLM token streaming where you want to:
|
|
1444
|
+
* - Accumulate tokens locally (no API call per token)
|
|
1445
|
+
* - Charge once at the end of the stream
|
|
1446
|
+
* - Handle partial failures (charge for what was delivered)
|
|
1447
|
+
*
|
|
1448
|
+
* @param options - StreamMeter configuration
|
|
1449
|
+
* @returns A new StreamMeter instance
|
|
1450
|
+
*
|
|
1451
|
+
* @example
|
|
1452
|
+
* ```typescript
|
|
1453
|
+
* const meter = drip.createStreamMeter({
|
|
1454
|
+
* customerId: 'cust_abc123',
|
|
1455
|
+
* meter: 'tokens',
|
|
1456
|
+
* });
|
|
1457
|
+
*
|
|
1458
|
+
* // Accumulate tokens as they stream
|
|
1459
|
+
* for await (const chunk of llmStream) {
|
|
1460
|
+
* meter.addSync(chunk.tokens);
|
|
1461
|
+
* yield chunk;
|
|
1462
|
+
* }
|
|
1463
|
+
*
|
|
1464
|
+
* // Single charge at end
|
|
1465
|
+
* const result = await meter.flush();
|
|
1466
|
+
* console.log(`Charged ${result.charge?.amountUsdc} for ${result.quantity} tokens`);
|
|
1467
|
+
* ```
|
|
1468
|
+
*
|
|
1469
|
+
* @example
|
|
1470
|
+
* ```typescript
|
|
1471
|
+
* // With auto-flush threshold
|
|
1472
|
+
* const meter = drip.createStreamMeter({
|
|
1473
|
+
* customerId: 'cust_abc123',
|
|
1474
|
+
* meter: 'tokens',
|
|
1475
|
+
* flushThreshold: 10000, // Charge every 10k tokens
|
|
1476
|
+
* });
|
|
1477
|
+
*
|
|
1478
|
+
* for await (const chunk of longStream) {
|
|
1479
|
+
* await meter.add(chunk.tokens); // May auto-flush
|
|
1480
|
+
* }
|
|
1481
|
+
*
|
|
1482
|
+
* await meter.flush(); // Final flush for remaining tokens
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
createStreamMeter(options: StreamMeterOptions): StreamMeter;
|
|
1244
1486
|
}
|
|
1245
1487
|
|
|
1246
|
-
export { type BalanceResult, type Charge, type ChargeParams, type ChargeResult, type ChargeStatus, type CheckoutParams, type CheckoutResult, type CreateCustomerParams, type CreateWebhookParams, type CreateWebhookResponse, type CreateWorkflowParams, type Customer, type DeleteWebhookResponse, Drip, type DripConfig, DripError, type EmitEventParams, type EndRunParams, type EventResult, type ListChargesOptions, type ListChargesResponse, type ListCustomersOptions, type ListCustomersResponse, type ListMetersResponse, type ListWebhooksResponse, type Meter, type RecordRunEvent, type RecordRunParams, type RecordRunResult, type RunResult, type RunStatus, type RunTimeline, type StartRunParams, type Webhook, type WebhookEventType, type Workflow, Drip as default };
|
|
1488
|
+
export { type BalanceResult, type Charge, type ChargeParams, type ChargeResult, type ChargeStatus, type CheckoutParams, type CheckoutResult, type CreateCustomerParams, type CreateWebhookParams, type CreateWebhookResponse, type CreateWorkflowParams, type Customer, type DeleteWebhookResponse, Drip, type DripConfig, DripError, type EmitEventParams, type EndRunParams, type EventResult, type ListChargesOptions, type ListChargesResponse, type ListCustomersOptions, type ListCustomersResponse, type ListMetersResponse, type ListWebhooksResponse, type Meter, type RecordRunEvent, type RecordRunParams, type RecordRunResult, type RunResult, type RunStatus, type RunTimeline, type StartRunParams, StreamMeter, type StreamMeterFlushResult, type StreamMeterOptions, type Webhook, type WebhookEventType, type Workflow, Drip as default };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
export{
|
|
1
|
+
var R=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});var m=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(t,e){this._chargeFn=t,this._options=e;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(t){return t<=0?null:(this._total+=t,this._options.onAdd?.(t,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(t){t<=0||(this._total+=t,this._options.onAdd?.(t,this._total));}async flush(){let t=this._total;if(this._total=0,t===0)return {success:true,quantity:0,charge:null,isReplay:false};let e=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:void 0,r=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:t,idempotencyKey:e,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let s={success:r.success,quantity:t,charge:r.charge,isReplay:r.isReplay};return this._options.onFlush?.(s),s}reset(){this._total=0;}};var d=class a extends Error{constructor(e,r,s){super(e);this.statusCode=r;this.code=s;this.name="DripError",Object.setPrototypeOf(this,a.prototype);}},y=class{apiKey;baseUrl;timeout;constructor(t){if(!t.apiKey)throw new Error("Drip API key is required");this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||"https://api.drip.dev/v1",this.timeout=t.timeout||3e4;}async request(t,e={}){let r=new AbortController,s=setTimeout(()=>r.abort(),this.timeout);try{let n=await fetch(`${this.baseUrl}${t}`,{...e,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...e.headers}});if(n.status===204)return {success:!0};let i=await n.json();if(!n.ok)throw new d(i.message||i.error||"Request failed",n.status,i.code);return i}catch(n){throw n instanceof d?n:n instanceof Error&&n.name==="AbortError"?new d("Request timed out",408,"TIMEOUT"):new d(n instanceof Error?n.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(s);}}async ping(){let t=Date.now(),e=await fetch(`${this.baseUrl.replace("/v1","")}/health`,{headers:{Authorization:`Bearer ${this.apiKey}`}}),r=Date.now()-t,s=await e.json();return {ok:s.status==="healthy",status:s.status,latencyMs:r,timestamp:s.timestamp}}async createCustomer(t){return this.request("/customers",{method:"POST",body:JSON.stringify(t)})}async getCustomer(t){return this.request(`/customers/${t}`)}async listCustomers(t){let e=new URLSearchParams;t?.limit&&e.set("limit",t.limit.toString()),t?.status&&e.set("status",t.status);let r=e.toString(),s=r?`/customers?${r}`:"/customers";return this.request(s)}async getBalance(t){return this.request(`/customers/${t}/balance`)}async charge(t){return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:t.customerId,usageType:t.meter,quantity:t.quantity,idempotencyKey:t.idempotencyKey,metadata:t.metadata})})}async getCharge(t){return this.request(`/charges/${t}`)}async listCharges(t){let e=new URLSearchParams;t?.customerId&&e.set("customerId",t.customerId),t?.status&&e.set("status",t.status),t?.limit&&e.set("limit",t.limit.toString()),t?.offset&&e.set("offset",t.offset.toString());let r=e.toString(),s=r?`/charges?${r}`:"/charges";return this.request(s)}async getChargeStatus(t){return this.request(`/charges/${t}/status`)}async checkout(t){let e=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:t.customerId,external_customer_id:t.externalCustomerId,amount:t.amount,return_url:t.returnUrl,cancel_url:t.cancelUrl,metadata:t.metadata})});return {id:e.id,url:e.url,expiresAt:e.expires_at,amountUsd:e.amount_usd}}async createWebhook(t){return this.request("/webhooks",{method:"POST",body:JSON.stringify(t)})}async listWebhooks(){return this.request("/webhooks")}async getWebhook(t){return this.request(`/webhooks/${t}`)}async deleteWebhook(t){return this.request(`/webhooks/${t}`,{method:"DELETE"})}async testWebhook(t){return this.request(`/webhooks/${t}/test`,{method:"POST"})}async rotateWebhookSecret(t){return this.request(`/webhooks/${t}/rotate-secret`,{method:"POST"})}async createWorkflow(t){return this.request("/workflows",{method:"POST",body:JSON.stringify(t)})}async listWorkflows(){return this.request("/workflows")}async startRun(t){return this.request("/runs",{method:"POST",body:JSON.stringify(t)})}async endRun(t,e){return this.request(`/runs/${t}`,{method:"PATCH",body:JSON.stringify(e)})}async getRunTimeline(t){return this.request(`/runs/${t}`)}async emitEvent(t){return this.request("/events",{method:"POST",body:JSON.stringify(t)})}async emitEventsBatch(t){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:t})})}async listMeters(){let t=await this.request("/pricing-plans");return {data:t.data.map(e=>({id:e.id,name:e.name,meter:e.unitType,unitPriceUsd:e.unitPriceUsd,isActive:e.isActive})),count:t.count}}async recordRun(t){let e=Date.now(),r=t.workflow,s=t.workflow;if(!t.workflow.startsWith("wf_"))try{let l=(await this.listWorkflows()).data.find(o=>o.slug===t.workflow||o.id===t.workflow);if(l)r=l.id,s=l.name;else {let o=await this.createWorkflow({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,f=>f.toUpperCase()),slug:t.workflow,productSurface:"AGENT"});r=o.id,s=o.name;}}catch{r=t.workflow;}let n=await this.startRun({customerId:t.customerId,workflowId:r,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata}),i=0,u=0;if(t.events.length>0){let b=t.events.map((o,f)=>({runId:n.id,eventType:o.eventType,quantity:o.quantity,units:o.units,description:o.description,costUnits:o.costUnits,metadata:o.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${o.eventType}:${f}`:void 0})),l=await this.emitEventsBatch(b);i=l.created,u=l.duplicates;}let g=await this.endRun(n.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),h=Date.now()-e,p=t.events.length>0?`${i} events recorded`:"no events",w=`${t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB"} ${s}: ${p} (${g.durationMs??h}ms)`;return {run:{id:n.id,workflowId:r,workflowName:s,status:t.status,durationMs:g.durationMs},events:{created:i,duplicates:u},totalCostUnits:g.totalCostUnits,summary:w}}static generateIdempotencyKey(t){let e=[t.customerId,t.runId??"no_run",t.stepName,String(t.sequence??0)],r=0,s=e.join("|");for(let n=0;n<s.length;n++){let i=s.charCodeAt(n);r=(r<<5)-r+i,r=r&r;}return `drip_${Math.abs(r).toString(36)}_${t.stepName.slice(0,16)}`}static async verifyWebhookSignature(t,e,r){if(!t||!e||!r)return false;try{let s=new TextEncoder,n=s.encode(r),i=s.encode(t),u=await crypto.subtle.importKey("raw",n,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),g=await crypto.subtle.sign("HMAC",u,i),h=Array.from(new Uint8Array(g)).map(c=>c.toString(16).padStart(2,"0")).join("");if(e.length!==h.length)return !1;let p=0;for(let c=0;c<e.length;c++)p|=e.charCodeAt(c)^h.charCodeAt(c);return p===0}catch{return false}}static verifyWebhookSignatureSync(t,e,r){if(!t||!e||!r)return false;try{let s=R("crypto"),n=s.createHmac("sha256",r).update(t).digest("hex"),i=Buffer.from(e),u=Buffer.from(n);return i.length!==u.length?!1:s.timingSafeEqual(i,u)}catch{return false}}createStreamMeter(t){return new m(this.charge.bind(this),t)}},E=y;
|
|
2
|
+
export{y as Drip,d as DripError,m as StreamMeter,E as default};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|