@drip-sdk/node 1.1.0 → 1.1.1
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 +159 -293
- package/dist/core.cjs +2 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +165 -43
- package/dist/core.d.ts +165 -43
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- 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 +171 -54
- package/dist/index.d.ts +171 -54
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +2 -2
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.d.cts +2 -2
- package/dist/langchain.d.ts +2 -2
- package/dist/langchain.js +2 -2
- package/dist/langchain.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.ts
CHANGED
|
@@ -498,17 +498,29 @@ interface RetryOptions {
|
|
|
498
498
|
}
|
|
499
499
|
/**
|
|
500
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)
|
|
501
505
|
*/
|
|
502
506
|
interface DripConfig {
|
|
503
507
|
/**
|
|
504
508
|
* Your Drip API key. Obtain this from the Drip dashboard.
|
|
505
|
-
*
|
|
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..."
|
|
506
518
|
*/
|
|
507
|
-
apiKey
|
|
519
|
+
apiKey?: string;
|
|
508
520
|
/**
|
|
509
521
|
* Base URL for the Drip API. Defaults to production API.
|
|
510
|
-
*
|
|
511
|
-
* @default "https://
|
|
522
|
+
* Falls back to `DRIP_BASE_URL` environment variable if not provided.
|
|
523
|
+
* @default "https://drip-app-hlunj.ondigitalocean.app/v1"
|
|
512
524
|
*/
|
|
513
525
|
baseUrl?: string;
|
|
514
526
|
/**
|
|
@@ -645,8 +657,8 @@ interface ChargeParams {
|
|
|
645
657
|
*/
|
|
646
658
|
quantity: number;
|
|
647
659
|
/**
|
|
648
|
-
* Unique key to prevent duplicate charges.
|
|
649
|
-
*
|
|
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.
|
|
650
662
|
* @example "req_abc123"
|
|
651
663
|
*/
|
|
652
664
|
idempotencyKey?: string;
|
|
@@ -944,7 +956,7 @@ interface CreateWorkflowParams {
|
|
|
944
956
|
/** URL-safe identifier (lowercase alphanumeric with underscores/hyphens) */
|
|
945
957
|
slug: string;
|
|
946
958
|
/** Type of workflow */
|
|
947
|
-
productSurface?: 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';
|
|
959
|
+
productSurface?: 'API' | 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';
|
|
948
960
|
/** Optional description */
|
|
949
961
|
description?: string;
|
|
950
962
|
/** Additional metadata */
|
|
@@ -1231,47 +1243,88 @@ interface RecordRunResult {
|
|
|
1231
1243
|
summary: string;
|
|
1232
1244
|
}
|
|
1233
1245
|
/**
|
|
1234
|
-
* Full run timeline response.
|
|
1246
|
+
* Full run timeline response from GET /runs/:id/timeline.
|
|
1235
1247
|
*/
|
|
1236
1248
|
interface RunTimeline {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
endedAt: string | null;
|
|
1246
|
-
durationMs: number | null;
|
|
1247
|
-
errorMessage: string | null;
|
|
1248
|
-
errorCode: string | null;
|
|
1249
|
-
correlationId: string | null;
|
|
1250
|
-
metadata: Record<string, unknown> | null;
|
|
1251
|
-
};
|
|
1252
|
-
timeline: Array<{
|
|
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<{
|
|
1253
1257
|
id: string;
|
|
1254
1258
|
eventType: string;
|
|
1255
|
-
|
|
1256
|
-
|
|
1259
|
+
actionName: string | null;
|
|
1260
|
+
outcome: 'SUCCESS' | 'FAILED' | 'PENDING' | 'TIMEOUT' | 'RETRYING';
|
|
1261
|
+
explanation: string | null;
|
|
1257
1262
|
description: string | null;
|
|
1258
|
-
costUnits: number | null;
|
|
1259
1263
|
timestamp: string;
|
|
1260
|
-
|
|
1264
|
+
durationMs: number | null;
|
|
1261
1265
|
parentEventId: string | null;
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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[];
|
|
1275
|
+
} | null;
|
|
1276
|
+
metadata: {
|
|
1277
|
+
usageType: string;
|
|
1278
|
+
quantity: number;
|
|
1279
|
+
units: string | null;
|
|
1266
1280
|
} | null;
|
|
1267
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';
|
|
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;
|
|
1268
1320
|
totals: {
|
|
1269
1321
|
eventCount: number;
|
|
1270
1322
|
totalQuantity: string;
|
|
1271
1323
|
totalCostUnits: string;
|
|
1272
|
-
totalChargedUsdc: string;
|
|
1273
1324
|
};
|
|
1274
|
-
|
|
1325
|
+
_links: {
|
|
1326
|
+
timeline: string;
|
|
1327
|
+
};
|
|
1275
1328
|
}
|
|
1276
1329
|
/**
|
|
1277
1330
|
* Parameters for wrapping an external API call with usage tracking.
|
|
@@ -1376,6 +1429,14 @@ declare class Drip {
|
|
|
1376
1429
|
private readonly baseUrl;
|
|
1377
1430
|
private readonly timeout;
|
|
1378
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';
|
|
1379
1440
|
/**
|
|
1380
1441
|
* Creates a new Drip SDK client.
|
|
1381
1442
|
*
|
|
@@ -1386,23 +1447,29 @@ declare class Drip {
|
|
|
1386
1447
|
* ```typescript
|
|
1387
1448
|
* // Basic usage
|
|
1388
1449
|
* const drip = new Drip({
|
|
1389
|
-
* apiKey: '
|
|
1450
|
+
* apiKey: 'sk_live_...',
|
|
1390
1451
|
* });
|
|
1391
1452
|
*
|
|
1392
1453
|
* // With production resilience (recommended)
|
|
1393
1454
|
* const drip = new Drip({
|
|
1394
|
-
* apiKey: '
|
|
1455
|
+
* apiKey: 'sk_live_...',
|
|
1395
1456
|
* resilience: true,
|
|
1396
1457
|
* });
|
|
1397
1458
|
*
|
|
1398
1459
|
* // High throughput mode
|
|
1399
1460
|
* const drip = new Drip({
|
|
1400
|
-
* apiKey: '
|
|
1461
|
+
* apiKey: 'sk_live_...',
|
|
1401
1462
|
* resilience: 'high-throughput',
|
|
1402
1463
|
* });
|
|
1403
1464
|
* ```
|
|
1404
1465
|
*/
|
|
1405
|
-
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;
|
|
1406
1473
|
/**
|
|
1407
1474
|
* Makes an authenticated request to the Drip API.
|
|
1408
1475
|
* @internal
|
|
@@ -1874,8 +1941,8 @@ declare class Drip {
|
|
|
1874
1941
|
* @example
|
|
1875
1942
|
* ```typescript
|
|
1876
1943
|
* const workflow = await drip.createWorkflow({
|
|
1877
|
-
* name: '
|
|
1878
|
-
* slug: '
|
|
1944
|
+
* name: 'Document Processing',
|
|
1945
|
+
* slug: 'doc_processing',
|
|
1879
1946
|
* productSurface: 'AGENT',
|
|
1880
1947
|
* });
|
|
1881
1948
|
* ```
|
|
@@ -1940,33 +2007,53 @@ declare class Drip {
|
|
|
1940
2007
|
totalCostUnits: string | null;
|
|
1941
2008
|
}>;
|
|
1942
2009
|
/**
|
|
1943
|
-
* 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.
|
|
1944
2026
|
*
|
|
1945
2027
|
* This is the key endpoint for debugging "what happened" in an execution.
|
|
1946
2028
|
*
|
|
1947
2029
|
* @param runId - The run ID
|
|
1948
|
-
* @
|
|
2030
|
+
* @param options - Pagination and filtering options
|
|
2031
|
+
* @returns Full timeline with events, anomalies, and summary
|
|
1949
2032
|
*
|
|
1950
2033
|
* @example
|
|
1951
2034
|
* ```typescript
|
|
1952
|
-
* const
|
|
2035
|
+
* const timeline = await drip.getRunTimeline('run_abc123');
|
|
1953
2036
|
*
|
|
1954
|
-
* console.log(`Status: ${
|
|
1955
|
-
* console.log(`
|
|
1956
|
-
* console.log(`Total cost: ${totals.totalCostUnits}`);
|
|
2037
|
+
* console.log(`Status: ${timeline.status}`);
|
|
2038
|
+
* console.log(`Events: ${timeline.summary.totalEvents}`);
|
|
1957
2039
|
*
|
|
1958
|
-
* for (const event of timeline) {
|
|
1959
|
-
* console.log(`${event.eventType}: ${event.
|
|
2040
|
+
* for (const event of timeline.events) {
|
|
2041
|
+
* console.log(`${event.eventType}: ${event.outcome}`);
|
|
1960
2042
|
* }
|
|
1961
2043
|
* ```
|
|
1962
2044
|
*/
|
|
1963
|
-
getRunTimeline(runId: string
|
|
2045
|
+
getRunTimeline(runId: string, options?: {
|
|
2046
|
+
limit?: number;
|
|
2047
|
+
cursor?: string;
|
|
2048
|
+
includeAnomalies?: boolean;
|
|
2049
|
+
collapseRetries?: boolean;
|
|
2050
|
+
}): Promise<RunTimeline>;
|
|
1964
2051
|
/**
|
|
1965
2052
|
* Emits an event to a run.
|
|
1966
2053
|
*
|
|
1967
|
-
*
|
|
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.
|
|
1968
2056
|
* Use `Drip.generateIdempotencyKey()` for deterministic key generation.
|
|
1969
|
-
* If `idempotencyKey` is omitted, repeated calls may create duplicate events.
|
|
1970
2057
|
*
|
|
1971
2058
|
* @param params - Event parameters
|
|
1972
2059
|
* @returns The created event
|
|
@@ -2007,10 +2094,13 @@ declare class Drip {
|
|
|
2007
2094
|
success: boolean;
|
|
2008
2095
|
created: number;
|
|
2009
2096
|
duplicates: number;
|
|
2097
|
+
skipped: number;
|
|
2010
2098
|
events: Array<{
|
|
2011
2099
|
id: string;
|
|
2012
2100
|
eventType: string;
|
|
2013
2101
|
isDuplicate: boolean;
|
|
2102
|
+
skipped?: boolean;
|
|
2103
|
+
reason?: string;
|
|
2014
2104
|
}>;
|
|
2015
2105
|
}>;
|
|
2016
2106
|
/**
|
|
@@ -2135,7 +2225,7 @@ declare class Drip {
|
|
|
2135
2225
|
* // Record a complete agent run in one call
|
|
2136
2226
|
* const result = await drip.recordRun({
|
|
2137
2227
|
* customerId: 'cust_123',
|
|
2138
|
-
* workflow: '
|
|
2228
|
+
* workflow: 'doc_processing', // Auto-creates if doesn't exist
|
|
2139
2229
|
* events: [
|
|
2140
2230
|
* { eventType: 'agent.start', description: 'Started processing' },
|
|
2141
2231
|
* { eventType: 'tool.ocr', quantity: 3, units: 'pages', costUnits: 0.15 },
|
|
@@ -2154,7 +2244,7 @@ declare class Drip {
|
|
|
2154
2244
|
* // Record a failed run with error details
|
|
2155
2245
|
* const result = await drip.recordRun({
|
|
2156
2246
|
* customerId: 'cust_123',
|
|
2157
|
-
* workflow: '
|
|
2247
|
+
* workflow: 'doc_processing',
|
|
2158
2248
|
* events: [
|
|
2159
2249
|
* { eventType: 'agent.start', description: 'Started processing' },
|
|
2160
2250
|
* { eventType: 'tool.ocr', quantity: 1, units: 'pages' },
|
|
@@ -2326,4 +2416,31 @@ declare class Drip {
|
|
|
2326
2416
|
createStreamMeter(options: StreamMeterOptions): StreamMeter;
|
|
2327
2417
|
}
|
|
2328
2418
|
|
|
2329
|
-
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var M=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+a+'" is not supported')});var w=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,t){this._chargeFn=e,this._options=t;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isReplay:false};let t=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:void 0,r=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:t,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let n={success:r.success,quantity:e,charge:r.charge,isReplay:r.isReplay};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var A={requestsPerSecond:100,burstSize:200,enabled:true},k=class{config;tokens;lastRefill;constructor(e){this.config={...A,...e},this.tokens=this.config.burstSize,this.lastRefill=Date.now();}refill(){let e=Date.now(),t=(e-this.lastRefill)/1e3;this.tokens=Math.min(this.config.burstSize,this.tokens+t*this.config.requestsPerSecond),this.lastRefill=e;}async acquire(e){if(!this.config.enabled)return true;let t=e!==void 0?Date.now()+e:void 0;for(;;){if(this.refill(),this.tokens>=1)return this.tokens-=1,true;let r=(1-this.tokens)/this.config.requestsPerSecond*1e3;if(t!==void 0){let n=t-Date.now();if(n<=0)return false;await this.sleep(Math.min(r,n));}else await this.sleep(r);}}tryAcquire(){return this.config.enabled?(this.refill(),this.tokens>=1?(this.tokens-=1,true):false):true}get availableTokens(){return this.refill(),this.tokens}sleep(e){return new Promise(t=>setTimeout(t,e))}},W={maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},S=class a extends Error{attempts;lastError;constructor(e,t){super(`Retry exhausted after ${e} attempts: ${t.message}`),this.name="RetryExhaustedError",this.attempts=e,this.lastError=t,Object.setPrototypeOf(this,a.prototype);}};function O(a,e){let t=e.baseDelayMs*Math.pow(e.exponentialBase,a);if(t=Math.min(t,e.maxDelayMs),e.jitter>0){let r=t*e.jitter;t+=Math.random()*2*r-r;}return Math.max(0,t)}function _(a,e){if(a instanceof Error){if(a.message.includes("fetch")||a.message.includes("network")||a.message.includes("ECONNREFUSED")||a.message.includes("ETIMEDOUT"))return true;let t=a.statusCode;if(t!==void 0)return e.retryableStatusCodes.includes(t)}return false}var B={failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},x=class a extends Error{circuitName;timeUntilRetryMs;constructor(e,t){super(`Circuit '${e}' is open. Retry in ${Math.round(t)}ms`),this.name="CircuitBreakerOpenError",this.circuitName=e,this.timeUntilRetryMs=t,Object.setPrototypeOf(this,a.prototype);}},T=class{name;config;state="closed";failureCount=0;successCount=0;lastFailureTime=null;constructor(e,t){this.name=e,this.config={...B,...t};}getState(){return this.checkStateTransition(),this.state}checkStateTransition(){this.state==="open"&&this.lastFailureTime!==null&&Date.now()-this.lastFailureTime>=this.config.timeoutMs&&(this.state="half_open",this.successCount=0);}recordSuccess(){this.config.enabled&&(this.state==="half_open"?(this.successCount+=1,this.successCount>=this.config.successThreshold&&(this.state="closed",this.failureCount=0)):this.state==="closed"&&(this.failureCount=0));}recordFailure(){this.config.enabled&&(this.failureCount+=1,this.lastFailureTime=Date.now(),this.state==="half_open"?this.state="open":this.state==="closed"&&this.failureCount>=this.config.failureThreshold&&(this.state="open"));}allowRequest(){return !this.config.enabled||(this.checkStateTransition(),this.state==="closed")?true:this.state==="half_open"}getTimeUntilRetry(){if(this.state!=="open"||this.lastFailureTime===null)return 0;let e=Date.now()-this.lastFailureTime;return Math.max(0,this.config.timeoutMs-e)}reset(){this.state="closed",this.failureCount=0,this.successCount=0,this.lastFailureTime=null;}},P=class{windowSize;metrics=[];totalRequests=0;totalSuccesses=0;totalFailures=0;constructor(e=1e3){this.windowSize=e;}record(e){for(this.metrics.push(e),this.totalRequests+=1,e.success?this.totalSuccesses+=1:this.totalFailures+=1;this.metrics.length>this.windowSize;)this.metrics.shift();}getSummary(){if(this.metrics.length===0)return {windowSize:0,totalRequests:0,totalSuccesses:0,totalFailures:0,successRate:0,avgLatencyMs:0,minLatencyMs:0,maxLatencyMs:0,p50LatencyMs:0,p95LatencyMs:0,p99LatencyMs:0,requestsByEndpoint:{},errorsByType:{}};let e=this.metrics.map(i=>i.durationMs).sort((i,o)=>i-o),t=this.metrics.filter(i=>i.success).length,r={};for(let i of this.metrics)r[i.endpoint]=(r[i.endpoint]??0)+1;let n={};for(let i of this.metrics)i.error&&(n[i.error]=(n[i.error]??0)+1);let s=e.reduce((i,o)=>i+o,0);return {windowSize:this.metrics.length,totalRequests:this.totalRequests,totalSuccesses:this.totalSuccesses,totalFailures:this.totalFailures,successRate:t/this.metrics.length*100,avgLatencyMs:s/e.length,minLatencyMs:e[0],maxLatencyMs:e[e.length-1],p50LatencyMs:e[Math.floor(e.length*.5)],p95LatencyMs:e[Math.floor(e.length*.95)],p99LatencyMs:e[Math.floor(e.length*.99)],requestsByEndpoint:r,errorsByType:n}}reset(){this.metrics.length=0,this.totalRequests=0,this.totalSuccesses=0,this.totalFailures=0;}};function b(){return {rateLimiter:{requestsPerSecond:100,burstSize:200,enabled:true},retry:{maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},collectMetrics:true}}function F(){return {rateLimiter:{...A,enabled:false},retry:{...W,enabled:false},circuitBreaker:{...B,enabled:false},collectMetrics:false}}function E(){return {rateLimiter:{requestsPerSecond:1e3,burstSize:2e3,enabled:true},retry:{maxRetries:2,baseDelayMs:50,maxDelayMs:5e3,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:10,successThreshold:3,timeoutMs:15e3,enabled:true},collectMetrics:true}}var R=class{config;rateLimiter;circuitBreaker;metrics;constructor(e){this.config={...b(),...e,rateLimiter:{...b().rateLimiter,...e?.rateLimiter},retry:{...b().retry,...e?.retry},circuitBreaker:{...b().circuitBreaker,...e?.circuitBreaker}},this.rateLimiter=new k(this.config.rateLimiter),this.circuitBreaker=new T("drip_api",this.config.circuitBreaker),this.metrics=this.config.collectMetrics?new P:null;}async execute(e,t="UNKNOWN",r="unknown"){let n=performance.now(),s=0,i=null;if(!await this.rateLimiter.acquire(3e4))throw new Error("Rate limiter timeout");if(!this.circuitBreaker.allowRequest())throw new x(this.circuitBreaker.name,this.circuitBreaker.getTimeUntilRetry());for(let u=0;u<=this.config.retry.maxRetries;u++)try{let c=await e();if(this.circuitBreaker.recordSuccess(),this.metrics){let d=performance.now()-n;this.metrics.record({method:t,endpoint:r,statusCode:200,durationMs:d,success:!0,timestamp:Date.now(),retryCount:s});}return c}catch(c){if(i=c instanceof Error?c:new Error(String(c)),this.config.retry.enabled&&_(c,this.config.retry)&&u<this.config.retry.maxRetries){s+=1;let h=O(u,this.config.retry);await this.sleep(h);continue}if(this.circuitBreaker.recordFailure(),this.metrics){let h=performance.now()-n,y=c.statusCode??null;this.metrics.record({method:t,endpoint:r,statusCode:y,durationMs:h,success:false,timestamp:Date.now(),error:i.name,retryCount:s});}throw c}throw i?new S(this.config.retry.maxRetries+1,i):new Error("Unexpected execution path")}getMetrics(){return this.metrics?.getSummary()??null}getHealth(){return {circuitBreaker:{state:this.circuitBreaker.getState(),timeUntilRetryMs:this.circuitBreaker.getTimeUntilRetry()},rateLimiter:{availableTokens:this.rateLimiter.availableTokens,requestsPerSecond:this.config.rateLimiter.requestsPerSecond},metrics:this.getMetrics()}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var I={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function $(a){return a instanceof Error&&(a.message.includes("fetch")||a.message.includes("network"))?true:a instanceof p?a.statusCode>=500||a.statusCode===408||a.statusCode===429:false}async function K(a,e={}){let t=e.maxAttempts??I.maxAttempts,r=e.baseDelayMs??I.baseDelayMs,n=e.maxDelayMs??I.maxDelayMs,s=e.isRetryable??$,i;for(let o=1;o<=t;o++)try{return await a()}catch(u){if(i=u,o===t||!s(u))throw u;let c=Math.min(r*Math.pow(2,o-1)+Math.random()*100,n);await new Promise(d=>setTimeout(d,c));}throw i}var p=class a extends Error{constructor(t,r,n){super(t);this.statusCode=r;this.code=n;this.name="DripError",Object.setPrototypeOf(this,a.prototype);}},v=class{apiKey;baseUrl;timeout;resilience;constructor(e){if(!e.apiKey)throw new Error("Drip API key is required");this.apiKey=e.apiKey,this.baseUrl=e.baseUrl||"https://api.drip.dev/v1",this.timeout=e.timeout||3e4,e.resilience===true?this.resilience=new R(b()):e.resilience==="high-throughput"?this.resilience=new R(E()):e.resilience&&typeof e.resilience=="object"?this.resilience=new R(e.resilience):this.resilience=null;}async request(e,t={}){let r=(t.method??"GET").toUpperCase();return this.resilience?this.resilience.execute(()=>this.rawRequest(e,t),r,e):this.rawRequest(e,t)}async rawRequest(e,t={}){let r=new AbortController,n=setTimeout(()=>r.abort(),this.timeout);try{let s=await fetch(`${this.baseUrl}${e}`,{...t,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...t.headers}});if(s.status===204)return {success:!0};let i=await s.json();if(!s.ok)throw new p(i.message||i.error||"Request failed",s.status,i.code);return i}catch(s){throw s instanceof p?s:s instanceof Error&&s.name==="AbortError"?new p("Request timed out",408,"TIMEOUT"):new p(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let e=new AbortController,t=setTimeout(()=>e.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:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),i=Date.now()-n,o="unknown",u=Date.now();try{let c=await s.json();typeof c.status=="string"&&(o=c.status),typeof c.timestamp=="number"&&(u=c.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:u}}catch(s){throw s instanceof Error&&s.name==="AbortError"?new p("Request timed out",408,"TIMEOUT"):new p(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(t);}}getMetrics(){return this.resilience?.getMetrics()??null}getHealth(){return this.resilience?.getHealth()??null}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let t=new URLSearchParams;e?.limit&&t.set("limit",e.limit.toString()),e?.status&&t.set("status",e.status);let r=t.toString(),n=r?`/customers?${r}`:"/customers";return this.request(n)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:e.idempotencyKey,metadata:e.metadata})})}async wrapApiCall(e){let t=e.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,r=await e.call(),n=e.extractUsage(r),s=await K(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:n,idempotencyKey:t,metadata:e.metadata}),e.retryOptions);return {result:r,charge:s,idempotencyKey:t}}async trackUsage(e){return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:e.idempotencyKey,units:e.units,description:e.description,metadata:e.metadata})})}async getCharge(e){return this.request(`/charges/${e}`)}async listCharges(e){let t=new URLSearchParams;e?.customerId&&t.set("customerId",e.customerId),e?.status&&t.set("status",e.status),e?.limit&&t.set("limit",e.limit.toString()),e?.offset&&t.set("offset",e.offset.toString());let r=t.toString(),n=r?`/charges?${r}`:"/charges";return this.request(n)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let t=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:t.id,url:t.url,expiresAt:t.expires_at,amountUsd:t.amount_usd}}async createWebhook(e){return this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.request("/webhooks")}async getWebhook(e){return this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,t){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(t)})}async getRunTimeline(e){return this.request(`/runs/${e}`)}async emitEvent(e){return this.request("/events",{method:"POST",body:JSON.stringify(e)})}async emitEventsBatch(e){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:e})})}async listMeters(){let e=await this.request("/pricing-plans");return {data:e.data.map(t=>({id:t.id,name:t.name,meter:t.unitType,unitPriceUsd:t.unitPriceUsd,isActive:t.isActive})),count:e.count}}async estimateFromUsage(e){let t=e.periodStart instanceof Date?e.periodStart.toISOString():e.periodStart,r=e.periodEnd instanceof Date?e.periodEnd.toISOString():e.periodEnd;return this.request("/dashboard/cost-estimate/from-usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,periodStart:t,periodEnd:r,defaultUnitPrice:e.defaultUnitPrice,includeChargedEvents:e.includeChargedEvents,usageTypes:e.usageTypes,customPricing:e.customPricing})})}async estimateFromHypothetical(e){return this.request("/dashboard/cost-estimate/hypothetical",{method:"POST",body:JSON.stringify({items:e.items,defaultUnitPrice:e.defaultUnitPrice,customPricing:e.customPricing})})}async recordRun(e){let t=Date.now(),r=e.workflow,n=e.workflow;if(!e.workflow.startsWith("wf_"))try{let m=(await this.listWorkflows()).data.find(l=>l.slug===e.workflow||l.id===e.workflow);if(m)r=m.id,n=m.name;else {let l=await this.createWorkflow({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,f=>f.toUpperCase()),slug:e.workflow,productSurface:"AGENT"});r=l.id,n=l.name;}}catch{r=e.workflow;}let s=await this.startRun({customerId:e.customerId,workflowId:r,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata}),i=0,o=0;if(e.events.length>0){let C=e.events.map((l,f)=>({runId:s.id,eventType:l.eventType,quantity:l.quantity,units:l.units,description:l.description,costUnits:l.costUnits,metadata:l.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${l.eventType}:${f}`:void 0})),m=await this.emitEventsBatch(C);i=m.created,o=m.duplicates;}let u=await this.endRun(s.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),c=Date.now()-t,d=e.events.length>0?`${i} events recorded`:"no events",y=`${e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB"} ${n}: ${d} (${u.durationMs??c}ms)`;return {run:{id:s.id,workflowId:r,workflowName:n,status:e.status,durationMs:u.durationMs},events:{created:i,duplicates:o},totalCostUnits:u.totalCostUnits,summary:y}}static generateIdempotencyKey(e){let t=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)],r=0,n=t.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)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(g=>g.startsWith("t=")),o=s.find(g=>g.startsWith("v1="));if(!i||!o)return !1;let u=parseInt(i.slice(2),10),c=o.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let h=`${u}.${e}`,y=new TextEncoder,C=y.encode(r),m=y.encode(h),l=globalThis.crypto?.subtle??M("crypto").webcrypto.subtle,f=await l.importKey("raw",C,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),N=await l.sign("HMAC",f,m),U=Array.from(new Uint8Array(N)).map(g=>g.toString(16).padStart(2,"0")).join("");if(c.length!==U.length)return !1;let q=0;for(let g=0;g<c.length;g++)q|=c.charCodeAt(g)^U.charCodeAt(g);return q===0}catch{return false}}static verifyWebhookSignatureSync(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(f=>f.startsWith("t=")),o=s.find(f=>f.startsWith("v1="));if(!i||!o)return !1;let u=parseInt(i.slice(2),10),c=o.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let h=M("crypto"),y=`${u}.${e}`,C=h.createHmac("sha256",r).update(y).digest("hex"),m=Buffer.from(c),l=Buffer.from(C);return m.length!==l.length?!1:h.timingSafeEqual(m,l)}catch{return false}}static generateWebhookSignature(e,t,r){let n=M("crypto"),s=r??Math.floor(Date.now()/1e3),i=`${s}.${e}`,o=n.createHmac("sha256",t).update(i).digest("hex");return `t=${s},v1=${o}`}createStreamMeter(e){return new w(this.charge.bind(this),e)}},X=v;
|
|
2
|
-
export{
|
|
1
|
+
import {createHash}from'crypto';var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var K=0;function b(o,...e){let t=++K,r=e.filter(s=>s!==void 0).map(String);r.push(String(t));let n=createHash("sha256").update(r.join("|")).digest("hex").slice(0,24);return `${o}_${n}`}var k=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,t){this._chargeFn=e,this._options=t;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isReplay:false};let t=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:b("stream",this._options.customerId,this._options.meter,e,this._flushCount),r=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:t,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let n={success:r.success,quantity:e,charge:r.charge,isReplay:r.isReplay};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var O={requestsPerSecond:100,burstSize:200,enabled:true},T=class{config;tokens;lastRefill;constructor(e){this.config={...O,...e},this.tokens=this.config.burstSize,this.lastRefill=Date.now();}refill(){let e=Date.now(),t=(e-this.lastRefill)/1e3;this.tokens=Math.min(this.config.burstSize,this.tokens+t*this.config.requestsPerSecond),this.lastRefill=e;}async acquire(e){if(!this.config.enabled)return true;let t=e!==void 0?Date.now()+e:void 0;for(;;){if(this.refill(),this.tokens>=1)return this.tokens-=1,true;let r=(1-this.tokens)/this.config.requestsPerSecond*1e3;if(t!==void 0){let n=t-Date.now();if(n<=0)return false;await this.sleep(Math.min(r,n));}else await this.sleep(r);}}tryAcquire(){return this.config.enabled?(this.refill(),this.tokens>=1?(this.tokens-=1,true):false):true}get availableTokens(){return this.refill(),this.tokens}sleep(e){return new Promise(t=>setTimeout(t,e))}},H={maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},E=class o extends Error{attempts;lastError;constructor(e,t){super(`Retry exhausted after ${e} attempts: ${t.message}`),this.name="RetryExhaustedError",this.attempts=e,this.lastError=t,Object.setPrototypeOf(this,o.prototype);}};function N(o,e){let t=e.baseDelayMs*Math.pow(e.exponentialBase,o);if(t=Math.min(t,e.maxDelayMs),e.jitter>0){let r=t*e.jitter;t+=Math.random()*2*r-r;}return Math.max(0,t)}function W(o,e){if(o instanceof Error){if(o.message.includes("fetch")||o.message.includes("network")||o.message.includes("ECONNREFUSED")||o.message.includes("ETIMEDOUT"))return true;let t=o.statusCode;if(t!==void 0)return e.retryableStatusCodes.includes(t)}return false}var B={failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},P=class o extends Error{circuitName;timeUntilRetryMs;constructor(e,t){super(`Circuit '${e}' is open. Retry in ${Math.round(t)}ms`),this.name="CircuitBreakerOpenError",this.circuitName=e,this.timeUntilRetryMs=t,Object.setPrototypeOf(this,o.prototype);}},x=class{name;config;state="closed";failureCount=0;successCount=0;lastFailureTime=null;constructor(e,t){this.name=e,this.config={...B,...t};}getState(){return this.checkStateTransition(),this.state}checkStateTransition(){this.state==="open"&&this.lastFailureTime!==null&&Date.now()-this.lastFailureTime>=this.config.timeoutMs&&(this.state="half_open",this.successCount=0);}recordSuccess(){this.config.enabled&&(this.state==="half_open"?(this.successCount+=1,this.successCount>=this.config.successThreshold&&(this.state="closed",this.failureCount=0)):this.state==="closed"&&(this.failureCount=0));}recordFailure(){this.config.enabled&&(this.failureCount+=1,this.lastFailureTime=Date.now(),this.state==="half_open"?this.state="open":this.state==="closed"&&this.failureCount>=this.config.failureThreshold&&(this.state="open"));}allowRequest(){return !this.config.enabled||(this.checkStateTransition(),this.state==="closed")?true:this.state==="half_open"}getTimeUntilRetry(){if(this.state!=="open"||this.lastFailureTime===null)return 0;let e=Date.now()-this.lastFailureTime;return Math.max(0,this.config.timeoutMs-e)}reset(){this.state="closed",this.failureCount=0,this.successCount=0,this.lastFailureTime=null;}},I=class{windowSize;metrics=[];totalRequests=0;totalSuccesses=0;totalFailures=0;constructor(e=1e3){this.windowSize=e;}record(e){for(this.metrics.push(e),this.totalRequests+=1,e.success?this.totalSuccesses+=1:this.totalFailures+=1;this.metrics.length>this.windowSize;)this.metrics.shift();}getSummary(){if(this.metrics.length===0)return {windowSize:0,totalRequests:0,totalSuccesses:0,totalFailures:0,successRate:0,avgLatencyMs:0,minLatencyMs:0,maxLatencyMs:0,p50LatencyMs:0,p95LatencyMs:0,p99LatencyMs:0,requestsByEndpoint:{},errorsByType:{}};let e=this.metrics.map(i=>i.durationMs).sort((i,a)=>i-a),t=this.metrics.filter(i=>i.success).length,r={};for(let i of this.metrics)r[i.endpoint]=(r[i.endpoint]??0)+1;let n={};for(let i of this.metrics)i.error&&(n[i.error]=(n[i.error]??0)+1);let s=e.reduce((i,a)=>i+a,0);return {windowSize:this.metrics.length,totalRequests:this.totalRequests,totalSuccesses:this.totalSuccesses,totalFailures:this.totalFailures,successRate:t/this.metrics.length*100,avgLatencyMs:s/e.length,minLatencyMs:e[0],maxLatencyMs:e[e.length-1],p50LatencyMs:e[Math.floor(e.length*.5)],p95LatencyMs:e[Math.floor(e.length*.95)],p99LatencyMs:e[Math.floor(e.length*.99)],requestsByEndpoint:r,errorsByType:n}}reset(){this.metrics.length=0,this.totalRequests=0,this.totalSuccesses=0,this.totalFailures=0;}};function R(){return {rateLimiter:{requestsPerSecond:100,burstSize:200,enabled:true},retry:{maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},collectMetrics:true}}function j(){return {rateLimiter:{...O,enabled:false},retry:{...H,enabled:false},circuitBreaker:{...B,enabled:false},collectMetrics:false}}function q(){return {rateLimiter:{requestsPerSecond:1e3,burstSize:2e3,enabled:true},retry:{maxRetries:2,baseDelayMs:50,maxDelayMs:5e3,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:10,successThreshold:3,timeoutMs:15e3,enabled:true},collectMetrics:true}}var C=class{config;rateLimiter;circuitBreaker;metrics;constructor(e){this.config={...R(),...e,rateLimiter:{...R().rateLimiter,...e?.rateLimiter},retry:{...R().retry,...e?.retry},circuitBreaker:{...R().circuitBreaker,...e?.circuitBreaker}},this.rateLimiter=new T(this.config.rateLimiter),this.circuitBreaker=new x("drip_api",this.config.circuitBreaker),this.metrics=this.config.collectMetrics?new I:null;}async execute(e,t="UNKNOWN",r="unknown"){let n=performance.now(),s=0,i=null;if(!await this.rateLimiter.acquire(3e4))throw new Error("Rate limiter timeout");if(!this.circuitBreaker.allowRequest())throw new P(this.circuitBreaker.name,this.circuitBreaker.getTimeUntilRetry());for(let u=0;u<=this.config.retry.maxRetries;u++)try{let c=await e();if(this.circuitBreaker.recordSuccess(),this.metrics){let d=performance.now()-n;this.metrics.record({method:t,endpoint:r,statusCode:200,durationMs:d,success:!0,timestamp:Date.now(),retryCount:s});}return c}catch(c){if(i=c instanceof Error?c:new Error(String(c)),this.config.retry.enabled&&W(c,this.config.retry)&&u<this.config.retry.maxRetries){s+=1;let p=N(u,this.config.retry);await this.sleep(p);continue}if(this.circuitBreaker.recordFailure(),this.metrics){let p=performance.now()-n,y=c.statusCode??null;this.metrics.record({method:t,endpoint:r,statusCode:y,durationMs:p,success:false,timestamp:Date.now(),error:i.name,retryCount:s});}throw c}throw i?new E(this.config.retry.maxRetries+1,i):new Error("Unexpected execution path")}getMetrics(){return this.metrics?.getSummary()??null}getHealth(){return {circuitBreaker:{state:this.circuitBreaker.getState(),timeUntilRetryMs:this.circuitBreaker.getTimeUntilRetry()},rateLimiter:{availableTokens:this.rateLimiter.availableTokens,requestsPerSecond:this.config.rateLimiter.requestsPerSecond},metrics:this.getMetrics()}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var A={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function G(o){return o instanceof Error&&(o.message.includes("fetch")||o.message.includes("network"))?true:o instanceof f?o.statusCode>=500||o.statusCode===408||o.statusCode===429:false}async function z(o,e={}){let t=e.maxAttempts??A.maxAttempts,r=e.baseDelayMs??A.baseDelayMs,n=e.maxDelayMs??A.maxDelayMs,s=e.isRetryable??G,i;for(let a=1;a<=t;a++)try{return await o()}catch(u){if(i=u,a===t||!s(u))throw u;let c=Math.min(r*Math.pow(2,a-1)+Math.random()*100,n);await new Promise(d=>setTimeout(d,c));}throw i}var f=class o extends Error{constructor(t,r,n){super(t);this.statusCode=r;this.code=n;this.name="DripError",Object.setPrototypeOf(this,o.prototype);}},M=class{apiKey;baseUrl;timeout;resilience;keyType;constructor(e={}){let t=e.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),r=e.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!t)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=t,this.baseUrl=r||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=e.timeout||3e4,t.startsWith("sk_")?this.keyType="secret":t.startsWith("pk_")?this.keyType="public":this.keyType="unknown",e.resilience===true?this.resilience=new C(R()):e.resilience==="high-throughput"?this.resilience=new C(q()):e.resilience&&typeof e.resilience=="object"?this.resilience=new C(e.resilience):this.resilience=null;}assertSecretKey(e){if(this.keyType==="public")throw new f(`${e} requires a secret key (sk_). You are using a public key (pk_), which cannot access this endpoint. Use a secret key for webhook, API key, and feature flag management.`,403,"PUBLIC_KEY_NOT_ALLOWED")}async request(e,t={}){let r=(t.method??"GET").toUpperCase();return this.resilience?this.resilience.execute(()=>this.rawRequest(e,t),r,e):this.rawRequest(e,t)}async rawRequest(e,t={}){let r=new AbortController,n=setTimeout(()=>r.abort(),this.timeout);try{let s=await fetch(`${this.baseUrl}${e}`,{...t,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...t.headers}});if(s.status===204)return {success:!0};let i=await s.json();if(!s.ok)throw new f(i.message||i.error||"Request failed",s.status,i.code);return i}catch(s){throw s instanceof f?s:s instanceof Error&&s.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let e=new AbortController,t=setTimeout(()=>e.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:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),i=Date.now()-n,a="unknown",u=Date.now();try{let c=await s.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(u=c.timestamp);}catch{a=s.ok?"healthy":`error:${s.status}`;}return !s.ok&&a==="unknown"&&(a=`error:${s.status}`),{ok:s.ok&&a==="healthy",status:a,latencyMs:i,timestamp:u}}catch(s){throw s instanceof Error&&s.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(t);}}getMetrics(){return this.resilience?.getMetrics()??null}getHealth(){return this.resilience?.getHealth()??null}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let t=new URLSearchParams;e?.limit&&t.set("limit",e.limit.toString()),e?.status&&t.set("status",e.status);let r=t.toString(),n=r?`/customers?${r}`:"/customers";return this.request(n)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){let t=e.idempotencyKey??b("chg",e.customerId,e.meter,e.quantity);return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,metadata:e.metadata})})}async wrapApiCall(e){let t=e.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,r=await e.call(),n=e.extractUsage(r),s=await z(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:n,idempotencyKey:t,metadata:e.metadata}),e.retryOptions);return {result:r,charge:s,idempotencyKey:t}}async trackUsage(e){let t=e.idempotencyKey??b("track",e.customerId,e.meter,e.quantity);return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,units:e.units,description:e.description,metadata:e.metadata})})}async getCharge(e){return this.request(`/charges/${e}`)}async listCharges(e){let t=new URLSearchParams;e?.customerId&&t.set("customerId",e.customerId),e?.status&&t.set("status",e.status),e?.limit&&t.set("limit",e.limit.toString()),e?.offset&&t.set("offset",e.offset.toString());let r=t.toString(),n=r?`/charges?${r}`:"/charges";return this.request(n)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let t=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:t.id,url:t.url,expiresAt:t.expires_at,amountUsd:t.amount_usd}}async createWebhook(e){return this.assertSecretKey("createWebhook()"),this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.assertSecretKey("listWebhooks()"),this.request("/webhooks")}async getWebhook(e){return this.assertSecretKey("getWebhook()"),this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.assertSecretKey("deleteWebhook()"),this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.assertSecretKey("testWebhook()"),this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.assertSecretKey("rotateWebhookSecret()"),this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,t){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(t)})}async getRun(e){return this.request(`/runs/${e}`)}async getRunTimeline(e,t){let r=new URLSearchParams;t?.limit&&r.set("limit",t.limit.toString()),t?.cursor&&r.set("cursor",t.cursor),t?.includeAnomalies!==void 0&&r.set("includeAnomalies",String(t.includeAnomalies)),t?.collapseRetries!==void 0&&r.set("collapseRetries",String(t.collapseRetries));let n=r.toString(),s=n?`/runs/${e}/timeline?${n}`:`/runs/${e}/timeline`;return this.request(s)}async emitEvent(e){let t=e.idempotencyKey??b("evt",e.runId,e.eventType,e.quantity);return this.request("/run-events",{method:"POST",body:JSON.stringify({...e,idempotencyKey:t})})}async emitEventsBatch(e){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:e})})}async listMeters(){let e=await this.request("/pricing-plans");return {data:e.data.map(t=>({id:t.id,name:t.name,meter:t.unitType,unitPriceUsd:t.unitPriceUsd,isActive:t.isActive})),count:e.count}}async estimateFromUsage(e){let t=e.periodStart instanceof Date?e.periodStart.toISOString():e.periodStart,r=e.periodEnd instanceof Date?e.periodEnd.toISOString():e.periodEnd;return this.request("/dashboard/cost-estimate/from-usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,periodStart:t,periodEnd:r,defaultUnitPrice:e.defaultUnitPrice,includeChargedEvents:e.includeChargedEvents,usageTypes:e.usageTypes,customPricing:e.customPricing})})}async estimateFromHypothetical(e){return this.request("/dashboard/cost-estimate/hypothetical",{method:"POST",body:JSON.stringify({items:e.items,defaultUnitPrice:e.defaultUnitPrice,customPricing:e.customPricing})})}async recordRun(e){let t=Date.now(),r=e.workflow,n=e.workflow;if(!e.workflow.startsWith("wf_"))try{let m=(await this.listWorkflows()).data.find(l=>l.slug===e.workflow||l.id===e.workflow);if(m)r=m.id,n=m.name;else {let l=await this.createWorkflow({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,g=>g.toUpperCase()),slug:e.workflow,productSurface:"AGENT"});r=l.id,n=l.name;}}catch{r=e.workflow;}let s=await this.startRun({customerId:e.customerId,workflowId:r,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata}),i=0,a=0;if(e.events.length>0){let w=e.events.map((l,g)=>({runId:s.id,eventType:l.eventType,quantity:l.quantity,units:l.units,description:l.description,costUnits:l.costUnits,metadata:l.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${l.eventType}:${g}`:b("run",s.id,l.eventType,g)})),m=await this.emitEventsBatch(w);i=m.created,a=m.duplicates;}let u=await this.endRun(s.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),c=Date.now()-t,d=e.events.length>0?`${i} events recorded`:"no events",y=`${e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB"} ${n}: ${d} (${u.durationMs??c}ms)`;return {run:{id:s.id,workflowId:r,workflowName:n,status:e.status,durationMs:u.durationMs},events:{created:i,duplicates:a},totalCostUnits:u.totalCostUnits,summary:y}}static generateIdempotencyKey(e){let r=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)].join("|");return `drip_${S("crypto").createHash("sha256").update(r).digest("hex").slice(0,32)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(h=>h.startsWith("t=")),a=s.find(h=>h.startsWith("v1="));if(!i||!a)return !1;let u=parseInt(i.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let p=`${u}.${e}`,y=new TextEncoder,w=y.encode(r),m=y.encode(p),l=globalThis.crypto?.subtle??S("crypto").webcrypto.subtle,g=await l.importKey("raw",w,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),F=await l.sign("HMAC",g,m),L=Array.from(new Uint8Array(F)).map(h=>h.toString(16).padStart(2,"0")).join("");if(c.length!==L.length)return !1;let _=0;for(let h=0;h<c.length;h++)_|=c.charCodeAt(h)^L.charCodeAt(h);return _===0}catch{return false}}static verifyWebhookSignatureSync(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(g=>g.startsWith("t=")),a=s.find(g=>g.startsWith("v1="));if(!i||!a)return !1;let u=parseInt(i.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let p=S("crypto"),y=`${u}.${e}`,w=p.createHmac("sha256",r).update(y).digest("hex"),m=Buffer.from(c),l=Buffer.from(w);return m.length!==l.length?!1:p.timingSafeEqual(m,l)}catch{return false}}static generateWebhookSignature(e,t,r){let n=S("crypto"),s=r??Math.floor(Date.now()/1e3),i=`${s}.${e}`,a=n.createHmac("sha256",t).update(i).digest("hex");return `t=${s},v1=${a}`}createStreamMeter(e){return new k(this.charge.bind(this),e)}},ue=M,U=null;function J(){return U||(U=new M),U}var ce=new Proxy({},{get(o,e){let t=J(),r=t[e];return typeof r=="function"?r.bind(t):r}});
|
|
2
|
+
export{x as CircuitBreaker,P as CircuitBreakerOpenError,M as Drip,f as DripError,I as MetricsCollector,T as RateLimiter,C as ResilienceManager,E as RetryExhaustedError,k as StreamMeter,N as calculateBackoff,R as createDefaultResilienceConfig,j as createDisabledResilienceConfig,q as createHighThroughputResilienceConfig,ue as default,ce as drip,W as isRetryableError};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|