@newyorkcompute/kalshi-core 0.1.1 → 0.2.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/cache.d.ts +71 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +96 -0
- package/dist/cache.js.map +1 -0
- package/dist/cache.test.d.ts +5 -0
- package/dist/cache.test.d.ts.map +1 -0
- package/dist/cache.test.js +108 -0
- package/dist/cache.test.js.map +1 -0
- package/dist/format.d.ts +39 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +80 -0
- package/dist/format.js.map +1 -1
- package/dist/format.test.js +66 -2
- package/dist/format.test.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/rate-limiter.d.ts +80 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +180 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/rate-limiter.test.d.ts +5 -0
- package/dist/rate-limiter.test.d.ts.map +1 -0
- package/dist/rate-limiter.test.js +175 -0
- package/dist/rate-limiter.test.js.map +1 -0
- package/package.json +1 -1
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL Cache
|
|
3
|
+
*
|
|
4
|
+
* Lightweight in-memory cache with time-to-live expiration.
|
|
5
|
+
* Useful for caching API responses to reduce rate limiting.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Get cached data if not expired
|
|
9
|
+
*
|
|
10
|
+
* @param key - Cache key
|
|
11
|
+
* @param ttl - Time-to-live in milliseconds (default: 60s)
|
|
12
|
+
* @returns Cached data or null if expired/missing
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const data = getCached<Market[]>('markets', 30000);
|
|
17
|
+
* if (!data) {
|
|
18
|
+
* // Fetch fresh data
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function getCached<T>(key: string, ttl?: number): T | null;
|
|
23
|
+
/**
|
|
24
|
+
* Set cached data with current timestamp
|
|
25
|
+
*
|
|
26
|
+
* @param key - Cache key
|
|
27
|
+
* @param data - Data to cache
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* setCache('markets', marketData);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function setCache<T>(key: string, data: T): void;
|
|
35
|
+
/**
|
|
36
|
+
* Clear specific cache entry
|
|
37
|
+
*
|
|
38
|
+
* @param key - Cache key to clear
|
|
39
|
+
*/
|
|
40
|
+
export declare function clearCache(key: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Clear all cache entries
|
|
43
|
+
*/
|
|
44
|
+
export declare function clearAllCache(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Get cache statistics (for debugging)
|
|
47
|
+
*
|
|
48
|
+
* @returns Object with cache size and keys
|
|
49
|
+
*/
|
|
50
|
+
export declare function getCacheStats(): {
|
|
51
|
+
size: number;
|
|
52
|
+
keys: string[];
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Common TTL constants for Kalshi data
|
|
56
|
+
*/
|
|
57
|
+
export declare const CACHE_TTL: {
|
|
58
|
+
/** 5 minutes - Trade history doesn't change rapidly */
|
|
59
|
+
readonly PRICE_HISTORY: number;
|
|
60
|
+
/** 10 minutes - Market titles/close times rarely change */
|
|
61
|
+
readonly MARKET_METADATA: number;
|
|
62
|
+
/** 1 minute - Events list */
|
|
63
|
+
readonly EVENTS: number;
|
|
64
|
+
/** No cache - Orderbook needs real-time data */
|
|
65
|
+
readonly ORDERBOOK: 0;
|
|
66
|
+
/** No cache - Balance needs real-time data */
|
|
67
|
+
readonly BALANCE: 0;
|
|
68
|
+
/** No cache - Positions need real-time data */
|
|
69
|
+
readonly POSITIONS: 0;
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,GAAG,CAAC,GAAG,IAAI,CAY7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAKtD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKhE;AAED;;GAEG;AACH,eAAO,MAAM,SAAS;IACpB,uDAAuD;;IAEvD,2DAA2D;;IAE3D,6BAA6B;;IAE7B,gDAAgD;;IAEhD,8CAA8C;;IAE9C,+CAA+C;;CAEvC,CAAC"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL Cache
|
|
3
|
+
*
|
|
4
|
+
* Lightweight in-memory cache with time-to-live expiration.
|
|
5
|
+
* Useful for caching API responses to reduce rate limiting.
|
|
6
|
+
*/
|
|
7
|
+
// Cache storage
|
|
8
|
+
const cache = new Map();
|
|
9
|
+
// Default TTL: 1 minute
|
|
10
|
+
const DEFAULT_TTL = 60 * 1000;
|
|
11
|
+
/**
|
|
12
|
+
* Get cached data if not expired
|
|
13
|
+
*
|
|
14
|
+
* @param key - Cache key
|
|
15
|
+
* @param ttl - Time-to-live in milliseconds (default: 60s)
|
|
16
|
+
* @returns Cached data or null if expired/missing
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const data = getCached<Market[]>('markets', 30000);
|
|
21
|
+
* if (!data) {
|
|
22
|
+
* // Fetch fresh data
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function getCached(key, ttl = DEFAULT_TTL) {
|
|
27
|
+
const entry = cache.get(key);
|
|
28
|
+
if (!entry)
|
|
29
|
+
return null;
|
|
30
|
+
const isExpired = Date.now() - entry.timestamp > ttl;
|
|
31
|
+
if (isExpired) {
|
|
32
|
+
cache.delete(key);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return entry.data;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Set cached data with current timestamp
|
|
39
|
+
*
|
|
40
|
+
* @param key - Cache key
|
|
41
|
+
* @param data - Data to cache
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* setCache('markets', marketData);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function setCache(key, data) {
|
|
49
|
+
cache.set(key, {
|
|
50
|
+
data,
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clear specific cache entry
|
|
56
|
+
*
|
|
57
|
+
* @param key - Cache key to clear
|
|
58
|
+
*/
|
|
59
|
+
export function clearCache(key) {
|
|
60
|
+
cache.delete(key);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clear all cache entries
|
|
64
|
+
*/
|
|
65
|
+
export function clearAllCache() {
|
|
66
|
+
cache.clear();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get cache statistics (for debugging)
|
|
70
|
+
*
|
|
71
|
+
* @returns Object with cache size and keys
|
|
72
|
+
*/
|
|
73
|
+
export function getCacheStats() {
|
|
74
|
+
return {
|
|
75
|
+
size: cache.size,
|
|
76
|
+
keys: Array.from(cache.keys()),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Common TTL constants for Kalshi data
|
|
81
|
+
*/
|
|
82
|
+
export const CACHE_TTL = {
|
|
83
|
+
/** 5 minutes - Trade history doesn't change rapidly */
|
|
84
|
+
PRICE_HISTORY: 5 * 60 * 1000,
|
|
85
|
+
/** 10 minutes - Market titles/close times rarely change */
|
|
86
|
+
MARKET_METADATA: 10 * 60 * 1000,
|
|
87
|
+
/** 1 minute - Events list */
|
|
88
|
+
EVENTS: 60 * 1000,
|
|
89
|
+
/** No cache - Orderbook needs real-time data */
|
|
90
|
+
ORDERBOOK: 0,
|
|
91
|
+
/** No cache - Balance needs real-time data */
|
|
92
|
+
BALANCE: 0,
|
|
93
|
+
/** No cache - Positions need real-time data */
|
|
94
|
+
POSITIONS: 0,
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,gBAAgB;AAChB,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;AAErD,wBAAwB;AACxB,MAAM,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9B;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAI,GAAW,EAAE,MAAc,WAAW;IACjE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;IACrD,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,IAAS,CAAC;AACzB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,QAAQ,CAAI,GAAW,EAAE,IAAO;IAC9C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;QACb,IAAI;QACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,uDAAuD;IACvD,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IAC5B,2DAA2D;IAC3D,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,EAAE,GAAG,IAAI;IACjB,gDAAgD;IAChD,SAAS,EAAE,CAAC;IACZ,8CAA8C;IAC9C,OAAO,EAAE,CAAC;IACV,+CAA+C;IAC/C,SAAS,EAAE,CAAC;CACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../src/cache.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
5
|
+
import { getCached, setCache, clearCache, clearAllCache, getCacheStats, CACHE_TTL, } from './cache.js';
|
|
6
|
+
describe('cache', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
clearAllCache();
|
|
9
|
+
vi.useFakeTimers();
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.useRealTimers();
|
|
13
|
+
});
|
|
14
|
+
describe('setCache and getCached', () => {
|
|
15
|
+
it('should store and retrieve data', () => {
|
|
16
|
+
const data = { ticker: 'KXBTC', price: 50 };
|
|
17
|
+
setCache('market-1', data);
|
|
18
|
+
const result = getCached('market-1');
|
|
19
|
+
expect(result).toEqual(data);
|
|
20
|
+
});
|
|
21
|
+
it('should return null for non-existent key', () => {
|
|
22
|
+
const result = getCached('non-existent');
|
|
23
|
+
expect(result).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
it('should return null after TTL expires', () => {
|
|
26
|
+
const data = { value: 42 };
|
|
27
|
+
setCache('test-key', data);
|
|
28
|
+
// Advance time past default TTL (60s)
|
|
29
|
+
vi.advanceTimersByTime(61 * 1000);
|
|
30
|
+
const result = getCached('test-key');
|
|
31
|
+
expect(result).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
it('should return data before TTL expires', () => {
|
|
34
|
+
const data = { value: 42 };
|
|
35
|
+
setCache('test-key', data);
|
|
36
|
+
// Advance time but stay within TTL
|
|
37
|
+
vi.advanceTimersByTime(30 * 1000);
|
|
38
|
+
const result = getCached('test-key');
|
|
39
|
+
expect(result).toEqual(data);
|
|
40
|
+
});
|
|
41
|
+
it('should respect custom TTL', () => {
|
|
42
|
+
const data = { value: 'test' };
|
|
43
|
+
const customTTL = 5000; // 5 seconds
|
|
44
|
+
setCache('custom-ttl', data);
|
|
45
|
+
// Still valid at 4 seconds
|
|
46
|
+
vi.advanceTimersByTime(4000);
|
|
47
|
+
expect(getCached('custom-ttl', customTTL)).toEqual(data);
|
|
48
|
+
// Expired at 6 seconds
|
|
49
|
+
vi.advanceTimersByTime(2000);
|
|
50
|
+
expect(getCached('custom-ttl', customTTL)).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it('should overwrite existing cache entry', () => {
|
|
53
|
+
setCache('key', { v: 1 });
|
|
54
|
+
setCache('key', { v: 2 });
|
|
55
|
+
const result = getCached('key');
|
|
56
|
+
expect(result).toEqual({ v: 2 });
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('clearCache', () => {
|
|
60
|
+
it('should clear specific cache entry', () => {
|
|
61
|
+
setCache('key1', 'value1');
|
|
62
|
+
setCache('key2', 'value2');
|
|
63
|
+
clearCache('key1');
|
|
64
|
+
expect(getCached('key1')).toBeNull();
|
|
65
|
+
expect(getCached('key2')).toBe('value2');
|
|
66
|
+
});
|
|
67
|
+
it('should handle clearing non-existent key', () => {
|
|
68
|
+
expect(() => clearCache('non-existent')).not.toThrow();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('clearAllCache', () => {
|
|
72
|
+
it('should clear all cache entries', () => {
|
|
73
|
+
setCache('key1', 'value1');
|
|
74
|
+
setCache('key2', 'value2');
|
|
75
|
+
setCache('key3', 'value3');
|
|
76
|
+
clearAllCache();
|
|
77
|
+
expect(getCached('key1')).toBeNull();
|
|
78
|
+
expect(getCached('key2')).toBeNull();
|
|
79
|
+
expect(getCached('key3')).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('getCacheStats', () => {
|
|
83
|
+
it('should return cache statistics', () => {
|
|
84
|
+
setCache('key1', 'value1');
|
|
85
|
+
setCache('key2', 'value2');
|
|
86
|
+
const stats = getCacheStats();
|
|
87
|
+
expect(stats.size).toBe(2);
|
|
88
|
+
expect(stats.keys).toContain('key1');
|
|
89
|
+
expect(stats.keys).toContain('key2');
|
|
90
|
+
});
|
|
91
|
+
it('should return empty stats for empty cache', () => {
|
|
92
|
+
const stats = getCacheStats();
|
|
93
|
+
expect(stats.size).toBe(0);
|
|
94
|
+
expect(stats.keys).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe('CACHE_TTL constants', () => {
|
|
98
|
+
it('should have expected TTL values', () => {
|
|
99
|
+
expect(CACHE_TTL.PRICE_HISTORY).toBe(5 * 60 * 1000);
|
|
100
|
+
expect(CACHE_TTL.MARKET_METADATA).toBe(10 * 60 * 1000);
|
|
101
|
+
expect(CACHE_TTL.EVENTS).toBe(60 * 1000);
|
|
102
|
+
expect(CACHE_TTL.ORDERBOOK).toBe(0);
|
|
103
|
+
expect(CACHE_TTL.BALANCE).toBe(0);
|
|
104
|
+
expect(CACHE_TTL.POSITIONS).toBe(0);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../src/cache.test.ts"],"names":[],"mappings":"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"}
|
package/dist/format.d.ts
CHANGED
|
@@ -25,8 +25,47 @@ export declare function formatPriceChange(change: number): string;
|
|
|
25
25
|
export declare function formatCompactNumber(value: number | undefined | null): string;
|
|
26
26
|
/**
|
|
27
27
|
* Format a timestamp to a relative time string
|
|
28
|
+
*
|
|
29
|
+
* @param timestamp - Date string or Date object
|
|
30
|
+
* @returns Relative time string (e.g., "2d ago", "in 3h")
|
|
28
31
|
*/
|
|
29
32
|
export declare function formatRelativeTime(timestamp: string | Date): string;
|
|
33
|
+
/**
|
|
34
|
+
* Format market close time as expiry string
|
|
35
|
+
*
|
|
36
|
+
* Handles various time ranges:
|
|
37
|
+
* - Minutes: "45m"
|
|
38
|
+
* - Hours: "3h 45m"
|
|
39
|
+
* - Days: "2d 14h" or "45d" (for >30 days)
|
|
40
|
+
* - Years: "2y 3mo" or "2y"
|
|
41
|
+
* - Very distant (>10y): "distant"
|
|
42
|
+
* - Past: "CLOSED"
|
|
43
|
+
*
|
|
44
|
+
* @param closeTime - ISO date string for market close time
|
|
45
|
+
* @returns Formatted expiry string
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* formatExpiry('2025-12-31T23:59:59Z') // "1y 2mo"
|
|
50
|
+
* formatExpiry('2099-01-01T00:00:00Z') // "distant"
|
|
51
|
+
* formatExpiry('2024-01-01T00:00:00Z') // "CLOSED" (if past)
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatExpiry(closeTime?: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Calculate spread between best bid and best ask
|
|
57
|
+
*
|
|
58
|
+
* @param bestBid - Best (highest) bid price
|
|
59
|
+
* @param bestAsk - Best (lowest) ask price
|
|
60
|
+
* @returns Spread in cents, or null if either price is missing
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* calculateSpread(48, 52) // 4
|
|
65
|
+
* calculateSpread(null, 52) // null
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function calculateSpread(bestBid: number | null | undefined, bestAsk: number | null | undefined): number | null;
|
|
30
69
|
/**
|
|
31
70
|
* Truncate a string to a maximum length with ellipsis
|
|
32
71
|
*/
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAKpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAMvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAKtE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAY5E;AAED
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAKpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAMvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAKtE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAY5E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAuBnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CA4CvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,MAAM,GAAG,IAAI,CAGf;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,GAAG,OAAgB,GAC/B,MAAM,CAMR"}
|
package/dist/format.js
CHANGED
|
@@ -60,6 +60,9 @@ export function formatCompactNumber(value) {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Format a timestamp to a relative time string
|
|
63
|
+
*
|
|
64
|
+
* @param timestamp - Date string or Date object
|
|
65
|
+
* @returns Relative time string (e.g., "2d ago", "in 3h")
|
|
63
66
|
*/
|
|
64
67
|
export function formatRelativeTime(timestamp) {
|
|
65
68
|
const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
|
|
@@ -83,6 +86,83 @@ export function formatRelativeTime(timestamp) {
|
|
|
83
86
|
}
|
|
84
87
|
return "now";
|
|
85
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Format market close time as expiry string
|
|
91
|
+
*
|
|
92
|
+
* Handles various time ranges:
|
|
93
|
+
* - Minutes: "45m"
|
|
94
|
+
* - Hours: "3h 45m"
|
|
95
|
+
* - Days: "2d 14h" or "45d" (for >30 days)
|
|
96
|
+
* - Years: "2y 3mo" or "2y"
|
|
97
|
+
* - Very distant (>10y): "distant"
|
|
98
|
+
* - Past: "CLOSED"
|
|
99
|
+
*
|
|
100
|
+
* @param closeTime - ISO date string for market close time
|
|
101
|
+
* @returns Formatted expiry string
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* formatExpiry('2025-12-31T23:59:59Z') // "1y 2mo"
|
|
106
|
+
* formatExpiry('2099-01-01T00:00:00Z') // "distant"
|
|
107
|
+
* formatExpiry('2024-01-01T00:00:00Z') // "CLOSED" (if past)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function formatExpiry(closeTime) {
|
|
111
|
+
if (!closeTime)
|
|
112
|
+
return '';
|
|
113
|
+
const now = new Date();
|
|
114
|
+
const close = new Date(closeTime);
|
|
115
|
+
const diffMs = close.getTime() - now.getTime();
|
|
116
|
+
if (diffMs <= 0)
|
|
117
|
+
return 'CLOSED';
|
|
118
|
+
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
119
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
120
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
121
|
+
const diffYears = Math.floor(diffDays / 365);
|
|
122
|
+
// For very distant dates, show "distant"
|
|
123
|
+
if (diffYears > 10) {
|
|
124
|
+
return 'distant';
|
|
125
|
+
}
|
|
126
|
+
// For dates > 1 year, show years and months
|
|
127
|
+
if (diffYears >= 1) {
|
|
128
|
+
const remainingMonths = Math.floor((diffDays % 365) / 30);
|
|
129
|
+
return remainingMonths > 0 ? `${diffYears}y ${remainingMonths}mo` : `${diffYears}y`;
|
|
130
|
+
}
|
|
131
|
+
// For dates > 30 days, show just days
|
|
132
|
+
if (diffDays > 30) {
|
|
133
|
+
return `${diffDays}d`;
|
|
134
|
+
}
|
|
135
|
+
// For dates with days remaining, show days and hours
|
|
136
|
+
if (diffDays > 0) {
|
|
137
|
+
const remainingHours = diffHours % 24;
|
|
138
|
+
return `${diffDays}d ${remainingHours}h`;
|
|
139
|
+
}
|
|
140
|
+
// For dates with hours remaining, show hours and minutes
|
|
141
|
+
if (diffHours > 0) {
|
|
142
|
+
const remainingMins = diffMins % 60;
|
|
143
|
+
return `${diffHours}h ${remainingMins}m`;
|
|
144
|
+
}
|
|
145
|
+
// Just minutes
|
|
146
|
+
return `${diffMins}m`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Calculate spread between best bid and best ask
|
|
150
|
+
*
|
|
151
|
+
* @param bestBid - Best (highest) bid price
|
|
152
|
+
* @param bestAsk - Best (lowest) ask price
|
|
153
|
+
* @returns Spread in cents, or null if either price is missing
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* calculateSpread(48, 52) // 4
|
|
158
|
+
* calculateSpread(null, 52) // null
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export function calculateSpread(bestBid, bestAsk) {
|
|
162
|
+
if (bestBid == null || bestAsk == null)
|
|
163
|
+
return null;
|
|
164
|
+
return bestAsk - bestBid;
|
|
165
|
+
}
|
|
86
166
|
/**
|
|
87
167
|
* Truncate a string to a maximum length with ellipsis
|
|
88
168
|
*/
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgC;IAC1D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,GAAG,KAAK,GAAG,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgC;IAC7D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,GAAG,GAAG,CAAC;IAC5B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,MAAM,MAAM,EAAE,CAAC;IACxB,CAAC;SAAM,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,MAAM,EAAE,CAAC;IACvB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAgC;IAClE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,CAAC;IACD,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACnB,OAAO,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
package/dist/format.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, truncate, padString, } from "./format.js";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, formatExpiry, calculateSpread, truncate, padString, } from "./format.js";
|
|
3
3
|
describe("formatPrice", () => {
|
|
4
4
|
it("formats cents with cent symbol", () => {
|
|
5
5
|
expect(formatPrice(45)).toBe("45¢");
|
|
@@ -87,4 +87,68 @@ describe("padString", () => {
|
|
|
87
87
|
expect(padString("Hello World", 5)).toBe("Hello");
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
|
+
describe("formatExpiry", () => {
|
|
91
|
+
beforeEach(() => {
|
|
92
|
+
vi.useFakeTimers();
|
|
93
|
+
vi.setSystemTime(new Date('2025-06-15T12:00:00Z'));
|
|
94
|
+
});
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
vi.useRealTimers();
|
|
97
|
+
});
|
|
98
|
+
it("returns empty string for undefined", () => {
|
|
99
|
+
expect(formatExpiry(undefined)).toBe('');
|
|
100
|
+
});
|
|
101
|
+
it("returns CLOSED for past dates", () => {
|
|
102
|
+
expect(formatExpiry('2025-06-14T12:00:00Z')).toBe('CLOSED');
|
|
103
|
+
expect(formatExpiry('2020-01-01T00:00:00Z')).toBe('CLOSED');
|
|
104
|
+
});
|
|
105
|
+
it("formats minutes", () => {
|
|
106
|
+
expect(formatExpiry('2025-06-15T12:30:00Z')).toBe('30m');
|
|
107
|
+
expect(formatExpiry('2025-06-15T12:45:00Z')).toBe('45m');
|
|
108
|
+
});
|
|
109
|
+
it("formats hours and minutes", () => {
|
|
110
|
+
expect(formatExpiry('2025-06-15T15:30:00Z')).toBe('3h 30m');
|
|
111
|
+
expect(formatExpiry('2025-06-15T20:00:00Z')).toBe('8h 0m');
|
|
112
|
+
});
|
|
113
|
+
it("formats days and hours", () => {
|
|
114
|
+
expect(formatExpiry('2025-06-17T12:00:00Z')).toBe('2d 0h');
|
|
115
|
+
expect(formatExpiry('2025-06-20T18:00:00Z')).toBe('5d 6h');
|
|
116
|
+
});
|
|
117
|
+
it("formats just days for >30 days", () => {
|
|
118
|
+
expect(formatExpiry('2025-08-15T12:00:00Z')).toBe('61d');
|
|
119
|
+
expect(formatExpiry('2025-10-15T12:00:00Z')).toBe('122d');
|
|
120
|
+
});
|
|
121
|
+
it("formats years and months", () => {
|
|
122
|
+
expect(formatExpiry('2026-06-15T12:00:00Z')).toBe('1y');
|
|
123
|
+
expect(formatExpiry('2027-09-15T12:00:00Z')).toBe('2y 3mo');
|
|
124
|
+
});
|
|
125
|
+
it("returns distant for >10 years", () => {
|
|
126
|
+
expect(formatExpiry('2036-06-15T12:00:00Z')).toBe('distant');
|
|
127
|
+
expect(formatExpiry('2099-01-01T00:00:00Z')).toBe('distant');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe("calculateSpread", () => {
|
|
131
|
+
it("calculates spread correctly", () => {
|
|
132
|
+
expect(calculateSpread(48, 52)).toBe(4);
|
|
133
|
+
expect(calculateSpread(45, 55)).toBe(10);
|
|
134
|
+
expect(calculateSpread(50, 51)).toBe(1);
|
|
135
|
+
});
|
|
136
|
+
it("returns null for null bid", () => {
|
|
137
|
+
expect(calculateSpread(null, 52)).toBeNull();
|
|
138
|
+
});
|
|
139
|
+
it("returns null for null ask", () => {
|
|
140
|
+
expect(calculateSpread(48, null)).toBeNull();
|
|
141
|
+
});
|
|
142
|
+
it("returns null for undefined values", () => {
|
|
143
|
+
expect(calculateSpread(undefined, 52)).toBeNull();
|
|
144
|
+
expect(calculateSpread(48, undefined)).toBeNull();
|
|
145
|
+
expect(calculateSpread(undefined, undefined)).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
it("handles zero spread", () => {
|
|
148
|
+
expect(calculateSpread(50, 50)).toBe(0);
|
|
149
|
+
});
|
|
150
|
+
it("handles negative spread (crossed market)", () => {
|
|
151
|
+
expect(calculateSpread(52, 48)).toBe(-4);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
90
154
|
//# sourceMappingURL=format.test.js.map
|
package/dist/format.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.test.js","sourceRoot":"","sources":["../src/format.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"format.test.js","sourceRoot":"","sources":["../src/format.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Shared utilities for Kalshi prediction market tools.
|
|
5
5
|
*/
|
|
6
6
|
export { type KalshiConfig, DEFAULT_BASE_PATH, DEMO_BASE_PATH, getKalshiConfig, createSdkConfig, createMarketApi, createPortfolioApi, createOrdersApi, createEventsApi, } from "./config.js";
|
|
7
|
-
export { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, formatRelativeTime, truncate, padString, } from "./format.js";
|
|
7
|
+
export { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, formatRelativeTime, formatExpiry, calculateSpread, truncate, padString, } from "./format.js";
|
|
8
|
+
export { getCached, setCache, clearCache, clearAllCache, getCacheStats, CACHE_TTL, } from "./cache.js";
|
|
9
|
+
export { createRateLimiter, isRateLimitError, type RateLimiter, type RateLimiterConfig, type RateLimiterState, } from "./rate-limiter.js";
|
|
8
10
|
export * from "./types.js";
|
|
9
11
|
export { withTimeout, TimeoutError } from "./with-timeout.js";
|
|
10
12
|
export { validateOrder, type OrderValidationInput, type OrderValidationResult, } from "./validate-order.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,KAAK,YAAY,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,MAAM,aAAa,CAAC;AAGrB,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAG9D,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,KAAK,YAAY,EACjB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,GACV,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,UAAU,EACV,aAAa,EACb,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAG9D,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
// Configuration
|
|
7
7
|
export { DEFAULT_BASE_PATH, DEMO_BASE_PATH, getKalshiConfig, createSdkConfig, createMarketApi, createPortfolioApi, createOrdersApi, createEventsApi, } from "./config.js";
|
|
8
8
|
// Formatting utilities
|
|
9
|
-
export { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, formatRelativeTime, truncate, padString, } from "./format.js";
|
|
9
|
+
export { formatPrice, formatCurrency, formatPercent, formatPriceChange, formatCompactNumber, formatRelativeTime, formatExpiry, calculateSpread, truncate, padString, } from "./format.js";
|
|
10
|
+
// Cache utilities
|
|
11
|
+
export { getCached, setCache, clearCache, clearAllCache, getCacheStats, CACHE_TTL, } from "./cache.js";
|
|
12
|
+
// Rate limiting
|
|
13
|
+
export { createRateLimiter, isRateLimitError, } from "./rate-limiter.js";
|
|
10
14
|
// Types
|
|
11
15
|
export * from "./types.js";
|
|
12
16
|
// Utilities
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gBAAgB;AAChB,OAAO,EAEL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE9D,aAAa;AACb,OAAO,EACL,aAAa,GAGd,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,gBAAgB;AAChB,OAAO,EAEL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,kBAAkB;AAClB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,UAAU,EACV,aAAa,EACb,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,gBAAgB;AAChB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,GAIjB,MAAM,mBAAmB,CAAC;AAE3B,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE9D,aAAa;AACb,OAAO,EACL,aAAa,GAGd,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter
|
|
3
|
+
*
|
|
4
|
+
* Implements exponential backoff and circuit breaker patterns
|
|
5
|
+
* for handling API rate limits gracefully.
|
|
6
|
+
*/
|
|
7
|
+
export interface RateLimiterConfig {
|
|
8
|
+
/** Minimum interval between requests in ms (default: 1000) */
|
|
9
|
+
minInterval: number;
|
|
10
|
+
/** Maximum interval between requests in ms (default: 60000) */
|
|
11
|
+
maxInterval: number;
|
|
12
|
+
/** Multiplier for exponential backoff (default: 2) */
|
|
13
|
+
backoffMultiplier: number;
|
|
14
|
+
/** Number of consecutive failures before circuit opens (default: 5) */
|
|
15
|
+
circuitBreakerThreshold: number;
|
|
16
|
+
/** Time to wait before attempting to close circuit in ms (default: 30000) */
|
|
17
|
+
circuitResetTimeout: number;
|
|
18
|
+
}
|
|
19
|
+
export interface RateLimiterState {
|
|
20
|
+
/** Current interval between requests */
|
|
21
|
+
currentInterval: number;
|
|
22
|
+
/** Number of consecutive failures */
|
|
23
|
+
consecutiveFailures: number;
|
|
24
|
+
/** Whether the circuit breaker is open (blocking requests) */
|
|
25
|
+
isCircuitOpen: boolean;
|
|
26
|
+
/** Whether rate limiting is active */
|
|
27
|
+
isRateLimited: boolean;
|
|
28
|
+
/** Timestamp of last successful request */
|
|
29
|
+
lastSuccessTime: number;
|
|
30
|
+
/** Timestamp of last failure */
|
|
31
|
+
lastFailureTime: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a rate limiter instance
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const limiter = createRateLimiter();
|
|
39
|
+
*
|
|
40
|
+
* async function fetchData() {
|
|
41
|
+
* if (limiter.shouldBlock()) {
|
|
42
|
+
* return; // Skip request
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* try {
|
|
46
|
+
* const data = await api.getData();
|
|
47
|
+
* limiter.recordSuccess();
|
|
48
|
+
* return data;
|
|
49
|
+
* } catch (err) {
|
|
50
|
+
* if (isRateLimitError(err)) {
|
|
51
|
+
* limiter.recordRateLimitError();
|
|
52
|
+
* } else {
|
|
53
|
+
* limiter.recordFailure();
|
|
54
|
+
* }
|
|
55
|
+
* throw err;
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function createRateLimiter(config?: Partial<RateLimiterConfig>): {
|
|
61
|
+
shouldBlock: () => boolean;
|
|
62
|
+
getCurrentInterval: () => number;
|
|
63
|
+
getState: () => Readonly<RateLimiterState>;
|
|
64
|
+
recordSuccess: () => void;
|
|
65
|
+
recordRateLimitError: () => void;
|
|
66
|
+
recordFailure: () => void;
|
|
67
|
+
reset: () => void;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Type for the rate limiter instance
|
|
71
|
+
*/
|
|
72
|
+
export type RateLimiter = ReturnType<typeof createRateLimiter>;
|
|
73
|
+
/**
|
|
74
|
+
* Check if an error is a rate limit error (HTTP 429)
|
|
75
|
+
*
|
|
76
|
+
* @param error - Error to check
|
|
77
|
+
* @returns true if the error is a rate limit error
|
|
78
|
+
*/
|
|
79
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
80
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,uBAAuB,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,aAAa,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,aAAa,EAAE,OAAO,CAAC;IACvB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,eAAe,EAAE,MAAM,CAAC;CACzB;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;uBAiB/C,OAAO;8BAOA,MAAM;oBAOhB,QAAQ,CAAC,gBAAgB,CAAC;yBAOrB,IAAI;gCAmBG,IAAI;yBAoBX,IAAI;iBA8BZ,IAAI;EAyBvB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBxD"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter
|
|
3
|
+
*
|
|
4
|
+
* Implements exponential backoff and circuit breaker patterns
|
|
5
|
+
* for handling API rate limits gracefully.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
minInterval: 1000,
|
|
9
|
+
maxInterval: 60000,
|
|
10
|
+
backoffMultiplier: 2,
|
|
11
|
+
circuitBreakerThreshold: 5,
|
|
12
|
+
circuitResetTimeout: 30000,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Create a rate limiter instance
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const limiter = createRateLimiter();
|
|
20
|
+
*
|
|
21
|
+
* async function fetchData() {
|
|
22
|
+
* if (limiter.shouldBlock()) {
|
|
23
|
+
* return; // Skip request
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* try {
|
|
27
|
+
* const data = await api.getData();
|
|
28
|
+
* limiter.recordSuccess();
|
|
29
|
+
* return data;
|
|
30
|
+
* } catch (err) {
|
|
31
|
+
* if (isRateLimitError(err)) {
|
|
32
|
+
* limiter.recordRateLimitError();
|
|
33
|
+
* } else {
|
|
34
|
+
* limiter.recordFailure();
|
|
35
|
+
* }
|
|
36
|
+
* throw err;
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function createRateLimiter(config = {}) {
|
|
42
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
43
|
+
let state = {
|
|
44
|
+
currentInterval: cfg.minInterval,
|
|
45
|
+
consecutiveFailures: 0,
|
|
46
|
+
isCircuitOpen: false,
|
|
47
|
+
isRateLimited: false,
|
|
48
|
+
lastSuccessTime: 0,
|
|
49
|
+
lastFailureTime: 0,
|
|
50
|
+
};
|
|
51
|
+
let circuitResetTimer = null;
|
|
52
|
+
/**
|
|
53
|
+
* Check if requests should be blocked
|
|
54
|
+
*/
|
|
55
|
+
function shouldBlock() {
|
|
56
|
+
return state.isCircuitOpen;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the current recommended interval between requests
|
|
60
|
+
*/
|
|
61
|
+
function getCurrentInterval() {
|
|
62
|
+
return state.currentInterval;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the current state (for debugging/display)
|
|
66
|
+
*/
|
|
67
|
+
function getState() {
|
|
68
|
+
return { ...state };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Record a successful request - resets backoff
|
|
72
|
+
*/
|
|
73
|
+
function recordSuccess() {
|
|
74
|
+
state.consecutiveFailures = 0;
|
|
75
|
+
state.currentInterval = cfg.minInterval;
|
|
76
|
+
state.isRateLimited = false;
|
|
77
|
+
state.lastSuccessTime = Date.now();
|
|
78
|
+
// Close circuit if it was open
|
|
79
|
+
if (state.isCircuitOpen) {
|
|
80
|
+
state.isCircuitOpen = false;
|
|
81
|
+
if (circuitResetTimer) {
|
|
82
|
+
clearTimeout(circuitResetTimer);
|
|
83
|
+
circuitResetTimer = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Record a rate limit error (429) - applies exponential backoff
|
|
89
|
+
*/
|
|
90
|
+
function recordRateLimitError() {
|
|
91
|
+
state.consecutiveFailures++;
|
|
92
|
+
state.isRateLimited = true;
|
|
93
|
+
state.lastFailureTime = Date.now();
|
|
94
|
+
// Apply exponential backoff
|
|
95
|
+
state.currentInterval = Math.min(state.currentInterval * cfg.backoffMultiplier, cfg.maxInterval);
|
|
96
|
+
// Open circuit if threshold reached
|
|
97
|
+
if (state.consecutiveFailures >= cfg.circuitBreakerThreshold) {
|
|
98
|
+
openCircuit();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Record a general failure (not rate limit)
|
|
103
|
+
*/
|
|
104
|
+
function recordFailure() {
|
|
105
|
+
state.consecutiveFailures++;
|
|
106
|
+
state.lastFailureTime = Date.now();
|
|
107
|
+
// Open circuit if threshold reached
|
|
108
|
+
if (state.consecutiveFailures >= cfg.circuitBreakerThreshold) {
|
|
109
|
+
openCircuit();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Open the circuit breaker (block all requests temporarily)
|
|
114
|
+
*/
|
|
115
|
+
function openCircuit() {
|
|
116
|
+
if (state.isCircuitOpen)
|
|
117
|
+
return;
|
|
118
|
+
state.isCircuitOpen = true;
|
|
119
|
+
// Schedule circuit reset
|
|
120
|
+
circuitResetTimer = setTimeout(() => {
|
|
121
|
+
state.isCircuitOpen = false;
|
|
122
|
+
state.consecutiveFailures = 0;
|
|
123
|
+
state.currentInterval = cfg.minInterval;
|
|
124
|
+
circuitResetTimer = null;
|
|
125
|
+
}, cfg.circuitResetTimeout);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Reset the rate limiter to initial state
|
|
129
|
+
*/
|
|
130
|
+
function reset() {
|
|
131
|
+
if (circuitResetTimer) {
|
|
132
|
+
clearTimeout(circuitResetTimer);
|
|
133
|
+
circuitResetTimer = null;
|
|
134
|
+
}
|
|
135
|
+
state = {
|
|
136
|
+
currentInterval: cfg.minInterval,
|
|
137
|
+
consecutiveFailures: 0,
|
|
138
|
+
isCircuitOpen: false,
|
|
139
|
+
isRateLimited: false,
|
|
140
|
+
lastSuccessTime: 0,
|
|
141
|
+
lastFailureTime: 0,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
shouldBlock,
|
|
146
|
+
getCurrentInterval,
|
|
147
|
+
getState,
|
|
148
|
+
recordSuccess,
|
|
149
|
+
recordRateLimitError,
|
|
150
|
+
recordFailure,
|
|
151
|
+
reset,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if an error is a rate limit error (HTTP 429)
|
|
156
|
+
*
|
|
157
|
+
* @param error - Error to check
|
|
158
|
+
* @returns true if the error is a rate limit error
|
|
159
|
+
*/
|
|
160
|
+
export function isRateLimitError(error) {
|
|
161
|
+
if (!error || typeof error !== 'object')
|
|
162
|
+
return false;
|
|
163
|
+
// Check for status code 429
|
|
164
|
+
if ('status' in error && error.status === 429)
|
|
165
|
+
return true;
|
|
166
|
+
if ('response' in error) {
|
|
167
|
+
const response = error.response;
|
|
168
|
+
if (response?.status === 429)
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
// Check error message
|
|
172
|
+
if ('message' in error && typeof error.message === 'string') {
|
|
173
|
+
const msg = error.message.toLowerCase();
|
|
174
|
+
if (msg.includes('429') || msg.includes('rate limit') || msg.includes('too many requests')) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.test.d.ts","sourceRoot":"","sources":["../src/rate-limiter.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
5
|
+
import { createRateLimiter, isRateLimitError, } from './rate-limiter.js';
|
|
6
|
+
describe('createRateLimiter', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.useFakeTimers();
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.useRealTimers();
|
|
12
|
+
});
|
|
13
|
+
describe('initial state', () => {
|
|
14
|
+
it('should not block requests initially', () => {
|
|
15
|
+
const limiter = createRateLimiter();
|
|
16
|
+
expect(limiter.shouldBlock()).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('should have minimum interval initially', () => {
|
|
19
|
+
const limiter = createRateLimiter({ minInterval: 1000 });
|
|
20
|
+
expect(limiter.getCurrentInterval()).toBe(1000);
|
|
21
|
+
});
|
|
22
|
+
it('should have correct initial state', () => {
|
|
23
|
+
const limiter = createRateLimiter();
|
|
24
|
+
const state = limiter.getState();
|
|
25
|
+
expect(state.consecutiveFailures).toBe(0);
|
|
26
|
+
expect(state.isCircuitOpen).toBe(false);
|
|
27
|
+
expect(state.isRateLimited).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('recordSuccess', () => {
|
|
31
|
+
it('should reset consecutive failures', () => {
|
|
32
|
+
const limiter = createRateLimiter();
|
|
33
|
+
limiter.recordFailure();
|
|
34
|
+
limiter.recordFailure();
|
|
35
|
+
expect(limiter.getState().consecutiveFailures).toBe(2);
|
|
36
|
+
limiter.recordSuccess();
|
|
37
|
+
expect(limiter.getState().consecutiveFailures).toBe(0);
|
|
38
|
+
});
|
|
39
|
+
it('should reset interval to minimum', () => {
|
|
40
|
+
const limiter = createRateLimiter({ minInterval: 1000 });
|
|
41
|
+
limiter.recordRateLimitError();
|
|
42
|
+
expect(limiter.getCurrentInterval()).toBeGreaterThan(1000);
|
|
43
|
+
limiter.recordSuccess();
|
|
44
|
+
expect(limiter.getCurrentInterval()).toBe(1000);
|
|
45
|
+
});
|
|
46
|
+
it('should clear rate limited flag', () => {
|
|
47
|
+
const limiter = createRateLimiter();
|
|
48
|
+
limiter.recordRateLimitError();
|
|
49
|
+
expect(limiter.getState().isRateLimited).toBe(true);
|
|
50
|
+
limiter.recordSuccess();
|
|
51
|
+
expect(limiter.getState().isRateLimited).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('recordRateLimitError', () => {
|
|
55
|
+
it('should increase interval with exponential backoff', () => {
|
|
56
|
+
const limiter = createRateLimiter({
|
|
57
|
+
minInterval: 1000,
|
|
58
|
+
backoffMultiplier: 2,
|
|
59
|
+
});
|
|
60
|
+
limiter.recordRateLimitError();
|
|
61
|
+
expect(limiter.getCurrentInterval()).toBe(2000);
|
|
62
|
+
limiter.recordRateLimitError();
|
|
63
|
+
expect(limiter.getCurrentInterval()).toBe(4000);
|
|
64
|
+
limiter.recordRateLimitError();
|
|
65
|
+
expect(limiter.getCurrentInterval()).toBe(8000);
|
|
66
|
+
});
|
|
67
|
+
it('should not exceed max interval', () => {
|
|
68
|
+
const limiter = createRateLimiter({
|
|
69
|
+
minInterval: 1000,
|
|
70
|
+
maxInterval: 5000,
|
|
71
|
+
backoffMultiplier: 2,
|
|
72
|
+
});
|
|
73
|
+
// 1000 -> 2000 -> 4000 -> 5000 (capped)
|
|
74
|
+
limiter.recordRateLimitError();
|
|
75
|
+
limiter.recordRateLimitError();
|
|
76
|
+
limiter.recordRateLimitError();
|
|
77
|
+
limiter.recordRateLimitError();
|
|
78
|
+
expect(limiter.getCurrentInterval()).toBe(5000);
|
|
79
|
+
});
|
|
80
|
+
it('should set rate limited flag', () => {
|
|
81
|
+
const limiter = createRateLimiter();
|
|
82
|
+
limiter.recordRateLimitError();
|
|
83
|
+
expect(limiter.getState().isRateLimited).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
it('should increment consecutive failures', () => {
|
|
86
|
+
const limiter = createRateLimiter();
|
|
87
|
+
limiter.recordRateLimitError();
|
|
88
|
+
expect(limiter.getState().consecutiveFailures).toBe(1);
|
|
89
|
+
limiter.recordRateLimitError();
|
|
90
|
+
expect(limiter.getState().consecutiveFailures).toBe(2);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('circuit breaker', () => {
|
|
94
|
+
it('should open circuit after threshold failures', () => {
|
|
95
|
+
const limiter = createRateLimiter({
|
|
96
|
+
circuitBreakerThreshold: 3,
|
|
97
|
+
});
|
|
98
|
+
limiter.recordFailure();
|
|
99
|
+
limiter.recordFailure();
|
|
100
|
+
expect(limiter.shouldBlock()).toBe(false);
|
|
101
|
+
limiter.recordFailure();
|
|
102
|
+
expect(limiter.shouldBlock()).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it('should reset circuit after timeout', () => {
|
|
105
|
+
const limiter = createRateLimiter({
|
|
106
|
+
circuitBreakerThreshold: 2,
|
|
107
|
+
circuitResetTimeout: 5000,
|
|
108
|
+
});
|
|
109
|
+
limiter.recordFailure();
|
|
110
|
+
limiter.recordFailure();
|
|
111
|
+
expect(limiter.shouldBlock()).toBe(true);
|
|
112
|
+
// Advance time past reset timeout
|
|
113
|
+
vi.advanceTimersByTime(5001);
|
|
114
|
+
expect(limiter.shouldBlock()).toBe(false);
|
|
115
|
+
expect(limiter.getState().consecutiveFailures).toBe(0);
|
|
116
|
+
});
|
|
117
|
+
it('should close circuit on success', () => {
|
|
118
|
+
const limiter = createRateLimiter({
|
|
119
|
+
circuitBreakerThreshold: 2,
|
|
120
|
+
});
|
|
121
|
+
limiter.recordFailure();
|
|
122
|
+
limiter.recordFailure();
|
|
123
|
+
expect(limiter.shouldBlock()).toBe(true);
|
|
124
|
+
limiter.recordSuccess();
|
|
125
|
+
expect(limiter.shouldBlock()).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('reset', () => {
|
|
129
|
+
it('should reset all state', () => {
|
|
130
|
+
const limiter = createRateLimiter({
|
|
131
|
+
minInterval: 1000,
|
|
132
|
+
circuitBreakerThreshold: 2,
|
|
133
|
+
});
|
|
134
|
+
limiter.recordRateLimitError();
|
|
135
|
+
limiter.recordRateLimitError();
|
|
136
|
+
expect(limiter.shouldBlock()).toBe(true);
|
|
137
|
+
expect(limiter.getCurrentInterval()).toBeGreaterThan(1000);
|
|
138
|
+
limiter.reset();
|
|
139
|
+
expect(limiter.shouldBlock()).toBe(false);
|
|
140
|
+
expect(limiter.getCurrentInterval()).toBe(1000);
|
|
141
|
+
expect(limiter.getState().consecutiveFailures).toBe(0);
|
|
142
|
+
expect(limiter.getState().isRateLimited).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
describe('isRateLimitError', () => {
|
|
147
|
+
it('should return true for error with status 429', () => {
|
|
148
|
+
expect(isRateLimitError({ status: 429 })).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
it('should return true for error with response.status 429', () => {
|
|
151
|
+
expect(isRateLimitError({ response: { status: 429 } })).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
it('should return true for error message containing 429', () => {
|
|
154
|
+
expect(isRateLimitError({ message: 'Error 429: Too Many Requests' })).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
it('should return true for error message containing rate limit', () => {
|
|
157
|
+
expect(isRateLimitError({ message: 'Rate limit exceeded' })).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
it('should return true for error message containing too many requests', () => {
|
|
160
|
+
expect(isRateLimitError({ message: 'too many requests' })).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('should return false for other errors', () => {
|
|
163
|
+
expect(isRateLimitError({ status: 500 })).toBe(false);
|
|
164
|
+
expect(isRateLimitError({ message: 'Internal server error' })).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
it('should return false for null/undefined', () => {
|
|
167
|
+
expect(isRateLimitError(null)).toBe(false);
|
|
168
|
+
expect(isRateLimitError(undefined)).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
it('should return false for non-objects', () => {
|
|
171
|
+
expect(isRateLimitError('error')).toBe(false);
|
|
172
|
+
expect(isRateLimitError(123)).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
//# sourceMappingURL=rate-limiter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.test.js","sourceRoot":"","sources":["../src/rate-limiter.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED