@drip-sdk/node 1.1.0 → 1.1.2

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,284 @@
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)
22
+ ### 2. Set your API key
17
23
 
18
- For most use cases, import the **Core SDK** - a simplified API focused on two concepts:
24
+ ```bash
25
+ # Secret key — full API access (server-side only, never expose publicly)
26
+ export DRIP_API_KEY=sk_test_...
19
27
 
20
- 1. **Usage Tracking** - Record metered usage events
21
- 2. **Execution Logging** - Log agent runs with detailed event traces
28
+ # Or public key read/write access for usage, customers, billing (safe for client-side)
29
+ export DRIP_API_KEY=pk_test_...
30
+ ```
31
+
32
+ ### 3. Create a customer and track usage
22
33
 
23
34
  ```typescript
24
- import { Drip } from '@drip-sdk/node/core';
35
+ import { drip } from '@drip-sdk/node';
25
36
 
26
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
37
+ // Create a customer first
38
+ const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
27
39
 
28
- // Verify connection
29
- await drip.ping();
40
+ // Track usage — that's it
41
+ await drip.trackUsage({ customerId: customer.id, meter: 'api_calls', quantity: 1 });
30
42
  ```
31
43
 
32
- ### Track Usage
44
+ The `drip` singleton reads `DRIP_API_KEY` from your environment automatically.
45
+
46
+ ### Alternative: Explicit Configuration
33
47
 
34
48
  ```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
- });
49
+ import { Drip } from '@drip-sdk/node';
50
+
51
+ // Auto-reads DRIP_API_KEY from environment
52
+ const drip = new Drip();
53
+
54
+ // Or pass config explicitly with a secret key (full access)
55
+ const drip = new Drip({ apiKey: 'sk_test_...' });
56
+
57
+ // Or with a public key (safe for client-side, limited scope)
58
+ const drip = new Drip({ apiKey: 'pk_test_...' });
42
59
  ```
43
60
 
44
- ### Record Agent Runs
61
+ ### Full Example
45
62
 
46
63
  ```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
- });
64
+ import { drip } from '@drip-sdk/node';
65
+
66
+ async function main() {
67
+ // Verify connectivity
68
+ await drip.ping();
69
+
70
+ // Create a customer (at least one of externalCustomerId or onchainAddress required)
71
+ const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
72
+
73
+ // Record usage
74
+ await drip.trackUsage({
75
+ customerId: customer.id,
76
+ meter: 'llm_tokens',
77
+ quantity: 842,
78
+ metadata: { model: 'gpt-4o-mini' },
79
+ });
80
+
81
+ // Record an execution lifecycle
82
+ await drip.recordRun({
83
+ customerId: customer.id,
84
+ workflow: 'research-agent',
85
+ events: [
86
+ { eventType: 'llm.call', quantity: 1700, units: 'tokens' },
87
+ { eventType: 'tool.call', quantity: 1 },
88
+ ],
89
+ status: 'COMPLETED',
90
+ });
91
+
92
+ console.log(`Customer ${customer.id}: usage + run recorded`);
93
+ }
58
94
 
59
- console.log(result.summary);
60
- // Output: "Research Agent: 3 events recorded (2.5s)"
95
+ main();
61
96
  ```
62
97
 
63
- ### Core SDK Methods
64
-
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 |
98
+ **Expected result:**
99
+ - No errors
100
+ - Events appear in your Drip dashboard within seconds
78
101
 
79
102
  ---
80
103
 
81
- ## Full SDK
104
+ ## Core Concepts (2-minute mental model)
82
105
 
83
- For billing, webhooks, and advanced features, use the full SDK:
106
+ | Concept | Description |
107
+ |---------|-------------|
108
+ | `customerId` | The end user, API key, or account you're attributing usage to |
109
+ | `meter` | What you're measuring (tokens, requests, seconds, rows, etc.) |
110
+ | `quantity` | Numeric usage for that meter |
111
+ | `run` | A single execution or request lifecycle (success / failure / duration) |
112
+ | `correlationId` | Optional. Your trace/request ID for linking Drip data with your APM (OpenTelemetry, Datadog, etc.) |
84
113
 
