@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stream-meter.ts","../src/resilience.ts","../src/index.ts","../src/integrations/langchain.ts"],"names":["StreamMeter","chargeFn","options","quantity","idempotencyKey","chargeResult","result","DEFAULT_RATE_LIMITER_CONFIG","RateLimiter","config","now","elapsed","timeout","deadline","waitTime","remaining","ms","resolve","RetryExhaustedError","_RetryExhaustedError","attempts","lastError","calculateBackoff","attempt","delay","jitterRange","isRetryableError","error","statusCode","DEFAULT_CIRCUIT_BREAKER_CONFIG","CircuitBreakerOpenError","_CircuitBreakerOpenError","circuitName","timeUntilRetryMs","CircuitBreaker","name","MetricsCollector","windowSize","metrics","latencies","m","a","b","successes","byEndpoint","errors","sum","createDefaultResilienceConfig","createHighThroughputResilienceConfig","ResilienceManager","fn","method","endpoint","startTime","retryCount","duration","DEFAULT_RETRY_CONFIG","defaultIsRetryable","DripError","retryWithBackoff","maxAttempts","baseDelayMs","maxDelayMs","isRetryable","_DripError","message","code","Drip","path","controller","timeoutId","res","data","healthBaseUrl","start","response","latencyMs","status","timestamp","params","customerId","query","charge","chargeId","webhookId","runId","events","plan","periodStart","periodEnd","workflowId","workflowName","existing","w","created","c","run","eventsCreated","eventsDuplicates","batchEvents","event","index","batchResult","endResult","durationMs","eventSummary","summary","components","hash","str","i","char","payload","signature","secret","tolerance","parts","timestampPart","p","signaturePart","providedSignature","signaturePayload","encoder","keyData","payloadData","subtle","cryptoKey","signatureBuffer","expectedSignature","crypto","sigBuffer","expectedBuffer","ts","OPENAI_PRICING","ANTHROPIC_PRICING","getModelPricing","modelName","modelLower","key","pricing","calculateCost","inputTokens","outputTokens","inputCost","outputCost","DripCallbackHandler","value","errorMessage","serialized","prompts","_parentRunId","_extraParams","_tags","_metadata","state","tokenUsage","totalTokens","cost","_token","_idx","_runId","messages","msgList","inputStr","toolName","output","inputs","chainType","outputs","action","toolInput","finish","actionCount","retrieverName","documents","_text"],"mappings":"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","file":"langchain.cjs","sourcesContent":["/**\n * StreamMeter - Accumulate usage locally and charge once at the end.\n *\n * Perfect for LLM token streaming and other high-frequency metering scenarios\n * where you want to avoid making an API call for every small increment.\n *\n * @example\n * ```typescript\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * });\n *\n * for await (const chunk of llmStream) {\n * meter.add(chunk.tokens);\n * }\n *\n * const result = await meter.flush();\n * console.log(`Charged ${result.charge.amountUsdc} for ${result.quantity} tokens`);\n * ```\n */\n\nimport type { ChargeResult, ChargeParams } from './index.js';\n\n/**\n * Options for creating a StreamMeter.\n */\nexport interface StreamMeterOptions {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n */\n meter: string;\n\n /**\n * Unique key to prevent duplicate charges.\n * If not provided, one will be generated.\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to the charge.\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Auto-flush when accumulated quantity reaches this threshold.\n * Useful for long-running streams where you want periodic charges.\n */\n flushThreshold?: number;\n\n /**\n * Callback invoked on each add() call.\n * Useful for logging or progress tracking.\n */\n onAdd?: (quantity: number, total: number) => void;\n\n /**\n * Callback invoked after each successful flush.\n */\n onFlush?: (result: StreamMeterFlushResult) => void;\n}\n\n/**\n * Result of flushing a StreamMeter.\n */\nexport interface StreamMeterFlushResult {\n /** Whether the flush was successful */\n success: boolean;\n\n /** The quantity that was charged */\n quantity: number;\n\n /** The charge result from the API (if quantity > 0) */\n charge: ChargeResult['charge'] | null;\n\n /** Whether this was an idempotent replay */\n isReplay: boolean;\n}\n\n/**\n * Internal charge function type (injected by Drip class).\n */\nexport type ChargeFn = (params: ChargeParams) => Promise<ChargeResult>;\n\n/**\n * StreamMeter accumulates usage locally and charges once when flushed.\n *\n * This is ideal for:\n * - LLM token streaming (charge once at end of stream)\n * - High-frequency events (batch small increments)\n * - Partial failure handling (charge for what was delivered)\n */\nexport class StreamMeter {\n private _total: number = 0;\n private _flushed: boolean = false;\n private _flushCount: number = 0;\n private readonly _chargeFn: ChargeFn;\n private readonly _options: StreamMeterOptions;\n\n /**\n * Creates a new StreamMeter.\n *\n * @param chargeFn - The charge function from Drip client\n * @param options - StreamMeter configuration\n */\n constructor(chargeFn: ChargeFn, options: StreamMeterOptions) {\n this._chargeFn = chargeFn;\n this._options = options;\n }\n\n /**\n * Current accumulated quantity (not yet charged).\n */\n get total(): number {\n return this._total;\n }\n\n /**\n * Whether this meter has been flushed at least once.\n */\n get isFlushed(): boolean {\n return this._flushed;\n }\n\n /**\n * Number of times this meter has been flushed.\n */\n get flushCount(): number {\n return this._flushCount;\n }\n\n /**\n * Add quantity to the accumulated total.\n *\n * If a flushThreshold is set and the total exceeds it,\n * this will automatically trigger a flush.\n *\n * @param quantity - Amount to add (must be positive)\n * @returns Promise that resolves when add completes (may trigger auto-flush)\n */\n async add(quantity: number): Promise<StreamMeterFlushResult | null> {\n if (quantity <= 0) {\n return null;\n }\n\n this._total += quantity;\n\n // Invoke callback if provided\n this._options.onAdd?.(quantity, this._total);\n\n // Check for auto-flush threshold\n if (\n this._options.flushThreshold !== undefined &&\n this._total >= this._options.flushThreshold\n ) {\n return this.flush();\n }\n\n return null;\n }\n\n /**\n * Synchronously add quantity without auto-flush.\n * Use this for maximum performance when you don't need threshold-based flushing.\n *\n * @param quantity - Amount to add (must be positive)\n */\n addSync(quantity: number): void {\n if (quantity <= 0) {\n return;\n }\n\n this._total += quantity;\n\n // Invoke callback if provided\n this._options.onAdd?.(quantity, this._total);\n }\n\n /**\n * Flush accumulated usage and charge the customer.\n *\n * If total is 0, returns a success result with no charge.\n * After flush, the meter resets to 0 and can be reused.\n *\n * @returns The flush result including charge details\n */\n async flush(): Promise<StreamMeterFlushResult> {\n const quantity = this._total;\n\n // Reset total before charging to avoid double-counting on retry\n this._total = 0;\n\n // Nothing to charge\n if (quantity === 0) {\n const result: StreamMeterFlushResult = {\n success: true,\n quantity: 0,\n charge: null,\n isReplay: false,\n };\n return result;\n }\n\n // Generate idempotency key for this flush\n const idempotencyKey = this._options.idempotencyKey\n ? `${this._options.idempotencyKey}_flush_${this._flushCount}`\n : undefined;\n\n // Charge the customer\n const chargeResult = await this._chargeFn({\n customerId: this._options.customerId,\n meter: this._options.meter,\n quantity,\n idempotencyKey,\n metadata: this._options.metadata,\n });\n\n this._flushed = true;\n this._flushCount++;\n\n const result: StreamMeterFlushResult = {\n success: chargeResult.success,\n quantity,\n charge: chargeResult.charge,\n isReplay: chargeResult.isReplay,\n };\n\n // Invoke callback if provided\n this._options.onFlush?.(result);\n\n return result;\n }\n\n /**\n * Reset the meter without charging.\n * Use this to discard accumulated usage (e.g., on error before delivery).\n */\n reset(): void {\n this._total = 0;\n }\n}\n","/**\n * Production-grade resilience patterns for the Drip SDK.\n *\n * This module provides:\n * - Rate limiting (token bucket algorithm)\n * - Retry with exponential backoff\n * - Circuit breaker pattern\n * - Request metrics and observability\n */\n\n// =============================================================================\n// Rate Limiter (Token Bucket Algorithm)\n// =============================================================================\n\n/**\n * Configuration for rate limiting.\n */\nexport interface RateLimiterConfig {\n /**\n * Maximum requests per second.\n * @default 100\n */\n requestsPerSecond: number;\n\n /**\n * Maximum burst size (bucket capacity).\n * @default 200\n */\n burstSize: number;\n\n /**\n * Whether rate limiting is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default rate limiter configuration.\n */\nexport const DEFAULT_RATE_LIMITER_CONFIG: RateLimiterConfig = {\n requestsPerSecond: 100,\n burstSize: 200,\n enabled: true,\n};\n\n/**\n * Thread-safe token bucket rate limiter.\n *\n * Allows bursting up to `burstSize` requests, then limits to\n * `requestsPerSecond` sustained rate.\n */\nexport class RateLimiter {\n private readonly config: RateLimiterConfig;\n private tokens: number;\n private lastRefill: number;\n\n constructor(config?: Partial<RateLimiterConfig>) {\n this.config = { ...DEFAULT_RATE_LIMITER_CONFIG, ...config };\n this.tokens = this.config.burstSize;\n this.lastRefill = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time.\n */\n private refill(): void {\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000; // Convert to seconds\n this.tokens = Math.min(\n this.config.burstSize,\n this.tokens + elapsed * this.config.requestsPerSecond\n );\n this.lastRefill = now;\n }\n\n /**\n * Acquire a token, blocking if necessary.\n *\n * @param timeout - Maximum time to wait for a token in ms (undefined = wait forever)\n * @returns Promise that resolves to true if token acquired, false if timeout\n */\n async acquire(timeout?: number): Promise<boolean> {\n if (!this.config.enabled) {\n return true;\n }\n\n const deadline = timeout !== undefined ? Date.now() + timeout : undefined;\n\n while (true) {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n // Calculate wait time for next token\n const waitTime = ((1 - this.tokens) / this.config.requestsPerSecond) * 1000;\n\n if (deadline !== undefined) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n return false;\n }\n await this.sleep(Math.min(waitTime, remaining));\n } else {\n await this.sleep(waitTime);\n }\n }\n }\n\n /**\n * Try to acquire a token without waiting.\n *\n * @returns true if token acquired, false otherwise\n */\n tryAcquire(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n return false;\n }\n\n /**\n * Get current number of available tokens.\n */\n get availableTokens(): number {\n this.refill();\n return this.tokens;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n// =============================================================================\n// Retry with Exponential Backoff\n// =============================================================================\n\n/**\n * Configuration for retry behavior.\n */\nexport interface RetryConfig {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxRetries: number;\n\n /**\n * Base delay in milliseconds.\n * @default 100\n */\n baseDelayMs: number;\n\n /**\n * Maximum delay in milliseconds.\n * @default 10000\n */\n maxDelayMs: number;\n\n /**\n * Exponential backoff base.\n * @default 2\n */\n exponentialBase: number;\n\n /**\n * Random jitter factor (0-1).\n * @default 0.1\n */\n jitter: number;\n\n /**\n * HTTP status codes that should trigger retry.\n * @default [429, 500, 502, 503, 504]\n */\n retryableStatusCodes: number[];\n\n /**\n * Whether retry is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default retry configuration.\n */\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: 3,\n baseDelayMs: 100,\n maxDelayMs: 10000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n};\n\n/**\n * Error thrown when all retry attempts have been exhausted.\n */\nexport class RetryExhaustedError extends Error {\n readonly attempts: number;\n readonly lastError: Error;\n\n constructor(attempts: number, lastError: Error) {\n super(`Retry exhausted after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n this.attempts = attempts;\n this.lastError = lastError;\n Object.setPrototypeOf(this, RetryExhaustedError.prototype);\n }\n}\n\n/**\n * Calculate backoff delay for a given attempt.\n */\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n let delay = config.baseDelayMs * Math.pow(config.exponentialBase, attempt);\n delay = Math.min(delay, config.maxDelayMs);\n\n // Add jitter\n if (config.jitter > 0) {\n const jitterRange = delay * config.jitter;\n delay += Math.random() * 2 * jitterRange - jitterRange;\n }\n\n return Math.max(0, delay);\n}\n\n/**\n * Check if an error is retryable based on configuration.\n */\nexport function isRetryableError(\n error: unknown,\n config: RetryConfig\n): boolean {\n if (error instanceof Error) {\n // Check for network errors\n if (\n error.message.includes('fetch') ||\n error.message.includes('network') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('ETIMEDOUT')\n ) {\n return true;\n }\n\n // Check for status code on error object\n const statusCode = (error as { statusCode?: number }).statusCode;\n if (statusCode !== undefined) {\n return config.retryableStatusCodes.includes(statusCode);\n }\n }\n\n return false;\n}\n\n// =============================================================================\n// Circuit Breaker\n// =============================================================================\n\n/**\n * Circuit breaker states.\n */\nexport type CircuitState = 'closed' | 'open' | 'half_open';\n\n/**\n * Configuration for circuit breaker.\n */\nexport interface CircuitBreakerConfig {\n /**\n * Number of failures before opening circuit.\n * @default 5\n */\n failureThreshold: number;\n\n /**\n * Number of successes in half-open to close circuit.\n * @default 2\n */\n successThreshold: number;\n\n /**\n * Milliseconds to wait before transitioning from open to half-open.\n * @default 30000\n */\n timeoutMs: number;\n\n /**\n * Whether circuit breaker is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default circuit breaker configuration.\n */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n successThreshold: 2,\n timeoutMs: 30000,\n enabled: true,\n};\n\n/**\n * Error thrown when circuit breaker is open.\n */\nexport class CircuitBreakerOpenError extends Error {\n readonly circuitName: string;\n readonly timeUntilRetryMs: number;\n\n constructor(circuitName: string, timeUntilRetryMs: number) {\n super(\n `Circuit '${circuitName}' is open. Retry in ${Math.round(timeUntilRetryMs)}ms`\n );\n this.name = 'CircuitBreakerOpenError';\n this.circuitName = circuitName;\n this.timeUntilRetryMs = timeUntilRetryMs;\n Object.setPrototypeOf(this, CircuitBreakerOpenError.prototype);\n }\n}\n\n/**\n * Circuit breaker pattern implementation.\n *\n * Prevents cascading failures by failing fast when a service is unhealthy.\n */\nexport class CircuitBreaker {\n readonly name: string;\n private readonly config: CircuitBreakerConfig;\n private state: CircuitState = 'closed';\n private failureCount = 0;\n private successCount = 0;\n private lastFailureTime: number | null = null;\n\n constructor(name: string, config?: Partial<CircuitBreakerConfig>) {\n this.name = name;\n this.config = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };\n }\n\n /**\n * Get current circuit state.\n */\n getState(): CircuitState {\n this.checkStateTransition();\n return this.state;\n }\n\n /**\n * Check if state should transition based on timeout.\n */\n private checkStateTransition(): void {\n if (this.state === 'open' && this.lastFailureTime !== null) {\n const elapsed = Date.now() - this.lastFailureTime;\n if (elapsed >= this.config.timeoutMs) {\n this.state = 'half_open';\n this.successCount = 0;\n }\n }\n }\n\n /**\n * Record a successful call.\n */\n recordSuccess(): void {\n if (!this.config.enabled) {\n return;\n }\n\n if (this.state === 'half_open') {\n this.successCount += 1;\n if (this.successCount >= this.config.successThreshold) {\n this.state = 'closed';\n this.failureCount = 0;\n }\n } else if (this.state === 'closed') {\n // Reset failure count on success\n this.failureCount = 0;\n }\n }\n\n /**\n * Record a failed call.\n */\n recordFailure(): void {\n if (!this.config.enabled) {\n return;\n }\n\n this.failureCount += 1;\n this.lastFailureTime = Date.now();\n\n if (this.state === 'half_open') {\n // Any failure in half-open returns to open\n this.state = 'open';\n } else if (this.state === 'closed') {\n if (this.failureCount >= this.config.failureThreshold) {\n this.state = 'open';\n }\n }\n }\n\n /**\n * Check if a request should be allowed.\n */\n allowRequest(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n this.checkStateTransition();\n\n if (this.state === 'closed') {\n return true;\n } else if (this.state === 'half_open') {\n return true; // Allow test request\n } else {\n return false;\n }\n }\n\n /**\n * Get milliseconds until circuit transitions to half-open.\n */\n getTimeUntilRetry(): number {\n if (this.state !== 'open' || this.lastFailureTime === null) {\n return 0;\n }\n const elapsed = Date.now() - this.lastFailureTime;\n return Math.max(0, this.config.timeoutMs - elapsed);\n }\n\n /**\n * Reset the circuit breaker to initial state.\n */\n reset(): void {\n this.state = 'closed';\n this.failureCount = 0;\n this.successCount = 0;\n this.lastFailureTime = null;\n }\n}\n\n// =============================================================================\n// Metrics and Observability\n// =============================================================================\n\n/**\n * Metrics for a single request.\n */\nexport interface RequestMetrics {\n method: string;\n endpoint: string;\n statusCode: number | null;\n durationMs: number;\n success: boolean;\n timestamp: number;\n error?: string;\n retryCount: number;\n}\n\n/**\n * Aggregated metrics summary.\n */\nexport interface MetricsSummary {\n windowSize: number;\n totalRequests: number;\n totalSuccesses: number;\n totalFailures: number;\n successRate: number;\n avgLatencyMs: number;\n minLatencyMs: number;\n maxLatencyMs: number;\n p50LatencyMs: number;\n p95LatencyMs: number;\n p99LatencyMs: number;\n requestsByEndpoint: Record<string, number>;\n errorsByType: Record<string, number>;\n}\n\n/**\n * Collects and aggregates request metrics.\n *\n * Thread-safe metrics collection with windowed aggregation.\n */\nexport class MetricsCollector {\n private readonly windowSize: number;\n private readonly metrics: RequestMetrics[] = [];\n private totalRequests = 0;\n private totalSuccesses = 0;\n private totalFailures = 0;\n\n constructor(windowSize = 1000) {\n this.windowSize = windowSize;\n }\n\n /**\n * Record a request's metrics.\n */\n record(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n this.totalRequests += 1;\n\n if (metrics.success) {\n this.totalSuccesses += 1;\n } else {\n this.totalFailures += 1;\n }\n\n // Maintain window size\n while (this.metrics.length > this.windowSize) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get aggregated metrics summary.\n */\n getSummary(): MetricsSummary {\n if (this.metrics.length === 0) {\n return {\n windowSize: 0,\n totalRequests: 0,\n totalSuccesses: 0,\n totalFailures: 0,\n successRate: 0,\n avgLatencyMs: 0,\n minLatencyMs: 0,\n maxLatencyMs: 0,\n p50LatencyMs: 0,\n p95LatencyMs: 0,\n p99LatencyMs: 0,\n requestsByEndpoint: {},\n errorsByType: {},\n };\n }\n\n const latencies = this.metrics.map((m) => m.durationMs).sort((a, b) => a - b);\n const successes = this.metrics.filter((m) => m.success).length;\n\n // Group by endpoint\n const byEndpoint: Record<string, number> = {};\n for (const m of this.metrics) {\n byEndpoint[m.endpoint] = (byEndpoint[m.endpoint] ?? 0) + 1;\n }\n\n // Group errors\n const errors: Record<string, number> = {};\n for (const m of this.metrics) {\n if (m.error) {\n errors[m.error] = (errors[m.error] ?? 0) + 1;\n }\n }\n\n const sum = latencies.reduce((a, b) => a + b, 0);\n\n return {\n windowSize: this.metrics.length,\n totalRequests: this.totalRequests,\n totalSuccesses: this.totalSuccesses,\n totalFailures: this.totalFailures,\n successRate: (successes / this.metrics.length) * 100,\n avgLatencyMs: sum / latencies.length,\n minLatencyMs: latencies[0],\n maxLatencyMs: latencies[latencies.length - 1],\n p50LatencyMs: latencies[Math.floor(latencies.length * 0.5)],\n p95LatencyMs: latencies[Math.floor(latencies.length * 0.95)],\n p99LatencyMs: latencies[Math.floor(latencies.length * 0.99)],\n requestsByEndpoint: byEndpoint,\n errorsByType: errors,\n };\n }\n\n /**\n * Reset all metrics.\n */\n reset(): void {\n this.metrics.length = 0;\n this.totalRequests = 0;\n this.totalSuccesses = 0;\n this.totalFailures = 0;\n }\n}\n\n// =============================================================================\n// Combined Resilience Manager\n// =============================================================================\n\n/**\n * Combined configuration for all resilience features.\n */\nexport interface ResilienceConfig {\n rateLimiter: RateLimiterConfig;\n retry: RetryConfig;\n circuitBreaker: CircuitBreakerConfig;\n collectMetrics: boolean;\n}\n\n/**\n * Create default production configuration.\n */\nexport function createDefaultResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: {\n requestsPerSecond: 100,\n burstSize: 200,\n enabled: true,\n },\n retry: {\n maxRetries: 3,\n baseDelayMs: 100,\n maxDelayMs: 10000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n },\n circuitBreaker: {\n failureThreshold: 5,\n successThreshold: 2,\n timeoutMs: 30000,\n enabled: true,\n },\n collectMetrics: true,\n };\n}\n\n/**\n * Create configuration with all features disabled.\n */\nexport function createDisabledResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: { ...DEFAULT_RATE_LIMITER_CONFIG, enabled: false },\n retry: { ...DEFAULT_RETRY_CONFIG, enabled: false },\n circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, enabled: false },\n collectMetrics: false,\n };\n}\n\n/**\n * Create configuration optimized for high throughput.\n */\nexport function createHighThroughputResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: {\n requestsPerSecond: 1000,\n burstSize: 2000,\n enabled: true,\n },\n retry: {\n maxRetries: 2,\n baseDelayMs: 50,\n maxDelayMs: 5000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n },\n circuitBreaker: {\n failureThreshold: 10,\n successThreshold: 3,\n timeoutMs: 15000,\n enabled: true,\n },\n collectMetrics: true,\n };\n}\n\n/**\n * Health status of resilience components.\n */\nexport interface ResilienceHealth {\n circuitBreaker: {\n state: CircuitState;\n timeUntilRetryMs: number;\n };\n rateLimiter: {\n availableTokens: number;\n requestsPerSecond: number;\n };\n metrics: MetricsSummary | null;\n}\n\n/**\n * Manages all resilience features for the SDK.\n *\n * Provides a unified interface for rate limiting, retry, circuit breaker,\n * and metrics collection.\n */\nexport class ResilienceManager {\n readonly config: ResilienceConfig;\n readonly rateLimiter: RateLimiter;\n readonly circuitBreaker: CircuitBreaker;\n readonly metrics: MetricsCollector | null;\n\n constructor(config?: Partial<ResilienceConfig>) {\n this.config = {\n ...createDefaultResilienceConfig(),\n ...config,\n rateLimiter: {\n ...createDefaultResilienceConfig().rateLimiter,\n ...config?.rateLimiter,\n },\n retry: {\n ...createDefaultResilienceConfig().retry,\n ...config?.retry,\n },\n circuitBreaker: {\n ...createDefaultResilienceConfig().circuitBreaker,\n ...config?.circuitBreaker,\n },\n };\n\n this.rateLimiter = new RateLimiter(this.config.rateLimiter);\n this.circuitBreaker = new CircuitBreaker('drip_api', this.config.circuitBreaker);\n this.metrics = this.config.collectMetrics ? new MetricsCollector() : null;\n }\n\n /**\n * Execute a function with all resilience features.\n *\n * @param fn - The function to execute\n * @param method - HTTP method for metrics\n * @param endpoint - Endpoint for metrics\n * @returns Result of the function\n */\n async execute<T>(\n fn: () => Promise<T>,\n method = 'UNKNOWN',\n endpoint = 'unknown'\n ): Promise<T> {\n const startTime = performance.now();\n let retryCount = 0;\n let lastError: Error | null = null;\n\n // Rate limiting\n const acquired = await this.rateLimiter.acquire(30000);\n if (!acquired) {\n throw new Error('Rate limiter timeout');\n }\n\n // Circuit breaker check\n if (!this.circuitBreaker.allowRequest()) {\n throw new CircuitBreakerOpenError(\n this.circuitBreaker.name,\n this.circuitBreaker.getTimeUntilRetry()\n );\n }\n\n // Execute with retry\n for (let attempt = 0; attempt <= this.config.retry.maxRetries; attempt++) {\n try {\n const result = await fn();\n this.circuitBreaker.recordSuccess();\n\n // Record success metrics\n if (this.metrics) {\n const duration = performance.now() - startTime;\n this.metrics.record({\n method,\n endpoint,\n statusCode: 200,\n durationMs: duration,\n success: true,\n timestamp: Date.now(),\n retryCount,\n });\n }\n\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if retryable\n const isRetryable =\n this.config.retry.enabled &&\n isRetryableError(error, this.config.retry);\n\n if (isRetryable && attempt < this.config.retry.maxRetries) {\n retryCount += 1;\n const delay = calculateBackoff(attempt, this.config.retry);\n await this.sleep(delay);\n continue;\n }\n\n // Not retryable or exhausted retries\n this.circuitBreaker.recordFailure();\n\n // Record failure metrics\n if (this.metrics) {\n const duration = performance.now() - startTime;\n const statusCode = (error as { statusCode?: number }).statusCode ?? null;\n this.metrics.record({\n method,\n endpoint,\n statusCode,\n durationMs: duration,\n success: false,\n timestamp: Date.now(),\n error: lastError.name,\n retryCount,\n });\n }\n\n throw error;\n }\n }\n\n // Should not reach here\n if (lastError) {\n throw new RetryExhaustedError(this.config.retry.maxRetries + 1, lastError);\n }\n throw new Error('Unexpected execution path');\n }\n\n /**\n * Get current metrics summary.\n */\n getMetrics(): MetricsSummary | null {\n return this.metrics?.getSummary() ?? null;\n }\n\n /**\n * Get health status of all resilience components.\n */\n getHealth(): ResilienceHealth {\n return {\n circuitBreaker: {\n state: this.circuitBreaker.getState(),\n timeUntilRetryMs: this.circuitBreaker.getTimeUntilRetry(),\n },\n rateLimiter: {\n availableTokens: this.rateLimiter.availableTokens,\n requestsPerSecond: this.config.rateLimiter.requestsPerSecond,\n },\n metrics: this.getMetrics(),\n };\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * Drip SDK - Usage-based billing for Node.js\n *\n * The official SDK for integrating with the Drip billing platform.\n * Provides methods for managing customers, recording usage, handling charges,\n * and configuring webhooks.\n *\n * @packageDocumentation\n */\n\nimport { StreamMeter, type StreamMeterOptions } from './stream-meter.js';\nimport {\n ResilienceManager,\n type ResilienceConfig,\n type ResilienceHealth,\n type MetricsSummary,\n createDefaultResilienceConfig,\n createDisabledResilienceConfig,\n createHighThroughputResilienceConfig,\n} from './resilience.js';\n\n// ============================================================================\n// Retry Utility\n// ============================================================================\n\n/**\n * Default retry configuration.\n */\nconst DEFAULT_RETRY_CONFIG = {\n maxAttempts: 3,\n baseDelayMs: 100,\n maxDelayMs: 5000,\n} as const;\n\n/**\n * Retry options for API calls.\n */\nexport interface RetryOptions {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxAttempts?: number;\n\n /**\n * Base delay between retries in milliseconds (exponential backoff).\n * @default 100\n */\n baseDelayMs?: number;\n\n /**\n * Maximum delay between retries in milliseconds.\n * @default 5000\n */\n maxDelayMs?: number;\n\n /**\n * Custom function to determine if an error is retryable.\n * By default, retries on network errors and 5xx status codes.\n */\n isRetryable?: (error: unknown) => boolean;\n}\n\n/**\n * Default function to determine if an error is retryable.\n */\nfunction defaultIsRetryable(error: unknown): boolean {\n // Retry on network errors\n if (error instanceof Error) {\n if (error.message.includes('fetch') || error.message.includes('network')) {\n return true;\n }\n }\n\n // Retry on 5xx errors and timeouts\n if (error instanceof DripError) {\n return error.statusCode >= 500 || error.statusCode === 408 || error.statusCode === 429;\n }\n\n return false;\n}\n\n/**\n * Executes a function with exponential backoff retry.\n * @internal\n */\nasync function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs;\n const isRetryable = options.isRetryable ?? defaultIsRetryable;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Don't retry if it's the last attempt or error isn't retryable\n if (attempt === maxAttempts || !isRetryable(error)) {\n throw error;\n }\n\n // Exponential backoff with jitter\n const delay = Math.min(\n baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100,\n maxDelayMs,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n // Should never reach here, but TypeScript needs it\n throw lastError;\n}\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration options for the Drip SDK client.\n */\nexport interface DripConfig {\n /**\n * Your Drip API key. Obtain this from the Drip dashboard.\n * @example \"drip_live_abc123...\"\n */\n apiKey: string;\n\n /**\n * Base URL for the Drip API. Defaults to production API.\n * Override for staging/development environments.\n * @default \"https://api.drip.dev/v1\"\n */\n baseUrl?: string;\n\n /**\n * Request timeout in milliseconds.\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Enable production resilience features (rate limiting, retry with backoff,\n * circuit breaker, metrics).\n *\n * - `true`: Use default production settings (100 req/s, 3 retries)\n * - `'high-throughput'`: Optimized for high throughput (1000 req/s, 2 retries)\n * - `ResilienceConfig`: Custom configuration object\n * - `undefined`/`false`: Disabled (default for backward compatibility)\n *\n * @example\n * ```typescript\n * // Enable with defaults\n * const drip = new Drip({ apiKey: '...', resilience: true });\n *\n * // High throughput mode\n * const drip = new Drip({ apiKey: '...', resilience: 'high-throughput' });\n *\n * // Custom config\n * const drip = new Drip({\n * apiKey: '...',\n * resilience: {\n * rateLimiter: { requestsPerSecond: 500, burstSize: 1000, enabled: true },\n * retry: { maxRetries: 5, enabled: true },\n * circuitBreaker: { failureThreshold: 10, enabled: true },\n * collectMetrics: true,\n * },\n * });\n * ```\n */\n resilience?: boolean | 'high-throughput' | Partial<ResilienceConfig>;\n}\n\n// ============================================================================\n// Customer Types\n// ============================================================================\n\n/**\n * Parameters for creating a new customer.\n */\nexport interface CreateCustomerParams {\n /**\n * Your internal customer/user ID for reconciliation.\n * @example \"user_12345\"\n */\n externalCustomerId?: string;\n\n /**\n * The customer's Drip Smart Account address (derived from their EOA).\n * @example \"0x1234567890abcdef...\"\n */\n onchainAddress: string;\n\n /**\n * Additional metadata to store with the customer.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * A Drip customer record.\n */\nexport interface Customer {\n /** Unique customer ID in Drip */\n id: string;\n\n /** Your business ID (optional - may not be returned by all endpoints) */\n businessId?: string;\n\n /** Your external customer ID (if provided) */\n externalCustomerId: string | null;\n\n /** Customer's on-chain address */\n onchainAddress: string;\n\n /** Custom metadata */\n metadata: Record<string, unknown> | null;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of last update */\n updatedAt: string;\n}\n\n/**\n * Options for listing customers.\n */\nexport interface ListCustomersOptions {\n /**\n * Maximum number of customers to return (1-100).\n * @default 100\n */\n limit?: number;\n\n /**\n * Filter by customer status.\n */\n status?: 'ACTIVE' | 'LOW_BALANCE' | 'PAUSED';\n}\n\n/**\n * Response from listing customers.\n */\nexport interface ListCustomersResponse {\n /** Array of customers */\n data: Customer[];\n\n /** Total count returned */\n count: number;\n}\n\n/**\n * Customer balance information.\n */\nexport interface BalanceResult {\n /** Customer ID */\n customerId: string;\n\n /** On-chain address */\n onchainAddress: string;\n\n /** Balance in USDC (6 decimals) - matches backend field name */\n balanceUsdc: string;\n\n /** Pending charges in USDC */\n pendingChargesUsdc: string;\n\n /** Available USDC (balance minus pending) */\n availableUsdc: string;\n\n /** ISO timestamp of last balance sync */\n lastSyncedAt: string | null;\n}\n\n// ============================================================================\n// Usage & Charge Types\n// ============================================================================\n\n/**\n * Parameters for recording usage and charging a customer.\n */\nexport interface ChargeParams {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n * @example \"api_calls\", \"compute_seconds\", \"storage_gb\"\n */\n meter: string;\n\n /**\n * The quantity of usage to record.\n * Will be multiplied by the meter's unit price.\n */\n quantity: number;\n\n /**\n * Unique key to prevent duplicate charges.\n * If provided, retrying with the same key returns the original charge.\n * @example \"req_abc123\"\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of a successful charge operation.\n */\nexport interface ChargeResult {\n /** Whether the charge was successful */\n success: boolean;\n\n /** The usage event ID */\n usageEventId: string;\n\n /** True if this was an idempotent replay (returned cached result from previous request) */\n isReplay: boolean;\n\n /** Details about the charge */\n charge: {\n /** Unique charge ID */\n id: string;\n\n /** Amount charged in USDC (6 decimals) */\n amountUsdc: string;\n\n /** Amount in native token */\n amountToken: string;\n\n /** Blockchain transaction hash */\n txHash: string;\n\n /** Current status of the charge */\n status: ChargeStatus;\n };\n}\n\n/**\n * Possible charge statuses.\n */\nexport type ChargeStatus =\n | 'PENDING'\n | 'SUBMITTED'\n | 'CONFIRMED'\n | 'FAILED'\n | 'REFUNDED';\n\n/**\n * Parameters for tracking usage without billing.\n * Use this for internal visibility, pilots, or pre-billing tracking.\n */\nexport interface TrackUsageParams {\n /**\n * The Drip customer ID to track usage for.\n * @example \"cust_abc123\"\n */\n customerId: string;\n\n /**\n * The meter/usage type (e.g., 'api_calls', 'tokens').\n */\n meter: string;\n\n /**\n * The quantity of usage to record.\n */\n quantity: number;\n\n /**\n * Unique key to prevent duplicate records.\n */\n idempotencyKey?: string;\n\n /**\n * Human-readable unit label (e.g., 'tokens', 'requests').\n */\n units?: string;\n\n /**\n * Human-readable description of this usage event.\n */\n description?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of tracking usage (no billing).\n */\nexport interface TrackUsageResult {\n /** Whether the usage was recorded */\n success: boolean;\n\n /** The usage event ID */\n usageEventId: string;\n\n /** Customer ID */\n customerId: string;\n\n /** Usage type that was recorded */\n usageType: string;\n\n /** Quantity recorded */\n quantity: number;\n\n /** Whether this customer is internal-only */\n isInternal: boolean;\n\n /** Confirmation message */\n message: string;\n}\n\n/**\n * A detailed charge record.\n */\nexport interface Charge {\n /** Unique charge ID */\n id: string;\n\n /** Associated usage event ID */\n usageId: string;\n\n /** Customer ID */\n customerId: string;\n\n /** Customer details */\n customer: {\n id: string;\n onchainAddress: string;\n externalCustomerId: string | null;\n };\n\n /** Usage event details */\n usageEvent: {\n id: string;\n type: string;\n quantity: string;\n metadata: Record<string, unknown> | null;\n };\n\n /** Amount in USDC */\n amountUsdc: string;\n\n /** Amount in native token */\n amountToken: string;\n\n /** Transaction hash (if submitted) */\n txHash: string | null;\n\n /** Block number (if confirmed) */\n blockNumber: string | null;\n\n /** Current status */\n status: ChargeStatus;\n\n /** Failure reason (if failed) */\n failureReason: string | null;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of confirmation */\n confirmedAt: string | null;\n}\n\n/**\n * Options for listing charges.\n */\nexport interface ListChargesOptions {\n /**\n * Filter by customer ID.\n */\n customerId?: string;\n\n /**\n * Filter by charge status.\n */\n status?: ChargeStatus;\n\n /**\n * Maximum number of charges to return (1-100).\n * @default 100\n */\n limit?: number;\n\n /**\n * Number of charges to skip (for pagination).\n * @default 0\n */\n offset?: number;\n}\n\n/**\n * Response from listing charges.\n */\nexport interface ListChargesResponse {\n /** Array of charges */\n data: Charge[];\n\n /** Total count returned */\n count: number;\n}\n\n// ============================================================================\n// Webhook Types\n// ============================================================================\n\n/**\n * Available webhook event types.\n */\nexport type WebhookEventType =\n | 'customer.balance.low'\n | 'usage.recorded'\n | 'charge.succeeded'\n | 'charge.failed'\n | 'customer.deposit.confirmed'\n | 'customer.withdraw.confirmed'\n | 'customer.usage_cap.reached'\n | 'webhook.endpoint.unhealthy'\n | 'customer.created'\n | 'api_key.created'\n | 'pricing_plan.updated'\n | 'transaction.created'\n | 'transaction.pending'\n | 'transaction.confirmed'\n | 'transaction.failed';\n\n/**\n * Parameters for creating a webhook.\n */\nexport interface CreateWebhookParams {\n /**\n * The URL to send webhook events to.\n * Must be HTTPS in production.\n * @example \"https://api.yourapp.com/webhooks/drip\"\n */\n url: string;\n\n /**\n * Array of event types to subscribe to.\n * @example [\"charge.succeeded\", \"charge.failed\"]\n */\n events: WebhookEventType[];\n\n /**\n * Optional description for the webhook.\n */\n description?: string;\n}\n\n/**\n * A webhook configuration.\n */\nexport interface Webhook {\n /** Unique webhook ID */\n id: string;\n\n /** Webhook endpoint URL */\n url: string;\n\n /** Subscribed event types */\n events: string[];\n\n /** Description */\n description: string | null;\n\n /** Whether the webhook is active */\n isActive: boolean;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of last update */\n updatedAt: string;\n\n /** Delivery statistics */\n stats?: {\n totalDeliveries: number;\n successfulDeliveries: number;\n failedDeliveries: number;\n lastDeliveryAt: string | null;\n };\n}\n\n/**\n * Response from creating a webhook.\n */\nexport interface CreateWebhookResponse extends Webhook {\n /**\n * The webhook signing secret.\n * Only returned once at creation time - save it securely!\n */\n secret: string;\n\n /** Reminder to save the secret */\n message: string;\n}\n\n/**\n * Response from listing webhooks.\n */\nexport interface ListWebhooksResponse {\n /** Array of webhooks */\n data: Webhook[];\n\n /** Total count */\n count: number;\n}\n\n/**\n * Response from deleting a webhook.\n */\nexport interface DeleteWebhookResponse {\n /** Whether the deletion was successful */\n success: boolean;\n}\n\n// ============================================================================\n// Checkout Types\n// ============================================================================\n\n/**\n * Parameters for creating a checkout session.\n * This is the primary way to get money into a customer's account.\n */\nexport interface CheckoutParams {\n /**\n * Existing customer ID (optional).\n * If not provided, a new customer is created after payment.\n */\n customerId?: string;\n\n /**\n * Your internal customer/user ID for new customers.\n * Used to link the Drip customer to your system.\n */\n externalCustomerId?: string;\n\n /**\n * Amount in cents (e.g., 5000 = $50.00).\n * Minimum: 500 ($5.00)\n * Maximum: 1000000 ($10,000.00)\n */\n amount: number;\n\n /**\n * URL to redirect after successful payment.\n * Query params will be added: session_id, customer_id, status\n */\n returnUrl: string;\n\n /**\n * URL to redirect if user cancels (optional).\n */\n cancelUrl?: string;\n\n /**\n * Custom metadata to attach to this checkout.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of creating a checkout session.\n */\nexport interface CheckoutResult {\n /** Checkout session ID */\n id: string;\n\n /** URL to redirect user to for payment */\n url: string;\n\n /** ISO timestamp when session expires (30 minutes) */\n expiresAt: string;\n\n /** Amount in USD */\n amountUsd: number;\n}\n\n// ============================================================================\n// Run & Event Types (Execution Ledger)\n// ============================================================================\n\n/**\n * Parameters for creating a new workflow.\n */\nexport interface CreateWorkflowParams {\n /** Human-readable workflow name */\n name: string;\n\n /** URL-safe identifier (lowercase alphanumeric with underscores/hyphens) */\n slug: string;\n\n /** Type of workflow */\n productSurface?: 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';\n\n /** Optional description */\n description?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * A workflow definition.\n */\nexport interface Workflow {\n id: string;\n name: string;\n slug: string;\n productSurface: string;\n description: string | null;\n isActive: boolean;\n createdAt: string;\n}\n\n/**\n * Parameters for starting a new agent run.\n */\nexport interface StartRunParams {\n /** Customer ID this run belongs to */\n customerId: string;\n\n /** Workflow ID this run executes */\n workflowId: string;\n\n /** Your external run ID for correlation */\n externalRunId?: string;\n\n /** Correlation ID for distributed tracing */\n correlationId?: string;\n\n /** Parent run ID for nested runs */\n parentRunId?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of starting a run.\n */\nexport interface RunResult {\n id: string;\n customerId: string;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n correlationId: string | null;\n createdAt: string;\n}\n\n/**\n * Parameters for ending/updating a run.\n */\nexport interface EndRunParams {\n /** New status for the run */\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT';\n\n /** Error message if failed */\n errorMessage?: string;\n\n /** Error code for categorization */\n errorCode?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Possible run statuses.\n */\nexport type RunStatus =\n | 'PENDING'\n | 'RUNNING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'TIMEOUT';\n\n/**\n * Parameters for emitting an event to a run.\n */\nexport interface EmitEventParams {\n /** Run ID to attach this event to */\n runId: string;\n\n /** Event type (e.g., \"agent.step\", \"rpc.request\") */\n eventType: string;\n\n /** Quantity of units consumed */\n quantity?: number;\n\n /** Human-readable unit label */\n units?: string;\n\n /** Human-readable description */\n description?: string;\n\n /** Cost in abstract units */\n costUnits?: number;\n\n /** Currency for cost */\n costCurrency?: string;\n\n /** Correlation ID for tracing */\n correlationId?: string;\n\n /** Parent event ID for trace tree */\n parentEventId?: string;\n\n /** OpenTelemetry-style span ID */\n spanId?: string;\n\n /** Idempotency key (auto-generated if not provided) */\n idempotencyKey?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of emitting an event.\n */\nexport interface EventResult {\n id: string;\n runId: string;\n eventType: string;\n quantity: number;\n costUnits: number | null;\n isDuplicate: boolean;\n timestamp: string;\n}\n\n// ============================================================================\n// Meter Types\n// ============================================================================\n\n/**\n * A meter (usage type) from a pricing plan.\n */\nexport interface Meter {\n /** Pricing plan ID */\n id: string;\n\n /** Human-readable name */\n name: string;\n\n /** The meter/usage type identifier (use this in charge() calls) */\n meter: string;\n\n /** Price per unit in USD */\n unitPriceUsd: string;\n\n /** Whether this meter is active */\n isActive: boolean;\n}\n\n/**\n * Response from listing meters.\n */\nexport interface ListMetersResponse {\n /** Array of available meters */\n data: Meter[];\n\n /** Total count */\n count: number;\n}\n\n// ============================================================================\n// Cost Estimation Types\n// ============================================================================\n\n/**\n * Custom pricing map for cost estimation.\n * Maps usage type to unit price (e.g., { \"api_call\": \"0.005\", \"token\": \"0.0001\" })\n */\nexport type CustomPricing = Record<string, string>;\n\n/**\n * Parameters for estimating costs from historical usage events.\n */\nexport interface EstimateFromUsageParams {\n /** Filter to a specific customer (optional) */\n customerId?: string;\n\n /** Start of the period to estimate */\n periodStart: Date | string;\n\n /** End of the period to estimate */\n periodEnd: Date | string;\n\n /** Default price for usage types without pricing plans */\n defaultUnitPrice?: string;\n\n /** Include events that already have charges (default: true) */\n includeChargedEvents?: boolean;\n\n /** Filter to specific usage types */\n usageTypes?: string[];\n\n /** Custom pricing overrides (takes precedence over DB pricing) */\n customPricing?: CustomPricing;\n}\n\n/**\n * A usage item for hypothetical cost estimation.\n */\nexport interface HypotheticalUsageItem {\n /** The usage type (e.g., \"api_call\", \"token\") */\n usageType: string;\n\n /** The quantity of usage */\n quantity: number;\n\n /** Override unit price for this specific item */\n unitPriceOverride?: string;\n}\n\n/**\n * Parameters for estimating costs from hypothetical usage.\n */\nexport interface EstimateFromHypotheticalParams {\n /** List of usage items to estimate */\n items: HypotheticalUsageItem[];\n\n /** Default price for usage types without pricing plans */\n defaultUnitPrice?: string;\n\n /** Custom pricing overrides (takes precedence over DB pricing) */\n customPricing?: CustomPricing;\n}\n\n/**\n * A line item in the cost estimate.\n */\nexport interface CostEstimateLineItem {\n /** The usage type */\n usageType: string;\n\n /** Total quantity */\n quantity: string;\n\n /** Unit price used */\n unitPrice: string;\n\n /** Estimated cost in USDC */\n estimatedCostUsdc: string;\n\n /** Number of events (for usage-based estimates) */\n eventCount?: number;\n\n /** Whether a pricing plan was found for this usage type */\n hasPricingPlan: boolean;\n}\n\n/**\n * Response from cost estimation.\n */\nexport interface CostEstimateResponse {\n /** Business ID (optional - may not be returned by all endpoints) */\n businessId?: string;\n\n /** Customer ID (if filtered) */\n customerId?: string;\n\n /** Period start (for usage-based estimates) */\n periodStart?: string;\n\n /** Period end (for usage-based estimates) */\n periodEnd?: string;\n\n /** Breakdown by usage type */\n lineItems: CostEstimateLineItem[];\n\n /** Subtotal in USDC */\n subtotalUsdc: string;\n\n /** Total estimated cost in USDC */\n estimatedTotalUsdc: string;\n\n /** Currency (always USDC) */\n currency: 'USDC';\n\n /** Indicates this is an estimate, not a charge */\n isEstimate: true;\n\n /** When the estimate was generated */\n generatedAt: string;\n\n /** Notes about the estimate (e.g., missing pricing plans, custom pricing applied) */\n notes: string[];\n}\n\n// ============================================================================\n// Record Run Types (Simplified API)\n// ============================================================================\n\n/**\n * A single event to record in a run.\n */\nexport interface RecordRunEvent {\n /** Event type (e.g., \"agent.step\", \"tool.call\") */\n eventType: string;\n\n /** Quantity of units consumed */\n quantity?: number;\n\n /** Human-readable unit label */\n units?: string;\n\n /** Human-readable description */\n description?: string;\n\n /** Cost in abstract units */\n costUnits?: number;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for recording a complete run in one call.\n * This is the simplified API that combines workflow, run, and events.\n */\nexport interface RecordRunParams {\n /** Customer ID this run belongs to */\n customerId: string;\n\n /**\n * Workflow identifier. Can be:\n * - An existing workflow ID (e.g., \"wf_abc123\")\n * - A slug that will be auto-created if it doesn't exist (e.g., \"my_agent\")\n */\n workflow: string;\n\n /** Events that occurred during the run */\n events: RecordRunEvent[];\n\n /** Final status of the run */\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT';\n\n /** Error message if status is FAILED */\n errorMessage?: string;\n\n /** Error code if status is FAILED */\n errorCode?: string;\n\n /** Your external run ID for correlation */\n externalRunId?: string;\n\n /** Correlation ID for distributed tracing */\n correlationId?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of recording a run.\n */\nexport interface RecordRunResult {\n /** The created run */\n run: {\n id: string;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n durationMs: number | null;\n };\n\n /** Summary of events created */\n events: {\n created: number;\n duplicates: number;\n };\n\n /** Total cost computed */\n totalCostUnits: string | null;\n\n /** Human-readable summary */\n summary: string;\n}\n\n/**\n * Full run timeline response.\n */\nexport interface RunTimeline {\n run: {\n id: string;\n customerId: string;\n customerName: string | null;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n startedAt: string | null;\n endedAt: string | null;\n durationMs: number | null;\n errorMessage: string | null;\n errorCode: string | null;\n correlationId: string | null;\n metadata: Record<string, unknown> | null;\n };\n timeline: Array<{\n id: string;\n eventType: string;\n quantity: number;\n units: string | null;\n description: string | null;\n costUnits: number | null;\n timestamp: string;\n correlationId: string | null;\n parentEventId: string | null;\n charge: {\n id: string;\n amountUsdc: string;\n status: string;\n } | null;\n }>;\n totals: {\n eventCount: number;\n totalQuantity: string;\n totalCostUnits: string;\n totalChargedUsdc: string;\n };\n summary: string;\n}\n\n// ============================================================================\n// Wrap API Call Types\n// ============================================================================\n\n/**\n * Parameters for wrapping an external API call with usage tracking.\n * This ensures usage is recorded even if there's a crash/failure after the API call.\n */\nexport interface WrapApiCallParams<T> {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n */\n meter: string;\n\n /**\n * The async function that makes the external API call.\n * This is the call you want to track (e.g., OpenAI, Anthropic, etc.)\n */\n call: () => Promise<T>;\n\n /**\n * Function to extract the usage quantity from the API call result.\n * @example (result) => result.usage.total_tokens\n */\n extractUsage: (result: T) => number;\n\n /**\n * Custom idempotency key prefix.\n * If not provided, a unique key is generated.\n * The key ensures retries don't double-charge.\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Retry configuration for the Drip charge call.\n * The external API call is NOT retried (only called once).\n */\n retryOptions?: RetryOptions;\n}\n\n/**\n * Result of a wrapped API call.\n */\nexport interface WrapApiCallResult<T> {\n /**\n * The result from the external API call.\n */\n result: T;\n\n /**\n * The charge result from Drip.\n */\n charge: ChargeResult;\n\n /**\n * The idempotency key used (useful for debugging).\n */\n idempotencyKey: string;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * Error thrown by Drip SDK operations.\n */\nexport class DripError extends Error {\n /**\n * Creates a new DripError.\n * @param message - Human-readable error message\n * @param statusCode - HTTP status code from the API\n * @param code - Machine-readable error code\n */\n constructor(\n message: string,\n public statusCode: number,\n public code?: string,\n ) {\n super(message);\n this.name = 'DripError';\n Object.setPrototypeOf(this, DripError.prototype);\n }\n}\n\n// ============================================================================\n// Main SDK Class\n// ============================================================================\n\n/**\n * The main Drip SDK client.\n *\n * @example\n * ```typescript\n * import { Drip } from '@drip-sdk/node';\n *\n * const drip = new Drip({\n * apiKey: process.env.DRIP_API_KEY!,\n * });\n *\n * // Create a customer\n * const customer = await drip.createCustomer({\n * onchainAddress: '0x...',\n * externalCustomerId: 'user_123',\n * });\n *\n * // Record usage and charge\n * const result = await drip.charge({\n * customerId: customer.id,\n * meter: 'api_calls',\n * quantity: 100,\n * });\n *\n * console.log(`Charged ${result.charge.amountUsdc} USDC`);\n * ```\n */\nexport class Drip {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly resilience: ResilienceManager | null;\n\n /**\n * Creates a new Drip SDK client.\n *\n * @param config - Configuration options\n * @throws {Error} If apiKey is not provided\n *\n * @example\n * ```typescript\n * // Basic usage\n * const drip = new Drip({\n * apiKey: 'drip_live_abc123...',\n * });\n *\n * // With production resilience (recommended)\n * const drip = new Drip({\n * apiKey: 'drip_live_abc123...',\n * resilience: true,\n * });\n *\n * // High throughput mode\n * const drip = new Drip({\n * apiKey: 'drip_live_abc123...',\n * resilience: 'high-throughput',\n * });\n * ```\n */\n constructor(config: DripConfig) {\n if (!config.apiKey) {\n throw new Error('Drip API key is required');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.drip.dev/v1';\n this.timeout = config.timeout || 30000;\n\n // Setup resilience manager\n if (config.resilience === true) {\n this.resilience = new ResilienceManager(createDefaultResilienceConfig());\n } else if (config.resilience === 'high-throughput') {\n this.resilience = new ResilienceManager(createHighThroughputResilienceConfig());\n } else if (config.resilience && typeof config.resilience === 'object') {\n this.resilience = new ResilienceManager(config.resilience);\n } else {\n this.resilience = null;\n }\n }\n\n /**\n * Makes an authenticated request to the Drip API.\n * @internal\n */\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n // Extract method for metrics\n const method = (options.method ?? 'GET').toUpperCase();\n\n // Use resilience manager if enabled\n if (this.resilience) {\n return this.resilience.execute(\n () => this.rawRequest<T>(path, options),\n method,\n path\n );\n }\n\n return this.rawRequest<T>(path, options);\n }\n\n /**\n * Execute the actual HTTP request (internal).\n * @internal\n */\n private async rawRequest<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n // Handle 204 No Content\n if (res.status === 204) {\n return { success: true } as T;\n }\n\n const data = await res.json();\n\n if (!res.ok) {\n throw new DripError(\n data.message || data.error || 'Request failed',\n res.status,\n data.code,\n );\n }\n\n return data as T;\n } catch (error) {\n if (error instanceof DripError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new DripError('Request timed out', 408, 'TIMEOUT');\n }\n throw new DripError(\n error instanceof Error ? error.message : 'Unknown error',\n 0,\n 'UNKNOWN',\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // ==========================================================================\n // Health Check Methods\n // ==========================================================================\n\n /**\n * Pings the Drip API to check connectivity and measure latency.\n *\n * @returns Health status with latency information\n * @throws {DripError} If the request fails or times out\n *\n * @example\n * ```typescript\n * const health = await drip.ping();\n * if (health.ok) {\n * console.log(`API healthy, latency: ${health.latencyMs}ms`);\n * }\n * ```\n */\n async ping(): Promise<{ ok: boolean; status: string; latencyMs: number; timestamp: number }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Safely construct health endpoint URL\n let healthBaseUrl = this.baseUrl;\n if (healthBaseUrl.endsWith('/v1/')) {\n healthBaseUrl = healthBaseUrl.slice(0, -4);\n } else if (healthBaseUrl.endsWith('/v1')) {\n healthBaseUrl = healthBaseUrl.slice(0, -3);\n }\n healthBaseUrl = healthBaseUrl.replace(/\\/+$/, '');\n\n const start = Date.now();\n\n try {\n const response = await fetch(`${healthBaseUrl}/health`, {\n signal: controller.signal,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n const latencyMs = Date.now() - start;\n\n // Try to parse JSON, but handle non-JSON responses gracefully\n let status = 'unknown';\n let timestamp = Date.now();\n\n try {\n const data = await response.json() as { status?: string; timestamp?: number };\n if (typeof data.status === 'string') {\n status = data.status;\n }\n if (typeof data.timestamp === 'number') {\n timestamp = data.timestamp;\n }\n } catch {\n // Non-JSON response, derive status from HTTP code\n status = response.ok ? 'healthy' : `error:${response.status}`;\n }\n\n // For non-OK HTTP responses, set appropriate status\n if (!response.ok && status === 'unknown') {\n status = `error:${response.status}`;\n }\n\n return {\n ok: response.ok && status === 'healthy',\n status,\n latencyMs,\n timestamp,\n };\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new DripError('Request timed out', 408, 'TIMEOUT');\n }\n throw new DripError(\n error instanceof Error ? error.message : 'Unknown error',\n 0,\n 'UNKNOWN',\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // ==========================================================================\n // Resilience Methods\n // ==========================================================================\n\n /**\n * Get SDK metrics (requires resilience to be enabled).\n *\n * Returns aggregated metrics including success rates, latencies, and errors.\n *\n * @returns Metrics summary or null if resilience is not enabled\n *\n * @example\n * ```typescript\n * const drip = new Drip({ apiKey: '...', resilience: true });\n * // ... make some requests ...\n *\n * const metrics = drip.getMetrics();\n * if (metrics) {\n * console.log(`Success rate: ${metrics.successRate.toFixed(1)}%`);\n * console.log(`P95 latency: ${metrics.p95LatencyMs.toFixed(0)}ms`);\n * }\n * ```\n */\n getMetrics(): MetricsSummary | null {\n return this.resilience?.getMetrics() ?? null;\n }\n\n /**\n * Get SDK health status (requires resilience to be enabled).\n *\n * Returns health status including circuit breaker state and rate limiter status.\n *\n * @returns Health status or null if resilience is not enabled\n *\n * @example\n * ```typescript\n * const drip = new Drip({ apiKey: '...', resilience: true });\n *\n * const health = drip.getHealth();\n * if (health) {\n * console.log(`Circuit: ${health.circuitBreaker.state}`);\n * console.log(`Available tokens: ${health.rateLimiter.availableTokens}`);\n * }\n * ```\n */\n getHealth(): ResilienceHealth | null {\n return this.resilience?.getHealth() ?? null;\n }\n\n // ==========================================================================\n // Customer Methods\n // ==========================================================================\n\n /**\n * Creates a new customer in your Drip account.\n *\n * @param params - Customer creation parameters\n * @returns The created customer\n * @throws {DripError} If creation fails (e.g., duplicate customer)\n *\n * @example\n * ```typescript\n * const customer = await drip.createCustomer({\n * onchainAddress: '0x1234567890abcdef...',\n * externalCustomerId: 'user_123',\n * metadata: { plan: 'pro' },\n * });\n * ```\n */\n async createCustomer(params: CreateCustomerParams): Promise<Customer> {\n return this.request<Customer>('/customers', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Retrieves a customer by their Drip ID.\n *\n * @param customerId - The Drip customer ID\n * @returns The customer details\n * @throws {DripError} If customer not found (404)\n *\n * @example\n * ```typescript\n * const customer = await drip.getCustomer('cust_abc123');\n * console.log(customer.onchainAddress);\n * ```\n */\n async getCustomer(customerId: string): Promise<Customer> {\n return this.request<Customer>(`/customers/${customerId}`);\n }\n\n /**\n * Lists all customers for your business.\n *\n * @param options - Optional filtering and pagination\n * @returns List of customers\n *\n * @example\n * ```typescript\n * // List all customers\n * const { data: customers } = await drip.listCustomers();\n *\n * // List with filters\n * const { data: activeCustomers } = await drip.listCustomers({\n * status: 'ACTIVE',\n * limit: 50,\n * });\n * ```\n */\n async listCustomers(\n options?: ListCustomersOptions,\n ): Promise<ListCustomersResponse> {\n const params = new URLSearchParams();\n\n if (options?.limit) {\n params.set('limit', options.limit.toString());\n }\n if (options?.status) {\n params.set('status', options.status);\n }\n\n const query = params.toString();\n const path = query ? `/customers?${query}` : '/customers';\n\n return this.request<ListCustomersResponse>(path);\n }\n\n /**\n * Gets the current balance for a customer.\n *\n * @param customerId - The Drip customer ID\n * @returns Current balance in USDC and native token\n *\n * @example\n * ```typescript\n * const balance = await drip.getBalance('cust_abc123');\n * console.log(`Balance: ${balance.balanceUSDC} USDC`);\n * ```\n */\n async getBalance(customerId: string): Promise<BalanceResult> {\n return this.request<BalanceResult>(`/customers/${customerId}/balance`);\n }\n\n // ==========================================================================\n // Charge Methods\n // ==========================================================================\n\n /**\n * Records usage and charges a customer.\n *\n * This is the primary method for billing customers. It:\n * 1. Records the usage event\n * 2. Calculates the charge based on your pricing plan\n * 3. Executes the on-chain charge\n *\n * @param params - Charge parameters\n * @returns The charge result\n * @throws {DripError} If charge fails (insufficient balance, invalid customer, etc.)\n *\n * @example\n * ```typescript\n * const result = await drip.charge({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * quantity: 100,\n * idempotencyKey: 'req_unique_123',\n * });\n *\n * if (result.success) {\n * console.log(`Charged ${result.charge.amountUsdc} USDC`);\n * console.log(`TX: ${result.charge.txHash}`);\n * }\n * ```\n */\n async charge(params: ChargeParams): Promise<ChargeResult> {\n return this.request<ChargeResult>('/usage', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n usageType: params.meter,\n quantity: params.quantity,\n idempotencyKey: params.idempotencyKey,\n metadata: params.metadata,\n }),\n });\n }\n\n /**\n * Wraps an external API call with guaranteed usage recording.\n *\n * **This solves the crash-before-record problem:**\n * ```typescript\n * // DANGEROUS - usage lost if crash between lines 1 and 2:\n * const response = await openai.chat.completions.create({...}); // line 1\n * await drip.charge({ tokens: response.usage.total_tokens }); // line 2\n *\n * // SAFE - wrapApiCall guarantees recording with retry:\n * const { result } = await drip.wrapApiCall({\n * call: () => openai.chat.completions.create({...}),\n * extractUsage: (r) => r.usage.total_tokens,\n * ...\n * });\n * ```\n *\n * How it works:\n * 1. Generates idempotency key BEFORE the API call\n * 2. Makes the external API call (once, no retry)\n * 3. Records usage in Drip with retry + idempotency\n * 4. If recording fails transiently, retries are safe (no double-charge)\n *\n * @param params - Wrap parameters including the call and usage extractor\n * @returns The API result and charge details\n * @throws {DripError} If the Drip charge fails after retries\n * @throws {Error} If the external API call fails\n *\n * @example\n * ```typescript\n * // OpenAI example\n * const { result, charge } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * call: () => openai.chat.completions.create({\n * model: 'gpt-4',\n * messages: [{ role: 'user', content: 'Hello!' }],\n * }),\n * extractUsage: (r) => r.usage?.total_tokens ?? 0,\n * });\n *\n * console.log(result.choices[0].message.content);\n * console.log(`Charged: ${charge.charge.amountUsdc} USDC`);\n * ```\n *\n * @example\n * ```typescript\n * // Anthropic example\n * const { result, charge } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * call: () => anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * }),\n * extractUsage: (r) => r.usage.input_tokens + r.usage.output_tokens,\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With custom retry options\n * const { result } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * call: () => fetch('https://api.example.com/expensive'),\n * extractUsage: () => 1, // Fixed cost per call\n * retryOptions: {\n * maxAttempts: 5,\n * baseDelayMs: 200,\n * },\n * });\n * ```\n */\n async wrapApiCall<T>(params: WrapApiCallParams<T>): Promise<WrapApiCallResult<T>> {\n // Generate idempotency key BEFORE the call - this is the key insight!\n // Even if we crash after the API call, retrying with the same key is safe.\n const idempotencyKey = params.idempotencyKey\n ?? `wrap_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;\n\n // Step 1: Make the external API call (no retry - we don't control this)\n const result = await params.call();\n\n // Step 2: Extract usage from the result\n const quantity = params.extractUsage(result);\n\n // Step 3: Record usage in Drip with retry (idempotency makes this safe)\n const charge = await retryWithBackoff(\n () =>\n this.charge({\n customerId: params.customerId,\n meter: params.meter,\n quantity,\n idempotencyKey,\n metadata: params.metadata,\n }),\n params.retryOptions,\n );\n\n return {\n result,\n charge,\n idempotencyKey,\n };\n }\n\n /**\n * Records usage for internal visibility WITHOUT billing.\n *\n * Use this for:\n * - Tracking internal team usage without charging\n * - Pilot programs where you want visibility before billing\n * - Pre-billing tracking before customer has on-chain wallet\n *\n * This does NOT:\n * - Create a Charge record\n * - Require customer balance\n * - Require blockchain/wallet setup\n *\n * For billing, use `charge()` instead.\n *\n * @param params - The usage tracking parameters\n * @returns The tracked usage event\n *\n * @example\n * ```typescript\n * const result = await drip.trackUsage({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * quantity: 100,\n * description: 'API calls during trial period',\n * });\n *\n * console.log(`Tracked: ${result.usageEventId}`);\n * ```\n */\n async trackUsage(params: TrackUsageParams): Promise<TrackUsageResult> {\n return this.request<TrackUsageResult>('/usage/internal', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n usageType: params.meter,\n quantity: params.quantity,\n idempotencyKey: params.idempotencyKey,\n units: params.units,\n description: params.description,\n metadata: params.metadata,\n }),\n });\n }\n\n /**\n * Retrieves a specific charge by ID.\n *\n * @param chargeId - The charge ID\n * @returns The charge details\n * @throws {DripError} If charge not found (404)\n *\n * @example\n * ```typescript\n * const charge = await drip.getCharge('chg_abc123');\n * console.log(`Status: ${charge.status}`);\n * ```\n */\n async getCharge(chargeId: string): Promise<Charge> {\n return this.request<Charge>(`/charges/${chargeId}`);\n }\n\n /**\n * Lists charges for your business.\n *\n * @param options - Optional filtering and pagination\n * @returns List of charges\n *\n * @example\n * ```typescript\n * // List all charges\n * const { data: charges } = await drip.listCharges();\n *\n * // List charges for a specific customer\n * const { data: customerCharges } = await drip.listCharges({\n * customerId: 'cust_abc123',\n * status: 'CONFIRMED',\n * });\n * ```\n */\n async listCharges(options?: ListChargesOptions): Promise<ListChargesResponse> {\n const params = new URLSearchParams();\n\n if (options?.customerId) {\n params.set('customerId', options.customerId);\n }\n if (options?.status) {\n params.set('status', options.status);\n }\n if (options?.limit) {\n params.set('limit', options.limit.toString());\n }\n if (options?.offset) {\n params.set('offset', options.offset.toString());\n }\n\n const query = params.toString();\n const path = query ? `/charges?${query}` : '/charges';\n\n return this.request<ListChargesResponse>(path);\n }\n\n /**\n * Gets the current status of a charge.\n *\n * Useful for polling charge status after async operations.\n *\n * @param chargeId - The charge ID\n * @returns Current charge status\n *\n * @example\n * ```typescript\n * const status = await drip.getChargeStatus('chg_abc123');\n * if (status.status === 'CONFIRMED') {\n * console.log('Charge confirmed!');\n * }\n * ```\n */\n async getChargeStatus(\n chargeId: string,\n ): Promise<{ status: ChargeStatus; txHash?: string }> {\n return this.request<{ status: ChargeStatus; txHash?: string }>(\n `/charges/${chargeId}/status`,\n );\n }\n\n // ==========================================================================\n // Checkout Methods (Fiat On-Ramp)\n // ==========================================================================\n\n /**\n * Creates a checkout session to add funds to a customer's account.\n *\n * This is the PRIMARY method for getting money into Drip. It returns a URL\n * to a hosted checkout page where customers can pay via:\n * - Bank transfer (ACH) - $0.50 flat fee, 1-2 business days\n * - Debit card - 1.5% fee, instant\n * - Direct USDC - no fee, instant\n *\n * After payment, the customer is redirected to your returnUrl with:\n * - session_id: The checkout session ID\n * - customer_id: The Drip customer ID\n * - status: \"success\" or \"failed\"\n *\n * @param params - Checkout parameters\n * @returns Checkout session with redirect URL\n *\n * @example\n * ```typescript\n * // Basic checkout\n * const { url } = await drip.checkout({\n * customerId: 'cust_abc123',\n * amount: 5000, // $50.00\n * returnUrl: 'https://myapp.com/dashboard',\n * });\n *\n * // Redirect user to checkout\n * res.redirect(url);\n * ```\n *\n * @example\n * ```typescript\n * // Checkout for new customer\n * const { url, id } = await drip.checkout({\n * externalCustomerId: 'user_123', // Your user ID\n * amount: 10000, // $100.00\n * returnUrl: 'https://myapp.com/welcome',\n * metadata: { plan: 'pro' },\n * });\n * ```\n */\n async checkout(params: CheckoutParams): Promise<CheckoutResult> {\n const response = await this.request<{\n id: string;\n url: string;\n expires_at: string;\n amount_usd: number;\n }>('/checkout', {\n method: 'POST',\n body: JSON.stringify({\n customer_id: params.customerId,\n external_customer_id: params.externalCustomerId,\n amount: params.amount,\n return_url: params.returnUrl,\n cancel_url: params.cancelUrl,\n metadata: params.metadata,\n }),\n });\n\n return {\n id: response.id,\n url: response.url,\n expiresAt: response.expires_at,\n amountUsd: response.amount_usd,\n };\n }\n\n // ==========================================================================\n // Webhook Methods\n // ==========================================================================\n\n /**\n * Creates a new webhook endpoint.\n *\n * The webhook secret is only returned once at creation time.\n * Store it securely for verifying webhook signatures.\n *\n * @param config - Webhook configuration\n * @returns The created webhook with its secret\n *\n * @example\n * ```typescript\n * const webhook = await drip.createWebhook({\n * url: 'https://api.yourapp.com/webhooks/drip',\n * events: ['charge.succeeded', 'charge.failed'],\n * description: 'Main webhook endpoint',\n * });\n *\n * // IMPORTANT: Save this secret securely!\n * console.log(`Webhook secret: ${webhook.secret}`);\n * ```\n */\n async createWebhook(\n config: CreateWebhookParams,\n ): Promise<CreateWebhookResponse> {\n return this.request<CreateWebhookResponse>('/webhooks', {\n method: 'POST',\n body: JSON.stringify(config),\n });\n }\n\n /**\n * Lists all webhook endpoints for your business.\n *\n * @returns List of webhooks with delivery statistics\n *\n * @example\n * ```typescript\n * const { data: webhooks } = await drip.listWebhooks();\n * webhooks.forEach(wh => {\n * console.log(`${wh.url}: ${wh.stats?.successfulDeliveries} successful`);\n * });\n * ```\n */\n async listWebhooks(): Promise<ListWebhooksResponse> {\n return this.request<ListWebhooksResponse>('/webhooks');\n }\n\n /**\n * Retrieves a specific webhook by ID.\n *\n * @param webhookId - The webhook ID\n * @returns The webhook details with statistics\n * @throws {DripError} If webhook not found (404)\n *\n * @example\n * ```typescript\n * const webhook = await drip.getWebhook('wh_abc123');\n * console.log(`Events: ${webhook.events.join(', ')}`);\n * ```\n */\n async getWebhook(webhookId: string): Promise<Webhook> {\n return this.request<Webhook>(`/webhooks/${webhookId}`);\n }\n\n /**\n * Deletes a webhook endpoint.\n *\n * @param webhookId - The webhook ID to delete\n * @returns Success confirmation\n * @throws {DripError} If webhook not found (404)\n *\n * @example\n * ```typescript\n * await drip.deleteWebhook('wh_abc123');\n * console.log('Webhook deleted');\n * ```\n */\n async deleteWebhook(webhookId: string): Promise<DeleteWebhookResponse> {\n return this.request<DeleteWebhookResponse>(`/webhooks/${webhookId}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Tests a webhook by sending a test event.\n *\n * @param webhookId - The webhook ID to test\n * @returns Test result\n *\n * @example\n * ```typescript\n * const result = await drip.testWebhook('wh_abc123');\n * console.log(`Test status: ${result.status}`);\n * ```\n */\n async testWebhook(\n webhookId: string,\n ): Promise<{ message: string; deliveryId: string | null; status: string }> {\n return this.request<{\n message: string;\n deliveryId: string | null;\n status: string;\n }>(`/webhooks/${webhookId}/test`, {\n method: 'POST',\n });\n }\n\n /**\n * Rotates the signing secret for a webhook.\n *\n * After rotation, update your application to use the new secret.\n *\n * @param webhookId - The webhook ID\n * @returns The new secret\n *\n * @example\n * ```typescript\n * const { secret } = await drip.rotateWebhookSecret('wh_abc123');\n * console.log(`New secret: ${secret}`);\n * // Update your application with the new secret!\n * ```\n */\n async rotateWebhookSecret(\n webhookId: string,\n ): Promise<{ secret: string; message: string }> {\n return this.request<{ secret: string; message: string }>(\n `/webhooks/${webhookId}/rotate-secret`,\n { method: 'POST' },\n );\n }\n\n // ==========================================================================\n // Run & Event Methods (Execution Ledger)\n // ==========================================================================\n\n /**\n * Creates a new workflow definition.\n *\n * @param params - Workflow creation parameters\n * @returns The created workflow\n *\n * @example\n * ```typescript\n * const workflow = await drip.createWorkflow({\n * name: 'Prescription Intake',\n * slug: 'prescription_intake',\n * productSurface: 'AGENT',\n * });\n * ```\n */\n async createWorkflow(params: CreateWorkflowParams): Promise<Workflow> {\n return this.request<Workflow>('/workflows', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Lists all workflows for your business.\n *\n * @returns List of workflows\n */\n async listWorkflows(): Promise<{ data: Workflow[]; count: number }> {\n return this.request<{ data: Workflow[]; count: number }>('/workflows');\n }\n\n /**\n * Starts a new agent run for tracking execution.\n *\n * @param params - Run parameters\n * @returns The started run\n *\n * @example\n * ```typescript\n * const run = await drip.startRun({\n * customerId: 'cust_abc123',\n * workflowId: 'wf_xyz789',\n * correlationId: 'req_unique_123',\n * });\n *\n * // Emit events during execution...\n *\n * await drip.endRun(run.id, { status: 'COMPLETED' });\n * ```\n */\n async startRun(params: StartRunParams): Promise<RunResult> {\n return this.request<RunResult>('/runs', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Ends a run with a final status.\n *\n * @param runId - The run ID to end\n * @param params - End parameters including status\n * @returns Updated run info\n *\n * @example\n * ```typescript\n * await drip.endRun(run.id, {\n * status: 'COMPLETED',\n * });\n *\n * // Or with error:\n * await drip.endRun(run.id, {\n * status: 'FAILED',\n * errorMessage: 'Customer validation failed',\n * errorCode: 'VALIDATION_ERROR',\n * });\n * ```\n */\n async endRun(\n runId: string,\n params: EndRunParams,\n ): Promise<{\n id: string;\n status: RunStatus;\n endedAt: string | null;\n durationMs: number | null;\n eventCount: number;\n totalCostUnits: string | null;\n }> {\n return this.request(`/runs/${runId}`, {\n method: 'PATCH',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Gets a run's full timeline with events and computed totals.\n *\n * This is the key endpoint for debugging \"what happened\" in an execution.\n *\n * @param runId - The run ID\n * @returns Full timeline with events and summary\n *\n * @example\n * ```typescript\n * const { run, timeline, totals, summary } = await drip.getRunTimeline('run_abc123');\n *\n * console.log(`Status: ${run.status}`);\n * console.log(`Summary: ${summary}`);\n * console.log(`Total cost: ${totals.totalCostUnits}`);\n *\n * for (const event of timeline) {\n * console.log(`${event.eventType}: ${event.quantity} ${event.units}`);\n * }\n * ```\n */\n async getRunTimeline(runId: string): Promise<RunTimeline> {\n return this.request<RunTimeline>(`/runs/${runId}`);\n }\n\n /**\n * Emits an event to a run.\n *\n * Events can be stored idempotently when an `idempotencyKey` is provided.\n * Use `Drip.generateIdempotencyKey()` for deterministic key generation.\n * If `idempotencyKey` is omitted, repeated calls may create duplicate events.\n *\n * @param params - Event parameters\n * @returns The created event\n *\n * @example\n * ```typescript\n * await drip.emitEvent({\n * runId: run.id,\n * eventType: 'agent.validate',\n * quantity: 1,\n * description: 'Validated prescription format',\n * costUnits: 0.001,\n * });\n * ```\n */\n async emitEvent(params: EmitEventParams): Promise<EventResult> {\n return this.request<EventResult>('/events', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Emits multiple events in a single request.\n *\n * @param events - Array of events to emit\n * @returns Summary of created events\n *\n * @example\n * ```typescript\n * const result = await drip.emitEventsBatch([\n * { runId: run.id, eventType: 'agent.step1', quantity: 1 },\n * { runId: run.id, eventType: 'agent.step2', quantity: 100, units: 'tokens' },\n * ]);\n *\n * console.log(`Created: ${result.created}, Duplicates: ${result.duplicates}`);\n * ```\n */\n async emitEventsBatch(\n events: Array<Omit<EmitEventParams, 'runId'> & {\n runId?: string;\n customerId?: string;\n workflowId?: string;\n }>,\n ): Promise<{\n success: boolean;\n created: number;\n duplicates: number;\n events: Array<{ id: string; eventType: string; isDuplicate: boolean }>;\n }> {\n return this.request('/run-events/batch', {\n method: 'POST',\n body: JSON.stringify({ events }),\n });\n }\n\n // ==========================================================================\n // Simplified API Methods\n // ==========================================================================\n\n /**\n * Lists all available meters (usage types) for your business.\n *\n * Use this to discover what meter names are valid for the `charge()` method.\n * Meters are defined by your pricing plans.\n *\n * @returns List of available meters with their prices\n *\n * @example\n * ```typescript\n * const { data: meters } = await drip.listMeters();\n *\n * console.log('Available meters:');\n * for (const meter of meters) {\n * console.log(` ${meter.meter}: $${meter.unitPriceUsd}/unit`);\n * }\n *\n * // Use in charge():\n * await drip.charge({\n * customerId: 'cust_123',\n * meter: meters[0].meter, // Use a valid meter name\n * quantity: 100,\n * });\n * ```\n */\n async listMeters(): Promise<ListMetersResponse> {\n const response = await this.request<{\n data: Array<{\n id: string;\n name: string;\n unitType: string;\n unitPriceUsd: string;\n isActive: boolean;\n }>;\n count: number;\n }>('/pricing-plans');\n\n return {\n data: response.data.map((plan) => ({\n id: plan.id,\n name: plan.name,\n meter: plan.unitType,\n unitPriceUsd: plan.unitPriceUsd,\n isActive: plan.isActive,\n })),\n count: response.count,\n };\n }\n\n // ==========================================================================\n // Cost Estimation Methods\n // ==========================================================================\n\n /**\n * Estimates costs from historical usage events.\n *\n * Use this to preview what existing usage would cost before creating charges,\n * or to run \"what-if\" scenarios with custom pricing.\n *\n * @param params - Parameters for the estimate\n * @returns Cost estimate with line item breakdown\n *\n * @example\n * ```typescript\n * // Estimate costs for last month's usage\n * const estimate = await drip.estimateFromUsage({\n * periodStart: new Date('2024-01-01'),\n * periodEnd: new Date('2024-01-31'),\n * });\n *\n * console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);\n * ```\n *\n * @example\n * ```typescript\n * // \"What-if\" scenario with custom pricing\n * const estimate = await drip.estimateFromUsage({\n * periodStart: new Date('2024-01-01'),\n * periodEnd: new Date('2024-01-31'),\n * customPricing: {\n * 'api_call': '0.005', // What if we charged $0.005 per call?\n * 'token': '0.0001', // What if we charged $0.0001 per token?\n * },\n * });\n * ```\n */\n async estimateFromUsage(params: EstimateFromUsageParams): Promise<CostEstimateResponse> {\n const periodStart = params.periodStart instanceof Date\n ? params.periodStart.toISOString()\n : params.periodStart;\n const periodEnd = params.periodEnd instanceof Date\n ? params.periodEnd.toISOString()\n : params.periodEnd;\n\n return this.request<CostEstimateResponse>('/dashboard/cost-estimate/from-usage', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n periodStart,\n periodEnd,\n defaultUnitPrice: params.defaultUnitPrice,\n includeChargedEvents: params.includeChargedEvents,\n usageTypes: params.usageTypes,\n customPricing: params.customPricing,\n }),\n });\n }\n\n /**\n * Estimates costs from hypothetical usage.\n *\n * Use this for \"what-if\" scenarios, budget planning, or to preview\n * costs before usage occurs.\n *\n * @param params - Parameters for the estimate\n * @returns Cost estimate with line item breakdown\n *\n * @example\n * ```typescript\n * // Estimate what 10,000 API calls and 1M tokens would cost\n * const estimate = await drip.estimateFromHypothetical({\n * items: [\n * { usageType: 'api_call', quantity: 10000 },\n * { usageType: 'token', quantity: 1000000 },\n * ],\n * });\n *\n * console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);\n * for (const item of estimate.lineItems) {\n * console.log(` ${item.usageType}: ${item.quantity} × $${item.unitPrice} = $${item.estimatedCostUsdc}`);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Compare different pricing scenarios\n * const currentPricing = await drip.estimateFromHypothetical({\n * items: [{ usageType: 'api_call', quantity: 100000 }],\n * });\n *\n * const newPricing = await drip.estimateFromHypothetical({\n * items: [{ usageType: 'api_call', quantity: 100000 }],\n * customPricing: { 'api_call': '0.0005' }, // 50% discount\n * });\n *\n * console.log(`Current: $${currentPricing.estimatedTotalUsdc}`);\n * console.log(`With 50% discount: $${newPricing.estimatedTotalUsdc}`);\n * ```\n */\n async estimateFromHypothetical(params: EstimateFromHypotheticalParams): Promise<CostEstimateResponse> {\n return this.request<CostEstimateResponse>('/dashboard/cost-estimate/hypothetical', {\n method: 'POST',\n body: JSON.stringify({\n items: params.items,\n defaultUnitPrice: params.defaultUnitPrice,\n customPricing: params.customPricing,\n }),\n });\n }\n\n /**\n * Records a complete agent run in a single call.\n *\n * This is the **simplified API** that combines:\n * - Workflow creation (if needed)\n * - Run creation\n * - Event emission\n * - Run completion\n *\n * Use this instead of the individual `startRun()`, `emitEvent()`, `endRun()` calls\n * when you have all the run data available at once.\n *\n * @param params - Run parameters including events\n * @returns The created run with event summary\n *\n * @example\n * ```typescript\n * // Record a complete agent run in one call\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'prescription_intake', // Auto-creates if doesn't exist\n * events: [\n * { eventType: 'agent.start', description: 'Started processing' },\n * { eventType: 'tool.ocr', quantity: 3, units: 'pages', costUnits: 0.15 },\n * { eventType: 'tool.validate', quantity: 1, costUnits: 0.05 },\n * { eventType: 'agent.complete', description: 'Finished successfully' },\n * ],\n * status: 'COMPLETED',\n * });\n *\n * console.log(`Run ${result.run.id}: ${result.summary}`);\n * console.log(`Events: ${result.events.created} created`);\n * ```\n *\n * @example\n * ```typescript\n * // Record a failed run with error details\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'prescription_intake',\n * events: [\n * { eventType: 'agent.start', description: 'Started processing' },\n * { eventType: 'tool.ocr', quantity: 1, units: 'pages' },\n * { eventType: 'error', description: 'OCR failed: image too blurry' },\n * ],\n * status: 'FAILED',\n * errorMessage: 'OCR processing failed',\n * errorCode: 'OCR_QUALITY_ERROR',\n * });\n * ```\n */\n async recordRun(params: RecordRunParams): Promise<RecordRunResult> {\n const startTime = Date.now();\n\n // Step 1: Ensure workflow exists (get or create)\n let workflowId = params.workflow;\n let workflowName = params.workflow;\n\n // If it looks like a slug (no underscore prefix), try to find/create it\n if (!params.workflow.startsWith('wf_')) {\n try {\n // Try to find existing workflow by slug\n const workflows = await this.listWorkflows();\n const existing = workflows.data.find(\n (w) => w.slug === params.workflow || w.id === params.workflow,\n );\n\n if (existing) {\n workflowId = existing.id;\n workflowName = existing.name;\n } else {\n // Create new workflow with the slug\n const created = await this.createWorkflow({\n name: params.workflow.replace(/[_-]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()),\n slug: params.workflow,\n productSurface: 'AGENT',\n });\n workflowId = created.id;\n workflowName = created.name;\n }\n } catch {\n // If lookup fails, assume it's an ID\n workflowId = params.workflow;\n }\n }\n\n // Step 2: Create the run\n const run = await this.startRun({\n customerId: params.customerId,\n workflowId,\n externalRunId: params.externalRunId,\n correlationId: params.correlationId,\n metadata: params.metadata,\n });\n\n // Step 3: Emit all events in batch\n let eventsCreated = 0;\n let eventsDuplicates = 0;\n\n if (params.events.length > 0) {\n const batchEvents = params.events.map((event, index) => ({\n runId: run.id,\n eventType: event.eventType,\n quantity: event.quantity,\n units: event.units,\n description: event.description,\n costUnits: event.costUnits,\n metadata: event.metadata,\n idempotencyKey: params.externalRunId\n ? `${params.externalRunId}:${event.eventType}:${index}`\n : undefined,\n }));\n\n const batchResult = await this.emitEventsBatch(batchEvents);\n eventsCreated = batchResult.created;\n eventsDuplicates = batchResult.duplicates;\n }\n\n // Step 4: End the run\n const endResult = await this.endRun(run.id, {\n status: params.status,\n errorMessage: params.errorMessage,\n errorCode: params.errorCode,\n });\n\n const durationMs = Date.now() - startTime;\n\n // Build summary\n const eventSummary = params.events.length > 0\n ? `${eventsCreated} events recorded`\n : 'no events';\n const statusEmoji = params.status === 'COMPLETED' ? '✓' : params.status === 'FAILED' ? '✗' : '○';\n const summary = `${statusEmoji} ${workflowName}: ${eventSummary} (${endResult.durationMs ?? durationMs}ms)`;\n\n return {\n run: {\n id: run.id,\n workflowId,\n workflowName,\n status: params.status,\n durationMs: endResult.durationMs,\n },\n events: {\n created: eventsCreated,\n duplicates: eventsDuplicates,\n },\n totalCostUnits: endResult.totalCostUnits,\n summary,\n };\n }\n\n /**\n * Generates a deterministic idempotency key.\n *\n * Use this to ensure \"one logical action = one event\" even with retries.\n * The key is generated from customerId + runId + stepName + sequence.\n *\n * @param params - Key generation parameters\n * @returns A deterministic idempotency key\n *\n * @example\n * ```typescript\n * const key = Drip.generateIdempotencyKey({\n * customerId: 'cust_123',\n * runId: 'run_456',\n * stepName: 'validate_prescription',\n * sequence: 1,\n * });\n *\n * await drip.emitEvent({\n * runId: 'run_456',\n * eventType: 'agent.validate',\n * idempotencyKey: key,\n * });\n * ```\n */\n static generateIdempotencyKey(params: {\n customerId: string;\n runId?: string;\n stepName: string;\n sequence?: number;\n }): string {\n const components = [\n params.customerId,\n params.runId ?? 'no_run',\n params.stepName,\n String(params.sequence ?? 0),\n ];\n\n // Simple hash function for deterministic key generation\n let hash = 0;\n const str = components.join('|');\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n\n return `drip_${Math.abs(hash).toString(36)}_${params.stepName.slice(0, 16)}`;\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Verifies a webhook signature using HMAC-SHA256.\n *\n * Call this when receiving webhook events to ensure they're authentic.\n * This is an async method that uses the Web Crypto API for secure verification.\n *\n * @param payload - The raw request body (string)\n * @param signature - The x-drip-signature header value\n * @param secret - Your webhook secret\n * @returns Promise resolving to whether the signature is valid\n *\n * @example\n * ```typescript\n * app.post('/webhooks/drip', async (req, res) => {\n * const isValid = await Drip.verifyWebhookSignature(\n * req.rawBody,\n * req.headers['x-drip-signature'],\n * process.env.DRIP_WEBHOOK_SECRET!,\n * );\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook...\n * });\n * ```\n */\n static async verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string,\n tolerance = 300, // 5 minutes default\n ): Promise<boolean> {\n if (!payload || !signature || !secret) {\n return false;\n }\n\n try {\n // Parse signature format: t=timestamp,v1=hexsignature\n const parts = signature.split(',');\n const timestampPart = parts.find((p) => p.startsWith('t='));\n const signaturePart = parts.find((p) => p.startsWith('v1='));\n\n if (!timestampPart || !signaturePart) {\n return false;\n }\n\n const timestamp = parseInt(timestampPart.slice(2), 10);\n const providedSignature = signaturePart.slice(3);\n\n if (isNaN(timestamp)) {\n return false;\n }\n\n // Check timestamp tolerance\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return false;\n }\n\n // Compute expected signature using timestamp.payload format\n const signaturePayload = `${timestamp}.${payload}`;\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const payloadData = encoder.encode(signaturePayload);\n\n // Get the subtle crypto API - use globalThis.crypto for browsers/edge runtimes,\n // or fall back to Node.js webcrypto for Node.js 18+\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const subtle = globalThis.crypto?.subtle ?? (require('crypto') as typeof import('crypto')).webcrypto.subtle;\n\n // Import the secret as an HMAC key\n const cryptoKey = await subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n\n // Sign the payload\n const signatureBuffer = await subtle.sign(\n 'HMAC',\n cryptoKey,\n payloadData,\n );\n\n // Convert to hex string\n const expectedSignature = Array.from(new Uint8Array(signatureBuffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n // Constant-time comparison to prevent timing attacks\n if (providedSignature.length !== expectedSignature.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < providedSignature.length; i++) {\n result |= providedSignature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n }\n\n return result === 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Synchronously verifies a webhook signature using HMAC-SHA256.\n *\n * This method uses Node.js crypto module and is only available in Node.js environments.\n * For edge runtimes or browsers, use the async `verifyWebhookSignature` method instead.\n *\n * @param payload - The raw request body (string)\n * @param signature - The x-drip-signature header value\n * @param secret - Your webhook secret\n * @returns Whether the signature is valid\n *\n * @example\n * ```typescript\n * app.post('/webhooks/drip', (req, res) => {\n * const isValid = Drip.verifyWebhookSignatureSync(\n * req.rawBody,\n * req.headers['x-drip-signature'],\n * process.env.DRIP_WEBHOOK_SECRET!,\n * );\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook...\n * });\n * ```\n */\n static verifyWebhookSignatureSync(\n payload: string,\n signature: string,\n secret: string,\n tolerance = 300, // 5 minutes default\n ): boolean {\n if (!payload || !signature || !secret) {\n return false;\n }\n\n try {\n // Parse signature format: t=timestamp,v1=hexsignature\n const parts = signature.split(',');\n const timestampPart = parts.find((p) => p.startsWith('t='));\n const signaturePart = parts.find((p) => p.startsWith('v1='));\n\n if (!timestampPart || !signaturePart) {\n return false;\n }\n\n const timestamp = parseInt(timestampPart.slice(2), 10);\n const providedSignature = signaturePart.slice(3);\n\n if (isNaN(timestamp)) {\n return false;\n }\n\n // Check timestamp tolerance\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return false;\n }\n\n // Dynamic import to avoid bundling issues in edge runtimes\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const crypto = require('crypto') as typeof import('crypto');\n\n // Compute expected signature using timestamp.payload format\n const signaturePayload = `${timestamp}.${payload}`;\n const expectedSignature = crypto\n .createHmac('sha256', secret)\n .update(signaturePayload)\n .digest('hex');\n\n // Use timingSafeEqual for constant-time comparison\n const sigBuffer = Buffer.from(providedSignature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n }\n\n /**\n * Generates a webhook signature for testing purposes.\n *\n * This method creates a signature in the same format the Drip backend uses,\n * allowing you to test your webhook handling code locally.\n *\n * @param payload - The webhook payload (JSON string)\n * @param secret - The webhook secret\n * @param timestamp - Optional timestamp (defaults to current time)\n * @returns Signature in format: t=timestamp,v1=hexsignature\n *\n * @example\n * ```typescript\n * const payload = JSON.stringify({ type: 'charge.succeeded', data: {...} });\n * const signature = Drip.generateWebhookSignature(payload, 'whsec_test123');\n *\n * // Use in tests:\n * const isValid = Drip.verifyWebhookSignatureSync(payload, signature, 'whsec_test123');\n * console.log(isValid); // true\n * ```\n */\n static generateWebhookSignature(\n payload: string,\n secret: string,\n timestamp?: number,\n ): string {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const crypto = require('crypto') as typeof import('crypto');\n\n const ts = timestamp ?? Math.floor(Date.now() / 1000);\n const signaturePayload = `${ts}.${payload}`;\n const signature = crypto\n .createHmac('sha256', secret)\n .update(signaturePayload)\n .digest('hex');\n\n return `t=${ts},v1=${signature}`;\n }\n\n // ==========================================================================\n // StreamMeter Factory\n // ==========================================================================\n\n /**\n * Creates a StreamMeter for accumulating usage and charging once.\n *\n * Perfect for LLM token streaming where you want to:\n * - Accumulate tokens locally (no API call per token)\n * - Charge once at the end of the stream\n * - Handle partial failures (charge for what was delivered)\n *\n * @param options - StreamMeter configuration\n * @returns A new StreamMeter instance\n *\n * @example\n * ```typescript\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * });\n *\n * // Accumulate tokens as they stream\n * for await (const chunk of llmStream) {\n * meter.addSync(chunk.tokens);\n * yield chunk;\n * }\n *\n * // Single charge at end\n * const result = await meter.flush();\n * console.log(`Charged ${result.charge?.amountUsdc} for ${result.quantity} tokens`);\n * ```\n *\n * @example\n * ```typescript\n * // With auto-flush threshold\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * flushThreshold: 10000, // Charge every 10k tokens\n * });\n *\n * for await (const chunk of longStream) {\n * await meter.add(chunk.tokens); // May auto-flush\n * }\n *\n * await meter.flush(); // Final flush for remaining tokens\n * ```\n */\n createStreamMeter(options: StreamMeterOptions): StreamMeter {\n return new StreamMeter(this.charge.bind(this), options);\n }\n}\n\n// Re-export StreamMeter types\nexport { StreamMeter } from './stream-meter.js';\nexport type { StreamMeterOptions, StreamMeterFlushResult } from './stream-meter.js';\n\n// Re-export Resilience types and utilities\nexport {\n ResilienceManager,\n RateLimiter,\n CircuitBreaker,\n MetricsCollector,\n RetryExhaustedError,\n CircuitBreakerOpenError,\n createDefaultResilienceConfig,\n createDisabledResilienceConfig,\n createHighThroughputResilienceConfig,\n calculateBackoff,\n isRetryableError,\n} from './resilience.js';\n\nexport type {\n ResilienceConfig,\n ResilienceHealth,\n RateLimiterConfig,\n RetryConfig,\n CircuitBreakerConfig,\n CircuitState,\n RequestMetrics,\n MetricsSummary,\n} from './resilience.js';\n\n// Default export for convenience\nexport default Drip;\n","/**\n * Drip LangChain integration.\n *\n * This module provides callback handlers for tracking LangChain LLM, tool,\n * chain, and agent usage with automatic billing through the Drip API.\n *\n * @example\n * ```typescript\n * import { DripCallbackHandler } from '@drip-sdk/node/langchain';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const handler = new DripCallbackHandler({\n * apiKey: 'drip_sk_...',\n * customerId: 'cus_123',\n * workflow: 'chatbot',\n * });\n *\n * const llm = new ChatOpenAI({\n * callbacks: [handler],\n * });\n *\n * const response = await llm.invoke('Hello, world!');\n * // Usage is automatically tracked and billed\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Drip } from '../index.js';\n\n// =============================================================================\n// Model Pricing\n// =============================================================================\n\n/**\n * Pricing per 1M tokens for a model.\n */\nexport interface ModelPricing {\n /** Cost per 1M input/prompt tokens in USD */\n input: number;\n /** Cost per 1M output/completion tokens in USD */\n output: number;\n}\n\n/**\n * OpenAI pricing per 1M tokens (as of late 2024).\n */\nexport const OPENAI_PRICING: Record<string, ModelPricing> = {\n 'gpt-4o': { input: 2.5, output: 10.0 },\n 'gpt-4o-mini': { input: 0.15, output: 0.6 },\n 'gpt-4-turbo': { input: 10.0, output: 30.0 },\n 'gpt-4': { input: 30.0, output: 60.0 },\n 'gpt-4-32k': { input: 60.0, output: 120.0 },\n 'gpt-3.5-turbo': { input: 0.5, output: 1.5 },\n 'gpt-3.5-turbo-16k': { input: 3.0, output: 4.0 },\n // Embedding models\n 'text-embedding-3-small': { input: 0.02, output: 0.0 },\n 'text-embedding-3-large': { input: 0.13, output: 0.0 },\n 'text-embedding-ada-002': { input: 0.1, output: 0.0 },\n} as const;\n\n/**\n * Anthropic pricing per 1M tokens.\n */\nexport const ANTHROPIC_PRICING: Record<string, ModelPricing> = {\n 'claude-3-5-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-opus': { input: 15.0, output: 75.0 },\n 'claude-3-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-haiku': { input: 0.25, output: 1.25 },\n 'claude-2.1': { input: 8.0, output: 24.0 },\n 'claude-2.0': { input: 8.0, output: 24.0 },\n 'claude-instant-1.2': { input: 0.8, output: 2.4 },\n} as const;\n\n/**\n * Get pricing for a model by name.\n *\n * @param modelName - The model name/identifier.\n * @returns Pricing object with input/output costs per 1M tokens, or undefined if unknown.\n */\nexport function getModelPricing(modelName: string): ModelPricing | undefined {\n const modelLower = modelName.toLowerCase();\n\n // Check OpenAI models\n for (const [key, pricing] of Object.entries(OPENAI_PRICING)) {\n if (modelLower.includes(key)) {\n return pricing;\n }\n }\n\n // Check Anthropic models\n for (const [key, pricing] of Object.entries(ANTHROPIC_PRICING)) {\n if (modelLower.includes(key)) {\n return pricing;\n }\n }\n\n return undefined;\n}\n\n/**\n * Calculate the cost for a model invocation.\n *\n * @param modelName - The model name.\n * @param inputTokens - Number of input/prompt tokens.\n * @param outputTokens - Number of output/completion tokens.\n * @returns Cost in USD, or undefined if pricing is unknown.\n */\nexport function calculateCost(\n modelName: string,\n inputTokens: number,\n outputTokens: number,\n): number | undefined {\n const pricing = getModelPricing(modelName);\n if (pricing === undefined) {\n return undefined;\n }\n\n const inputCost = (inputTokens / 1_000_000) * pricing.input;\n const outputCost = (outputTokens / 1_000_000) * pricing.output;\n\n return inputCost + outputCost;\n}\n\n// =============================================================================\n// Tracking State Types\n// =============================================================================\n\n/**\n * State for tracking an LLM call.\n */\ninterface LLMCallState {\n runId: string;\n model: string;\n startTime: number;\n prompts: string[];\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n error: string | null;\n}\n\n/**\n * State for tracking a tool call.\n */\ninterface ToolCallState {\n runId: string;\n toolName: string;\n startTime: number;\n inputStr: string;\n outputStr: string;\n error: string | null;\n}\n\n/**\n * State for tracking a chain execution.\n */\ninterface ChainCallState {\n runId: string;\n chainType: string;\n startTime: number;\n inputs: Record<string, unknown>;\n outputs: Record<string, unknown>;\n error: string | null;\n}\n\n/**\n * State for tracking agent execution.\n */\ninterface AgentCallState {\n runId: string;\n startTime: number;\n actions: Array<{\n tool: string;\n toolInput: string;\n log: string | null;\n }>;\n finalOutput: string | null;\n error: string | null;\n}\n\n// =============================================================================\n// LangChain Types (minimal type definitions to avoid direct dependency)\n// =============================================================================\n\n/**\n * Serialized representation from LangChain.\n */\ninterface Serialized {\n name?: string;\n id?: string[];\n}\n\n/**\n * LLM result from LangChain.\n */\ninterface LLMResult {\n llm_output?: {\n token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n } | null;\n}\n\n/**\n * Agent action from LangChain.\n */\ninterface AgentAction {\n tool: string;\n toolInput: string | Record<string, unknown>;\n log: string;\n}\n\n/**\n * Agent finish from LangChain.\n */\ninterface AgentFinish {\n returnValues: Record<string, unknown>;\n log: string;\n}\n\n/**\n * Document from LangChain.\n */\ninterface Document {\n pageContent: string;\n metadata: Record<string, unknown>;\n}\n\n// =============================================================================\n// Callback Handler Configuration\n// =============================================================================\n\n/**\n * Configuration options for DripCallbackHandler.\n */\nexport interface DripCallbackHandlerOptions {\n /**\n * Drip API key. Falls back to DRIP_API_KEY environment variable.\n */\n apiKey?: string;\n\n /**\n * The customer ID to bill usage to.\n * Can be set later via the `customerId` property.\n */\n customerId?: string;\n\n /**\n * Workflow name or ID for grouping runs.\n * @default \"langchain\"\n */\n workflow?: string;\n\n /**\n * Base URL for the Drip API.\n */\n baseUrl?: string;\n\n /**\n * Whether to automatically create runs when LLM calls start.\n * @default true\n */\n autoCreateRun?: boolean;\n\n /**\n * Whether to emit events on errors.\n * @default true\n */\n emitOnError?: boolean;\n\n /**\n * Additional metadata to attach to all events.\n */\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// DripCallbackHandler\n// =============================================================================\n\n/**\n * LangChain callback handler for Drip usage tracking.\n *\n * This handler automatically tracks LLM calls, tool usage, chain executions,\n * and agent actions, emitting events to the Drip API for billing.\n *\n * @example\n * ```typescript\n * import { DripCallbackHandler } from '@drip-sdk/node/langchain';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const handler = new DripCallbackHandler({\n * apiKey: 'drip_sk_...',\n * customerId: 'cus_123',\n * workflow: 'chatbot',\n * });\n *\n * const llm = new ChatOpenAI({\n * callbacks: [handler],\n * });\n *\n * const response = await llm.invoke('Hello!');\n * ```\n */\nexport class DripCallbackHandler {\n private readonly _client: Drip;\n private _customerId: string | undefined;\n private readonly _workflow: string;\n private readonly _autoCreateRun: boolean;\n private readonly _emitOnError: boolean;\n private readonly _baseMetadata: Record<string, unknown>;\n\n // Active tracking state\n private _currentRunId: string | null = null;\n private readonly _llmCalls: Map<string, LLMCallState> = new Map();\n private readonly _toolCalls: Map<string, ToolCallState> = new Map();\n private readonly _chainCalls: Map<string, ChainCallState> = new Map();\n private readonly _agentCalls: Map<string, AgentCallState> = new Map();\n\n constructor(options: DripCallbackHandlerOptions = {}) {\n this._client = new Drip({\n apiKey: options.apiKey ?? process.env.DRIP_API_KEY ?? '',\n baseUrl: options.baseUrl,\n });\n this._customerId = options.customerId;\n this._workflow = options.workflow ?? 'langchain';\n this._autoCreateRun = options.autoCreateRun ?? true;\n this._emitOnError = options.emitOnError ?? true;\n this._baseMetadata = options.metadata ?? {};\n }\n\n /**\n * Get the customer ID.\n * @throws Error if customer ID is not set.\n */\n get customerId(): string {\n if (this._customerId === undefined) {\n throw new Error('customerId must be set before using the handler');\n }\n return this._customerId;\n }\n\n /**\n * Set the customer ID.\n */\n set customerId(value: string) {\n this._customerId = value;\n }\n\n /**\n * Get the current run ID.\n */\n get runId(): string | null {\n return this._currentRunId;\n }\n\n /**\n * Manually start a new run.\n *\n * @param options - Run options.\n * @returns The created run ID.\n */\n async startRun(options: {\n externalRunId?: string;\n correlationId?: string;\n metadata?: Record<string, unknown>;\n } = {}): Promise<string> {\n const result = await this._client.recordRun({\n customerId: this.customerId,\n workflow: this._workflow,\n events: [],\n status: 'COMPLETED',\n externalRunId: options.externalRunId,\n correlationId: options.correlationId,\n metadata: { ...this._baseMetadata, ...(options.metadata ?? {}) },\n });\n this._currentRunId = result.run.id;\n return this._currentRunId;\n }\n\n /**\n * Manually end the current run.\n *\n * @param status - Final status.\n * @param errorMessage - Error message for failed runs.\n */\n async endRun(\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT' = 'COMPLETED',\n errorMessage?: string,\n ): Promise<void> {\n if (this._currentRunId) {\n await this._client.endRun(this._currentRunId, {\n status,\n errorMessage,\n });\n this._currentRunId = null;\n }\n }\n\n /**\n * Ensure a run exists, creating one if autoCreateRun is enabled.\n */\n private async _ensureRun(): Promise<string> {\n if (this._currentRunId === null) {\n if (this._autoCreateRun) {\n const result = await this._client.startRun({\n customerId: this.customerId,\n workflowId: this._workflow,\n metadata: this._baseMetadata,\n });\n this._currentRunId = result.id;\n } else {\n throw new Error('No active run. Call startRun() first.');\n }\n }\n return this._currentRunId;\n }\n\n /**\n * Emit an event to the Drip API.\n */\n private async _emitEvent(params: {\n eventType: string;\n quantity?: number;\n units?: string;\n description?: string;\n costUnits?: number;\n metadata?: Record<string, unknown>;\n idempotencySuffix?: string;\n }): Promise<void> {\n const runId = await this._ensureRun();\n\n let idempotencyKey: string | undefined;\n if (params.idempotencySuffix) {\n idempotencyKey = Drip.generateIdempotencyKey({\n customerId: this.customerId,\n stepName: `${params.eventType}:${params.idempotencySuffix}`,\n runId,\n });\n }\n\n await this._client.emitEvent({\n runId,\n eventType: params.eventType,\n quantity: params.quantity,\n units: params.units,\n description: params.description,\n costUnits: params.costUnits,\n idempotencyKey,\n metadata: { ...this._baseMetadata, ...(params.metadata ?? {}) },\n });\n }\n\n // ===========================================================================\n // LLM Callbacks\n // ===========================================================================\n\n /**\n * Called when LLM starts running.\n */\n handleLLMStart(\n serialized: Serialized,\n prompts: string[],\n runId: string,\n _parentRunId?: string,\n _extraParams?: Record<string, unknown>,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const modelName = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n this._llmCalls.set(runId, {\n runId,\n model: modelName,\n startTime: Date.now(),\n prompts,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n error: null,\n });\n }\n\n /**\n * Called when LLM ends running.\n */\n async handleLLMEnd(\n response: LLMResult,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._llmCalls.get(runId);\n this._llmCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n // Extract token usage\n const tokenUsage = response.llm_output?.token_usage ?? {};\n const inputTokens = tokenUsage.prompt_tokens ?? 0;\n const outputTokens = tokenUsage.completion_tokens ?? 0;\n const totalTokens = tokenUsage.total_tokens ?? (inputTokens + outputTokens);\n\n // Calculate cost\n const cost = calculateCost(state.model, inputTokens, outputTokens);\n\n // Emit event\n await this._emitEvent({\n eventType: 'llm.completion',\n quantity: totalTokens,\n units: 'tokens',\n description: `LLM call to ${state.model}`,\n costUnits: cost,\n metadata: {\n model: state.model,\n inputTokens,\n outputTokens,\n latencyMs,\n promptCount: state.prompts.length,\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when LLM errors.\n */\n async handleLLMError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._llmCalls.get(runId);\n this._llmCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'llm.error',\n quantity: 1,\n units: 'errors',\n description: `LLM error: ${error.name}`,\n metadata: {\n model: state.model,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n /**\n * Called on new LLM token (streaming).\n * Tokens are tracked at completion, not per-token.\n */\n handleLLMNewToken(\n _token: string,\n _idx: { prompt: number; completion: number },\n _runId: string,\n _parentRunId?: string,\n ): void {\n // Tokens tracked at completion, not per-token\n }\n\n // ===========================================================================\n // Chat Model Callbacks\n // ===========================================================================\n\n /**\n * Called when chat model starts running.\n */\n handleChatModelStart(\n serialized: Serialized,\n messages: unknown[][],\n runId: string,\n _parentRunId?: string,\n _extraParams?: Record<string, unknown>,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const modelName = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n // Convert messages to string representation for tracking\n const prompts = messages.map((msgList) => JSON.stringify(msgList));\n\n this._llmCalls.set(runId, {\n runId,\n model: modelName,\n startTime: Date.now(),\n prompts,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n error: null,\n });\n }\n\n // ===========================================================================\n // Tool Callbacks\n // ===========================================================================\n\n /**\n * Called when tool starts running.\n */\n handleToolStart(\n serialized: Serialized,\n inputStr: string,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const toolName = serialized.name ?? 'unknown_tool';\n\n this._toolCalls.set(runId, {\n runId,\n toolName,\n startTime: Date.now(),\n inputStr: inputStr.slice(0, 1000), // Truncate long inputs\n outputStr: '',\n error: null,\n });\n }\n\n /**\n * Called when tool ends running.\n */\n async handleToolEnd(\n output: string,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'tool.call',\n quantity: 1,\n units: 'calls',\n description: `Tool: ${state.toolName}`,\n metadata: {\n toolName: state.toolName,\n latencyMs,\n inputPreview: state.inputStr.slice(0, 200),\n outputPreview: String(output).slice(0, 200),\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when tool errors.\n */\n async handleToolError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'tool.error',\n quantity: 1,\n units: 'errors',\n description: `Tool error: ${state.toolName}`,\n metadata: {\n toolName: state.toolName,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Chain Callbacks\n // ===========================================================================\n\n /**\n * Called when chain starts running.\n */\n handleChainStart(\n serialized: Serialized,\n inputs: Record<string, unknown>,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const chainType = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n this._chainCalls.set(runId, {\n runId,\n chainType,\n startTime: Date.now(),\n inputs,\n outputs: {},\n error: null,\n });\n }\n\n /**\n * Called when chain ends running.\n */\n async handleChainEnd(\n outputs: Record<string, unknown>,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._chainCalls.get(runId);\n this._chainCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'chain.execution',\n quantity: 1,\n units: 'executions',\n description: `Chain: ${state.chainType}`,\n metadata: {\n chainType: state.chainType,\n latencyMs,\n inputKeys: Object.keys(state.inputs),\n outputKeys: Object.keys(outputs),\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when chain errors.\n */\n async handleChainError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._chainCalls.get(runId);\n this._chainCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'chain.error',\n quantity: 1,\n units: 'errors',\n description: `Chain error: ${state.chainType}`,\n metadata: {\n chainType: state.chainType,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Agent Callbacks\n // ===========================================================================\n\n /**\n * Called when agent takes an action.\n */\n async handleAgentAction(\n action: AgentAction,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n let state = this._agentCalls.get(runId);\n\n if (!state) {\n state = {\n runId,\n startTime: Date.now(),\n actions: [],\n finalOutput: null,\n error: null,\n };\n this._agentCalls.set(runId, state);\n }\n\n const toolInput = typeof action.toolInput === 'string'\n ? action.toolInput\n : JSON.stringify(action.toolInput);\n\n state.actions.push({\n tool: action.tool,\n toolInput: toolInput.slice(0, 500),\n log: action.log?.slice(0, 500) ?? null,\n });\n\n // Emit action event\n await this._emitEvent({\n eventType: 'agent.action',\n quantity: 1,\n units: 'actions',\n description: `Agent action: ${action.tool}`,\n metadata: {\n tool: action.tool,\n actionCount: state.actions.length,\n },\n idempotencySuffix: `${runId}:${state.actions.length}`,\n });\n }\n\n /**\n * Called when agent finishes.\n */\n async handleAgentEnd(\n finish: AgentFinish,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._agentCalls.get(runId);\n this._agentCalls.delete(runId);\n\n let latencyMs = 0;\n let actionCount = 0;\n\n if (state) {\n latencyMs = Date.now() - state.startTime;\n actionCount = state.actions.length;\n }\n\n await this._emitEvent({\n eventType: 'agent.finish',\n quantity: actionCount || 1,\n units: 'actions',\n description: 'Agent run completed',\n metadata: {\n latencyMs,\n actionCount,\n outputPreview: JSON.stringify(finish.returnValues).slice(0, 500),\n },\n idempotencySuffix: runId,\n });\n }\n\n // ===========================================================================\n // Retriever Callbacks\n // ===========================================================================\n\n /**\n * Called when retriever starts running.\n */\n handleRetrieverStart(\n serialized: Serialized,\n query: string,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const retrieverName = serialized.name ?? 'retriever';\n\n // Track as a tool call\n this._toolCalls.set(runId, {\n runId,\n toolName: `retriever:${retrieverName}`,\n startTime: Date.now(),\n inputStr: query.slice(0, 1000),\n outputStr: '',\n error: null,\n });\n }\n\n /**\n * Called when retriever ends running.\n */\n async handleRetrieverEnd(\n documents: Document[],\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'retriever.query',\n quantity: documents.length,\n units: 'documents',\n description: `Retriever: ${state.toolName}`,\n metadata: {\n retriever: state.toolName,\n queryPreview: state.inputStr.slice(0, 200),\n documentCount: documents.length,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when retriever errors.\n */\n async handleRetrieverError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'retriever.error',\n quantity: 1,\n units: 'errors',\n description: `Retriever error: ${state.toolName}`,\n metadata: {\n retriever: state.toolName,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Text Callbacks\n // ===========================================================================\n\n /**\n * Called when arbitrary text is received.\n * Optional: override to track text events if needed.\n */\n handleText(\n _text: string,\n _runId: string,\n _parentRunId?: string,\n ): void {\n // Optional: track text events if needed\n }\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport {\n DripCallbackHandler as default,\n};\n"]}
1
+ {"version":3,"sources":["../src/idempotency.ts","../src/stream-meter.ts","../src/resilience.ts","../src/index.ts","../src/integrations/langchain.ts"],"names":["_callCounter","deterministicIdempotencyKey","prefix","components","seq","parts","c","hash","createHash","StreamMeter","chargeFn","options","quantity","idempotencyKey","chargeResult","result","DEFAULT_RATE_LIMITER_CONFIG","RateLimiter","config","now","elapsed","timeout","deadline","waitTime","remaining","ms","resolve","RetryExhaustedError","_RetryExhaustedError","attempts","lastError","calculateBackoff","attempt","delay","jitterRange","isRetryableError","error","statusCode","DEFAULT_CIRCUIT_BREAKER_CONFIG","CircuitBreakerOpenError","_CircuitBreakerOpenError","circuitName","timeUntilRetryMs","CircuitBreaker","name","MetricsCollector","windowSize","metrics","latencies","m","a","b","successes","byEndpoint","errors","sum","createDefaultResilienceConfig","createHighThroughputResilienceConfig","ResilienceManager","fn","method","endpoint","startTime","retryCount","duration","DEFAULT_RETRY_CONFIG","defaultIsRetryable","DripError","retryWithBackoff","maxAttempts","baseDelayMs","maxDelayMs","isRetryable","_DripError","message","code","Drip","apiKey","baseUrl","operation","path","controller","timeoutId","res","data","healthBaseUrl","start","response","latencyMs","status","timestamp","params","customerId","query","charge","chargeId","webhookId","runId","events","plan","periodStart","periodEnd","workflowId","workflowName","existing","w","created","run","eventsCreated","eventsDuplicates","batchEvents","event","index","batchResult","endResult","durationMs","eventSummary","summary","str","payload","signature","secret","tolerance","timestampPart","p","signaturePart","providedSignature","signaturePayload","encoder","keyData","payloadData","subtle","cryptoKey","signatureBuffer","expectedSignature","i","crypto","sigBuffer","expectedBuffer","ts","_singleton","getSingleton","_target","prop","instance","value","OPENAI_PRICING","ANTHROPIC_PRICING","getModelPricing","modelName","modelLower","key","pricing","calculateCost","inputTokens","outputTokens","inputCost","outputCost","DripCallbackHandler","errorMessage","serialized","prompts","_parentRunId","_extraParams","_tags","_metadata","state","tokenUsage","totalTokens","cost","_token","_idx","_runId","messages","msgList","inputStr","toolName","output","inputs","chainType","outputs","action","toolInput","finish","actionCount","retrieverName","documents","_text"],"mappings":"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","file":"langchain.cjs","sourcesContent":["/**\n * Deterministic idempotency key generation for SDK calls.\n *\n * Keys are:\n * - **Unique per call** — a monotonic counter ensures two rapid calls with\n * identical parameters produce different keys.\n * - **Stable across retries** — the key is generated once per SDK method\n * invocation and reused for every retry attempt.\n * - **Deterministic** — no randomness; keys are reproducible given the same\n * counter state.\n *\n * @internal\n */\nimport { createHash } from 'crypto';\n\nlet _callCounter = 0;\n\n/**\n * Generate a deterministic, unique idempotency key.\n *\n * @param prefix - Short prefix for the key type (e.g. `chg`, `track`, `evt`, `run`, `stream`)\n * @param components - Call-specific values (customerId, meter, quantity, etc.)\n * @returns A key like `chg_<24-char hex hash>`\n */\nexport function deterministicIdempotencyKey(\n prefix: string,\n ...components: Array<string | number | undefined>\n): string {\n const seq = ++_callCounter;\n const parts = components.filter((c) => c !== undefined).map(String);\n parts.push(String(seq));\n const hash = createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 24);\n return `${prefix}_${hash}`;\n}\n\n/**\n * Reset counter — only for tests.\n * @internal\n */\nexport function _resetCallCounter(): void {\n _callCounter = 0;\n}\n","/**\n * StreamMeter - Accumulate usage locally and charge once at the end.\n *\n * Perfect for LLM token streaming and other high-frequency metering scenarios\n * where you want to avoid making an API call for every small increment.\n *\n * @example\n * ```typescript\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * });\n *\n * for await (const chunk of llmStream) {\n * meter.add(chunk.tokens);\n * }\n *\n * const result = await meter.flush();\n * console.log(`Charged ${result.charge.amountUsdc} for ${result.quantity} tokens`);\n * ```\n */\n\nimport type { ChargeResult, ChargeParams } from './index.js';\nimport { deterministicIdempotencyKey } from './idempotency.js';\n\n/**\n * Options for creating a StreamMeter.\n */\nexport interface StreamMeterOptions {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n */\n meter: string;\n\n /**\n * Unique key to prevent duplicate charges.\n * If not provided, one will be generated.\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to the charge.\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Auto-flush when accumulated quantity reaches this threshold.\n * Useful for long-running streams where you want periodic charges.\n */\n flushThreshold?: number;\n\n /**\n * Callback invoked on each add() call.\n * Useful for logging or progress tracking.\n */\n onAdd?: (quantity: number, total: number) => void;\n\n /**\n * Callback invoked after each successful flush.\n */\n onFlush?: (result: StreamMeterFlushResult) => void;\n}\n\n/**\n * Result of flushing a StreamMeter.\n */\nexport interface StreamMeterFlushResult {\n /** Whether the flush was successful */\n success: boolean;\n\n /** The quantity that was charged */\n quantity: number;\n\n /** The charge result from the API (if quantity > 0) */\n charge: ChargeResult['charge'] | null;\n\n /** Whether this was an idempotent replay */\n isReplay: boolean;\n}\n\n/**\n * Internal charge function type (injected by Drip class).\n */\nexport type ChargeFn = (params: ChargeParams) => Promise<ChargeResult>;\n\n/**\n * StreamMeter accumulates usage locally and charges once when flushed.\n *\n * This is ideal for:\n * - LLM token streaming (charge once at end of stream)\n * - High-frequency events (batch small increments)\n * - Partial failure handling (charge for what was delivered)\n */\nexport class StreamMeter {\n private _total: number = 0;\n private _flushed: boolean = false;\n private _flushCount: number = 0;\n private readonly _chargeFn: ChargeFn;\n private readonly _options: StreamMeterOptions;\n\n /**\n * Creates a new StreamMeter.\n *\n * @param chargeFn - The charge function from Drip client\n * @param options - StreamMeter configuration\n */\n constructor(chargeFn: ChargeFn, options: StreamMeterOptions) {\n this._chargeFn = chargeFn;\n this._options = options;\n }\n\n /**\n * Current accumulated quantity (not yet charged).\n */\n get total(): number {\n return this._total;\n }\n\n /**\n * Whether this meter has been flushed at least once.\n */\n get isFlushed(): boolean {\n return this._flushed;\n }\n\n /**\n * Number of times this meter has been flushed.\n */\n get flushCount(): number {\n return this._flushCount;\n }\n\n /**\n * Add quantity to the accumulated total.\n *\n * If a flushThreshold is set and the total exceeds it,\n * this will automatically trigger a flush.\n *\n * @param quantity - Amount to add (must be positive)\n * @returns Promise that resolves when add completes (may trigger auto-flush)\n */\n async add(quantity: number): Promise<StreamMeterFlushResult | null> {\n if (quantity <= 0) {\n return null;\n }\n\n this._total += quantity;\n\n // Invoke callback if provided\n this._options.onAdd?.(quantity, this._total);\n\n // Check for auto-flush threshold\n if (\n this._options.flushThreshold !== undefined &&\n this._total >= this._options.flushThreshold\n ) {\n return this.flush();\n }\n\n return null;\n }\n\n /**\n * Synchronously add quantity without auto-flush.\n * Use this for maximum performance when you don't need threshold-based flushing.\n *\n * @param quantity - Amount to add (must be positive)\n */\n addSync(quantity: number): void {\n if (quantity <= 0) {\n return;\n }\n\n this._total += quantity;\n\n // Invoke callback if provided\n this._options.onAdd?.(quantity, this._total);\n }\n\n /**\n * Flush accumulated usage and charge the customer.\n *\n * If total is 0, returns a success result with no charge.\n * After flush, the meter resets to 0 and can be reused.\n *\n * @returns The flush result including charge details\n */\n async flush(): Promise<StreamMeterFlushResult> {\n const quantity = this._total;\n\n // Reset total before charging to avoid double-counting on retry\n this._total = 0;\n\n // Nothing to charge\n if (quantity === 0) {\n const result: StreamMeterFlushResult = {\n success: true,\n quantity: 0,\n charge: null,\n isReplay: false,\n };\n return result;\n }\n\n // Generate idempotency key for this flush\n const idempotencyKey = this._options.idempotencyKey\n ? `${this._options.idempotencyKey}_flush_${this._flushCount}`\n : deterministicIdempotencyKey('stream', this._options.customerId, this._options.meter, quantity, this._flushCount);\n\n // Charge the customer\n const chargeResult = await this._chargeFn({\n customerId: this._options.customerId,\n meter: this._options.meter,\n quantity,\n idempotencyKey,\n metadata: this._options.metadata,\n });\n\n this._flushed = true;\n this._flushCount++;\n\n const result: StreamMeterFlushResult = {\n success: chargeResult.success,\n quantity,\n charge: chargeResult.charge,\n isReplay: chargeResult.isReplay,\n };\n\n // Invoke callback if provided\n this._options.onFlush?.(result);\n\n return result;\n }\n\n /**\n * Reset the meter without charging.\n * Use this to discard accumulated usage (e.g., on error before delivery).\n */\n reset(): void {\n this._total = 0;\n }\n}\n","/**\n * Production-grade resilience patterns for the Drip SDK.\n *\n * This module provides:\n * - Rate limiting (token bucket algorithm)\n * - Retry with exponential backoff\n * - Circuit breaker pattern\n * - Request metrics and observability\n */\n\n// =============================================================================\n// Rate Limiter (Token Bucket Algorithm)\n// =============================================================================\n\n/**\n * Configuration for rate limiting.\n */\nexport interface RateLimiterConfig {\n /**\n * Maximum requests per second.\n * @default 100\n */\n requestsPerSecond: number;\n\n /**\n * Maximum burst size (bucket capacity).\n * @default 200\n */\n burstSize: number;\n\n /**\n * Whether rate limiting is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default rate limiter configuration.\n */\nexport const DEFAULT_RATE_LIMITER_CONFIG: RateLimiterConfig = {\n requestsPerSecond: 100,\n burstSize: 200,\n enabled: true,\n};\n\n/**\n * Thread-safe token bucket rate limiter.\n *\n * Allows bursting up to `burstSize` requests, then limits to\n * `requestsPerSecond` sustained rate.\n */\nexport class RateLimiter {\n private readonly config: RateLimiterConfig;\n private tokens: number;\n private lastRefill: number;\n\n constructor(config?: Partial<RateLimiterConfig>) {\n this.config = { ...DEFAULT_RATE_LIMITER_CONFIG, ...config };\n this.tokens = this.config.burstSize;\n this.lastRefill = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time.\n */\n private refill(): void {\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000; // Convert to seconds\n this.tokens = Math.min(\n this.config.burstSize,\n this.tokens + elapsed * this.config.requestsPerSecond\n );\n this.lastRefill = now;\n }\n\n /**\n * Acquire a token, blocking if necessary.\n *\n * @param timeout - Maximum time to wait for a token in ms (undefined = wait forever)\n * @returns Promise that resolves to true if token acquired, false if timeout\n */\n async acquire(timeout?: number): Promise<boolean> {\n if (!this.config.enabled) {\n return true;\n }\n\n const deadline = timeout !== undefined ? Date.now() + timeout : undefined;\n\n while (true) {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n // Calculate wait time for next token\n const waitTime = ((1 - this.tokens) / this.config.requestsPerSecond) * 1000;\n\n if (deadline !== undefined) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n return false;\n }\n await this.sleep(Math.min(waitTime, remaining));\n } else {\n await this.sleep(waitTime);\n }\n }\n }\n\n /**\n * Try to acquire a token without waiting.\n *\n * @returns true if token acquired, false otherwise\n */\n tryAcquire(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n return false;\n }\n\n /**\n * Get current number of available tokens.\n */\n get availableTokens(): number {\n this.refill();\n return this.tokens;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n// =============================================================================\n// Retry with Exponential Backoff\n// =============================================================================\n\n/**\n * Configuration for retry behavior.\n */\nexport interface RetryConfig {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxRetries: number;\n\n /**\n * Base delay in milliseconds.\n * @default 100\n */\n baseDelayMs: number;\n\n /**\n * Maximum delay in milliseconds.\n * @default 10000\n */\n maxDelayMs: number;\n\n /**\n * Exponential backoff base.\n * @default 2\n */\n exponentialBase: number;\n\n /**\n * Random jitter factor (0-1).\n * @default 0.1\n */\n jitter: number;\n\n /**\n * HTTP status codes that should trigger retry.\n * @default [429, 500, 502, 503, 504]\n */\n retryableStatusCodes: number[];\n\n /**\n * Whether retry is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default retry configuration.\n */\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxRetries: 3,\n baseDelayMs: 100,\n maxDelayMs: 10000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n};\n\n/**\n * Error thrown when all retry attempts have been exhausted.\n */\nexport class RetryExhaustedError extends Error {\n readonly attempts: number;\n readonly lastError: Error;\n\n constructor(attempts: number, lastError: Error) {\n super(`Retry exhausted after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n this.attempts = attempts;\n this.lastError = lastError;\n Object.setPrototypeOf(this, RetryExhaustedError.prototype);\n }\n}\n\n/**\n * Calculate backoff delay for a given attempt.\n */\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n let delay = config.baseDelayMs * Math.pow(config.exponentialBase, attempt);\n delay = Math.min(delay, config.maxDelayMs);\n\n // Add jitter\n if (config.jitter > 0) {\n const jitterRange = delay * config.jitter;\n delay += Math.random() * 2 * jitterRange - jitterRange;\n }\n\n return Math.max(0, delay);\n}\n\n/**\n * Check if an error is retryable based on configuration.\n */\nexport function isRetryableError(\n error: unknown,\n config: RetryConfig\n): boolean {\n if (error instanceof Error) {\n // Check for network errors\n if (\n error.message.includes('fetch') ||\n error.message.includes('network') ||\n error.message.includes('ECONNREFUSED') ||\n error.message.includes('ETIMEDOUT')\n ) {\n return true;\n }\n\n // Check for status code on error object\n const statusCode = (error as { statusCode?: number }).statusCode;\n if (statusCode !== undefined) {\n return config.retryableStatusCodes.includes(statusCode);\n }\n }\n\n return false;\n}\n\n// =============================================================================\n// Circuit Breaker\n// =============================================================================\n\n/**\n * Circuit breaker states.\n */\nexport type CircuitState = 'closed' | 'open' | 'half_open';\n\n/**\n * Configuration for circuit breaker.\n */\nexport interface CircuitBreakerConfig {\n /**\n * Number of failures before opening circuit.\n * @default 5\n */\n failureThreshold: number;\n\n /**\n * Number of successes in half-open to close circuit.\n * @default 2\n */\n successThreshold: number;\n\n /**\n * Milliseconds to wait before transitioning from open to half-open.\n * @default 30000\n */\n timeoutMs: number;\n\n /**\n * Whether circuit breaker is enabled.\n * @default true\n */\n enabled: boolean;\n}\n\n/**\n * Default circuit breaker configuration.\n */\nexport const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {\n failureThreshold: 5,\n successThreshold: 2,\n timeoutMs: 30000,\n enabled: true,\n};\n\n/**\n * Error thrown when circuit breaker is open.\n */\nexport class CircuitBreakerOpenError extends Error {\n readonly circuitName: string;\n readonly timeUntilRetryMs: number;\n\n constructor(circuitName: string, timeUntilRetryMs: number) {\n super(\n `Circuit '${circuitName}' is open. Retry in ${Math.round(timeUntilRetryMs)}ms`\n );\n this.name = 'CircuitBreakerOpenError';\n this.circuitName = circuitName;\n this.timeUntilRetryMs = timeUntilRetryMs;\n Object.setPrototypeOf(this, CircuitBreakerOpenError.prototype);\n }\n}\n\n/**\n * Circuit breaker pattern implementation.\n *\n * Prevents cascading failures by failing fast when a service is unhealthy.\n */\nexport class CircuitBreaker {\n readonly name: string;\n private readonly config: CircuitBreakerConfig;\n private state: CircuitState = 'closed';\n private failureCount = 0;\n private successCount = 0;\n private lastFailureTime: number | null = null;\n\n constructor(name: string, config?: Partial<CircuitBreakerConfig>) {\n this.name = name;\n this.config = { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config };\n }\n\n /**\n * Get current circuit state.\n */\n getState(): CircuitState {\n this.checkStateTransition();\n return this.state;\n }\n\n /**\n * Check if state should transition based on timeout.\n */\n private checkStateTransition(): void {\n if (this.state === 'open' && this.lastFailureTime !== null) {\n const elapsed = Date.now() - this.lastFailureTime;\n if (elapsed >= this.config.timeoutMs) {\n this.state = 'half_open';\n this.successCount = 0;\n }\n }\n }\n\n /**\n * Record a successful call.\n */\n recordSuccess(): void {\n if (!this.config.enabled) {\n return;\n }\n\n if (this.state === 'half_open') {\n this.successCount += 1;\n if (this.successCount >= this.config.successThreshold) {\n this.state = 'closed';\n this.failureCount = 0;\n }\n } else if (this.state === 'closed') {\n // Reset failure count on success\n this.failureCount = 0;\n }\n }\n\n /**\n * Record a failed call.\n */\n recordFailure(): void {\n if (!this.config.enabled) {\n return;\n }\n\n this.failureCount += 1;\n this.lastFailureTime = Date.now();\n\n if (this.state === 'half_open') {\n // Any failure in half-open returns to open\n this.state = 'open';\n } else if (this.state === 'closed') {\n if (this.failureCount >= this.config.failureThreshold) {\n this.state = 'open';\n }\n }\n }\n\n /**\n * Check if a request should be allowed.\n */\n allowRequest(): boolean {\n if (!this.config.enabled) {\n return true;\n }\n\n this.checkStateTransition();\n\n if (this.state === 'closed') {\n return true;\n } else if (this.state === 'half_open') {\n return true; // Allow test request\n } else {\n return false;\n }\n }\n\n /**\n * Get milliseconds until circuit transitions to half-open.\n */\n getTimeUntilRetry(): number {\n if (this.state !== 'open' || this.lastFailureTime === null) {\n return 0;\n }\n const elapsed = Date.now() - this.lastFailureTime;\n return Math.max(0, this.config.timeoutMs - elapsed);\n }\n\n /**\n * Reset the circuit breaker to initial state.\n */\n reset(): void {\n this.state = 'closed';\n this.failureCount = 0;\n this.successCount = 0;\n this.lastFailureTime = null;\n }\n}\n\n// =============================================================================\n// Metrics and Observability\n// =============================================================================\n\n/**\n * Metrics for a single request.\n */\nexport interface RequestMetrics {\n method: string;\n endpoint: string;\n statusCode: number | null;\n durationMs: number;\n success: boolean;\n timestamp: number;\n error?: string;\n retryCount: number;\n}\n\n/**\n * Aggregated metrics summary.\n */\nexport interface MetricsSummary {\n windowSize: number;\n totalRequests: number;\n totalSuccesses: number;\n totalFailures: number;\n successRate: number;\n avgLatencyMs: number;\n minLatencyMs: number;\n maxLatencyMs: number;\n p50LatencyMs: number;\n p95LatencyMs: number;\n p99LatencyMs: number;\n requestsByEndpoint: Record<string, number>;\n errorsByType: Record<string, number>;\n}\n\n/**\n * Collects and aggregates request metrics.\n *\n * Thread-safe metrics collection with windowed aggregation.\n */\nexport class MetricsCollector {\n private readonly windowSize: number;\n private readonly metrics: RequestMetrics[] = [];\n private totalRequests = 0;\n private totalSuccesses = 0;\n private totalFailures = 0;\n\n constructor(windowSize = 1000) {\n this.windowSize = windowSize;\n }\n\n /**\n * Record a request's metrics.\n */\n record(metrics: RequestMetrics): void {\n this.metrics.push(metrics);\n this.totalRequests += 1;\n\n if (metrics.success) {\n this.totalSuccesses += 1;\n } else {\n this.totalFailures += 1;\n }\n\n // Maintain window size\n while (this.metrics.length > this.windowSize) {\n this.metrics.shift();\n }\n }\n\n /**\n * Get aggregated metrics summary.\n */\n getSummary(): MetricsSummary {\n if (this.metrics.length === 0) {\n return {\n windowSize: 0,\n totalRequests: 0,\n totalSuccesses: 0,\n totalFailures: 0,\n successRate: 0,\n avgLatencyMs: 0,\n minLatencyMs: 0,\n maxLatencyMs: 0,\n p50LatencyMs: 0,\n p95LatencyMs: 0,\n p99LatencyMs: 0,\n requestsByEndpoint: {},\n errorsByType: {},\n };\n }\n\n const latencies = this.metrics.map((m) => m.durationMs).sort((a, b) => a - b);\n const successes = this.metrics.filter((m) => m.success).length;\n\n // Group by endpoint\n const byEndpoint: Record<string, number> = {};\n for (const m of this.metrics) {\n byEndpoint[m.endpoint] = (byEndpoint[m.endpoint] ?? 0) + 1;\n }\n\n // Group errors\n const errors: Record<string, number> = {};\n for (const m of this.metrics) {\n if (m.error) {\n errors[m.error] = (errors[m.error] ?? 0) + 1;\n }\n }\n\n const sum = latencies.reduce((a, b) => a + b, 0);\n\n return {\n windowSize: this.metrics.length,\n totalRequests: this.totalRequests,\n totalSuccesses: this.totalSuccesses,\n totalFailures: this.totalFailures,\n successRate: (successes / this.metrics.length) * 100,\n avgLatencyMs: sum / latencies.length,\n minLatencyMs: latencies[0],\n maxLatencyMs: latencies[latencies.length - 1],\n p50LatencyMs: latencies[Math.floor(latencies.length * 0.5)],\n p95LatencyMs: latencies[Math.floor(latencies.length * 0.95)],\n p99LatencyMs: latencies[Math.floor(latencies.length * 0.99)],\n requestsByEndpoint: byEndpoint,\n errorsByType: errors,\n };\n }\n\n /**\n * Reset all metrics.\n */\n reset(): void {\n this.metrics.length = 0;\n this.totalRequests = 0;\n this.totalSuccesses = 0;\n this.totalFailures = 0;\n }\n}\n\n// =============================================================================\n// Combined Resilience Manager\n// =============================================================================\n\n/**\n * Combined configuration for all resilience features.\n */\nexport interface ResilienceConfig {\n rateLimiter: RateLimiterConfig;\n retry: RetryConfig;\n circuitBreaker: CircuitBreakerConfig;\n collectMetrics: boolean;\n}\n\n/**\n * Create default production configuration.\n */\nexport function createDefaultResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: {\n requestsPerSecond: 100,\n burstSize: 200,\n enabled: true,\n },\n retry: {\n maxRetries: 3,\n baseDelayMs: 100,\n maxDelayMs: 10000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n },\n circuitBreaker: {\n failureThreshold: 5,\n successThreshold: 2,\n timeoutMs: 30000,\n enabled: true,\n },\n collectMetrics: true,\n };\n}\n\n/**\n * Create configuration with all features disabled.\n */\nexport function createDisabledResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: { ...DEFAULT_RATE_LIMITER_CONFIG, enabled: false },\n retry: { ...DEFAULT_RETRY_CONFIG, enabled: false },\n circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG, enabled: false },\n collectMetrics: false,\n };\n}\n\n/**\n * Create configuration optimized for high throughput.\n */\nexport function createHighThroughputResilienceConfig(): ResilienceConfig {\n return {\n rateLimiter: {\n requestsPerSecond: 1000,\n burstSize: 2000,\n enabled: true,\n },\n retry: {\n maxRetries: 2,\n baseDelayMs: 50,\n maxDelayMs: 5000,\n exponentialBase: 2,\n jitter: 0.1,\n retryableStatusCodes: [429, 500, 502, 503, 504],\n enabled: true,\n },\n circuitBreaker: {\n failureThreshold: 10,\n successThreshold: 3,\n timeoutMs: 15000,\n enabled: true,\n },\n collectMetrics: true,\n };\n}\n\n/**\n * Health status of resilience components.\n */\nexport interface ResilienceHealth {\n circuitBreaker: {\n state: CircuitState;\n timeUntilRetryMs: number;\n };\n rateLimiter: {\n availableTokens: number;\n requestsPerSecond: number;\n };\n metrics: MetricsSummary | null;\n}\n\n/**\n * Manages all resilience features for the SDK.\n *\n * Provides a unified interface for rate limiting, retry, circuit breaker,\n * and metrics collection.\n */\nexport class ResilienceManager {\n readonly config: ResilienceConfig;\n readonly rateLimiter: RateLimiter;\n readonly circuitBreaker: CircuitBreaker;\n readonly metrics: MetricsCollector | null;\n\n constructor(config?: Partial<ResilienceConfig>) {\n this.config = {\n ...createDefaultResilienceConfig(),\n ...config,\n rateLimiter: {\n ...createDefaultResilienceConfig().rateLimiter,\n ...config?.rateLimiter,\n },\n retry: {\n ...createDefaultResilienceConfig().retry,\n ...config?.retry,\n },\n circuitBreaker: {\n ...createDefaultResilienceConfig().circuitBreaker,\n ...config?.circuitBreaker,\n },\n };\n\n this.rateLimiter = new RateLimiter(this.config.rateLimiter);\n this.circuitBreaker = new CircuitBreaker('drip_api', this.config.circuitBreaker);\n this.metrics = this.config.collectMetrics ? new MetricsCollector() : null;\n }\n\n /**\n * Execute a function with all resilience features.\n *\n * @param fn - The function to execute\n * @param method - HTTP method for metrics\n * @param endpoint - Endpoint for metrics\n * @returns Result of the function\n */\n async execute<T>(\n fn: () => Promise<T>,\n method = 'UNKNOWN',\n endpoint = 'unknown'\n ): Promise<T> {\n const startTime = performance.now();\n let retryCount = 0;\n let lastError: Error | null = null;\n\n // Rate limiting\n const acquired = await this.rateLimiter.acquire(30000);\n if (!acquired) {\n throw new Error('Rate limiter timeout');\n }\n\n // Circuit breaker check\n if (!this.circuitBreaker.allowRequest()) {\n throw new CircuitBreakerOpenError(\n this.circuitBreaker.name,\n this.circuitBreaker.getTimeUntilRetry()\n );\n }\n\n // Execute with retry\n for (let attempt = 0; attempt <= this.config.retry.maxRetries; attempt++) {\n try {\n const result = await fn();\n this.circuitBreaker.recordSuccess();\n\n // Record success metrics\n if (this.metrics) {\n const duration = performance.now() - startTime;\n this.metrics.record({\n method,\n endpoint,\n statusCode: 200,\n durationMs: duration,\n success: true,\n timestamp: Date.now(),\n retryCount,\n });\n }\n\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if retryable\n const isRetryable =\n this.config.retry.enabled &&\n isRetryableError(error, this.config.retry);\n\n if (isRetryable && attempt < this.config.retry.maxRetries) {\n retryCount += 1;\n const delay = calculateBackoff(attempt, this.config.retry);\n await this.sleep(delay);\n continue;\n }\n\n // Not retryable or exhausted retries\n this.circuitBreaker.recordFailure();\n\n // Record failure metrics\n if (this.metrics) {\n const duration = performance.now() - startTime;\n const statusCode = (error as { statusCode?: number }).statusCode ?? null;\n this.metrics.record({\n method,\n endpoint,\n statusCode,\n durationMs: duration,\n success: false,\n timestamp: Date.now(),\n error: lastError.name,\n retryCount,\n });\n }\n\n throw error;\n }\n }\n\n // Should not reach here\n if (lastError) {\n throw new RetryExhaustedError(this.config.retry.maxRetries + 1, lastError);\n }\n throw new Error('Unexpected execution path');\n }\n\n /**\n * Get current metrics summary.\n */\n getMetrics(): MetricsSummary | null {\n return this.metrics?.getSummary() ?? null;\n }\n\n /**\n * Get health status of all resilience components.\n */\n getHealth(): ResilienceHealth {\n return {\n circuitBreaker: {\n state: this.circuitBreaker.getState(),\n timeUntilRetryMs: this.circuitBreaker.getTimeUntilRetry(),\n },\n rateLimiter: {\n availableTokens: this.rateLimiter.availableTokens,\n requestsPerSecond: this.config.rateLimiter.requestsPerSecond,\n },\n metrics: this.getMetrics(),\n };\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * Drip SDK - Usage-based billing for Node.js\n *\n * The official SDK for integrating with the Drip billing platform.\n * Provides methods for managing customers, recording usage, handling charges,\n * and configuring webhooks.\n *\n * @packageDocumentation\n */\n\nimport { StreamMeter, type StreamMeterOptions } from './stream-meter.js';\nimport { deterministicIdempotencyKey } from './idempotency.js';\nimport {\n ResilienceManager,\n type ResilienceConfig,\n type ResilienceHealth,\n type MetricsSummary,\n createDefaultResilienceConfig,\n createDisabledResilienceConfig,\n createHighThroughputResilienceConfig,\n} from './resilience.js';\n\n// ============================================================================\n// Retry Utility\n// ============================================================================\n\n/**\n * Default retry configuration.\n */\nconst DEFAULT_RETRY_CONFIG = {\n maxAttempts: 3,\n baseDelayMs: 100,\n maxDelayMs: 5000,\n} as const;\n\n/**\n * Retry options for API calls.\n */\nexport interface RetryOptions {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxAttempts?: number;\n\n /**\n * Base delay between retries in milliseconds (exponential backoff).\n * @default 100\n */\n baseDelayMs?: number;\n\n /**\n * Maximum delay between retries in milliseconds.\n * @default 5000\n */\n maxDelayMs?: number;\n\n /**\n * Custom function to determine if an error is retryable.\n * By default, retries on network errors and 5xx status codes.\n */\n isRetryable?: (error: unknown) => boolean;\n}\n\n/**\n * Default function to determine if an error is retryable.\n */\nfunction defaultIsRetryable(error: unknown): boolean {\n // Retry on network errors\n if (error instanceof Error) {\n if (error.message.includes('fetch') || error.message.includes('network')) {\n return true;\n }\n }\n\n // Retry on 5xx errors and timeouts\n if (error instanceof DripError) {\n return error.statusCode >= 500 || error.statusCode === 408 || error.statusCode === 429;\n }\n\n return false;\n}\n\n/**\n * Executes a function with exponential backoff retry.\n * @internal\n */\nasync function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs;\n const isRetryable = options.isRetryable ?? defaultIsRetryable;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Don't retry if it's the last attempt or error isn't retryable\n if (attempt === maxAttempts || !isRetryable(error)) {\n throw error;\n }\n\n // Exponential backoff with jitter\n const delay = Math.min(\n baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100,\n maxDelayMs,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n // Should never reach here, but TypeScript needs it\n throw lastError;\n}\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\n/**\n * Configuration options for the Drip SDK client.\n *\n * All fields are optional - the SDK will read from environment variables:\n * - `DRIP_API_KEY` - Your Drip API key\n * - `DRIP_BASE_URL` - Override API base URL (optional)\n */\nexport interface DripConfig {\n /**\n * Your Drip API key. Obtain this from the Drip dashboard.\n * Falls back to `DRIP_API_KEY` environment variable if not provided.\n *\n * Supports both key types:\n * - **Secret keys** (`sk_live_...` / `sk_test_...`): Full access to all endpoints\n * - **Public keys** (`pk_live_...` / `pk_test_...`): Safe for client-side use.\n * Can access usage, customers, charges, sessions, analytics, etc.\n * Cannot access webhook management, API key management, or feature flags.\n *\n * @example \"sk_live_abc123...\" or \"pk_live_abc123...\"\n */\n apiKey?: string;\n\n /**\n * Base URL for the Drip API. Defaults to production API.\n * Falls back to `DRIP_BASE_URL` environment variable if not provided.\n * @default \"https://drip-app-hlunj.ondigitalocean.app/v1\"\n */\n baseUrl?: string;\n\n /**\n * Request timeout in milliseconds.\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Enable production resilience features (rate limiting, retry with backoff,\n * circuit breaker, metrics).\n *\n * - `true`: Use default production settings (100 req/s, 3 retries)\n * - `'high-throughput'`: Optimized for high throughput (1000 req/s, 2 retries)\n * - `ResilienceConfig`: Custom configuration object\n * - `undefined`/`false`: Disabled (default for backward compatibility)\n *\n * @example\n * ```typescript\n * // Enable with defaults\n * const drip = new Drip({ apiKey: '...', resilience: true });\n *\n * // High throughput mode\n * const drip = new Drip({ apiKey: '...', resilience: 'high-throughput' });\n *\n * // Custom config\n * const drip = new Drip({\n * apiKey: '...',\n * resilience: {\n * rateLimiter: { requestsPerSecond: 500, burstSize: 1000, enabled: true },\n * retry: { maxRetries: 5, enabled: true },\n * circuitBreaker: { failureThreshold: 10, enabled: true },\n * collectMetrics: true,\n * },\n * });\n * ```\n */\n resilience?: boolean | 'high-throughput' | Partial<ResilienceConfig>;\n}\n\n// ============================================================================\n// Customer Types\n// ============================================================================\n\n/**\n * Parameters for creating a new customer.\n */\nexport interface CreateCustomerParams {\n /**\n * Your internal customer/user ID for reconciliation.\n * @example \"user_12345\"\n */\n externalCustomerId?: string;\n\n /**\n * The customer's Drip Smart Account address (derived from their EOA).\n * @example \"0x1234567890abcdef...\"\n */\n onchainAddress: string;\n\n /**\n * Additional metadata to store with the customer.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * A Drip customer record.\n */\nexport interface Customer {\n /** Unique customer ID in Drip */\n id: string;\n\n /** Your business ID (optional - may not be returned by all endpoints) */\n businessId?: string;\n\n /** Your external customer ID (if provided) */\n externalCustomerId: string | null;\n\n /** Customer's on-chain address */\n onchainAddress: string;\n\n /** Custom metadata */\n metadata: Record<string, unknown> | null;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of last update */\n updatedAt: string;\n}\n\n/**\n * Options for listing customers.\n */\nexport interface ListCustomersOptions {\n /**\n * Maximum number of customers to return (1-100).\n * @default 100\n */\n limit?: number;\n\n /**\n * Filter by customer status.\n */\n status?: 'ACTIVE' | 'LOW_BALANCE' | 'PAUSED';\n}\n\n/**\n * Response from listing customers.\n */\nexport interface ListCustomersResponse {\n /** Array of customers */\n data: Customer[];\n\n /** Total count returned */\n count: number;\n}\n\n/**\n * Customer balance information.\n */\nexport interface BalanceResult {\n /** Customer ID */\n customerId: string;\n\n /** On-chain address */\n onchainAddress: string;\n\n /** Balance in USDC (6 decimals) - matches backend field name */\n balanceUsdc: string;\n\n /** Pending charges in USDC */\n pendingChargesUsdc: string;\n\n /** Available USDC (balance minus pending) */\n availableUsdc: string;\n\n /** ISO timestamp of last balance sync */\n lastSyncedAt: string | null;\n}\n\n// ============================================================================\n// Usage & Charge Types\n// ============================================================================\n\n/**\n * Parameters for recording usage and charging a customer.\n */\nexport interface ChargeParams {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n * @example \"api_calls\", \"compute_seconds\", \"storage_gb\"\n */\n meter: string;\n\n /**\n * The quantity of usage to record.\n * Will be multiplied by the meter's unit price.\n */\n quantity: number;\n\n /**\n * Unique key to prevent duplicate charges and map each call to a single event.\n * Auto-generated if not provided. Retrying with the same key returns the original charge.\n * @example \"req_abc123\"\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of a successful charge operation.\n */\nexport interface ChargeResult {\n /** Whether the charge was successful */\n success: boolean;\n\n /** The usage event ID */\n usageEventId: string;\n\n /** True if this was an idempotent replay (returned cached result from previous request) */\n isReplay: boolean;\n\n /** Details about the charge */\n charge: {\n /** Unique charge ID */\n id: string;\n\n /** Amount charged in USDC (6 decimals) */\n amountUsdc: string;\n\n /** Amount in native token */\n amountToken: string;\n\n /** Blockchain transaction hash */\n txHash: string;\n\n /** Current status of the charge */\n status: ChargeStatus;\n };\n}\n\n/**\n * Possible charge statuses.\n */\nexport type ChargeStatus =\n | 'PENDING'\n | 'SUBMITTED'\n | 'CONFIRMED'\n | 'FAILED'\n | 'REFUNDED';\n\n/**\n * Parameters for tracking usage without billing.\n * Use this for internal visibility, pilots, or pre-billing tracking.\n */\nexport interface TrackUsageParams {\n /**\n * The Drip customer ID to track usage for.\n * @example \"cust_abc123\"\n */\n customerId: string;\n\n /**\n * The meter/usage type (e.g., 'api_calls', 'tokens').\n */\n meter: string;\n\n /**\n * The quantity of usage to record.\n */\n quantity: number;\n\n /**\n * Unique key to prevent duplicate records.\n */\n idempotencyKey?: string;\n\n /**\n * Human-readable unit label (e.g., 'tokens', 'requests').\n */\n units?: string;\n\n /**\n * Human-readable description of this usage event.\n */\n description?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of tracking usage (no billing).\n */\nexport interface TrackUsageResult {\n /** Whether the usage was recorded */\n success: boolean;\n\n /** The usage event ID */\n usageEventId: string;\n\n /** Customer ID */\n customerId: string;\n\n /** Usage type that was recorded */\n usageType: string;\n\n /** Quantity recorded */\n quantity: number;\n\n /** Whether this customer is internal-only */\n isInternal: boolean;\n\n /** Confirmation message */\n message: string;\n}\n\n/**\n * A detailed charge record.\n */\nexport interface Charge {\n /** Unique charge ID */\n id: string;\n\n /** Associated usage event ID */\n usageId: string;\n\n /** Customer ID */\n customerId: string;\n\n /** Customer details */\n customer: {\n id: string;\n onchainAddress: string;\n externalCustomerId: string | null;\n };\n\n /** Usage event details */\n usageEvent: {\n id: string;\n type: string;\n quantity: string;\n metadata: Record<string, unknown> | null;\n };\n\n /** Amount in USDC */\n amountUsdc: string;\n\n /** Amount in native token */\n amountToken: string;\n\n /** Transaction hash (if submitted) */\n txHash: string | null;\n\n /** Block number (if confirmed) */\n blockNumber: string | null;\n\n /** Current status */\n status: ChargeStatus;\n\n /** Failure reason (if failed) */\n failureReason: string | null;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of confirmation */\n confirmedAt: string | null;\n}\n\n/**\n * Options for listing charges.\n */\nexport interface ListChargesOptions {\n /**\n * Filter by customer ID.\n */\n customerId?: string;\n\n /**\n * Filter by charge status.\n */\n status?: ChargeStatus;\n\n /**\n * Maximum number of charges to return (1-100).\n * @default 100\n */\n limit?: number;\n\n /**\n * Number of charges to skip (for pagination).\n * @default 0\n */\n offset?: number;\n}\n\n/**\n * Response from listing charges.\n */\nexport interface ListChargesResponse {\n /** Array of charges */\n data: Charge[];\n\n /** Total count returned */\n count: number;\n}\n\n// ============================================================================\n// Webhook Types\n// ============================================================================\n\n/**\n * Available webhook event types.\n */\nexport type WebhookEventType =\n | 'customer.balance.low'\n | 'usage.recorded'\n | 'charge.succeeded'\n | 'charge.failed'\n | 'customer.deposit.confirmed'\n | 'customer.withdraw.confirmed'\n | 'customer.usage_cap.reached'\n | 'webhook.endpoint.unhealthy'\n | 'customer.created'\n | 'api_key.created'\n | 'pricing_plan.updated'\n | 'transaction.created'\n | 'transaction.pending'\n | 'transaction.confirmed'\n | 'transaction.failed';\n\n/**\n * Parameters for creating a webhook.\n */\nexport interface CreateWebhookParams {\n /**\n * The URL to send webhook events to.\n * Must be HTTPS in production.\n * @example \"https://api.yourapp.com/webhooks/drip\"\n */\n url: string;\n\n /**\n * Array of event types to subscribe to.\n * @example [\"charge.succeeded\", \"charge.failed\"]\n */\n events: WebhookEventType[];\n\n /**\n * Optional description for the webhook.\n */\n description?: string;\n}\n\n/**\n * A webhook configuration.\n */\nexport interface Webhook {\n /** Unique webhook ID */\n id: string;\n\n /** Webhook endpoint URL */\n url: string;\n\n /** Subscribed event types */\n events: string[];\n\n /** Description */\n description: string | null;\n\n /** Whether the webhook is active */\n isActive: boolean;\n\n /** ISO timestamp of creation */\n createdAt: string;\n\n /** ISO timestamp of last update */\n updatedAt: string;\n\n /** Delivery statistics */\n stats?: {\n totalDeliveries: number;\n successfulDeliveries: number;\n failedDeliveries: number;\n lastDeliveryAt: string | null;\n };\n}\n\n/**\n * Response from creating a webhook.\n */\nexport interface CreateWebhookResponse extends Webhook {\n /**\n * The webhook signing secret.\n * Only returned once at creation time - save it securely!\n */\n secret: string;\n\n /** Reminder to save the secret */\n message: string;\n}\n\n/**\n * Response from listing webhooks.\n */\nexport interface ListWebhooksResponse {\n /** Array of webhooks */\n data: Webhook[];\n\n /** Total count */\n count: number;\n}\n\n/**\n * Response from deleting a webhook.\n */\nexport interface DeleteWebhookResponse {\n /** Whether the deletion was successful */\n success: boolean;\n}\n\n// ============================================================================\n// Checkout Types\n// ============================================================================\n\n/**\n * Parameters for creating a checkout session.\n * This is the primary way to get money into a customer's account.\n */\nexport interface CheckoutParams {\n /**\n * Existing customer ID (optional).\n * If not provided, a new customer is created after payment.\n */\n customerId?: string;\n\n /**\n * Your internal customer/user ID for new customers.\n * Used to link the Drip customer to your system.\n */\n externalCustomerId?: string;\n\n /**\n * Amount in cents (e.g., 5000 = $50.00).\n * Minimum: 500 ($5.00)\n * Maximum: 1000000 ($10,000.00)\n */\n amount: number;\n\n /**\n * URL to redirect after successful payment.\n * Query params will be added: session_id, customer_id, status\n */\n returnUrl: string;\n\n /**\n * URL to redirect if user cancels (optional).\n */\n cancelUrl?: string;\n\n /**\n * Custom metadata to attach to this checkout.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of creating a checkout session.\n */\nexport interface CheckoutResult {\n /** Checkout session ID */\n id: string;\n\n /** URL to redirect user to for payment */\n url: string;\n\n /** ISO timestamp when session expires (30 minutes) */\n expiresAt: string;\n\n /** Amount in USD */\n amountUsd: number;\n}\n\n// ============================================================================\n// Run & Event Types (Execution Ledger)\n// ============================================================================\n\n/**\n * Parameters for creating a new workflow.\n */\nexport interface CreateWorkflowParams {\n /** Human-readable workflow name */\n name: string;\n\n /** URL-safe identifier (lowercase alphanumeric with underscores/hyphens) */\n slug: string;\n\n /** Type of workflow */\n productSurface?: 'API' | 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';\n\n /** Optional description */\n description?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * A workflow definition.\n */\nexport interface Workflow {\n id: string;\n name: string;\n slug: string;\n productSurface: string;\n description: string | null;\n isActive: boolean;\n createdAt: string;\n}\n\n/**\n * Parameters for starting a new agent run.\n */\nexport interface StartRunParams {\n /** Customer ID this run belongs to */\n customerId: string;\n\n /** Workflow ID this run executes */\n workflowId: string;\n\n /** Your external run ID for correlation */\n externalRunId?: string;\n\n /** Correlation ID for distributed tracing */\n correlationId?: string;\n\n /** Parent run ID for nested runs */\n parentRunId?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of starting a run.\n */\nexport interface RunResult {\n id: string;\n customerId: string;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n correlationId: string | null;\n createdAt: string;\n}\n\n/**\n * Parameters for ending/updating a run.\n */\nexport interface EndRunParams {\n /** New status for the run */\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT';\n\n /** Error message if failed */\n errorMessage?: string;\n\n /** Error code for categorization */\n errorCode?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Possible run statuses.\n */\nexport type RunStatus =\n | 'PENDING'\n | 'RUNNING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'TIMEOUT';\n\n/**\n * Parameters for emitting an event to a run.\n */\nexport interface EmitEventParams {\n /** Run ID to attach this event to */\n runId: string;\n\n /** Event type (e.g., \"agent.step\", \"rpc.request\") */\n eventType: string;\n\n /** Quantity of units consumed */\n quantity?: number;\n\n /** Human-readable unit label */\n units?: string;\n\n /** Human-readable description */\n description?: string;\n\n /** Cost in abstract units */\n costUnits?: number;\n\n /** Currency for cost */\n costCurrency?: string;\n\n /** Correlation ID for tracing */\n correlationId?: string;\n\n /** Parent event ID for trace tree */\n parentEventId?: string;\n\n /** OpenTelemetry-style span ID */\n spanId?: string;\n\n /** Idempotency key (auto-generated if not provided) */\n idempotencyKey?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of emitting an event.\n */\nexport interface EventResult {\n id: string;\n runId: string;\n eventType: string;\n quantity: number;\n costUnits: number | null;\n isDuplicate: boolean;\n timestamp: string;\n}\n\n// ============================================================================\n// Meter Types\n// ============================================================================\n\n/**\n * A meter (usage type) from a pricing plan.\n */\nexport interface Meter {\n /** Pricing plan ID */\n id: string;\n\n /** Human-readable name */\n name: string;\n\n /** The meter/usage type identifier (use this in charge() calls) */\n meter: string;\n\n /** Price per unit in USD */\n unitPriceUsd: string;\n\n /** Whether this meter is active */\n isActive: boolean;\n}\n\n/**\n * Response from listing meters.\n */\nexport interface ListMetersResponse {\n /** Array of available meters */\n data: Meter[];\n\n /** Total count */\n count: number;\n}\n\n// ============================================================================\n// Cost Estimation Types\n// ============================================================================\n\n/**\n * Custom pricing map for cost estimation.\n * Maps usage type to unit price (e.g., { \"api_call\": \"0.005\", \"token\": \"0.0001\" })\n */\nexport type CustomPricing = Record<string, string>;\n\n/**\n * Parameters for estimating costs from historical usage events.\n */\nexport interface EstimateFromUsageParams {\n /** Filter to a specific customer (optional) */\n customerId?: string;\n\n /** Start of the period to estimate */\n periodStart: Date | string;\n\n /** End of the period to estimate */\n periodEnd: Date | string;\n\n /** Default price for usage types without pricing plans */\n defaultUnitPrice?: string;\n\n /** Include events that already have charges (default: true) */\n includeChargedEvents?: boolean;\n\n /** Filter to specific usage types */\n usageTypes?: string[];\n\n /** Custom pricing overrides (takes precedence over DB pricing) */\n customPricing?: CustomPricing;\n}\n\n/**\n * A usage item for hypothetical cost estimation.\n */\nexport interface HypotheticalUsageItem {\n /** The usage type (e.g., \"api_call\", \"token\") */\n usageType: string;\n\n /** The quantity of usage */\n quantity: number;\n\n /** Override unit price for this specific item */\n unitPriceOverride?: string;\n}\n\n/**\n * Parameters for estimating costs from hypothetical usage.\n */\nexport interface EstimateFromHypotheticalParams {\n /** List of usage items to estimate */\n items: HypotheticalUsageItem[];\n\n /** Default price for usage types without pricing plans */\n defaultUnitPrice?: string;\n\n /** Custom pricing overrides (takes precedence over DB pricing) */\n customPricing?: CustomPricing;\n}\n\n/**\n * A line item in the cost estimate.\n */\nexport interface CostEstimateLineItem {\n /** The usage type */\n usageType: string;\n\n /** Total quantity */\n quantity: string;\n\n /** Unit price used */\n unitPrice: string;\n\n /** Estimated cost in USDC */\n estimatedCostUsdc: string;\n\n /** Number of events (for usage-based estimates) */\n eventCount?: number;\n\n /** Whether a pricing plan was found for this usage type */\n hasPricingPlan: boolean;\n}\n\n/**\n * Response from cost estimation.\n */\nexport interface CostEstimateResponse {\n /** Business ID (optional - may not be returned by all endpoints) */\n businessId?: string;\n\n /** Customer ID (if filtered) */\n customerId?: string;\n\n /** Period start (for usage-based estimates) */\n periodStart?: string;\n\n /** Period end (for usage-based estimates) */\n periodEnd?: string;\n\n /** Breakdown by usage type */\n lineItems: CostEstimateLineItem[];\n\n /** Subtotal in USDC */\n subtotalUsdc: string;\n\n /** Total estimated cost in USDC */\n estimatedTotalUsdc: string;\n\n /** Currency (always USDC) */\n currency: 'USDC';\n\n /** Indicates this is an estimate, not a charge */\n isEstimate: true;\n\n /** When the estimate was generated */\n generatedAt: string;\n\n /** Notes about the estimate (e.g., missing pricing plans, custom pricing applied) */\n notes: string[];\n}\n\n// ============================================================================\n// Record Run Types (Simplified API)\n// ============================================================================\n\n/**\n * A single event to record in a run.\n */\nexport interface RecordRunEvent {\n /** Event type (e.g., \"agent.step\", \"tool.call\") */\n eventType: string;\n\n /** Quantity of units consumed */\n quantity?: number;\n\n /** Human-readable unit label */\n units?: string;\n\n /** Human-readable description */\n description?: string;\n\n /** Cost in abstract units */\n costUnits?: number;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Parameters for recording a complete run in one call.\n * This is the simplified API that combines workflow, run, and events.\n */\nexport interface RecordRunParams {\n /** Customer ID this run belongs to */\n customerId: string;\n\n /**\n * Workflow identifier. Can be:\n * - An existing workflow ID (e.g., \"wf_abc123\")\n * - A slug that will be auto-created if it doesn't exist (e.g., \"my_agent\")\n */\n workflow: string;\n\n /** Events that occurred during the run */\n events: RecordRunEvent[];\n\n /** Final status of the run */\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT';\n\n /** Error message if status is FAILED */\n errorMessage?: string;\n\n /** Error code if status is FAILED */\n errorCode?: string;\n\n /** Your external run ID for correlation */\n externalRunId?: string;\n\n /** Correlation ID for distributed tracing */\n correlationId?: string;\n\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of recording a run.\n */\nexport interface RecordRunResult {\n /** The created run */\n run: {\n id: string;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n durationMs: number | null;\n };\n\n /** Summary of events created */\n events: {\n created: number;\n duplicates: number;\n };\n\n /** Total cost computed */\n totalCostUnits: string | null;\n\n /** Human-readable summary */\n summary: string;\n}\n\n/**\n * Full run timeline response from GET /runs/:id/timeline.\n */\nexport interface RunTimeline {\n runId: string;\n workflowId: string | null;\n customerId: string;\n status: RunStatus;\n startedAt: string | null;\n endedAt: string | null;\n durationMs: number | null;\n events: Array<{\n id: string;\n eventType: string;\n actionName: string | null;\n outcome: 'SUCCESS' | 'FAILED' | 'PENDING' | 'TIMEOUT' | 'RETRYING';\n explanation: string | null;\n description: string | null;\n timestamp: string;\n durationMs: number | null;\n parentEventId: string | null;\n retryOfEventId: string | null;\n attemptNumber: number;\n retriedByEventId: string | null;\n costUsdc: string | null;\n isRetry: boolean;\n retryChain: {\n totalAttempts: number;\n finalOutcome: string;\n events: string[];\n } | null;\n metadata: {\n usageType: string;\n quantity: number;\n units: string | null;\n } | null;\n }>;\n anomalies: Array<{\n id: string;\n type: string;\n severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n title: string;\n explanation: string;\n relatedEventIds: string[];\n detectedAt: string;\n status: 'OPEN' | 'INVESTIGATING' | 'RESOLVED' | 'FALSE_POSITIVE' | 'IGNORED';\n }>;\n summary: {\n totalEvents: number;\n byType: Record<string, number>;\n byOutcome: Record<string, number>;\n retriedEvents: number;\n failedEvents: number;\n totalCostUsdc: string | null;\n };\n hasMore: boolean;\n nextCursor: string | null;\n}\n\n/**\n * Run details response from GET /runs/:id.\n */\nexport interface RunDetails {\n id: string;\n customerId: string;\n customerName: string | null;\n workflowId: string;\n workflowName: string;\n status: RunStatus;\n startedAt: string | null;\n endedAt: string | null;\n durationMs: number | null;\n errorMessage: string | null;\n errorCode: string | null;\n correlationId: string | null;\n metadata: Record<string, unknown> | null;\n totals: {\n eventCount: number;\n totalQuantity: string;\n totalCostUnits: string;\n };\n _links: {\n timeline: string;\n };\n}\n\n// ============================================================================\n// Wrap API Call Types\n// ============================================================================\n\n/**\n * Parameters for wrapping an external API call with usage tracking.\n * This ensures usage is recorded even if there's a crash/failure after the API call.\n */\nexport interface WrapApiCallParams<T> {\n /**\n * The Drip customer ID to charge.\n */\n customerId: string;\n\n /**\n * The usage meter/type to record against.\n * Must match a meter configured in your pricing plan.\n */\n meter: string;\n\n /**\n * The async function that makes the external API call.\n * This is the call you want to track (e.g., OpenAI, Anthropic, etc.)\n */\n call: () => Promise<T>;\n\n /**\n * Function to extract the usage quantity from the API call result.\n * @example (result) => result.usage.total_tokens\n */\n extractUsage: (result: T) => number;\n\n /**\n * Custom idempotency key prefix.\n * If not provided, a unique key is generated.\n * The key ensures retries don't double-charge.\n */\n idempotencyKey?: string;\n\n /**\n * Additional metadata to attach to this usage event.\n */\n metadata?: Record<string, unknown>;\n\n /**\n * Retry configuration for the Drip charge call.\n * The external API call is NOT retried (only called once).\n */\n retryOptions?: RetryOptions;\n}\n\n/**\n * Result of a wrapped API call.\n */\nexport interface WrapApiCallResult<T> {\n /**\n * The result from the external API call.\n */\n result: T;\n\n /**\n * The charge result from Drip.\n */\n charge: ChargeResult;\n\n /**\n * The idempotency key used (useful for debugging).\n */\n idempotencyKey: string;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * Error thrown by Drip SDK operations.\n */\nexport class DripError extends Error {\n /**\n * Creates a new DripError.\n * @param message - Human-readable error message\n * @param statusCode - HTTP status code from the API\n * @param code - Machine-readable error code\n */\n constructor(\n message: string,\n public statusCode: number,\n public code?: string,\n ) {\n super(message);\n this.name = 'DripError';\n Object.setPrototypeOf(this, DripError.prototype);\n }\n}\n\n// ============================================================================\n// Main SDK Class\n// ============================================================================\n\n/**\n * The main Drip SDK client.\n *\n * @example\n * ```typescript\n * import { Drip } from '@drip-sdk/node';\n *\n * const drip = new Drip({\n * apiKey: process.env.DRIP_API_KEY!,\n * });\n *\n * // Create a customer\n * const customer = await drip.createCustomer({\n * onchainAddress: '0x...',\n * externalCustomerId: 'user_123',\n * });\n *\n * // Record usage and charge\n * const result = await drip.charge({\n * customerId: customer.id,\n * meter: 'api_calls',\n * quantity: 100,\n * });\n *\n * console.log(`Charged ${result.charge.amountUsdc} USDC`);\n * ```\n */\nexport class Drip {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n private readonly resilience: ResilienceManager | null;\n\n /**\n * The type of API key being used.\n *\n * - `'secret'` — Full access (sk_live_... / sk_test_...)\n * - `'public'` — Client-safe, restricted access (pk_live_... / pk_test_...)\n * - `'unknown'` — Key format not recognized (legacy or custom)\n */\n readonly keyType: 'secret' | 'public' | 'unknown';\n\n /**\n * Creates a new Drip SDK client.\n *\n * @param config - Configuration options\n * @throws {Error} If apiKey is not provided\n *\n * @example\n * ```typescript\n * // Basic usage\n * const drip = new Drip({\n * apiKey: 'sk_live_...',\n * });\n *\n * // With production resilience (recommended)\n * const drip = new Drip({\n * apiKey: 'sk_live_...',\n * resilience: true,\n * });\n *\n * // High throughput mode\n * const drip = new Drip({\n * apiKey: 'sk_live_...',\n * resilience: 'high-throughput',\n * });\n * ```\n */\n constructor(config: DripConfig = {}) {\n // Read from config or fall back to environment variables\n const apiKey = config.apiKey ?? (typeof process !== 'undefined' ? process.env.DRIP_API_KEY : undefined);\n const baseUrl = config.baseUrl ?? (typeof process !== 'undefined' ? process.env.DRIP_BASE_URL : undefined);\n\n if (!apiKey) {\n throw new Error(\n 'Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.'\n );\n }\n\n this.apiKey = apiKey;\n this.baseUrl = baseUrl || 'https://drip-app-hlunj.ondigitalocean.app/v1';\n this.timeout = config.timeout || 30000;\n\n // Detect key type from prefix\n if (apiKey.startsWith('sk_')) {\n this.keyType = 'secret';\n } else if (apiKey.startsWith('pk_')) {\n this.keyType = 'public';\n } else {\n this.keyType = 'unknown';\n }\n\n // Setup resilience manager\n if (config.resilience === true) {\n this.resilience = new ResilienceManager(createDefaultResilienceConfig());\n } else if (config.resilience === 'high-throughput') {\n this.resilience = new ResilienceManager(createHighThroughputResilienceConfig());\n } else if (config.resilience && typeof config.resilience === 'object') {\n this.resilience = new ResilienceManager(config.resilience);\n } else {\n this.resilience = null;\n }\n }\n\n /**\n * Asserts that the SDK was initialized with a secret key (sk_).\n * Throws a clear error if a public key is being used for a secret-key-only operation.\n * @internal\n */\n private assertSecretKey(operation: string): void {\n if (this.keyType === 'public') {\n throw new DripError(\n `${operation} requires a secret key (sk_). You are using a public key (pk_), which cannot access this endpoint. ` +\n `Use a secret key for webhook, API key, and feature flag management.`,\n 403,\n 'PUBLIC_KEY_NOT_ALLOWED',\n );\n }\n }\n\n /**\n * Makes an authenticated request to the Drip API.\n * @internal\n */\n private async request<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n // Extract method for metrics\n const method = (options.method ?? 'GET').toUpperCase();\n\n // Use resilience manager if enabled\n if (this.resilience) {\n return this.resilience.execute(\n () => this.rawRequest<T>(path, options),\n method,\n path\n );\n }\n\n return this.rawRequest<T>(path, options);\n }\n\n /**\n * Execute the actual HTTP request (internal).\n * @internal\n */\n private async rawRequest<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n // Handle 204 No Content\n if (res.status === 204) {\n return { success: true } as T;\n }\n\n const data = await res.json();\n\n if (!res.ok) {\n throw new DripError(\n data.message || data.error || 'Request failed',\n res.status,\n data.code,\n );\n }\n\n return data as T;\n } catch (error) {\n if (error instanceof DripError) {\n throw error;\n }\n if (error instanceof Error && error.name === 'AbortError') {\n throw new DripError('Request timed out', 408, 'TIMEOUT');\n }\n throw new DripError(\n error instanceof Error ? error.message : 'Unknown error',\n 0,\n 'UNKNOWN',\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // ==========================================================================\n // Health Check Methods\n // ==========================================================================\n\n /**\n * Pings the Drip API to check connectivity and measure latency.\n *\n * @returns Health status with latency information\n * @throws {DripError} If the request fails or times out\n *\n * @example\n * ```typescript\n * const health = await drip.ping();\n * if (health.ok) {\n * console.log(`API healthy, latency: ${health.latencyMs}ms`);\n * }\n * ```\n */\n async ping(): Promise<{ ok: boolean; status: string; latencyMs: number; timestamp: number }> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n // Safely construct health endpoint URL\n let healthBaseUrl = this.baseUrl;\n if (healthBaseUrl.endsWith('/v1/')) {\n healthBaseUrl = healthBaseUrl.slice(0, -4);\n } else if (healthBaseUrl.endsWith('/v1')) {\n healthBaseUrl = healthBaseUrl.slice(0, -3);\n }\n healthBaseUrl = healthBaseUrl.replace(/\\/+$/, '');\n\n const start = Date.now();\n\n try {\n const response = await fetch(`${healthBaseUrl}/health`, {\n signal: controller.signal,\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n const latencyMs = Date.now() - start;\n\n // Try to parse JSON, but handle non-JSON responses gracefully\n let status = 'unknown';\n let timestamp = Date.now();\n\n try {\n const data = await response.json() as { status?: string; timestamp?: number };\n if (typeof data.status === 'string') {\n status = data.status;\n }\n if (typeof data.timestamp === 'number') {\n timestamp = data.timestamp;\n }\n } catch {\n // Non-JSON response, derive status from HTTP code\n status = response.ok ? 'healthy' : `error:${response.status}`;\n }\n\n // For non-OK HTTP responses, set appropriate status\n if (!response.ok && status === 'unknown') {\n status = `error:${response.status}`;\n }\n\n return {\n ok: response.ok && status === 'healthy',\n status,\n latencyMs,\n timestamp,\n };\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new DripError('Request timed out', 408, 'TIMEOUT');\n }\n throw new DripError(\n error instanceof Error ? error.message : 'Unknown error',\n 0,\n 'UNKNOWN',\n );\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // ==========================================================================\n // Resilience Methods\n // ==========================================================================\n\n /**\n * Get SDK metrics (requires resilience to be enabled).\n *\n * Returns aggregated metrics including success rates, latencies, and errors.\n *\n * @returns Metrics summary or null if resilience is not enabled\n *\n * @example\n * ```typescript\n * const drip = new Drip({ apiKey: '...', resilience: true });\n * // ... make some requests ...\n *\n * const metrics = drip.getMetrics();\n * if (metrics) {\n * console.log(`Success rate: ${metrics.successRate.toFixed(1)}%`);\n * console.log(`P95 latency: ${metrics.p95LatencyMs.toFixed(0)}ms`);\n * }\n * ```\n */\n getMetrics(): MetricsSummary | null {\n return this.resilience?.getMetrics() ?? null;\n }\n\n /**\n * Get SDK health status (requires resilience to be enabled).\n *\n * Returns health status including circuit breaker state and rate limiter status.\n *\n * @returns Health status or null if resilience is not enabled\n *\n * @example\n * ```typescript\n * const drip = new Drip({ apiKey: '...', resilience: true });\n *\n * const health = drip.getHealth();\n * if (health) {\n * console.log(`Circuit: ${health.circuitBreaker.state}`);\n * console.log(`Available tokens: ${health.rateLimiter.availableTokens}`);\n * }\n * ```\n */\n getHealth(): ResilienceHealth | null {\n return this.resilience?.getHealth() ?? null;\n }\n\n // ==========================================================================\n // Customer Methods\n // ==========================================================================\n\n /**\n * Creates a new customer in your Drip account.\n *\n * @param params - Customer creation parameters\n * @returns The created customer\n * @throws {DripError} If creation fails (e.g., duplicate customer)\n *\n * @example\n * ```typescript\n * const customer = await drip.createCustomer({\n * onchainAddress: '0x1234567890abcdef...',\n * externalCustomerId: 'user_123',\n * metadata: { plan: 'pro' },\n * });\n * ```\n */\n async createCustomer(params: CreateCustomerParams): Promise<Customer> {\n return this.request<Customer>('/customers', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Retrieves a customer by their Drip ID.\n *\n * @param customerId - The Drip customer ID\n * @returns The customer details\n * @throws {DripError} If customer not found (404)\n *\n * @example\n * ```typescript\n * const customer = await drip.getCustomer('cust_abc123');\n * console.log(customer.onchainAddress);\n * ```\n */\n async getCustomer(customerId: string): Promise<Customer> {\n return this.request<Customer>(`/customers/${customerId}`);\n }\n\n /**\n * Lists all customers for your business.\n *\n * @param options - Optional filtering and pagination\n * @returns List of customers\n *\n * @example\n * ```typescript\n * // List all customers\n * const { data: customers } = await drip.listCustomers();\n *\n * // List with filters\n * const { data: activeCustomers } = await drip.listCustomers({\n * status: 'ACTIVE',\n * limit: 50,\n * });\n * ```\n */\n async listCustomers(\n options?: ListCustomersOptions,\n ): Promise<ListCustomersResponse> {\n const params = new URLSearchParams();\n\n if (options?.limit) {\n params.set('limit', options.limit.toString());\n }\n if (options?.status) {\n params.set('status', options.status);\n }\n\n const query = params.toString();\n const path = query ? `/customers?${query}` : '/customers';\n\n return this.request<ListCustomersResponse>(path);\n }\n\n /**\n * Gets the current balance for a customer.\n *\n * @param customerId - The Drip customer ID\n * @returns Current balance in USDC and native token\n *\n * @example\n * ```typescript\n * const balance = await drip.getBalance('cust_abc123');\n * console.log(`Balance: ${balance.balanceUSDC} USDC`);\n * ```\n */\n async getBalance(customerId: string): Promise<BalanceResult> {\n return this.request<BalanceResult>(`/customers/${customerId}/balance`);\n }\n\n // ==========================================================================\n // Charge Methods\n // ==========================================================================\n\n /**\n * Records usage and charges a customer.\n *\n * This is the primary method for billing customers. It:\n * 1. Records the usage event\n * 2. Calculates the charge based on your pricing plan\n * 3. Executes the on-chain charge\n *\n * @param params - Charge parameters\n * @returns The charge result\n * @throws {DripError} If charge fails (insufficient balance, invalid customer, etc.)\n *\n * @example\n * ```typescript\n * const result = await drip.charge({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * quantity: 100,\n * idempotencyKey: 'req_unique_123',\n * });\n *\n * if (result.success) {\n * console.log(`Charged ${result.charge.amountUsdc} USDC`);\n * console.log(`TX: ${result.charge.txHash}`);\n * }\n * ```\n */\n async charge(params: ChargeParams): Promise<ChargeResult> {\n const idempotencyKey = params.idempotencyKey\n ?? deterministicIdempotencyKey('chg', params.customerId, params.meter, params.quantity);\n\n return this.request<ChargeResult>('/usage', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n usageType: params.meter,\n quantity: params.quantity,\n idempotencyKey,\n metadata: params.metadata,\n }),\n });\n }\n\n /**\n * Wraps an external API call with guaranteed usage recording.\n *\n * **This solves the crash-before-record problem:**\n * ```typescript\n * // DANGEROUS - usage lost if crash between lines 1 and 2:\n * const response = await openai.chat.completions.create({...}); // line 1\n * await drip.charge({ tokens: response.usage.total_tokens }); // line 2\n *\n * // SAFE - wrapApiCall guarantees recording with retry:\n * const { result } = await drip.wrapApiCall({\n * call: () => openai.chat.completions.create({...}),\n * extractUsage: (r) => r.usage.total_tokens,\n * ...\n * });\n * ```\n *\n * How it works:\n * 1. Generates idempotency key BEFORE the API call\n * 2. Makes the external API call (once, no retry)\n * 3. Records usage in Drip with retry + idempotency\n * 4. If recording fails transiently, retries are safe (no double-charge)\n *\n * @param params - Wrap parameters including the call and usage extractor\n * @returns The API result and charge details\n * @throws {DripError} If the Drip charge fails after retries\n * @throws {Error} If the external API call fails\n *\n * @example\n * ```typescript\n * // OpenAI example\n * const { result, charge } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * call: () => openai.chat.completions.create({\n * model: 'gpt-4',\n * messages: [{ role: 'user', content: 'Hello!' }],\n * }),\n * extractUsage: (r) => r.usage?.total_tokens ?? 0,\n * });\n *\n * console.log(result.choices[0].message.content);\n * console.log(`Charged: ${charge.charge.amountUsdc} USDC`);\n * ```\n *\n * @example\n * ```typescript\n * // Anthropic example\n * const { result, charge } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * call: () => anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * max_tokens: 1024,\n * messages: [{ role: 'user', content: 'Hello!' }],\n * }),\n * extractUsage: (r) => r.usage.input_tokens + r.usage.output_tokens,\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With custom retry options\n * const { result } = await drip.wrapApiCall({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * call: () => fetch('https://api.example.com/expensive'),\n * extractUsage: () => 1, // Fixed cost per call\n * retryOptions: {\n * maxAttempts: 5,\n * baseDelayMs: 200,\n * },\n * });\n * ```\n */\n async wrapApiCall<T>(params: WrapApiCallParams<T>): Promise<WrapApiCallResult<T>> {\n // Generate idempotency key BEFORE the call - this is the key insight!\n // Even if we crash after the API call, retrying with the same key is safe.\n const idempotencyKey = params.idempotencyKey\n ?? `wrap_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;\n\n // Step 1: Make the external API call (no retry - we don't control this)\n const result = await params.call();\n\n // Step 2: Extract usage from the result\n const quantity = params.extractUsage(result);\n\n // Step 3: Record usage in Drip with retry (idempotency makes this safe)\n const charge = await retryWithBackoff(\n () =>\n this.charge({\n customerId: params.customerId,\n meter: params.meter,\n quantity,\n idempotencyKey,\n metadata: params.metadata,\n }),\n params.retryOptions,\n );\n\n return {\n result,\n charge,\n idempotencyKey,\n };\n }\n\n /**\n * Records usage for internal visibility WITHOUT billing.\n *\n * Use this for:\n * - Tracking internal team usage without charging\n * - Pilot programs where you want visibility before billing\n * - Pre-billing tracking before customer has on-chain wallet\n *\n * This does NOT:\n * - Create a Charge record\n * - Require customer balance\n * - Require blockchain/wallet setup\n *\n * For billing, use `charge()` instead.\n *\n * @param params - The usage tracking parameters\n * @returns The tracked usage event\n *\n * @example\n * ```typescript\n * const result = await drip.trackUsage({\n * customerId: 'cust_abc123',\n * meter: 'api_calls',\n * quantity: 100,\n * description: 'API calls during trial period',\n * });\n *\n * console.log(`Tracked: ${result.usageEventId}`);\n * ```\n */\n async trackUsage(params: TrackUsageParams): Promise<TrackUsageResult> {\n const idempotencyKey = params.idempotencyKey\n ?? deterministicIdempotencyKey('track', params.customerId, params.meter, params.quantity);\n\n return this.request<TrackUsageResult>('/usage/internal', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n usageType: params.meter,\n quantity: params.quantity,\n idempotencyKey,\n units: params.units,\n description: params.description,\n metadata: params.metadata,\n }),\n });\n }\n\n /**\n * Retrieves a specific charge by ID.\n *\n * @param chargeId - The charge ID\n * @returns The charge details\n * @throws {DripError} If charge not found (404)\n *\n * @example\n * ```typescript\n * const charge = await drip.getCharge('chg_abc123');\n * console.log(`Status: ${charge.status}`);\n * ```\n */\n async getCharge(chargeId: string): Promise<Charge> {\n return this.request<Charge>(`/charges/${chargeId}`);\n }\n\n /**\n * Lists charges for your business.\n *\n * @param options - Optional filtering and pagination\n * @returns List of charges\n *\n * @example\n * ```typescript\n * // List all charges\n * const { data: charges } = await drip.listCharges();\n *\n * // List charges for a specific customer\n * const { data: customerCharges } = await drip.listCharges({\n * customerId: 'cust_abc123',\n * status: 'CONFIRMED',\n * });\n * ```\n */\n async listCharges(options?: ListChargesOptions): Promise<ListChargesResponse> {\n const params = new URLSearchParams();\n\n if (options?.customerId) {\n params.set('customerId', options.customerId);\n }\n if (options?.status) {\n params.set('status', options.status);\n }\n if (options?.limit) {\n params.set('limit', options.limit.toString());\n }\n if (options?.offset) {\n params.set('offset', options.offset.toString());\n }\n\n const query = params.toString();\n const path = query ? `/charges?${query}` : '/charges';\n\n return this.request<ListChargesResponse>(path);\n }\n\n /**\n * Gets the current status of a charge.\n *\n * Useful for polling charge status after async operations.\n *\n * @param chargeId - The charge ID\n * @returns Current charge status\n *\n * @example\n * ```typescript\n * const status = await drip.getChargeStatus('chg_abc123');\n * if (status.status === 'CONFIRMED') {\n * console.log('Charge confirmed!');\n * }\n * ```\n */\n async getChargeStatus(\n chargeId: string,\n ): Promise<{ status: ChargeStatus; txHash?: string }> {\n return this.request<{ status: ChargeStatus; txHash?: string }>(\n `/charges/${chargeId}/status`,\n );\n }\n\n // ==========================================================================\n // Checkout Methods (Fiat On-Ramp)\n // ==========================================================================\n\n /**\n * Creates a checkout session to add funds to a customer's account.\n *\n * This is the PRIMARY method for getting money into Drip. It returns a URL\n * to a hosted checkout page where customers can pay via:\n * - Bank transfer (ACH) - $0.50 flat fee, 1-2 business days\n * - Debit card - 1.5% fee, instant\n * - Direct USDC - no fee, instant\n *\n * After payment, the customer is redirected to your returnUrl with:\n * - session_id: The checkout session ID\n * - customer_id: The Drip customer ID\n * - status: \"success\" or \"failed\"\n *\n * @param params - Checkout parameters\n * @returns Checkout session with redirect URL\n *\n * @example\n * ```typescript\n * // Basic checkout\n * const { url } = await drip.checkout({\n * customerId: 'cust_abc123',\n * amount: 5000, // $50.00\n * returnUrl: 'https://myapp.com/dashboard',\n * });\n *\n * // Redirect user to checkout\n * res.redirect(url);\n * ```\n *\n * @example\n * ```typescript\n * // Checkout for new customer\n * const { url, id } = await drip.checkout({\n * externalCustomerId: 'user_123', // Your user ID\n * amount: 10000, // $100.00\n * returnUrl: 'https://myapp.com/welcome',\n * metadata: { plan: 'pro' },\n * });\n * ```\n */\n async checkout(params: CheckoutParams): Promise<CheckoutResult> {\n const response = await this.request<{\n id: string;\n url: string;\n expires_at: string;\n amount_usd: number;\n }>('/checkout', {\n method: 'POST',\n body: JSON.stringify({\n customer_id: params.customerId,\n external_customer_id: params.externalCustomerId,\n amount: params.amount,\n return_url: params.returnUrl,\n cancel_url: params.cancelUrl,\n metadata: params.metadata,\n }),\n });\n\n return {\n id: response.id,\n url: response.url,\n expiresAt: response.expires_at,\n amountUsd: response.amount_usd,\n };\n }\n\n // ==========================================================================\n // Webhook Methods\n // ==========================================================================\n\n /**\n * Creates a new webhook endpoint.\n *\n * The webhook secret is only returned once at creation time.\n * Store it securely for verifying webhook signatures.\n *\n * @param config - Webhook configuration\n * @returns The created webhook with its secret\n *\n * @example\n * ```typescript\n * const webhook = await drip.createWebhook({\n * url: 'https://api.yourapp.com/webhooks/drip',\n * events: ['charge.succeeded', 'charge.failed'],\n * description: 'Main webhook endpoint',\n * });\n *\n * // IMPORTANT: Save this secret securely!\n * console.log(`Webhook secret: ${webhook.secret}`);\n * ```\n */\n async createWebhook(\n config: CreateWebhookParams,\n ): Promise<CreateWebhookResponse> {\n this.assertSecretKey('createWebhook()');\n return this.request<CreateWebhookResponse>('/webhooks', {\n method: 'POST',\n body: JSON.stringify(config),\n });\n }\n\n /**\n * Lists all webhook endpoints for your business.\n *\n * @returns List of webhooks with delivery statistics\n *\n * @example\n * ```typescript\n * const { data: webhooks } = await drip.listWebhooks();\n * webhooks.forEach(wh => {\n * console.log(`${wh.url}: ${wh.stats?.successfulDeliveries} successful`);\n * });\n * ```\n */\n async listWebhooks(): Promise<ListWebhooksResponse> {\n this.assertSecretKey('listWebhooks()');\n return this.request<ListWebhooksResponse>('/webhooks');\n }\n\n /**\n * Retrieves a specific webhook by ID.\n *\n * @param webhookId - The webhook ID\n * @returns The webhook details with statistics\n * @throws {DripError} If webhook not found (404)\n *\n * @example\n * ```typescript\n * const webhook = await drip.getWebhook('wh_abc123');\n * console.log(`Events: ${webhook.events.join(', ')}`);\n * ```\n */\n async getWebhook(webhookId: string): Promise<Webhook> {\n this.assertSecretKey('getWebhook()');\n return this.request<Webhook>(`/webhooks/${webhookId}`);\n }\n\n /**\n * Deletes a webhook endpoint.\n *\n * @param webhookId - The webhook ID to delete\n * @returns Success confirmation\n * @throws {DripError} If webhook not found (404)\n *\n * @example\n * ```typescript\n * await drip.deleteWebhook('wh_abc123');\n * console.log('Webhook deleted');\n * ```\n */\n async deleteWebhook(webhookId: string): Promise<DeleteWebhookResponse> {\n this.assertSecretKey('deleteWebhook()');\n return this.request<DeleteWebhookResponse>(`/webhooks/${webhookId}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Tests a webhook by sending a test event.\n *\n * @param webhookId - The webhook ID to test\n * @returns Test result\n *\n * @example\n * ```typescript\n * const result = await drip.testWebhook('wh_abc123');\n * console.log(`Test status: ${result.status}`);\n * ```\n */\n async testWebhook(\n webhookId: string,\n ): Promise<{ message: string; deliveryId: string | null; status: string }> {\n this.assertSecretKey('testWebhook()');\n return this.request<{\n message: string;\n deliveryId: string | null;\n status: string;\n }>(`/webhooks/${webhookId}/test`, {\n method: 'POST',\n });\n }\n\n /**\n * Rotates the signing secret for a webhook.\n *\n * After rotation, update your application to use the new secret.\n *\n * @param webhookId - The webhook ID\n * @returns The new secret\n *\n * @example\n * ```typescript\n * const { secret } = await drip.rotateWebhookSecret('wh_abc123');\n * console.log(`New secret: ${secret}`);\n * // Update your application with the new secret!\n * ```\n */\n async rotateWebhookSecret(\n webhookId: string,\n ): Promise<{ secret: string; message: string }> {\n this.assertSecretKey('rotateWebhookSecret()');\n return this.request<{ secret: string; message: string }>(\n `/webhooks/${webhookId}/rotate-secret`,\n { method: 'POST' },\n );\n }\n\n // ==========================================================================\n // Run & Event Methods (Execution Ledger)\n // ==========================================================================\n\n /**\n * Creates a new workflow definition.\n *\n * @param params - Workflow creation parameters\n * @returns The created workflow\n *\n * @example\n * ```typescript\n * const workflow = await drip.createWorkflow({\n * name: 'Document Processing',\n * slug: 'doc_processing',\n * productSurface: 'AGENT',\n * });\n * ```\n */\n async createWorkflow(params: CreateWorkflowParams): Promise<Workflow> {\n return this.request<Workflow>('/workflows', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Lists all workflows for your business.\n *\n * @returns List of workflows\n */\n async listWorkflows(): Promise<{ data: Workflow[]; count: number }> {\n return this.request<{ data: Workflow[]; count: number }>('/workflows');\n }\n\n /**\n * Starts a new agent run for tracking execution.\n *\n * @param params - Run parameters\n * @returns The started run\n *\n * @example\n * ```typescript\n * const run = await drip.startRun({\n * customerId: 'cust_abc123',\n * workflowId: 'wf_xyz789',\n * correlationId: 'req_unique_123',\n * });\n *\n * // Emit events during execution...\n *\n * await drip.endRun(run.id, { status: 'COMPLETED' });\n * ```\n */\n async startRun(params: StartRunParams): Promise<RunResult> {\n return this.request<RunResult>('/runs', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Ends a run with a final status.\n *\n * @param runId - The run ID to end\n * @param params - End parameters including status\n * @returns Updated run info\n *\n * @example\n * ```typescript\n * await drip.endRun(run.id, {\n * status: 'COMPLETED',\n * });\n *\n * // Or with error:\n * await drip.endRun(run.id, {\n * status: 'FAILED',\n * errorMessage: 'Customer validation failed',\n * errorCode: 'VALIDATION_ERROR',\n * });\n * ```\n */\n async endRun(\n runId: string,\n params: EndRunParams,\n ): Promise<{\n id: string;\n status: RunStatus;\n endedAt: string | null;\n durationMs: number | null;\n eventCount: number;\n totalCostUnits: string | null;\n }> {\n return this.request(`/runs/${runId}`, {\n method: 'PATCH',\n body: JSON.stringify(params),\n });\n }\n\n /**\n * Gets run details with summary totals.\n *\n * For full event history with retry chains and anomalies, use `getRunTimeline()`.\n *\n * @param runId - The run ID\n * @returns Run details with totals\n *\n * @example\n * ```typescript\n * const run = await drip.getRun('run_abc123');\n * console.log(`Status: ${run.status}, Events: ${run.totals.eventCount}`);\n * ```\n */\n async getRun(runId: string): Promise<RunDetails> {\n return this.request<RunDetails>(`/runs/${runId}`);\n }\n\n /**\n * Gets a run's full timeline with events, anomalies, and analytics.\n *\n * This is the key endpoint for debugging \"what happened\" in an execution.\n *\n * @param runId - The run ID\n * @param options - Pagination and filtering options\n * @returns Full timeline with events, anomalies, and summary\n *\n * @example\n * ```typescript\n * const timeline = await drip.getRunTimeline('run_abc123');\n *\n * console.log(`Status: ${timeline.status}`);\n * console.log(`Events: ${timeline.summary.totalEvents}`);\n *\n * for (const event of timeline.events) {\n * console.log(`${event.eventType}: ${event.outcome}`);\n * }\n * ```\n */\n async getRunTimeline(\n runId: string,\n options?: { limit?: number; cursor?: string; includeAnomalies?: boolean; collapseRetries?: boolean },\n ): Promise<RunTimeline> {\n const params = new URLSearchParams();\n if (options?.limit) params.set('limit', options.limit.toString());\n if (options?.cursor) params.set('cursor', options.cursor);\n if (options?.includeAnomalies !== undefined) params.set('includeAnomalies', String(options.includeAnomalies));\n if (options?.collapseRetries !== undefined) params.set('collapseRetries', String(options.collapseRetries));\n\n const query = params.toString();\n const path = query ? `/runs/${runId}/timeline?${query}` : `/runs/${runId}/timeline`;\n\n return this.request<RunTimeline>(path);\n }\n\n /**\n * Emits an event to a run.\n *\n * Each event is assigned a unique idempotency key (auto-generated if not provided).\n * This maps each inference or API call to a single trackable event.\n * Use `Drip.generateIdempotencyKey()` for deterministic key generation.\n *\n * @param params - Event parameters\n * @returns The created event\n *\n * @example\n * ```typescript\n * await drip.emitEvent({\n * runId: run.id,\n * eventType: 'agent.validate',\n * quantity: 1,\n * description: 'Validated prescription format',\n * costUnits: 0.001,\n * });\n * ```\n */\n async emitEvent(params: EmitEventParams): Promise<EventResult> {\n const idempotencyKey = params.idempotencyKey\n ?? deterministicIdempotencyKey('evt', params.runId, params.eventType, params.quantity);\n\n return this.request<EventResult>('/run-events', {\n method: 'POST',\n body: JSON.stringify({ ...params, idempotencyKey }),\n });\n }\n\n /**\n * Emits multiple events in a single request.\n *\n * @param events - Array of events to emit\n * @returns Summary of created events\n *\n * @example\n * ```typescript\n * const result = await drip.emitEventsBatch([\n * { runId: run.id, eventType: 'agent.step1', quantity: 1 },\n * { runId: run.id, eventType: 'agent.step2', quantity: 100, units: 'tokens' },\n * ]);\n *\n * console.log(`Created: ${result.created}, Duplicates: ${result.duplicates}`);\n * ```\n */\n async emitEventsBatch(\n events: Array<Omit<EmitEventParams, 'runId'> & {\n runId?: string;\n customerId?: string;\n workflowId?: string;\n }>,\n ): Promise<{\n success: boolean;\n created: number;\n duplicates: number;\n skipped: number;\n events: Array<{ id: string; eventType: string; isDuplicate: boolean; skipped?: boolean; reason?: string }>;\n }> {\n return this.request('/run-events/batch', {\n method: 'POST',\n body: JSON.stringify({ events }),\n });\n }\n\n // ==========================================================================\n // Simplified API Methods\n // ==========================================================================\n\n /**\n * Lists all available meters (usage types) for your business.\n *\n * Use this to discover what meter names are valid for the `charge()` method.\n * Meters are defined by your pricing plans.\n *\n * @returns List of available meters with their prices\n *\n * @example\n * ```typescript\n * const { data: meters } = await drip.listMeters();\n *\n * console.log('Available meters:');\n * for (const meter of meters) {\n * console.log(` ${meter.meter}: $${meter.unitPriceUsd}/unit`);\n * }\n *\n * // Use in charge():\n * await drip.charge({\n * customerId: 'cust_123',\n * meter: meters[0].meter, // Use a valid meter name\n * quantity: 100,\n * });\n * ```\n */\n async listMeters(): Promise<ListMetersResponse> {\n const response = await this.request<{\n data: Array<{\n id: string;\n name: string;\n unitType: string;\n unitPriceUsd: string;\n isActive: boolean;\n }>;\n count: number;\n }>('/pricing-plans');\n\n return {\n data: response.data.map((plan) => ({\n id: plan.id,\n name: plan.name,\n meter: plan.unitType,\n unitPriceUsd: plan.unitPriceUsd,\n isActive: plan.isActive,\n })),\n count: response.count,\n };\n }\n\n // ==========================================================================\n // Cost Estimation Methods\n // ==========================================================================\n\n /**\n * Estimates costs from historical usage events.\n *\n * Use this to preview what existing usage would cost before creating charges,\n * or to run \"what-if\" scenarios with custom pricing.\n *\n * @param params - Parameters for the estimate\n * @returns Cost estimate with line item breakdown\n *\n * @example\n * ```typescript\n * // Estimate costs for last month's usage\n * const estimate = await drip.estimateFromUsage({\n * periodStart: new Date('2024-01-01'),\n * periodEnd: new Date('2024-01-31'),\n * });\n *\n * console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);\n * ```\n *\n * @example\n * ```typescript\n * // \"What-if\" scenario with custom pricing\n * const estimate = await drip.estimateFromUsage({\n * periodStart: new Date('2024-01-01'),\n * periodEnd: new Date('2024-01-31'),\n * customPricing: {\n * 'api_call': '0.005', // What if we charged $0.005 per call?\n * 'token': '0.0001', // What if we charged $0.0001 per token?\n * },\n * });\n * ```\n */\n async estimateFromUsage(params: EstimateFromUsageParams): Promise<CostEstimateResponse> {\n const periodStart = params.periodStart instanceof Date\n ? params.periodStart.toISOString()\n : params.periodStart;\n const periodEnd = params.periodEnd instanceof Date\n ? params.periodEnd.toISOString()\n : params.periodEnd;\n\n return this.request<CostEstimateResponse>('/dashboard/cost-estimate/from-usage', {\n method: 'POST',\n body: JSON.stringify({\n customerId: params.customerId,\n periodStart,\n periodEnd,\n defaultUnitPrice: params.defaultUnitPrice,\n includeChargedEvents: params.includeChargedEvents,\n usageTypes: params.usageTypes,\n customPricing: params.customPricing,\n }),\n });\n }\n\n /**\n * Estimates costs from hypothetical usage.\n *\n * Use this for \"what-if\" scenarios, budget planning, or to preview\n * costs before usage occurs.\n *\n * @param params - Parameters for the estimate\n * @returns Cost estimate with line item breakdown\n *\n * @example\n * ```typescript\n * // Estimate what 10,000 API calls and 1M tokens would cost\n * const estimate = await drip.estimateFromHypothetical({\n * items: [\n * { usageType: 'api_call', quantity: 10000 },\n * { usageType: 'token', quantity: 1000000 },\n * ],\n * });\n *\n * console.log(`Estimated total: $${estimate.estimatedTotalUsdc}`);\n * for (const item of estimate.lineItems) {\n * console.log(` ${item.usageType}: ${item.quantity} × $${item.unitPrice} = $${item.estimatedCostUsdc}`);\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Compare different pricing scenarios\n * const currentPricing = await drip.estimateFromHypothetical({\n * items: [{ usageType: 'api_call', quantity: 100000 }],\n * });\n *\n * const newPricing = await drip.estimateFromHypothetical({\n * items: [{ usageType: 'api_call', quantity: 100000 }],\n * customPricing: { 'api_call': '0.0005' }, // 50% discount\n * });\n *\n * console.log(`Current: $${currentPricing.estimatedTotalUsdc}`);\n * console.log(`With 50% discount: $${newPricing.estimatedTotalUsdc}`);\n * ```\n */\n async estimateFromHypothetical(params: EstimateFromHypotheticalParams): Promise<CostEstimateResponse> {\n return this.request<CostEstimateResponse>('/dashboard/cost-estimate/hypothetical', {\n method: 'POST',\n body: JSON.stringify({\n items: params.items,\n defaultUnitPrice: params.defaultUnitPrice,\n customPricing: params.customPricing,\n }),\n });\n }\n\n /**\n * Records a complete agent run in a single call.\n *\n * This is the **simplified API** that combines:\n * - Workflow creation (if needed)\n * - Run creation\n * - Event emission\n * - Run completion\n *\n * Use this instead of the individual `startRun()`, `emitEvent()`, `endRun()` calls\n * when you have all the run data available at once.\n *\n * @param params - Run parameters including events\n * @returns The created run with event summary\n *\n * @example\n * ```typescript\n * // Record a complete agent run in one call\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'doc_processing', // Auto-creates if doesn't exist\n * events: [\n * { eventType: 'agent.start', description: 'Started processing' },\n * { eventType: 'tool.ocr', quantity: 3, units: 'pages', costUnits: 0.15 },\n * { eventType: 'tool.validate', quantity: 1, costUnits: 0.05 },\n * { eventType: 'agent.complete', description: 'Finished successfully' },\n * ],\n * status: 'COMPLETED',\n * });\n *\n * console.log(`Run ${result.run.id}: ${result.summary}`);\n * console.log(`Events: ${result.events.created} created`);\n * ```\n *\n * @example\n * ```typescript\n * // Record a failed run with error details\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'doc_processing',\n * events: [\n * { eventType: 'agent.start', description: 'Started processing' },\n * { eventType: 'tool.ocr', quantity: 1, units: 'pages' },\n * { eventType: 'error', description: 'OCR failed: image too blurry' },\n * ],\n * status: 'FAILED',\n * errorMessage: 'OCR processing failed',\n * errorCode: 'OCR_QUALITY_ERROR',\n * });\n * ```\n */\n async recordRun(params: RecordRunParams): Promise<RecordRunResult> {\n const startTime = Date.now();\n\n // Step 1: Ensure workflow exists (get or create)\n let workflowId = params.workflow;\n let workflowName = params.workflow;\n\n // If it looks like a slug (no underscore prefix), try to find/create it\n if (!params.workflow.startsWith('wf_')) {\n try {\n // Try to find existing workflow by slug\n const workflows = await this.listWorkflows();\n const existing = workflows.data.find(\n (w) => w.slug === params.workflow || w.id === params.workflow,\n );\n\n if (existing) {\n workflowId = existing.id;\n workflowName = existing.name;\n } else {\n // Create new workflow with the slug\n const created = await this.createWorkflow({\n name: params.workflow.replace(/[_-]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()),\n slug: params.workflow,\n productSurface: 'AGENT',\n });\n workflowId = created.id;\n workflowName = created.name;\n }\n } catch {\n // Workflow resolution failed — fall back to using the slug directly.\n workflowId = params.workflow;\n }\n }\n\n // Step 2: Create the run\n const run = await this.startRun({\n customerId: params.customerId,\n workflowId,\n externalRunId: params.externalRunId,\n correlationId: params.correlationId,\n metadata: params.metadata,\n });\n\n // Step 3: Emit all events in batch\n let eventsCreated = 0;\n let eventsDuplicates = 0;\n\n if (params.events.length > 0) {\n const batchEvents = params.events.map((event, index) => ({\n runId: run.id,\n eventType: event.eventType,\n quantity: event.quantity,\n units: event.units,\n description: event.description,\n costUnits: event.costUnits,\n metadata: event.metadata,\n idempotencyKey: params.externalRunId\n ? `${params.externalRunId}:${event.eventType}:${index}`\n : deterministicIdempotencyKey('run', run.id, event.eventType, index),\n }));\n\n const batchResult = await this.emitEventsBatch(batchEvents);\n eventsCreated = batchResult.created;\n eventsDuplicates = batchResult.duplicates;\n }\n\n // Step 4: End the run\n const endResult = await this.endRun(run.id, {\n status: params.status,\n errorMessage: params.errorMessage,\n errorCode: params.errorCode,\n });\n\n const durationMs = Date.now() - startTime;\n\n // Build summary\n const eventSummary = params.events.length > 0\n ? `${eventsCreated} events recorded`\n : 'no events';\n const statusEmoji = params.status === 'COMPLETED' ? '✓' : params.status === 'FAILED' ? '✗' : '○';\n const summary = `${statusEmoji} ${workflowName}: ${eventSummary} (${endResult.durationMs ?? durationMs}ms)`;\n\n return {\n run: {\n id: run.id,\n workflowId,\n workflowName,\n status: params.status,\n durationMs: endResult.durationMs,\n },\n events: {\n created: eventsCreated,\n duplicates: eventsDuplicates,\n },\n totalCostUnits: endResult.totalCostUnits,\n summary,\n };\n }\n\n /**\n * Generates a deterministic idempotency key.\n *\n * Use this to ensure \"one logical action = one event\" even with retries.\n * The key is generated from customerId + runId + stepName + sequence.\n *\n * @param params - Key generation parameters\n * @returns A deterministic idempotency key\n *\n * @example\n * ```typescript\n * const key = Drip.generateIdempotencyKey({\n * customerId: 'cust_123',\n * runId: 'run_456',\n * stepName: 'validate_prescription',\n * sequence: 1,\n * });\n *\n * await drip.emitEvent({\n * runId: 'run_456',\n * eventType: 'agent.validate',\n * idempotencyKey: key,\n * });\n * ```\n */\n static generateIdempotencyKey(params: {\n customerId: string;\n runId?: string;\n stepName: string;\n sequence?: number;\n }): string {\n const components = [\n params.customerId,\n params.runId ?? 'no_run',\n params.stepName,\n String(params.sequence ?? 0),\n ];\n\n const str = components.join('|');\n const crypto = require('crypto') as typeof import('crypto');\n const hash = crypto.createHash('sha256').update(str).digest('hex').slice(0, 32);\n\n return `drip_${hash}_${params.stepName.slice(0, 16)}`;\n }\n\n // ==========================================================================\n // Utility Methods\n // ==========================================================================\n\n /**\n * Verifies a webhook signature using HMAC-SHA256.\n *\n * Call this when receiving webhook events to ensure they're authentic.\n * This is an async method that uses the Web Crypto API for secure verification.\n *\n * @param payload - The raw request body (string)\n * @param signature - The x-drip-signature header value\n * @param secret - Your webhook secret\n * @returns Promise resolving to whether the signature is valid\n *\n * @example\n * ```typescript\n * app.post('/webhooks/drip', async (req, res) => {\n * const isValid = await Drip.verifyWebhookSignature(\n * req.rawBody,\n * req.headers['x-drip-signature'],\n * process.env.DRIP_WEBHOOK_SECRET!,\n * );\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook...\n * });\n * ```\n */\n static async verifyWebhookSignature(\n payload: string,\n signature: string,\n secret: string,\n tolerance = 300, // 5 minutes default\n ): Promise<boolean> {\n if (!payload || !signature || !secret) {\n return false;\n }\n\n try {\n // Parse signature format: t=timestamp,v1=hexsignature\n const parts = signature.split(',');\n const timestampPart = parts.find((p) => p.startsWith('t='));\n const signaturePart = parts.find((p) => p.startsWith('v1='));\n\n if (!timestampPart || !signaturePart) {\n return false;\n }\n\n const timestamp = parseInt(timestampPart.slice(2), 10);\n const providedSignature = signaturePart.slice(3);\n\n if (isNaN(timestamp)) {\n return false;\n }\n\n // Check timestamp tolerance\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return false;\n }\n\n // Compute expected signature using timestamp.payload format\n const signaturePayload = `${timestamp}.${payload}`;\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const payloadData = encoder.encode(signaturePayload);\n\n // Get the subtle crypto API - use globalThis.crypto for browsers/edge runtimes,\n // or fall back to Node.js webcrypto for Node.js 18+\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const subtle = globalThis.crypto?.subtle ?? (require('crypto') as typeof import('crypto')).webcrypto.subtle;\n\n // Import the secret as an HMAC key\n const cryptoKey = await subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n\n // Sign the payload\n const signatureBuffer = await subtle.sign(\n 'HMAC',\n cryptoKey,\n payloadData,\n );\n\n // Convert to hex string\n const expectedSignature = Array.from(new Uint8Array(signatureBuffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n // Constant-time comparison to prevent timing attacks\n if (providedSignature.length !== expectedSignature.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < providedSignature.length; i++) {\n result |= providedSignature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n }\n\n return result === 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Synchronously verifies a webhook signature using HMAC-SHA256.\n *\n * This method uses Node.js crypto module and is only available in Node.js environments.\n * For edge runtimes or browsers, use the async `verifyWebhookSignature` method instead.\n *\n * @param payload - The raw request body (string)\n * @param signature - The x-drip-signature header value\n * @param secret - Your webhook secret\n * @returns Whether the signature is valid\n *\n * @example\n * ```typescript\n * app.post('/webhooks/drip', (req, res) => {\n * const isValid = Drip.verifyWebhookSignatureSync(\n * req.rawBody,\n * req.headers['x-drip-signature'],\n * process.env.DRIP_WEBHOOK_SECRET!,\n * );\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook...\n * });\n * ```\n */\n static verifyWebhookSignatureSync(\n payload: string,\n signature: string,\n secret: string,\n tolerance = 300, // 5 minutes default\n ): boolean {\n if (!payload || !signature || !secret) {\n return false;\n }\n\n try {\n // Parse signature format: t=timestamp,v1=hexsignature\n const parts = signature.split(',');\n const timestampPart = parts.find((p) => p.startsWith('t='));\n const signaturePart = parts.find((p) => p.startsWith('v1='));\n\n if (!timestampPart || !signaturePart) {\n return false;\n }\n\n const timestamp = parseInt(timestampPart.slice(2), 10);\n const providedSignature = signaturePart.slice(3);\n\n if (isNaN(timestamp)) {\n return false;\n }\n\n // Check timestamp tolerance\n const now = Math.floor(Date.now() / 1000);\n if (Math.abs(now - timestamp) > tolerance) {\n return false;\n }\n\n // Dynamic import to avoid bundling issues in edge runtimes\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const crypto = require('crypto') as typeof import('crypto');\n\n // Compute expected signature using timestamp.payload format\n const signaturePayload = `${timestamp}.${payload}`;\n const expectedSignature = crypto\n .createHmac('sha256', secret)\n .update(signaturePayload)\n .digest('hex');\n\n // Use timingSafeEqual for constant-time comparison\n const sigBuffer = Buffer.from(providedSignature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n }\n\n /**\n * Generates a webhook signature for testing purposes.\n *\n * This method creates a signature in the same format the Drip backend uses,\n * allowing you to test your webhook handling code locally.\n *\n * @param payload - The webhook payload (JSON string)\n * @param secret - The webhook secret\n * @param timestamp - Optional timestamp (defaults to current time)\n * @returns Signature in format: t=timestamp,v1=hexsignature\n *\n * @example\n * ```typescript\n * const payload = JSON.stringify({ type: 'charge.succeeded', data: {...} });\n * const signature = Drip.generateWebhookSignature(payload, 'whsec_test123');\n *\n * // Use in tests:\n * const isValid = Drip.verifyWebhookSignatureSync(payload, signature, 'whsec_test123');\n * console.log(isValid); // true\n * ```\n */\n static generateWebhookSignature(\n payload: string,\n secret: string,\n timestamp?: number,\n ): string {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const crypto = require('crypto') as typeof import('crypto');\n\n const ts = timestamp ?? Math.floor(Date.now() / 1000);\n const signaturePayload = `${ts}.${payload}`;\n const signature = crypto\n .createHmac('sha256', secret)\n .update(signaturePayload)\n .digest('hex');\n\n return `t=${ts},v1=${signature}`;\n }\n\n // ==========================================================================\n // StreamMeter Factory\n // ==========================================================================\n\n /**\n * Creates a StreamMeter for accumulating usage and charging once.\n *\n * Perfect for LLM token streaming where you want to:\n * - Accumulate tokens locally (no API call per token)\n * - Charge once at the end of the stream\n * - Handle partial failures (charge for what was delivered)\n *\n * @param options - StreamMeter configuration\n * @returns A new StreamMeter instance\n *\n * @example\n * ```typescript\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * });\n *\n * // Accumulate tokens as they stream\n * for await (const chunk of llmStream) {\n * meter.addSync(chunk.tokens);\n * yield chunk;\n * }\n *\n * // Single charge at end\n * const result = await meter.flush();\n * console.log(`Charged ${result.charge?.amountUsdc} for ${result.quantity} tokens`);\n * ```\n *\n * @example\n * ```typescript\n * // With auto-flush threshold\n * const meter = drip.createStreamMeter({\n * customerId: 'cust_abc123',\n * meter: 'tokens',\n * flushThreshold: 10000, // Charge every 10k tokens\n * });\n *\n * for await (const chunk of longStream) {\n * await meter.add(chunk.tokens); // May auto-flush\n * }\n *\n * await meter.flush(); // Final flush for remaining tokens\n * ```\n */\n createStreamMeter(options: StreamMeterOptions): StreamMeter {\n return new StreamMeter(this.charge.bind(this), options);\n }\n}\n\n// Re-export StreamMeter types\nexport { StreamMeter } from './stream-meter.js';\nexport type { StreamMeterOptions, StreamMeterFlushResult } from './stream-meter.js';\n\n// Re-export Resilience types and utilities\nexport {\n ResilienceManager,\n RateLimiter,\n CircuitBreaker,\n MetricsCollector,\n RetryExhaustedError,\n CircuitBreakerOpenError,\n createDefaultResilienceConfig,\n createDisabledResilienceConfig,\n createHighThroughputResilienceConfig,\n calculateBackoff,\n isRetryableError,\n} from './resilience.js';\n\nexport type {\n ResilienceConfig,\n ResilienceHealth,\n RateLimiterConfig,\n RetryConfig,\n CircuitBreakerConfig,\n CircuitState,\n RequestMetrics,\n MetricsSummary,\n} from './resilience.js';\n\n// Default export for convenience\nexport default Drip;\n\n// ============================================================================\n// Pre-initialized Singleton\n// ============================================================================\n\n/**\n * Pre-initialized Drip client singleton.\n *\n * Reads configuration from environment variables:\n * - `DRIP_API_KEY` (required)\n * - `DRIP_BASE_URL` (optional)\n *\n * @example\n * ```typescript\n * import { drip } from '@drip-sdk/node';\n *\n * // One line to track usage\n * await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });\n *\n * // One line to charge\n * await drip.charge({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });\n * ```\n *\n * @throws {Error} on first use if DRIP_API_KEY is not set\n */\nlet _singleton: Drip | null = null;\n\nfunction getSingleton(): Drip {\n if (!_singleton) {\n _singleton = new Drip();\n }\n return _singleton;\n}\n\n/**\n * Pre-initialized Drip client singleton.\n *\n * Uses lazy initialization - only creates the client when first accessed.\n * Reads `DRIP_API_KEY` from environment variables.\n *\n * @example\n * ```typescript\n * import { drip } from '@drip-sdk/node';\n *\n * // Track usage with one line\n * await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });\n *\n * // Charge with one line\n * await drip.charge({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });\n *\n * // Record a run\n * await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'agent-run',\n * events: [{ eventType: 'llm.call', quantity: 1000, units: 'tokens' }],\n * status: 'COMPLETED',\n * });\n * ```\n */\nexport const drip: Drip = new Proxy({} as Drip, {\n get(_target, prop) {\n const instance = getSingleton();\n const value = instance[prop as keyof Drip];\n if (typeof value === 'function') {\n return value.bind(instance);\n }\n return value;\n },\n});\n","/**\n * Drip LangChain integration.\n *\n * This module provides callback handlers for tracking LangChain LLM, tool,\n * chain, and agent usage with automatic billing through the Drip API.\n *\n * @example\n * ```typescript\n * import { DripCallbackHandler } from '@drip-sdk/node/langchain';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const handler = new DripCallbackHandler({\n * apiKey: 'sk_live_...',\n * customerId: 'cus_123',\n * workflow: 'chatbot',\n * });\n *\n * const llm = new ChatOpenAI({\n * callbacks: [handler],\n * });\n *\n * const response = await llm.invoke('Hello, world!');\n * // Usage is automatically tracked and billed\n * ```\n *\n * @packageDocumentation\n */\n\nimport { Drip } from '../index.js';\n\n// =============================================================================\n// Model Pricing\n// =============================================================================\n\n/**\n * Pricing per 1M tokens for a model.\n */\nexport interface ModelPricing {\n /** Cost per 1M input/prompt tokens in USD */\n input: number;\n /** Cost per 1M output/completion tokens in USD */\n output: number;\n}\n\n/**\n * OpenAI pricing per 1M tokens (as of late 2024).\n */\nexport const OPENAI_PRICING: Record<string, ModelPricing> = {\n 'gpt-4o': { input: 2.5, output: 10.0 },\n 'gpt-4o-mini': { input: 0.15, output: 0.6 },\n 'gpt-4-turbo': { input: 10.0, output: 30.0 },\n 'gpt-4': { input: 30.0, output: 60.0 },\n 'gpt-4-32k': { input: 60.0, output: 120.0 },\n 'gpt-3.5-turbo': { input: 0.5, output: 1.5 },\n 'gpt-3.5-turbo-16k': { input: 3.0, output: 4.0 },\n // Embedding models\n 'text-embedding-3-small': { input: 0.02, output: 0.0 },\n 'text-embedding-3-large': { input: 0.13, output: 0.0 },\n 'text-embedding-ada-002': { input: 0.1, output: 0.0 },\n} as const;\n\n/**\n * Anthropic pricing per 1M tokens.\n */\nexport const ANTHROPIC_PRICING: Record<string, ModelPricing> = {\n 'claude-3-5-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-opus': { input: 15.0, output: 75.0 },\n 'claude-3-sonnet': { input: 3.0, output: 15.0 },\n 'claude-3-haiku': { input: 0.25, output: 1.25 },\n 'claude-2.1': { input: 8.0, output: 24.0 },\n 'claude-2.0': { input: 8.0, output: 24.0 },\n 'claude-instant-1.2': { input: 0.8, output: 2.4 },\n} as const;\n\n/**\n * Get pricing for a model by name.\n *\n * @param modelName - The model name/identifier.\n * @returns Pricing object with input/output costs per 1M tokens, or undefined if unknown.\n */\nexport function getModelPricing(modelName: string): ModelPricing | undefined {\n const modelLower = modelName.toLowerCase();\n\n // Check OpenAI models\n for (const [key, pricing] of Object.entries(OPENAI_PRICING)) {\n if (modelLower.includes(key)) {\n return pricing;\n }\n }\n\n // Check Anthropic models\n for (const [key, pricing] of Object.entries(ANTHROPIC_PRICING)) {\n if (modelLower.includes(key)) {\n return pricing;\n }\n }\n\n return undefined;\n}\n\n/**\n * Calculate the cost for a model invocation.\n *\n * @param modelName - The model name.\n * @param inputTokens - Number of input/prompt tokens.\n * @param outputTokens - Number of output/completion tokens.\n * @returns Cost in USD, or undefined if pricing is unknown.\n */\nexport function calculateCost(\n modelName: string,\n inputTokens: number,\n outputTokens: number,\n): number | undefined {\n const pricing = getModelPricing(modelName);\n if (pricing === undefined) {\n return undefined;\n }\n\n const inputCost = (inputTokens / 1_000_000) * pricing.input;\n const outputCost = (outputTokens / 1_000_000) * pricing.output;\n\n return inputCost + outputCost;\n}\n\n// =============================================================================\n// Tracking State Types\n// =============================================================================\n\n/**\n * State for tracking an LLM call.\n */\ninterface LLMCallState {\n runId: string;\n model: string;\n startTime: number;\n prompts: string[];\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n error: string | null;\n}\n\n/**\n * State for tracking a tool call.\n */\ninterface ToolCallState {\n runId: string;\n toolName: string;\n startTime: number;\n inputStr: string;\n outputStr: string;\n error: string | null;\n}\n\n/**\n * State for tracking a chain execution.\n */\ninterface ChainCallState {\n runId: string;\n chainType: string;\n startTime: number;\n inputs: Record<string, unknown>;\n outputs: Record<string, unknown>;\n error: string | null;\n}\n\n/**\n * State for tracking agent execution.\n */\ninterface AgentCallState {\n runId: string;\n startTime: number;\n actions: Array<{\n tool: string;\n toolInput: string;\n log: string | null;\n }>;\n finalOutput: string | null;\n error: string | null;\n}\n\n// =============================================================================\n// LangChain Types (minimal type definitions to avoid direct dependency)\n// =============================================================================\n\n/**\n * Serialized representation from LangChain.\n */\ninterface Serialized {\n name?: string;\n id?: string[];\n}\n\n/**\n * LLM result from LangChain.\n */\ninterface LLMResult {\n llm_output?: {\n token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n } | null;\n}\n\n/**\n * Agent action from LangChain.\n */\ninterface AgentAction {\n tool: string;\n toolInput: string | Record<string, unknown>;\n log: string;\n}\n\n/**\n * Agent finish from LangChain.\n */\ninterface AgentFinish {\n returnValues: Record<string, unknown>;\n log: string;\n}\n\n/**\n * Document from LangChain.\n */\ninterface Document {\n pageContent: string;\n metadata: Record<string, unknown>;\n}\n\n// =============================================================================\n// Callback Handler Configuration\n// =============================================================================\n\n/**\n * Configuration options for DripCallbackHandler.\n */\nexport interface DripCallbackHandlerOptions {\n /**\n * Drip API key. Falls back to DRIP_API_KEY environment variable.\n */\n apiKey?: string;\n\n /**\n * The customer ID to bill usage to.\n * Can be set later via the `customerId` property.\n */\n customerId?: string;\n\n /**\n * Workflow name or ID for grouping runs.\n * @default \"langchain\"\n */\n workflow?: string;\n\n /**\n * Base URL for the Drip API.\n */\n baseUrl?: string;\n\n /**\n * Whether to automatically create runs when LLM calls start.\n * @default true\n */\n autoCreateRun?: boolean;\n\n /**\n * Whether to emit events on errors.\n * @default true\n */\n emitOnError?: boolean;\n\n /**\n * Additional metadata to attach to all events.\n */\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// DripCallbackHandler\n// =============================================================================\n\n/**\n * LangChain callback handler for Drip usage tracking.\n *\n * This handler automatically tracks LLM calls, tool usage, chain executions,\n * and agent actions, emitting events to the Drip API for billing.\n *\n * @example\n * ```typescript\n * import { DripCallbackHandler } from '@drip-sdk/node/langchain';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const handler = new DripCallbackHandler({\n * apiKey: 'sk_live_...',\n * customerId: 'cus_123',\n * workflow: 'chatbot',\n * });\n *\n * const llm = new ChatOpenAI({\n * callbacks: [handler],\n * });\n *\n * const response = await llm.invoke('Hello!');\n * ```\n */\nexport class DripCallbackHandler {\n private readonly _client: Drip;\n private _customerId: string | undefined;\n private readonly _workflow: string;\n private readonly _autoCreateRun: boolean;\n private readonly _emitOnError: boolean;\n private readonly _baseMetadata: Record<string, unknown>;\n\n // Active tracking state\n private _currentRunId: string | null = null;\n private readonly _llmCalls: Map<string, LLMCallState> = new Map();\n private readonly _toolCalls: Map<string, ToolCallState> = new Map();\n private readonly _chainCalls: Map<string, ChainCallState> = new Map();\n private readonly _agentCalls: Map<string, AgentCallState> = new Map();\n\n constructor(options: DripCallbackHandlerOptions = {}) {\n this._client = new Drip({\n apiKey: options.apiKey ?? process.env.DRIP_API_KEY ?? '',\n baseUrl: options.baseUrl,\n });\n this._customerId = options.customerId;\n this._workflow = options.workflow ?? 'langchain';\n this._autoCreateRun = options.autoCreateRun ?? true;\n this._emitOnError = options.emitOnError ?? true;\n this._baseMetadata = options.metadata ?? {};\n }\n\n /**\n * Get the customer ID.\n * @throws Error if customer ID is not set.\n */\n get customerId(): string {\n if (this._customerId === undefined) {\n throw new Error('customerId must be set before using the handler');\n }\n return this._customerId;\n }\n\n /**\n * Set the customer ID.\n */\n set customerId(value: string) {\n this._customerId = value;\n }\n\n /**\n * Get the current run ID.\n */\n get runId(): string | null {\n return this._currentRunId;\n }\n\n /**\n * Manually start a new run.\n *\n * @param options - Run options.\n * @returns The created run ID.\n */\n async startRun(options: {\n externalRunId?: string;\n correlationId?: string;\n metadata?: Record<string, unknown>;\n } = {}): Promise<string> {\n const result = await this._client.recordRun({\n customerId: this.customerId,\n workflow: this._workflow,\n events: [],\n status: 'COMPLETED',\n externalRunId: options.externalRunId,\n correlationId: options.correlationId,\n metadata: { ...this._baseMetadata, ...(options.metadata ?? {}) },\n });\n this._currentRunId = result.run.id;\n return this._currentRunId;\n }\n\n /**\n * Manually end the current run.\n *\n * @param status - Final status.\n * @param errorMessage - Error message for failed runs.\n */\n async endRun(\n status: 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TIMEOUT' = 'COMPLETED',\n errorMessage?: string,\n ): Promise<void> {\n if (this._currentRunId) {\n await this._client.endRun(this._currentRunId, {\n status,\n errorMessage,\n });\n this._currentRunId = null;\n }\n }\n\n /**\n * Ensure a run exists, creating one if autoCreateRun is enabled.\n */\n private async _ensureRun(): Promise<string> {\n if (this._currentRunId === null) {\n if (this._autoCreateRun) {\n const result = await this._client.startRun({\n customerId: this.customerId,\n workflowId: this._workflow,\n metadata: this._baseMetadata,\n });\n this._currentRunId = result.id;\n } else {\n throw new Error('No active run. Call startRun() first.');\n }\n }\n return this._currentRunId;\n }\n\n /**\n * Emit an event to the Drip API.\n */\n private async _emitEvent(params: {\n eventType: string;\n quantity?: number;\n units?: string;\n description?: string;\n costUnits?: number;\n metadata?: Record<string, unknown>;\n idempotencySuffix?: string;\n }): Promise<void> {\n const runId = await this._ensureRun();\n\n let idempotencyKey: string | undefined;\n if (params.idempotencySuffix) {\n idempotencyKey = Drip.generateIdempotencyKey({\n customerId: this.customerId,\n stepName: `${params.eventType}:${params.idempotencySuffix}`,\n runId,\n });\n }\n\n await this._client.emitEvent({\n runId,\n eventType: params.eventType,\n quantity: params.quantity,\n units: params.units,\n description: params.description,\n costUnits: params.costUnits,\n idempotencyKey,\n metadata: { ...this._baseMetadata, ...(params.metadata ?? {}) },\n });\n }\n\n // ===========================================================================\n // LLM Callbacks\n // ===========================================================================\n\n /**\n * Called when LLM starts running.\n */\n handleLLMStart(\n serialized: Serialized,\n prompts: string[],\n runId: string,\n _parentRunId?: string,\n _extraParams?: Record<string, unknown>,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const modelName = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n this._llmCalls.set(runId, {\n runId,\n model: modelName,\n startTime: Date.now(),\n prompts,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n error: null,\n });\n }\n\n /**\n * Called when LLM ends running.\n */\n async handleLLMEnd(\n response: LLMResult,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._llmCalls.get(runId);\n this._llmCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n // Extract token usage\n const tokenUsage = response.llm_output?.token_usage ?? {};\n const inputTokens = tokenUsage.prompt_tokens ?? 0;\n const outputTokens = tokenUsage.completion_tokens ?? 0;\n const totalTokens = tokenUsage.total_tokens ?? (inputTokens + outputTokens);\n\n // Calculate cost\n const cost = calculateCost(state.model, inputTokens, outputTokens);\n\n // Emit event\n await this._emitEvent({\n eventType: 'llm.completion',\n quantity: totalTokens,\n units: 'tokens',\n description: `LLM call to ${state.model}`,\n costUnits: cost,\n metadata: {\n model: state.model,\n inputTokens,\n outputTokens,\n latencyMs,\n promptCount: state.prompts.length,\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when LLM errors.\n */\n async handleLLMError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._llmCalls.get(runId);\n this._llmCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'llm.error',\n quantity: 1,\n units: 'errors',\n description: `LLM error: ${error.name}`,\n metadata: {\n model: state.model,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n /**\n * Called on new LLM token (streaming).\n * Tokens are tracked at completion, not per-token.\n */\n handleLLMNewToken(\n _token: string,\n _idx: { prompt: number; completion: number },\n _runId: string,\n _parentRunId?: string,\n ): void {\n // Tokens tracked at completion, not per-token\n }\n\n // ===========================================================================\n // Chat Model Callbacks\n // ===========================================================================\n\n /**\n * Called when chat model starts running.\n */\n handleChatModelStart(\n serialized: Serialized,\n messages: unknown[][],\n runId: string,\n _parentRunId?: string,\n _extraParams?: Record<string, unknown>,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const modelName = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n // Convert messages to string representation for tracking\n const prompts = messages.map((msgList) => JSON.stringify(msgList));\n\n this._llmCalls.set(runId, {\n runId,\n model: modelName,\n startTime: Date.now(),\n prompts,\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n error: null,\n });\n }\n\n // ===========================================================================\n // Tool Callbacks\n // ===========================================================================\n\n /**\n * Called when tool starts running.\n */\n handleToolStart(\n serialized: Serialized,\n inputStr: string,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const toolName = serialized.name ?? 'unknown_tool';\n\n this._toolCalls.set(runId, {\n runId,\n toolName,\n startTime: Date.now(),\n inputStr: inputStr.slice(0, 1000), // Truncate long inputs\n outputStr: '',\n error: null,\n });\n }\n\n /**\n * Called when tool ends running.\n */\n async handleToolEnd(\n output: string,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'tool.call',\n quantity: 1,\n units: 'calls',\n description: `Tool: ${state.toolName}`,\n metadata: {\n toolName: state.toolName,\n latencyMs,\n inputPreview: state.inputStr.slice(0, 200),\n outputPreview: String(output).slice(0, 200),\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when tool errors.\n */\n async handleToolError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'tool.error',\n quantity: 1,\n units: 'errors',\n description: `Tool error: ${state.toolName}`,\n metadata: {\n toolName: state.toolName,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Chain Callbacks\n // ===========================================================================\n\n /**\n * Called when chain starts running.\n */\n handleChainStart(\n serialized: Serialized,\n inputs: Record<string, unknown>,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const chainType = serialized.name ?? serialized.id?.at(-1) ?? 'unknown';\n\n this._chainCalls.set(runId, {\n runId,\n chainType,\n startTime: Date.now(),\n inputs,\n outputs: {},\n error: null,\n });\n }\n\n /**\n * Called when chain ends running.\n */\n async handleChainEnd(\n outputs: Record<string, unknown>,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._chainCalls.get(runId);\n this._chainCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'chain.execution',\n quantity: 1,\n units: 'executions',\n description: `Chain: ${state.chainType}`,\n metadata: {\n chainType: state.chainType,\n latencyMs,\n inputKeys: Object.keys(state.inputs),\n outputKeys: Object.keys(outputs),\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when chain errors.\n */\n async handleChainError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._chainCalls.get(runId);\n this._chainCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'chain.error',\n quantity: 1,\n units: 'errors',\n description: `Chain error: ${state.chainType}`,\n metadata: {\n chainType: state.chainType,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Agent Callbacks\n // ===========================================================================\n\n /**\n * Called when agent takes an action.\n */\n async handleAgentAction(\n action: AgentAction,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n let state = this._agentCalls.get(runId);\n\n if (!state) {\n state = {\n runId,\n startTime: Date.now(),\n actions: [],\n finalOutput: null,\n error: null,\n };\n this._agentCalls.set(runId, state);\n }\n\n const toolInput = typeof action.toolInput === 'string'\n ? action.toolInput\n : JSON.stringify(action.toolInput);\n\n state.actions.push({\n tool: action.tool,\n toolInput: toolInput.slice(0, 500),\n log: action.log?.slice(0, 500) ?? null,\n });\n\n // Emit action event\n await this._emitEvent({\n eventType: 'agent.action',\n quantity: 1,\n units: 'actions',\n description: `Agent action: ${action.tool}`,\n metadata: {\n tool: action.tool,\n actionCount: state.actions.length,\n },\n idempotencySuffix: `${runId}:${state.actions.length}`,\n });\n }\n\n /**\n * Called when agent finishes.\n */\n async handleAgentEnd(\n finish: AgentFinish,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._agentCalls.get(runId);\n this._agentCalls.delete(runId);\n\n let latencyMs = 0;\n let actionCount = 0;\n\n if (state) {\n latencyMs = Date.now() - state.startTime;\n actionCount = state.actions.length;\n }\n\n await this._emitEvent({\n eventType: 'agent.finish',\n quantity: actionCount || 1,\n units: 'actions',\n description: 'Agent run completed',\n metadata: {\n latencyMs,\n actionCount,\n outputPreview: JSON.stringify(finish.returnValues).slice(0, 500),\n },\n idempotencySuffix: runId,\n });\n }\n\n // ===========================================================================\n // Retriever Callbacks\n // ===========================================================================\n\n /**\n * Called when retriever starts running.\n */\n handleRetrieverStart(\n serialized: Serialized,\n query: string,\n runId: string,\n _parentRunId?: string,\n _tags?: string[],\n _metadata?: Record<string, unknown>,\n ): void {\n const retrieverName = serialized.name ?? 'retriever';\n\n // Track as a tool call\n this._toolCalls.set(runId, {\n runId,\n toolName: `retriever:${retrieverName}`,\n startTime: Date.now(),\n inputStr: query.slice(0, 1000),\n outputStr: '',\n error: null,\n });\n }\n\n /**\n * Called when retriever ends running.\n */\n async handleRetrieverEnd(\n documents: Document[],\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n const latencyMs = Date.now() - state.startTime;\n\n await this._emitEvent({\n eventType: 'retriever.query',\n quantity: documents.length,\n units: 'documents',\n description: `Retriever: ${state.toolName}`,\n metadata: {\n retriever: state.toolName,\n queryPreview: state.inputStr.slice(0, 200),\n documentCount: documents.length,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n\n /**\n * Called when retriever errors.\n */\n async handleRetrieverError(\n error: Error,\n runId: string,\n _parentRunId?: string,\n ): Promise<void> {\n const state = this._toolCalls.get(runId);\n this._toolCalls.delete(runId);\n\n if (!state) {\n return;\n }\n\n if (this._emitOnError) {\n const latencyMs = Date.now() - state.startTime;\n await this._emitEvent({\n eventType: 'retriever.error',\n quantity: 1,\n units: 'errors',\n description: `Retriever error: ${state.toolName}`,\n metadata: {\n retriever: state.toolName,\n errorType: error.name,\n errorMessage: error.message,\n latencyMs,\n },\n idempotencySuffix: runId,\n });\n }\n }\n\n // ===========================================================================\n // Text Callbacks\n // ===========================================================================\n\n /**\n * Called when arbitrary text is received.\n * Optional: override to track text events if needed.\n */\n handleText(\n _text: string,\n _runId: string,\n _parentRunId?: string,\n ): void {\n // Optional: track text events if needed\n }\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport {\n DripCallbackHandler as default,\n};\n"]}