@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/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 };