85
- ```typescript
86
- import { Drip } from '@drip-sdk/node';
114
+ **Status values:** `PENDING` | `RUNNING` | `COMPLETED` | `FAILED`
87
115
 
88
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
116
+ **Event schema:** Payloads are schema-flexible. Drip stores events as structured JSON and does not enforce a fixed event taxonomy.
89
117
 
90
- // All Core SDK methods plus:
91
- // - charge(), getBalance(), getCharge(), listCharges()
92
- // - createWebhook(), listWebhooks(), deleteWebhook()
93
- // - estimateFromUsage(), estimateFromHypothetical()
94
- // - checkout(), and more
95
- ```
118
+ Drip is append-only and idempotent-friendly. You can safely retry events.
96
119
 
97
- ## Quick Start (Full SDK)
120
+ > **Distributed tracing:** Pass `correlationId` to `startRun()`, `recordRun()`, or `emitEvent()` to cross-reference Drip billing with your observability stack. See [FULL_SDK.md](./FULL_SDK.md#distributed-tracing-correlationid) for details.
98
121
 
99
- ### Track Usage
122
+ ---
100
123
 
101
- ```typescript
102
- import { Drip } from '@drip-sdk/node';
124
+ ## Idempotency Keys
103
125
 
104
- const drip = new Drip({ apiKey: process.env.DRIP_API_KEY! });
126
+ 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.
105
127
 
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
- });
128
+ `recordRun` generates idempotency keys internally for its batch events (using `externalRunId` when provided, otherwise deterministic keys).
113
129
 
114
- // Check accumulated usage
115
- const balance = await drip.getBalance('cus_123');
116
- console.log(`Total usage: $${balance.totalUsageUsd}`);
117
- ```
130
+ ### Auto-generated keys (default)
118
131
 
119
- ### Log Agent Runs (AI/Agent API)
132
+ When you omit `idempotencyKey`, the SDK generates one automatically. The auto key is:
120
133
 
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
- });
134
+ - **Unique per call** — two separate calls with identical parameters produce different keys (a monotonic counter ensures this).
135
+ - **Stable across retries** the key is generated once and reused for all retry attempts of that call, so network retries are safely deduplicated.
136
+ - **Deterministic** no randomness; keys are reproducible for debugging.
133
137
 
134
- console.log(result.summary);
135
- // Output: "Research Agent: 3 events recorded (2.5s)"
136
- ```
137
-
138
- ### View Execution Traces
139
-
140
- ```typescript
141
- // Get detailed timeline of an agent run
142
- const timeline = await drip.getRunTimeline(runId);
138
+ This means you get **free retry safety** with zero configuration.
143
139
 
144
- for (const event of timeline.events) {
145
- console.log(`${event.eventType}: ${event.duration}ms`);
146
- }
147
- ```
140
+ > **Note:** `wrapApiCall` generates a time-based key when no explicit `idempotencyKey` is provided. Pass your own key if you need deterministic deduplication with `wrapApiCall`.
148
141
 
149
- ## Use Cases
142
+ ### When to pass explicit keys
150
143
 
151
- ### RPC Providers
144
+ 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:
152
145
 
153
146
  ```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
