@http-client-toolkit/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +160 -0
- package/lib/index.cjs +615 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +382 -0
- package/lib/index.d.ts +382 -0
- package/lib/index.js +604 -0
- package/lib/index.js.map +1 -0
- package/package.json +76 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for caching API responses with TTL support
|
|
5
|
+
*/
|
|
6
|
+
interface CacheStore<T = unknown> {
|
|
7
|
+
/**
|
|
8
|
+
* Retrieve a cached value by hash key
|
|
9
|
+
* @param hash The hash key of the cached item
|
|
10
|
+
* @returns The cached value or undefined if not found or expired
|
|
11
|
+
*/
|
|
12
|
+
get(hash: string): Promise<T | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* Store a value in the cache with a TTL
|
|
15
|
+
* @param hash The hash key for the cached item
|
|
16
|
+
* @param value The value to cache
|
|
17
|
+
* @param ttlSeconds TTL in seconds after which the item expires
|
|
18
|
+
*/
|
|
19
|
+
set(hash: string, value: T, ttlSeconds: number): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Remove a cached item by hash key
|
|
22
|
+
* @param hash The hash key of the cached item
|
|
23
|
+
*/
|
|
24
|
+
delete(hash: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Clear all cached items
|
|
27
|
+
*/
|
|
28
|
+
clear(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Interface for deduplicating concurrent API requests
|
|
33
|
+
*/
|
|
34
|
+
interface DedupeStore<T = unknown> {
|
|
35
|
+
/**
|
|
36
|
+
* Wait for the result of an existing request if one is in progress
|
|
37
|
+
* @param hash The hash key of the request
|
|
38
|
+
* @returns The result if found, otherwise undefined
|
|
39
|
+
*/
|
|
40
|
+
waitFor(hash: string): Promise<T | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* Register a new request and get a job ID
|
|
43
|
+
* @param hash The hash key of the request
|
|
44
|
+
* @returns A unique job ID for this request
|
|
45
|
+
*/
|
|
46
|
+
register(hash: string): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Atomically register or join an in-flight request.
|
|
49
|
+
*
|
|
50
|
+
* When provided, this allows callers to determine ownership and guarantees
|
|
51
|
+
* that only one caller executes the upstream request while others wait.
|
|
52
|
+
* Implementations that do not provide this method remain compatible, but
|
|
53
|
+
* may allow duplicate upstream requests under extreme races.
|
|
54
|
+
*
|
|
55
|
+
* @param hash The hash key of the request
|
|
56
|
+
* @returns The job id and whether the caller owns execution
|
|
57
|
+
*/
|
|
58
|
+
registerOrJoin?(hash: string): Promise<{
|
|
59
|
+
jobId: string;
|
|
60
|
+
isOwner: boolean;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Mark a request as complete with its result
|
|
64
|
+
* @param hash The hash key of the request
|
|
65
|
+
* @param value The result of the request
|
|
66
|
+
*/
|
|
67
|
+
complete(hash: string, value: T): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Mark a request as failed with an error
|
|
70
|
+
* @param hash The hash key of the request
|
|
71
|
+
* @param error The error that occurred
|
|
72
|
+
*/
|
|
73
|
+
fail(hash: string, error: Error): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a request is currently in progress
|
|
76
|
+
* @param hash The hash key of the request
|
|
77
|
+
* @returns True if the request is in progress
|
|
78
|
+
*/
|
|
79
|
+
isInProgress(hash: string): Promise<boolean>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Priority level for API requests
|
|
84
|
+
*/
|
|
85
|
+
type RequestPriority = 'user' | 'background';
|
|
86
|
+
/**
|
|
87
|
+
* Adaptive configuration schema with validation and defaults
|
|
88
|
+
*/
|
|
89
|
+
declare const AdaptiveConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
90
|
+
monitoringWindowMs: z.ZodDefault<z.ZodNumber>;
|
|
91
|
+
highActivityThreshold: z.ZodDefault<z.ZodNumber>;
|
|
92
|
+
moderateActivityThreshold: z.ZodDefault<z.ZodNumber>;
|
|
93
|
+
recalculationIntervalMs: z.ZodDefault<z.ZodNumber>;
|
|
94
|
+
sustainedInactivityThresholdMs: z.ZodDefault<z.ZodNumber>;
|
|
95
|
+
backgroundPauseOnIncreasingTrend: z.ZodDefault<z.ZodBoolean>;
|
|
96
|
+
maxUserScaling: z.ZodDefault<z.ZodNumber>;
|
|
97
|
+
minUserReserved: z.ZodDefault<z.ZodNumber>;
|
|
98
|
+
}, "strip", z.ZodTypeAny, {
|
|
99
|
+
monitoringWindowMs: number;
|
|
100
|
+
highActivityThreshold: number;
|
|
101
|
+
moderateActivityThreshold: number;
|
|
102
|
+
recalculationIntervalMs: number;
|
|
103
|
+
sustainedInactivityThresholdMs: number;
|
|
104
|
+
backgroundPauseOnIncreasingTrend: boolean;
|
|
105
|
+
maxUserScaling: number;
|
|
106
|
+
minUserReserved: number;
|
|
107
|
+
}, {
|
|
108
|
+
monitoringWindowMs?: number | undefined;
|
|
109
|
+
highActivityThreshold?: number | undefined;
|
|
110
|
+
moderateActivityThreshold?: number | undefined;
|
|
111
|
+
recalculationIntervalMs?: number | undefined;
|
|
112
|
+
sustainedInactivityThresholdMs?: number | undefined;
|
|
113
|
+
backgroundPauseOnIncreasingTrend?: boolean | undefined;
|
|
114
|
+
maxUserScaling?: number | undefined;
|
|
115
|
+
minUserReserved?: number | undefined;
|
|
116
|
+
}>, {
|
|
117
|
+
monitoringWindowMs: number;
|
|
118
|
+
highActivityThreshold: number;
|
|
119
|
+
moderateActivityThreshold: number;
|
|
120
|
+
recalculationIntervalMs: number;
|
|
121
|
+
sustainedInactivityThresholdMs: number;
|
|
122
|
+
backgroundPauseOnIncreasingTrend: boolean;
|
|
123
|
+
maxUserScaling: number;
|
|
124
|
+
minUserReserved: number;
|
|
125
|
+
}, {
|
|
126
|
+
monitoringWindowMs?: number | undefined;
|
|
127
|
+
highActivityThreshold?: number | undefined;
|
|
128
|
+
moderateActivityThreshold?: number | undefined;
|
|
129
|
+
recalculationIntervalMs?: number | undefined;
|
|
130
|
+
sustainedInactivityThresholdMs?: number | undefined;
|
|
131
|
+
backgroundPauseOnIncreasingTrend?: boolean | undefined;
|
|
132
|
+
maxUserScaling?: number | undefined;
|
|
133
|
+
minUserReserved?: number | undefined;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Configuration for adaptive rate limiting
|
|
137
|
+
*/
|
|
138
|
+
type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;
|
|
139
|
+
/**
|
|
140
|
+
* Interface for rate limiting API requests per resource
|
|
141
|
+
*/
|
|
142
|
+
interface RateLimitStore {
|
|
143
|
+
/**
|
|
144
|
+
* Check if a request to a resource can proceed based on rate limits
|
|
145
|
+
* @param resource The resource name (e.g., 'issues', 'characters')
|
|
146
|
+
* @returns True if the request can proceed, false if rate limited
|
|
147
|
+
*/
|
|
148
|
+
canProceed(resource: string): Promise<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* Record a request to a resource for rate limiting tracking
|
|
151
|
+
* @param resource The resource name (e.g., 'issues', 'characters')
|
|
152
|
+
*/
|
|
153
|
+
record(resource: string): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Get the current rate limit status for a resource
|
|
156
|
+
* @param resource The resource name
|
|
157
|
+
* @returns Rate limit information including remaining requests and reset time
|
|
158
|
+
*/
|
|
159
|
+
getStatus(resource: string): Promise<{
|
|
160
|
+
remaining: number;
|
|
161
|
+
resetTime: Date;
|
|
162
|
+
limit: number;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Reset rate limits for a resource (useful for testing)
|
|
166
|
+
* @param resource The resource name
|
|
167
|
+
*/
|
|
168
|
+
reset(resource: string): Promise<void>;
|
|
169
|
+
/**
|
|
170
|
+
* Get the time in milliseconds until the next request can be made
|
|
171
|
+
* @param resource The resource name
|
|
172
|
+
* @returns Milliseconds to wait, or 0 if no waiting is needed
|
|
173
|
+
*/
|
|
174
|
+
getWaitTime(resource: string): Promise<number>;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Enhanced interface for adaptive rate limiting stores with priority support
|
|
178
|
+
*/
|
|
179
|
+
interface AdaptiveRateLimitStore extends RateLimitStore {
|
|
180
|
+
/**
|
|
181
|
+
* Check if a request to a resource can proceed based on rate limits
|
|
182
|
+
* @param resource The resource name (e.g., 'issues', 'characters')
|
|
183
|
+
* @param priority The priority level of the request (defaults to 'background')
|
|
184
|
+
* @returns True if the request can proceed, false if rate limited
|
|
185
|
+
*/
|
|
186
|
+
canProceed(resource: string, priority?: RequestPriority): Promise<boolean>;
|
|
187
|
+
/**
|
|
188
|
+
* Record a request to a resource for rate limiting tracking
|
|
189
|
+
* @param resource The resource name (e.g., 'issues', 'characters')
|
|
190
|
+
* @param priority The priority level of the request (defaults to 'background')
|
|
191
|
+
*/
|
|
192
|
+
record(resource: string, priority?: RequestPriority): Promise<void>;
|
|
193
|
+
/**
|
|
194
|
+
* Get the current rate limit status for a resource
|
|
195
|
+
* @param resource The resource name
|
|
196
|
+
* @returns Rate limit information including remaining requests and reset time
|
|
197
|
+
*/
|
|
198
|
+
getStatus(resource: string): Promise<{
|
|
199
|
+
remaining: number;
|
|
200
|
+
resetTime: Date;
|
|
201
|
+
limit: number;
|
|
202
|
+
adaptive?: {
|
|
203
|
+
userReserved: number;
|
|
204
|
+
backgroundMax: number;
|
|
205
|
+
backgroundPaused: boolean;
|
|
206
|
+
recentUserActivity: number;
|
|
207
|
+
reason: string;
|
|
208
|
+
};
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Get the time in milliseconds until the next request can be made
|
|
212
|
+
* @param resource The resource name
|
|
213
|
+
* @param priority The priority level of the request (defaults to 'background')
|
|
214
|
+
* @returns Milliseconds to wait, or 0 if no waiting is needed
|
|
215
|
+
*/
|
|
216
|
+
getWaitTime(resource: string, priority?: RequestPriority): Promise<number>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Creates a consistent hash for API requests to use as cache/dedupe keys
|
|
221
|
+
* @param endpoint The API endpoint
|
|
222
|
+
* @param params The request parameters
|
|
223
|
+
* @returns A SHA-256 hash of the request
|
|
224
|
+
*/
|
|
225
|
+
declare function hashRequest(endpoint: string, params?: Record<string, unknown>): string;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Configuration for per-resource rate limiting.
|
|
229
|
+
*
|
|
230
|
+
* This interface is shared by all store implementations (e.g. in-memory,
|
|
231
|
+
* SQLite) so that callers can use a single canonical type.
|
|
232
|
+
*/
|
|
233
|
+
interface RateLimitConfig {
|
|
234
|
+
/** Number of requests allowed per time window */
|
|
235
|
+
limit: number;
|
|
236
|
+
/** Duration of the window in milliseconds */
|
|
237
|
+
windowMs: number;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Default rate-limit window: 60 requests per minute.
|
|
241
|
+
*
|
|
242
|
+
* Store implementations can reference this to avoid duplicating magic numbers.
|
|
243
|
+
*/
|
|
244
|
+
declare const DEFAULT_RATE_LIMIT: RateLimitConfig;
|
|
245
|
+
|
|
246
|
+
interface ActivityMetrics {
|
|
247
|
+
recentUserRequests: Array<number>;
|
|
248
|
+
recentBackgroundRequests: Array<number>;
|
|
249
|
+
userActivityTrend: 'increasing' | 'stable' | 'decreasing' | 'none';
|
|
250
|
+
}
|
|
251
|
+
interface DynamicCapacityResult {
|
|
252
|
+
userReserved: number;
|
|
253
|
+
backgroundMax: number;
|
|
254
|
+
backgroundPaused: boolean;
|
|
255
|
+
reason: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Calculates dynamic capacity allocation based on real-time user activity patterns
|
|
259
|
+
*/
|
|
260
|
+
declare class AdaptiveCapacityCalculator {
|
|
261
|
+
readonly config: z.infer<typeof AdaptiveConfigSchema>;
|
|
262
|
+
constructor(config?: Partial<z.input<typeof AdaptiveConfigSchema>>);
|
|
263
|
+
calculateDynamicCapacity(resource: string, totalLimit: number, activityMetrics: ActivityMetrics): DynamicCapacityResult;
|
|
264
|
+
getRecentActivity(requests: Array<number>): number;
|
|
265
|
+
calculateActivityTrend(requests: Array<number>): 'increasing' | 'stable' | 'decreasing' | 'none';
|
|
266
|
+
private getUserMultiplier;
|
|
267
|
+
private getSustainedInactivityPeriod;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface HttpClientContract {
|
|
271
|
+
/**
|
|
272
|
+
* Perform a GET request.
|
|
273
|
+
*
|
|
274
|
+
* @param url Full request URL
|
|
275
|
+
* @param options Optional configuration – primarily an AbortSignal so
|
|
276
|
+
* callers can cancel long-running or rate-limited waits.
|
|
277
|
+
*/
|
|
278
|
+
get<Result>(url: string, options?: {
|
|
279
|
+
/**
|
|
280
|
+
* AbortSignal that allows the caller to cancel the request, including any
|
|
281
|
+
* internal rate-limit wait. If the signal is aborted while waiting the
|
|
282
|
+
* promise rejects with an `AbortError`-like `Error` instance.
|
|
283
|
+
*/
|
|
284
|
+
signal?: AbortSignal;
|
|
285
|
+
/**
|
|
286
|
+
* Priority level for the request (affects rate limiting behavior)
|
|
287
|
+
*/
|
|
288
|
+
priority?: RequestPriority;
|
|
289
|
+
}): Promise<Result>;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
interface HttpClientStores {
|
|
293
|
+
cache?: CacheStore;
|
|
294
|
+
dedupe?: DedupeStore;
|
|
295
|
+
rateLimit?: RateLimitStore | AdaptiveRateLimitStore;
|
|
296
|
+
}
|
|
297
|
+
interface HttpClientOptions {
|
|
298
|
+
/**
|
|
299
|
+
* Default cache TTL in seconds
|
|
300
|
+
*/
|
|
301
|
+
defaultCacheTTL?: number;
|
|
302
|
+
/**
|
|
303
|
+
* Whether to throw errors on rate limit violations
|
|
304
|
+
*/
|
|
305
|
+
throwOnRateLimit?: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* Maximum time to wait for rate limit in milliseconds
|
|
308
|
+
*/
|
|
309
|
+
maxWaitTime?: number;
|
|
310
|
+
/**
|
|
311
|
+
* Optional response transformer applied to the raw response data.
|
|
312
|
+
* Use this for converting snake_case to camelCase, etc.
|
|
313
|
+
*/
|
|
314
|
+
responseTransformer?: (data: unknown) => unknown;
|
|
315
|
+
/**
|
|
316
|
+
* Optional error handler to convert errors into domain-specific error types.
|
|
317
|
+
* If not provided, a generic HttpClientError is thrown.
|
|
318
|
+
*/
|
|
319
|
+
errorHandler?: (error: unknown) => Error;
|
|
320
|
+
/**
|
|
321
|
+
* Optional response validator/handler called after transformation.
|
|
322
|
+
* Use this to inspect the response and throw domain-specific errors
|
|
323
|
+
* based on response content (e.g., API-level error codes).
|
|
324
|
+
*/
|
|
325
|
+
responseHandler?: (data: unknown) => unknown;
|
|
326
|
+
/**
|
|
327
|
+
* Configure rate-limit response header names for standards and custom APIs.
|
|
328
|
+
*/
|
|
329
|
+
rateLimitHeaders?: {
|
|
330
|
+
retryAfter?: Array<string>;
|
|
331
|
+
limit?: Array<string>;
|
|
332
|
+
remaining?: Array<string>;
|
|
333
|
+
reset?: Array<string>;
|
|
334
|
+
combined?: Array<string>;
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
declare class HttpClient implements HttpClientContract {
|
|
338
|
+
private _http;
|
|
339
|
+
private stores;
|
|
340
|
+
private serverCooldowns;
|
|
341
|
+
private options;
|
|
342
|
+
constructor(stores?: HttpClientStores, options?: HttpClientOptions);
|
|
343
|
+
private normalizeRateLimitHeaders;
|
|
344
|
+
private normalizeHeaderNames;
|
|
345
|
+
/**
|
|
346
|
+
* Infer the resource name from the endpoint URL
|
|
347
|
+
* @param url The full URL or endpoint path
|
|
348
|
+
* @returns The resource name for rate limiting
|
|
349
|
+
*/
|
|
350
|
+
private inferResource;
|
|
351
|
+
/**
|
|
352
|
+
* Extract endpoint and params from URL for request hashing
|
|
353
|
+
* @param url The full URL
|
|
354
|
+
* @returns Object with endpoint and params for hashing
|
|
355
|
+
*/
|
|
356
|
+
private parseUrlForHashing;
|
|
357
|
+
private getOriginScope;
|
|
358
|
+
private getHeaderValue;
|
|
359
|
+
private parseIntegerHeader;
|
|
360
|
+
private parseRetryAfterMs;
|
|
361
|
+
private parseResetMs;
|
|
362
|
+
private parseCombinedRateLimitHeader;
|
|
363
|
+
private applyServerRateLimitHints;
|
|
364
|
+
private enforceServerCooldown;
|
|
365
|
+
private enforceStoreRateLimit;
|
|
366
|
+
private generateClientError;
|
|
367
|
+
get<Result>(url: string, options?: {
|
|
368
|
+
signal?: AbortSignal;
|
|
369
|
+
priority?: RequestPriority;
|
|
370
|
+
}): Promise<Result>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Base error class for HTTP client errors.
|
|
375
|
+
* Consumers can extend this for domain-specific error handling.
|
|
376
|
+
*/
|
|
377
|
+
declare class HttpClientError extends Error {
|
|
378
|
+
readonly statusCode?: number;
|
|
379
|
+
constructor(message: string, statusCode?: number);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export { type ActivityMetrics, AdaptiveCapacityCalculator, type AdaptiveConfig, AdaptiveConfigSchema, type AdaptiveRateLimitStore, type CacheStore, DEFAULT_RATE_LIMIT, type DedupeStore, type DynamicCapacityResult, HttpClient, type HttpClientContract, HttpClientError, type HttpClientOptions, type HttpClientStores, type RateLimitConfig, type RateLimitStore, type RequestPriority, hashRequest };
|