@perkos/service-pricing 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +317 -0
- package/dist/index.d.ts +317 -0
- package/dist/index.js +387 -0
- package/dist/index.mjs +345 -0
- package/package.json +44 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Address } from '@perkos/types-x402';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @perkos/service-pricing
|
|
5
|
+
* Dynamic pricing service with strategy patterns for x402 payment protocol
|
|
6
|
+
*
|
|
7
|
+
* Provides vendor-centric pricing with multiple strategy types:
|
|
8
|
+
* - Fixed: Static price per request
|
|
9
|
+
* - Tiered: Volume-based pricing tiers
|
|
10
|
+
* - Usage-based: Pay per unit (token, byte, time)
|
|
11
|
+
* - Time-bucket: Peak/off-peak pricing
|
|
12
|
+
* - Subscription: Recurring plans with credits
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
type PricingStrategyType = "fixed" | "tiered" | "usage-based" | "time-bucket" | "auction" | "subscription" | "custom";
|
|
16
|
+
type PricingUnit = "per-request" | "per-token" | "per-byte" | "per-second" | "per-minute" | "per-hour" | "per-day" | "per-month";
|
|
17
|
+
type UserTier = "free" | "basic" | "premium" | "enterprise";
|
|
18
|
+
interface PricingContext {
|
|
19
|
+
request: RequestContext;
|
|
20
|
+
user?: UserContext;
|
|
21
|
+
vendor: VendorContext;
|
|
22
|
+
endpoint: EndpointContext;
|
|
23
|
+
environment?: EnvironmentContext;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
}
|
|
26
|
+
interface RequestContext {
|
|
27
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
28
|
+
path: string;
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
query: Record<string, string>;
|
|
31
|
+
body?: unknown;
|
|
32
|
+
contentLength?: number;
|
|
33
|
+
clientIp?: string;
|
|
34
|
+
}
|
|
35
|
+
interface UserContext {
|
|
36
|
+
address: Address;
|
|
37
|
+
tier?: UserTier;
|
|
38
|
+
usage?: UsageHistory;
|
|
39
|
+
reputation?: number;
|
|
40
|
+
subscription?: SubscriptionStatus;
|
|
41
|
+
}
|
|
42
|
+
interface UsageHistory {
|
|
43
|
+
requestCount: number;
|
|
44
|
+
totalVolume: string;
|
|
45
|
+
periodStart: number;
|
|
46
|
+
periodEnd: number;
|
|
47
|
+
}
|
|
48
|
+
interface SubscriptionStatus {
|
|
49
|
+
active: boolean;
|
|
50
|
+
planId: string;
|
|
51
|
+
tierName: string;
|
|
52
|
+
remainingCredits?: number;
|
|
53
|
+
expiresAt?: number;
|
|
54
|
+
}
|
|
55
|
+
interface VendorContext {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
walletAddress: Address;
|
|
59
|
+
network: string;
|
|
60
|
+
chainId: number | null;
|
|
61
|
+
asset: string;
|
|
62
|
+
basePriceUsd: string | null;
|
|
63
|
+
pricingConfig: VendorPricingConfig | null;
|
|
64
|
+
}
|
|
65
|
+
interface EndpointContext {
|
|
66
|
+
id: string;
|
|
67
|
+
path: string;
|
|
68
|
+
method: string;
|
|
69
|
+
priceUsd: string;
|
|
70
|
+
pricingConfig?: EndpointPricingConfig | null;
|
|
71
|
+
}
|
|
72
|
+
interface EnvironmentContext {
|
|
73
|
+
networkLoad?: number;
|
|
74
|
+
timeBucket?: "peak" | "off-peak" | "normal";
|
|
75
|
+
gasPrice?: string;
|
|
76
|
+
marketRate?: number;
|
|
77
|
+
}
|
|
78
|
+
interface PriceResult {
|
|
79
|
+
amount: string;
|
|
80
|
+
asset: Address;
|
|
81
|
+
network: string;
|
|
82
|
+
breakdown?: PriceBreakdown[];
|
|
83
|
+
validUntil?: number;
|
|
84
|
+
minAmount?: string;
|
|
85
|
+
display?: PriceDisplay;
|
|
86
|
+
cacheKey?: string;
|
|
87
|
+
}
|
|
88
|
+
interface PriceBreakdown {
|
|
89
|
+
component: string;
|
|
90
|
+
amount: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
percentage?: number;
|
|
93
|
+
}
|
|
94
|
+
interface PriceDisplay {
|
|
95
|
+
formatted: string;
|
|
96
|
+
symbol: string;
|
|
97
|
+
decimals: number;
|
|
98
|
+
}
|
|
99
|
+
interface VendorPricingConfig {
|
|
100
|
+
id: string;
|
|
101
|
+
vendorId: string;
|
|
102
|
+
strategyType: PricingStrategyType;
|
|
103
|
+
name: string;
|
|
104
|
+
isDefault: boolean;
|
|
105
|
+
parameters: StrategyParameters;
|
|
106
|
+
cache?: CacheConfig;
|
|
107
|
+
routePatterns?: string[];
|
|
108
|
+
priority: number;
|
|
109
|
+
enabled: boolean;
|
|
110
|
+
createdAt: string;
|
|
111
|
+
updatedAt: string;
|
|
112
|
+
}
|
|
113
|
+
interface EndpointPricingConfig {
|
|
114
|
+
strategyType?: PricingStrategyType;
|
|
115
|
+
parameters?: Partial<StrategyParameters>;
|
|
116
|
+
cache?: Partial<CacheConfig>;
|
|
117
|
+
}
|
|
118
|
+
interface CacheConfig {
|
|
119
|
+
enabled: boolean;
|
|
120
|
+
ttlSeconds: number;
|
|
121
|
+
customKeyTemplate?: string;
|
|
122
|
+
}
|
|
123
|
+
type StrategyParameters = FixedPricingParams | TieredPricingParams | UsageBasedPricingParams | TimeBucketPricingParams | AuctionPricingParams | SubscriptionPricingParams | CustomPricingParams;
|
|
124
|
+
interface FixedPricingParams {
|
|
125
|
+
type: "fixed";
|
|
126
|
+
price: string;
|
|
127
|
+
asset: Address;
|
|
128
|
+
network: string;
|
|
129
|
+
}
|
|
130
|
+
interface TieredPricingParams {
|
|
131
|
+
type: "tiered";
|
|
132
|
+
tiers: PricingTier[];
|
|
133
|
+
asset: Address;
|
|
134
|
+
network: string;
|
|
135
|
+
resetPeriod: number;
|
|
136
|
+
}
|
|
137
|
+
interface PricingTier {
|
|
138
|
+
name: string;
|
|
139
|
+
upTo: number;
|
|
140
|
+
pricePerRequest: string;
|
|
141
|
+
discount?: number;
|
|
142
|
+
}
|
|
143
|
+
interface UsageBasedPricingParams {
|
|
144
|
+
type: "usage-based";
|
|
145
|
+
pricePerUnit: string;
|
|
146
|
+
unit: PricingUnit;
|
|
147
|
+
minimumCharge?: string;
|
|
148
|
+
maximumCharge?: string;
|
|
149
|
+
asset: Address;
|
|
150
|
+
network: string;
|
|
151
|
+
unitExtractor?: string;
|
|
152
|
+
}
|
|
153
|
+
interface TimeBucketPricingParams {
|
|
154
|
+
type: "time-bucket";
|
|
155
|
+
basePrice: string;
|
|
156
|
+
peakMultiplier: number;
|
|
157
|
+
offPeakMultiplier: number;
|
|
158
|
+
peakHours: Array<{
|
|
159
|
+
start: number;
|
|
160
|
+
end: number;
|
|
161
|
+
}>;
|
|
162
|
+
timezone: string;
|
|
163
|
+
asset: Address;
|
|
164
|
+
network: string;
|
|
165
|
+
}
|
|
166
|
+
interface AuctionPricingParams {
|
|
167
|
+
type: "auction";
|
|
168
|
+
floorPrice: string;
|
|
169
|
+
ceilingPrice: string;
|
|
170
|
+
adjustmentRate: number;
|
|
171
|
+
demandFactor?: number;
|
|
172
|
+
asset: Address;
|
|
173
|
+
network: string;
|
|
174
|
+
}
|
|
175
|
+
interface SubscriptionPricingParams {
|
|
176
|
+
type: "subscription";
|
|
177
|
+
plans: SubscriptionPlan[];
|
|
178
|
+
overagePrice?: string;
|
|
179
|
+
asset: Address;
|
|
180
|
+
network: string;
|
|
181
|
+
}
|
|
182
|
+
interface SubscriptionPlan {
|
|
183
|
+
id: string;
|
|
184
|
+
name: string;
|
|
185
|
+
monthlyPrice: string;
|
|
186
|
+
includedRequests: number;
|
|
187
|
+
features: string[];
|
|
188
|
+
}
|
|
189
|
+
interface CustomPricingParams {
|
|
190
|
+
type: "custom";
|
|
191
|
+
calculatorEndpoint?: string;
|
|
192
|
+
customParams: Record<string, unknown>;
|
|
193
|
+
asset: Address;
|
|
194
|
+
network: string;
|
|
195
|
+
}
|
|
196
|
+
interface PricingStrategy {
|
|
197
|
+
readonly name: string;
|
|
198
|
+
readonly type: PricingStrategyType;
|
|
199
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
200
|
+
generateCacheKey(context: PricingContext): string;
|
|
201
|
+
validate(): ValidationResult;
|
|
202
|
+
}
|
|
203
|
+
interface ValidationResult {
|
|
204
|
+
valid: boolean;
|
|
205
|
+
errors?: string[];
|
|
206
|
+
}
|
|
207
|
+
interface PricingServiceConfig {
|
|
208
|
+
cache: CacheConfig;
|
|
209
|
+
enableLogging: boolean;
|
|
210
|
+
fallbackPrice?: string;
|
|
211
|
+
fallbackAsset?: Address;
|
|
212
|
+
fallbackNetwork?: string;
|
|
213
|
+
usdcDecimals: number;
|
|
214
|
+
}
|
|
215
|
+
declare const ASSET_SYMBOLS: Record<string, string>;
|
|
216
|
+
/**
|
|
217
|
+
* Convert USD string to atomic units (USDC decimals = 6)
|
|
218
|
+
*/
|
|
219
|
+
declare function usdToAtomicUnits(usdAmount: string, decimals?: number): string;
|
|
220
|
+
/**
|
|
221
|
+
* Convert atomic units to USD string
|
|
222
|
+
*/
|
|
223
|
+
declare function atomicUnitsToUsd(atomicUnits: string, decimals?: number): string;
|
|
224
|
+
/**
|
|
225
|
+
* Format price for display
|
|
226
|
+
*/
|
|
227
|
+
declare function formatPrice(amount: bigint, asset: Address, decimals?: number): PriceDisplay;
|
|
228
|
+
/**
|
|
229
|
+
* Apply percentage discount to amount
|
|
230
|
+
*/
|
|
231
|
+
declare function applyDiscount(amount: bigint, discountPercent: number): bigint;
|
|
232
|
+
/**
|
|
233
|
+
* Apply multiplier to amount
|
|
234
|
+
*/
|
|
235
|
+
declare function applyMultiplier(amount: bigint, multiplier: number): bigint;
|
|
236
|
+
/**
|
|
237
|
+
* Generate deterministic cache key
|
|
238
|
+
*/
|
|
239
|
+
declare function generateCacheKey(strategyType: string, configId: string, context: PricingContext): string;
|
|
240
|
+
/**
|
|
241
|
+
* Sort object keys deterministically
|
|
242
|
+
*/
|
|
243
|
+
declare function sortObject(obj: Record<string, unknown>): Record<string, unknown>;
|
|
244
|
+
/**
|
|
245
|
+
* Get current time bucket based on timezone
|
|
246
|
+
*/
|
|
247
|
+
declare function getTimeBucket(timezone: string, peakHours: Array<{
|
|
248
|
+
start: number;
|
|
249
|
+
end: number;
|
|
250
|
+
}>): "peak" | "off-peak" | "normal";
|
|
251
|
+
/**
|
|
252
|
+
* Determine tier for request count
|
|
253
|
+
*/
|
|
254
|
+
declare function determineTier(requestCount: number, tiers: PricingTier[]): PricingTier | undefined;
|
|
255
|
+
/**
|
|
256
|
+
* Calculate usage-based units from request
|
|
257
|
+
*/
|
|
258
|
+
declare function calculateUsageUnits(context: PricingContext, unit: PricingUnit): number;
|
|
259
|
+
/**
|
|
260
|
+
* Build price result structure
|
|
261
|
+
*/
|
|
262
|
+
declare function buildPriceResult(amount: bigint, asset: Address, network: string, ttlSeconds?: number, breakdown?: PriceBreakdown[]): PriceResult;
|
|
263
|
+
/**
|
|
264
|
+
* Abstract base class for pricing strategies
|
|
265
|
+
*/
|
|
266
|
+
declare abstract class BasePricingStrategy implements PricingStrategy {
|
|
267
|
+
abstract readonly name: string;
|
|
268
|
+
abstract readonly type: PricingStrategyType;
|
|
269
|
+
protected config: VendorPricingConfig;
|
|
270
|
+
protected decimals: number;
|
|
271
|
+
constructor(config: VendorPricingConfig, decimals?: number);
|
|
272
|
+
abstract calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
273
|
+
generateCacheKey(context: PricingContext): string;
|
|
274
|
+
validate(): ValidationResult;
|
|
275
|
+
protected getParams<T extends StrategyParameters>(): T;
|
|
276
|
+
protected getCacheTtl(): number;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Fixed pricing strategy - static price per request
|
|
280
|
+
*/
|
|
281
|
+
declare class FixedPricingStrategy extends BasePricingStrategy {
|
|
282
|
+
readonly name = "Fixed Pricing";
|
|
283
|
+
readonly type: PricingStrategyType;
|
|
284
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
285
|
+
validate(): ValidationResult;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Tiered pricing strategy - volume-based discounts
|
|
289
|
+
*/
|
|
290
|
+
declare class TieredPricingStrategy extends BasePricingStrategy {
|
|
291
|
+
readonly name = "Tiered Pricing";
|
|
292
|
+
readonly type: PricingStrategyType;
|
|
293
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
294
|
+
validate(): ValidationResult;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Usage-based pricing strategy - pay per unit
|
|
298
|
+
*/
|
|
299
|
+
declare class UsageBasedPricingStrategy extends BasePricingStrategy {
|
|
300
|
+
readonly name = "Usage-Based Pricing";
|
|
301
|
+
readonly type: PricingStrategyType;
|
|
302
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Time-bucket pricing strategy - peak/off-peak pricing
|
|
306
|
+
*/
|
|
307
|
+
declare class TimeBucketPricingStrategy extends BasePricingStrategy {
|
|
308
|
+
readonly name = "Time-Bucket Pricing";
|
|
309
|
+
readonly type: PricingStrategyType;
|
|
310
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Create a pricing strategy instance from configuration
|
|
314
|
+
*/
|
|
315
|
+
declare function createPricingStrategy(config: VendorPricingConfig, decimals?: number): PricingStrategy;
|
|
316
|
+
|
|
317
|
+
export { ASSET_SYMBOLS, type AuctionPricingParams, BasePricingStrategy, type CacheConfig, type CustomPricingParams, type EndpointContext, type EndpointPricingConfig, type EnvironmentContext, type FixedPricingParams, FixedPricingStrategy, type PriceBreakdown, type PriceDisplay, type PriceResult, type PricingContext, type PricingServiceConfig, type PricingStrategy, type PricingStrategyType, type PricingTier, type PricingUnit, type RequestContext, type StrategyParameters, type SubscriptionPlan, type SubscriptionPricingParams, type SubscriptionStatus, type TieredPricingParams, TieredPricingStrategy, type TimeBucketPricingParams, TimeBucketPricingStrategy, type UsageBasedPricingParams, UsageBasedPricingStrategy, type UsageHistory, type UserContext, type UserTier, type ValidationResult, type VendorContext, type VendorPricingConfig, applyDiscount, applyMultiplier, atomicUnitsToUsd, buildPriceResult, calculateUsageUnits, createPricingStrategy, determineTier, formatPrice, generateCacheKey, getTimeBucket, sortObject, usdToAtomicUnits };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Address } from '@perkos/types-x402';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @perkos/service-pricing
|
|
5
|
+
* Dynamic pricing service with strategy patterns for x402 payment protocol
|
|
6
|
+
*
|
|
7
|
+
* Provides vendor-centric pricing with multiple strategy types:
|
|
8
|
+
* - Fixed: Static price per request
|
|
9
|
+
* - Tiered: Volume-based pricing tiers
|
|
10
|
+
* - Usage-based: Pay per unit (token, byte, time)
|
|
11
|
+
* - Time-bucket: Peak/off-peak pricing
|
|
12
|
+
* - Subscription: Recurring plans with credits
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
type PricingStrategyType = "fixed" | "tiered" | "usage-based" | "time-bucket" | "auction" | "subscription" | "custom";
|
|
16
|
+
type PricingUnit = "per-request" | "per-token" | "per-byte" | "per-second" | "per-minute" | "per-hour" | "per-day" | "per-month";
|
|
17
|
+
type UserTier = "free" | "basic" | "premium" | "enterprise";
|
|
18
|
+
interface PricingContext {
|
|
19
|
+
request: RequestContext;
|
|
20
|
+
user?: UserContext;
|
|
21
|
+
vendor: VendorContext;
|
|
22
|
+
endpoint: EndpointContext;
|
|
23
|
+
environment?: EnvironmentContext;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
}
|
|
26
|
+
interface RequestContext {
|
|
27
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
28
|
+
path: string;
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
query: Record<string, string>;
|
|
31
|
+
body?: unknown;
|
|
32
|
+
contentLength?: number;
|
|
33
|
+
clientIp?: string;
|
|
34
|
+
}
|
|
35
|
+
interface UserContext {
|
|
36
|
+
address: Address;
|
|
37
|
+
tier?: UserTier;
|
|
38
|
+
usage?: UsageHistory;
|
|
39
|
+
reputation?: number;
|
|
40
|
+
subscription?: SubscriptionStatus;
|
|
41
|
+
}
|
|
42
|
+
interface UsageHistory {
|
|
43
|
+
requestCount: number;
|
|
44
|
+
totalVolume: string;
|
|
45
|
+
periodStart: number;
|
|
46
|
+
periodEnd: number;
|
|
47
|
+
}
|
|
48
|
+
interface SubscriptionStatus {
|
|
49
|
+
active: boolean;
|
|
50
|
+
planId: string;
|
|
51
|
+
tierName: string;
|
|
52
|
+
remainingCredits?: number;
|
|
53
|
+
expiresAt?: number;
|
|
54
|
+
}
|
|
55
|
+
interface VendorContext {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
walletAddress: Address;
|
|
59
|
+
network: string;
|
|
60
|
+
chainId: number | null;
|
|
61
|
+
asset: string;
|
|
62
|
+
basePriceUsd: string | null;
|
|
63
|
+
pricingConfig: VendorPricingConfig | null;
|
|
64
|
+
}
|
|
65
|
+
interface EndpointContext {
|
|
66
|
+
id: string;
|
|
67
|
+
path: string;
|
|
68
|
+
method: string;
|
|
69
|
+
priceUsd: string;
|
|
70
|
+
pricingConfig?: EndpointPricingConfig | null;
|
|
71
|
+
}
|
|
72
|
+
interface EnvironmentContext {
|
|
73
|
+
networkLoad?: number;
|
|
74
|
+
timeBucket?: "peak" | "off-peak" | "normal";
|
|
75
|
+
gasPrice?: string;
|
|
76
|
+
marketRate?: number;
|
|
77
|
+
}
|
|
78
|
+
interface PriceResult {
|
|
79
|
+
amount: string;
|
|
80
|
+
asset: Address;
|
|
81
|
+
network: string;
|
|
82
|
+
breakdown?: PriceBreakdown[];
|
|
83
|
+
validUntil?: number;
|
|
84
|
+
minAmount?: string;
|
|
85
|
+
display?: PriceDisplay;
|
|
86
|
+
cacheKey?: string;
|
|
87
|
+
}
|
|
88
|
+
interface PriceBreakdown {
|
|
89
|
+
component: string;
|
|
90
|
+
amount: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
percentage?: number;
|
|
93
|
+
}
|
|
94
|
+
interface PriceDisplay {
|
|
95
|
+
formatted: string;
|
|
96
|
+
symbol: string;
|
|
97
|
+
decimals: number;
|
|
98
|
+
}
|
|
99
|
+
interface VendorPricingConfig {
|
|
100
|
+
id: string;
|
|
101
|
+
vendorId: string;
|
|
102
|
+
strategyType: PricingStrategyType;
|
|
103
|
+
name: string;
|
|
104
|
+
isDefault: boolean;
|
|
105
|
+
parameters: StrategyParameters;
|
|
106
|
+
cache?: CacheConfig;
|
|
107
|
+
routePatterns?: string[];
|
|
108
|
+
priority: number;
|
|
109
|
+
enabled: boolean;
|
|
110
|
+
createdAt: string;
|
|
111
|
+
updatedAt: string;
|
|
112
|
+
}
|
|
113
|
+
interface EndpointPricingConfig {
|
|
114
|
+
strategyType?: PricingStrategyType;
|
|
115
|
+
parameters?: Partial<StrategyParameters>;
|
|
116
|
+
cache?: Partial<CacheConfig>;
|
|
117
|
+
}
|
|
118
|
+
interface CacheConfig {
|
|
119
|
+
enabled: boolean;
|
|
120
|
+
ttlSeconds: number;
|
|
121
|
+
customKeyTemplate?: string;
|
|
122
|
+
}
|
|
123
|
+
type StrategyParameters = FixedPricingParams | TieredPricingParams | UsageBasedPricingParams | TimeBucketPricingParams | AuctionPricingParams | SubscriptionPricingParams | CustomPricingParams;
|
|
124
|
+
interface FixedPricingParams {
|
|
125
|
+
type: "fixed";
|
|
126
|
+
price: string;
|
|
127
|
+
asset: Address;
|
|
128
|
+
network: string;
|
|
129
|
+
}
|
|
130
|
+
interface TieredPricingParams {
|
|
131
|
+
type: "tiered";
|
|
132
|
+
tiers: PricingTier[];
|
|
133
|
+
asset: Address;
|
|
134
|
+
network: string;
|
|
135
|
+
resetPeriod: number;
|
|
136
|
+
}
|
|
137
|
+
interface PricingTier {
|
|
138
|
+
name: string;
|
|
139
|
+
upTo: number;
|
|
140
|
+
pricePerRequest: string;
|
|
141
|
+
discount?: number;
|
|
142
|
+
}
|
|
143
|
+
interface UsageBasedPricingParams {
|
|
144
|
+
type: "usage-based";
|
|
145
|
+
pricePerUnit: string;
|
|
146
|
+
unit: PricingUnit;
|
|
147
|
+
minimumCharge?: string;
|
|
148
|
+
maximumCharge?: string;
|
|
149
|
+
asset: Address;
|
|
150
|
+
network: string;
|
|
151
|
+
unitExtractor?: string;
|
|
152
|
+
}
|
|
153
|
+
interface TimeBucketPricingParams {
|
|
154
|
+
type: "time-bucket";
|
|
155
|
+
basePrice: string;
|
|
156
|
+
peakMultiplier: number;
|
|
157
|
+
offPeakMultiplier: number;
|
|
158
|
+
peakHours: Array<{
|
|
159
|
+
start: number;
|
|
160
|
+
end: number;
|
|
161
|
+
}>;
|
|
162
|
+
timezone: string;
|
|
163
|
+
asset: Address;
|
|
164
|
+
network: string;
|
|
165
|
+
}
|
|
166
|
+
interface AuctionPricingParams {
|
|
167
|
+
type: "auction";
|
|
168
|
+
floorPrice: string;
|
|
169
|
+
ceilingPrice: string;
|
|
170
|
+
adjustmentRate: number;
|
|
171
|
+
demandFactor?: number;
|
|
172
|
+
asset: Address;
|
|
173
|
+
network: string;
|
|
174
|
+
}
|
|
175
|
+
interface SubscriptionPricingParams {
|
|
176
|
+
type: "subscription";
|
|
177
|
+
plans: SubscriptionPlan[];
|
|
178
|
+
overagePrice?: string;
|
|
179
|
+
asset: Address;
|
|
180
|
+
network: string;
|
|
181
|
+
}
|
|
182
|
+
interface SubscriptionPlan {
|
|
183
|
+
id: string;
|
|
184
|
+
name: string;
|
|
185
|
+
monthlyPrice: string;
|
|
186
|
+
includedRequests: number;
|
|
187
|
+
features: string[];
|
|
188
|
+
}
|
|
189
|
+
interface CustomPricingParams {
|
|
190
|
+
type: "custom";
|
|
191
|
+
calculatorEndpoint?: string;
|
|
192
|
+
customParams: Record<string, unknown>;
|
|
193
|
+
asset: Address;
|
|
194
|
+
network: string;
|
|
195
|
+
}
|
|
196
|
+
interface PricingStrategy {
|
|
197
|
+
readonly name: string;
|
|
198
|
+
readonly type: PricingStrategyType;
|
|
199
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
200
|
+
generateCacheKey(context: PricingContext): string;
|
|
201
|
+
validate(): ValidationResult;
|
|
202
|
+
}
|
|
203
|
+
interface ValidationResult {
|
|
204
|
+
valid: boolean;
|
|
205
|
+
errors?: string[];
|
|
206
|
+
}
|
|
207
|
+
interface PricingServiceConfig {
|
|
208
|
+
cache: CacheConfig;
|
|
209
|
+
enableLogging: boolean;
|
|
210
|
+
fallbackPrice?: string;
|
|
211
|
+
fallbackAsset?: Address;
|
|
212
|
+
fallbackNetwork?: string;
|
|
213
|
+
usdcDecimals: number;
|
|
214
|
+
}
|
|
215
|
+
declare const ASSET_SYMBOLS: Record<string, string>;
|
|
216
|
+
/**
|
|
217
|
+
* Convert USD string to atomic units (USDC decimals = 6)
|
|
218
|
+
*/
|
|
219
|
+
declare function usdToAtomicUnits(usdAmount: string, decimals?: number): string;
|
|
220
|
+
/**
|
|
221
|
+
* Convert atomic units to USD string
|
|
222
|
+
*/
|
|
223
|
+
declare function atomicUnitsToUsd(atomicUnits: string, decimals?: number): string;
|
|
224
|
+
/**
|
|
225
|
+
* Format price for display
|
|
226
|
+
*/
|
|
227
|
+
declare function formatPrice(amount: bigint, asset: Address, decimals?: number): PriceDisplay;
|
|
228
|
+
/**
|
|
229
|
+
* Apply percentage discount to amount
|
|
230
|
+
*/
|
|
231
|
+
declare function applyDiscount(amount: bigint, discountPercent: number): bigint;
|
|
232
|
+
/**
|
|
233
|
+
* Apply multiplier to amount
|
|
234
|
+
*/
|
|
235
|
+
declare function applyMultiplier(amount: bigint, multiplier: number): bigint;
|
|
236
|
+
/**
|
|
237
|
+
* Generate deterministic cache key
|
|
238
|
+
*/
|
|
239
|
+
declare function generateCacheKey(strategyType: string, configId: string, context: PricingContext): string;
|
|
240
|
+
/**
|
|
241
|
+
* Sort object keys deterministically
|
|
242
|
+
*/
|
|
243
|
+
declare function sortObject(obj: Record<string, unknown>): Record<string, unknown>;
|
|
244
|
+
/**
|
|
245
|
+
* Get current time bucket based on timezone
|
|
246
|
+
*/
|
|
247
|
+
declare function getTimeBucket(timezone: string, peakHours: Array<{
|
|
248
|
+
start: number;
|
|
249
|
+
end: number;
|
|
250
|
+
}>): "peak" | "off-peak" | "normal";
|
|
251
|
+
/**
|
|
252
|
+
* Determine tier for request count
|
|
253
|
+
*/
|
|
254
|
+
declare function determineTier(requestCount: number, tiers: PricingTier[]): PricingTier | undefined;
|
|
255
|
+
/**
|
|
256
|
+
* Calculate usage-based units from request
|
|
257
|
+
*/
|
|
258
|
+
declare function calculateUsageUnits(context: PricingContext, unit: PricingUnit): number;
|
|
259
|
+
/**
|
|
260
|
+
* Build price result structure
|
|
261
|
+
*/
|
|
262
|
+
declare function buildPriceResult(amount: bigint, asset: Address, network: string, ttlSeconds?: number, breakdown?: PriceBreakdown[]): PriceResult;
|
|
263
|
+
/**
|
|
264
|
+
* Abstract base class for pricing strategies
|
|
265
|
+
*/
|
|
266
|
+
declare abstract class BasePricingStrategy implements PricingStrategy {
|
|
267
|
+
abstract readonly name: string;
|
|
268
|
+
abstract readonly type: PricingStrategyType;
|
|
269
|
+
protected config: VendorPricingConfig;
|
|
270
|
+
protected decimals: number;
|
|
271
|
+
constructor(config: VendorPricingConfig, decimals?: number);
|
|
272
|
+
abstract calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
273
|
+
generateCacheKey(context: PricingContext): string;
|
|
274
|
+
validate(): ValidationResult;
|
|
275
|
+
protected getParams<T extends StrategyParameters>(): T;
|
|
276
|
+
protected getCacheTtl(): number;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Fixed pricing strategy - static price per request
|
|
280
|
+
*/
|
|
281
|
+
declare class FixedPricingStrategy extends BasePricingStrategy {
|
|
282
|
+
readonly name = "Fixed Pricing";
|
|
283
|
+
readonly type: PricingStrategyType;
|
|
284
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
285
|
+
validate(): ValidationResult;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Tiered pricing strategy - volume-based discounts
|
|
289
|
+
*/
|
|
290
|
+
declare class TieredPricingStrategy extends BasePricingStrategy {
|
|
291
|
+
readonly name = "Tiered Pricing";
|
|
292
|
+
readonly type: PricingStrategyType;
|
|
293
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
294
|
+
validate(): ValidationResult;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Usage-based pricing strategy - pay per unit
|
|
298
|
+
*/
|
|
299
|
+
declare class UsageBasedPricingStrategy extends BasePricingStrategy {
|
|
300
|
+
readonly name = "Usage-Based Pricing";
|
|
301
|
+
readonly type: PricingStrategyType;
|
|
302
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Time-bucket pricing strategy - peak/off-peak pricing
|
|
306
|
+
*/
|
|
307
|
+
declare class TimeBucketPricingStrategy extends BasePricingStrategy {
|
|
308
|
+
readonly name = "Time-Bucket Pricing";
|
|
309
|
+
readonly type: PricingStrategyType;
|
|
310
|
+
calculatePrice(context: PricingContext): Promise<PriceResult>;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Create a pricing strategy instance from configuration
|
|
314
|
+
*/
|
|
315
|
+
declare function createPricingStrategy(config: VendorPricingConfig, decimals?: number): PricingStrategy;
|
|
316
|
+
|
|
317
|
+
export { ASSET_SYMBOLS, type AuctionPricingParams, BasePricingStrategy, type CacheConfig, type CustomPricingParams, type EndpointContext, type EndpointPricingConfig, type EnvironmentContext, type FixedPricingParams, FixedPricingStrategy, type PriceBreakdown, type PriceDisplay, type PriceResult, type PricingContext, type PricingServiceConfig, type PricingStrategy, type PricingStrategyType, type PricingTier, type PricingUnit, type RequestContext, type StrategyParameters, type SubscriptionPlan, type SubscriptionPricingParams, type SubscriptionStatus, type TieredPricingParams, TieredPricingStrategy, type TimeBucketPricingParams, TimeBucketPricingStrategy, type UsageBasedPricingParams, UsageBasedPricingStrategy, type UsageHistory, type UserContext, type UserTier, type ValidationResult, type VendorContext, type VendorPricingConfig, applyDiscount, applyMultiplier, atomicUnitsToUsd, buildPriceResult, calculateUsageUnits, createPricingStrategy, determineTier, formatPrice, generateCacheKey, getTimeBucket, sortObject, usdToAtomicUnits };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ASSET_SYMBOLS: () => ASSET_SYMBOLS,
|
|
24
|
+
BasePricingStrategy: () => BasePricingStrategy,
|
|
25
|
+
FixedPricingStrategy: () => FixedPricingStrategy,
|
|
26
|
+
TieredPricingStrategy: () => TieredPricingStrategy,
|
|
27
|
+
TimeBucketPricingStrategy: () => TimeBucketPricingStrategy,
|
|
28
|
+
UsageBasedPricingStrategy: () => UsageBasedPricingStrategy,
|
|
29
|
+
applyDiscount: () => applyDiscount,
|
|
30
|
+
applyMultiplier: () => applyMultiplier,
|
|
31
|
+
atomicUnitsToUsd: () => atomicUnitsToUsd,
|
|
32
|
+
buildPriceResult: () => buildPriceResult,
|
|
33
|
+
calculateUsageUnits: () => calculateUsageUnits,
|
|
34
|
+
createPricingStrategy: () => createPricingStrategy,
|
|
35
|
+
determineTier: () => determineTier,
|
|
36
|
+
formatPrice: () => formatPrice,
|
|
37
|
+
generateCacheKey: () => generateCacheKey,
|
|
38
|
+
getTimeBucket: () => getTimeBucket,
|
|
39
|
+
sortObject: () => sortObject,
|
|
40
|
+
usdToAtomicUnits: () => usdToAtomicUnits
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
|
+
var ASSET_SYMBOLS = {
|
|
44
|
+
// USDC addresses across networks
|
|
45
|
+
"0x036CbD53842c5426634e7929541eC2318f3dCF7e": "USDC",
|
|
46
|
+
// Base Sepolia
|
|
47
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": "USDC",
|
|
48
|
+
// Base Mainnet
|
|
49
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": "USDC",
|
|
50
|
+
// Ethereum Mainnet
|
|
51
|
+
"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359": "USDC",
|
|
52
|
+
// Polygon
|
|
53
|
+
"0xaf88d065e77c8cC2239327C5EDb3A432268e5831": "USDC",
|
|
54
|
+
// Arbitrum
|
|
55
|
+
"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85": "USDC",
|
|
56
|
+
// Optimism
|
|
57
|
+
"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": "USDC"
|
|
58
|
+
// Avalanche
|
|
59
|
+
};
|
|
60
|
+
function usdToAtomicUnits(usdAmount, decimals = 6) {
|
|
61
|
+
const parts = usdAmount.split(".");
|
|
62
|
+
const integerPart = parts[0] || "0";
|
|
63
|
+
const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
|
|
64
|
+
const atomicUnits = BigInt(integerPart + fractionalPart);
|
|
65
|
+
return atomicUnits.toString();
|
|
66
|
+
}
|
|
67
|
+
function atomicUnitsToUsd(atomicUnits, decimals = 6) {
|
|
68
|
+
const amount = BigInt(atomicUnits);
|
|
69
|
+
const divisor = BigInt(10 ** decimals);
|
|
70
|
+
const integerPart = amount / divisor;
|
|
71
|
+
const fractionalPart = amount % divisor;
|
|
72
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
73
|
+
return `${integerPart}.${fractionalStr}`;
|
|
74
|
+
}
|
|
75
|
+
function formatPrice(amount, asset, decimals = 6) {
|
|
76
|
+
const divisor = BigInt(10 ** decimals);
|
|
77
|
+
const integerPart = amount / divisor;
|
|
78
|
+
const fractionalPart = amount % divisor;
|
|
79
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
80
|
+
const formatted = `${integerPart}.${fractionalStr}`;
|
|
81
|
+
const symbol = ASSET_SYMBOLS[asset] || ASSET_SYMBOLS[asset.toLowerCase()] || "USDC";
|
|
82
|
+
return {
|
|
83
|
+
formatted,
|
|
84
|
+
symbol,
|
|
85
|
+
decimals
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function applyDiscount(amount, discountPercent) {
|
|
89
|
+
if (discountPercent <= 0 || discountPercent > 100) return amount;
|
|
90
|
+
const multiplier = BigInt(Math.floor((100 - discountPercent) * 100));
|
|
91
|
+
return amount * multiplier / 10000n;
|
|
92
|
+
}
|
|
93
|
+
function applyMultiplier(amount, multiplier) {
|
|
94
|
+
const multiplierInt = BigInt(Math.floor(multiplier * 1e4));
|
|
95
|
+
return amount * multiplierInt / 10000n;
|
|
96
|
+
}
|
|
97
|
+
function generateCacheKey(strategyType, configId, context) {
|
|
98
|
+
const keyComponents = {
|
|
99
|
+
strategy: strategyType,
|
|
100
|
+
configId,
|
|
101
|
+
method: context.request.method,
|
|
102
|
+
path: context.request.path,
|
|
103
|
+
query: sortObject(context.request.query),
|
|
104
|
+
bodyHash: context.request.body ? hashObject(context.request.body) : null,
|
|
105
|
+
vendorId: context.vendor.id,
|
|
106
|
+
endpointId: context.endpoint.id,
|
|
107
|
+
user: context.user?.address || "anonymous"
|
|
108
|
+
};
|
|
109
|
+
const keyString = JSON.stringify(keyComponents);
|
|
110
|
+
return simpleHash(keyString);
|
|
111
|
+
}
|
|
112
|
+
function sortObject(obj) {
|
|
113
|
+
if (!obj || typeof obj !== "object") return {};
|
|
114
|
+
return Object.keys(obj).sort().reduce((acc, key) => {
|
|
115
|
+
acc[key] = obj[key];
|
|
116
|
+
return acc;
|
|
117
|
+
}, {});
|
|
118
|
+
}
|
|
119
|
+
function simpleHash(str) {
|
|
120
|
+
let hash = 0;
|
|
121
|
+
for (let i = 0; i < str.length; i++) {
|
|
122
|
+
const char = str.charCodeAt(i);
|
|
123
|
+
hash = (hash << 5) - hash + char;
|
|
124
|
+
hash = hash & hash;
|
|
125
|
+
}
|
|
126
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
127
|
+
}
|
|
128
|
+
function hashObject(obj) {
|
|
129
|
+
return simpleHash(JSON.stringify(obj));
|
|
130
|
+
}
|
|
131
|
+
function getTimeBucket(timezone, peakHours) {
|
|
132
|
+
try {
|
|
133
|
+
const now = /* @__PURE__ */ new Date();
|
|
134
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
135
|
+
hour: "numeric",
|
|
136
|
+
hour12: false,
|
|
137
|
+
timeZone: timezone
|
|
138
|
+
});
|
|
139
|
+
const hour = parseInt(formatter.format(now), 10);
|
|
140
|
+
for (const peak of peakHours) {
|
|
141
|
+
if (hour >= peak.start && hour < peak.end) {
|
|
142
|
+
return "peak";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (hour >= 22 || hour < 6) {
|
|
146
|
+
return "off-peak";
|
|
147
|
+
}
|
|
148
|
+
return "normal";
|
|
149
|
+
} catch {
|
|
150
|
+
return "normal";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function determineTier(requestCount, tiers) {
|
|
154
|
+
const sortedTiers = [...tiers].sort((a, b) => a.upTo - b.upTo);
|
|
155
|
+
for (const tier of sortedTiers) {
|
|
156
|
+
if (tier.upTo === -1 || requestCount <= tier.upTo) {
|
|
157
|
+
return tier;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return sortedTiers[sortedTiers.length - 1];
|
|
161
|
+
}
|
|
162
|
+
function calculateUsageUnits(context, unit) {
|
|
163
|
+
switch (unit) {
|
|
164
|
+
case "per-request":
|
|
165
|
+
return 1;
|
|
166
|
+
case "per-byte":
|
|
167
|
+
return context.request.contentLength || 0;
|
|
168
|
+
case "per-token":
|
|
169
|
+
return Math.ceil((context.request.contentLength || 0) / 4);
|
|
170
|
+
default:
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function buildPriceResult(amount, asset, network, ttlSeconds = 300, breakdown) {
|
|
175
|
+
return {
|
|
176
|
+
amount: amount.toString(),
|
|
177
|
+
asset,
|
|
178
|
+
network,
|
|
179
|
+
breakdown,
|
|
180
|
+
validUntil: Math.floor(Date.now() / 1e3) + ttlSeconds,
|
|
181
|
+
display: formatPrice(amount, asset)
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
var BasePricingStrategy = class {
|
|
185
|
+
constructor(config, decimals = 6) {
|
|
186
|
+
this.config = config;
|
|
187
|
+
this.decimals = decimals;
|
|
188
|
+
}
|
|
189
|
+
generateCacheKey(context) {
|
|
190
|
+
return generateCacheKey(this.type, this.config.id, context);
|
|
191
|
+
}
|
|
192
|
+
validate() {
|
|
193
|
+
const errors = [];
|
|
194
|
+
if (!this.config.parameters) {
|
|
195
|
+
errors.push("Strategy parameters are required");
|
|
196
|
+
}
|
|
197
|
+
if (!this.config.vendorId) {
|
|
198
|
+
errors.push("Vendor ID is required");
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
valid: errors.length === 0,
|
|
202
|
+
errors: errors.length > 0 ? errors : void 0
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
getParams() {
|
|
206
|
+
return this.config.parameters;
|
|
207
|
+
}
|
|
208
|
+
getCacheTtl() {
|
|
209
|
+
return this.config.cache?.ttlSeconds ?? 300;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var FixedPricingStrategy = class extends BasePricingStrategy {
|
|
213
|
+
constructor() {
|
|
214
|
+
super(...arguments);
|
|
215
|
+
this.name = "Fixed Pricing";
|
|
216
|
+
this.type = "fixed";
|
|
217
|
+
}
|
|
218
|
+
async calculatePrice(context) {
|
|
219
|
+
const params = this.getParams();
|
|
220
|
+
const amount = BigInt(params.price);
|
|
221
|
+
return buildPriceResult(
|
|
222
|
+
amount,
|
|
223
|
+
params.asset,
|
|
224
|
+
params.network,
|
|
225
|
+
this.getCacheTtl(),
|
|
226
|
+
[{ component: "Base Price", amount: params.price }]
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
validate() {
|
|
230
|
+
const base = super.validate();
|
|
231
|
+
if (!base.valid) return base;
|
|
232
|
+
const errors = [];
|
|
233
|
+
const params = this.getParams();
|
|
234
|
+
if (!params.price || BigInt(params.price) <= 0n) {
|
|
235
|
+
errors.push("Fixed price must be positive");
|
|
236
|
+
}
|
|
237
|
+
if (!params.asset) {
|
|
238
|
+
errors.push("Asset address is required");
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
valid: errors.length === 0,
|
|
242
|
+
errors: errors.length > 0 ? errors : void 0
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var TieredPricingStrategy = class extends BasePricingStrategy {
|
|
247
|
+
constructor() {
|
|
248
|
+
super(...arguments);
|
|
249
|
+
this.name = "Tiered Pricing";
|
|
250
|
+
this.type = "tiered";
|
|
251
|
+
}
|
|
252
|
+
async calculatePrice(context) {
|
|
253
|
+
const params = this.getParams();
|
|
254
|
+
const requestCount = context.user?.usage?.requestCount || 0;
|
|
255
|
+
const tier = determineTier(requestCount, params.tiers);
|
|
256
|
+
if (!tier) {
|
|
257
|
+
throw new Error("No applicable pricing tier found");
|
|
258
|
+
}
|
|
259
|
+
let amount = BigInt(tier.pricePerRequest);
|
|
260
|
+
if (tier.discount && tier.discount > 0) {
|
|
261
|
+
amount = applyDiscount(amount, tier.discount);
|
|
262
|
+
}
|
|
263
|
+
return buildPriceResult(
|
|
264
|
+
amount,
|
|
265
|
+
params.asset,
|
|
266
|
+
params.network,
|
|
267
|
+
this.getCacheTtl(),
|
|
268
|
+
[
|
|
269
|
+
{ component: "Base Price", amount: tier.pricePerRequest },
|
|
270
|
+
...tier.discount ? [{ component: "Tier Discount", amount: `-${tier.discount}%`, percentage: tier.discount }] : []
|
|
271
|
+
]
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
validate() {
|
|
275
|
+
const base = super.validate();
|
|
276
|
+
if (!base.valid) return base;
|
|
277
|
+
const errors = [];
|
|
278
|
+
const params = this.getParams();
|
|
279
|
+
if (!params.tiers || params.tiers.length === 0) {
|
|
280
|
+
errors.push("At least one pricing tier is required");
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
valid: errors.length === 0,
|
|
284
|
+
errors: errors.length > 0 ? errors : void 0
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
var UsageBasedPricingStrategy = class extends BasePricingStrategy {
|
|
289
|
+
constructor() {
|
|
290
|
+
super(...arguments);
|
|
291
|
+
this.name = "Usage-Based Pricing";
|
|
292
|
+
this.type = "usage-based";
|
|
293
|
+
}
|
|
294
|
+
async calculatePrice(context) {
|
|
295
|
+
const params = this.getParams();
|
|
296
|
+
const units = calculateUsageUnits(context, params.unit);
|
|
297
|
+
let amount = BigInt(params.pricePerUnit) * BigInt(units);
|
|
298
|
+
if (params.minimumCharge) {
|
|
299
|
+
const min = BigInt(params.minimumCharge);
|
|
300
|
+
if (amount < min) {
|
|
301
|
+
amount = min;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (params.maximumCharge) {
|
|
305
|
+
const max = BigInt(params.maximumCharge);
|
|
306
|
+
if (amount > max) {
|
|
307
|
+
amount = max;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return buildPriceResult(
|
|
311
|
+
amount,
|
|
312
|
+
params.asset,
|
|
313
|
+
params.network,
|
|
314
|
+
this.getCacheTtl(),
|
|
315
|
+
[
|
|
316
|
+
{ component: `${units} ${params.unit}`, amount: amount.toString() }
|
|
317
|
+
]
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
var TimeBucketPricingStrategy = class extends BasePricingStrategy {
|
|
322
|
+
constructor() {
|
|
323
|
+
super(...arguments);
|
|
324
|
+
this.name = "Time-Bucket Pricing";
|
|
325
|
+
this.type = "time-bucket";
|
|
326
|
+
}
|
|
327
|
+
async calculatePrice(context) {
|
|
328
|
+
const params = this.getParams();
|
|
329
|
+
const bucket = getTimeBucket(params.timezone, params.peakHours);
|
|
330
|
+
let amount = BigInt(params.basePrice);
|
|
331
|
+
let multiplier = 1;
|
|
332
|
+
switch (bucket) {
|
|
333
|
+
case "peak":
|
|
334
|
+
multiplier = params.peakMultiplier;
|
|
335
|
+
break;
|
|
336
|
+
case "off-peak":
|
|
337
|
+
multiplier = params.offPeakMultiplier;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
amount = applyMultiplier(amount, multiplier);
|
|
341
|
+
return buildPriceResult(
|
|
342
|
+
amount,
|
|
343
|
+
params.asset,
|
|
344
|
+
params.network,
|
|
345
|
+
this.getCacheTtl(),
|
|
346
|
+
[
|
|
347
|
+
{ component: "Base Price", amount: params.basePrice },
|
|
348
|
+
{ component: `${bucket} multiplier`, amount: `x${multiplier}` }
|
|
349
|
+
]
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
function createPricingStrategy(config, decimals = 6) {
|
|
354
|
+
switch (config.strategyType) {
|
|
355
|
+
case "fixed":
|
|
356
|
+
return new FixedPricingStrategy(config, decimals);
|
|
357
|
+
case "tiered":
|
|
358
|
+
return new TieredPricingStrategy(config, decimals);
|
|
359
|
+
case "usage-based":
|
|
360
|
+
return new UsageBasedPricingStrategy(config, decimals);
|
|
361
|
+
case "time-bucket":
|
|
362
|
+
return new TimeBucketPricingStrategy(config, decimals);
|
|
363
|
+
default:
|
|
364
|
+
throw new Error(`Unsupported strategy type: ${config.strategyType}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
368
|
+
0 && (module.exports = {
|
|
369
|
+
ASSET_SYMBOLS,
|
|
370
|
+
BasePricingStrategy,
|
|
371
|
+
FixedPricingStrategy,
|
|
372
|
+
TieredPricingStrategy,
|
|
373
|
+
TimeBucketPricingStrategy,
|
|
374
|
+
UsageBasedPricingStrategy,
|
|
375
|
+
applyDiscount,
|
|
376
|
+
applyMultiplier,
|
|
377
|
+
atomicUnitsToUsd,
|
|
378
|
+
buildPriceResult,
|
|
379
|
+
calculateUsageUnits,
|
|
380
|
+
createPricingStrategy,
|
|
381
|
+
determineTier,
|
|
382
|
+
formatPrice,
|
|
383
|
+
generateCacheKey,
|
|
384
|
+
getTimeBucket,
|
|
385
|
+
sortObject,
|
|
386
|
+
usdToAtomicUnits
|
|
387
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var ASSET_SYMBOLS = {
|
|
3
|
+
// USDC addresses across networks
|
|
4
|
+
"0x036CbD53842c5426634e7929541eC2318f3dCF7e": "USDC",
|
|
5
|
+
// Base Sepolia
|
|
6
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": "USDC",
|
|
7
|
+
// Base Mainnet
|
|
8
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": "USDC",
|
|
9
|
+
// Ethereum Mainnet
|
|
10
|
+
"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359": "USDC",
|
|
11
|
+
// Polygon
|
|
12
|
+
"0xaf88d065e77c8cC2239327C5EDb3A432268e5831": "USDC",
|
|
13
|
+
// Arbitrum
|
|
14
|
+
"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85": "USDC",
|
|
15
|
+
// Optimism
|
|
16
|
+
"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E": "USDC"
|
|
17
|
+
// Avalanche
|
|
18
|
+
};
|
|
19
|
+
function usdToAtomicUnits(usdAmount, decimals = 6) {
|
|
20
|
+
const parts = usdAmount.split(".");
|
|
21
|
+
const integerPart = parts[0] || "0";
|
|
22
|
+
const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
|
|
23
|
+
const atomicUnits = BigInt(integerPart + fractionalPart);
|
|
24
|
+
return atomicUnits.toString();
|
|
25
|
+
}
|
|
26
|
+
function atomicUnitsToUsd(atomicUnits, decimals = 6) {
|
|
27
|
+
const amount = BigInt(atomicUnits);
|
|
28
|
+
const divisor = BigInt(10 ** decimals);
|
|
29
|
+
const integerPart = amount / divisor;
|
|
30
|
+
const fractionalPart = amount % divisor;
|
|
31
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
32
|
+
return `${integerPart}.${fractionalStr}`;
|
|
33
|
+
}
|
|
34
|
+
function formatPrice(amount, asset, decimals = 6) {
|
|
35
|
+
const divisor = BigInt(10 ** decimals);
|
|
36
|
+
const integerPart = amount / divisor;
|
|
37
|
+
const fractionalPart = amount % divisor;
|
|
38
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
39
|
+
const formatted = `${integerPart}.${fractionalStr}`;
|
|
40
|
+
const symbol = ASSET_SYMBOLS[asset] || ASSET_SYMBOLS[asset.toLowerCase()] || "USDC";
|
|
41
|
+
return {
|
|
42
|
+
formatted,
|
|
43
|
+
symbol,
|
|
44
|
+
decimals
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function applyDiscount(amount, discountPercent) {
|
|
48
|
+
if (discountPercent <= 0 || discountPercent > 100) return amount;
|
|
49
|
+
const multiplier = BigInt(Math.floor((100 - discountPercent) * 100));
|
|
50
|
+
return amount * multiplier / 10000n;
|
|
51
|
+
}
|
|
52
|
+
function applyMultiplier(amount, multiplier) {
|
|
53
|
+
const multiplierInt = BigInt(Math.floor(multiplier * 1e4));
|
|
54
|
+
return amount * multiplierInt / 10000n;
|
|
55
|
+
}
|
|
56
|
+
function generateCacheKey(strategyType, configId, context) {
|
|
57
|
+
const keyComponents = {
|
|
58
|
+
strategy: strategyType,
|
|
59
|
+
configId,
|
|
60
|
+
method: context.request.method,
|
|
61
|
+
path: context.request.path,
|
|
62
|
+
query: sortObject(context.request.query),
|
|
63
|
+
bodyHash: context.request.body ? hashObject(context.request.body) : null,
|
|
64
|
+
vendorId: context.vendor.id,
|
|
65
|
+
endpointId: context.endpoint.id,
|
|
66
|
+
user: context.user?.address || "anonymous"
|
|
67
|
+
};
|
|
68
|
+
const keyString = JSON.stringify(keyComponents);
|
|
69
|
+
return simpleHash(keyString);
|
|
70
|
+
}
|
|
71
|
+
function sortObject(obj) {
|
|
72
|
+
if (!obj || typeof obj !== "object") return {};
|
|
73
|
+
return Object.keys(obj).sort().reduce((acc, key) => {
|
|
74
|
+
acc[key] = obj[key];
|
|
75
|
+
return acc;
|
|
76
|
+
}, {});
|
|
77
|
+
}
|
|
78
|
+
function simpleHash(str) {
|
|
79
|
+
let hash = 0;
|
|
80
|
+
for (let i = 0; i < str.length; i++) {
|
|
81
|
+
const char = str.charCodeAt(i);
|
|
82
|
+
hash = (hash << 5) - hash + char;
|
|
83
|
+
hash = hash & hash;
|
|
84
|
+
}
|
|
85
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
86
|
+
}
|
|
87
|
+
function hashObject(obj) {
|
|
88
|
+
return simpleHash(JSON.stringify(obj));
|
|
89
|
+
}
|
|
90
|
+
function getTimeBucket(timezone, peakHours) {
|
|
91
|
+
try {
|
|
92
|
+
const now = /* @__PURE__ */ new Date();
|
|
93
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
94
|
+
hour: "numeric",
|
|
95
|
+
hour12: false,
|
|
96
|
+
timeZone: timezone
|
|
97
|
+
});
|
|
98
|
+
const hour = parseInt(formatter.format(now), 10);
|
|
99
|
+
for (const peak of peakHours) {
|
|
100
|
+
if (hour >= peak.start && hour < peak.end) {
|
|
101
|
+
return "peak";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (hour >= 22 || hour < 6) {
|
|
105
|
+
return "off-peak";
|
|
106
|
+
}
|
|
107
|
+
return "normal";
|
|
108
|
+
} catch {
|
|
109
|
+
return "normal";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function determineTier(requestCount, tiers) {
|
|
113
|
+
const sortedTiers = [...tiers].sort((a, b) => a.upTo - b.upTo);
|
|
114
|
+
for (const tier of sortedTiers) {
|
|
115
|
+
if (tier.upTo === -1 || requestCount <= tier.upTo) {
|
|
116
|
+
return tier;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return sortedTiers[sortedTiers.length - 1];
|
|
120
|
+
}
|
|
121
|
+
function calculateUsageUnits(context, unit) {
|
|
122
|
+
switch (unit) {
|
|
123
|
+
case "per-request":
|
|
124
|
+
return 1;
|
|
125
|
+
case "per-byte":
|
|
126
|
+
return context.request.contentLength || 0;
|
|
127
|
+
case "per-token":
|
|
128
|
+
return Math.ceil((context.request.contentLength || 0) / 4);
|
|
129
|
+
default:
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function buildPriceResult(amount, asset, network, ttlSeconds = 300, breakdown) {
|
|
134
|
+
return {
|
|
135
|
+
amount: amount.toString(),
|
|
136
|
+
asset,
|
|
137
|
+
network,
|
|
138
|
+
breakdown,
|
|
139
|
+
validUntil: Math.floor(Date.now() / 1e3) + ttlSeconds,
|
|
140
|
+
display: formatPrice(amount, asset)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
var BasePricingStrategy = class {
|
|
144
|
+
constructor(config, decimals = 6) {
|
|
145
|
+
this.config = config;
|
|
146
|
+
this.decimals = decimals;
|
|
147
|
+
}
|
|
148
|
+
generateCacheKey(context) {
|
|
149
|
+
return generateCacheKey(this.type, this.config.id, context);
|
|
150
|
+
}
|
|
151
|
+
validate() {
|
|
152
|
+
const errors = [];
|
|
153
|
+
if (!this.config.parameters) {
|
|
154
|
+
errors.push("Strategy parameters are required");
|
|
155
|
+
}
|
|
156
|
+
if (!this.config.vendorId) {
|
|
157
|
+
errors.push("Vendor ID is required");
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
valid: errors.length === 0,
|
|
161
|
+
errors: errors.length > 0 ? errors : void 0
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
getParams() {
|
|
165
|
+
return this.config.parameters;
|
|
166
|
+
}
|
|
167
|
+
getCacheTtl() {
|
|
168
|
+
return this.config.cache?.ttlSeconds ?? 300;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var FixedPricingStrategy = class extends BasePricingStrategy {
|
|
172
|
+
constructor() {
|
|
173
|
+
super(...arguments);
|
|
174
|
+
this.name = "Fixed Pricing";
|
|
175
|
+
this.type = "fixed";
|
|
176
|
+
}
|
|
177
|
+
async calculatePrice(context) {
|
|
178
|
+
const params = this.getParams();
|
|
179
|
+
const amount = BigInt(params.price);
|
|
180
|
+
return buildPriceResult(
|
|
181
|
+
amount,
|
|
182
|
+
params.asset,
|
|
183
|
+
params.network,
|
|
184
|
+
this.getCacheTtl(),
|
|
185
|
+
[{ component: "Base Price", amount: params.price }]
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
validate() {
|
|
189
|
+
const base = super.validate();
|
|
190
|
+
if (!base.valid) return base;
|
|
191
|
+
const errors = [];
|
|
192
|
+
const params = this.getParams();
|
|
193
|
+
if (!params.price || BigInt(params.price) <= 0n) {
|
|
194
|
+
errors.push("Fixed price must be positive");
|
|
195
|
+
}
|
|
196
|
+
if (!params.asset) {
|
|
197
|
+
errors.push("Asset address is required");
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
valid: errors.length === 0,
|
|
201
|
+
errors: errors.length > 0 ? errors : void 0
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var TieredPricingStrategy = class extends BasePricingStrategy {
|
|
206
|
+
constructor() {
|
|
207
|
+
super(...arguments);
|
|
208
|
+
this.name = "Tiered Pricing";
|
|
209
|
+
this.type = "tiered";
|
|
210
|
+
}
|
|
211
|
+
async calculatePrice(context) {
|
|
212
|
+
const params = this.getParams();
|
|
213
|
+
const requestCount = context.user?.usage?.requestCount || 0;
|
|
214
|
+
const tier = determineTier(requestCount, params.tiers);
|
|
215
|
+
if (!tier) {
|
|
216
|
+
throw new Error("No applicable pricing tier found");
|
|
217
|
+
}
|
|
218
|
+
let amount = BigInt(tier.pricePerRequest);
|
|
219
|
+
if (tier.discount && tier.discount > 0) {
|
|
220
|
+
amount = applyDiscount(amount, tier.discount);
|
|
221
|
+
}
|
|
222
|
+
return buildPriceResult(
|
|
223
|
+
amount,
|
|
224
|
+
params.asset,
|
|
225
|
+
params.network,
|
|
226
|
+
this.getCacheTtl(),
|
|
227
|
+
[
|
|
228
|
+
{ component: "Base Price", amount: tier.pricePerRequest },
|
|
229
|
+
...tier.discount ? [{ component: "Tier Discount", amount: `-${tier.discount}%`, percentage: tier.discount }] : []
|
|
230
|
+
]
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
validate() {
|
|
234
|
+
const base = super.validate();
|
|
235
|
+
if (!base.valid) return base;
|
|
236
|
+
const errors = [];
|
|
237
|
+
const params = this.getParams();
|
|
238
|
+
if (!params.tiers || params.tiers.length === 0) {
|
|
239
|
+
errors.push("At least one pricing tier is required");
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
valid: errors.length === 0,
|
|
243
|
+
errors: errors.length > 0 ? errors : void 0
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var UsageBasedPricingStrategy = class extends BasePricingStrategy {
|
|
248
|
+
constructor() {
|
|
249
|
+
super(...arguments);
|
|
250
|
+
this.name = "Usage-Based Pricing";
|
|
251
|
+
this.type = "usage-based";
|
|
252
|
+
}
|
|
253
|
+
async calculatePrice(context) {
|
|
254
|
+
const params = this.getParams();
|
|
255
|
+
const units = calculateUsageUnits(context, params.unit);
|
|
256
|
+
let amount = BigInt(params.pricePerUnit) * BigInt(units);
|
|
257
|
+
if (params.minimumCharge) {
|
|
258
|
+
const min = BigInt(params.minimumCharge);
|
|
259
|
+
if (amount < min) {
|
|
260
|
+
amount = min;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (params.maximumCharge) {
|
|
264
|
+
const max = BigInt(params.maximumCharge);
|
|
265
|
+
if (amount > max) {
|
|
266
|
+
amount = max;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return buildPriceResult(
|
|
270
|
+
amount,
|
|
271
|
+
params.asset,
|
|
272
|
+
params.network,
|
|
273
|
+
this.getCacheTtl(),
|
|
274
|
+
[
|
|
275
|
+
{ component: `${units} ${params.unit}`, amount: amount.toString() }
|
|
276
|
+
]
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var TimeBucketPricingStrategy = class extends BasePricingStrategy {
|
|
281
|
+
constructor() {
|
|
282
|
+
super(...arguments);
|
|
283
|
+
this.name = "Time-Bucket Pricing";
|
|
284
|
+
this.type = "time-bucket";
|
|
285
|
+
}
|
|
286
|
+
async calculatePrice(context) {
|
|
287
|
+
const params = this.getParams();
|
|
288
|
+
const bucket = getTimeBucket(params.timezone, params.peakHours);
|
|
289
|
+
let amount = BigInt(params.basePrice);
|
|
290
|
+
let multiplier = 1;
|
|
291
|
+
switch (bucket) {
|
|
292
|
+
case "peak":
|
|
293
|
+
multiplier = params.peakMultiplier;
|
|
294
|
+
break;
|
|
295
|
+
case "off-peak":
|
|
296
|
+
multiplier = params.offPeakMultiplier;
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
amount = applyMultiplier(amount, multiplier);
|
|
300
|
+
return buildPriceResult(
|
|
301
|
+
amount,
|
|
302
|
+
params.asset,
|
|
303
|
+
params.network,
|
|
304
|
+
this.getCacheTtl(),
|
|
305
|
+
[
|
|
306
|
+
{ component: "Base Price", amount: params.basePrice },
|
|
307
|
+
{ component: `${bucket} multiplier`, amount: `x${multiplier}` }
|
|
308
|
+
]
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
function createPricingStrategy(config, decimals = 6) {
|
|
313
|
+
switch (config.strategyType) {
|
|
314
|
+
case "fixed":
|
|
315
|
+
return new FixedPricingStrategy(config, decimals);
|
|
316
|
+
case "tiered":
|
|
317
|
+
return new TieredPricingStrategy(config, decimals);
|
|
318
|
+
case "usage-based":
|
|
319
|
+
return new UsageBasedPricingStrategy(config, decimals);
|
|
320
|
+
case "time-bucket":
|
|
321
|
+
return new TimeBucketPricingStrategy(config, decimals);
|
|
322
|
+
default:
|
|
323
|
+
throw new Error(`Unsupported strategy type: ${config.strategyType}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
export {
|
|
327
|
+
ASSET_SYMBOLS,
|
|
328
|
+
BasePricingStrategy,
|
|
329
|
+
FixedPricingStrategy,
|
|
330
|
+
TieredPricingStrategy,
|
|
331
|
+
TimeBucketPricingStrategy,
|
|
332
|
+
UsageBasedPricingStrategy,
|
|
333
|
+
applyDiscount,
|
|
334
|
+
applyMultiplier,
|
|
335
|
+
atomicUnitsToUsd,
|
|
336
|
+
buildPriceResult,
|
|
337
|
+
calculateUsageUnits,
|
|
338
|
+
createPricingStrategy,
|
|
339
|
+
determineTier,
|
|
340
|
+
formatPrice,
|
|
341
|
+
generateCacheKey,
|
|
342
|
+
getTimeBucket,
|
|
343
|
+
sortObject,
|
|
344
|
+
usdToAtomicUnits
|
|
345
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@perkos/service-pricing",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Dynamic pricing service with strategy patterns for x402 payment protocol",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"pricing",
|
|
24
|
+
"dynamic-pricing",
|
|
25
|
+
"x402",
|
|
26
|
+
"payment",
|
|
27
|
+
"strategy-pattern",
|
|
28
|
+
"tiered-pricing",
|
|
29
|
+
"usage-based-pricing"
|
|
30
|
+
],
|
|
31
|
+
"author": "PerkOS",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/PerkOS-xyz/pkg-service-pricing"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@perkos/types-x402": "^1.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"tsup": "^8.5.0",
|
|
42
|
+
"typescript": "^5.8.3"
|
|
43
|
+
}
|
|
44
|
+
}
|