- ```
167
-
168
- ### API Companies
147
+ const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
169
148
 
170
- ```typescript
171
- // Track API usage with endpoint attribution
172
- await drip.trackUsage({
173
- customerId: 'cus_123',
149
+ await drip.charge({
150
+ customerId: customer.id,
174
151
  meter: 'api_calls',
175
152
  quantity: 1,
176
- metadata: {
177
- endpoint: '/v1/embeddings',
178
- tokens: 1500,
179
- model: 'text-embedding-3-small',
180
- },
153
+ idempotencyKey: `order_${orderId}_charge`, // your business-level key
181
154
  });
182
155
  ```
183
156
 
184
- ### AI Agents
157
+ Common patterns:
158
+ - `order_${orderId}` — one charge per order
159
+ - `run_${runId}_step_${stepIndex}` — one charge per pipeline step
160
+ - `invoice_${invoiceId}` — one charge per invoice
185
161
 
186
- ```typescript
187
- // Track agent execution with detailed events
188
- const run = await drip.startRun({
189
- customerId: 'cus_123',
190
- workflowSlug: 'document-processor',
191
- });
162
+ ### StreamMeter
192
163
 
193
- await drip.emitEvent({
194
- runId: run.id,
195
- eventType: 'ocr.process',
196
- quantity: 5,
197
- units: 'pages',
198
- });
164
+ `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
165
 
200
- await drip.emitEvent({
201
- runId: run.id,
202
- eventType: 'llm.summarize',
203
- model: 'gpt-4',
204
- inputTokens: 10000,
205
- outputTokens: 500,
206
- });
166
+ ---
207
167
 
208
- await drip.endRun(run.id, { status: 'COMPLETED' });
209
- ```
168
+ ## API Key Types
210
169
 
211
- ## Full SDK API Reference
170
+ Drip issues two key types per API key pair. Each has different access scopes:
212
171
 
213
- ### Usage & Billing
172
+ | Key Type | Prefix | Access | Use In |
173
+ |----------|--------|--------|--------|
174
+ | **Secret Key** | `sk_live_` / `sk_test_` | Full API access (all endpoints) | Server-side only |
175
+ | **Public Key** | `pk_live_` / `pk_test_` | Usage tracking, customers, billing, analytics, sessions | Client-side safe |
214
176
 
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 |
177
+ ### What public keys **can** access
178
+ - Usage tracking (`trackUsage`, `recordRun`, `startRun`, `emitEvent`, etc.)
179
+ - Customer management (`createCustomer`, `getCustomer`, `listCustomers`)
180
+ - Billing & charges (`charge`, `getBalance`, `listCharges`, etc.)
181
+ - Pricing plans, sessions, analytics, usage caps, refunds
223
182
 
224
- ### Execution Logging
183
+ ### What public keys **cannot** access (secret key required)
184
+ - Webhook management (`createWebhook`, `listWebhooks`, `deleteWebhook`, etc.)
185
+ - API key management (create, rotate, revoke keys)
186
+ - Feature flag management
225
187
 
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 |
188
+ 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
189
 
237
- ### Customer Management
190
+ ```typescript
191
+ const drip = new Drip({ apiKey: 'pk_test_...' });
192
+ console.log(drip.keyType); // 'public'
238
193
 
239
- | Method | Description |
240
- |--------|-------------|
241
- | `createCustomer(params)` | Create a customer |
242
- | `getCustomer(customerId)` | Get customer details |
243
- | `listCustomers(options)` | List all customers |
194
+ // Create a customer first, then track usage
195
+ const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
196
+ await drip.trackUsage({ customerId: customer.id, meter: 'api_calls', quantity: 1 });
244
197
 
245
- ### Webhooks
198
+ // This throws DripError(403, 'PUBLIC_KEY_NOT_ALLOWED')
199
+ await drip.createWebhook({ url: '...', events: ['charge.succeeded'] });
200
+ ```
246
201
 
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 |
202
+ ---
256
203
 
257
- ### Cost Estimation
204
+ ## SDK Variants
258
205
 
259
- | Method | Description |
260
- |--------|-------------|
261
- | `estimateFromUsage(params)` | Estimate cost from usage data |
262
- | `estimateFromHypothetical(params)` | Estimate from hypothetical usage |
206
+ | Variant | Description |
207
+ |---------|-------------|
208
+ | **Core SDK** (recommended for pilots) | Usage tracking + execution logging only |
209
+ | **Full SDK** | Includes billing, balances, and workflows (for later stages) |
210
+
211
+ ---
263
212
 
264
- ### Other
213
+ ## Core SDK Methods
265
214
 
266
215
  | Method | Description |
267
216
  |--------|-------------|
268
- | `checkout(params)` | Create checkout session (fiat on-ramp) |
269
- | `listMeters()` | List available meters |
270
217
  | `ping()` | Verify API connection |
218
+ | `createCustomer(params)` | Create a customer |
219
+ | `getCustomer(customerId)` | Get customer details |
220
+ | `listCustomers(options)` | List all customers |
221
+ | `trackUsage(params)` | Record metered usage |
222
+ | `recordRun(params)` | Log complete agent run (simplified) |
223
+ | `startRun(params)` | Start execution trace |
224
+ | `emitEvent(params)` | Log event within run |
225
+ | `emitEventsBatch(params)` | Batch log events |
226
+ | `endRun(runId, params)` | Complete execution trace |
227
+ | `getRun(runId)` | Get run details and summary |
228
+ | `getRunTimeline(runId)` | Get execution timeline |
271
229
 
272
- ## Streaming Meter (LLM Token Streaming)
230
+ ### Creating Customers
273
231
 
274
- For LLM token streaming, accumulate usage locally and charge once:
232
+ All parameters are optional, but at least one of `externalCustomerId` or `onchainAddress` must be provided:
275
233
 
276
234
  ```typescript
277
- const meter = drip.createStreamMeter({
278
- customerId: 'cus_123',
279
- meter: 'tokens',
280
- });
235
+ // Simplest just your internal user ID
236
+ const customer = await drip.createCustomer({ externalCustomerId: 'user_123' });
281
237
 
282
- for await (const chunk of llmStream) {
283
- meter.add(chunk.tokens);
284
- yield chunk;
285
- }
238
+ // With an on-chain address (for on-chain billing)
239
+ const customer = await drip.createCustomer({
240
+ onchainAddress: '0x1234...',
241
+ externalCustomerId: 'user_123',
242
+ });
286
243
 
287
- // Single API call at end
288
- await meter.flush();
244
+ // Internal/non-billing customer (for tracking only)
245
+ const customer = await drip.createCustomer({
246
+ externalCustomerId: 'internal-team',
247
+ isInternal: true,
248
+ });
289
249
  ```
290
250
 
291
- ## Framework Middleware
251
+ | Parameter | Type | Required | Description |
252
+ |-----------|------|----------|-------------|
253
+ | `externalCustomerId` | `string` | No* | Your internal user/account ID |
254
+ | `onchainAddress` | `string` | No* | Customer's Ethereum address |
255
+ | `isInternal` | `boolean` | No | Mark as internal (non-billing). Default: `false` |
256
+ | `metadata` | `object` | No | Arbitrary key-value metadata |
292
257
 
293
- ### Next.js
258
+ \*At least one of `externalCustomerId` or `onchainAddress` is required.
294
259
 
295
- ```typescript
296
- import { withDrip } from '@drip-sdk/node/next';
260
+ ---
297
261
 
298
- export const POST = withDrip({
299
- meter: 'api_calls',
300
- quantity: 1,
301
- }, async (req, { customerId }) => {
302
- return Response.json({ result: 'success' });
303
- });
304
- ```
262
+ ## Who This Is For
305
263
 
306
- ### Express
264
+ - AI agents (token metering, tool calls, execution traces)
265
+ - API companies (per-request billing, endpoint attribution)
266
+ - RPC providers (multi-chain call tracking)
267
+ - Cloud/infra (compute seconds, storage, bandwidth)
307
268
 
308
- ```typescript
309
- import { dripMiddleware } from '@drip-sdk/node/express';
269
+ ---
310
270
 
311
- app.use('/api', dripMiddleware({
312
- meter: 'api_calls',
313
- quantity: 1,
314
- }));
315
- ```
271
+ ## Full SDK (Billing, Webhooks, Integrations)
316
272
 
317
- ## LangChain Integration
273
+ For billing, webhooks, middleware, and advanced features:
318
274
 
319
275
  ```typescript
320
- import { DripCallbackHandler } from '@drip-sdk/node/langchain';
276
+ import { Drip } from '@drip-sdk/node';
277
+ ```
321
278
 
322
- const handler = new DripCallbackHandler({
323
- drip,
324
- customerId: 'cus_123',
325
- });
279
+ See **[FULL_SDK.md](./FULL_SDK.md)** for complete documentation.
326
280
 
327
- // Automatically tracks all LLM calls and tool usage
328
- await agent.invoke({ input: '...' }, { callbacks: [handler] });
329
- ```
281
+ ---
330
282
 
331
283
  ## Error Handling
332
284
 
@@ -342,50 +294,7 @@ try {
342
294
  }
343
295
  ```
344
296
 
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
- ```
297
+ ---
389
298
 
390
299
  ## Requirements
391
300
 
@@ -393,6 +302,6 @@ import type {
393
302
 
394
303
  ## Links
395
304
 
396
- - [Documentation](https://docs.drippay.dev)
397
- - [GitHub](https://github.com/MichaelLevin5908/drip)
305
+ - [Full SDK Documentation](./FULL_SDK.md)
306
+ - [API Documentation](https://drippay.dev/api-reference)
398
307
  - [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 k=0;function p(m,...t){let e=++k,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 a=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 i=await r.json();if(!r.ok)throw new a(i.message||i.error||"Request failed",r.status,i.code);return i}catch(r){throw r instanceof a?r:r instanceof Error&&r.name==="AbortError"?new a("Request timed out",408,"TIMEOUT"):new a(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}`}}),i=Date.now()-s,o="unknown",d=Date.now();try{let u=await r.json();typeof u.status=="string"&&(o=u.status),typeof u.timestamp=="number"&&(d=u.timestamp);}catch{o=r.ok?"healthy":`error:${r.status}`;}return !r.ok&&o==="unknown"&&(o=`error:${r.status}`),{ok:r.ok&&o==="healthy",status:o,latencyMs:i,timestamp:d}}catch(r){throw r instanceof Error&&r.name==="AbortError"?new a("Request timed out",408,"TIMEOUT"):new a(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??p("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??p("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){try{return await this.request("/runs/record",{method:"POST",body:JSON.stringify({customerId:t.customerId,workflow:t.workflow,events:t.events,status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode,externalRunId:t.externalRunId,correlationId:t.correlationId,metadata:t.metadata})})}catch(e){if(e instanceof a&&e.statusCode===404)return this._recordRunFallback(t);throw e}}async _recordRunFallback(t){let e=Date.now(),n=t.workflow,s=t.workflow,{data:r}=await this.listWorkflows(),i=r.find(l=>l.slug===t.workflow||l.id===t.workflow);if(i)n=i.id,s=i.name;else {let l=await this.request("/workflows",{method:"POST",body:JSON.stringify({name:t.workflow.replace(/[_-]/g," ").replace(/\b\w/g,y=>y.toUpperCase()),slug:t.workflow,productSurface:"CUSTOM"})});n=l.id,s=l.name;}let o=await this.startRun({customerId:t.customerId,workflowId:n,correlationId:t.correlationId,externalRunId:t.externalRunId,metadata:t.metadata}),d=0,u=0;if(t.events.length>0){let l=t.events.map((c,I)=>({runId:o.id,eventType:c.eventType,quantity:c.quantity??1,units:c.units,description:c.description,costUnits:c.costUnits,metadata:c.metadata,idempotencyKey:t.externalRunId?`${t.externalRunId}:${c.eventType}:${I}`:void 0})),y=await this.emitEventsBatch(l);d=y.created,u=y.duplicates;}let g=await this.endRun(o.id,{status:t.status,errorMessage:t.errorMessage,errorCode:t.errorCode}),w=Date.now()-e,h=t.status==="COMPLETED"?"\u2713":t.status==="FAILED"?"\u2717":"\u25CB";return {run:{id:o.id,workflowId:n,workflowName:s,status:g.status,durationMs:g.durationMs??w},events:{created:d,duplicates:u},totalCostUnits:g.totalCostUnits??null,summary:`${h} ${s}: ${d} events recorded (${g.durationMs??w}ms)`}}},S=f,R=null;function C(){return R||(R=new f),R}var O=new Proxy({},{get(m,t){let e=C(),n=e[t];return typeof n=="function"?n.bind(e):n}});
2
+ exports.Drip=f;exports.DripError=a;exports.default=S;exports.drip=O;//# sourceMappingURL=core.cjs.map
3
3
  //# sourceMappingURL=core.cjs.map