@drip-sdk/node 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,332 +1,241 @@
1
- # @drip-sdk/node
1
+ # Drip SDK (Node.js)
2
2
 
3
- The official Node.js SDK for **Drip** - Usage tracking and cost attribution for metered infrastructure.
3
+ Drip is a lightweight SDK for **usage tracking and execution logging** in systems where cost is tied to computation — AI agents, APIs, background jobs, and infra workloads.
4
4
 
5
- Drip is the system of record for usage. We capture high-frequency metering for RPC providers, API companies, and AI agents - with cost attribution, execution traces, and real-time analytics.
5
+ This **Core SDK** is designed for pilots: it records *what ran* and *how much it used*, without handling billing or balances.
6
6
 
7
- [![npm version](https://badge.fury.io/js/@drip-sdk/node.svg)](https://www.npmjs.com/package/@drip-sdk/node)
7
+ **One line to start tracking:** `await drip.trackUsage({ customerId, meter, quantity })`
8
+
9
+ [![npm version](https://img.shields.io/npm/v/%40drip-sdk%2Fnode.svg)](https://www.npmjs.com/package/@drip-sdk/node)
8
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
11
 
10
- ## Installation
12
+ ---
13
+
14
+ ## 60-Second Quickstart (Core SDK)
15
+
16
+ ### 1. Install
11
17
 
12
18
  ```bash
13
19
  npm install @drip-sdk/node
14
20
  ```
15
21
 
16
- ## Core SDK (Recommended for Pilots)
17
-
18
- For most use cases, import the **Core SDK** - a simplified API focused on two concepts:
22
+ ### 2. Set your API key
19
23
 
20
- 1. **Usage Tracking** - Record metered usage events
21
- 2. **Execution Logging** - Log agent runs with detailed event traces
22
-
23
- ```typescript
24
- import { Drip } from '@drip-sdk/node/core';
25
-
26
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
24
+ ```bash
25
+ # Secret key full API access (server-side only, never expose publicly)
26
+ export DRIP_API_KEY=sk_test_...
27
27
 
28
- // Verify connection
29
- await drip.ping();
28
+ # Or public key — read/write access for usage, customers, billing (safe for client-side)
29
+ export DRIP_API_KEY=pk_test_...
30
30
  ```
31
31
 
32
- ### Track Usage
32
+ ### 3. Track usage (one line)
33
33
 
34
34
  ```typescript
35
- // Track any metered usage (no billing - just recording)
36
- await drip.trackUsage({
37
- customerId: 'cus_123',
38
- meter: 'api_calls',
39
- quantity: 1,
40
- metadata: { endpoint: '/v1/generate', method: 'POST' },
41
- });
35
+ import { drip } from '@drip-sdk/node';
36
+
37
+ // Track usage - that's it
38
+ await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
42
39
  ```
43
40
 
44
- ### Record Agent Runs
41
+ The `drip` singleton reads `DRIP_API_KEY` from your environment automatically.
45
42
 
46
- ```typescript
47
- // Record a complete agent execution with one call
48
- const result = await drip.recordRun({
49
- customerId: 'cus_123',
50
- workflow: 'research-agent',
51
- events: [
52
- { eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
53
- { eventType: 'tool.call', name: 'web-search', duration: 1500 },
54
- { eventType: 'llm.call', model: 'gpt-4', inputTokens: 200, outputTokens: 800 },
55
- ],
56
- status: 'COMPLETED',
57
- });
58
-
59
- console.log(result.summary);
60
- // Output: "Research Agent: 3 events recorded (2.5s)"
61
- ```
43
+ ### Alternative: Explicit Configuration
62
44
 
63
- ### Core SDK Methods
45
+ ```typescript
46
+ import { Drip } from '@drip-sdk/node';
64
47
 
65
- | Method | Description |
66
- |--------|-------------|
67
- | `ping()` | Verify API connection |
68
- | `createCustomer(params)` | Create a customer |
69
- | `getCustomer(customerId)` | Get customer details |
70
- | `listCustomers(options)` | List all customers |
71
- | `trackUsage(params)` | Record metered usage |
72
- | `recordRun(params)` | Log complete agent run (simplified) |
73
- | `startRun(params)` | Start execution trace |
74
- | `emitEvent(params)` | Log event within run |
75
- | `emitEventsBatch(params)` | Batch log events |
76
- | `endRun(runId, params)` | Complete execution trace |
77
- | `getRunTimeline(runId)` | Get execution timeline |
48
+ // Auto-reads DRIP_API_KEY from environment
49
+ const drip = new Drip();
78
50
 
79
- ---
51
+ // Or pass config explicitly with a secret key (full access)
52
+ const drip = new Drip({ apiKey: 'sk_test_...' });
80
53
 
81
- ## Full SDK
54
+ // Or with a public key (safe for client-side, limited scope)
55
+ const drip = new Drip({ apiKey: 'pk_test_...' });
56
+ ```
82
57
 
83
- For billing, webhooks, and advanced features, use the full SDK:
58
+ ### Full Example
84
59
 
85
60
  ```typescript
86
- import { Drip } from '@drip-sdk/node';
87
-
88
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
61
+ import { drip } from '@drip-sdk/node';
62
+
63
+ async function main() {
64
+ // Verify connectivity
65
+ await drip.ping();
66
+
67
+ // Record usage
68
+ await drip.trackUsage({
69
+ customerId: 'customer_123',
70
+ meter: 'llm_tokens',
71
+ quantity: 842,
72
+ metadata: { model: 'gpt-4o-mini' },
73
+ });
74
+
75
+ // Record an execution lifecycle
76
+ await drip.recordRun({
77
+ customerId: 'customer_123',
78
+ workflow: 'research-agent',
79
+ events: [
80
+ { eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
81
+ { eventType: 'tool.call', name: 'web-search', duration: 1500 },
82
+ ],
83
+ status: 'COMPLETED',
84
+ });
85
+
86
+ console.log('Usage + run recorded');
87
+ }
89
88
 
90
- // All Core SDK methods plus:
91
- // - charge(), getBalance(), getCharge(), listCharges()
92
- // - createWebhook(), listWebhooks(), deleteWebhook()
93
- // - estimateFromUsage(), estimateFromHypothetical()
94
- // - checkout(), and more
89
+ main();
95
90
  ```
96
91
 
97
- ## Quick Start (Full SDK)
92
+ **Expected result:**
93
+ - No errors
94
+ - Events appear in your Drip dashboard within seconds
98
95
 
99
- ### Track Usage
96
+ ---
100
97
 
101
- ```typescript
102
- import { Drip } from '@drip-sdk/node';
98
+ ## Core Concepts (2-minute mental model)
103
99
 
104
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
100
+ | Concept | Description |
101
+ |---------|-------------|
102
+ | `customerId` | The end user, API key, or account you're attributing usage to |
103
+ | `meter` | What you're measuring (tokens, requests, seconds, rows, etc.) |
104
+ | `quantity` | Numeric usage for that meter |
105
+ | `run` | A single execution or request lifecycle (success / failure / duration) |
105
106
 
106
- // Track any metered usage
107
- await drip.trackUsage({
108
- customerId: 'cus_123',
109
- meter: 'api_calls',
110
- quantity: 1,
111
- metadata: { endpoint: '/v1/generate', method: 'POST' },
112
- });
107
+ **Status values:** `PENDING` | `RUNNING` | `COMPLETED` | `FAILED`
113
108
 
114
- // Check accumulated usage
115
- const balance = await drip.getBalance('cus_123');
116
- console.log(`Total usage: $${balance.totalUsageUsd}`);
117
- ```
109
+ **Event schema:** Payloads are schema-flexible. Drip stores events as structured JSON and does not enforce a fixed event taxonomy.
118
110
 
119
- ### Log Agent Runs (AI/Agent API)
111
+ Drip is append-only and idempotent-friendly. You can safely retry events.
120
112
 
121
- ```typescript
122
- // Record a complete agent execution with one call
123
- const result = await drip.recordRun({
124
- customerId: 'cus_123',
125
- workflow: 'research-agent',
126
- events: [
127
- { eventType: 'llm.call', model: 'gpt-4', inputTokens: 500, outputTokens: 1200 },
128
- { eventType: 'tool.call', name: 'web-search', duration: 1500 },
129
- { eventType: 'llm.call', model: 'gpt-4', inputTokens: 200, outputTokens: 800 },
130
- ],
131
- status: 'COMPLETED',
132
- });
113
+ ---
133
114
 
134
- console.log(result.summary);
135
- // Output: "Research Agent: 3 events recorded (2.5s)"
136
- ```
115
+ ## Idempotency Keys
137
116
 
138
- ### View Execution Traces
117
+ Every mutating SDK method (`charge`, `trackUsage`, `emitEvent`) accepts an optional `idempotencyKey` parameter. The server uses this key to deduplicate requests — if two requests share the same key, only the first is processed.
139
118
 
140
- ```typescript
141
- // Get detailed timeline of an agent run
142
- const timeline = await drip.getRunTimeline(runId);
119
+ `recordRun` generates idempotency keys internally for its batch events (using `externalRunId` when provided, otherwise deterministic keys).
143
120
 
144
- for (const event of timeline.events) {
145
- console.log(`${event.eventType}: ${event.duration}ms`);
146
- }
147
- ```
121
+ ### Auto-generated keys (default)
148
122
 
149
- ## Use Cases
123
+ When you omit `idempotencyKey`, the SDK generates one automatically. The auto key is:
150
124
 
151
- ### RPC Providers
125
+ - **Unique per call** — two separate calls with identical parameters produce different keys (a monotonic counter ensures this).
126
+ - **Stable across retries** — the key is generated once and reused for all retry attempts of that call, so network retries are safely deduplicated.
127
+ - **Deterministic** — no randomness; keys are reproducible for debugging.
152
128
 
153
- ```typescript
154
- // Track per-method usage with chain and latency
155
- await drip.trackUsage({
156
- customerId: apiKeyOwner,
157
- meter: 'rpc_calls',
158
- quantity: 1,
159
- metadata: {
160
- method: 'eth_call',
161
- chain: 'ethereum',
162
- latencyMs: 45,
163
- cacheHit: false,
164
- },
165
- });
166
- ```
129
+ This means you get **free retry safety** with zero configuration.
130
+
131
+ > **Note:** `wrapApiCall` generates a time-based key when no explicit `idempotencyKey` is provided. Pass your own key if you need deterministic deduplication with `wrapApiCall`.
132
+
133
+ ### When to pass explicit keys
167
134
 
168
- ### API Companies
135
+ Pass your own `idempotencyKey` when you need **application-level deduplication** — e.g., to guarantee that a specific business operation is billed exactly once, even across process restarts:
169
136
 
170
137
  ```typescript
171
- // Track API usage with endpoint attribution
172
- await drip.trackUsage({
173
- customerId: 'cus_123',
138
+ await drip.charge({
139
+ customerId: 'cust_123',
174
140
  meter: 'api_calls',
175
141
  quantity: 1,
176
- metadata: {
177
- endpoint: '/v1/embeddings',
178
- tokens: 1500,
179
- model: 'text-embedding-3-small',
180
- },
142
+ idempotencyKey: `order_${orderId}_charge`, // your business-level key
181
143
  });
182
144
  ```
183
145
 
184
- ### AI Agents
146
+ Common patterns:
147
+ - `order_${orderId}` — one charge per order
148
+ - `run_${runId}_step_${stepIndex}` — one charge per pipeline step
149
+ - `invoice_${invoiceId}` — one charge per invoice
185
150
 
186
- ```typescript
187
- // Track agent execution with detailed events
188
- const run = await drip.startRun({
189
- customerId: 'cus_123',
190
- workflowSlug: 'document-processor',
191
- });
151
+ ### StreamMeter
192
152
 
193
- await drip.emitEvent({
194
- runId: run.id,
195
- eventType: 'ocr.process',
196
- quantity: 5,
197
- units: 'pages',
198
- });
153
+ `StreamMeter` also auto-generates idempotency keys per flush. If you provide an `idempotencyKey` in the options, each flush appends a counter (`_flush_0`, `_flush_1`, etc.) to keep multi-flush scenarios safe.
199
154
 
200
- await drip.emitEvent({
201
- runId: run.id,
202
- eventType: 'llm.summarize',
203
- model: 'gpt-4',
204
- inputTokens: 10000,
205
- outputTokens: 500,
206
- });
155
+ ---
207
156
 
208
- await drip.endRun(run.id, { status: 'COMPLETED' });
209
- ```
157
+ ## API Key Types
210
158
 
211
- ## Full SDK API Reference
159
+ Drip issues two key types per API key pair. Each has different access scopes:
212
160
 
213
- ### Usage & Billing
161
+ | Key Type | Prefix | Access | Use In |
162
+ |----------|--------|--------|--------|
163
+ | **Secret Key** | `sk_live_` / `sk_test_` | Full API access (all endpoints) | Server-side only |
164
+ | **Public Key** | `pk_live_` / `pk_test_` | Usage tracking, customers, billing, analytics, sessions | Client-side safe |
214
165
 
215
- | Method | Description |
216
- |--------|-------------|
217
- | `trackUsage(params)` | Record metered usage (no billing) |
218
- | `charge(params)` | Create a billable charge |
219
- | `getBalance(customerId)` | Get balance and usage summary |
220
- | `getCharge(chargeId)` | Get charge details |
221
- | `listCharges(options)` | List all charges |
222
- | `getChargeStatus(chargeId)` | Get charge status |
166
+ ### What public keys **can** access
167
+ - Usage tracking (`trackUsage`, `recordRun`, `startRun`, `emitEvent`, etc.)
168
+ - Customer management (`createCustomer`, `getCustomer`, `listCustomers`)
169
+ - Billing & charges (`charge`, `getBalance`, `listCharges`, etc.)
170
+ - Pricing plans, sessions, analytics, usage caps, refunds
223
171
 
224
- ### Execution Logging
172
+ ### What public keys **cannot** access (secret key required)
173
+ - Webhook management (`createWebhook`, `listWebhooks`, `deleteWebhook`, etc.)
174
+ - API key management (create, rotate, revoke keys)
175
+ - Feature flag management
225
176
 
226
- | Method | Description |
227
- |--------|-------------|
228
- | `recordRun(params)` | Log complete agent run (simplified) |
229
- | `startRun(params)` | Start execution trace |
230
- | `emitEvent(params)` | Log event within run |
231
- | `emitEventsBatch(params)` | Batch log events |
232
- | `endRun(runId, params)` | Complete execution trace |
233
- | `getRunTimeline(runId)` | Get execution timeline |
234
- | `createWorkflow(params)` | Create a workflow |
235
- | `listWorkflows()` | List all workflows |
177
+ The SDK detects your key type automatically and will throw a `DripError` with code `PUBLIC_KEY_NOT_ALLOWED` (HTTP 403) if you attempt a secret-key-only operation with a public key.
236
178
 
237
- ### Customer Management
179
+ ```typescript
180
+ const drip = new Drip({ apiKey: 'pk_test_...' });
181
+ console.log(drip.keyType); // 'public'
238
182
 
239
- | Method | Description |
240
- |--------|-------------|
241
- | `createCustomer(params)` | Create a customer |
242
- | `getCustomer(customerId)` | Get customer details |
243
- | `listCustomers(options)` | List all customers |
183
+ // This works fine
184
+ await drip.trackUsage({ customerId: 'cust_123', meter: 'api_calls', quantity: 1 });
185
+
186
+ // This throws DripError(403, 'PUBLIC_KEY_NOT_ALLOWED')
187
+ await drip.createWebhook({ url: '...', events: ['charge.succeeded'] });
188
+ ```
244
189
 
245
- ### Webhooks
190
+ ---
246
191
 
247
- | Method | Description |
248
- |--------|-------------|
249
- | `createWebhook(params)` | Create webhook endpoint |
250
- | `listWebhooks()` | List all webhooks |
251
- | `getWebhook(webhookId)` | Get webhook details |
252
- | `deleteWebhook(webhookId)` | Delete a webhook |
253
- | `testWebhook(webhookId)` | Test a webhook |
254
- | `rotateWebhookSecret(webhookId)` | Rotate webhook secret |
255
- | `Drip.verifyWebhookSignature()` | Verify webhook signature |
192
+ ## SDK Variants
256
193
 
257
- ### Cost Estimation
194
+ | Variant | Description |
195
+ |---------|-------------|
196
+ | **Core SDK** (recommended for pilots) | Usage tracking + execution logging only |
197
+ | **Full SDK** | Includes billing, balances, and workflows (for later stages) |
258
198
 
259
- | Method | Description |
260
- |--------|-------------|
261
- | `estimateFromUsage(params)` | Estimate cost from usage data |
262
- | `estimateFromHypothetical(params)` | Estimate from hypothetical usage |
199
+ ---
263
200
 
264
- ### Other
201
+ ## Core SDK Methods
265
202
 
266
203
  | Method | Description |
267
204
  |--------|-------------|
268
- | `checkout(params)` | Create checkout session (fiat on-ramp) |
269
- | `listMeters()` | List available meters |
270
205
  | `ping()` | Verify API connection |
206
+ | `createCustomer(params)` | Create a customer |
207
+ | `getCustomer(customerId)` | Get customer details |
208
+ | `listCustomers(options)` | List all customers |
209
+ | `trackUsage(params)` | Record metered usage |
210
+ | `recordRun(params)` | Log complete agent run (simplified) |
211
+ | `startRun(params)` | Start execution trace |
212
+ | `emitEvent(params)` | Log event within run |
213
+ | `emitEventsBatch(params)` | Batch log events |
214
+ | `endRun(runId, params)` | Complete execution trace |
215
+ | `getRunTimeline(runId)` | Get execution timeline |
271
216
 
272
- ## Streaming Meter (LLM Token Streaming)
273
-
274
- For LLM token streaming, accumulate usage locally and charge once:
275
-
276
- ```typescript
277
- const meter = drip.createStreamMeter({
278
- customerId: 'cus_123',
279
- meter: 'tokens',
280
- });
281
-
282
- for await (const chunk of llmStream) {
283
- meter.add(chunk.tokens);
284
- yield chunk;
285
- }
286
-
287
- // Single API call at end
288
- await meter.flush();
289
- ```
217
+ ---
290
218
 
291
- ## Framework Middleware
219
+ ## Who This Is For
292
220
 
293
- ### Next.js
221
+ - AI agents (token metering, tool calls, execution traces)
222
+ - API companies (per-request billing, endpoint attribution)
223
+ - RPC providers (multi-chain call tracking)
224
+ - Cloud/infra (compute seconds, storage, bandwidth)
294
225
 
295
- ```typescript
296
- import { withDrip } from '@drip-sdk/node/next';
226
+ ---
297
227
 
298
- export const POST = withDrip({
299
- meter: 'api_calls',
300
- quantity: 1,
301
- }, async (req, { customerId }) => {
302
- return Response.json({ result: 'success' });
303
- });
304
- ```
228
+ ## Full SDK (Billing, Webhooks, Integrations)
305
229
 
306
- ### Express
230
+ For billing, webhooks, middleware, and advanced features:
307
231
 
308
232
  ```typescript
309
- import { dripMiddleware } from '@drip-sdk/node/express';
310
-
311
- app.use('/api', dripMiddleware({
312
- meter: 'api_calls',
313
- quantity: 1,
314
- }));
233
+ import { Drip } from '@drip-sdk/node';
315
234
  ```
316
235
 
317
- ## LangChain Integration
318
-
319
- ```typescript
320
- import { DripCallbackHandler } from '@drip-sdk/node/langchain';
321
-
322
- const handler = new DripCallbackHandler({
323
- drip,
324
- customerId: 'cus_123',
325
- });
236
+ See **[FULL_SDK.md](./FULL_SDK.md)** for complete documentation.
326
237
 
327
- // Automatically tracks all LLM calls and tool usage
328
- await agent.invoke({ input: '...' }, { callbacks: [handler] });
329
- ```
238
+ ---
330
239
 
331
240
  ## Error Handling
332
241
 
@@ -342,50 +251,7 @@ try {
342
251
  }
343
252
  ```
344
253
 
345
- ## Billing (Full SDK Only)
346
-
347
- ```typescript
348
- import { Drip } from '@drip-sdk/node';
349
-
350
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
351
-
352
- // Create a billable charge
353
- const result = await drip.charge({
354
- customerId: 'cus_123',
355
- meter: 'api_calls',
356
- quantity: 1,
357
- });
358
-
359
- // Get customer balance
360
- const balance = await drip.getBalance('cus_123');
361
- console.log(`Balance: $${balance.balanceUsdc}`);
362
-
363
- // Query charges
364
- const charge = await drip.getCharge(chargeId);
365
- const charges = await drip.listCharges({ customerId: 'cus_123' });
366
-
367
- // Cost estimation
368
- await drip.estimateFromUsage({ customerId, startDate, endDate });
369
- await drip.estimateFromHypothetical({ items: [...] });
370
-
371
- // Checkout (fiat on-ramp)
372
- await drip.checkout({ customerId: 'cus_123', amountUsd: 5000 });
373
- ```
374
-
375
- ## TypeScript Support
376
-
377
- Full TypeScript support with exported types:
378
-
379
- ```typescript
380
- import type {
381
- Customer,
382
- Charge,
383
- ChargeResult,
384
- TrackUsageParams,
385
- RunResult,
386
- Webhook,
387
- } from '@drip-sdk/node';
388
- ```
254
+ ---
389
255
 
390
256
  ## Requirements
391
257
 
@@ -393,6 +259,6 @@ import type {
393
259
 
394
260
  ## Links
395
261
 
396
- - [Documentation](https://docs.drippay.dev)
397
- - [GitHub](https://github.com/MichaelLevin5908/drip)
262
+ - [Full SDK Documentation](./FULL_SDK.md)
263
+ - [API Documentation](https://drippay.dev/api-reference)
398
264
  - [npm](https://www.npmjs.com/package/@drip-sdk/node)
package/dist/core.cjs CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var u=class w extends Error{constructor(n,r,i){super(n);this.statusCode=r;this.code=i;this.name="DripError",Object.setPrototypeOf(this,w.prototype);}},g=class{apiKey;baseUrl;timeout;constructor(t){if(!t.apiKey)throw new Error("Drip API key is required");this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||"https://api.drip.dev/v1",this.timeout=t.timeout||3e4;}async request(t,n={}){let r=new AbortController,i=setTimeout(()=>r.abort(),this.timeout);try{let e=await fetch(`${this.baseUrl}${t}`,{...n,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...n.headers}});if(e.status===204)return {success:!0};let o=await e.json();if(!e.ok)throw new u(o.message||o.error||"Request failed",e.status,o.code);return o}catch(e){throw e instanceof u?e:e instanceof Error&&e.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(e instanceof Error?e.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(i);}}async ping(){let t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout),r=this.baseUrl;r.endsWith("/v1/")?r=r.slice(0,-4):r.endsWith("/v1")&&(r=r.slice(0,-3)),r=r.replace(/\/+$/,"");let i=Date.now();try{let e=await fetch(`${r}/health`,{signal:t.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),o=Date.now()-i,a="unknown",l=Date.now();try{let c=await e.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(l=c.timestamp);}catch{a=e.ok?"healthy":`error:${e.status}`;}return !e.ok&&a==="unknown"&&(a=`error:${e.status}`),{ok:e.ok&&a==="healthy",status:a,latencyMs:o,timestamp:l}}catch(e){throw e instanceof Error&&e.name==="AbortError"?new u("Request timed out",408,"TIMEOUT"):new u(e instanceof Error?e.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}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 n=new URLSearchParams;t?.limit&&n.set("limit",t.limit.toString()),t?.status&&n.set("status",t.status);let r=n.toString(),i=r?`/customers?${r}`:"/customers";return this.request(i)}async trackUsage(t){return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:t.customerId,usageType:t.meter,quantity:t.quantity,idempotencyKey:t.idempotencyKey,units:t.units,description:t.description,metadata:t.metadata})})}async 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,n){return this.request(`/runs/${t}`,{method:"PATCH",body:JSON.stringify(n)})}async getRunTimeline(t){return this.request(`/runs/${t}`)}async emitEvent(t){return this.request("/events",{method:"POST",body:JSON.stringify(t)})}async emitEventsBatch(t){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:t})})}async recordRun(t){let n=Date.now(),r=t.workflow,i=t.workflow;if(!t.workflow.startsWith("wf_"))try{let d=(await this.listWorkflows()).data.find(s=>s.slug===t.workflow||s.id===t.workflow);if(d)r=d.id,i=d.name;else {let s=await this.createWorkflow({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,m=>m.toUpperCase()),slug:t.workflow,productSurface:"CUSTOM"});r=s.id,i=s.name;}}catch{r=t.workflow;}let e=await this.startRun({customerId:t.customerId,workflowId:r,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata}),o=0,a=0;if(t.events.length>0){let y=t.events.map((s,m)=>({runId:e.id,eventType:s.eventType,quantity:s.quantity,units:s.units,description:s.description,costUnits:s.costUnits,metadata:s.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${s.eventType}:${m}`:void 0})),d=await this.emitEventsBatch(y);o=d.created,a=d.duplicates;}let l=await this.endRun(e.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),c=Date.now()-n,f=t.events.length>0?`${o} events recorded`:"no events",p=`${t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB"} ${i}: ${f} (${l.durationMs??c}ms)`;return {run:{id:e.id,workflowId:r,workflowName:i,status:t.status,durationMs:l.durationMs},events:{created:o,duplicates:a},totalCostUnits:l.totalCostUnits,summary:p}}},b=g;
2
- exports.Drip=g;exports.DripError=u;exports.default=b;//# sourceMappingURL=core.cjs.map
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var crypto=require('crypto');var I=0;function y(m,...t){let e=++I,n=t.filter(r=>r!==void 0).map(String);n.push(String(e));let s=crypto.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}}},S=f,p=null;function k(){return p||(p=new f),p}var U=new Proxy({},{get(m,t){let e=k(),n=e[t];return typeof n=="function"?n.bind(e):n}});
2
+ exports.Drip=f;exports.DripError=u;exports.default=S;exports.drip=U;//# sourceMappingURL=core.cjs.map
3
3
  //# sourceMappingURL=core.cjs.map