@drip-sdk/node 1.0.10 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.js ADDED
@@ -0,0 +1,3 @@
1
+ import {createHash}from'crypto';var k=0;function y(m,...t){let e=++k,n=t.filter(r=>r!==void 0).map(String);n.push(String(e));let s=createHash("sha256").update(n.join("|")).digest("hex").slice(0,24);return `${m}_${s}`}var u=class m extends Error{constructor(e,n,s){super(e);this.statusCode=n;this.code=s;this.name="DripError",Object.setPrototypeOf(this,m.prototype);}},f=class{apiKey;baseUrl;timeout;keyType;constructor(t={}){let e=t.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),n=t.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!e)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=e,this.baseUrl=n||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=t.timeout||3e4,e.startsWith("sk_")?this.keyType="secret":e.startsWith("pk_")?this.keyType="public":this.keyType="unknown";}async request(t,e={}){let n=new AbortController,s=setTimeout(()=>n.abort(),this.timeout);try{let r=await fetch(`${this.baseUrl}${t}`,{...e,signal:n.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...e.headers}});if(r.status===204)return {success:!0};let o=await r.json();if(!r.ok)throw new u(o.message||o.error||"Request failed",r.status,o.code);return o}catch(r){throw r instanceof u?r:r instanceof Error&&r.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(r instanceof Error?r.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(s);}}async ping(){let t=new AbortController,e=setTimeout(()=>t.abort(),this.timeout),n=this.baseUrl;n.endsWith("/v1/")?n=n.slice(0,-4):n.endsWith("/v1")&&(n=n.slice(0,-3)),n=n.replace(/\/+$/,"");let s=Date.now();try{let r=await fetch(`${n}/health`,{signal:t.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),o=Date.now()-s,a="unknown",l=Date.now();try{let c=await r.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(l=c.timestamp);}catch{a=r.ok?"healthy":`error:${r.status}`;}return !r.ok&&a==="unknown"&&(a=`error:${r.status}`),{ok:r.ok&&a==="healthy",status:a,latencyMs:o,timestamp:l}}catch(r){throw r instanceof Error&&r.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(r instanceof Error?r.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(e);}}async createCustomer(t){return this.request("/customers",{method:"POST",body:JSON.stringify(t)})}async getCustomer(t){return this.request(`/customers/${t}`)}async listCustomers(t){let e=new URLSearchParams;t?.limit&&e.set("limit",t.limit.toString()),t?.status&&e.set("status",t.status);let n=e.toString(),s=n?`/customers?${n}`:"/customers";return this.request(s)}async trackUsage(t){let e=t.idempotencyKey??y("track",t.customerId,t.meter,t.quantity);return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:t.customerId,usageType:t.meter,quantity:t.quantity,idempotencyKey:e,units:t.units,description:t.description,metadata:t.metadata})})}async createWorkflow(t){return this.request("/workflows",{method:"POST",body:JSON.stringify(t)})}async listWorkflows(){return this.request("/workflows")}async startRun(t){return this.request("/runs",{method:"POST",body:JSON.stringify(t)})}async endRun(t,e){return this.request(`/runs/${t}`,{method:"PATCH",body:JSON.stringify(e)})}async getRun(t){return this.request(`/runs/${t}`)}async getRunTimeline(t,e){let n=new URLSearchParams;e?.limit&&n.set("limit",e.limit.toString()),e?.cursor&&n.set("cursor",e.cursor),e?.includeAnomalies!==void 0&&n.set("includeAnomalies",String(e.includeAnomalies)),e?.collapseRetries!==void 0&&n.set("collapseRetries",String(e.collapseRetries));let s=n.toString(),r=s?`/runs/${t}/timeline?${s}`:`/runs/${t}/timeline`;return this.request(r)}async emitEvent(t){let e=t.idempotencyKey??y("evt",t.runId,t.eventType,t.quantity);return this.request("/run-events",{method:"POST",body:JSON.stringify({...t,idempotencyKey:e})})}async emitEventsBatch(t){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:t})})}async recordRun(t){let e=Date.now(),n=t.workflow,s=t.workflow;if(!t.workflow.startsWith("wf_"))try{let d=(await this.listWorkflows()).data.find(i=>i.slug===t.workflow||i.id===t.workflow);if(d)n=d.id,s=d.name;else {let i=await this.createWorkflow({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,g=>g.toUpperCase()),slug:t.workflow,productSurface:"CUSTOM"});n=i.id,s=i.name;}}catch{n=t.workflow;}let r=await this.startRun({customerId:t.customerId,workflowId:n,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata}),o=0,a=0;if(t.events.length>0){let w=t.events.map((i,g)=>({runId:r.id,eventType:i.eventType,quantity:i.quantity,units:i.units,description:i.description,costUnits:i.costUnits,metadata:i.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${i.eventType}:${g}`:y("run",r.id,i.eventType,g)})),d=await this.emitEventsBatch(w);o=d.created,a=d.duplicates;}let l=await this.endRun(r.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),c=Date.now()-e,R=t.events.length>0?`${o} events recorded`:"no events",h=`${t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB"} ${s}: ${R} (${l.durationMs??c}ms)`;return {run:{id:r.id,workflowId:n,workflowName:s,status:t.status,durationMs:l.durationMs},events:{created:o,duplicates:a},totalCostUnits:l.totalCostUnits,summary:h}}},U=f,p=null;function C(){return p||(p=new f),p}var O=new Proxy({},{get(m,t){let e=C(),n=e[t];return typeof n=="function"?n.bind(e):n}});
2
+ export{f as Drip,u as DripError,U as default,O as drip};//# sourceMappingURL=core.js.map
3
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/idempotency.ts","../src/core.ts"],"names":["_callCounter","deterministicIdempotencyKey","prefix","components","seq","parts","c","hash","createHash","DripError","_DripError","message","statusCode","code","Drip","config","apiKey","baseUrl","path","options","controller","timeoutId","res","data","error","healthBaseUrl","start","response","latencyMs","status","timestamp","params","customerId","query","idempotencyKey","runId","events","startTime","workflowId","workflowName","existing","w","created","run","eventsCreated","eventsDuplicates","batchEvents","event","index","batchResult","endResult","durationMs","eventSummary","summary","core_default","_singleton","getSingleton","drip","_target","prop","instance","value"],"mappings":"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","file":"core.js","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 * Drip SDK Core - Essential API for pilots and new integrations\n *\n * This SDK focuses on two core concepts:\n * - **Usage tracking**: trackUsage() for recording usage without billing\n * - **Execution logging**: recordRun() and related methods for tracking runs/events\n *\n * For billing, webhooks, cost estimation, and advanced features:\n * `import { Drip } from '@drip-sdk/node'`\n *\n * @packageDocumentation\n */\n\nimport { deterministicIdempotencyKey } from './idempotency.js';\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 tracking, customers, runs, and events.\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// ============================================================================\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// Usage Tracking Types\n// ============================================================================\n\n/**\n * Parameters for tracking usage without billing.\n */\nexport interface TrackUsageParams {\n /**\n * The Drip customer ID to track usage for.\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 * Auto-generated if not provided, ensuring every call is individually trackable.\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// Run & Event Types (Execution Ledger)\n// ============================================================================\n\n/**\n * Parameters for starting a new 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 * Possible run statuses.\n */\nexport type RunStatus =\n | 'PENDING'\n | 'RUNNING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'TIMEOUT';\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 * Result of ending a run.\n */\nexport interface EndRunResult {\n id: string;\n status: RunStatus;\n endedAt: string | null;\n durationMs: number | null;\n eventCount: number;\n totalCostUnits: string | null;\n}\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., \"request.start\", \"llm.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 /** 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 */\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 * A single event to record in a run.\n */\nexport interface RecordRunEvent {\n /** Event type (e.g., \"request.start\", \"llm.call\", \"request.end\") */\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 */\nexport interface RecordRunParams {\n /** Customer ID this run belongs to */\n customerId: string;\n\n /**\n * Workflow/request type identifier. Examples:\n * - \"rpc-request\" for RPC providers\n * - \"api-request\" for API providers\n * - \"agent-run\" for AI agents\n *\n * Auto-creates if it doesn't exist.\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// Internal Types (used by recordRun)\n// ============================================================================\n\ninterface 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\ninterface CreateWorkflowParams {\n name: string;\n slug: string;\n productSurface?: 'API' | 'RPC' | 'WEBHOOK' | 'AGENT' | 'PIPELINE' | 'CUSTOM';\n description?: string;\n metadata?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * Error thrown by Drip SDK operations.\n */\nexport class DripError extends Error {\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// Core SDK Class\n// ============================================================================\n\n/**\n * Drip SDK Core - Essential API for pilots and new integrations.\n *\n * Two core concepts:\n * - **Usage tracking**: `trackUsage()` - record usage without billing\n * - **Execution logging**: `recordRun()` - track request/run lifecycle with events\n *\n * For billing (`charge()`), webhooks, and advanced features:\n * ```typescript\n * import { Drip } from '@drip-sdk/node';\n * ```\n *\n * @example\n * ```typescript\n * import { Drip } from '@drip-sdk/node/core';\n *\n * const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });\n *\n * // Verify connection\n * const health = await drip.ping();\n * console.log(`API healthy: ${health.ok}`);\n *\n * // Track usage (no billing)\n * await drip.trackUsage({\n * customerId: 'cust_123',\n * meter: 'api_calls',\n * quantity: 1,\n * });\n *\n * // Record a complete request/run with events\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'rpc-request', // or 'api-request', 'agent-run'\n * events: [\n * { eventType: 'request.start' },\n * { eventType: 'llm.call', quantity: 1500, units: 'tokens' },\n * { eventType: 'request.end' },\n * ],\n * status: 'COMPLETED',\n * });\n *\n * console.log(result.summary);\n * ```\n */\nexport class Drip {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\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 (all optional, reads from env vars)\n * @throws {Error} If apiKey is not provided and DRIP_API_KEY env var is not set\n *\n * @example\n * ```typescript\n * // Option 1: Explicit config\n * const drip = new Drip({ apiKey: 'your-api-key' });\n *\n * // Option 2: Auto-config from environment (recommended)\n * // Set DRIP_API_KEY env var, then:\n * const drip = new Drip();\n *\n * // Option 3: Use pre-initialized singleton\n * import { drip } from '@drip-sdk/node';\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\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 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 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\n // ==========================================================================\n\n /**\n * Pings the Drip API to check connectivity and measure latency.\n *\n * Use this to verify:\n * - API key is valid\n * - Base URL is correct\n * - Network connectivity works\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 * } else {\n * console.error(`API unhealthy: ${health.status}`);\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 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 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 status = response.ok ? 'healthy' : `error:${response.status}`;\n }\n\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 // 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 * });\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 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 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 // Usage Tracking (No Billing)\n // ==========================================================================\n\n /**\n * Records usage for tracking WITHOUT billing.\n *\n * Use this for:\n * - Pilot programs (track before billing)\n * - Internal team usage\n * - Pre-billing tracking before customer setup\n *\n * For actual billing, use `charge()` from the full SDK.\n *\n * @param params - 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 pilot',\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 // Private Workflow Methods (used by recordRun)\n // ==========================================================================\n\n private async createWorkflow(params: CreateWorkflowParams): Promise<Workflow> {\n return this.request<Workflow>('/workflows', {\n method: 'POST',\n body: JSON.stringify(params),\n });\n }\n\n private async listWorkflows(): Promise<{ data: Workflow[]; count: number }> {\n return this.request<{ data: Workflow[]; count: number }>('/workflows');\n }\n\n // ==========================================================================\n // Run & Event Methods (Execution Ledger)\n // ==========================================================================\n\n /**\n * Starts a new 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 * });\n *\n * // Emit events during execution...\n * await drip.emitEvent({ runId: run.id, eventType: 'llm.call', quantity: 1000 });\n *\n * // End the run\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 async endRun(runId: string, params: EndRunParams): Promise<EndRunResult> {\n return this.request<EndRunResult>(`/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 * @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 * @param params - Event parameters\n * @returns The created event\n *\n * @example\n * ```typescript\n * await drip.emitEvent({\n * runId: run.id,\n * eventType: 'llm.call',\n * quantity: 1500,\n * units: 'tokens',\n * description: 'GPT-4 completion',\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 async emitEventsBatch(\n events: Array<EmitEventParams>,\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 * Records a complete request/run in a single call.\n *\n * This is the **hero method** for tracking execution. It combines:\n * - Workflow creation (auto-creates if needed)\n * - Run creation\n * - Event emission\n * - Run completion\n *\n * @param params - Run parameters including events\n * @returns The created run with event summary\n *\n * @example\n * ```typescript\n * // RPC provider example\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'rpc-request',\n * events: [\n * { eventType: 'request.start' },\n * { eventType: 'eth_call', quantity: 1 },\n * { eventType: 'request.end' },\n * ],\n * status: 'COMPLETED',\n * });\n *\n * // API provider example\n * const result = await drip.recordRun({\n * customerId: 'cust_123',\n * workflow: 'api-request',\n * events: [\n * { eventType: 'request.start' },\n * { eventType: 'llm.call', quantity: 2000, units: 'tokens' },\n * { eventType: 'request.end' },\n * ],\n * status: 'COMPLETED',\n * });\n *\n * console.log(result.summary);\n * // Output: \"✓ Rpc Request: 3 events recorded (152ms)\"\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 (!params.workflow.startsWith('wf_')) {\n try {\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 const created = await this.createWorkflow({\n name: params.workflow.replace(/[_-]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()),\n slug: params.workflow,\n productSurface: 'CUSTOM',\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// 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 *\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 * // 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"]}
package/dist/express.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var crypto=require('crypto');var E=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var w=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,r){this._chargeFn=e,this._options=r;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isReplay:false};let r=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:void 0,s=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:r,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let i={success:s.success,quantity:e,charge:s.charge,isReplay:s.isReplay};return this._options.onFlush?.(i),i}reset(){this._total=0;}};var I={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function U(t){return t instanceof Error&&(t.message.includes("fetch")||t.message.includes("network"))?true:t instanceof f?t.statusCode>=500||t.statusCode===408||t.statusCode===429:false}async function N(t,e={}){let r=e.maxAttempts??I.maxAttempts,s=e.baseDelayMs??I.baseDelayMs,i=e.maxDelayMs??I.maxDelayMs,n=e.isRetryable??U,o;for(let a=1;a<=r;a++)try{return await t()}catch(c){if(o=c,a===r||!n(c))throw c;let m=Math.min(s*Math.pow(2,a-1)+Math.random()*100,i);await new Promise(d=>setTimeout(d,m));}throw o}var f=class t extends Error{constructor(r,s,i){super(r);this.statusCode=s;this.code=i;this.name="DripError",Object.setPrototypeOf(this,t.prototype);}},P=class{apiKey;baseUrl;timeout;constructor(e){if(!e.apiKey)throw new Error("Drip API key is required");this.apiKey=e.apiKey,this.baseUrl=e.baseUrl||"https://api.drip.dev/v1",this.timeout=e.timeout||3e4;}async request(e,r={}){let s=new AbortController,i=setTimeout(()=>s.abort(),this.timeout);try{let n=await fetch(`${this.baseUrl}${e}`,{...r,signal:s.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...r.headers}});if(n.status===204)return {success:!0};let o=await n.json();if(!n.ok)throw new f(o.message||o.error||"Request failed",n.status,o.code);return o}catch(n){throw n instanceof f?n:n instanceof Error&&n.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(n instanceof Error?n.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(i);}}async ping(){let e=new AbortController,r=setTimeout(()=>e.abort(),this.timeout),s=this.baseUrl;s.endsWith("/v1/")?s=s.slice(0,-4):s.endsWith("/v1")&&(s=s.slice(0,-3)),s=s.replace(/\/+$/,"");let i=Date.now();try{let n=await fetch(`${s}/health`,{signal:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),o=Date.now()-i,a="unknown",c=Date.now();try{let m=await n.json();typeof m.status=="string"&&(a=m.status),typeof m.timestamp=="number"&&(c=m.timestamp);}catch{a=n.ok?"healthy":`error:${n.status}`;}return !n.ok&&a==="unknown"&&(a=`error:${n.status}`),{ok:n.ok&&a==="healthy",status:a,latencyMs:o,timestamp:c}}catch(n){throw n instanceof Error&&n.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(n instanceof Error?n.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(r);}}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let r=new URLSearchParams;e?.limit&&r.set("limit",e.limit.toString()),e?.status&&r.set("status",e.status);let s=r.toString(),i=s?`/customers?${s}`:"/customers";return this.request(i)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:e.idempotencyKey,metadata:e.metadata})})}async wrapApiCall(e){let r=e.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,s=await e.call(),i=e.extractUsage(s),n=await N(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:i,idempotencyKey:r,metadata:e.metadata}),e.retryOptions);return {result:s,charge:n,idempotencyKey:r}}async trackUsage(e){return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:e.idempotencyKey,units:e.units,description:e.description,metadata:e.metadata})})}async getCharge(e){return this.request(`/charges/${e}`)}async listCharges(e){let r=new URLSearchParams;e?.customerId&&r.set("customerId",e.customerId),e?.status&&r.set("status",e.status),e?.limit&&r.set("limit",e.limit.toString()),e?.offset&&r.set("offset",e.offset.toString());let s=r.toString(),i=s?`/charges?${s}`:"/charges";return this.request(i)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let r=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:r.id,url:r.url,expiresAt:r.expires_at,amountUsd:r.amount_usd}}async createWebhook(e){return this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.request("/webhooks")}async getWebhook(e){return this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,r){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(r)})}async getRunTimeline(e){return this.request(`/runs/${e}`)}async emitEvent(e){return this.request("/events",{method:"POST",body:JSON.stringify(e)})}async emitEventsBatch(e){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:e})})}async listMeters(){let e=await this.request("/pricing-plans");return {data:e.data.map(r=>({id:r.id,name:r.name,meter:r.unitType,unitPriceUsd:r.unitPriceUsd,isActive:r.isActive})),count:e.count}}async recordRun(e){let r=Date.now(),s=e.workflow,i=e.workflow;if(!e.workflow.startsWith("wf_"))try{let g=(await this.listWorkflows()).data.find(l=>l.slug===e.workflow||l.id===e.workflow);if(g)s=g.id,i=g.name;else {let l=await this.createWorkflow({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,x=>x.toUpperCase()),slug:e.workflow,productSurface:"AGENT"});s=l.id,i=l.name;}}catch{s=e.workflow;}let n=await this.startRun({customerId:e.customerId,workflowId:s,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata}),o=0,a=0;if(e.events.length>0){let p=e.events.map((l,x)=>({runId:n.id,eventType:l.eventType,quantity:l.quantity,units:l.units,description:l.description,costUnits:l.costUnits,metadata:l.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${l.eventType}:${x}`:void 0})),g=await this.emitEventsBatch(p);o=g.created,a=g.duplicates;}let c=await this.endRun(n.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),m=Date.now()-r,d=e.events.length>0?`${o} events recorded`:"no events",y=`${e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB"} ${i}: ${d} (${c.durationMs??m}ms)`;return {run:{id:n.id,workflowId:s,workflowName:i,status:e.status,durationMs:c.durationMs},events:{created:o,duplicates:a},totalCostUnits:c.totalCostUnits,summary:y}}static generateIdempotencyKey(e){let r=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)],s=0,i=r.join("|");for(let n=0;n<i.length;n++){let o=i.charCodeAt(n);s=(s<<5)-s+o,s=s&s;}return `drip_${Math.abs(s).toString(36)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,r,s,i=300){if(!e||!r||!s)return false;try{let n=r.split(","),o=n.find(b=>b.startsWith("t=")),a=n.find(b=>b.startsWith("v1="));if(!o||!a)return !1;let c=parseInt(o.slice(2),10),m=a.slice(3);if(isNaN(c))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-c)>i)return !1;let u=`${c}.${e}`,y=new TextEncoder,p=y.encode(s),g=y.encode(u),l=globalThis.crypto?.subtle??E("crypto").webcrypto.subtle,x=await l.importKey("raw",p,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),O=await l.sign("HMAC",x,g),k=Array.from(new Uint8Array(O)).map(b=>b.toString(16).padStart(2,"0")).join("");if(m.length!==k.length)return !1;let q=0;for(let b=0;b<m.length;b++)q|=m.charCodeAt(b)^k.charCodeAt(b);return q===0}catch{return false}}static verifyWebhookSignatureSync(e,r,s,i=300){if(!e||!r||!s)return false;try{let n=r.split(","),o=n.find(x=>x.startsWith("t=")),a=n.find(x=>x.startsWith("v1="));if(!o||!a)return !1;let c=parseInt(o.slice(2),10),m=a.slice(3);if(isNaN(c))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-c)>i)return !1;let u=E("crypto"),y=`${c}.${e}`,p=u.createHmac("sha256",s).update(y).digest("hex"),g=Buffer.from(m),l=Buffer.from(p);return g.length!==l.length?!1:u.timingSafeEqual(g,l)}catch{return false}}static generateWebhookSignature(e,r,s){let i=E("crypto"),n=s??Math.floor(Date.now()/1e3),o=`${n}.${e}`,a=i.createHmac("sha256",r).update(o).digest("hex");return `t=${n},v1=${a}`}createStreamMeter(e){return new w(this.charge.bind(this),e)}};var h=class t extends Error{constructor(r,s,i,n){super(r);this.code=s;this.statusCode=i;this.details=n;this.name="DripMiddlewareError",Object.setPrototypeOf(this,t.prototype);}};var L=300,K=300,$=["x-payment-signature","x-payment-session-key","x-payment-smart-account","x-payment-timestamp","x-payment-amount","x-payment-recipient","x-payment-usage-id","x-payment-nonce"];function F(t){return t.toLowerCase()}function R(t,e){let r=F(e);if(t[r]!==void 0){let s=t[r];return Array.isArray(s)?s[0]:s}for(let[s,i]of Object.entries(t))if(s.toLowerCase()===r)return Array.isArray(i)?i[0]:i}function D(t){return $.every(e=>R(t,e)!==void 0)}function X(t){let e=R(t,"x-payment-signature"),r=R(t,"x-payment-session-key"),s=R(t,"x-payment-smart-account"),i=R(t,"x-payment-timestamp"),n=R(t,"x-payment-amount"),o=R(t,"x-payment-recipient"),a=R(t,"x-payment-usage-id"),c=R(t,"x-payment-nonce");if(!e||!r||!s||!i||!n||!o||!a||!c)return null;let m=parseInt(i,10);if(isNaN(m)||Math.floor(Date.now()/1e3)-m>K)return null;let u=(y,p)=>{if(!y.startsWith("0x"))return false;let g=y.slice(2);return g.length<p?false:/^[a-fA-F0-9]+$/.test(g)};return !u(e,130)||!u(r,64)||!u(s,40)?null:{signature:e,sessionKeyId:r,smartAccount:s,timestamp:m,amount:n,recipient:o,usageId:a,nonce:c}}function H(t){let e=Math.floor(Date.now()/1e3),r=e+(t.expiresInSec??L),s=`${e}-${crypto.randomBytes(16).toString("hex")}`,i=t.usageId;i.startsWith("0x")||(i=v(i));let n={"X-Payment-Required":"true","X-Payment-Amount":t.amount,"X-Payment-Recipient":t.recipient,"X-Payment-Usage-Id":i,"X-Payment-Description":t.description??"API usage charge","X-Payment-Expires":String(r),"X-Payment-Nonce":s,"X-Payment-Timestamp":String(e)},o={amount:t.amount,recipient:t.recipient,usageId:i,description:t.description??"API usage charge",expiresAt:r,nonce:s,timestamp:e};return {headers:n,paymentRequest:o}}function v(t){let e=5381,r=52711;for(let n=0;n<t.length;n++){let o=t.charCodeAt(n);e=(e<<5)+e^o,r=(r<<5)+r^o;}return `0x${Math.abs(e*31+r).toString(16).padStart(16,"0").slice(0,16).padEnd(64,"0")}`}async function j(t,e){let r=e.customerResolver??"header";if(typeof r=="function")return r(t);if(r==="header"){let s=R(t.headers,"x-drip-customer-id")??R(t.headers,"x-customer-id");if(!s)throw new h("Missing customer ID. Include X-Drip-Customer-Id header.","CUSTOMER_RESOLUTION_FAILED",400);return s}if(r==="query"){let s=t.query??{},i=s.drip_customer_id??s.customer_id,n=Array.isArray(i)?i[0]:i;if(!n)throw new h("Missing customer ID. Include drip_customer_id query parameter.","CUSTOMER_RESOLUTION_FAILED",400);return n}throw new h(`Invalid customer resolver: ${r}`,"CONFIGURATION_ERROR",500)}async function B(t,e){return typeof e.quantity=="function"?e.quantity(t):e.quantity}async function G(t,e,r){if(r.idempotencyKey)return r.idempotencyKey(t);let s=Date.now(),i=[t.method,t.url,e,s];return `drip_${v(i.join("|")).slice(2,18)}`}function T(t){let e=t.apiKey??process.env.DRIP_API_KEY;if(!e)throw new h("Missing Drip API key. Set DRIP_API_KEY environment variable or pass apiKey in config.","CONFIGURATION_ERROR",500);return new P({apiKey:e,baseUrl:t.baseUrl??process.env.DRIP_API_URL})}async function A(t,e){if(e.skipInDevelopment&&process.env.NODE_ENV==="development"){console.warn("[Drip] Skipping billing in development mode. Set skipInDevelopment: false or NODE_ENV to production to enable billing.");let r=T(e),s={success:true,usageEventId:"dev_usage_event",isReplay:false,charge:{id:"dev_charge",amountUsdc:"0.00",amountToken:"0",txHash:"0x0",status:"CONFIRMED"}};return {success:true,state:{customerId:"dev_customer",quantity:typeof e.quantity=="number"?e.quantity:1,idempotencyKey:"dev_idempotency",hasPaymentProof:false},charge:s,drip:r,isReplay:false}}try{let r=T(e),s=await j(t,e),i=await B(t,e),n=await G(t,s,e),o=D(t.headers),a=o?X(t.headers):void 0,c={customerId:s,quantity:i,idempotencyKey:n,hasPaymentProof:o,paymentProof:a??void 0},m=typeof e.metadata=="function"?e.metadata(t):e.metadata;try{let d=await r.charge({customerId:s,meter:e.meter,quantity:i,idempotencyKey:n,metadata:m});return e.onCharge&&await e.onCharge(d,t),{success:!0,state:c,charge:d,drip:r,isReplay:d.isReplay??!1}}catch(d){if(d instanceof f){if(d.statusCode===402){let u=process.env.DRIP_RECIPIENT_ADDRESS;if(!u)throw new h("DRIP_RECIPIENT_ADDRESS environment variable must be configured for x402 payment flow.","CONFIGURATION_ERROR",500);let y="0.01",p=d.message.match(/amount[:\s]+([0-9.]+)/i);p?y=p[1]:y=(i*1e-4).toFixed(6);let{headers:g,paymentRequest:l}=H({amount:y,recipient:u,usageId:n,description:`${e.meter} usage charge`});return {success:!1,error:new h("Insufficient balance. Payment required.","PAYMENT_REQUIRED",402),paymentRequired:{headers:g,paymentRequest:l}}}throw e.onError&&await e.onError(d,t),new h(d.message,"CHARGE_FAILED",d.statusCode,{code:d.code})}throw d}}catch(r){if(r instanceof h)return {success:false,error:r};let s=r instanceof Error?r.message:"Unknown error";return {success:false,error:new h(s,"INTERNAL_ERROR",500)}}}function S(t){let e={};for(let[r,s]of Object.entries(t))e[r.toLowerCase()]=Array.isArray(s)?s[0]:s;return e}function J(t,e,r){t.status(402).set(e).json({error:"Payment required",code:"PAYMENT_REQUIRED",paymentRequest:r,instructions:{step1:"Sign the payment request with your session key using EIP-712",step2:"Retry the request with X-Payment-* headers",documentation:"https://docs.drip.dev/x402"}});}function Y(t,e,r,s,i){t.status(s).json({error:e,code:r,...i&&{details:i}});}function M(t){let e=t.attachToRequest??true;return async(r,s,i)=>{let n={method:r.method,url:r.originalUrl||r.url,headers:S(r.headers),query:r.query},o=typeof t.quantity=="function"?await t.quantity(r):t.quantity,a;if(typeof t.customerResolver=="function"){let p=t.customerResolver;a=async()=>p(r);}else a=t.customerResolver;let c;if(typeof t.idempotencyKey=="function"){let p=t.idempotencyKey;c=async()=>p(r);}let m=typeof t.metadata=="function"?t.metadata(r):t.metadata,d={meter:t.meter,quantity:o,apiKey:t.apiKey,baseUrl:t.baseUrl,customerResolver:a,idempotencyKey:c,metadata:m,skipInDevelopment:t.skipInDevelopment,onCharge:void 0,onError:void 0},u=await A(n,d);if(!u.success){if(t.errorHandler&&await t.errorHandler(u.error,r,s))return;if(u.paymentRequired){J(s,u.paymentRequired.headers,u.paymentRequired.paymentRequest);return}Y(s,u.error.message,u.error.code,u.error.statusCode,u.error.details);return}t.onCharge&&await t.onCharge(u.charge,r);let y={drip:u.drip,customerId:u.state.customerId,charge:u.charge,isReplay:u.isReplay};e&&(r.drip=y),i();}}function Q(t){return e=>M({...t,...e})}function z(t){return D(S(t.headers))}function _(t){return "drip"in t&&typeof t.drip=="object"}function V(t){if(!_(t))throw new Error("Drip context not found on request. Ensure dripMiddleware is applied before this route.");return t.drip}
2
- exports.Drip=P;exports.DripError=f;exports.DripMiddlewareError=h;exports.createDripMiddleware=Q;exports.dripMiddleware=M;exports.getDripContext=V;exports.hasDripContext=_;exports.hasPaymentProofHeaders=z;//# sourceMappingURL=express.cjs.map
1
+ 'use strict';var crypto=require('crypto');var k=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});var Y=0;function w(r,...e){let t=++Y,s=e.filter(i=>i!==void 0).map(String);s.push(String(t));let n=crypto.createHash("sha256").update(s.join("|")).digest("hex").slice(0,24);return `${r}_${n}`}var T=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,t){this._chargeFn=e,this._options=t;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isReplay:false};let t=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:w("stream",this._options.customerId,this._options.meter,e,this._flushCount),s=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:t,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let n={success:s.success,quantity:e,charge:s.charge,isReplay:s.isReplay};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var J={requestsPerSecond:100,burstSize:200,enabled:true},S=class{config;tokens;lastRefill;constructor(e){this.config={...J,...e},this.tokens=this.config.burstSize,this.lastRefill=Date.now();}refill(){let e=Date.now(),t=(e-this.lastRefill)/1e3;this.tokens=Math.min(this.config.burstSize,this.tokens+t*this.config.requestsPerSecond),this.lastRefill=e;}async acquire(e){if(!this.config.enabled)return true;let t=e!==void 0?Date.now()+e:void 0;for(;;){if(this.refill(),this.tokens>=1)return this.tokens-=1,true;let s=(1-this.tokens)/this.config.requestsPerSecond*1e3;if(t!==void 0){let n=t-Date.now();if(n<=0)return false;await this.sleep(Math.min(s,n));}else await this.sleep(s);}}tryAcquire(){return this.config.enabled?(this.refill(),this.tokens>=1?(this.tokens-=1,true):false):true}get availableTokens(){return this.refill(),this.tokens}sleep(e){return new Promise(t=>setTimeout(t,e))}};var D=class r extends Error{attempts;lastError;constructor(e,t){super(`Retry exhausted after ${e} attempts: ${t.message}`),this.name="RetryExhaustedError",this.attempts=e,this.lastError=t,Object.setPrototypeOf(this,r.prototype);}};function W(r,e){let t=e.baseDelayMs*Math.pow(e.exponentialBase,r);if(t=Math.min(t,e.maxDelayMs),e.jitter>0){let s=t*e.jitter;t+=Math.random()*2*s-s;}return Math.max(0,t)}function F(r,e){if(r instanceof Error){if(r.message.includes("fetch")||r.message.includes("network")||r.message.includes("ECONNREFUSED")||r.message.includes("ETIMEDOUT"))return true;let t=r.statusCode;if(t!==void 0)return e.retryableStatusCodes.includes(t)}return false}var Q={failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},q=class r extends Error{circuitName;timeUntilRetryMs;constructor(e,t){super(`Circuit '${e}' is open. Retry in ${Math.round(t)}ms`),this.name="CircuitBreakerOpenError",this.circuitName=e,this.timeUntilRetryMs=t,Object.setPrototypeOf(this,r.prototype);}},M=class{name;config;state="closed";failureCount=0;successCount=0;lastFailureTime=null;constructor(e,t){this.name=e,this.config={...Q,...t};}getState(){return this.checkStateTransition(),this.state}checkStateTransition(){this.state==="open"&&this.lastFailureTime!==null&&Date.now()-this.lastFailureTime>=this.config.timeoutMs&&(this.state="half_open",this.successCount=0);}recordSuccess(){this.config.enabled&&(this.state==="half_open"?(this.successCount+=1,this.successCount>=this.config.successThreshold&&(this.state="closed",this.failureCount=0)):this.state==="closed"&&(this.failureCount=0));}recordFailure(){this.config.enabled&&(this.failureCount+=1,this.lastFailureTime=Date.now(),this.state==="half_open"?this.state="open":this.state==="closed"&&this.failureCount>=this.config.failureThreshold&&(this.state="open"));}allowRequest(){return !this.config.enabled||(this.checkStateTransition(),this.state==="closed")?true:this.state==="half_open"}getTimeUntilRetry(){if(this.state!=="open"||this.lastFailureTime===null)return 0;let e=Date.now()-this.lastFailureTime;return Math.max(0,this.config.timeoutMs-e)}reset(){this.state="closed",this.failureCount=0,this.successCount=0,this.lastFailureTime=null;}},v=class{windowSize;metrics=[];totalRequests=0;totalSuccesses=0;totalFailures=0;constructor(e=1e3){this.windowSize=e;}record(e){for(this.metrics.push(e),this.totalRequests+=1,e.success?this.totalSuccesses+=1:this.totalFailures+=1;this.metrics.length>this.windowSize;)this.metrics.shift();}getSummary(){if(this.metrics.length===0)return {windowSize:0,totalRequests:0,totalSuccesses:0,totalFailures:0,successRate:0,avgLatencyMs:0,minLatencyMs:0,maxLatencyMs:0,p50LatencyMs:0,p95LatencyMs:0,p99LatencyMs:0,requestsByEndpoint:{},errorsByType:{}};let e=this.metrics.map(o=>o.durationMs).sort((o,a)=>o-a),t=this.metrics.filter(o=>o.success).length,s={};for(let o of this.metrics)s[o.endpoint]=(s[o.endpoint]??0)+1;let n={};for(let o of this.metrics)o.error&&(n[o.error]=(n[o.error]??0)+1);let i=e.reduce((o,a)=>o+a,0);return {windowSize:this.metrics.length,totalRequests:this.totalRequests,totalSuccesses:this.totalSuccesses,totalFailures:this.totalFailures,successRate:t/this.metrics.length*100,avgLatencyMs:i/e.length,minLatencyMs:e[0],maxLatencyMs:e[e.length-1],p50LatencyMs:e[Math.floor(e.length*.5)],p95LatencyMs:e[Math.floor(e.length*.95)],p99LatencyMs:e[Math.floor(e.length*.99)],requestsByEndpoint:s,errorsByType:n}}reset(){this.metrics.length=0,this.totalRequests=0,this.totalSuccesses=0,this.totalFailures=0;}};function E(){return {rateLimiter:{requestsPerSecond:100,burstSize:200,enabled:true},retry:{maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},collectMetrics:true}}function A(){return {rateLimiter:{requestsPerSecond:1e3,burstSize:2e3,enabled:true},retry:{maxRetries:2,baseDelayMs:50,maxDelayMs:5e3,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:10,successThreshold:3,timeoutMs:15e3,enabled:true},collectMetrics:true}}var P=class{config;rateLimiter;circuitBreaker;metrics;constructor(e){this.config={...E(),...e,rateLimiter:{...E().rateLimiter,...e?.rateLimiter},retry:{...E().retry,...e?.retry},circuitBreaker:{...E().circuitBreaker,...e?.circuitBreaker}},this.rateLimiter=new S(this.config.rateLimiter),this.circuitBreaker=new M("drip_api",this.config.circuitBreaker),this.metrics=this.config.collectMetrics?new v:null;}async execute(e,t="UNKNOWN",s="unknown"){let n=performance.now(),i=0,o=null;if(!await this.rateLimiter.acquire(3e4))throw new Error("Rate limiter timeout");if(!this.circuitBreaker.allowRequest())throw new q(this.circuitBreaker.name,this.circuitBreaker.getTimeUntilRetry());for(let u=0;u<=this.config.retry.maxRetries;u++)try{let c=await e();if(this.circuitBreaker.recordSuccess(),this.metrics){let d=performance.now()-n;this.metrics.record({method:t,endpoint:s,statusCode:200,durationMs:d,success:!0,timestamp:Date.now(),retryCount:i});}return c}catch(c){if(o=c instanceof Error?c:new Error(String(c)),this.config.retry.enabled&&F(c,this.config.retry)&&u<this.config.retry.maxRetries){i+=1;let l=W(u,this.config.retry);await this.sleep(l);continue}if(this.circuitBreaker.recordFailure(),this.metrics){let l=performance.now()-n,g=c.statusCode??null;this.metrics.record({method:t,endpoint:s,statusCode:g,durationMs:l,success:false,timestamp:Date.now(),error:o.name,retryCount:i});}throw c}throw o?new D(this.config.retry.maxRetries+1,o):new Error("Unexpected execution path")}getMetrics(){return this.metrics?.getSummary()??null}getHealth(){return {circuitBreaker:{state:this.circuitBreaker.getState(),timeUntilRetryMs:this.circuitBreaker.getTimeUntilRetry()},rateLimiter:{availableTokens:this.rateLimiter.availableTokens,requestsPerSecond:this.config.rateLimiter.requestsPerSecond},metrics:this.getMetrics()}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var _={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function V(r){return r instanceof Error&&(r.message.includes("fetch")||r.message.includes("network"))?true:r instanceof y?r.statusCode>=500||r.statusCode===408||r.statusCode===429:false}async function Z(r,e={}){let t=e.maxAttempts??_.maxAttempts,s=e.baseDelayMs??_.baseDelayMs,n=e.maxDelayMs??_.maxDelayMs,i=e.isRetryable??V,o;for(let a=1;a<=t;a++)try{return await r()}catch(u){if(o=u,a===t||!i(u))throw u;let c=Math.min(s*Math.pow(2,a-1)+Math.random()*100,n);await new Promise(d=>setTimeout(d,c));}throw o}var y=class r extends Error{constructor(t,s,n){super(t);this.statusCode=s;this.code=n;this.name="DripError",Object.setPrototypeOf(this,r.prototype);}},I=class{apiKey;baseUrl;timeout;resilience;keyType;constructor(e={}){let t=e.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),s=e.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!t)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=t,this.baseUrl=s||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=e.timeout||3e4,t.startsWith("sk_")?this.keyType="secret":t.startsWith("pk_")?this.keyType="public":this.keyType="unknown",e.resilience===true?this.resilience=new P(E()):e.resilience==="high-throughput"?this.resilience=new P(A()):e.resilience&&typeof e.resilience=="object"?this.resilience=new P(e.resilience):this.resilience=null;}assertSecretKey(e){if(this.keyType==="public")throw new y(`${e} requires a secret key (sk_). You are using a public key (pk_), which cannot access this endpoint. Use a secret key for webhook, API key, and feature flag management.`,403,"PUBLIC_KEY_NOT_ALLOWED")}async request(e,t={}){let s=(t.method??"GET").toUpperCase();return this.resilience?this.resilience.execute(()=>this.rawRequest(e,t),s,e):this.rawRequest(e,t)}async rawRequest(e,t={}){let s=new AbortController,n=setTimeout(()=>s.abort(),this.timeout);try{let i=await fetch(`${this.baseUrl}${e}`,{...t,signal:s.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...t.headers}});if(i.status===204)return {success:!0};let o=await i.json();if(!i.ok)throw new y(o.message||o.error||"Request failed",i.status,o.code);return o}catch(i){throw i instanceof y?i:i instanceof Error&&i.name==="AbortError"?new y("Request timed out",408,"TIMEOUT"):new y(i instanceof Error?i.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let e=new AbortController,t=setTimeout(()=>e.abort(),this.timeout),s=this.baseUrl;s.endsWith("/v1/")?s=s.slice(0,-4):s.endsWith("/v1")&&(s=s.slice(0,-3)),s=s.replace(/\/+$/,"");let n=Date.now();try{let i=await fetch(`${s}/health`,{signal:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),o=Date.now()-n,a="unknown",u=Date.now();try{let c=await i.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(u=c.timestamp);}catch{a=i.ok?"healthy":`error:${i.status}`;}return !i.ok&&a==="unknown"&&(a=`error:${i.status}`),{ok:i.ok&&a==="healthy",status:a,latencyMs:o,timestamp:u}}catch(i){throw i instanceof Error&&i.name==="AbortError"?new y("Request timed out",408,"TIMEOUT"):new y(i instanceof Error?i.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(t);}}getMetrics(){return this.resilience?.getMetrics()??null}getHealth(){return this.resilience?.getHealth()??null}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let t=new URLSearchParams;e?.limit&&t.set("limit",e.limit.toString()),e?.status&&t.set("status",e.status);let s=t.toString(),n=s?`/customers?${s}`:"/customers";return this.request(n)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){let t=e.idempotencyKey??w("chg",e.customerId,e.meter,e.quantity);return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,metadata:e.metadata})})}async wrapApiCall(e){let t=e.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,s=await e.call(),n=e.extractUsage(s),i=await Z(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:n,idempotencyKey:t,metadata:e.metadata}),e.retryOptions);return {result:s,charge:i,idempotencyKey:t}}async trackUsage(e){let t=e.idempotencyKey??w("track",e.customerId,e.meter,e.quantity);return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,units:e.units,description:e.description,metadata:e.metadata})})}async getCharge(e){return this.request(`/charges/${e}`)}async listCharges(e){let t=new URLSearchParams;e?.customerId&&t.set("customerId",e.customerId),e?.status&&t.set("status",e.status),e?.limit&&t.set("limit",e.limit.toString()),e?.offset&&t.set("offset",e.offset.toString());let s=t.toString(),n=s?`/charges?${s}`:"/charges";return this.request(n)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let t=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:t.id,url:t.url,expiresAt:t.expires_at,amountUsd:t.amount_usd}}async createWebhook(e){return this.assertSecretKey("createWebhook()"),this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.assertSecretKey("listWebhooks()"),this.request("/webhooks")}async getWebhook(e){return this.assertSecretKey("getWebhook()"),this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.assertSecretKey("deleteWebhook()"),this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.assertSecretKey("testWebhook()"),this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.assertSecretKey("rotateWebhookSecret()"),this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,t){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(t)})}async getRun(e){return this.request(`/runs/${e}`)}async getRunTimeline(e,t){let s=new URLSearchParams;t?.limit&&s.set("limit",t.limit.toString()),t?.cursor&&s.set("cursor",t.cursor),t?.includeAnomalies!==void 0&&s.set("includeAnomalies",String(t.includeAnomalies)),t?.collapseRetries!==void 0&&s.set("collapseRetries",String(t.collapseRetries));let n=s.toString(),i=n?`/runs/${e}/timeline?${n}`:`/runs/${e}/timeline`;return this.request(i)}async emitEvent(e){let t=e.idempotencyKey??w("evt",e.runId,e.eventType,e.quantity);return this.request("/run-events",{method:"POST",body:JSON.stringify({...e,idempotencyKey:t})})}async emitEventsBatch(e){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:e})})}async listMeters(){let e=await this.request("/pricing-plans");return {data:e.data.map(t=>({id:t.id,name:t.name,meter:t.unitType,unitPriceUsd:t.unitPriceUsd,isActive:t.isActive})),count:e.count}}async estimateFromUsage(e){let t=e.periodStart instanceof Date?e.periodStart.toISOString():e.periodStart,s=e.periodEnd instanceof Date?e.periodEnd.toISOString():e.periodEnd;return this.request("/dashboard/cost-estimate/from-usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,periodStart:t,periodEnd:s,defaultUnitPrice:e.defaultUnitPrice,includeChargedEvents:e.includeChargedEvents,usageTypes:e.usageTypes,customPricing:e.customPricing})})}async estimateFromHypothetical(e){return this.request("/dashboard/cost-estimate/hypothetical",{method:"POST",body:JSON.stringify({items:e.items,defaultUnitPrice:e.defaultUnitPrice,customPricing:e.customPricing})})}async recordRun(e){let t=Date.now(),s=e.workflow,n=e.workflow;if(!e.workflow.startsWith("wf_"))try{let h=(await this.listWorkflows()).data.find(m=>m.slug===e.workflow||m.id===e.workflow);if(h)s=h.id,n=h.name;else {let m=await this.createWorkflow({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,R=>R.toUpperCase()),slug:e.workflow,productSurface:"AGENT"});s=m.id,n=m.name;}}catch{s=e.workflow;}let i=await this.startRun({customerId:e.customerId,workflowId:s,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata}),o=0,a=0;if(e.events.length>0){let p=e.events.map((m,R)=>({runId:i.id,eventType:m.eventType,quantity:m.quantity,units:m.units,description:m.description,costUnits:m.costUnits,metadata:m.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${m.eventType}:${R}`:w("run",i.id,m.eventType,R)})),h=await this.emitEventsBatch(p);o=h.created,a=h.duplicates;}let u=await this.endRun(i.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),c=Date.now()-t,d=e.events.length>0?`${o} events recorded`:"no events",g=`${e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB"} ${n}: ${d} (${u.durationMs??c}ms)`;return {run:{id:i.id,workflowId:s,workflowName:n,status:e.status,durationMs:u.durationMs},events:{created:o,duplicates:a},totalCostUnits:u.totalCostUnits,summary:g}}static generateIdempotencyKey(e){let s=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)].join("|");return `drip_${k("crypto").createHash("sha256").update(s).digest("hex").slice(0,32)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,t,s,n=300){if(!e||!t||!s)return false;try{let i=t.split(","),o=i.find(C=>C.startsWith("t=")),a=i.find(C=>C.startsWith("v1="));if(!o||!a)return !1;let u=parseInt(o.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let l=`${u}.${e}`,g=new TextEncoder,p=g.encode(s),h=g.encode(l),m=globalThis.crypto?.subtle??k("crypto").webcrypto.subtle,R=await m.importKey("raw",p,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),G=await m.sign("HMAC",R,h),L=Array.from(new Uint8Array(G)).map(C=>C.toString(16).padStart(2,"0")).join("");if(c.length!==L.length)return !1;let N=0;for(let C=0;C<c.length;C++)N|=c.charCodeAt(C)^L.charCodeAt(C);return N===0}catch{return false}}static verifyWebhookSignatureSync(e,t,s,n=300){if(!e||!t||!s)return false;try{let i=t.split(","),o=i.find(R=>R.startsWith("t=")),a=i.find(R=>R.startsWith("v1="));if(!o||!a)return !1;let u=parseInt(o.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let l=k("crypto"),g=`${u}.${e}`,p=l.createHmac("sha256",s).update(g).digest("hex"),h=Buffer.from(c),m=Buffer.from(p);return h.length!==m.length?!1:l.timingSafeEqual(h,m)}catch{return false}}static generateWebhookSignature(e,t,s){let n=k("crypto"),i=s??Math.floor(Date.now()/1e3),o=`${i}.${e}`,a=n.createHmac("sha256",t).update(o).digest("hex");return `t=${i},v1=${a}`}createStreamMeter(e){return new T(this.charge.bind(this),e)}};var U=null;function ee(){return U||(U=new I),U}new Proxy({},{get(r,e){let t=ee(),s=t[e];return typeof s=="function"?s.bind(t):s}});var f=class r extends Error{constructor(t,s,n,i){super(t);this.code=s;this.statusCode=n;this.details=i;this.name="DripMiddlewareError",Object.setPrototypeOf(this,r.prototype);}};var re=300,se=300,ne=["x-payment-signature","x-payment-session-key","x-payment-smart-account","x-payment-timestamp","x-payment-amount","x-payment-recipient","x-payment-usage-id","x-payment-nonce"];function ie(r){return r.toLowerCase()}function b(r,e){let t=ie(e);if(r[t]!==void 0){let s=r[t];return Array.isArray(s)?s[0]:s}for(let[s,n]of Object.entries(r))if(s.toLowerCase()===t)return Array.isArray(n)?n[0]:n}function O(r){return ne.every(e=>b(r,e)!==void 0)}function oe(r){let e=b(r,"x-payment-signature"),t=b(r,"x-payment-session-key"),s=b(r,"x-payment-smart-account"),n=b(r,"x-payment-timestamp"),i=b(r,"x-payment-amount"),o=b(r,"x-payment-recipient"),a=b(r,"x-payment-usage-id"),u=b(r,"x-payment-nonce");if(!e||!t||!s||!n||!i||!o||!a||!u)return null;let c=parseInt(n,10);if(isNaN(c)||Math.floor(Date.now()/1e3)-c>se)return null;let l=(g,p)=>{if(!g.startsWith("0x"))return false;let h=g.slice(2);return h.length<p?false:/^[a-fA-F0-9]+$/.test(h)};return !l(e,130)||!l(t,64)||!l(s,40)?null:{signature:e,sessionKeyId:t,smartAccount:s,timestamp:c,amount:i,recipient:o,usageId:a,nonce:u}}function ae(r){let e=Math.floor(Date.now()/1e3),t=e+(r.expiresInSec??re),s=`${e}-${crypto.randomBytes(16).toString("hex")}`,n=r.usageId;n.startsWith("0x")||(n=B(n));let i={"X-Payment-Required":"true","X-Payment-Amount":r.amount,"X-Payment-Recipient":r.recipient,"X-Payment-Usage-Id":n,"X-Payment-Description":r.description??"API usage charge","X-Payment-Expires":String(t),"X-Payment-Nonce":s,"X-Payment-Timestamp":String(e)},o={amount:r.amount,recipient:r.recipient,usageId:n,description:r.description??"API usage charge",expiresAt:t,nonce:s,timestamp:e};return {headers:i,paymentRequest:o}}function B(r){let{createHash:e}=k("crypto");return `0x${e("sha256").update(r).digest("hex")}`}async function ue(r,e){let t=e.customerResolver??"header";if(typeof t=="function")return t(r);if(t==="header"){let s=b(r.headers,"x-drip-customer-id")??b(r.headers,"x-customer-id");if(!s)throw new f("Missing customer ID. Include X-Drip-Customer-Id header.","CUSTOMER_RESOLUTION_FAILED",400);return s}if(t==="query"){let s=r.query??{},n=s.drip_customer_id??s.customer_id,i=Array.isArray(n)?n[0]:n;if(!i)throw new f("Missing customer ID. Include drip_customer_id query parameter.","CUSTOMER_RESOLUTION_FAILED",400);return i}throw new f(`Invalid customer resolver: ${t}`,"CONFIGURATION_ERROR",500)}async function ce(r,e){return typeof e.quantity=="function"?e.quantity(r):e.quantity}async function le(r,e,t){if(t.idempotencyKey)return t.idempotencyKey(r);let s=Date.now(),n=[r.method,r.url,e,s];return `drip_${B(n.join("|")).slice(2,18)}`}function K(r){let e=r.apiKey??process.env.DRIP_API_KEY;if(!e)throw new f("Missing Drip API key. Set DRIP_API_KEY environment variable or pass apiKey in config.","CONFIGURATION_ERROR",500);return new I({apiKey:e,baseUrl:r.baseUrl??process.env.DRIP_API_URL})}async function $(r,e){if(e.skipInDevelopment&&process.env.NODE_ENV==="development"){let t=K(e),s={success:true,usageEventId:"dev_usage_event",isReplay:false,charge:{id:"dev_charge",amountUsdc:"0.00",amountToken:"0",txHash:"0x0",status:"CONFIRMED"}};return {success:true,state:{customerId:"dev_customer",quantity:typeof e.quantity=="number"?e.quantity:1,idempotencyKey:"dev_idempotency",hasPaymentProof:false},charge:s,drip:t,isReplay:false}}try{let t=K(e),s=await ue(r,e),n=await ce(r,e),i=await le(r,s,e),o=O(r.headers),a=o?oe(r.headers):void 0,u={customerId:s,quantity:n,idempotencyKey:i,hasPaymentProof:o,paymentProof:a??void 0},c=typeof e.metadata=="function"?e.metadata(r):e.metadata;try{let d=await t.charge({customerId:s,meter:e.meter,quantity:n,idempotencyKey:i,metadata:c});return e.onCharge&&await e.onCharge(d,r),{success:!0,state:u,charge:d,drip:t,isReplay:d.isReplay??!1}}catch(d){if(d instanceof y){if(d.statusCode===402){let l=process.env.DRIP_RECIPIENT_ADDRESS;if(!l)throw new f("DRIP_RECIPIENT_ADDRESS environment variable must be configured for x402 payment flow.","CONFIGURATION_ERROR",500);let g="0.01",p=d.message.match(/amount[:\s]+([0-9.]+)/i);p?g=p[1]:g=(n*1e-4).toFixed(6);let{headers:h,paymentRequest:m}=ae({amount:g,recipient:l,usageId:i,description:`${e.meter} usage charge`});return {success:!1,error:new f("Insufficient balance. Payment required.","PAYMENT_REQUIRED",402),paymentRequired:{headers:h,paymentRequest:m}}}throw e.onError&&await e.onError(d,r),new f(d.message,"CHARGE_FAILED",d.statusCode,{code:d.code})}throw d}}catch(t){if(t instanceof f)return {success:false,error:t};let s=t instanceof Error?t.message:"Unknown error";return {success:false,error:new f(s,"INTERNAL_ERROR",500)}}}function H(r){let e={};for(let[t,s]of Object.entries(r))e[t.toLowerCase()]=Array.isArray(s)?s[0]:s;return e}function de(r,e,t){r.status(402).set(e).json({error:"Payment required",code:"PAYMENT_REQUIRED",paymentRequest:t,instructions:{step1:"Sign the payment request with your session key using EIP-712",step2:"Retry the request with X-Payment-* headers",documentation:"https://docs.drip.dev/x402"}});}function me(r,e,t,s,n){r.status(s).json({error:e,code:t,...n&&{details:n}});}function X(r){let e=r.attachToRequest??true;return async(t,s,n)=>{let i={method:t.method,url:t.originalUrl||t.url,headers:H(t.headers),query:t.query},o=typeof r.quantity=="function"?await r.quantity(t):r.quantity,a;if(typeof r.customerResolver=="function"){let p=r.customerResolver;a=async()=>p(t);}else a=r.customerResolver;let u;if(typeof r.idempotencyKey=="function"){let p=r.idempotencyKey;u=async()=>p(t);}let c=typeof r.metadata=="function"?r.metadata(t):r.metadata,d={meter:r.meter,quantity:o,apiKey:r.apiKey,baseUrl:r.baseUrl,customerResolver:a,idempotencyKey:u,metadata:c,skipInDevelopment:r.skipInDevelopment,onCharge:void 0,onError:void 0},l=await $(i,d);if(!l.success){if(r.errorHandler&&await r.errorHandler(l.error,t,s))return;if(l.paymentRequired){de(s,l.paymentRequired.headers,l.paymentRequired.paymentRequest);return}me(s,l.error.message,l.error.code,l.error.statusCode,l.error.details);return}r.onCharge&&await r.onCharge(l.charge,t);let g={drip:l.drip,customerId:l.state.customerId,charge:l.charge,isReplay:l.isReplay};e&&(t.drip=g),n();}}function ge(r){return e=>X({...r,...e})}function pe(r){return O(H(r.headers))}function j(r){return "drip"in r&&typeof r.drip=="object"}function he(r){if(!j(r))throw new Error("Drip context not found on request. Ensure dripMiddleware is applied before this route.");return r.drip}
2
+ exports.Drip=I;exports.DripError=y;exports.DripMiddlewareError=f;exports.createDripMiddleware=ge;exports.dripMiddleware=X;exports.getDripContext=he;exports.hasDripContext=j;exports.hasPaymentProofHeaders=pe;//# sourceMappingURL=express.cjs.map
3
3
  //# sourceMappingURL=express.cjs.map