@drip-sdk/node 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +159 -1
- package/dist/index.d.ts +159 -1
- 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
|
@@ -150,6 +150,31 @@ declare class StreamMeter {
|
|
|
150
150
|
* @packageDocumentation
|
|
151
151
|
*/
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Retry options for API calls.
|
|
155
|
+
*/
|
|
156
|
+
interface RetryOptions {
|
|
157
|
+
/**
|
|
158
|
+
* Maximum number of retry attempts.
|
|
159
|
+
* @default 3
|
|
160
|
+
*/
|
|
161
|
+
maxAttempts?: number;
|
|
162
|
+
/**
|
|
163
|
+
* Base delay between retries in milliseconds (exponential backoff).
|
|
164
|
+
* @default 100
|
|
165
|
+
*/
|
|
166
|
+
baseDelayMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Maximum delay between retries in milliseconds.
|
|
169
|
+
* @default 5000
|
|
170
|
+
*/
|
|
171
|
+
maxDelayMs?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Custom function to determine if an error is retryable.
|
|
174
|
+
* By default, retries on network errors and 5xx status codes.
|
|
175
|
+
*/
|
|
176
|
+
isRetryable?: (error: unknown) => boolean;
|
|
177
|
+
}
|
|
153
178
|
/**
|
|
154
179
|
* Configuration options for the Drip SDK client.
|
|
155
180
|
*/
|
|
@@ -807,6 +832,63 @@ interface RunTimeline {
|
|
|
807
832
|
};
|
|
808
833
|
summary: string;
|
|
809
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Parameters for wrapping an external API call with usage tracking.
|
|
837
|
+
* This ensures usage is recorded even if there's a crash/failure after the API call.
|
|
838
|
+
*/
|
|
839
|
+
interface WrapApiCallParams<T> {
|
|
840
|
+
/**
|
|
841
|
+
* The Drip customer ID to charge.
|
|
842
|
+
*/
|
|
843
|
+
customerId: string;
|
|
844
|
+
/**
|
|
845
|
+
* The usage meter/type to record against.
|
|
846
|
+
* Must match a meter configured in your pricing plan.
|
|
847
|
+
*/
|
|
848
|
+
meter: string;
|
|
849
|
+
/**
|
|
850
|
+
* The async function that makes the external API call.
|
|
851
|
+
* This is the call you want to track (e.g., OpenAI, Anthropic, etc.)
|
|
852
|
+
*/
|
|
853
|
+
call: () => Promise<T>;
|
|
854
|
+
/**
|
|
855
|
+
* Function to extract the usage quantity from the API call result.
|
|
856
|
+
* @example (result) => result.usage.total_tokens
|
|
857
|
+
*/
|
|
858
|
+
extractUsage: (result: T) => number;
|
|
859
|
+
/**
|
|
860
|
+
* Custom idempotency key prefix.
|
|
861
|
+
* If not provided, a unique key is generated.
|
|
862
|
+
* The key ensures retries don't double-charge.
|
|
863
|
+
*/
|
|
864
|
+
idempotencyKey?: string;
|
|
865
|
+
/**
|
|
866
|
+
* Additional metadata to attach to this usage event.
|
|
867
|
+
*/
|
|
868
|
+
metadata?: Record<string, unknown>;
|
|
869
|
+
/**
|
|
870
|
+
* Retry configuration for the Drip charge call.
|
|
871
|
+
* The external API call is NOT retried (only called once).
|
|
872
|
+
*/
|
|
873
|
+
retryOptions?: RetryOptions;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Result of a wrapped API call.
|
|
877
|
+
*/
|
|
878
|
+
interface WrapApiCallResult<T> {
|
|
879
|
+
/**
|
|
880
|
+
* The result from the external API call.
|
|
881
|
+
*/
|
|
882
|
+
result: T;
|
|
883
|
+
/**
|
|
884
|
+
* The charge result from Drip.
|
|
885
|
+
*/
|
|
886
|
+
charge: ChargeResult;
|
|
887
|
+
/**
|
|
888
|
+
* The idempotency key used (useful for debugging).
|
|
889
|
+
*/
|
|
890
|
+
idempotencyKey: string;
|
|
891
|
+
}
|
|
810
892
|
/**
|
|
811
893
|
* Error thrown by Drip SDK operations.
|
|
812
894
|
*/
|
|
@@ -982,6 +1064,82 @@ declare class Drip {
|
|
|
982
1064
|
* ```
|
|
983
1065
|
*/
|
|
984
1066
|
charge(params: ChargeParams): Promise<ChargeResult>;
|
|
1067
|
+
/**
|
|
1068
|
+
* Wraps an external API call with guaranteed usage recording.
|
|
1069
|
+
*
|
|
1070
|
+
* **This solves the crash-before-record problem:**
|
|
1071
|
+
* ```typescript
|
|
1072
|
+
* // DANGEROUS - usage lost if crash between lines 1 and 2:
|
|
1073
|
+
* const response = await openai.chat.completions.create({...}); // line 1
|
|
1074
|
+
* await drip.charge({ tokens: response.usage.total_tokens }); // line 2
|
|
1075
|
+
*
|
|
1076
|
+
* // SAFE - wrapApiCall guarantees recording with retry:
|
|
1077
|
+
* const { result } = await drip.wrapApiCall({
|
|
1078
|
+
* call: () => openai.chat.completions.create({...}),
|
|
1079
|
+
* extractUsage: (r) => r.usage.total_tokens,
|
|
1080
|
+
* ...
|
|
1081
|
+
* });
|
|
1082
|
+
* ```
|
|
1083
|
+
*
|
|
1084
|
+
* How it works:
|
|
1085
|
+
* 1. Generates idempotency key BEFORE the API call
|
|
1086
|
+
* 2. Makes the external API call (once, no retry)
|
|
1087
|
+
* 3. Records usage in Drip with retry + idempotency
|
|
1088
|
+
* 4. If recording fails transiently, retries are safe (no double-charge)
|
|
1089
|
+
*
|
|
1090
|
+
* @param params - Wrap parameters including the call and usage extractor
|
|
1091
|
+
* @returns The API result and charge details
|
|
1092
|
+
* @throws {DripError} If the Drip charge fails after retries
|
|
1093
|
+
* @throws {Error} If the external API call fails
|
|
1094
|
+
*
|
|
1095
|
+
* @example
|
|
1096
|
+
* ```typescript
|
|
1097
|
+
* // OpenAI example
|
|
1098
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1099
|
+
* customerId: 'cust_abc123',
|
|
1100
|
+
* meter: 'tokens',
|
|
1101
|
+
* call: () => openai.chat.completions.create({
|
|
1102
|
+
* model: 'gpt-4',
|
|
1103
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1104
|
+
* }),
|
|
1105
|
+
* extractUsage: (r) => r.usage?.total_tokens ?? 0,
|
|
1106
|
+
* });
|
|
1107
|
+
*
|
|
1108
|
+
* console.log(result.choices[0].message.content);
|
|
1109
|
+
* console.log(`Charged: ${charge.charge.amountUsdc} USDC`);
|
|
1110
|
+
* ```
|
|
1111
|
+
*
|
|
1112
|
+
* @example
|
|
1113
|
+
* ```typescript
|
|
1114
|
+
* // Anthropic example
|
|
1115
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1116
|
+
* customerId: 'cust_abc123',
|
|
1117
|
+
* meter: 'tokens',
|
|
1118
|
+
* call: () => anthropic.messages.create({
|
|
1119
|
+
* model: 'claude-3-opus-20240229',
|
|
1120
|
+
* max_tokens: 1024,
|
|
1121
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1122
|
+
* }),
|
|
1123
|
+
* extractUsage: (r) => r.usage.input_tokens + r.usage.output_tokens,
|
|
1124
|
+
* });
|
|
1125
|
+
* ```
|
|
1126
|
+
*
|
|
1127
|
+
* @example
|
|
1128
|
+
* ```typescript
|
|
1129
|
+
* // With custom retry options
|
|
1130
|
+
* const { result } = await drip.wrapApiCall({
|
|
1131
|
+
* customerId: 'cust_abc123',
|
|
1132
|
+
* meter: 'api_calls',
|
|
1133
|
+
* call: () => fetch('https://api.example.com/expensive'),
|
|
1134
|
+
* extractUsage: () => 1, // Fixed cost per call
|
|
1135
|
+
* retryOptions: {
|
|
1136
|
+
* maxAttempts: 5,
|
|
1137
|
+
* baseDelayMs: 200,
|
|
1138
|
+
* },
|
|
1139
|
+
* });
|
|
1140
|
+
* ```
|
|
1141
|
+
*/
|
|
1142
|
+
wrapApiCall<T>(params: WrapApiCallParams<T>): Promise<WrapApiCallResult<T>>;
|
|
985
1143
|
/**
|
|
986
1144
|
* Records usage for internal visibility WITHOUT billing.
|
|
987
1145
|
*
|
|
@@ -1595,4 +1753,4 @@ declare class Drip {
|
|
|
1595
1753
|
|
|
1596
1754
|
// @ts-ignore
|
|
1597
1755
|
export = Drip;
|
|
1598
|
-
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 TrackUsageParams, type TrackUsageResult, type Webhook, type WebhookEventType, type Workflow };
|
|
1756
|
+
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 RetryOptions, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -150,6 +150,31 @@ declare class StreamMeter {
|
|
|
150
150
|
* @packageDocumentation
|
|
151
151
|
*/
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Retry options for API calls.
|
|
155
|
+
*/
|
|
156
|
+
interface RetryOptions {
|
|
157
|
+
/**
|
|
158
|
+
* Maximum number of retry attempts.
|
|
159
|
+
* @default 3
|
|
160
|
+
*/
|
|
161
|
+
maxAttempts?: number;
|
|
162
|
+
/**
|
|
163
|
+
* Base delay between retries in milliseconds (exponential backoff).
|
|
164
|
+
* @default 100
|
|
165
|
+
*/
|
|
166
|
+
baseDelayMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Maximum delay between retries in milliseconds.
|
|
169
|
+
* @default 5000
|
|
170
|
+
*/
|
|
171
|
+
maxDelayMs?: number;
|
|
172
|
+
/**
|
|
173
|
+
* Custom function to determine if an error is retryable.
|
|
174
|
+
* By default, retries on network errors and 5xx status codes.
|
|
175
|
+
*/
|
|
176
|
+
isRetryable?: (error: unknown) => boolean;
|
|
177
|
+
}
|
|
153
178
|
/**
|
|
154
179
|
* Configuration options for the Drip SDK client.
|
|
155
180
|
*/
|
|
@@ -807,6 +832,63 @@ interface RunTimeline {
|
|
|
807
832
|
};
|
|
808
833
|
summary: string;
|
|
809
834
|
}
|
|
835
|
+
/**
|
|
836
|
+
* Parameters for wrapping an external API call with usage tracking.
|
|
837
|
+
* This ensures usage is recorded even if there's a crash/failure after the API call.
|
|
838
|
+
*/
|
|
839
|
+
interface WrapApiCallParams<T> {
|
|
840
|
+
/**
|
|
841
|
+
* The Drip customer ID to charge.
|
|
842
|
+
*/
|
|
843
|
+
customerId: string;
|
|
844
|
+
/**
|
|
845
|
+
* The usage meter/type to record against.
|
|
846
|
+
* Must match a meter configured in your pricing plan.
|
|
847
|
+
*/
|
|
848
|
+
meter: string;
|
|
849
|
+
/**
|
|
850
|
+
* The async function that makes the external API call.
|
|
851
|
+
* This is the call you want to track (e.g., OpenAI, Anthropic, etc.)
|
|
852
|
+
*/
|
|
853
|
+
call: () => Promise<T>;
|
|
854
|
+
/**
|
|
855
|
+
* Function to extract the usage quantity from the API call result.
|
|
856
|
+
* @example (result) => result.usage.total_tokens
|
|
857
|
+
*/
|
|
858
|
+
extractUsage: (result: T) => number;
|
|
859
|
+
/**
|
|
860
|
+
* Custom idempotency key prefix.
|
|
861
|
+
* If not provided, a unique key is generated.
|
|
862
|
+
* The key ensures retries don't double-charge.
|
|
863
|
+
*/
|
|
864
|
+
idempotencyKey?: string;
|
|
865
|
+
/**
|
|
866
|
+
* Additional metadata to attach to this usage event.
|
|
867
|
+
*/
|
|
868
|
+
metadata?: Record<string, unknown>;
|
|
869
|
+
/**
|
|
870
|
+
* Retry configuration for the Drip charge call.
|
|
871
|
+
* The external API call is NOT retried (only called once).
|
|
872
|
+
*/
|
|
873
|
+
retryOptions?: RetryOptions;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Result of a wrapped API call.
|
|
877
|
+
*/
|
|
878
|
+
interface WrapApiCallResult<T> {
|
|
879
|
+
/**
|
|
880
|
+
* The result from the external API call.
|
|
881
|
+
*/
|
|
882
|
+
result: T;
|
|
883
|
+
/**
|
|
884
|
+
* The charge result from Drip.
|
|
885
|
+
*/
|
|
886
|
+
charge: ChargeResult;
|
|
887
|
+
/**
|
|
888
|
+
* The idempotency key used (useful for debugging).
|
|
889
|
+
*/
|
|
890
|
+
idempotencyKey: string;
|
|
891
|
+
}
|
|
810
892
|
/**
|
|
811
893
|
* Error thrown by Drip SDK operations.
|
|
812
894
|
*/
|
|
@@ -982,6 +1064,82 @@ declare class Drip {
|
|
|
982
1064
|
* ```
|
|
983
1065
|
*/
|
|
984
1066
|
charge(params: ChargeParams): Promise<ChargeResult>;
|
|
1067
|
+
/**
|
|
1068
|
+
* Wraps an external API call with guaranteed usage recording.
|
|
1069
|
+
*
|
|
1070
|
+
* **This solves the crash-before-record problem:**
|
|
1071
|
+
* ```typescript
|
|
1072
|
+
* // DANGEROUS - usage lost if crash between lines 1 and 2:
|
|
1073
|
+
* const response = await openai.chat.completions.create({...}); // line 1
|
|
1074
|
+
* await drip.charge({ tokens: response.usage.total_tokens }); // line 2
|
|
1075
|
+
*
|
|
1076
|
+
* // SAFE - wrapApiCall guarantees recording with retry:
|
|
1077
|
+
* const { result } = await drip.wrapApiCall({
|
|
1078
|
+
* call: () => openai.chat.completions.create({...}),
|
|
1079
|
+
* extractUsage: (r) => r.usage.total_tokens,
|
|
1080
|
+
* ...
|
|
1081
|
+
* });
|
|
1082
|
+
* ```
|
|
1083
|
+
*
|
|
1084
|
+
* How it works:
|
|
1085
|
+
* 1. Generates idempotency key BEFORE the API call
|
|
1086
|
+
* 2. Makes the external API call (once, no retry)
|
|
1087
|
+
* 3. Records usage in Drip with retry + idempotency
|
|
1088
|
+
* 4. If recording fails transiently, retries are safe (no double-charge)
|
|
1089
|
+
*
|
|
1090
|
+
* @param params - Wrap parameters including the call and usage extractor
|
|
1091
|
+
* @returns The API result and charge details
|
|
1092
|
+
* @throws {DripError} If the Drip charge fails after retries
|
|
1093
|
+
* @throws {Error} If the external API call fails
|
|
1094
|
+
*
|
|
1095
|
+
* @example
|
|
1096
|
+
* ```typescript
|
|
1097
|
+
* // OpenAI example
|
|
1098
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1099
|
+
* customerId: 'cust_abc123',
|
|
1100
|
+
* meter: 'tokens',
|
|
1101
|
+
* call: () => openai.chat.completions.create({
|
|
1102
|
+
* model: 'gpt-4',
|
|
1103
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1104
|
+
* }),
|
|
1105
|
+
* extractUsage: (r) => r.usage?.total_tokens ?? 0,
|
|
1106
|
+
* });
|
|
1107
|
+
*
|
|
1108
|
+
* console.log(result.choices[0].message.content);
|
|
1109
|
+
* console.log(`Charged: ${charge.charge.amountUsdc} USDC`);
|
|
1110
|
+
* ```
|
|
1111
|
+
*
|
|
1112
|
+
* @example
|
|
1113
|
+
* ```typescript
|
|
1114
|
+
* // Anthropic example
|
|
1115
|
+
* const { result, charge } = await drip.wrapApiCall({
|
|
1116
|
+
* customerId: 'cust_abc123',
|
|
1117
|
+
* meter: 'tokens',
|
|
1118
|
+
* call: () => anthropic.messages.create({
|
|
1119
|
+
* model: 'claude-3-opus-20240229',
|
|
1120
|
+
* max_tokens: 1024,
|
|
1121
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
1122
|
+
* }),
|
|
1123
|
+
* extractUsage: (r) => r.usage.input_tokens + r.usage.output_tokens,
|
|
1124
|
+
* });
|
|
1125
|
+
* ```
|
|
1126
|
+
*
|
|
1127
|
+
* @example
|
|
1128
|
+
* ```typescript
|
|
1129
|
+
* // With custom retry options
|
|
1130
|
+
* const { result } = await drip.wrapApiCall({
|
|
1131
|
+
* customerId: 'cust_abc123',
|
|
1132
|
+
* meter: 'api_calls',
|
|
1133
|
+
* call: () => fetch('https://api.example.com/expensive'),
|
|
1134
|
+
* extractUsage: () => 1, // Fixed cost per call
|
|
1135
|
+
* retryOptions: {
|
|
1136
|
+
* maxAttempts: 5,
|
|
1137
|
+
* baseDelayMs: 200,
|
|
1138
|
+
* },
|
|
1139
|
+
* });
|
|
1140
|
+
* ```
|
|
1141
|
+
*/
|
|
1142
|
+
wrapApiCall<T>(params: WrapApiCallParams<T>): Promise<WrapApiCallResult<T>>;
|
|
985
1143
|
/**
|
|
986
1144
|
* Records usage for internal visibility WITHOUT billing.
|
|
987
1145
|
*
|
|
@@ -1593,4 +1751,4 @@ declare class Drip {
|
|
|
1593
1751
|
createStreamMeter(options: StreamMeterOptions): StreamMeter;
|
|
1594
1752
|
}
|
|
1595
1753
|
|
|
1596
|
-
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 TrackUsageParams, type TrackUsageResult, type Webhook, type WebhookEventType, type Workflow, Drip as default };
|
|
1754
|
+
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 RetryOptions, 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, Drip as default };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
export{
|
|
1
|
+
var R=(u=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(u,{get:(t,e)=>(typeof require<"u"?require:t)[e]}):u)(function(u){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+u+'" is not supported')});var w=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 n={success:r.success,quantity:t,charge:r.charge,isReplay:r.isReplay};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var C={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function E(u){return u instanceof Error&&(u.message.includes("fetch")||u.message.includes("network"))?true:u instanceof h?u.statusCode>=500||u.statusCode===408||u.statusCode===429:false}async function v(u,t={}){let e=t.maxAttempts??C.maxAttempts,r=t.baseDelayMs??C.baseDelayMs,n=t.maxDelayMs??C.maxDelayMs,s=t.isRetryable??E,i;for(let o=1;o<=e;o++)try{return await u()}catch(c){if(i=c,o===e||!s(c))throw c;let l=Math.min(r*Math.pow(2,o-1)+Math.random()*100,n);await new Promise(f=>setTimeout(f,l));}throw i}var h=class u extends Error{constructor(e,r,n){super(e);this.statusCode=r;this.code=n;this.name="DripError",Object.setPrototypeOf(this,u.prototype);}},k=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,n=setTimeout(()=>r.abort(),this.timeout);try{let s=await fetch(`${this.baseUrl}${t}`,{...e,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...e.headers}});if(s.status===204)return {success:!0};let i=await s.json();if(!s.ok)throw new h(i.message||i.error||"Request failed",s.status,i.code);return i}catch(s){throw s instanceof h?s:s instanceof Error&&s.name==="AbortError"?new h("Request timed out",408,"TIMEOUT"):new h(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let t=new AbortController,e=setTimeout(()=>t.abort(),this.timeout),r=this.baseUrl;r.endsWith("/v1/")?r=r.slice(0,-4):r.endsWith("/v1")&&(r=r.slice(0,-3)),r=r.replace(/\/+$/,"");let n=Date.now();try{let s=await fetch(`${r}/health`,{signal:t.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),i=Date.now()-n,o="unknown",c=Date.now();try{let l=await s.json();typeof l.status=="string"&&(o=l.status),typeof l.timestamp=="number"&&(c=l.timestamp);}catch{o=s.ok?"healthy":`error:${s.status}`;}return !s.ok&&o==="unknown"&&(o=`error:${s.status}`),{ok:s.ok&&o==="healthy",status:o,latencyMs:i,timestamp:c}}catch(s){throw s instanceof Error&&s.name==="AbortError"?new h("Request timed out",408,"TIMEOUT"):new h(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(e);}}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(),n=r?`/customers?${r}`:"/customers";return this.request(n)}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 wrapApiCall(t){let e=t.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,r=await t.call(),n=t.extractUsage(r),s=await v(()=>this.charge({customerId:t.customerId,meter:t.meter,quantity:n,idempotencyKey:e,metadata:t.metadata}),t.retryOptions);return {result:r,charge:s,idempotencyKey:e}}async trackUsage(t){return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:t.customerId,usageType:t.meter,quantity:t.quantity,idempotencyKey:t.idempotencyKey,units:t.units,description:t.description,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(),n=r?`/charges?${r}`:"/charges";return this.request(n)}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,n=t.workflow;if(!t.workflow.startsWith("wf_"))try{let d=(await this.listWorkflows()).data.find(a=>a.slug===t.workflow||a.id===t.workflow);if(d)r=d.id,n=d.name;else {let a=await this.createWorkflow({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,g=>g.toUpperCase()),slug:t.workflow,productSurface:"AGENT"});r=a.id,n=a.name;}}catch{r=t.workflow;}let s=await this.startRun({customerId:t.customerId,workflowId:r,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata}),i=0,o=0;if(t.events.length>0){let y=t.events.map((a,g)=>({runId:s.id,eventType:a.eventType,quantity:a.quantity,units:a.units,description:a.description,costUnits:a.costUnits,metadata:a.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${a.eventType}:${g}`:void 0})),d=await this.emitEventsBatch(y);i=d.created,o=d.duplicates;}let c=await this.endRun(s.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),l=Date.now()-e,f=t.events.length>0?`${i} events recorded`:"no events",p=`${t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB"} ${n}: ${f} (${c.durationMs??l}ms)`;return {run:{id:s.id,workflowId:r,workflowName:n,status:t.status,durationMs:c.durationMs},events:{created:i,duplicates:o},totalCostUnits:c.totalCostUnits,summary:p}}static generateIdempotencyKey(t){let e=[t.customerId,t.runId??"no_run",t.stepName,String(t.sequence??0)],r=0,n=e.join("|");for(let s=0;s<n.length;s++){let i=n.charCodeAt(s);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,n=300){if(!t||!e||!r)return false;try{let s=e.split(","),i=s.find(m=>m.startsWith("t=")),o=s.find(m=>m.startsWith("v1="));if(!i||!o)return !1;let c=parseInt(i.slice(2),10),l=o.slice(3);if(isNaN(c))return !1;let f=Math.floor(Date.now()/1e3);if(Math.abs(f-c)>n)return !1;let b=`${c}.${t}`,p=new TextEncoder,y=p.encode(r),d=p.encode(b),a=globalThis.crypto?.subtle??R("crypto").webcrypto.subtle,g=await a.importKey("raw",y,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),x=await a.sign("HMAC",g,d),I=Array.from(new Uint8Array(x)).map(m=>m.toString(16).padStart(2,"0")).join("");if(l.length!==I.length)return !1;let P=0;for(let m=0;m<l.length;m++)P|=l.charCodeAt(m)^I.charCodeAt(m);return P===0}catch{return false}}static verifyWebhookSignatureSync(t,e,r,n=300){if(!t||!e||!r)return false;try{let s=e.split(","),i=s.find(g=>g.startsWith("t=")),o=s.find(g=>g.startsWith("v1="));if(!i||!o)return !1;let c=parseInt(i.slice(2),10),l=o.slice(3);if(isNaN(c))return !1;let f=Math.floor(Date.now()/1e3);if(Math.abs(f-c)>n)return !1;let b=R("crypto"),p=`${c}.${t}`,y=b.createHmac("sha256",r).update(p).digest("hex"),d=Buffer.from(l),a=Buffer.from(y);return d.length!==a.length?!1:b.timingSafeEqual(d,a)}catch{return false}}static generateWebhookSignature(t,e,r){let n=R("crypto"),s=r??Math.floor(Date.now()/1e3),i=`${s}.${t}`,o=n.createHmac("sha256",e).update(i).digest("hex");return `t=${s},v1=${o}`}createStreamMeter(t){return new w(this.charge.bind(this),t)}},_=k;
|
|
2
|
+
export{k as Drip,h as DripError,w as StreamMeter,_ as default};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|