@drip-sdk/node 1.1.1 → 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 +54 -11
- package/dist/core.cjs +2 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +17 -3
- package/dist/core.d.ts +17 -3
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- package/dist/express.cjs +1 -1
- package/dist/express.cjs.map +1 -1
- package/dist/express.d.cts +2 -2
- package/dist/express.d.ts +2 -2
- package/dist/express.js +1 -1
- package/dist/express.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -12
- package/dist/index.d.ts +44 -12
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/langchain.cjs +2 -2
- package/dist/langchain.cjs.map +1 -1
- package/dist/langchain.js +2 -2
- package/dist/langchain.js.map +1 -1
- package/dist/middleware.cjs +2 -2
- package/dist/middleware.cjs.map +1 -1
- package/dist/middleware.d.cts +3 -3
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.js +2 -2
- package/dist/middleware.js.map +1 -1
- package/dist/next.cjs +2 -2
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +2 -2
- package/dist/next.d.ts +2 -2
- package/dist/next.js +2 -2
- package/dist/next.js.map +1 -1
- package/dist/{types-B2qwDadD.d.ts → types-BQzxIAqF.d.ts} +1 -1
- package/dist/{types-Bo8SiUdl.d.cts → types-DX3iqXqe.d.cts} +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -68,7 +68,7 @@ interface StreamMeterFlushResult {
|
|
|
68
68
|
/** The charge result from the API (if quantity > 0) */
|
|
69
69
|
charge: ChargeResult['charge'] | null;
|
|
70
70
|
/** Whether this was an idempotent replay */
|
|
71
|
-
|
|
71
|
+
isDuplicate: boolean;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Internal charge function type (injected by Drip class).
|
|
@@ -565,14 +565,21 @@ interface DripConfig {
|
|
|
565
565
|
interface CreateCustomerParams {
|
|
566
566
|
/**
|
|
567
567
|
* Your internal customer/user ID for reconciliation.
|
|
568
|
+
* At least one of `externalCustomerId` or `onchainAddress` is required.
|
|
568
569
|
* @example "user_12345"
|
|
569
570
|
*/
|
|
570
571
|
externalCustomerId?: string;
|
|
571
572
|
/**
|
|
572
573
|
* The customer's Drip Smart Account address (derived from their EOA).
|
|
574
|
+
* At least one of `externalCustomerId` or `onchainAddress` is required.
|
|
573
575
|
* @example "0x1234567890abcdef..."
|
|
574
576
|
*/
|
|
575
|
-
onchainAddress
|
|
577
|
+
onchainAddress?: string;
|
|
578
|
+
/**
|
|
579
|
+
* Whether this customer is internal-only (usage tracked but not billed).
|
|
580
|
+
* @default false
|
|
581
|
+
*/
|
|
582
|
+
isInternal?: boolean;
|
|
576
583
|
/**
|
|
577
584
|
* Additional metadata to store with the customer.
|
|
578
585
|
*/
|
|
@@ -588,8 +595,12 @@ interface Customer {
|
|
|
588
595
|
businessId?: string;
|
|
589
596
|
/** Your external customer ID (if provided) */
|
|
590
597
|
externalCustomerId: string | null;
|
|
591
|
-
/** Customer's on-chain address */
|
|
592
|
-
onchainAddress: string;
|
|
598
|
+
/** Customer's on-chain address (null for internal-only customers) */
|
|
599
|
+
onchainAddress: string | null;
|
|
600
|
+
/** Whether this customer is internal-only (usage tracked but not billed) */
|
|
601
|
+
isInternal: boolean;
|
|
602
|
+
/** Customer status */
|
|
603
|
+
status: 'ACTIVE' | 'LOW_BALANCE' | 'PAUSED';
|
|
593
604
|
/** Custom metadata */
|
|
594
605
|
metadata: Record<string, unknown> | null;
|
|
595
606
|
/** ISO timestamp of creation */
|
|
@@ -675,8 +686,8 @@ interface ChargeResult {
|
|
|
675
686
|
success: boolean;
|
|
676
687
|
/** The usage event ID */
|
|
677
688
|
usageEventId: string;
|
|
678
|
-
/** True if this was
|
|
679
|
-
|
|
689
|
+
/** True if this was a deduplicated replay (returned cached result from previous request) */
|
|
690
|
+
isDuplicate: boolean;
|
|
680
691
|
/** Details about the charge */
|
|
681
692
|
charge: {
|
|
682
693
|
/** Unique charge ID */
|
|
@@ -694,7 +705,7 @@ interface ChargeResult {
|
|
|
694
705
|
/**
|
|
695
706
|
* Possible charge statuses.
|
|
696
707
|
*/
|
|
697
|
-
type ChargeStatus = 'PENDING' | '
|
|
708
|
+
type ChargeStatus = 'PENDING' | 'PENDING_SETTLEMENT' | 'CONFIRMED' | 'FAILED' | 'REFUNDED';
|
|
698
709
|
/**
|
|
699
710
|
* Parameters for tracking usage without billing.
|
|
700
711
|
* Use this for internal visibility, pilots, or pre-billing tracking.
|
|
@@ -859,16 +870,22 @@ interface Webhook {
|
|
|
859
870
|
description: string | null;
|
|
860
871
|
/** Whether the webhook is active */
|
|
861
872
|
isActive: boolean;
|
|
873
|
+
/** Health status of the webhook endpoint */
|
|
874
|
+
healthStatus: 'HEALTHY' | 'DEGRADED' | 'UNHEALTHY';
|
|
875
|
+
/** Number of consecutive delivery failures */
|
|
876
|
+
consecutiveFailures: number;
|
|
877
|
+
/** ISO timestamp of last health status change */
|
|
878
|
+
lastHealthChange: string | null;
|
|
862
879
|
/** ISO timestamp of creation */
|
|
863
880
|
createdAt: string;
|
|
864
881
|
/** ISO timestamp of last update */
|
|
865
882
|
updatedAt: string;
|
|
866
883
|
/** Delivery statistics */
|
|
867
884
|
stats?: {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
885
|
+
total: number;
|
|
886
|
+
delivered: number;
|
|
887
|
+
failed: number;
|
|
888
|
+
pending: number;
|
|
872
889
|
};
|
|
873
890
|
}
|
|
874
891
|
/**
|
|
@@ -970,6 +987,8 @@ interface Workflow {
|
|
|
970
987
|
name: string;
|
|
971
988
|
slug: string;
|
|
972
989
|
productSurface: string;
|
|
990
|
+
/** Blockchain chain (if applicable) */
|
|
991
|
+
chain: string | null;
|
|
973
992
|
description: string | null;
|
|
974
993
|
isActive: boolean;
|
|
975
994
|
createdAt: string;
|
|
@@ -1248,8 +1267,13 @@ interface RecordRunResult {
|
|
|
1248
1267
|
interface RunTimeline {
|
|
1249
1268
|
runId: string;
|
|
1250
1269
|
workflowId: string | null;
|
|
1270
|
+
workflowName: string | null;
|
|
1251
1271
|
customerId: string;
|
|
1252
1272
|
status: RunStatus;
|
|
1273
|
+
correlationId: string | null;
|
|
1274
|
+
metadata: Record<string, unknown> | null;
|
|
1275
|
+
errorMessage: string | null;
|
|
1276
|
+
errorCode: string | null;
|
|
1253
1277
|
startedAt: string | null;
|
|
1254
1278
|
endedAt: string | null;
|
|
1255
1279
|
durationMs: number | null;
|
|
@@ -1787,8 +1811,11 @@ declare class Drip {
|
|
|
1787
1811
|
* ```
|
|
1788
1812
|
*/
|
|
1789
1813
|
getChargeStatus(chargeId: string): Promise<{
|
|
1814
|
+
id: string;
|
|
1790
1815
|
status: ChargeStatus;
|
|
1791
|
-
txHash
|
|
1816
|
+
txHash: string | null;
|
|
1817
|
+
confirmedAt: string | null;
|
|
1818
|
+
failureReason: string | null;
|
|
1792
1819
|
}>;
|
|
1793
1820
|
/**
|
|
1794
1821
|
* Creates a checkout session to add funds to a customer's account.
|
|
@@ -2257,6 +2284,11 @@ declare class Drip {
|
|
|
2257
2284
|
* ```
|
|
2258
2285
|
*/
|
|
2259
2286
|
recordRun(params: RecordRunParams): Promise<RecordRunResult>;
|
|
2287
|
+
/**
|
|
2288
|
+
* 4-step orchestration fallback for servers without POST /runs/record.
|
|
2289
|
+
* @internal
|
|
2290
|
+
*/
|
|
2291
|
+
private _recordRunFallback;
|
|
2260
2292
|
/**
|
|
2261
2293
|
* Generates a deterministic idempotency key.
|
|
2262
2294
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ interface StreamMeterFlushResult {
|
|
|
68
68
|
/** The charge result from the API (if quantity > 0) */
|
|
69
69
|
charge: ChargeResult['charge'] | null;
|
|
70
70
|
/** Whether this was an idempotent replay */
|
|
71
|
-
|
|
71
|
+
isDuplicate: boolean;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Internal charge function type (injected by Drip class).
|
|
@@ -565,14 +565,21 @@ interface DripConfig {
|
|
|
565
565
|
interface CreateCustomerParams {
|
|
566
566
|
/**
|
|
567
567
|
* Your internal customer/user ID for reconciliation.
|
|
568
|
+
* At least one of `externalCustomerId` or `onchainAddress` is required.
|
|
568
569
|
* @example "user_12345"
|
|
569
570
|
*/
|
|
570
571
|
externalCustomerId?: string;
|
|
571
572
|
/**
|
|
572
573
|
* The customer's Drip Smart Account address (derived from their EOA).
|
|
574
|
+
* At least one of `externalCustomerId` or `onchainAddress` is required.
|
|
573
575
|
* @example "0x1234567890abcdef..."
|
|
574
576
|
*/
|
|
575
|
-
onchainAddress
|
|
577
|
+
onchainAddress?: string;
|
|
578
|
+
/**
|
|
579
|
+
* Whether this customer is internal-only (usage tracked but not billed).
|
|
580
|
+
* @default false
|
|
581
|
+
*/
|
|
582
|
+
isInternal?: boolean;
|
|
576
583
|
/**
|
|
577
584
|
* Additional metadata to store with the customer.
|
|
578
585
|
*/
|
|
@@ -588,8 +595,12 @@ interface Customer {
|
|
|
588
595
|
businessId?: string;
|
|
589
596
|
/** Your external customer ID (if provided) */
|
|
590
597
|
externalCustomerId: string | null;
|
|
591
|
-
/** Customer's on-chain address */
|
|
592
|
-
onchainAddress: string;
|
|
598
|
+
/** Customer's on-chain address (null for internal-only customers) */
|
|
599
|
+
onchainAddress: string | null;
|
|
600
|
+
/** Whether this customer is internal-only (usage tracked but not billed) */
|
|
601
|
+
isInternal: boolean;
|
|
602
|
+
/** Customer status */
|
|
603
|
+
status: 'ACTIVE' | 'LOW_BALANCE' | 'PAUSED';
|
|
593
604
|
/** Custom metadata */
|
|
594
605
|
metadata: Record<string, unknown> | null;
|
|
595
606
|
/** ISO timestamp of creation */
|
|
@@ -675,8 +686,8 @@ interface ChargeResult {
|
|
|
675
686
|
success: boolean;
|
|
676
687
|
/** The usage event ID */
|
|
677
688
|
usageEventId: string;
|
|
678
|
-
/** True if this was
|
|
679
|
-
|
|
689
|
+
/** True if this was a deduplicated replay (returned cached result from previous request) */
|
|
690
|
+
isDuplicate: boolean;
|
|
680
691
|
/** Details about the charge */
|
|
681
692
|
charge: {
|
|
682
693
|
/** Unique charge ID */
|
|
@@ -694,7 +705,7 @@ interface ChargeResult {
|
|
|
694
705
|
/**
|
|
695
706
|
* Possible charge statuses.
|
|
696
707
|
*/
|
|
697
|
-
type ChargeStatus = 'PENDING' | '
|
|
708
|
+
type ChargeStatus = 'PENDING' | 'PENDING_SETTLEMENT' | 'CONFIRMED' | 'FAILED' | 'REFUNDED';
|
|
698
709
|
/**
|
|
699
710
|
* Parameters for tracking usage without billing.
|
|
700
711
|
* Use this for internal visibility, pilots, or pre-billing tracking.
|
|
@@ -859,16 +870,22 @@ interface Webhook {
|
|
|
859
870
|
description: string | null;
|
|
860
871
|
/** Whether the webhook is active */
|
|
861
872
|
isActive: boolean;
|
|
873
|
+
/** Health status of the webhook endpoint */
|
|
874
|
+
healthStatus: 'HEALTHY' | 'DEGRADED' | 'UNHEALTHY';
|
|
875
|
+
/** Number of consecutive delivery failures */
|
|
876
|
+
consecutiveFailures: number;
|
|
877
|
+
/** ISO timestamp of last health status change */
|
|
878
|
+
lastHealthChange: string | null;
|
|
862
879
|
/** ISO timestamp of creation */
|
|
863
880
|
createdAt: string;
|
|
864
881
|
/** ISO timestamp of last update */
|
|
865
882
|
updatedAt: string;
|
|
866
883
|
/** Delivery statistics */
|
|
867
884
|
stats?: {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
885
|
+
total: number;
|
|
886
|
+
delivered: number;
|
|
887
|
+
failed: number;
|
|
888
|
+
pending: number;
|
|
872
889
|
};
|
|
873
890
|
}
|
|
874
891
|
/**
|
|
@@ -970,6 +987,8 @@ interface Workflow {
|
|
|
970
987
|
name: string;
|
|
971
988
|
slug: string;
|
|
972
989
|
productSurface: string;
|
|
990
|
+
/** Blockchain chain (if applicable) */
|
|
991
|
+
chain: string | null;
|
|
973
992
|
description: string | null;
|
|
974
993
|
isActive: boolean;
|
|
975
994
|
createdAt: string;
|
|
@@ -1248,8 +1267,13 @@ interface RecordRunResult {
|
|
|
1248
1267
|
interface RunTimeline {
|
|
1249
1268
|
runId: string;
|
|
1250
1269
|
workflowId: string | null;
|
|
1270
|
+
workflowName: string | null;
|
|
1251
1271
|
customerId: string;
|
|
1252
1272
|
status: RunStatus;
|
|
1273
|
+
correlationId: string | null;
|
|
1274
|
+
metadata: Record<string, unknown> | null;
|
|
1275
|
+
errorMessage: string | null;
|
|
1276
|
+
errorCode: string | null;
|
|
1253
1277
|
startedAt: string | null;
|
|
1254
1278
|
endedAt: string | null;
|
|
1255
1279
|
durationMs: number | null;
|
|
@@ -1787,8 +1811,11 @@ declare class Drip {
|
|
|
1787
1811
|
* ```
|
|
1788
1812
|
*/
|
|
1789
1813
|
getChargeStatus(chargeId: string): Promise<{
|
|
1814
|
+
id: string;
|
|
1790
1815
|
status: ChargeStatus;
|
|
1791
|
-
txHash
|
|
1816
|
+
txHash: string | null;
|
|
1817
|
+
confirmedAt: string | null;
|
|
1818
|
+
failureReason: string | null;
|
|
1792
1819
|
}>;
|
|
1793
1820
|
/**
|
|
1794
1821
|
* Creates a checkout session to add funds to a customer's account.
|
|
@@ -2257,6 +2284,11 @@ declare class Drip {
|
|
|
2257
2284
|
* ```
|
|
2258
2285
|
*/
|
|
2259
2286
|
recordRun(params: RecordRunParams): Promise<RecordRunResult>;
|
|
2287
|
+
/**
|
|
2288
|
+
* 4-step orchestration fallback for servers without POST /runs/record.
|
|
2289
|
+
* @internal
|
|
2290
|
+
*/
|
|
2291
|
+
private _recordRunFallback;
|
|
2260
2292
|
/**
|
|
2261
2293
|
* Generates a deterministic idempotency key.
|
|
2262
2294
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {createHash}from'crypto';var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var K=0;function b(o,...e){let t=++K,r=e.filter(s=>s!==void 0).map(String);r.push(String(t));let n=createHash("sha256").update(r.join("|")).digest("hex").slice(0,24);return `${o}_${n}`}var k=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,t){this._chargeFn=e,this._options=t;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isReplay:false};let t=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:b("stream",this._options.customerId,this._options.meter,e,this._flushCount),r=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:t,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let n={success:r.success,quantity:e,charge:r.charge,isReplay:r.isReplay};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var O={requestsPerSecond:100,burstSize:200,enabled:true},T=class{config;tokens;lastRefill;constructor(e){this.config={...O,...e},this.tokens=this.config.burstSize,this.lastRefill=Date.now();}refill(){let e=Date.now(),t=(e-this.lastRefill)/1e3;this.tokens=Math.min(this.config.burstSize,this.tokens+t*this.config.requestsPerSecond),this.lastRefill=e;}async acquire(e){if(!this.config.enabled)return true;let t=e!==void 0?Date.now()+e:void 0;for(;;){if(this.refill(),this.tokens>=1)return this.tokens-=1,true;let r=(1-this.tokens)/this.config.requestsPerSecond*1e3;if(t!==void 0){let n=t-Date.now();if(n<=0)return false;await this.sleep(Math.min(r,n));}else await this.sleep(r);}}tryAcquire(){return this.config.enabled?(this.refill(),this.tokens>=1?(this.tokens-=1,true):false):true}get availableTokens(){return this.refill(),this.tokens}sleep(e){return new Promise(t=>setTimeout(t,e))}},H={maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},E=class o extends Error{attempts;lastError;constructor(e,t){super(`Retry exhausted after ${e} attempts: ${t.message}`),this.name="RetryExhaustedError",this.attempts=e,this.lastError=t,Object.setPrototypeOf(this,o.prototype);}};function N(o,e){let t=e.baseDelayMs*Math.pow(e.exponentialBase,o);if(t=Math.min(t,e.maxDelayMs),e.jitter>0){let r=t*e.jitter;t+=Math.random()*2*r-r;}return Math.max(0,t)}function W(o,e){if(o instanceof Error){if(o.message.includes("fetch")||o.message.includes("network")||o.message.includes("ECONNREFUSED")||o.message.includes("ETIMEDOUT"))return true;let t=o.statusCode;if(t!==void 0)return e.retryableStatusCodes.includes(t)}return false}var B={failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},P=class o extends Error{circuitName;timeUntilRetryMs;constructor(e,t){super(`Circuit '${e}' is open. Retry in ${Math.round(t)}ms`),this.name="CircuitBreakerOpenError",this.circuitName=e,this.timeUntilRetryMs=t,Object.setPrototypeOf(this,o.prototype);}},x=class{name;config;state="closed";failureCount=0;successCount=0;lastFailureTime=null;constructor(e,t){this.name=e,this.config={...B,...t};}getState(){return this.checkStateTransition(),this.state}checkStateTransition(){this.state==="open"&&this.lastFailureTime!==null&&Date.now()-this.lastFailureTime>=this.config.timeoutMs&&(this.state="half_open",this.successCount=0);}recordSuccess(){this.config.enabled&&(this.state==="half_open"?(this.successCount+=1,this.successCount>=this.config.successThreshold&&(this.state="closed",this.failureCount=0)):this.state==="closed"&&(this.failureCount=0));}recordFailure(){this.config.enabled&&(this.failureCount+=1,this.lastFailureTime=Date.now(),this.state==="half_open"?this.state="open":this.state==="closed"&&this.failureCount>=this.config.failureThreshold&&(this.state="open"));}allowRequest(){return !this.config.enabled||(this.checkStateTransition(),this.state==="closed")?true:this.state==="half_open"}getTimeUntilRetry(){if(this.state!=="open"||this.lastFailureTime===null)return 0;let e=Date.now()-this.lastFailureTime;return Math.max(0,this.config.timeoutMs-e)}reset(){this.state="closed",this.failureCount=0,this.successCount=0,this.lastFailureTime=null;}},I=class{windowSize;metrics=[];totalRequests=0;totalSuccesses=0;totalFailures=0;constructor(e=1e3){this.windowSize=e;}record(e){for(this.metrics.push(e),this.totalRequests+=1,e.success?this.totalSuccesses+=1:this.totalFailures+=1;this.metrics.length>this.windowSize;)this.metrics.shift();}getSummary(){if(this.metrics.length===0)return {windowSize:0,totalRequests:0,totalSuccesses:0,totalFailures:0,successRate:0,avgLatencyMs:0,minLatencyMs:0,maxLatencyMs:0,p50LatencyMs:0,p95LatencyMs:0,p99LatencyMs:0,requestsByEndpoint:{},errorsByType:{}};let e=this.metrics.map(i=>i.durationMs).sort((i,a)=>i-a),t=this.metrics.filter(i=>i.success).length,r={};for(let i of this.metrics)r[i.endpoint]=(r[i.endpoint]??0)+1;let n={};for(let i of this.metrics)i.error&&(n[i.error]=(n[i.error]??0)+1);let s=e.reduce((i,a)=>i+a,0);return {windowSize:this.metrics.length,totalRequests:this.totalRequests,totalSuccesses:this.totalSuccesses,totalFailures:this.totalFailures,successRate:t/this.metrics.length*100,avgLatencyMs:s/e.length,minLatencyMs:e[0],maxLatencyMs:e[e.length-1],p50LatencyMs:e[Math.floor(e.length*.5)],p95LatencyMs:e[Math.floor(e.length*.95)],p99LatencyMs:e[Math.floor(e.length*.99)],requestsByEndpoint:r,errorsByType:n}}reset(){this.metrics.length=0,this.totalRequests=0,this.totalSuccesses=0,this.totalFailures=0;}};function R(){return {rateLimiter:{requestsPerSecond:100,burstSize:200,enabled:true},retry:{maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},collectMetrics:true}}function j(){return {rateLimiter:{...O,enabled:false},retry:{...H,enabled:false},circuitBreaker:{...B,enabled:false},collectMetrics:false}}function q(){return {rateLimiter:{requestsPerSecond:1e3,burstSize:2e3,enabled:true},retry:{maxRetries:2,baseDelayMs:50,maxDelayMs:5e3,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:10,successThreshold:3,timeoutMs:15e3,enabled:true},collectMetrics:true}}var C=class{config;rateLimiter;circuitBreaker;metrics;constructor(e){this.config={...R(),...e,rateLimiter:{...R().rateLimiter,...e?.rateLimiter},retry:{...R().retry,...e?.retry},circuitBreaker:{...R().circuitBreaker,...e?.circuitBreaker}},this.rateLimiter=new T(this.config.rateLimiter),this.circuitBreaker=new x("drip_api",this.config.circuitBreaker),this.metrics=this.config.collectMetrics?new I:null;}async execute(e,t="UNKNOWN",r="unknown"){let n=performance.now(),s=0,i=null;if(!await this.rateLimiter.acquire(3e4))throw new Error("Rate limiter timeout");if(!this.circuitBreaker.allowRequest())throw new P(this.circuitBreaker.name,this.circuitBreaker.getTimeUntilRetry());for(let u=0;u<=this.config.retry.maxRetries;u++)try{let c=await e();if(this.circuitBreaker.recordSuccess(),this.metrics){let d=performance.now()-n;this.metrics.record({method:t,endpoint:r,statusCode:200,durationMs:d,success:!0,timestamp:Date.now(),retryCount:s});}return c}catch(c){if(i=c instanceof Error?c:new Error(String(c)),this.config.retry.enabled&&W(c,this.config.retry)&&u<this.config.retry.maxRetries){s+=1;let p=N(u,this.config.retry);await this.sleep(p);continue}if(this.circuitBreaker.recordFailure(),this.metrics){let p=performance.now()-n,y=c.statusCode??null;this.metrics.record({method:t,endpoint:r,statusCode:y,durationMs:p,success:false,timestamp:Date.now(),error:i.name,retryCount:s});}throw c}throw i?new E(this.config.retry.maxRetries+1,i):new Error("Unexpected execution path")}getMetrics(){return this.metrics?.getSummary()??null}getHealth(){return {circuitBreaker:{state:this.circuitBreaker.getState(),timeUntilRetryMs:this.circuitBreaker.getTimeUntilRetry()},rateLimiter:{availableTokens:this.rateLimiter.availableTokens,requestsPerSecond:this.config.rateLimiter.requestsPerSecond},metrics:this.getMetrics()}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var A={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function G(o){return o instanceof Error&&(o.message.includes("fetch")||o.message.includes("network"))?true:o instanceof f?o.statusCode>=500||o.statusCode===408||o.statusCode===429:false}async function z(o,e={}){let t=e.maxAttempts??A.maxAttempts,r=e.baseDelayMs??A.baseDelayMs,n=e.maxDelayMs??A.maxDelayMs,s=e.isRetryable??G,i;for(let a=1;a<=t;a++)try{return await o()}catch(u){if(i=u,a===t||!s(u))throw u;let c=Math.min(r*Math.pow(2,a-1)+Math.random()*100,n);await new Promise(d=>setTimeout(d,c));}throw i}var f=class o extends Error{constructor(t,r,n){super(t);this.statusCode=r;this.code=n;this.name="DripError",Object.setPrototypeOf(this,o.prototype);}},M=class{apiKey;baseUrl;timeout;resilience;keyType;constructor(e={}){let t=e.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),r=e.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!t)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=t,this.baseUrl=r||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=e.timeout||3e4,t.startsWith("sk_")?this.keyType="secret":t.startsWith("pk_")?this.keyType="public":this.keyType="unknown",e.resilience===true?this.resilience=new C(R()):e.resilience==="high-throughput"?this.resilience=new C(q()):e.resilience&&typeof e.resilience=="object"?this.resilience=new C(e.resilience):this.resilience=null;}assertSecretKey(e){if(this.keyType==="public")throw new f(`${e} requires a secret key (sk_). You are using a public key (pk_), which cannot access this endpoint. Use a secret key for webhook, API key, and feature flag management.`,403,"PUBLIC_KEY_NOT_ALLOWED")}async request(e,t={}){let r=(t.method??"GET").toUpperCase();return this.resilience?this.resilience.execute(()=>this.rawRequest(e,t),r,e):this.rawRequest(e,t)}async rawRequest(e,t={}){let r=new AbortController,n=setTimeout(()=>r.abort(),this.timeout);try{let s=await fetch(`${this.baseUrl}${e}`,{...t,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...t.headers}});if(s.status===204)return {success:!0};let i=await s.json();if(!s.ok)throw new f(i.message||i.error||"Request failed",s.status,i.code);return i}catch(s){throw s instanceof f?s:s instanceof Error&&s.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let e=new AbortController,t=setTimeout(()=>e.abort(),this.timeout),r=this.baseUrl;r.endsWith("/v1/")?r=r.slice(0,-4):r.endsWith("/v1")&&(r=r.slice(0,-3)),r=r.replace(/\/+$/,"");let n=Date.now();try{let s=await fetch(`${r}/health`,{signal:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),i=Date.now()-n,a="unknown",u=Date.now();try{let c=await s.json();typeof c.status=="string"&&(a=c.status),typeof c.timestamp=="number"&&(u=c.timestamp);}catch{a=s.ok?"healthy":`error:${s.status}`;}return !s.ok&&a==="unknown"&&(a=`error:${s.status}`),{ok:s.ok&&a==="healthy",status:a,latencyMs:i,timestamp:u}}catch(s){throw s instanceof Error&&s.name==="AbortError"?new f("Request timed out",408,"TIMEOUT"):new f(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(t);}}getMetrics(){return this.resilience?.getMetrics()??null}getHealth(){return this.resilience?.getHealth()??null}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let t=new URLSearchParams;e?.limit&&t.set("limit",e.limit.toString()),e?.status&&t.set("status",e.status);let r=t.toString(),n=r?`/customers?${r}`:"/customers";return this.request(n)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){let t=e.idempotencyKey??b("chg",e.customerId,e.meter,e.quantity);return this.request("/usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,metadata:e.metadata})})}async wrapApiCall(e){let t=e.idempotencyKey??`wrap_${Date.now()}_${Math.random().toString(36).slice(2,11)}`,r=await e.call(),n=e.extractUsage(r),s=await z(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:n,idempotencyKey:t,metadata:e.metadata}),e.retryOptions);return {result:r,charge:s,idempotencyKey:t}}async trackUsage(e){let t=e.idempotencyKey??b("track",e.customerId,e.meter,e.quantity);return this.request("/usage/internal",{method:"POST",body:JSON.stringify({customerId:e.customerId,usageType:e.meter,quantity:e.quantity,idempotencyKey:t,units:e.units,description:e.description,metadata:e.metadata})})}async getCharge(e){return this.request(`/charges/${e}`)}async listCharges(e){let t=new URLSearchParams;e?.customerId&&t.set("customerId",e.customerId),e?.status&&t.set("status",e.status),e?.limit&&t.set("limit",e.limit.toString()),e?.offset&&t.set("offset",e.offset.toString());let r=t.toString(),n=r?`/charges?${r}`:"/charges";return this.request(n)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let t=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:t.id,url:t.url,expiresAt:t.expires_at,amountUsd:t.amount_usd}}async createWebhook(e){return this.assertSecretKey("createWebhook()"),this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.assertSecretKey("listWebhooks()"),this.request("/webhooks")}async getWebhook(e){return this.assertSecretKey("getWebhook()"),this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.assertSecretKey("deleteWebhook()"),this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.assertSecretKey("testWebhook()"),this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.assertSecretKey("rotateWebhookSecret()"),this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,t){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(t)})}async getRun(e){return this.request(`/runs/${e}`)}async getRunTimeline(e,t){let r=new URLSearchParams;t?.limit&&r.set("limit",t.limit.toString()),t?.cursor&&r.set("cursor",t.cursor),t?.includeAnomalies!==void 0&&r.set("includeAnomalies",String(t.includeAnomalies)),t?.collapseRetries!==void 0&&r.set("collapseRetries",String(t.collapseRetries));let n=r.toString(),s=n?`/runs/${e}/timeline?${n}`:`/runs/${e}/timeline`;return this.request(s)}async emitEvent(e){let t=e.idempotencyKey??b("evt",e.runId,e.eventType,e.quantity);return this.request("/run-events",{method:"POST",body:JSON.stringify({...e,idempotencyKey:t})})}async emitEventsBatch(e){return this.request("/run-events/batch",{method:"POST",body:JSON.stringify({events:e})})}async listMeters(){let e=await this.request("/pricing-plans");return {data:e.data.map(t=>({id:t.id,name:t.name,meter:t.unitType,unitPriceUsd:t.unitPriceUsd,isActive:t.isActive})),count:e.count}}async estimateFromUsage(e){let t=e.periodStart instanceof Date?e.periodStart.toISOString():e.periodStart,r=e.periodEnd instanceof Date?e.periodEnd.toISOString():e.periodEnd;return this.request("/dashboard/cost-estimate/from-usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,periodStart:t,periodEnd:r,defaultUnitPrice:e.defaultUnitPrice,includeChargedEvents:e.includeChargedEvents,usageTypes:e.usageTypes,customPricing:e.customPricing})})}async estimateFromHypothetical(e){return this.request("/dashboard/cost-estimate/hypothetical",{method:"POST",body:JSON.stringify({items:e.items,defaultUnitPrice:e.defaultUnitPrice,customPricing:e.customPricing})})}async recordRun(e){let t=Date.now(),r=e.workflow,n=e.workflow;if(!e.workflow.startsWith("wf_"))try{let m=(await this.listWorkflows()).data.find(l=>l.slug===e.workflow||l.id===e.workflow);if(m)r=m.id,n=m.name;else {let l=await this.createWorkflow({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,g=>g.toUpperCase()),slug:e.workflow,productSurface:"AGENT"});r=l.id,n=l.name;}}catch{r=e.workflow;}let s=await this.startRun({customerId:e.customerId,workflowId:r,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata}),i=0,a=0;if(e.events.length>0){let w=e.events.map((l,g)=>({runId:s.id,eventType:l.eventType,quantity:l.quantity,units:l.units,description:l.description,costUnits:l.costUnits,metadata:l.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${l.eventType}:${g}`:b("run",s.id,l.eventType,g)})),m=await this.emitEventsBatch(w);i=m.created,a=m.duplicates;}let u=await this.endRun(s.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),c=Date.now()-t,d=e.events.length>0?`${i} events recorded`:"no events",y=`${e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB"} ${n}: ${d} (${u.durationMs??c}ms)`;return {run:{id:s.id,workflowId:r,workflowName:n,status:e.status,durationMs:u.durationMs},events:{created:i,duplicates:a},totalCostUnits:u.totalCostUnits,summary:y}}static generateIdempotencyKey(e){let r=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)].join("|");return `drip_${S("crypto").createHash("sha256").update(r).digest("hex").slice(0,32)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(h=>h.startsWith("t=")),a=s.find(h=>h.startsWith("v1="));if(!i||!a)return !1;let u=parseInt(i.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let p=`${u}.${e}`,y=new TextEncoder,w=y.encode(r),m=y.encode(p),l=globalThis.crypto?.subtle??S("crypto").webcrypto.subtle,g=await l.importKey("raw",w,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),F=await l.sign("HMAC",g,m),L=Array.from(new Uint8Array(F)).map(h=>h.toString(16).padStart(2,"0")).join("");if(c.length!==L.length)return !1;let _=0;for(let h=0;h<c.length;h++)_|=c.charCodeAt(h)^L.charCodeAt(h);return _===0}catch{return false}}static verifyWebhookSignatureSync(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(g=>g.startsWith("t=")),a=s.find(g=>g.startsWith("v1="));if(!i||!a)return !1;let u=parseInt(i.slice(2),10),c=a.slice(3);if(isNaN(u))return !1;let d=Math.floor(Date.now()/1e3);if(Math.abs(d-u)>n)return !1;let p=S("crypto"),y=`${u}.${e}`,w=p.createHmac("sha256",r).update(y).digest("hex"),m=Buffer.from(c),l=Buffer.from(w);return m.length!==l.length?!1:p.timingSafeEqual(m,l)}catch{return false}}static generateWebhookSignature(e,t,r){let n=S("crypto"),s=r??Math.floor(Date.now()/1e3),i=`${s}.${e}`,a=n.createHmac("sha256",t).update(i).digest("hex");return `t=${s},v1=${a}`}createStreamMeter(e){return new k(this.charge.bind(this),e)}},ue=M,U=null;function J(){return U||(U=new M),U}var ce=new Proxy({},{get(o,e){let t=J(),r=t[e];return typeof r=="function"?r.bind(t):r}});
|
|
2
|
-
export{
|
|
1
|
+
import {createHash}from'crypto';var S=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+o+'" is not supported')});var K=0;function w(o,...e){let t=++K,r=e.filter(s=>s!==void 0).map(String);r.push(String(t));let n=createHash("sha256").update(r.join("|")).digest("hex").slice(0,24);return `${o}_${n}`}var k=class{_total=0;_flushed=false;_flushCount=0;_chargeFn;_options;constructor(e,t){this._chargeFn=e,this._options=t;}get total(){return this._total}get isFlushed(){return this._flushed}get flushCount(){return this._flushCount}async add(e){return e<=0?null:(this._total+=e,this._options.onAdd?.(e,this._total),this._options.flushThreshold!==void 0&&this._total>=this._options.flushThreshold?this.flush():null)}addSync(e){e<=0||(this._total+=e,this._options.onAdd?.(e,this._total));}async flush(){let e=this._total;if(this._total=0,e===0)return {success:true,quantity:0,charge:null,isDuplicate:false};let t=this._options.idempotencyKey?`${this._options.idempotencyKey}_flush_${this._flushCount}`:w("stream",this._options.customerId,this._options.meter,e,this._flushCount),r=await this._chargeFn({customerId:this._options.customerId,meter:this._options.meter,quantity:e,idempotencyKey:t,metadata:this._options.metadata});this._flushed=true,this._flushCount++;let n={success:r.success,quantity:e,charge:r.charge,isDuplicate:r.isDuplicate};return this._options.onFlush?.(n),n}reset(){this._total=0;}};var O={requestsPerSecond:100,burstSize:200,enabled:true},T=class{config;tokens;lastRefill;constructor(e){this.config={...O,...e},this.tokens=this.config.burstSize,this.lastRefill=Date.now();}refill(){let e=Date.now(),t=(e-this.lastRefill)/1e3;this.tokens=Math.min(this.config.burstSize,this.tokens+t*this.config.requestsPerSecond),this.lastRefill=e;}async acquire(e){if(!this.config.enabled)return true;let t=e!==void 0?Date.now()+e:void 0;for(;;){if(this.refill(),this.tokens>=1)return this.tokens-=1,true;let r=(1-this.tokens)/this.config.requestsPerSecond*1e3;if(t!==void 0){let n=t-Date.now();if(n<=0)return false;await this.sleep(Math.min(r,n));}else await this.sleep(r);}}tryAcquire(){return this.config.enabled?(this.refill(),this.tokens>=1?(this.tokens-=1,true):false):true}get availableTokens(){return this.refill(),this.tokens}sleep(e){return new Promise(t=>setTimeout(t,e))}},H={maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},E=class o extends Error{attempts;lastError;constructor(e,t){super(`Retry exhausted after ${e} attempts: ${t.message}`),this.name="RetryExhaustedError",this.attempts=e,this.lastError=t,Object.setPrototypeOf(this,o.prototype);}};function N(o,e){let t=e.baseDelayMs*Math.pow(e.exponentialBase,o);if(t=Math.min(t,e.maxDelayMs),e.jitter>0){let r=t*e.jitter;t+=Math.random()*2*r-r;}return Math.max(0,t)}function W(o,e){if(o instanceof Error){if(o.message.includes("fetch")||o.message.includes("network")||o.message.includes("ECONNREFUSED")||o.message.includes("ETIMEDOUT"))return true;let t=o.statusCode;if(t!==void 0)return e.retryableStatusCodes.includes(t)}return false}var B={failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},P=class o extends Error{circuitName;timeUntilRetryMs;constructor(e,t){super(`Circuit '${e}' is open. Retry in ${Math.round(t)}ms`),this.name="CircuitBreakerOpenError",this.circuitName=e,this.timeUntilRetryMs=t,Object.setPrototypeOf(this,o.prototype);}},I=class{name;config;state="closed";failureCount=0;successCount=0;lastFailureTime=null;constructor(e,t){this.name=e,this.config={...B,...t};}getState(){return this.checkStateTransition(),this.state}checkStateTransition(){this.state==="open"&&this.lastFailureTime!==null&&Date.now()-this.lastFailureTime>=this.config.timeoutMs&&(this.state="half_open",this.successCount=0);}recordSuccess(){this.config.enabled&&(this.state==="half_open"?(this.successCount+=1,this.successCount>=this.config.successThreshold&&(this.state="closed",this.failureCount=0)):this.state==="closed"&&(this.failureCount=0));}recordFailure(){this.config.enabled&&(this.failureCount+=1,this.lastFailureTime=Date.now(),this.state==="half_open"?this.state="open":this.state==="closed"&&this.failureCount>=this.config.failureThreshold&&(this.state="open"));}allowRequest(){return !this.config.enabled||(this.checkStateTransition(),this.state==="closed")?true:this.state==="half_open"}getTimeUntilRetry(){if(this.state!=="open"||this.lastFailureTime===null)return 0;let e=Date.now()-this.lastFailureTime;return Math.max(0,this.config.timeoutMs-e)}reset(){this.state="closed",this.failureCount=0,this.successCount=0,this.lastFailureTime=null;}},x=class{windowSize;metrics=[];totalRequests=0;totalSuccesses=0;totalFailures=0;constructor(e=1e3){this.windowSize=e;}record(e){for(this.metrics.push(e),this.totalRequests+=1,e.success?this.totalSuccesses+=1:this.totalFailures+=1;this.metrics.length>this.windowSize;)this.metrics.shift();}getSummary(){if(this.metrics.length===0)return {windowSize:0,totalRequests:0,totalSuccesses:0,totalFailures:0,successRate:0,avgLatencyMs:0,minLatencyMs:0,maxLatencyMs:0,p50LatencyMs:0,p95LatencyMs:0,p99LatencyMs:0,requestsByEndpoint:{},errorsByType:{}};let e=this.metrics.map(i=>i.durationMs).sort((i,a)=>i-a),t=this.metrics.filter(i=>i.success).length,r={};for(let i of this.metrics)r[i.endpoint]=(r[i.endpoint]??0)+1;let n={};for(let i of this.metrics)i.error&&(n[i.error]=(n[i.error]??0)+1);let s=e.reduce((i,a)=>i+a,0);return {windowSize:this.metrics.length,totalRequests:this.totalRequests,totalSuccesses:this.totalSuccesses,totalFailures:this.totalFailures,successRate:t/this.metrics.length*100,avgLatencyMs:s/e.length,minLatencyMs:e[0],maxLatencyMs:e[e.length-1],p50LatencyMs:e[Math.floor(e.length*.5)],p95LatencyMs:e[Math.floor(e.length*.95)],p99LatencyMs:e[Math.floor(e.length*.99)],requestsByEndpoint:r,errorsByType:n}}reset(){this.metrics.length=0,this.totalRequests=0,this.totalSuccesses=0,this.totalFailures=0;}};function R(){return {rateLimiter:{requestsPerSecond:100,burstSize:200,enabled:true},retry:{maxRetries:3,baseDelayMs:100,maxDelayMs:1e4,exponentialBase:2,jitter:.1,retryableStatusCodes:[429,500,502,503,504],enabled:true},circuitBreaker:{failureThreshold:5,successThreshold:2,timeoutMs:3e4,enabled:true},collectMetrics:true}}function j(){return {rateLimiter:{...O,enabled:false},retry:{...H,enabled:false},circuitBreaker:{...B,enabled:false},collectMetrics:false}}function 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 C=class{config;rateLimiter;circuitBreaker;metrics;constructor(e){this.config={...R(),...e,rateLimiter:{...R().rateLimiter,...e?.rateLimiter},retry:{...R().retry,...e?.retry},circuitBreaker:{...R().circuitBreaker,...e?.circuitBreaker}},this.rateLimiter=new T(this.config.rateLimiter),this.circuitBreaker=new I("drip_api",this.config.circuitBreaker),this.metrics=this.config.collectMetrics?new x:null;}async execute(e,t="UNKNOWN",r="unknown"){let n=performance.now(),s=0,i=null;if(!await this.rateLimiter.acquire(3e4))throw new Error("Rate limiter timeout");if(!this.circuitBreaker.allowRequest())throw new P(this.circuitBreaker.name,this.circuitBreaker.getTimeUntilRetry());for(let c=0;c<=this.config.retry.maxRetries;c++)try{let u=await e();if(this.circuitBreaker.recordSuccess(),this.metrics){let l=performance.now()-n;this.metrics.record({method:t,endpoint:r,statusCode:200,durationMs:l,success:!0,timestamp:Date.now(),retryCount:s});}return u}catch(u){if(i=u instanceof Error?u:new Error(String(u)),this.config.retry.enabled&&W(u,this.config.retry)&&c<this.config.retry.maxRetries){s+=1;let m=N(c,this.config.retry);await this.sleep(m);continue}if(this.circuitBreaker.recordFailure(),this.metrics){let m=performance.now()-n,y=u.statusCode??null;this.metrics.record({method:t,endpoint:r,statusCode:y,durationMs:m,success:false,timestamp:Date.now(),error:i.name,retryCount:s});}throw u}throw i?new E(this.config.retry.maxRetries+1,i):new Error("Unexpected execution path")}getMetrics(){return this.metrics?.getSummary()??null}getHealth(){return {circuitBreaker:{state:this.circuitBreaker.getState(),timeUntilRetryMs:this.circuitBreaker.getTimeUntilRetry()},rateLimiter:{availableTokens:this.rateLimiter.availableTokens,requestsPerSecond:this.config.rateLimiter.requestsPerSecond},metrics:this.getMetrics()}}sleep(e){return new Promise(t=>setTimeout(t,e))}};var q={maxAttempts:3,baseDelayMs:100,maxDelayMs:5e3};function G(o){return o instanceof Error&&(o.message.includes("fetch")||o.message.includes("network"))?true:o instanceof h?o.statusCode>=500||o.statusCode===408||o.statusCode===429:false}async function z(o,e={}){let t=e.maxAttempts??q.maxAttempts,r=e.baseDelayMs??q.baseDelayMs,n=e.maxDelayMs??q.maxDelayMs,s=e.isRetryable??G,i;for(let a=1;a<=t;a++)try{return await o()}catch(c){if(i=c,a===t||!s(c))throw c;let u=Math.min(r*Math.pow(2,a-1)+Math.random()*100,n);await new Promise(l=>setTimeout(l,u));}throw i}var h=class o extends Error{constructor(t,r,n){super(t);this.statusCode=r;this.code=n;this.name="DripError",Object.setPrototypeOf(this,o.prototype);}},M=class{apiKey;baseUrl;timeout;resilience;keyType;constructor(e={}){let t=e.apiKey??(typeof process<"u"?process.env.DRIP_API_KEY:void 0),r=e.baseUrl??(typeof process<"u"?process.env.DRIP_BASE_URL:void 0);if(!t)throw new Error("Drip API key is required. Either pass { apiKey } to constructor or set DRIP_API_KEY environment variable.");this.apiKey=t,this.baseUrl=r||"https://drip-app-hlunj.ondigitalocean.app/v1",this.timeout=e.timeout||3e4,t.startsWith("sk_")?this.keyType="secret":t.startsWith("pk_")?this.keyType="public":this.keyType="unknown",e.resilience===true?this.resilience=new C(R()):e.resilience==="high-throughput"?this.resilience=new C(A()):e.resilience&&typeof e.resilience=="object"?this.resilience=new C(e.resilience):this.resilience=null;}assertSecretKey(e){if(this.keyType==="public")throw new h(`${e} requires a secret key (sk_). You are using a public key (pk_), which cannot access this endpoint. Use a secret key for webhook, API key, and feature flag management.`,403,"PUBLIC_KEY_NOT_ALLOWED")}async request(e,t={}){let r=(t.method??"GET").toUpperCase();return this.resilience?this.resilience.execute(()=>this.rawRequest(e,t),r,e):this.rawRequest(e,t)}async rawRequest(e,t={}){let r=new AbortController,n=setTimeout(()=>r.abort(),this.timeout);try{let s=await fetch(`${this.baseUrl}${e}`,{...t,signal:r.signal,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,...t.headers}});if(s.status===204)return {success:!0};let i=await s.json();if(!s.ok)throw new h(i.message||i.error||"Request failed",s.status,i.code);return i}catch(s){throw s instanceof h?s:s instanceof Error&&s.name==="AbortError"?new h("Request timed out",408,"TIMEOUT"):new h(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(n);}}async ping(){let e=new AbortController,t=setTimeout(()=>e.abort(),this.timeout),r=this.baseUrl;r.endsWith("/v1/")?r=r.slice(0,-4):r.endsWith("/v1")&&(r=r.slice(0,-3)),r=r.replace(/\/+$/,"");let n=Date.now();try{let s=await fetch(`${r}/health`,{signal:e.signal,headers:{Authorization:`Bearer ${this.apiKey}`}}),i=Date.now()-n,a="unknown",c=Date.now();try{let u=await s.json();typeof u.status=="string"&&(a=u.status),typeof u.timestamp=="number"&&(c=u.timestamp);}catch{a=s.ok?"healthy":`error:${s.status}`;}return !s.ok&&a==="unknown"&&(a=`error:${s.status}`),{ok:s.ok&&a==="healthy",status:a,latencyMs:i,timestamp:c}}catch(s){throw s instanceof Error&&s.name==="AbortError"?new h("Request timed out",408,"TIMEOUT"):new h(s instanceof Error?s.message:"Unknown error",0,"UNKNOWN")}finally{clearTimeout(t);}}getMetrics(){return this.resilience?.getMetrics()??null}getHealth(){return this.resilience?.getHealth()??null}async createCustomer(e){return this.request("/customers",{method:"POST",body:JSON.stringify(e)})}async getCustomer(e){return this.request(`/customers/${e}`)}async listCustomers(e){let t=new URLSearchParams;e?.limit&&t.set("limit",e.limit.toString()),e?.status&&t.set("status",e.status);let r=t.toString(),n=r?`/customers?${r}`:"/customers";return this.request(n)}async getBalance(e){return this.request(`/customers/${e}/balance`)}async charge(e){let t=e.idempotencyKey??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)}`,r=await e.call(),n=e.extractUsage(r),s=await z(()=>this.charge({customerId:e.customerId,meter:e.meter,quantity:n,idempotencyKey:t,metadata:e.metadata}),e.retryOptions);return {result:r,charge:s,idempotencyKey:t}}async trackUsage(e){let t=e.idempotencyKey??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 r=t.toString(),n=r?`/charges?${r}`:"/charges";return this.request(n)}async getChargeStatus(e){return this.request(`/charges/${e}/status`)}async checkout(e){let t=await this.request("/checkout",{method:"POST",body:JSON.stringify({customer_id:e.customerId,external_customer_id:e.externalCustomerId,amount:e.amount,return_url:e.returnUrl,cancel_url:e.cancelUrl,metadata:e.metadata})});return {id:t.id,url:t.url,expiresAt:t.expires_at,amountUsd:t.amount_usd}}async createWebhook(e){return this.assertSecretKey("createWebhook()"),this.request("/webhooks",{method:"POST",body:JSON.stringify(e)})}async listWebhooks(){return this.assertSecretKey("listWebhooks()"),this.request("/webhooks")}async getWebhook(e){return this.assertSecretKey("getWebhook()"),this.request(`/webhooks/${e}`)}async deleteWebhook(e){return this.assertSecretKey("deleteWebhook()"),this.request(`/webhooks/${e}`,{method:"DELETE"})}async testWebhook(e){return this.assertSecretKey("testWebhook()"),this.request(`/webhooks/${e}/test`,{method:"POST"})}async rotateWebhookSecret(e){return this.assertSecretKey("rotateWebhookSecret()"),this.request(`/webhooks/${e}/rotate-secret`,{method:"POST"})}async createWorkflow(e){return this.request("/workflows",{method:"POST",body:JSON.stringify(e)})}async listWorkflows(){return this.request("/workflows")}async startRun(e){return this.request("/runs",{method:"POST",body:JSON.stringify(e)})}async endRun(e,t){return this.request(`/runs/${e}`,{method:"PATCH",body:JSON.stringify(t)})}async getRun(e){return this.request(`/runs/${e}`)}async getRunTimeline(e,t){let r=new URLSearchParams;t?.limit&&r.set("limit",t.limit.toString()),t?.cursor&&r.set("cursor",t.cursor),t?.includeAnomalies!==void 0&&r.set("includeAnomalies",String(t.includeAnomalies)),t?.collapseRetries!==void 0&&r.set("collapseRetries",String(t.collapseRetries));let n=r.toString(),s=n?`/runs/${e}/timeline?${n}`:`/runs/${e}/timeline`;return this.request(s)}async emitEvent(e){let t=e.idempotencyKey??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,r=e.periodEnd instanceof Date?e.periodEnd.toISOString():e.periodEnd;return this.request("/dashboard/cost-estimate/from-usage",{method:"POST",body:JSON.stringify({customerId:e.customerId,periodStart:t,periodEnd:r,defaultUnitPrice:e.defaultUnitPrice,includeChargedEvents:e.includeChargedEvents,usageTypes:e.usageTypes,customPricing:e.customPricing})})}async estimateFromHypothetical(e){return this.request("/dashboard/cost-estimate/hypothetical",{method:"POST",body:JSON.stringify({items:e.items,defaultUnitPrice:e.defaultUnitPrice,customPricing:e.customPricing})})}async recordRun(e){try{return await this.request("/runs/record",{method:"POST",body:JSON.stringify({customerId:e.customerId,workflow:e.workflow,events:e.events,status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode,externalRunId:e.externalRunId,correlationId:e.correlationId,metadata:e.metadata})})}catch(t){if(t instanceof h&&t.statusCode===404)return this._recordRunFallback(e);throw t}}async _recordRunFallback(e){let t=Date.now(),r=e.workflow,n=e.workflow,{data:s}=await this.listWorkflows(),i=s.find(g=>g.slug===e.workflow||g.id===e.workflow);if(i)r=i.id,n=i.name;else {let g=await this.request("/workflows",{method:"POST",body:JSON.stringify({name:e.workflow.replace(/[_-]/g," ").replace(/\b\w/g,p=>p.toUpperCase()),slug:e.workflow,productSurface:"CUSTOM"})});r=g.id,n=g.name;}let a=await this.startRun({customerId:e.customerId,workflowId:r,correlationId:e.correlationId,externalRunId:e.externalRunId,metadata:e.metadata}),c=0,u=0;if(e.events.length>0){let g=e.events.map((d,b)=>({runId:a.id,eventType:d.eventType,quantity:d.quantity??1,units:d.units,description:d.description,costUnits:d.costUnits,metadata:d.metadata,idempotencyKey:e.externalRunId?`${e.externalRunId}:${d.eventType}:${b}`:void 0})),p=await this.emitEventsBatch(g);c=p.created,u=p.duplicates;}let l=await this.endRun(a.id,{status:e.status,errorMessage:e.errorMessage,errorCode:e.errorCode}),m=Date.now()-t,y=e.status==="COMPLETED"?"\u2713":e.status==="FAILED"?"\u2717":"\u25CB";return {run:{id:a.id,workflowId:r,workflowName:n,status:l.status,durationMs:l.durationMs??m},events:{created:c,duplicates:u},totalCostUnits:l.totalCostUnits??null,summary:`${y} ${n}: ${c} events recorded (${l.durationMs??m}ms)`}}static generateIdempotencyKey(e){let r=[e.customerId,e.runId??"no_run",e.stepName,String(e.sequence??0)].join("|");return `drip_${S("crypto").createHash("sha256").update(r).digest("hex").slice(0,32)}_${e.stepName.slice(0,16)}`}static async verifyWebhookSignature(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(f=>f.startsWith("t=")),a=s.find(f=>f.startsWith("v1="));if(!i||!a)return !1;let c=parseInt(i.slice(2),10),u=a.slice(3);if(isNaN(c))return !1;let l=Math.floor(Date.now()/1e3);if(Math.abs(l-c)>n)return !1;let m=`${c}.${e}`,y=new TextEncoder,g=y.encode(r),p=y.encode(m),d=globalThis.crypto?.subtle??S("crypto").webcrypto.subtle,b=await d.importKey("raw",g,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),F=await d.sign("HMAC",b,p),L=Array.from(new Uint8Array(F)).map(f=>f.toString(16).padStart(2,"0")).join("");if(u.length!==L.length)return !1;let _=0;for(let f=0;f<u.length;f++)_|=u.charCodeAt(f)^L.charCodeAt(f);return _===0}catch{return false}}static verifyWebhookSignatureSync(e,t,r,n=300){if(!e||!t||!r)return false;try{let s=t.split(","),i=s.find(b=>b.startsWith("t=")),a=s.find(b=>b.startsWith("v1="));if(!i||!a)return !1;let c=parseInt(i.slice(2),10),u=a.slice(3);if(isNaN(c))return !1;let l=Math.floor(Date.now()/1e3);if(Math.abs(l-c)>n)return !1;let m=S("crypto"),y=`${c}.${e}`,g=m.createHmac("sha256",r).update(y).digest("hex"),p=Buffer.from(u),d=Buffer.from(g);return p.length!==d.length?!1:m.timingSafeEqual(p,d)}catch{return false}}static generateWebhookSignature(e,t,r){let n=S("crypto"),s=r??Math.floor(Date.now()/1e3),i=`${s}.${e}`,a=n.createHmac("sha256",t).update(i).digest("hex");return `t=${s},v1=${a}`}createStreamMeter(e){return new k(this.charge.bind(this),e)}},ue=M,U=null;function J(){return U||(U=new M),U}var ce=new Proxy({},{get(o,e){let t=J(),r=t[e];return typeof r=="function"?r.bind(t):r}});
|
|
2
|
+
export{I as CircuitBreaker,P as CircuitBreakerOpenError,M as Drip,h as DripError,x as MetricsCollector,T as RateLimiter,C as ResilienceManager,E as RetryExhaustedError,k as StreamMeter,N as calculateBackoff,R as createDefaultResilienceConfig,j as createDisabledResilienceConfig,A as createHighThroughputResilienceConfig,ue as default,ce as drip,W as isRetryableError};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|