@http-client-toolkit/core 0.0.1 → 0.1.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/lib/index.cjs CHANGED
@@ -1,13 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var axios = require('axios');
4
3
  var zod = require('zod');
5
4
  var crypto = require('crypto');
6
5
 
7
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
-
9
- var axios__default = /*#__PURE__*/_interopDefault(axios);
10
-
11
6
  var __async = (__this, __arguments, generator) => {
12
7
  return new Promise((resolve, reject) => {
13
8
  var fulfilled = (value) => {
@@ -264,7 +259,6 @@ var HttpClient = class {
264
259
  constructor(stores = {}, options = {}) {
265
260
  this.serverCooldowns = /* @__PURE__ */ new Map();
266
261
  var _a, _b, _c;
267
- this._http = axios__default.default.create();
268
262
  this.stores = stores;
269
263
  this.options = {
270
264
  defaultCacheTTL: (_a = options.defaultCacheTTL) != null ? _a : 3600,
@@ -361,6 +355,15 @@ var HttpClient = class {
361
355
  if (!headers) {
362
356
  return void 0;
363
357
  }
358
+ if (headers instanceof Headers) {
359
+ for (const rawName of names) {
360
+ const value = headers.get(rawName);
361
+ if (value !== null) {
362
+ return value;
363
+ }
364
+ }
365
+ return void 0;
366
+ }
364
367
  for (const rawName of names) {
365
368
  const name = rawName.toLowerCase();
366
369
  const value = (_a = headers[name]) != null ? _a : headers[rawName];
@@ -487,17 +490,24 @@ var HttpClient = class {
487
490
  return __async(this, null, function* () {
488
491
  const rateLimit = this.stores.rateLimit;
489
492
  const startedAt = Date.now();
493
+ const hasAtomicAcquire = typeof rateLimit.acquire === "function";
494
+ const canProceedNow = () => __async(this, null, function* () {
495
+ if (hasAtomicAcquire) {
496
+ return rateLimit.acquire(resource, priority);
497
+ }
498
+ return rateLimit.canProceed(resource, priority);
499
+ });
490
500
  if (this.options.throwOnRateLimit) {
491
- const canProceed = yield rateLimit.canProceed(resource, priority);
501
+ const canProceed = yield canProceedNow();
492
502
  if (!canProceed) {
493
503
  const waitTime = yield rateLimit.getWaitTime(resource, priority);
494
504
  throw new Error(
495
505
  `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`
496
506
  );
497
507
  }
498
- return;
508
+ return hasAtomicAcquire;
499
509
  }
500
- while (!(yield rateLimit.canProceed(resource, priority))) {
510
+ while (!(yield canProceedNow())) {
501
511
  const suggestedWaitMs = yield rateLimit.getWaitTime(resource, priority);
502
512
  const elapsedMs = Date.now() - startedAt;
503
513
  const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;
@@ -509,22 +519,52 @@ var HttpClient = class {
509
519
  const waitTime = suggestedWaitMs > 0 ? Math.min(suggestedWaitMs, remainingWaitBudgetMs) : Math.min(25, remainingWaitBudgetMs);
510
520
  yield wait(waitTime, signal);
511
521
  }
522
+ return hasAtomicAcquire;
512
523
  });
513
524
  }
514
525
  generateClientError(err) {
515
- var _a, _b, _c;
526
+ var _a, _b;
516
527
  if (this.options.errorHandler) {
517
528
  return this.options.errorHandler(err);
518
529
  }
519
530
  if (err instanceof HttpClientError) {
520
531
  return err;
521
532
  }
522
- const error = err;
523
- const statusCode = (_a = error.response) == null ? void 0 : _a.status;
524
- const errorMessage = (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.message;
525
- const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ""}`;
533
+ const responseError = err;
534
+ const statusCode = typeof ((_a = responseError.response) == null ? void 0 : _a.status) === "number" ? responseError.response.status : void 0;
535
+ const responseData = (_b = responseError.response) == null ? void 0 : _b.data;
536
+ const derivedResponseMessage = typeof responseData === "object" && responseData !== null ? responseData.message : void 0;
537
+ const responseMessage = typeof derivedResponseMessage === "string" ? derivedResponseMessage : void 0;
538
+ const errorMessage = err instanceof Error ? err.message : typeof err.message === "string" ? err.message : "Unknown error";
539
+ const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ""}`;
526
540
  return new HttpClientError(message, statusCode);
527
541
  }
542
+ parseResponseBody(response) {
543
+ return __async(this, null, function* () {
544
+ var _a, _b;
545
+ if (response.status === 204 || response.status === 205) {
546
+ return { data: void 0 };
547
+ }
548
+ const rawBody = yield response.text();
549
+ if (!rawBody) {
550
+ return { data: void 0 };
551
+ }
552
+ const contentType = (_b = (_a = response.headers.get("content-type")) == null ? void 0 : _a.toLowerCase()) != null ? _b : "";
553
+ const shouldAttemptJsonParsing = contentType.includes("application/json") || contentType.includes("+json") || rawBody.trimStart().startsWith("{") || rawBody.trimStart().startsWith("[");
554
+ if (!shouldAttemptJsonParsing) {
555
+ return { data: rawBody };
556
+ }
557
+ try {
558
+ const parsed = JSON.parse(rawBody);
559
+ if (typeof parsed === "object" && parsed !== null) {
560
+ return { data: parsed };
561
+ }
562
+ return { data: parsed };
563
+ } catch (e) {
564
+ return { data: rawBody };
565
+ }
566
+ });
567
+ }
528
568
  get(_0) {
529
569
  return __async(this, arguments, function* (url, options = {}) {
530
570
  const { signal, priority = "background" } = options;
@@ -556,16 +596,29 @@ var HttpClient = class {
556
596
  yield this.stores.dedupe.register(hash);
557
597
  }
558
598
  }
599
+ let alreadyRecordedRateLimit = false;
559
600
  if (this.stores.rateLimit) {
560
- yield this.enforceStoreRateLimit(resource, priority, signal);
601
+ alreadyRecordedRateLimit = yield this.enforceStoreRateLimit(
602
+ resource,
603
+ priority,
604
+ signal
605
+ );
606
+ }
607
+ const response = yield fetch(url, { signal });
608
+ this.applyServerRateLimitHints(url, response.headers, response.status);
609
+ const parsedBody = yield this.parseResponseBody(response);
610
+ if (!response.ok) {
611
+ const error = {
612
+ message: `Request failed with status ${response.status}`,
613
+ response: {
614
+ status: response.status,
615
+ data: parsedBody.data,
616
+ headers: response.headers
617
+ }
618
+ };
619
+ throw error;
561
620
  }
562
- const response = yield this._http.get(url, { signal });
563
- this.applyServerRateLimitHints(
564
- url,
565
- response.headers,
566
- response.status
567
- );
568
- let data = response.data;
621
+ let data = parsedBody.data;
569
622
  if (this.options.responseTransformer && data) {
570
623
  data = this.options.responseTransformer(data);
571
624
  }
@@ -573,7 +626,7 @@ var HttpClient = class {
573
626
  data = this.options.responseHandler(data);
574
627
  }
575
628
  const result = data;
576
- if (this.stores.rateLimit) {
629
+ if (this.stores.rateLimit && !alreadyRecordedRateLimit) {
577
630
  const rateLimit = this.stores.rateLimit;
578
631
  yield rateLimit.record(resource, priority);
579
632
  }
@@ -585,20 +638,15 @@ var HttpClient = class {
585
638
  }
586
639
  return result;
587
640
  } catch (error) {
588
- const axiosError = error;
589
- if (axiosError.response) {
590
- this.applyServerRateLimitHints(
591
- url,
592
- axiosError.response.headers,
593
- axiosError.response.status
594
- );
595
- }
596
641
  if (this.stores.dedupe) {
597
642
  yield this.stores.dedupe.fail(hash, error);
598
643
  }
599
644
  if (error instanceof Error && error.name === "AbortError") {
600
645
  throw error;
601
646
  }
647
+ if (error instanceof HttpClientError) {
648
+ throw error;
649
+ }
602
650
  throw this.generateClientError(error);
603
651
  }
604
652
  });
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/http-client-error.ts","../src/stores/rate-limit-store.ts","../src/stores/request-hasher.ts","../src/stores/rate-limit-config.ts","../src/stores/adaptive-capacity-calculator.ts","../src/http-client/http-client.ts"],"names":["z","createHash","baseUserCapacity","axios"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base error class for HTTP client errors.\n * Consumers can extend this for domain-specific error handling.\n */\nexport class HttpClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'HttpClientError';\n this.statusCode = statusCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Priority level for API requests\n */\nexport type RequestPriority = 'user' | 'background';\n\n/**\n * Adaptive configuration schema with validation and defaults\n */\nexport const AdaptiveConfigSchema = z\n .object({\n monitoringWindowMs: z\n .number()\n .positive()\n .default(15 * 60 * 1000), // 15 minutes\n highActivityThreshold: z.number().min(0).default(10), // requests per window\n moderateActivityThreshold: z.number().min(0).default(3),\n recalculationIntervalMs: z.number().positive().default(30000), // 30 seconds\n sustainedInactivityThresholdMs: z\n .number()\n .positive()\n .default(30 * 60 * 1000), // 30 minutes\n backgroundPauseOnIncreasingTrend: z.boolean().default(true),\n maxUserScaling: z.number().positive().default(2.0), // don't exceed 2x capacity\n minUserReserved: z.number().min(0).default(5), // requests minimum\n })\n .refine(\n (data) => {\n return data.moderateActivityThreshold < data.highActivityThreshold;\n },\n {\n message:\n 'moderateActivityThreshold must be less than highActivityThreshold',\n },\n );\n\n/**\n * Configuration for adaptive rate limiting\n */\nexport type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;\n\n/**\n * Interface for rate limiting API requests per resource\n */\nexport interface RateLimitStore {\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n */\n record(resource: string): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n }>;\n\n /**\n * Reset rate limits for a resource (useful for testing)\n * @param resource The resource name\n */\n reset(resource: string): Promise<void>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string): Promise<number>;\n}\n\n/**\n * Enhanced interface for adaptive rate limiting stores with priority support\n */\nexport interface AdaptiveRateLimitStore extends RateLimitStore {\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n */\n record(resource: string, priority?: RequestPriority): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n adaptive?: {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n recentUserActivity: number;\n reason: string;\n };\n }>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @param priority The priority level of the request (defaults to 'background')\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string, priority?: RequestPriority): Promise<number>;\n}\n","import { createHash } from 'crypto';\n\n/**\n * Creates a consistent hash for API requests to use as cache/dedupe keys\n * @param endpoint The API endpoint\n * @param params The request parameters\n * @returns A SHA-256 hash of the request\n */\nexport function hashRequest(\n endpoint: string,\n params: Record<string, unknown> = {},\n): string {\n const requestString = JSON.stringify({\n endpoint,\n params: sortObject(params),\n });\n\n return createHash('sha256').update(requestString).digest('hex');\n}\n\n/**\n * Normalises and sorts an object for hashing purposes.\n *\n * The ComicVine API transmits all query parameters as strings. To avoid cache\n * misses caused by treating `10` and `'10'` as different values we normalise\n * primitive types (number and boolean) to their string representation **before**\n * sorting. `undefined` values are intentionally kept as `undefined` so that\n * they are dropped by `JSON.stringify`, maintaining the existing behaviour\n * where an omitted parameter and an `undefined` parameter produce the same\n * hash.\n */\nfunction sortObject(obj: unknown): unknown {\n // Handle primitives first\n if (obj === null) {\n return null;\n }\n\n const objType = typeof obj;\n\n if (objType === 'undefined' || objType === 'string') {\n return obj;\n }\n\n if (objType === 'number' || objType === 'boolean') {\n // Convert to string so that 10 and '10' (or true and 'true') hash equally\n return String(obj);\n }\n\n // Recursively process arrays\n if (Array.isArray(obj)) {\n return obj.map(sortObject);\n }\n\n // For objects – sort keys and recurse\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj as Record<string, unknown>).sort();\n\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n const normalisedValue = sortObject(value);\n\n // Skip keys whose value normalises to undefined so omitted & undefined match\n if (normalisedValue !== undefined) {\n sorted[key] = normalisedValue;\n }\n }\n\n return sorted;\n}\n","/**\n * Configuration for per-resource rate limiting.\n *\n * This interface is shared by all store implementations (e.g. in-memory,\n * SQLite) so that callers can use a single canonical type.\n */\nexport interface RateLimitConfig {\n /** Number of requests allowed per time window */\n limit: number;\n /** Duration of the window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Default rate-limit window: 60 requests per minute.\n *\n * Store implementations can reference this to avoid duplicating magic numbers.\n */\nexport const DEFAULT_RATE_LIMIT: RateLimitConfig = {\n limit: 60,\n windowMs: 60_000,\n};\n","import { z } from 'zod';\nimport { AdaptiveConfigSchema } from './rate-limit-store.js';\n\ninterface ActivityMetrics {\n recentUserRequests: Array<number>;\n recentBackgroundRequests: Array<number>;\n userActivityTrend: 'increasing' | 'stable' | 'decreasing' | 'none';\n}\n\ninterface DynamicCapacityResult {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n reason: string;\n}\n\n/**\n * Calculates dynamic capacity allocation based on real-time user activity patterns\n */\nexport class AdaptiveCapacityCalculator {\n public readonly config: z.infer<typeof AdaptiveConfigSchema>;\n\n constructor(config: Partial<z.input<typeof AdaptiveConfigSchema>> = {}) {\n // Zod handles validation and applies defaults automatically\n this.config = AdaptiveConfigSchema.parse(config);\n }\n\n calculateDynamicCapacity(\n resource: string,\n totalLimit: number,\n activityMetrics: ActivityMetrics,\n ): DynamicCapacityResult {\n const recentUserActivity = this.getRecentActivity(\n activityMetrics.recentUserRequests,\n );\n const activityTrend = this.calculateActivityTrend(\n activityMetrics.recentUserRequests,\n );\n\n // Strategy 1: High Activity - Pause Background\n if (recentUserActivity >= this.config.highActivityThreshold) {\n const userCapacity = Math.min(\n totalLimit * 0.9,\n Math.floor(totalLimit * 0.5 * this.config.maxUserScaling), // 50% base * scaling factor\n );\n\n return {\n userReserved: userCapacity,\n backgroundMax: totalLimit - userCapacity,\n backgroundPaused:\n this.config.backgroundPauseOnIncreasingTrend &&\n activityTrend === 'increasing',\n reason: `High user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - prioritizing users`,\n };\n }\n\n // Strategy 2: Moderate Activity - Balanced Scaling\n if (recentUserActivity >= this.config.moderateActivityThreshold) {\n const userMultiplier = this.getUserMultiplier(\n recentUserActivity,\n activityTrend,\n );\n const baseUserCapacity = Math.floor(totalLimit * 0.4); // 40% base allocation\n const dynamicUserCapacity = Math.min(\n totalLimit * 0.7,\n baseUserCapacity * userMultiplier,\n );\n\n return {\n userReserved: dynamicUserCapacity,\n backgroundMax: totalLimit - dynamicUserCapacity,\n backgroundPaused: false,\n reason: `Moderate user activity - dynamic scaling (${userMultiplier.toFixed(1)}x user capacity)`,\n };\n }\n\n // Strategy 3: Low/No Activity - Background Scale Up\n if (recentUserActivity === 0) {\n // If there have never been any requests at all (fresh start), use default capacity allocation\n if (\n activityMetrics.recentUserRequests.length === 0 &&\n activityMetrics.recentBackgroundRequests.length === 0\n ) {\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for initial state\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit -\n Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: 'Initial state - default capacity allocation',\n };\n }\n\n // If there have never been user requests (only background), use background scale up\n if (activityMetrics.recentUserRequests.length === 0) {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'No user activity yet - background scale up with minimal user buffer',\n };\n }\n\n // There have been user requests before, check for sustained inactivity\n const sustainedInactivity = this.getSustainedInactivityPeriod(\n activityMetrics.recentUserRequests,\n );\n\n if (sustainedInactivity > this.config.sustainedInactivityThresholdMs) {\n return {\n userReserved: 0, // No reservation - background gets everything!\n backgroundMax: totalLimit, // Full capacity available\n backgroundPaused: false,\n reason: `Sustained zero activity (${Math.floor(sustainedInactivity / 60000)}+ min) - full capacity to background`,\n };\n } else {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'Recent zero activity - background scale up with minimal user buffer',\n };\n }\n }\n\n // Strategy 4: Very Low Activity - Gradual Background Scale Up\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for very low activity\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit - Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: `Low user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - background scale up`,\n };\n }\n\n getRecentActivity(requests: Array<number>): number {\n const cutoff = Date.now() - this.config.monitoringWindowMs;\n return requests.filter((timestamp) => timestamp > cutoff).length;\n }\n\n calculateActivityTrend(\n requests: Array<number>,\n ): 'increasing' | 'stable' | 'decreasing' | 'none' {\n const now = Date.now();\n const windowSize = this.config.monitoringWindowMs / 3; // Use 1/3 of monitoring window for trend\n const recent = requests.filter((t) => t > now - windowSize).length;\n const previous = requests.filter(\n (t) => t > now - 2 * windowSize && t <= now - windowSize,\n ).length;\n\n if (recent === 0 && previous === 0) return 'none';\n if (recent > previous * 1.5) return 'increasing';\n if (recent < previous * 0.5) return 'decreasing';\n return 'stable';\n }\n\n private getUserMultiplier(activity: number, trend: string): number {\n let base = Math.min(\n this.config.maxUserScaling,\n 1 + activity / this.config.highActivityThreshold,\n );\n\n // Adjust based on trend\n if (trend === 'increasing') base *= 1.2;\n if (trend === 'decreasing') base *= 0.8;\n\n return Math.max(1.0, base);\n }\n\n private getSustainedInactivityPeriod(requests: Array<number>): number {\n if (requests.length === 0) {\n // This should not be called when there are no requests (handled above)\n return 0;\n }\n\n const lastRequest = Math.max(...requests);\n return Date.now() - lastRequest;\n }\n}\n\n// Export types for use in other modules\nexport type { ActivityMetrics, DynamicCapacityResult };\n","import axios, { AxiosError } from 'axios';\nimport { HttpClientError } from '../errors/http-client-error.js';\nimport {\n CacheStore,\n DedupeStore,\n RateLimitStore,\n AdaptiveRateLimitStore,\n RequestPriority,\n hashRequest,\n} from '../stores/index.js';\nimport { HttpClientContract } from '../types/index.js';\n\nconst DEFAULT_RATE_LIMIT_HEADER_NAMES = {\n retryAfter: ['retry-after'],\n limit: ['ratelimit-limit', 'x-ratelimit-limit', 'rate-limit-limit'],\n remaining: [\n 'ratelimit-remaining',\n 'x-ratelimit-remaining',\n 'rate-limit-remaining',\n ],\n reset: ['ratelimit-reset', 'x-ratelimit-reset', 'rate-limit-reset'],\n combined: ['ratelimit'],\n} as const;\n\n/**\n * Wait for a specified period while supporting cancellation via AbortSignal.\n *\n * If the signal is aborted before the timeout completes the promise rejects\n * with an `Error` whose name is set to `AbortError`, mimicking DOMException in\n * browser environments without depending on it. This allows callers to use a\n * single `AbortController` for both the rate-limit wait *and* the subsequent\n * HTTP request.\n */\nfunction wait(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (signal) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timer);\n const err = new Error('Aborted');\n err.name = 'AbortError';\n reject(err);\n }\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n });\n}\n\nexport interface HttpClientStores {\n cache?: CacheStore;\n dedupe?: DedupeStore;\n rateLimit?: RateLimitStore | AdaptiveRateLimitStore;\n}\n\nexport interface HttpClientOptions {\n /**\n * Default cache TTL in seconds\n */\n defaultCacheTTL?: number;\n /**\n * Whether to throw errors on rate limit violations\n */\n throwOnRateLimit?: boolean;\n /**\n * Maximum time to wait for rate limit in milliseconds\n */\n maxWaitTime?: number;\n /**\n * Optional response transformer applied to the raw response data.\n * Use this for converting snake_case to camelCase, etc.\n */\n responseTransformer?: (data: unknown) => unknown;\n /**\n * Optional error handler to convert errors into domain-specific error types.\n * If not provided, a generic HttpClientError is thrown.\n */\n errorHandler?: (error: unknown) => Error;\n /**\n * Optional response validator/handler called after transformation.\n * Use this to inspect the response and throw domain-specific errors\n * based on response content (e.g., API-level error codes).\n */\n responseHandler?: (data: unknown) => unknown;\n /**\n * Configure rate-limit response header names for standards and custom APIs.\n */\n rateLimitHeaders?: {\n retryAfter?: Array<string>;\n limit?: Array<string>;\n remaining?: Array<string>;\n reset?: Array<string>;\n combined?: Array<string>;\n };\n}\n\ninterface RateLimitHeaderConfig {\n retryAfter: Array<string>;\n limit: Array<string>;\n remaining: Array<string>;\n reset: Array<string>;\n combined: Array<string>;\n}\n\nexport class HttpClient implements HttpClientContract {\n private _http;\n private stores: HttpClientStores;\n private serverCooldowns = new Map<string, number>();\n private options: Required<\n Pick<\n HttpClientOptions,\n 'defaultCacheTTL' | 'throwOnRateLimit' | 'maxWaitTime'\n >\n > &\n Pick<\n HttpClientOptions,\n 'responseTransformer' | 'errorHandler' | 'responseHandler'\n > & {\n rateLimitHeaders: RateLimitHeaderConfig;\n };\n\n constructor(stores: HttpClientStores = {}, options: HttpClientOptions = {}) {\n this._http = axios.create();\n this.stores = stores;\n this.options = {\n defaultCacheTTL: options.defaultCacheTTL ?? 3600,\n throwOnRateLimit: options.throwOnRateLimit ?? true,\n maxWaitTime: options.maxWaitTime ?? 60000,\n responseTransformer: options.responseTransformer,\n errorHandler: options.errorHandler,\n responseHandler: options.responseHandler,\n rateLimitHeaders: this.normalizeRateLimitHeaders(\n options.rateLimitHeaders,\n ),\n };\n }\n\n private normalizeRateLimitHeaders(\n customHeaders?: HttpClientOptions['rateLimitHeaders'],\n ): RateLimitHeaderConfig {\n return {\n retryAfter: this.normalizeHeaderNames(\n customHeaders?.retryAfter,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.retryAfter,\n ),\n limit: this.normalizeHeaderNames(\n customHeaders?.limit,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.limit,\n ),\n remaining: this.normalizeHeaderNames(\n customHeaders?.remaining,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.remaining,\n ),\n reset: this.normalizeHeaderNames(\n customHeaders?.reset,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.reset,\n ),\n combined: this.normalizeHeaderNames(\n customHeaders?.combined,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.combined,\n ),\n };\n }\n\n private normalizeHeaderNames(\n providedNames: Array<string> | undefined,\n defaultNames: ReadonlyArray<string>,\n ): Array<string> {\n if (!providedNames || providedNames.length === 0) {\n return [...defaultNames];\n }\n\n const customNames = providedNames\n .map((name) => name.trim().toLowerCase())\n .filter(Boolean);\n\n if (customNames.length === 0) {\n return [...defaultNames];\n }\n\n return [...new Set([...customNames, ...defaultNames])];\n }\n\n /**\n * Infer the resource name from the endpoint URL\n * @param url The full URL or endpoint path\n * @returns The resource name for rate limiting\n */\n private inferResource(url: string): string {\n try {\n const urlObj = new URL(url);\n // Use the first meaningful path segment as the resource name\n const segments = urlObj.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] || 'unknown';\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Extract endpoint and params from URL for request hashing\n * @param url The full URL\n * @returns Object with endpoint and params for hashing\n */\n private parseUrlForHashing(url: string): {\n endpoint: string;\n params: Record<string, unknown>;\n } {\n const urlObj = new URL(url);\n const endpoint = `${urlObj.origin}${urlObj.pathname}`;\n const params: Record<string, unknown> = {};\n\n urlObj.searchParams.forEach((value, key) => {\n const existing = params[key];\n\n // Keep repeated query keys as arrays so semantically distinct URLs like\n // `?tag=a&tag=b` and `?tag=b` do not hash to the same cache/dedupe key.\n if (existing === undefined) {\n params[key] = value;\n return;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n return;\n }\n\n params[key] = [existing, value];\n });\n\n return { endpoint, params };\n }\n\n private getOriginScope(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return 'unknown';\n }\n }\n\n private getHeaderValue(\n headers: Record<string, unknown> | undefined,\n names: Array<string>,\n ): string | undefined {\n if (!headers) {\n return undefined;\n }\n\n for (const rawName of names) {\n const name = rawName.toLowerCase();\n const value = headers[name] ?? headers[rawName];\n\n if (typeof value === 'string') {\n return value;\n }\n\n if (Array.isArray(value) && value.length > 0) {\n const first = value.find((entry) => typeof entry === 'string');\n if (typeof first === 'string') {\n return first;\n }\n }\n }\n\n return undefined;\n }\n\n private parseIntegerHeader(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value.trim(), 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n\n return parsed;\n }\n\n private parseRetryAfterMs(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = Number.parseInt(value.trim(), 10);\n if (Number.isFinite(numeric) && numeric >= 0) {\n return numeric * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (!Number.isFinite(dateMs)) {\n return undefined;\n }\n\n return Math.max(0, dateMs - Date.now());\n }\n\n private parseResetMs(value: string | undefined): number | undefined {\n const parsed = this.parseIntegerHeader(value);\n if (parsed === undefined) {\n return undefined;\n }\n\n if (parsed === 0) {\n return 0;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n\n if (parsed > nowSeconds + 1) {\n return Math.max(0, (parsed - nowSeconds) * 1000);\n }\n\n return parsed * 1000;\n }\n\n private parseCombinedRateLimitHeader(value: string | undefined): {\n remaining?: number;\n resetMs?: number;\n } {\n if (!value) {\n return {};\n }\n\n const remainingMatch = value.match(/(?:^|[;,])\\s*r\\s*=\\s*(\\d+)/i);\n const resetMatch = value.match(/(?:^|[;,])\\s*t\\s*=\\s*(\\d+)/i);\n\n return {\n remaining: remainingMatch\n ? this.parseIntegerHeader(remainingMatch[1])\n : undefined,\n resetMs: resetMatch ? this.parseResetMs(resetMatch[1]) : undefined,\n };\n }\n\n private applyServerRateLimitHints(\n url: string,\n headers: Record<string, unknown> | undefined,\n statusCode?: number,\n ): void {\n if (!headers) {\n return;\n }\n\n const config = this.options.rateLimitHeaders;\n const retryAfterRaw = this.getHeaderValue(headers, config.retryAfter);\n const resetRaw = this.getHeaderValue(headers, config.reset);\n const remainingRaw = this.getHeaderValue(headers, config.remaining);\n const combinedRaw = this.getHeaderValue(headers, config.combined);\n\n const retryAfterMs = this.parseRetryAfterMs(retryAfterRaw);\n const resetMs = this.parseResetMs(resetRaw);\n const remaining = this.parseIntegerHeader(remainingRaw);\n const combined = this.parseCombinedRateLimitHeader(combinedRaw);\n\n const effectiveRemaining = remaining ?? combined.remaining;\n const effectiveResetMs = resetMs ?? combined.resetMs;\n const hasRateLimitErrorStatus = statusCode === 429 || statusCode === 503;\n\n let waitMs: number | undefined;\n\n if (retryAfterMs !== undefined) {\n waitMs = retryAfterMs;\n } else if (\n effectiveResetMs !== undefined &&\n (hasRateLimitErrorStatus ||\n (effectiveRemaining !== undefined && effectiveRemaining <= 0))\n ) {\n waitMs = effectiveResetMs;\n }\n\n if (waitMs === undefined || waitMs <= 0) {\n return;\n }\n\n const scope = this.getOriginScope(url);\n this.serverCooldowns.set(scope, Date.now() + waitMs);\n }\n\n private async enforceServerCooldown(\n url: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const scope = this.getOriginScope(url);\n const startedAt = Date.now();\n\n // Re-check cooldown after each sleep so we never proceed while a server\n // cooldown is still active. This avoids bypassing limits when cooldown\n // duration is longer than maxWaitTime.\n while (true) {\n const cooldownUntil = this.serverCooldowns.get(scope);\n if (!cooldownUntil) {\n return;\n }\n\n const waitMs = cooldownUntil - Date.now();\n if (waitMs <= 0) {\n this.serverCooldowns.delete(scope);\n return;\n }\n\n if (this.options.throwOnRateLimit) {\n throw new Error(\n `Rate limit exceeded for origin '${scope}'. Wait ${waitMs}ms before retrying.`,\n );\n }\n\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for origin '${scope}'.`,\n );\n }\n\n await wait(Math.min(waitMs, remainingWaitBudgetMs), signal);\n }\n }\n\n private async enforceStoreRateLimit(\n resource: string,\n priority: RequestPriority,\n signal?: AbortSignal,\n ): Promise<void> {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n const startedAt = Date.now();\n\n if (this.options.throwOnRateLimit) {\n const canProceed = await rateLimit.canProceed(resource, priority);\n if (!canProceed) {\n const waitTime = await rateLimit.getWaitTime(resource, priority);\n throw new Error(\n `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`,\n );\n }\n return;\n }\n\n // Keep polling + waiting until the store explicitly allows the request or\n // we exhaust maxWaitTime. A single one-off sleep can otherwise let a request\n // through while still over limit.\n while (!(await rateLimit.canProceed(resource, priority))) {\n const suggestedWaitMs = await rateLimit.getWaitTime(resource, priority);\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for resource '${resource}'.`,\n );\n }\n\n // If a store reports \"blocked\" but no wait time, use a tiny backoff to\n // avoid a tight CPU loop while still converging quickly.\n const waitTime =\n suggestedWaitMs > 0\n ? Math.min(suggestedWaitMs, remainingWaitBudgetMs)\n : Math.min(25, remainingWaitBudgetMs);\n\n await wait(waitTime, signal);\n }\n }\n\n private generateClientError(err: unknown): Error {\n // If a custom error handler is provided, use it\n if (this.options.errorHandler) {\n return this.options.errorHandler(err);\n }\n\n if (err instanceof HttpClientError) {\n return err;\n }\n\n const error = err as AxiosError<{ message?: string }>;\n const statusCode = error.response?.status;\n const errorMessage = error.response?.data?.message;\n const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ''}`;\n\n return new HttpClientError(message, statusCode);\n }\n\n async get<Result>(\n url: string,\n options: { signal?: AbortSignal; priority?: RequestPriority } = {},\n ): Promise<Result> {\n const { signal, priority = 'background' } = options;\n const { endpoint, params } = this.parseUrlForHashing(url);\n const hash = hashRequest(endpoint, params);\n const resource = this.inferResource(url);\n\n try {\n await this.enforceServerCooldown(url, signal);\n\n // 1. Cache - check for cached response\n if (this.stores.cache) {\n const cachedResult = await this.stores.cache.get(hash);\n if (cachedResult !== undefined) {\n return cachedResult as Result;\n }\n }\n\n // 2. Deduplication - check for in-progress request\n if (this.stores.dedupe) {\n const existingResult = await this.stores.dedupe.waitFor(hash);\n if (existingResult !== undefined) {\n return existingResult as Result;\n }\n\n if (this.stores.dedupe.registerOrJoin) {\n const registration = await this.stores.dedupe.registerOrJoin(hash);\n\n if (!registration.isOwner) {\n const joinedResult = await this.stores.dedupe.waitFor(hash);\n if (joinedResult !== undefined) {\n return joinedResult as Result;\n }\n }\n } else {\n await this.stores.dedupe.register(hash);\n }\n }\n\n // 3. Rate limiting - check if request can proceed\n if (this.stores.rateLimit) {\n await this.enforceStoreRateLimit(resource, priority, signal);\n }\n\n // 4. Execute the actual HTTP request\n const response = await this._http.get(url, { signal });\n this.applyServerRateLimitHints(\n url,\n response.headers as Record<string, unknown>,\n response.status,\n );\n\n // 5. Apply response transformer if provided\n let data = response.data;\n if (this.options.responseTransformer && data) {\n data = this.options.responseTransformer(data);\n }\n\n // 6. Apply response handler if provided (for domain-specific validation)\n if (this.options.responseHandler) {\n data = this.options.responseHandler(data);\n }\n\n const result = data as Result;\n\n // 7. Record the request for rate limiting\n if (this.stores.rateLimit) {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n await rateLimit.record(resource, priority);\n }\n\n // 8. Cache the result\n if (this.stores.cache) {\n await this.stores.cache.set(hash, result, this.options.defaultCacheTTL);\n }\n\n // 9. Mark deduplication as complete\n if (this.stores.dedupe) {\n await this.stores.dedupe.complete(hash, result);\n }\n\n return result;\n } catch (error) {\n const axiosError = error as AxiosError;\n if (axiosError.response) {\n this.applyServerRateLimitHints(\n url,\n axiosError.response.headers as Record<string, unknown>,\n axiosError.response.status,\n );\n }\n\n // Mark deduplication as failed\n if (this.stores.dedupe) {\n await this.stores.dedupe.fail(hash, error as Error);\n }\n\n // Allow callers to detect aborts distinctly – do not wrap AbortError.\n if (error instanceof Error && error.name === 'AbortError') {\n throw error;\n }\n\n throw this.generateClientError(error);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors/http-client-error.ts","../src/stores/rate-limit-store.ts","../src/stores/request-hasher.ts","../src/stores/rate-limit-config.ts","../src/stores/adaptive-capacity-calculator.ts","../src/http-client/http-client.ts"],"names":["z","createHash","baseUserCapacity"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base error class for HTTP client errors.\n * Consumers can extend this for domain-specific error handling.\n */\nexport class HttpClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'HttpClientError';\n this.statusCode = statusCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Priority level for API requests\n */\nexport type RequestPriority = 'user' | 'background';\n\n/**\n * Adaptive configuration schema with validation and defaults\n */\nexport const AdaptiveConfigSchema = z\n .object({\n monitoringWindowMs: z\n .number()\n .positive()\n .default(15 * 60 * 1000), // 15 minutes\n highActivityThreshold: z.number().min(0).default(10), // requests per window\n moderateActivityThreshold: z.number().min(0).default(3),\n recalculationIntervalMs: z.number().positive().default(30000), // 30 seconds\n sustainedInactivityThresholdMs: z\n .number()\n .positive()\n .default(30 * 60 * 1000), // 30 minutes\n backgroundPauseOnIncreasingTrend: z.boolean().default(true),\n maxUserScaling: z.number().positive().default(2.0), // don't exceed 2x capacity\n minUserReserved: z.number().min(0).default(5), // requests minimum\n })\n .refine(\n (data) => {\n return data.moderateActivityThreshold < data.highActivityThreshold;\n },\n {\n message:\n 'moderateActivityThreshold must be less than highActivityThreshold',\n },\n );\n\n/**\n * Configuration for adaptive rate limiting\n */\nexport type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;\n\n/**\n * Interface for rate limiting API requests per resource\n */\nexport interface RateLimitStore {\n /**\n * Atomically acquire capacity for a request if the implementation supports it.\n * When present and returning true, callers should treat the request as already\n * recorded for rate-limit accounting.\n */\n acquire?(resource: string): Promise<boolean>;\n\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n */\n record(resource: string): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n }>;\n\n /**\n * Reset rate limits for a resource (useful for testing)\n * @param resource The resource name\n */\n reset(resource: string): Promise<void>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string): Promise<number>;\n}\n\n/**\n * Enhanced interface for adaptive rate limiting stores with priority support\n */\nexport interface AdaptiveRateLimitStore extends RateLimitStore {\n /**\n * Atomically acquire capacity for a request if the implementation supports it.\n */\n acquire?(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n */\n record(resource: string, priority?: RequestPriority): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n adaptive?: {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n recentUserActivity: number;\n reason: string;\n };\n }>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @param priority The priority level of the request (defaults to 'background')\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string, priority?: RequestPriority): Promise<number>;\n}\n","import { createHash } from 'crypto';\n\n/**\n * Creates a consistent hash for API requests to use as cache/dedupe keys\n * @param endpoint The API endpoint\n * @param params The request parameters\n * @returns A SHA-256 hash of the request\n */\nexport function hashRequest(\n endpoint: string,\n params: Record<string, unknown> = {},\n): string {\n const requestString = JSON.stringify({\n endpoint,\n params: sortObject(params),\n });\n\n return createHash('sha256').update(requestString).digest('hex');\n}\n\n/**\n * Normalises and sorts an object for hashing purposes.\n *\n * The ComicVine API transmits all query parameters as strings. To avoid cache\n * misses caused by treating `10` and `'10'` as different values we normalise\n * primitive types (number and boolean) to their string representation **before**\n * sorting. `undefined` values are intentionally kept as `undefined` so that\n * they are dropped by `JSON.stringify`, maintaining the existing behaviour\n * where an omitted parameter and an `undefined` parameter produce the same\n * hash.\n */\nfunction sortObject(obj: unknown): unknown {\n // Handle primitives first\n if (obj === null) {\n return null;\n }\n\n const objType = typeof obj;\n\n if (objType === 'undefined' || objType === 'string') {\n return obj;\n }\n\n if (objType === 'number' || objType === 'boolean') {\n // Convert to string so that 10 and '10' (or true and 'true') hash equally\n return String(obj);\n }\n\n // Recursively process arrays\n if (Array.isArray(obj)) {\n return obj.map(sortObject);\n }\n\n // For objects – sort keys and recurse\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj as Record<string, unknown>).sort();\n\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n const normalisedValue = sortObject(value);\n\n // Skip keys whose value normalises to undefined so omitted & undefined match\n if (normalisedValue !== undefined) {\n sorted[key] = normalisedValue;\n }\n }\n\n return sorted;\n}\n","/**\n * Configuration for per-resource rate limiting.\n *\n * This interface is shared by all store implementations (e.g. in-memory,\n * SQLite) so that callers can use a single canonical type.\n */\nexport interface RateLimitConfig {\n /** Number of requests allowed per time window */\n limit: number;\n /** Duration of the window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Default rate-limit window: 60 requests per minute.\n *\n * Store implementations can reference this to avoid duplicating magic numbers.\n */\nexport const DEFAULT_RATE_LIMIT: RateLimitConfig = {\n limit: 60,\n windowMs: 60_000,\n};\n","import { z } from 'zod';\nimport { AdaptiveConfigSchema } from './rate-limit-store.js';\n\ninterface ActivityMetrics {\n recentUserRequests: Array<number>;\n recentBackgroundRequests: Array<number>;\n userActivityTrend: 'increasing' | 'stable' | 'decreasing' | 'none';\n}\n\ninterface DynamicCapacityResult {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n reason: string;\n}\n\n/**\n * Calculates dynamic capacity allocation based on real-time user activity patterns\n */\nexport class AdaptiveCapacityCalculator {\n public readonly config: z.infer<typeof AdaptiveConfigSchema>;\n\n constructor(config: Partial<z.input<typeof AdaptiveConfigSchema>> = {}) {\n // Zod handles validation and applies defaults automatically\n this.config = AdaptiveConfigSchema.parse(config);\n }\n\n calculateDynamicCapacity(\n resource: string,\n totalLimit: number,\n activityMetrics: ActivityMetrics,\n ): DynamicCapacityResult {\n const recentUserActivity = this.getRecentActivity(\n activityMetrics.recentUserRequests,\n );\n const activityTrend = this.calculateActivityTrend(\n activityMetrics.recentUserRequests,\n );\n\n // Strategy 1: High Activity - Pause Background\n if (recentUserActivity >= this.config.highActivityThreshold) {\n const userCapacity = Math.min(\n totalLimit * 0.9,\n Math.floor(totalLimit * 0.5 * this.config.maxUserScaling), // 50% base * scaling factor\n );\n\n return {\n userReserved: userCapacity,\n backgroundMax: totalLimit - userCapacity,\n backgroundPaused:\n this.config.backgroundPauseOnIncreasingTrend &&\n activityTrend === 'increasing',\n reason: `High user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - prioritizing users`,\n };\n }\n\n // Strategy 2: Moderate Activity - Balanced Scaling\n if (recentUserActivity >= this.config.moderateActivityThreshold) {\n const userMultiplier = this.getUserMultiplier(\n recentUserActivity,\n activityTrend,\n );\n const baseUserCapacity = Math.floor(totalLimit * 0.4); // 40% base allocation\n const dynamicUserCapacity = Math.min(\n totalLimit * 0.7,\n baseUserCapacity * userMultiplier,\n );\n\n return {\n userReserved: dynamicUserCapacity,\n backgroundMax: totalLimit - dynamicUserCapacity,\n backgroundPaused: false,\n reason: `Moderate user activity - dynamic scaling (${userMultiplier.toFixed(1)}x user capacity)`,\n };\n }\n\n // Strategy 3: Low/No Activity - Background Scale Up\n if (recentUserActivity === 0) {\n // If there have never been any requests at all (fresh start), use default capacity allocation\n if (\n activityMetrics.recentUserRequests.length === 0 &&\n activityMetrics.recentBackgroundRequests.length === 0\n ) {\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for initial state\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit -\n Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: 'Initial state - default capacity allocation',\n };\n }\n\n // If there have never been user requests (only background), use background scale up\n if (activityMetrics.recentUserRequests.length === 0) {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'No user activity yet - background scale up with minimal user buffer',\n };\n }\n\n // There have been user requests before, check for sustained inactivity\n const sustainedInactivity = this.getSustainedInactivityPeriod(\n activityMetrics.recentUserRequests,\n );\n\n if (sustainedInactivity > this.config.sustainedInactivityThresholdMs) {\n return {\n userReserved: 0, // No reservation - background gets everything!\n backgroundMax: totalLimit, // Full capacity available\n backgroundPaused: false,\n reason: `Sustained zero activity (${Math.floor(sustainedInactivity / 60000)}+ min) - full capacity to background`,\n };\n } else {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'Recent zero activity - background scale up with minimal user buffer',\n };\n }\n }\n\n // Strategy 4: Very Low Activity - Gradual Background Scale Up\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for very low activity\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit - Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: `Low user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - background scale up`,\n };\n }\n\n getRecentActivity(requests: Array<number>): number {\n const cutoff = Date.now() - this.config.monitoringWindowMs;\n return requests.filter((timestamp) => timestamp > cutoff).length;\n }\n\n calculateActivityTrend(\n requests: Array<number>,\n ): 'increasing' | 'stable' | 'decreasing' | 'none' {\n const now = Date.now();\n const windowSize = this.config.monitoringWindowMs / 3; // Use 1/3 of monitoring window for trend\n const recent = requests.filter((t) => t > now - windowSize).length;\n const previous = requests.filter(\n (t) => t > now - 2 * windowSize && t <= now - windowSize,\n ).length;\n\n if (recent === 0 && previous === 0) return 'none';\n if (recent > previous * 1.5) return 'increasing';\n if (recent < previous * 0.5) return 'decreasing';\n return 'stable';\n }\n\n private getUserMultiplier(activity: number, trend: string): number {\n let base = Math.min(\n this.config.maxUserScaling,\n 1 + activity / this.config.highActivityThreshold,\n );\n\n // Adjust based on trend\n if (trend === 'increasing') base *= 1.2;\n if (trend === 'decreasing') base *= 0.8;\n\n return Math.max(1.0, base);\n }\n\n private getSustainedInactivityPeriod(requests: Array<number>): number {\n if (requests.length === 0) {\n // This should not be called when there are no requests (handled above)\n return 0;\n }\n\n const lastRequest = Math.max(...requests);\n return Date.now() - lastRequest;\n }\n}\n\n// Export types for use in other modules\nexport type { ActivityMetrics, DynamicCapacityResult };\n","import { HttpClientError } from '../errors/http-client-error.js';\nimport {\n CacheStore,\n DedupeStore,\n RateLimitStore,\n AdaptiveRateLimitStore,\n RequestPriority,\n hashRequest,\n} from '../stores/index.js';\nimport { HttpClientContract } from '../types/index.js';\n\nconst DEFAULT_RATE_LIMIT_HEADER_NAMES = {\n retryAfter: ['retry-after'],\n limit: ['ratelimit-limit', 'x-ratelimit-limit', 'rate-limit-limit'],\n remaining: [\n 'ratelimit-remaining',\n 'x-ratelimit-remaining',\n 'rate-limit-remaining',\n ],\n reset: ['ratelimit-reset', 'x-ratelimit-reset', 'rate-limit-reset'],\n combined: ['ratelimit'],\n} as const;\n\n/**\n * Wait for a specified period while supporting cancellation via AbortSignal.\n *\n * If the signal is aborted before the timeout completes the promise rejects\n * with an `Error` whose name is set to `AbortError`, mimicking DOMException in\n * browser environments without depending on it. This allows callers to use a\n * single `AbortController` for both the rate-limit wait *and* the subsequent\n * HTTP request.\n */\nfunction wait(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (signal) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timer);\n const err = new Error('Aborted');\n err.name = 'AbortError';\n reject(err);\n }\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n });\n}\n\nexport interface HttpClientStores {\n cache?: CacheStore;\n dedupe?: DedupeStore;\n rateLimit?: RateLimitStore | AdaptiveRateLimitStore;\n}\n\nexport interface HttpClientOptions {\n /**\n * Default cache TTL in seconds\n */\n defaultCacheTTL?: number;\n /**\n * Whether to throw errors on rate limit violations\n */\n throwOnRateLimit?: boolean;\n /**\n * Maximum time to wait for rate limit in milliseconds\n */\n maxWaitTime?: number;\n /**\n * Optional response transformer applied to the raw response data.\n * Use this for converting snake_case to camelCase, etc.\n */\n responseTransformer?: (data: unknown) => unknown;\n /**\n * Optional error handler to convert errors into domain-specific error types.\n * If not provided, a generic HttpClientError is thrown.\n */\n errorHandler?: (error: unknown) => Error;\n /**\n * Optional response validator/handler called after transformation.\n * Use this to inspect the response and throw domain-specific errors\n * based on response content (e.g., API-level error codes).\n */\n responseHandler?: (data: unknown) => unknown;\n /**\n * Configure rate-limit response header names for standards and custom APIs.\n */\n rateLimitHeaders?: {\n retryAfter?: Array<string>;\n limit?: Array<string>;\n remaining?: Array<string>;\n reset?: Array<string>;\n combined?: Array<string>;\n };\n}\n\ninterface RateLimitHeaderConfig {\n retryAfter: Array<string>;\n limit: Array<string>;\n remaining: Array<string>;\n reset: Array<string>;\n combined: Array<string>;\n}\n\ninterface ParsedResponseBody {\n data: unknown;\n}\n\ntype ErrorWithResponse = {\n message: string;\n response: {\n status: number;\n data: unknown;\n headers: Headers;\n };\n};\n\nexport class HttpClient implements HttpClientContract {\n private stores: HttpClientStores;\n private serverCooldowns = new Map<string, number>();\n private options: Required<\n Pick<\n HttpClientOptions,\n 'defaultCacheTTL' | 'throwOnRateLimit' | 'maxWaitTime'\n >\n > &\n Pick<\n HttpClientOptions,\n 'responseTransformer' | 'errorHandler' | 'responseHandler'\n > & {\n rateLimitHeaders: RateLimitHeaderConfig;\n };\n\n constructor(stores: HttpClientStores = {}, options: HttpClientOptions = {}) {\n this.stores = stores;\n this.options = {\n defaultCacheTTL: options.defaultCacheTTL ?? 3600,\n throwOnRateLimit: options.throwOnRateLimit ?? true,\n maxWaitTime: options.maxWaitTime ?? 60000,\n responseTransformer: options.responseTransformer,\n errorHandler: options.errorHandler,\n responseHandler: options.responseHandler,\n rateLimitHeaders: this.normalizeRateLimitHeaders(\n options.rateLimitHeaders,\n ),\n };\n }\n\n private normalizeRateLimitHeaders(\n customHeaders?: HttpClientOptions['rateLimitHeaders'],\n ): RateLimitHeaderConfig {\n return {\n retryAfter: this.normalizeHeaderNames(\n customHeaders?.retryAfter,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.retryAfter,\n ),\n limit: this.normalizeHeaderNames(\n customHeaders?.limit,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.limit,\n ),\n remaining: this.normalizeHeaderNames(\n customHeaders?.remaining,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.remaining,\n ),\n reset: this.normalizeHeaderNames(\n customHeaders?.reset,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.reset,\n ),\n combined: this.normalizeHeaderNames(\n customHeaders?.combined,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.combined,\n ),\n };\n }\n\n private normalizeHeaderNames(\n providedNames: Array<string> | undefined,\n defaultNames: ReadonlyArray<string>,\n ): Array<string> {\n if (!providedNames || providedNames.length === 0) {\n return [...defaultNames];\n }\n\n const customNames = providedNames\n .map((name) => name.trim().toLowerCase())\n .filter(Boolean);\n\n if (customNames.length === 0) {\n return [...defaultNames];\n }\n\n return [...new Set([...customNames, ...defaultNames])];\n }\n\n /**\n * Infer the resource name from the endpoint URL\n * @param url The full URL or endpoint path\n * @returns The resource name for rate limiting\n */\n private inferResource(url: string): string {\n try {\n const urlObj = new URL(url);\n // Use the first meaningful path segment as the resource name\n const segments = urlObj.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] || 'unknown';\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Extract endpoint and params from URL for request hashing\n * @param url The full URL\n * @returns Object with endpoint and params for hashing\n */\n private parseUrlForHashing(url: string): {\n endpoint: string;\n params: Record<string, unknown>;\n } {\n const urlObj = new URL(url);\n const endpoint = `${urlObj.origin}${urlObj.pathname}`;\n const params: Record<string, unknown> = {};\n\n urlObj.searchParams.forEach((value, key) => {\n const existing = params[key];\n\n // Keep repeated query keys as arrays so semantically distinct URLs like\n // `?tag=a&tag=b` and `?tag=b` do not hash to the same cache/dedupe key.\n if (existing === undefined) {\n params[key] = value;\n return;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n return;\n }\n\n params[key] = [existing, value];\n });\n\n return { endpoint, params };\n }\n\n private getOriginScope(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return 'unknown';\n }\n }\n\n private getHeaderValue(\n headers: Headers | Record<string, unknown> | undefined,\n names: Array<string>,\n ): string | undefined {\n if (!headers) {\n return undefined;\n }\n\n if (headers instanceof Headers) {\n for (const rawName of names) {\n const value = headers.get(rawName);\n if (value !== null) {\n return value;\n }\n }\n return undefined;\n }\n\n for (const rawName of names) {\n const name = rawName.toLowerCase();\n const value = headers[name] ?? headers[rawName];\n\n if (typeof value === 'string') {\n return value;\n }\n\n if (Array.isArray(value) && value.length > 0) {\n const first = value.find((entry) => typeof entry === 'string');\n if (typeof first === 'string') {\n return first;\n }\n }\n }\n\n return undefined;\n }\n\n private parseIntegerHeader(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value.trim(), 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n\n return parsed;\n }\n\n private parseRetryAfterMs(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = Number.parseInt(value.trim(), 10);\n if (Number.isFinite(numeric) && numeric >= 0) {\n return numeric * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (!Number.isFinite(dateMs)) {\n return undefined;\n }\n\n return Math.max(0, dateMs - Date.now());\n }\n\n private parseResetMs(value: string | undefined): number | undefined {\n const parsed = this.parseIntegerHeader(value);\n if (parsed === undefined) {\n return undefined;\n }\n\n if (parsed === 0) {\n return 0;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n\n if (parsed > nowSeconds + 1) {\n return Math.max(0, (parsed - nowSeconds) * 1000);\n }\n\n return parsed * 1000;\n }\n\n private parseCombinedRateLimitHeader(value: string | undefined): {\n remaining?: number;\n resetMs?: number;\n } {\n if (!value) {\n return {};\n }\n\n const remainingMatch = value.match(/(?:^|[;,])\\s*r\\s*=\\s*(\\d+)/i);\n const resetMatch = value.match(/(?:^|[;,])\\s*t\\s*=\\s*(\\d+)/i);\n\n return {\n remaining: remainingMatch\n ? this.parseIntegerHeader(remainingMatch[1])\n : undefined,\n resetMs: resetMatch ? this.parseResetMs(resetMatch[1]) : undefined,\n };\n }\n\n private applyServerRateLimitHints(\n url: string,\n headers: Headers | Record<string, unknown> | undefined,\n statusCode?: number,\n ): void {\n if (!headers) {\n return;\n }\n\n const config = this.options.rateLimitHeaders;\n const retryAfterRaw = this.getHeaderValue(headers, config.retryAfter);\n const resetRaw = this.getHeaderValue(headers, config.reset);\n const remainingRaw = this.getHeaderValue(headers, config.remaining);\n const combinedRaw = this.getHeaderValue(headers, config.combined);\n\n const retryAfterMs = this.parseRetryAfterMs(retryAfterRaw);\n const resetMs = this.parseResetMs(resetRaw);\n const remaining = this.parseIntegerHeader(remainingRaw);\n const combined = this.parseCombinedRateLimitHeader(combinedRaw);\n\n const effectiveRemaining = remaining ?? combined.remaining;\n const effectiveResetMs = resetMs ?? combined.resetMs;\n const hasRateLimitErrorStatus = statusCode === 429 || statusCode === 503;\n\n let waitMs: number | undefined;\n\n if (retryAfterMs !== undefined) {\n waitMs = retryAfterMs;\n } else if (\n effectiveResetMs !== undefined &&\n (hasRateLimitErrorStatus ||\n (effectiveRemaining !== undefined && effectiveRemaining <= 0))\n ) {\n waitMs = effectiveResetMs;\n }\n\n if (waitMs === undefined || waitMs <= 0) {\n return;\n }\n\n const scope = this.getOriginScope(url);\n this.serverCooldowns.set(scope, Date.now() + waitMs);\n }\n\n private async enforceServerCooldown(\n url: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const scope = this.getOriginScope(url);\n const startedAt = Date.now();\n\n // Re-check cooldown after each sleep so we never proceed while a server\n // cooldown is still active. This avoids bypassing limits when cooldown\n // duration is longer than maxWaitTime.\n while (true) {\n const cooldownUntil = this.serverCooldowns.get(scope);\n if (!cooldownUntil) {\n return;\n }\n\n const waitMs = cooldownUntil - Date.now();\n if (waitMs <= 0) {\n this.serverCooldowns.delete(scope);\n return;\n }\n\n if (this.options.throwOnRateLimit) {\n throw new Error(\n `Rate limit exceeded for origin '${scope}'. Wait ${waitMs}ms before retrying.`,\n );\n }\n\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for origin '${scope}'.`,\n );\n }\n\n await wait(Math.min(waitMs, remainingWaitBudgetMs), signal);\n }\n }\n\n private async enforceStoreRateLimit(\n resource: string,\n priority: RequestPriority,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n const startedAt = Date.now();\n const hasAtomicAcquire = typeof rateLimit.acquire === 'function';\n\n const canProceedNow = async (): Promise<boolean> => {\n if (hasAtomicAcquire) {\n return rateLimit.acquire!(resource, priority);\n }\n return rateLimit.canProceed(resource, priority);\n };\n\n if (this.options.throwOnRateLimit) {\n const canProceed = await canProceedNow();\n if (!canProceed) {\n const waitTime = await rateLimit.getWaitTime(resource, priority);\n throw new Error(\n `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`,\n );\n }\n return hasAtomicAcquire;\n }\n\n // Keep polling + waiting until the store explicitly allows the request or\n // we exhaust maxWaitTime. A single one-off sleep can otherwise let a request\n // through while still over limit.\n while (!(await canProceedNow())) {\n const suggestedWaitMs = await rateLimit.getWaitTime(resource, priority);\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for resource '${resource}'.`,\n );\n }\n\n // If a store reports \"blocked\" but no wait time, use a tiny backoff to\n // avoid a tight CPU loop while still converging quickly.\n const waitTime =\n suggestedWaitMs > 0\n ? Math.min(suggestedWaitMs, remainingWaitBudgetMs)\n : Math.min(25, remainingWaitBudgetMs);\n\n await wait(waitTime, signal);\n }\n\n return hasAtomicAcquire;\n }\n\n private generateClientError(err: unknown): Error {\n // If a custom error handler is provided, use it\n if (this.options.errorHandler) {\n return this.options.errorHandler(err);\n }\n\n if (err instanceof HttpClientError) {\n return err;\n }\n\n const responseError = err as Partial<ErrorWithResponse>;\n const statusCode =\n typeof responseError.response?.status === 'number'\n ? responseError.response.status\n : undefined;\n\n const responseData = responseError.response?.data;\n const derivedResponseMessage =\n typeof responseData === 'object' && responseData !== null\n ? (responseData as { message?: unknown }).message\n : undefined;\n const responseMessage =\n typeof derivedResponseMessage === 'string'\n ? derivedResponseMessage\n : undefined;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : typeof (err as { message?: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Unknown error';\n const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ''}`;\n\n return new HttpClientError(message, statusCode);\n }\n\n private async parseResponseBody(\n response: Response,\n ): Promise<ParsedResponseBody> {\n if (response.status === 204 || response.status === 205) {\n return { data: undefined };\n }\n\n const rawBody = await response.text();\n if (!rawBody) {\n return { data: undefined };\n }\n\n const contentType =\n response.headers.get('content-type')?.toLowerCase() ?? '';\n const shouldAttemptJsonParsing =\n contentType.includes('application/json') ||\n contentType.includes('+json') ||\n rawBody.trimStart().startsWith('{') ||\n rawBody.trimStart().startsWith('[');\n\n if (!shouldAttemptJsonParsing) {\n return { data: rawBody };\n }\n\n try {\n const parsed = JSON.parse(rawBody) as unknown;\n if (typeof parsed === 'object' && parsed !== null) {\n return { data: parsed };\n }\n\n return { data: parsed };\n } catch {\n return { data: rawBody };\n }\n }\n\n async get<Result>(\n url: string,\n options: { signal?: AbortSignal; priority?: RequestPriority } = {},\n ): Promise<Result> {\n const { signal, priority = 'background' } = options;\n const { endpoint, params } = this.parseUrlForHashing(url);\n const hash = hashRequest(endpoint, params);\n const resource = this.inferResource(url);\n\n try {\n await this.enforceServerCooldown(url, signal);\n\n // 1. Cache - check for cached response\n if (this.stores.cache) {\n const cachedResult = await this.stores.cache.get(hash);\n if (cachedResult !== undefined) {\n return cachedResult as Result;\n }\n }\n\n // 2. Deduplication - check for in-progress request\n if (this.stores.dedupe) {\n const existingResult = await this.stores.dedupe.waitFor(hash);\n if (existingResult !== undefined) {\n return existingResult as Result;\n }\n\n if (this.stores.dedupe.registerOrJoin) {\n const registration = await this.stores.dedupe.registerOrJoin(hash);\n\n if (!registration.isOwner) {\n const joinedResult = await this.stores.dedupe.waitFor(hash);\n if (joinedResult !== undefined) {\n return joinedResult as Result;\n }\n }\n } else {\n await this.stores.dedupe.register(hash);\n }\n }\n\n // 3. Rate limiting - check if request can proceed\n let alreadyRecordedRateLimit = false;\n if (this.stores.rateLimit) {\n alreadyRecordedRateLimit = await this.enforceStoreRateLimit(\n resource,\n priority,\n signal,\n );\n }\n\n // 4. Execute the actual HTTP request\n const response = await fetch(url, { signal });\n this.applyServerRateLimitHints(url, response.headers, response.status);\n\n const parsedBody = await this.parseResponseBody(response);\n\n if (!response.ok) {\n const error: ErrorWithResponse = {\n message: `Request failed with status ${response.status}`,\n response: {\n status: response.status,\n data: parsedBody.data,\n headers: response.headers,\n },\n };\n throw error;\n }\n\n // 5. Apply response transformer if provided\n let data: unknown = parsedBody.data;\n if (this.options.responseTransformer && data) {\n data = this.options.responseTransformer(data);\n }\n\n // 6. Apply response handler if provided (for domain-specific validation)\n if (this.options.responseHandler) {\n data = this.options.responseHandler(data);\n }\n\n const result = data as Result;\n\n // 7. Record the request for rate limiting\n if (this.stores.rateLimit && !alreadyRecordedRateLimit) {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n await rateLimit.record(resource, priority);\n }\n\n // 8. Cache the result\n if (this.stores.cache) {\n await this.stores.cache.set(hash, result, this.options.defaultCacheTTL);\n }\n\n // 9. Mark deduplication as complete\n if (this.stores.dedupe) {\n await this.stores.dedupe.complete(hash, result);\n }\n\n return result;\n } catch (error) {\n // Mark deduplication as failed\n if (this.stores.dedupe) {\n await this.stores.dedupe.fail(hash, error as Error);\n }\n\n // Allow callers to detect aborts distinctly – do not wrap AbortError.\n if (error instanceof Error && error.name === 'AbortError') {\n throw error;\n }\n\n // Already a processed error from the !response.ok branch above\n if (error instanceof HttpClientError) {\n throw error;\n }\n\n throw this.generateClientError(error);\n }\n }\n}\n"]}
package/lib/index.d.cts CHANGED
@@ -140,6 +140,12 @@ type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;
140
140
  * Interface for rate limiting API requests per resource
141
141
  */
142
142
  interface RateLimitStore {
143
+ /**
144
+ * Atomically acquire capacity for a request if the implementation supports it.
145
+ * When present and returning true, callers should treat the request as already
146
+ * recorded for rate-limit accounting.
147
+ */
148
+ acquire?(resource: string): Promise<boolean>;
143
149
  /**
144
150
  * Check if a request to a resource can proceed based on rate limits
145
151
  * @param resource The resource name (e.g., 'issues', 'characters')
@@ -177,6 +183,10 @@ interface RateLimitStore {
177
183
  * Enhanced interface for adaptive rate limiting stores with priority support
178
184
  */
179
185
  interface AdaptiveRateLimitStore extends RateLimitStore {
186
+ /**
187
+ * Atomically acquire capacity for a request if the implementation supports it.
188
+ */
189
+ acquire?(resource: string, priority?: RequestPriority): Promise<boolean>;
180
190
  /**
181
191
  * Check if a request to a resource can proceed based on rate limits
182
192
  * @param resource The resource name (e.g., 'issues', 'characters')
@@ -335,7 +345,6 @@ interface HttpClientOptions {
335
345
  };
336
346
  }
337
347
  declare class HttpClient implements HttpClientContract {
338
- private _http;
339
348
  private stores;
340
349
  private serverCooldowns;
341
350
  private options;
@@ -364,6 +373,7 @@ declare class HttpClient implements HttpClientContract {
364
373
  private enforceServerCooldown;
365
374
  private enforceStoreRateLimit;
366
375
  private generateClientError;
376
+ private parseResponseBody;
367
377
  get<Result>(url: string, options?: {
368
378
  signal?: AbortSignal;
369
379
  priority?: RequestPriority;
package/lib/index.d.ts CHANGED
@@ -140,6 +140,12 @@ type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;
140
140
  * Interface for rate limiting API requests per resource
141
141
  */
142
142
  interface RateLimitStore {
143
+ /**
144
+ * Atomically acquire capacity for a request if the implementation supports it.
145
+ * When present and returning true, callers should treat the request as already
146
+ * recorded for rate-limit accounting.
147
+ */
148
+ acquire?(resource: string): Promise<boolean>;
143
149
  /**
144
150
  * Check if a request to a resource can proceed based on rate limits
145
151
  * @param resource The resource name (e.g., 'issues', 'characters')
@@ -177,6 +183,10 @@ interface RateLimitStore {
177
183
  * Enhanced interface for adaptive rate limiting stores with priority support
178
184
  */
179
185
  interface AdaptiveRateLimitStore extends RateLimitStore {
186
+ /**
187
+ * Atomically acquire capacity for a request if the implementation supports it.
188
+ */
189
+ acquire?(resource: string, priority?: RequestPriority): Promise<boolean>;
180
190
  /**
181
191
  * Check if a request to a resource can proceed based on rate limits
182
192
  * @param resource The resource name (e.g., 'issues', 'characters')
@@ -335,7 +345,6 @@ interface HttpClientOptions {
335
345
  };
336
346
  }
337
347
  declare class HttpClient implements HttpClientContract {
338
- private _http;
339
348
  private stores;
340
349
  private serverCooldowns;
341
350
  private options;
@@ -364,6 +373,7 @@ declare class HttpClient implements HttpClientContract {
364
373
  private enforceServerCooldown;
365
374
  private enforceStoreRateLimit;
366
375
  private generateClientError;
376
+ private parseResponseBody;
367
377
  get<Result>(url: string, options?: {
368
378
  signal?: AbortSignal;
369
379
  priority?: RequestPriority;
package/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import axios from 'axios';
2
1
  import { z } from 'zod';
3
2
  import { createHash } from 'crypto';
4
3
 
@@ -258,7 +257,6 @@ var HttpClient = class {
258
257
  constructor(stores = {}, options = {}) {
259
258
  this.serverCooldowns = /* @__PURE__ */ new Map();
260
259
  var _a, _b, _c;
261
- this._http = axios.create();
262
260
  this.stores = stores;
263
261
  this.options = {
264
262
  defaultCacheTTL: (_a = options.defaultCacheTTL) != null ? _a : 3600,
@@ -355,6 +353,15 @@ var HttpClient = class {
355
353
  if (!headers) {
356
354
  return void 0;
357
355
  }
356
+ if (headers instanceof Headers) {
357
+ for (const rawName of names) {
358
+ const value = headers.get(rawName);
359
+ if (value !== null) {
360
+ return value;
361
+ }
362
+ }
363
+ return void 0;
364
+ }
358
365
  for (const rawName of names) {
359
366
  const name = rawName.toLowerCase();
360
367
  const value = (_a = headers[name]) != null ? _a : headers[rawName];
@@ -481,17 +488,24 @@ var HttpClient = class {
481
488
  return __async(this, null, function* () {
482
489
  const rateLimit = this.stores.rateLimit;
483
490
  const startedAt = Date.now();
491
+ const hasAtomicAcquire = typeof rateLimit.acquire === "function";
492
+ const canProceedNow = () => __async(this, null, function* () {
493
+ if (hasAtomicAcquire) {
494
+ return rateLimit.acquire(resource, priority);
495
+ }
496
+ return rateLimit.canProceed(resource, priority);
497
+ });
484
498
  if (this.options.throwOnRateLimit) {
485
- const canProceed = yield rateLimit.canProceed(resource, priority);
499
+ const canProceed = yield canProceedNow();
486
500
  if (!canProceed) {
487
501
  const waitTime = yield rateLimit.getWaitTime(resource, priority);
488
502
  throw new Error(
489
503
  `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`
490
504
  );
491
505
  }
492
- return;
506
+ return hasAtomicAcquire;
493
507
  }
494
- while (!(yield rateLimit.canProceed(resource, priority))) {
508
+ while (!(yield canProceedNow())) {
495
509
  const suggestedWaitMs = yield rateLimit.getWaitTime(resource, priority);
496
510
  const elapsedMs = Date.now() - startedAt;
497
511
  const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;
@@ -503,22 +517,52 @@ var HttpClient = class {
503
517
  const waitTime = suggestedWaitMs > 0 ? Math.min(suggestedWaitMs, remainingWaitBudgetMs) : Math.min(25, remainingWaitBudgetMs);
504
518
  yield wait(waitTime, signal);
505
519
  }
520
+ return hasAtomicAcquire;
506
521
  });
507
522
  }
508
523
  generateClientError(err) {
509
- var _a, _b, _c;
524
+ var _a, _b;
510
525
  if (this.options.errorHandler) {
511
526
  return this.options.errorHandler(err);
512
527
  }
513
528
  if (err instanceof HttpClientError) {
514
529
  return err;
515
530
  }
516
- const error = err;
517
- const statusCode = (_a = error.response) == null ? void 0 : _a.status;
518
- const errorMessage = (_c = (_b = error.response) == null ? void 0 : _b.data) == null ? void 0 : _c.message;
519
- const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ""}`;
531
+ const responseError = err;
532
+ const statusCode = typeof ((_a = responseError.response) == null ? void 0 : _a.status) === "number" ? responseError.response.status : void 0;
533
+ const responseData = (_b = responseError.response) == null ? void 0 : _b.data;
534
+ const derivedResponseMessage = typeof responseData === "object" && responseData !== null ? responseData.message : void 0;
535
+ const responseMessage = typeof derivedResponseMessage === "string" ? derivedResponseMessage : void 0;
536
+ const errorMessage = err instanceof Error ? err.message : typeof err.message === "string" ? err.message : "Unknown error";
537
+ const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ""}`;
520
538
  return new HttpClientError(message, statusCode);
521
539
  }
540
+ parseResponseBody(response) {
541
+ return __async(this, null, function* () {
542
+ var _a, _b;
543
+ if (response.status === 204 || response.status === 205) {
544
+ return { data: void 0 };
545
+ }
546
+ const rawBody = yield response.text();
547
+ if (!rawBody) {
548
+ return { data: void 0 };
549
+ }
550
+ const contentType = (_b = (_a = response.headers.get("content-type")) == null ? void 0 : _a.toLowerCase()) != null ? _b : "";
551
+ const shouldAttemptJsonParsing = contentType.includes("application/json") || contentType.includes("+json") || rawBody.trimStart().startsWith("{") || rawBody.trimStart().startsWith("[");
552
+ if (!shouldAttemptJsonParsing) {
553
+ return { data: rawBody };
554
+ }
555
+ try {
556
+ const parsed = JSON.parse(rawBody);
557
+ if (typeof parsed === "object" && parsed !== null) {
558
+ return { data: parsed };
559
+ }
560
+ return { data: parsed };
561
+ } catch (e) {
562
+ return { data: rawBody };
563
+ }
564
+ });
565
+ }
522
566
  get(_0) {
523
567
  return __async(this, arguments, function* (url, options = {}) {
524
568
  const { signal, priority = "background" } = options;
@@ -550,16 +594,29 @@ var HttpClient = class {
550
594
  yield this.stores.dedupe.register(hash);
551
595
  }
552
596
  }
597
+ let alreadyRecordedRateLimit = false;
553
598
  if (this.stores.rateLimit) {
554
- yield this.enforceStoreRateLimit(resource, priority, signal);
599
+ alreadyRecordedRateLimit = yield this.enforceStoreRateLimit(
600
+ resource,
601
+ priority,
602
+ signal
603
+ );
555
604
  }
556
- const response = yield this._http.get(url, { signal });
557
- this.applyServerRateLimitHints(
558
- url,
559
- response.headers,
560
- response.status
561
- );
562
- let data = response.data;
605
+ const response = yield fetch(url, { signal });
606
+ this.applyServerRateLimitHints(url, response.headers, response.status);
607
+ const parsedBody = yield this.parseResponseBody(response);
608
+ if (!response.ok) {
609
+ const error = {
610
+ message: `Request failed with status ${response.status}`,
611
+ response: {
612
+ status: response.status,
613
+ data: parsedBody.data,
614
+ headers: response.headers
615
+ }
616
+ };
617
+ throw error;
618
+ }
619
+ let data = parsedBody.data;
563
620
  if (this.options.responseTransformer && data) {
564
621
  data = this.options.responseTransformer(data);
565
622
  }
@@ -567,7 +624,7 @@ var HttpClient = class {
567
624
  data = this.options.responseHandler(data);
568
625
  }
569
626
  const result = data;
570
- if (this.stores.rateLimit) {
627
+ if (this.stores.rateLimit && !alreadyRecordedRateLimit) {
571
628
  const rateLimit = this.stores.rateLimit;
572
629
  yield rateLimit.record(resource, priority);
573
630
  }
@@ -579,20 +636,15 @@ var HttpClient = class {
579
636
  }
580
637
  return result;
581
638
  } catch (error) {
582
- const axiosError = error;
583
- if (axiosError.response) {
584
- this.applyServerRateLimitHints(
585
- url,
586
- axiosError.response.headers,
587
- axiosError.response.status
588
- );
589
- }
590
639
  if (this.stores.dedupe) {
591
640
  yield this.stores.dedupe.fail(hash, error);
592
641
  }
593
642
  if (error instanceof Error && error.name === "AbortError") {
594
643
  throw error;
595
644
  }
645
+ if (error instanceof HttpClientError) {
646
+ throw error;
647
+ }
596
648
  throw this.generateClientError(error);
597
649
  }
598
650
  });
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/http-client-error.ts","../src/stores/rate-limit-store.ts","../src/stores/request-hasher.ts","../src/stores/rate-limit-config.ts","../src/stores/adaptive-capacity-calculator.ts","../src/http-client/http-client.ts"],"names":["baseUserCapacity"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for HTTP client errors.\n * Consumers can extend this for domain-specific error handling.\n */\nexport class HttpClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'HttpClientError';\n this.statusCode = statusCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Priority level for API requests\n */\nexport type RequestPriority = 'user' | 'background';\n\n/**\n * Adaptive configuration schema with validation and defaults\n */\nexport const AdaptiveConfigSchema = z\n .object({\n monitoringWindowMs: z\n .number()\n .positive()\n .default(15 * 60 * 1000), // 15 minutes\n highActivityThreshold: z.number().min(0).default(10), // requests per window\n moderateActivityThreshold: z.number().min(0).default(3),\n recalculationIntervalMs: z.number().positive().default(30000), // 30 seconds\n sustainedInactivityThresholdMs: z\n .number()\n .positive()\n .default(30 * 60 * 1000), // 30 minutes\n backgroundPauseOnIncreasingTrend: z.boolean().default(true),\n maxUserScaling: z.number().positive().default(2.0), // don't exceed 2x capacity\n minUserReserved: z.number().min(0).default(5), // requests minimum\n })\n .refine(\n (data) => {\n return data.moderateActivityThreshold < data.highActivityThreshold;\n },\n {\n message:\n 'moderateActivityThreshold must be less than highActivityThreshold',\n },\n );\n\n/**\n * Configuration for adaptive rate limiting\n */\nexport type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;\n\n/**\n * Interface for rate limiting API requests per resource\n */\nexport interface RateLimitStore {\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n */\n record(resource: string): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n }>;\n\n /**\n * Reset rate limits for a resource (useful for testing)\n * @param resource The resource name\n */\n reset(resource: string): Promise<void>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string): Promise<number>;\n}\n\n/**\n * Enhanced interface for adaptive rate limiting stores with priority support\n */\nexport interface AdaptiveRateLimitStore extends RateLimitStore {\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n */\n record(resource: string, priority?: RequestPriority): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n adaptive?: {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n recentUserActivity: number;\n reason: string;\n };\n }>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @param priority The priority level of the request (defaults to 'background')\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string, priority?: RequestPriority): Promise<number>;\n}\n","import { createHash } from 'crypto';\n\n/**\n * Creates a consistent hash for API requests to use as cache/dedupe keys\n * @param endpoint The API endpoint\n * @param params The request parameters\n * @returns A SHA-256 hash of the request\n */\nexport function hashRequest(\n endpoint: string,\n params: Record<string, unknown> = {},\n): string {\n const requestString = JSON.stringify({\n endpoint,\n params: sortObject(params),\n });\n\n return createHash('sha256').update(requestString).digest('hex');\n}\n\n/**\n * Normalises and sorts an object for hashing purposes.\n *\n * The ComicVine API transmits all query parameters as strings. To avoid cache\n * misses caused by treating `10` and `'10'` as different values we normalise\n * primitive types (number and boolean) to their string representation **before**\n * sorting. `undefined` values are intentionally kept as `undefined` so that\n * they are dropped by `JSON.stringify`, maintaining the existing behaviour\n * where an omitted parameter and an `undefined` parameter produce the same\n * hash.\n */\nfunction sortObject(obj: unknown): unknown {\n // Handle primitives first\n if (obj === null) {\n return null;\n }\n\n const objType = typeof obj;\n\n if (objType === 'undefined' || objType === 'string') {\n return obj;\n }\n\n if (objType === 'number' || objType === 'boolean') {\n // Convert to string so that 10 and '10' (or true and 'true') hash equally\n return String(obj);\n }\n\n // Recursively process arrays\n if (Array.isArray(obj)) {\n return obj.map(sortObject);\n }\n\n // For objects – sort keys and recurse\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj as Record<string, unknown>).sort();\n\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n const normalisedValue = sortObject(value);\n\n // Skip keys whose value normalises to undefined so omitted & undefined match\n if (normalisedValue !== undefined) {\n sorted[key] = normalisedValue;\n }\n }\n\n return sorted;\n}\n","/**\n * Configuration for per-resource rate limiting.\n *\n * This interface is shared by all store implementations (e.g. in-memory,\n * SQLite) so that callers can use a single canonical type.\n */\nexport interface RateLimitConfig {\n /** Number of requests allowed per time window */\n limit: number;\n /** Duration of the window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Default rate-limit window: 60 requests per minute.\n *\n * Store implementations can reference this to avoid duplicating magic numbers.\n */\nexport const DEFAULT_RATE_LIMIT: RateLimitConfig = {\n limit: 60,\n windowMs: 60_000,\n};\n","import { z } from 'zod';\nimport { AdaptiveConfigSchema } from './rate-limit-store.js';\n\ninterface ActivityMetrics {\n recentUserRequests: Array<number>;\n recentBackgroundRequests: Array<number>;\n userActivityTrend: 'increasing' | 'stable' | 'decreasing' | 'none';\n}\n\ninterface DynamicCapacityResult {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n reason: string;\n}\n\n/**\n * Calculates dynamic capacity allocation based on real-time user activity patterns\n */\nexport class AdaptiveCapacityCalculator {\n public readonly config: z.infer<typeof AdaptiveConfigSchema>;\n\n constructor(config: Partial<z.input<typeof AdaptiveConfigSchema>> = {}) {\n // Zod handles validation and applies defaults automatically\n this.config = AdaptiveConfigSchema.parse(config);\n }\n\n calculateDynamicCapacity(\n resource: string,\n totalLimit: number,\n activityMetrics: ActivityMetrics,\n ): DynamicCapacityResult {\n const recentUserActivity = this.getRecentActivity(\n activityMetrics.recentUserRequests,\n );\n const activityTrend = this.calculateActivityTrend(\n activityMetrics.recentUserRequests,\n );\n\n // Strategy 1: High Activity - Pause Background\n if (recentUserActivity >= this.config.highActivityThreshold) {\n const userCapacity = Math.min(\n totalLimit * 0.9,\n Math.floor(totalLimit * 0.5 * this.config.maxUserScaling), // 50% base * scaling factor\n );\n\n return {\n userReserved: userCapacity,\n backgroundMax: totalLimit - userCapacity,\n backgroundPaused:\n this.config.backgroundPauseOnIncreasingTrend &&\n activityTrend === 'increasing',\n reason: `High user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - prioritizing users`,\n };\n }\n\n // Strategy 2: Moderate Activity - Balanced Scaling\n if (recentUserActivity >= this.config.moderateActivityThreshold) {\n const userMultiplier = this.getUserMultiplier(\n recentUserActivity,\n activityTrend,\n );\n const baseUserCapacity = Math.floor(totalLimit * 0.4); // 40% base allocation\n const dynamicUserCapacity = Math.min(\n totalLimit * 0.7,\n baseUserCapacity * userMultiplier,\n );\n\n return {\n userReserved: dynamicUserCapacity,\n backgroundMax: totalLimit - dynamicUserCapacity,\n backgroundPaused: false,\n reason: `Moderate user activity - dynamic scaling (${userMultiplier.toFixed(1)}x user capacity)`,\n };\n }\n\n // Strategy 3: Low/No Activity - Background Scale Up\n if (recentUserActivity === 0) {\n // If there have never been any requests at all (fresh start), use default capacity allocation\n if (\n activityMetrics.recentUserRequests.length === 0 &&\n activityMetrics.recentBackgroundRequests.length === 0\n ) {\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for initial state\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit -\n Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: 'Initial state - default capacity allocation',\n };\n }\n\n // If there have never been user requests (only background), use background scale up\n if (activityMetrics.recentUserRequests.length === 0) {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'No user activity yet - background scale up with minimal user buffer',\n };\n }\n\n // There have been user requests before, check for sustained inactivity\n const sustainedInactivity = this.getSustainedInactivityPeriod(\n activityMetrics.recentUserRequests,\n );\n\n if (sustainedInactivity > this.config.sustainedInactivityThresholdMs) {\n return {\n userReserved: 0, // No reservation - background gets everything!\n backgroundMax: totalLimit, // Full capacity available\n backgroundPaused: false,\n reason: `Sustained zero activity (${Math.floor(sustainedInactivity / 60000)}+ min) - full capacity to background`,\n };\n } else {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'Recent zero activity - background scale up with minimal user buffer',\n };\n }\n }\n\n // Strategy 4: Very Low Activity - Gradual Background Scale Up\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for very low activity\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit - Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: `Low user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - background scale up`,\n };\n }\n\n getRecentActivity(requests: Array<number>): number {\n const cutoff = Date.now() - this.config.monitoringWindowMs;\n return requests.filter((timestamp) => timestamp > cutoff).length;\n }\n\n calculateActivityTrend(\n requests: Array<number>,\n ): 'increasing' | 'stable' | 'decreasing' | 'none' {\n const now = Date.now();\n const windowSize = this.config.monitoringWindowMs / 3; // Use 1/3 of monitoring window for trend\n const recent = requests.filter((t) => t > now - windowSize).length;\n const previous = requests.filter(\n (t) => t > now - 2 * windowSize && t <= now - windowSize,\n ).length;\n\n if (recent === 0 && previous === 0) return 'none';\n if (recent > previous * 1.5) return 'increasing';\n if (recent < previous * 0.5) return 'decreasing';\n return 'stable';\n }\n\n private getUserMultiplier(activity: number, trend: string): number {\n let base = Math.min(\n this.config.maxUserScaling,\n 1 + activity / this.config.highActivityThreshold,\n );\n\n // Adjust based on trend\n if (trend === 'increasing') base *= 1.2;\n if (trend === 'decreasing') base *= 0.8;\n\n return Math.max(1.0, base);\n }\n\n private getSustainedInactivityPeriod(requests: Array<number>): number {\n if (requests.length === 0) {\n // This should not be called when there are no requests (handled above)\n return 0;\n }\n\n const lastRequest = Math.max(...requests);\n return Date.now() - lastRequest;\n }\n}\n\n// Export types for use in other modules\nexport type { ActivityMetrics, DynamicCapacityResult };\n","import axios, { AxiosError } from 'axios';\nimport { HttpClientError } from '../errors/http-client-error.js';\nimport {\n CacheStore,\n DedupeStore,\n RateLimitStore,\n AdaptiveRateLimitStore,\n RequestPriority,\n hashRequest,\n} from '../stores/index.js';\nimport { HttpClientContract } from '../types/index.js';\n\nconst DEFAULT_RATE_LIMIT_HEADER_NAMES = {\n retryAfter: ['retry-after'],\n limit: ['ratelimit-limit', 'x-ratelimit-limit', 'rate-limit-limit'],\n remaining: [\n 'ratelimit-remaining',\n 'x-ratelimit-remaining',\n 'rate-limit-remaining',\n ],\n reset: ['ratelimit-reset', 'x-ratelimit-reset', 'rate-limit-reset'],\n combined: ['ratelimit'],\n} as const;\n\n/**\n * Wait for a specified period while supporting cancellation via AbortSignal.\n *\n * If the signal is aborted before the timeout completes the promise rejects\n * with an `Error` whose name is set to `AbortError`, mimicking DOMException in\n * browser environments without depending on it. This allows callers to use a\n * single `AbortController` for both the rate-limit wait *and* the subsequent\n * HTTP request.\n */\nfunction wait(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (signal) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timer);\n const err = new Error('Aborted');\n err.name = 'AbortError';\n reject(err);\n }\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n });\n}\n\nexport interface HttpClientStores {\n cache?: CacheStore;\n dedupe?: DedupeStore;\n rateLimit?: RateLimitStore | AdaptiveRateLimitStore;\n}\n\nexport interface HttpClientOptions {\n /**\n * Default cache TTL in seconds\n */\n defaultCacheTTL?: number;\n /**\n * Whether to throw errors on rate limit violations\n */\n throwOnRateLimit?: boolean;\n /**\n * Maximum time to wait for rate limit in milliseconds\n */\n maxWaitTime?: number;\n /**\n * Optional response transformer applied to the raw response data.\n * Use this for converting snake_case to camelCase, etc.\n */\n responseTransformer?: (data: unknown) => unknown;\n /**\n * Optional error handler to convert errors into domain-specific error types.\n * If not provided, a generic HttpClientError is thrown.\n */\n errorHandler?: (error: unknown) => Error;\n /**\n * Optional response validator/handler called after transformation.\n * Use this to inspect the response and throw domain-specific errors\n * based on response content (e.g., API-level error codes).\n */\n responseHandler?: (data: unknown) => unknown;\n /**\n * Configure rate-limit response header names for standards and custom APIs.\n */\n rateLimitHeaders?: {\n retryAfter?: Array<string>;\n limit?: Array<string>;\n remaining?: Array<string>;\n reset?: Array<string>;\n combined?: Array<string>;\n };\n}\n\ninterface RateLimitHeaderConfig {\n retryAfter: Array<string>;\n limit: Array<string>;\n remaining: Array<string>;\n reset: Array<string>;\n combined: Array<string>;\n}\n\nexport class HttpClient implements HttpClientContract {\n private _http;\n private stores: HttpClientStores;\n private serverCooldowns = new Map<string, number>();\n private options: Required<\n Pick<\n HttpClientOptions,\n 'defaultCacheTTL' | 'throwOnRateLimit' | 'maxWaitTime'\n >\n > &\n Pick<\n HttpClientOptions,\n 'responseTransformer' | 'errorHandler' | 'responseHandler'\n > & {\n rateLimitHeaders: RateLimitHeaderConfig;\n };\n\n constructor(stores: HttpClientStores = {}, options: HttpClientOptions = {}) {\n this._http = axios.create();\n this.stores = stores;\n this.options = {\n defaultCacheTTL: options.defaultCacheTTL ?? 3600,\n throwOnRateLimit: options.throwOnRateLimit ?? true,\n maxWaitTime: options.maxWaitTime ?? 60000,\n responseTransformer: options.responseTransformer,\n errorHandler: options.errorHandler,\n responseHandler: options.responseHandler,\n rateLimitHeaders: this.normalizeRateLimitHeaders(\n options.rateLimitHeaders,\n ),\n };\n }\n\n private normalizeRateLimitHeaders(\n customHeaders?: HttpClientOptions['rateLimitHeaders'],\n ): RateLimitHeaderConfig {\n return {\n retryAfter: this.normalizeHeaderNames(\n customHeaders?.retryAfter,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.retryAfter,\n ),\n limit: this.normalizeHeaderNames(\n customHeaders?.limit,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.limit,\n ),\n remaining: this.normalizeHeaderNames(\n customHeaders?.remaining,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.remaining,\n ),\n reset: this.normalizeHeaderNames(\n customHeaders?.reset,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.reset,\n ),\n combined: this.normalizeHeaderNames(\n customHeaders?.combined,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.combined,\n ),\n };\n }\n\n private normalizeHeaderNames(\n providedNames: Array<string> | undefined,\n defaultNames: ReadonlyArray<string>,\n ): Array<string> {\n if (!providedNames || providedNames.length === 0) {\n return [...defaultNames];\n }\n\n const customNames = providedNames\n .map((name) => name.trim().toLowerCase())\n .filter(Boolean);\n\n if (customNames.length === 0) {\n return [...defaultNames];\n }\n\n return [...new Set([...customNames, ...defaultNames])];\n }\n\n /**\n * Infer the resource name from the endpoint URL\n * @param url The full URL or endpoint path\n * @returns The resource name for rate limiting\n */\n private inferResource(url: string): string {\n try {\n const urlObj = new URL(url);\n // Use the first meaningful path segment as the resource name\n const segments = urlObj.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] || 'unknown';\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Extract endpoint and params from URL for request hashing\n * @param url The full URL\n * @returns Object with endpoint and params for hashing\n */\n private parseUrlForHashing(url: string): {\n endpoint: string;\n params: Record<string, unknown>;\n } {\n const urlObj = new URL(url);\n const endpoint = `${urlObj.origin}${urlObj.pathname}`;\n const params: Record<string, unknown> = {};\n\n urlObj.searchParams.forEach((value, key) => {\n const existing = params[key];\n\n // Keep repeated query keys as arrays so semantically distinct URLs like\n // `?tag=a&tag=b` and `?tag=b` do not hash to the same cache/dedupe key.\n if (existing === undefined) {\n params[key] = value;\n return;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n return;\n }\n\n params[key] = [existing, value];\n });\n\n return { endpoint, params };\n }\n\n private getOriginScope(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return 'unknown';\n }\n }\n\n private getHeaderValue(\n headers: Record<string, unknown> | undefined,\n names: Array<string>,\n ): string | undefined {\n if (!headers) {\n return undefined;\n }\n\n for (const rawName of names) {\n const name = rawName.toLowerCase();\n const value = headers[name] ?? headers[rawName];\n\n if (typeof value === 'string') {\n return value;\n }\n\n if (Array.isArray(value) && value.length > 0) {\n const first = value.find((entry) => typeof entry === 'string');\n if (typeof first === 'string') {\n return first;\n }\n }\n }\n\n return undefined;\n }\n\n private parseIntegerHeader(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value.trim(), 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n\n return parsed;\n }\n\n private parseRetryAfterMs(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = Number.parseInt(value.trim(), 10);\n if (Number.isFinite(numeric) && numeric >= 0) {\n return numeric * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (!Number.isFinite(dateMs)) {\n return undefined;\n }\n\n return Math.max(0, dateMs - Date.now());\n }\n\n private parseResetMs(value: string | undefined): number | undefined {\n const parsed = this.parseIntegerHeader(value);\n if (parsed === undefined) {\n return undefined;\n }\n\n if (parsed === 0) {\n return 0;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n\n if (parsed > nowSeconds + 1) {\n return Math.max(0, (parsed - nowSeconds) * 1000);\n }\n\n return parsed * 1000;\n }\n\n private parseCombinedRateLimitHeader(value: string | undefined): {\n remaining?: number;\n resetMs?: number;\n } {\n if (!value) {\n return {};\n }\n\n const remainingMatch = value.match(/(?:^|[;,])\\s*r\\s*=\\s*(\\d+)/i);\n const resetMatch = value.match(/(?:^|[;,])\\s*t\\s*=\\s*(\\d+)/i);\n\n return {\n remaining: remainingMatch\n ? this.parseIntegerHeader(remainingMatch[1])\n : undefined,\n resetMs: resetMatch ? this.parseResetMs(resetMatch[1]) : undefined,\n };\n }\n\n private applyServerRateLimitHints(\n url: string,\n headers: Record<string, unknown> | undefined,\n statusCode?: number,\n ): void {\n if (!headers) {\n return;\n }\n\n const config = this.options.rateLimitHeaders;\n const retryAfterRaw = this.getHeaderValue(headers, config.retryAfter);\n const resetRaw = this.getHeaderValue(headers, config.reset);\n const remainingRaw = this.getHeaderValue(headers, config.remaining);\n const combinedRaw = this.getHeaderValue(headers, config.combined);\n\n const retryAfterMs = this.parseRetryAfterMs(retryAfterRaw);\n const resetMs = this.parseResetMs(resetRaw);\n const remaining = this.parseIntegerHeader(remainingRaw);\n const combined = this.parseCombinedRateLimitHeader(combinedRaw);\n\n const effectiveRemaining = remaining ?? combined.remaining;\n const effectiveResetMs = resetMs ?? combined.resetMs;\n const hasRateLimitErrorStatus = statusCode === 429 || statusCode === 503;\n\n let waitMs: number | undefined;\n\n if (retryAfterMs !== undefined) {\n waitMs = retryAfterMs;\n } else if (\n effectiveResetMs !== undefined &&\n (hasRateLimitErrorStatus ||\n (effectiveRemaining !== undefined && effectiveRemaining <= 0))\n ) {\n waitMs = effectiveResetMs;\n }\n\n if (waitMs === undefined || waitMs <= 0) {\n return;\n }\n\n const scope = this.getOriginScope(url);\n this.serverCooldowns.set(scope, Date.now() + waitMs);\n }\n\n private async enforceServerCooldown(\n url: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const scope = this.getOriginScope(url);\n const startedAt = Date.now();\n\n // Re-check cooldown after each sleep so we never proceed while a server\n // cooldown is still active. This avoids bypassing limits when cooldown\n // duration is longer than maxWaitTime.\n while (true) {\n const cooldownUntil = this.serverCooldowns.get(scope);\n if (!cooldownUntil) {\n return;\n }\n\n const waitMs = cooldownUntil - Date.now();\n if (waitMs <= 0) {\n this.serverCooldowns.delete(scope);\n return;\n }\n\n if (this.options.throwOnRateLimit) {\n throw new Error(\n `Rate limit exceeded for origin '${scope}'. Wait ${waitMs}ms before retrying.`,\n );\n }\n\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for origin '${scope}'.`,\n );\n }\n\n await wait(Math.min(waitMs, remainingWaitBudgetMs), signal);\n }\n }\n\n private async enforceStoreRateLimit(\n resource: string,\n priority: RequestPriority,\n signal?: AbortSignal,\n ): Promise<void> {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n const startedAt = Date.now();\n\n if (this.options.throwOnRateLimit) {\n const canProceed = await rateLimit.canProceed(resource, priority);\n if (!canProceed) {\n const waitTime = await rateLimit.getWaitTime(resource, priority);\n throw new Error(\n `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`,\n );\n }\n return;\n }\n\n // Keep polling + waiting until the store explicitly allows the request or\n // we exhaust maxWaitTime. A single one-off sleep can otherwise let a request\n // through while still over limit.\n while (!(await rateLimit.canProceed(resource, priority))) {\n const suggestedWaitMs = await rateLimit.getWaitTime(resource, priority);\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for resource '${resource}'.`,\n );\n }\n\n // If a store reports \"blocked\" but no wait time, use a tiny backoff to\n // avoid a tight CPU loop while still converging quickly.\n const waitTime =\n suggestedWaitMs > 0\n ? Math.min(suggestedWaitMs, remainingWaitBudgetMs)\n : Math.min(25, remainingWaitBudgetMs);\n\n await wait(waitTime, signal);\n }\n }\n\n private generateClientError(err: unknown): Error {\n // If a custom error handler is provided, use it\n if (this.options.errorHandler) {\n return this.options.errorHandler(err);\n }\n\n if (err instanceof HttpClientError) {\n return err;\n }\n\n const error = err as AxiosError<{ message?: string }>;\n const statusCode = error.response?.status;\n const errorMessage = error.response?.data?.message;\n const message = `${error.message}${errorMessage ? `, ${errorMessage}` : ''}`;\n\n return new HttpClientError(message, statusCode);\n }\n\n async get<Result>(\n url: string,\n options: { signal?: AbortSignal; priority?: RequestPriority } = {},\n ): Promise<Result> {\n const { signal, priority = 'background' } = options;\n const { endpoint, params } = this.parseUrlForHashing(url);\n const hash = hashRequest(endpoint, params);\n const resource = this.inferResource(url);\n\n try {\n await this.enforceServerCooldown(url, signal);\n\n // 1. Cache - check for cached response\n if (this.stores.cache) {\n const cachedResult = await this.stores.cache.get(hash);\n if (cachedResult !== undefined) {\n return cachedResult as Result;\n }\n }\n\n // 2. Deduplication - check for in-progress request\n if (this.stores.dedupe) {\n const existingResult = await this.stores.dedupe.waitFor(hash);\n if (existingResult !== undefined) {\n return existingResult as Result;\n }\n\n if (this.stores.dedupe.registerOrJoin) {\n const registration = await this.stores.dedupe.registerOrJoin(hash);\n\n if (!registration.isOwner) {\n const joinedResult = await this.stores.dedupe.waitFor(hash);\n if (joinedResult !== undefined) {\n return joinedResult as Result;\n }\n }\n } else {\n await this.stores.dedupe.register(hash);\n }\n }\n\n // 3. Rate limiting - check if request can proceed\n if (this.stores.rateLimit) {\n await this.enforceStoreRateLimit(resource, priority, signal);\n }\n\n // 4. Execute the actual HTTP request\n const response = await this._http.get(url, { signal });\n this.applyServerRateLimitHints(\n url,\n response.headers as Record<string, unknown>,\n response.status,\n );\n\n // 5. Apply response transformer if provided\n let data = response.data;\n if (this.options.responseTransformer && data) {\n data = this.options.responseTransformer(data);\n }\n\n // 6. Apply response handler if provided (for domain-specific validation)\n if (this.options.responseHandler) {\n data = this.options.responseHandler(data);\n }\n\n const result = data as Result;\n\n // 7. Record the request for rate limiting\n if (this.stores.rateLimit) {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n await rateLimit.record(resource, priority);\n }\n\n // 8. Cache the result\n if (this.stores.cache) {\n await this.stores.cache.set(hash, result, this.options.defaultCacheTTL);\n }\n\n // 9. Mark deduplication as complete\n if (this.stores.dedupe) {\n await this.stores.dedupe.complete(hash, result);\n }\n\n return result;\n } catch (error) {\n const axiosError = error as AxiosError;\n if (axiosError.response) {\n this.applyServerRateLimitHints(\n url,\n axiosError.response.headers as Record<string, unknown>,\n axiosError.response.status,\n );\n }\n\n // Mark deduplication as failed\n if (this.stores.dedupe) {\n await this.stores.dedupe.fail(hash, error as Error);\n }\n\n // Allow callers to detect aborts distinctly – do not wrap AbortError.\n if (error instanceof Error && error.name === 'AbortError') {\n throw error;\n }\n\n throw this.generateClientError(error);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors/http-client-error.ts","../src/stores/rate-limit-store.ts","../src/stores/request-hasher.ts","../src/stores/rate-limit-config.ts","../src/stores/adaptive-capacity-calculator.ts","../src/http-client/http-client.ts"],"names":["baseUserCapacity"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for HTTP client errors.\n * Consumers can extend this for domain-specific error handling.\n */\nexport class HttpClientError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'HttpClientError';\n this.statusCode = statusCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n","import { z } from 'zod';\n\n/**\n * Priority level for API requests\n */\nexport type RequestPriority = 'user' | 'background';\n\n/**\n * Adaptive configuration schema with validation and defaults\n */\nexport const AdaptiveConfigSchema = z\n .object({\n monitoringWindowMs: z\n .number()\n .positive()\n .default(15 * 60 * 1000), // 15 minutes\n highActivityThreshold: z.number().min(0).default(10), // requests per window\n moderateActivityThreshold: z.number().min(0).default(3),\n recalculationIntervalMs: z.number().positive().default(30000), // 30 seconds\n sustainedInactivityThresholdMs: z\n .number()\n .positive()\n .default(30 * 60 * 1000), // 30 minutes\n backgroundPauseOnIncreasingTrend: z.boolean().default(true),\n maxUserScaling: z.number().positive().default(2.0), // don't exceed 2x capacity\n minUserReserved: z.number().min(0).default(5), // requests minimum\n })\n .refine(\n (data) => {\n return data.moderateActivityThreshold < data.highActivityThreshold;\n },\n {\n message:\n 'moderateActivityThreshold must be less than highActivityThreshold',\n },\n );\n\n/**\n * Configuration for adaptive rate limiting\n */\nexport type AdaptiveConfig = z.infer<typeof AdaptiveConfigSchema>;\n\n/**\n * Interface for rate limiting API requests per resource\n */\nexport interface RateLimitStore {\n /**\n * Atomically acquire capacity for a request if the implementation supports it.\n * When present and returning true, callers should treat the request as already\n * recorded for rate-limit accounting.\n */\n acquire?(resource: string): Promise<boolean>;\n\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n */\n record(resource: string): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n }>;\n\n /**\n * Reset rate limits for a resource (useful for testing)\n * @param resource The resource name\n */\n reset(resource: string): Promise<void>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string): Promise<number>;\n}\n\n/**\n * Enhanced interface for adaptive rate limiting stores with priority support\n */\nexport interface AdaptiveRateLimitStore extends RateLimitStore {\n /**\n * Atomically acquire capacity for a request if the implementation supports it.\n */\n acquire?(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Check if a request to a resource can proceed based on rate limits\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n * @returns True if the request can proceed, false if rate limited\n */\n canProceed(resource: string, priority?: RequestPriority): Promise<boolean>;\n\n /**\n * Record a request to a resource for rate limiting tracking\n * @param resource The resource name (e.g., 'issues', 'characters')\n * @param priority The priority level of the request (defaults to 'background')\n */\n record(resource: string, priority?: RequestPriority): Promise<void>;\n\n /**\n * Get the current rate limit status for a resource\n * @param resource The resource name\n * @returns Rate limit information including remaining requests and reset time\n */\n getStatus(resource: string): Promise<{\n remaining: number;\n resetTime: Date;\n limit: number;\n adaptive?: {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n recentUserActivity: number;\n reason: string;\n };\n }>;\n\n /**\n * Get the time in milliseconds until the next request can be made\n * @param resource The resource name\n * @param priority The priority level of the request (defaults to 'background')\n * @returns Milliseconds to wait, or 0 if no waiting is needed\n */\n getWaitTime(resource: string, priority?: RequestPriority): Promise<number>;\n}\n","import { createHash } from 'crypto';\n\n/**\n * Creates a consistent hash for API requests to use as cache/dedupe keys\n * @param endpoint The API endpoint\n * @param params The request parameters\n * @returns A SHA-256 hash of the request\n */\nexport function hashRequest(\n endpoint: string,\n params: Record<string, unknown> = {},\n): string {\n const requestString = JSON.stringify({\n endpoint,\n params: sortObject(params),\n });\n\n return createHash('sha256').update(requestString).digest('hex');\n}\n\n/**\n * Normalises and sorts an object for hashing purposes.\n *\n * The ComicVine API transmits all query parameters as strings. To avoid cache\n * misses caused by treating `10` and `'10'` as different values we normalise\n * primitive types (number and boolean) to their string representation **before**\n * sorting. `undefined` values are intentionally kept as `undefined` so that\n * they are dropped by `JSON.stringify`, maintaining the existing behaviour\n * where an omitted parameter and an `undefined` parameter produce the same\n * hash.\n */\nfunction sortObject(obj: unknown): unknown {\n // Handle primitives first\n if (obj === null) {\n return null;\n }\n\n const objType = typeof obj;\n\n if (objType === 'undefined' || objType === 'string') {\n return obj;\n }\n\n if (objType === 'number' || objType === 'boolean') {\n // Convert to string so that 10 and '10' (or true and 'true') hash equally\n return String(obj);\n }\n\n // Recursively process arrays\n if (Array.isArray(obj)) {\n return obj.map(sortObject);\n }\n\n // For objects – sort keys and recurse\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(obj as Record<string, unknown>).sort();\n\n for (const key of keys) {\n const value = (obj as Record<string, unknown>)[key];\n const normalisedValue = sortObject(value);\n\n // Skip keys whose value normalises to undefined so omitted & undefined match\n if (normalisedValue !== undefined) {\n sorted[key] = normalisedValue;\n }\n }\n\n return sorted;\n}\n","/**\n * Configuration for per-resource rate limiting.\n *\n * This interface is shared by all store implementations (e.g. in-memory,\n * SQLite) so that callers can use a single canonical type.\n */\nexport interface RateLimitConfig {\n /** Number of requests allowed per time window */\n limit: number;\n /** Duration of the window in milliseconds */\n windowMs: number;\n}\n\n/**\n * Default rate-limit window: 60 requests per minute.\n *\n * Store implementations can reference this to avoid duplicating magic numbers.\n */\nexport const DEFAULT_RATE_LIMIT: RateLimitConfig = {\n limit: 60,\n windowMs: 60_000,\n};\n","import { z } from 'zod';\nimport { AdaptiveConfigSchema } from './rate-limit-store.js';\n\ninterface ActivityMetrics {\n recentUserRequests: Array<number>;\n recentBackgroundRequests: Array<number>;\n userActivityTrend: 'increasing' | 'stable' | 'decreasing' | 'none';\n}\n\ninterface DynamicCapacityResult {\n userReserved: number;\n backgroundMax: number;\n backgroundPaused: boolean;\n reason: string;\n}\n\n/**\n * Calculates dynamic capacity allocation based on real-time user activity patterns\n */\nexport class AdaptiveCapacityCalculator {\n public readonly config: z.infer<typeof AdaptiveConfigSchema>;\n\n constructor(config: Partial<z.input<typeof AdaptiveConfigSchema>> = {}) {\n // Zod handles validation and applies defaults automatically\n this.config = AdaptiveConfigSchema.parse(config);\n }\n\n calculateDynamicCapacity(\n resource: string,\n totalLimit: number,\n activityMetrics: ActivityMetrics,\n ): DynamicCapacityResult {\n const recentUserActivity = this.getRecentActivity(\n activityMetrics.recentUserRequests,\n );\n const activityTrend = this.calculateActivityTrend(\n activityMetrics.recentUserRequests,\n );\n\n // Strategy 1: High Activity - Pause Background\n if (recentUserActivity >= this.config.highActivityThreshold) {\n const userCapacity = Math.min(\n totalLimit * 0.9,\n Math.floor(totalLimit * 0.5 * this.config.maxUserScaling), // 50% base * scaling factor\n );\n\n return {\n userReserved: userCapacity,\n backgroundMax: totalLimit - userCapacity,\n backgroundPaused:\n this.config.backgroundPauseOnIncreasingTrend &&\n activityTrend === 'increasing',\n reason: `High user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - prioritizing users`,\n };\n }\n\n // Strategy 2: Moderate Activity - Balanced Scaling\n if (recentUserActivity >= this.config.moderateActivityThreshold) {\n const userMultiplier = this.getUserMultiplier(\n recentUserActivity,\n activityTrend,\n );\n const baseUserCapacity = Math.floor(totalLimit * 0.4); // 40% base allocation\n const dynamicUserCapacity = Math.min(\n totalLimit * 0.7,\n baseUserCapacity * userMultiplier,\n );\n\n return {\n userReserved: dynamicUserCapacity,\n backgroundMax: totalLimit - dynamicUserCapacity,\n backgroundPaused: false,\n reason: `Moderate user activity - dynamic scaling (${userMultiplier.toFixed(1)}x user capacity)`,\n };\n }\n\n // Strategy 3: Low/No Activity - Background Scale Up\n if (recentUserActivity === 0) {\n // If there have never been any requests at all (fresh start), use default capacity allocation\n if (\n activityMetrics.recentUserRequests.length === 0 &&\n activityMetrics.recentBackgroundRequests.length === 0\n ) {\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for initial state\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit -\n Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: 'Initial state - default capacity allocation',\n };\n }\n\n // If there have never been user requests (only background), use background scale up\n if (activityMetrics.recentUserRequests.length === 0) {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'No user activity yet - background scale up with minimal user buffer',\n };\n }\n\n // There have been user requests before, check for sustained inactivity\n const sustainedInactivity = this.getSustainedInactivityPeriod(\n activityMetrics.recentUserRequests,\n );\n\n if (sustainedInactivity > this.config.sustainedInactivityThresholdMs) {\n return {\n userReserved: 0, // No reservation - background gets everything!\n backgroundMax: totalLimit, // Full capacity available\n backgroundPaused: false,\n reason: `Sustained zero activity (${Math.floor(sustainedInactivity / 60000)}+ min) - full capacity to background`,\n };\n } else {\n return {\n userReserved: this.config.minUserReserved, // Minimal safety buffer\n backgroundMax: totalLimit - this.config.minUserReserved,\n backgroundPaused: false,\n reason:\n 'Recent zero activity - background scale up with minimal user buffer',\n };\n }\n }\n\n // Strategy 4: Very Low Activity - Gradual Background Scale Up\n const baseUserCapacity = Math.floor(totalLimit * 0.3); // 30% base for very low activity\n return {\n userReserved: Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundMax:\n totalLimit - Math.max(baseUserCapacity, this.config.minUserReserved),\n backgroundPaused: false,\n reason: `Low user activity (${recentUserActivity} requests/${this.config.monitoringWindowMs / 60000}min) - background scale up`,\n };\n }\n\n getRecentActivity(requests: Array<number>): number {\n const cutoff = Date.now() - this.config.monitoringWindowMs;\n return requests.filter((timestamp) => timestamp > cutoff).length;\n }\n\n calculateActivityTrend(\n requests: Array<number>,\n ): 'increasing' | 'stable' | 'decreasing' | 'none' {\n const now = Date.now();\n const windowSize = this.config.monitoringWindowMs / 3; // Use 1/3 of monitoring window for trend\n const recent = requests.filter((t) => t > now - windowSize).length;\n const previous = requests.filter(\n (t) => t > now - 2 * windowSize && t <= now - windowSize,\n ).length;\n\n if (recent === 0 && previous === 0) return 'none';\n if (recent > previous * 1.5) return 'increasing';\n if (recent < previous * 0.5) return 'decreasing';\n return 'stable';\n }\n\n private getUserMultiplier(activity: number, trend: string): number {\n let base = Math.min(\n this.config.maxUserScaling,\n 1 + activity / this.config.highActivityThreshold,\n );\n\n // Adjust based on trend\n if (trend === 'increasing') base *= 1.2;\n if (trend === 'decreasing') base *= 0.8;\n\n return Math.max(1.0, base);\n }\n\n private getSustainedInactivityPeriod(requests: Array<number>): number {\n if (requests.length === 0) {\n // This should not be called when there are no requests (handled above)\n return 0;\n }\n\n const lastRequest = Math.max(...requests);\n return Date.now() - lastRequest;\n }\n}\n\n// Export types for use in other modules\nexport type { ActivityMetrics, DynamicCapacityResult };\n","import { HttpClientError } from '../errors/http-client-error.js';\nimport {\n CacheStore,\n DedupeStore,\n RateLimitStore,\n AdaptiveRateLimitStore,\n RequestPriority,\n hashRequest,\n} from '../stores/index.js';\nimport { HttpClientContract } from '../types/index.js';\n\nconst DEFAULT_RATE_LIMIT_HEADER_NAMES = {\n retryAfter: ['retry-after'],\n limit: ['ratelimit-limit', 'x-ratelimit-limit', 'rate-limit-limit'],\n remaining: [\n 'ratelimit-remaining',\n 'x-ratelimit-remaining',\n 'rate-limit-remaining',\n ],\n reset: ['ratelimit-reset', 'x-ratelimit-reset', 'rate-limit-reset'],\n combined: ['ratelimit'],\n} as const;\n\n/**\n * Wait for a specified period while supporting cancellation via AbortSignal.\n *\n * If the signal is aborted before the timeout completes the promise rejects\n * with an `Error` whose name is set to `AbortError`, mimicking DOMException in\n * browser environments without depending on it. This allows callers to use a\n * single `AbortController` for both the rate-limit wait *and* the subsequent\n * HTTP request.\n */\nfunction wait(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (signal) {\n signal.removeEventListener('abort', onAbort);\n }\n resolve();\n }, ms);\n\n function onAbort() {\n clearTimeout(timer);\n const err = new Error('Aborted');\n err.name = 'AbortError';\n reject(err);\n }\n\n if (signal) {\n if (signal.aborted) {\n onAbort();\n } else {\n signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n });\n}\n\nexport interface HttpClientStores {\n cache?: CacheStore;\n dedupe?: DedupeStore;\n rateLimit?: RateLimitStore | AdaptiveRateLimitStore;\n}\n\nexport interface HttpClientOptions {\n /**\n * Default cache TTL in seconds\n */\n defaultCacheTTL?: number;\n /**\n * Whether to throw errors on rate limit violations\n */\n throwOnRateLimit?: boolean;\n /**\n * Maximum time to wait for rate limit in milliseconds\n */\n maxWaitTime?: number;\n /**\n * Optional response transformer applied to the raw response data.\n * Use this for converting snake_case to camelCase, etc.\n */\n responseTransformer?: (data: unknown) => unknown;\n /**\n * Optional error handler to convert errors into domain-specific error types.\n * If not provided, a generic HttpClientError is thrown.\n */\n errorHandler?: (error: unknown) => Error;\n /**\n * Optional response validator/handler called after transformation.\n * Use this to inspect the response and throw domain-specific errors\n * based on response content (e.g., API-level error codes).\n */\n responseHandler?: (data: unknown) => unknown;\n /**\n * Configure rate-limit response header names for standards and custom APIs.\n */\n rateLimitHeaders?: {\n retryAfter?: Array<string>;\n limit?: Array<string>;\n remaining?: Array<string>;\n reset?: Array<string>;\n combined?: Array<string>;\n };\n}\n\ninterface RateLimitHeaderConfig {\n retryAfter: Array<string>;\n limit: Array<string>;\n remaining: Array<string>;\n reset: Array<string>;\n combined: Array<string>;\n}\n\ninterface ParsedResponseBody {\n data: unknown;\n}\n\ntype ErrorWithResponse = {\n message: string;\n response: {\n status: number;\n data: unknown;\n headers: Headers;\n };\n};\n\nexport class HttpClient implements HttpClientContract {\n private stores: HttpClientStores;\n private serverCooldowns = new Map<string, number>();\n private options: Required<\n Pick<\n HttpClientOptions,\n 'defaultCacheTTL' | 'throwOnRateLimit' | 'maxWaitTime'\n >\n > &\n Pick<\n HttpClientOptions,\n 'responseTransformer' | 'errorHandler' | 'responseHandler'\n > & {\n rateLimitHeaders: RateLimitHeaderConfig;\n };\n\n constructor(stores: HttpClientStores = {}, options: HttpClientOptions = {}) {\n this.stores = stores;\n this.options = {\n defaultCacheTTL: options.defaultCacheTTL ?? 3600,\n throwOnRateLimit: options.throwOnRateLimit ?? true,\n maxWaitTime: options.maxWaitTime ?? 60000,\n responseTransformer: options.responseTransformer,\n errorHandler: options.errorHandler,\n responseHandler: options.responseHandler,\n rateLimitHeaders: this.normalizeRateLimitHeaders(\n options.rateLimitHeaders,\n ),\n };\n }\n\n private normalizeRateLimitHeaders(\n customHeaders?: HttpClientOptions['rateLimitHeaders'],\n ): RateLimitHeaderConfig {\n return {\n retryAfter: this.normalizeHeaderNames(\n customHeaders?.retryAfter,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.retryAfter,\n ),\n limit: this.normalizeHeaderNames(\n customHeaders?.limit,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.limit,\n ),\n remaining: this.normalizeHeaderNames(\n customHeaders?.remaining,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.remaining,\n ),\n reset: this.normalizeHeaderNames(\n customHeaders?.reset,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.reset,\n ),\n combined: this.normalizeHeaderNames(\n customHeaders?.combined,\n DEFAULT_RATE_LIMIT_HEADER_NAMES.combined,\n ),\n };\n }\n\n private normalizeHeaderNames(\n providedNames: Array<string> | undefined,\n defaultNames: ReadonlyArray<string>,\n ): Array<string> {\n if (!providedNames || providedNames.length === 0) {\n return [...defaultNames];\n }\n\n const customNames = providedNames\n .map((name) => name.trim().toLowerCase())\n .filter(Boolean);\n\n if (customNames.length === 0) {\n return [...defaultNames];\n }\n\n return [...new Set([...customNames, ...defaultNames])];\n }\n\n /**\n * Infer the resource name from the endpoint URL\n * @param url The full URL or endpoint path\n * @returns The resource name for rate limiting\n */\n private inferResource(url: string): string {\n try {\n const urlObj = new URL(url);\n // Use the first meaningful path segment as the resource name\n const segments = urlObj.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] || 'unknown';\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Extract endpoint and params from URL for request hashing\n * @param url The full URL\n * @returns Object with endpoint and params for hashing\n */\n private parseUrlForHashing(url: string): {\n endpoint: string;\n params: Record<string, unknown>;\n } {\n const urlObj = new URL(url);\n const endpoint = `${urlObj.origin}${urlObj.pathname}`;\n const params: Record<string, unknown> = {};\n\n urlObj.searchParams.forEach((value, key) => {\n const existing = params[key];\n\n // Keep repeated query keys as arrays so semantically distinct URLs like\n // `?tag=a&tag=b` and `?tag=b` do not hash to the same cache/dedupe key.\n if (existing === undefined) {\n params[key] = value;\n return;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n return;\n }\n\n params[key] = [existing, value];\n });\n\n return { endpoint, params };\n }\n\n private getOriginScope(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return 'unknown';\n }\n }\n\n private getHeaderValue(\n headers: Headers | Record<string, unknown> | undefined,\n names: Array<string>,\n ): string | undefined {\n if (!headers) {\n return undefined;\n }\n\n if (headers instanceof Headers) {\n for (const rawName of names) {\n const value = headers.get(rawName);\n if (value !== null) {\n return value;\n }\n }\n return undefined;\n }\n\n for (const rawName of names) {\n const name = rawName.toLowerCase();\n const value = headers[name] ?? headers[rawName];\n\n if (typeof value === 'string') {\n return value;\n }\n\n if (Array.isArray(value) && value.length > 0) {\n const first = value.find((entry) => typeof entry === 'string');\n if (typeof first === 'string') {\n return first;\n }\n }\n }\n\n return undefined;\n }\n\n private parseIntegerHeader(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number.parseInt(value.trim(), 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return undefined;\n }\n\n return parsed;\n }\n\n private parseRetryAfterMs(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const numeric = Number.parseInt(value.trim(), 10);\n if (Number.isFinite(numeric) && numeric >= 0) {\n return numeric * 1000;\n }\n\n const dateMs = Date.parse(value);\n if (!Number.isFinite(dateMs)) {\n return undefined;\n }\n\n return Math.max(0, dateMs - Date.now());\n }\n\n private parseResetMs(value: string | undefined): number | undefined {\n const parsed = this.parseIntegerHeader(value);\n if (parsed === undefined) {\n return undefined;\n }\n\n if (parsed === 0) {\n return 0;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n\n if (parsed > nowSeconds + 1) {\n return Math.max(0, (parsed - nowSeconds) * 1000);\n }\n\n return parsed * 1000;\n }\n\n private parseCombinedRateLimitHeader(value: string | undefined): {\n remaining?: number;\n resetMs?: number;\n } {\n if (!value) {\n return {};\n }\n\n const remainingMatch = value.match(/(?:^|[;,])\\s*r\\s*=\\s*(\\d+)/i);\n const resetMatch = value.match(/(?:^|[;,])\\s*t\\s*=\\s*(\\d+)/i);\n\n return {\n remaining: remainingMatch\n ? this.parseIntegerHeader(remainingMatch[1])\n : undefined,\n resetMs: resetMatch ? this.parseResetMs(resetMatch[1]) : undefined,\n };\n }\n\n private applyServerRateLimitHints(\n url: string,\n headers: Headers | Record<string, unknown> | undefined,\n statusCode?: number,\n ): void {\n if (!headers) {\n return;\n }\n\n const config = this.options.rateLimitHeaders;\n const retryAfterRaw = this.getHeaderValue(headers, config.retryAfter);\n const resetRaw = this.getHeaderValue(headers, config.reset);\n const remainingRaw = this.getHeaderValue(headers, config.remaining);\n const combinedRaw = this.getHeaderValue(headers, config.combined);\n\n const retryAfterMs = this.parseRetryAfterMs(retryAfterRaw);\n const resetMs = this.parseResetMs(resetRaw);\n const remaining = this.parseIntegerHeader(remainingRaw);\n const combined = this.parseCombinedRateLimitHeader(combinedRaw);\n\n const effectiveRemaining = remaining ?? combined.remaining;\n const effectiveResetMs = resetMs ?? combined.resetMs;\n const hasRateLimitErrorStatus = statusCode === 429 || statusCode === 503;\n\n let waitMs: number | undefined;\n\n if (retryAfterMs !== undefined) {\n waitMs = retryAfterMs;\n } else if (\n effectiveResetMs !== undefined &&\n (hasRateLimitErrorStatus ||\n (effectiveRemaining !== undefined && effectiveRemaining <= 0))\n ) {\n waitMs = effectiveResetMs;\n }\n\n if (waitMs === undefined || waitMs <= 0) {\n return;\n }\n\n const scope = this.getOriginScope(url);\n this.serverCooldowns.set(scope, Date.now() + waitMs);\n }\n\n private async enforceServerCooldown(\n url: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const scope = this.getOriginScope(url);\n const startedAt = Date.now();\n\n // Re-check cooldown after each sleep so we never proceed while a server\n // cooldown is still active. This avoids bypassing limits when cooldown\n // duration is longer than maxWaitTime.\n while (true) {\n const cooldownUntil = this.serverCooldowns.get(scope);\n if (!cooldownUntil) {\n return;\n }\n\n const waitMs = cooldownUntil - Date.now();\n if (waitMs <= 0) {\n this.serverCooldowns.delete(scope);\n return;\n }\n\n if (this.options.throwOnRateLimit) {\n throw new Error(\n `Rate limit exceeded for origin '${scope}'. Wait ${waitMs}ms before retrying.`,\n );\n }\n\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for origin '${scope}'.`,\n );\n }\n\n await wait(Math.min(waitMs, remainingWaitBudgetMs), signal);\n }\n }\n\n private async enforceStoreRateLimit(\n resource: string,\n priority: RequestPriority,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n const startedAt = Date.now();\n const hasAtomicAcquire = typeof rateLimit.acquire === 'function';\n\n const canProceedNow = async (): Promise<boolean> => {\n if (hasAtomicAcquire) {\n return rateLimit.acquire!(resource, priority);\n }\n return rateLimit.canProceed(resource, priority);\n };\n\n if (this.options.throwOnRateLimit) {\n const canProceed = await canProceedNow();\n if (!canProceed) {\n const waitTime = await rateLimit.getWaitTime(resource, priority);\n throw new Error(\n `Rate limit exceeded for resource '${resource}'. Wait ${waitTime}ms before retrying.`,\n );\n }\n return hasAtomicAcquire;\n }\n\n // Keep polling + waiting until the store explicitly allows the request or\n // we exhaust maxWaitTime. A single one-off sleep can otherwise let a request\n // through while still over limit.\n while (!(await canProceedNow())) {\n const suggestedWaitMs = await rateLimit.getWaitTime(resource, priority);\n const elapsedMs = Date.now() - startedAt;\n const remainingWaitBudgetMs = this.options.maxWaitTime - elapsedMs;\n\n if (remainingWaitBudgetMs <= 0) {\n throw new Error(\n `Rate limit wait exceeded maxWaitTime (${this.options.maxWaitTime}ms) for resource '${resource}'.`,\n );\n }\n\n // If a store reports \"blocked\" but no wait time, use a tiny backoff to\n // avoid a tight CPU loop while still converging quickly.\n const waitTime =\n suggestedWaitMs > 0\n ? Math.min(suggestedWaitMs, remainingWaitBudgetMs)\n : Math.min(25, remainingWaitBudgetMs);\n\n await wait(waitTime, signal);\n }\n\n return hasAtomicAcquire;\n }\n\n private generateClientError(err: unknown): Error {\n // If a custom error handler is provided, use it\n if (this.options.errorHandler) {\n return this.options.errorHandler(err);\n }\n\n if (err instanceof HttpClientError) {\n return err;\n }\n\n const responseError = err as Partial<ErrorWithResponse>;\n const statusCode =\n typeof responseError.response?.status === 'number'\n ? responseError.response.status\n : undefined;\n\n const responseData = responseError.response?.data;\n const derivedResponseMessage =\n typeof responseData === 'object' && responseData !== null\n ? (responseData as { message?: unknown }).message\n : undefined;\n const responseMessage =\n typeof derivedResponseMessage === 'string'\n ? derivedResponseMessage\n : undefined;\n\n const errorMessage =\n err instanceof Error\n ? err.message\n : typeof (err as { message?: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Unknown error';\n const message = `${errorMessage}${responseMessage ? `, ${responseMessage}` : ''}`;\n\n return new HttpClientError(message, statusCode);\n }\n\n private async parseResponseBody(\n response: Response,\n ): Promise<ParsedResponseBody> {\n if (response.status === 204 || response.status === 205) {\n return { data: undefined };\n }\n\n const rawBody = await response.text();\n if (!rawBody) {\n return { data: undefined };\n }\n\n const contentType =\n response.headers.get('content-type')?.toLowerCase() ?? '';\n const shouldAttemptJsonParsing =\n contentType.includes('application/json') ||\n contentType.includes('+json') ||\n rawBody.trimStart().startsWith('{') ||\n rawBody.trimStart().startsWith('[');\n\n if (!shouldAttemptJsonParsing) {\n return { data: rawBody };\n }\n\n try {\n const parsed = JSON.parse(rawBody) as unknown;\n if (typeof parsed === 'object' && parsed !== null) {\n return { data: parsed };\n }\n\n return { data: parsed };\n } catch {\n return { data: rawBody };\n }\n }\n\n async get<Result>(\n url: string,\n options: { signal?: AbortSignal; priority?: RequestPriority } = {},\n ): Promise<Result> {\n const { signal, priority = 'background' } = options;\n const { endpoint, params } = this.parseUrlForHashing(url);\n const hash = hashRequest(endpoint, params);\n const resource = this.inferResource(url);\n\n try {\n await this.enforceServerCooldown(url, signal);\n\n // 1. Cache - check for cached response\n if (this.stores.cache) {\n const cachedResult = await this.stores.cache.get(hash);\n if (cachedResult !== undefined) {\n return cachedResult as Result;\n }\n }\n\n // 2. Deduplication - check for in-progress request\n if (this.stores.dedupe) {\n const existingResult = await this.stores.dedupe.waitFor(hash);\n if (existingResult !== undefined) {\n return existingResult as Result;\n }\n\n if (this.stores.dedupe.registerOrJoin) {\n const registration = await this.stores.dedupe.registerOrJoin(hash);\n\n if (!registration.isOwner) {\n const joinedResult = await this.stores.dedupe.waitFor(hash);\n if (joinedResult !== undefined) {\n return joinedResult as Result;\n }\n }\n } else {\n await this.stores.dedupe.register(hash);\n }\n }\n\n // 3. Rate limiting - check if request can proceed\n let alreadyRecordedRateLimit = false;\n if (this.stores.rateLimit) {\n alreadyRecordedRateLimit = await this.enforceStoreRateLimit(\n resource,\n priority,\n signal,\n );\n }\n\n // 4. Execute the actual HTTP request\n const response = await fetch(url, { signal });\n this.applyServerRateLimitHints(url, response.headers, response.status);\n\n const parsedBody = await this.parseResponseBody(response);\n\n if (!response.ok) {\n const error: ErrorWithResponse = {\n message: `Request failed with status ${response.status}`,\n response: {\n status: response.status,\n data: parsedBody.data,\n headers: response.headers,\n },\n };\n throw error;\n }\n\n // 5. Apply response transformer if provided\n let data: unknown = parsedBody.data;\n if (this.options.responseTransformer && data) {\n data = this.options.responseTransformer(data);\n }\n\n // 6. Apply response handler if provided (for domain-specific validation)\n if (this.options.responseHandler) {\n data = this.options.responseHandler(data);\n }\n\n const result = data as Result;\n\n // 7. Record the request for rate limiting\n if (this.stores.rateLimit && !alreadyRecordedRateLimit) {\n const rateLimit = this.stores.rateLimit as AdaptiveRateLimitStore;\n await rateLimit.record(resource, priority);\n }\n\n // 8. Cache the result\n if (this.stores.cache) {\n await this.stores.cache.set(hash, result, this.options.defaultCacheTTL);\n }\n\n // 9. Mark deduplication as complete\n if (this.stores.dedupe) {\n await this.stores.dedupe.complete(hash, result);\n }\n\n return result;\n } catch (error) {\n // Mark deduplication as failed\n if (this.stores.dedupe) {\n await this.stores.dedupe.fail(hash, error as Error);\n }\n\n // Allow callers to detect aborts distinctly – do not wrap AbortError.\n if (error instanceof Error && error.name === 'AbortError') {\n throw error;\n }\n\n // Already a processed error from the !response.ok branch above\n if (error instanceof HttpClientError) {\n throw error;\n }\n\n throw this.generateClientError(error);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -22,7 +22,6 @@
22
22
  "@repo/vitest-config": "0.0.1"
23
23
  },
24
24
  "dependencies": {
25
- "axios": "1.13.5",
26
25
  "zod": "3.25.71"
27
26
  },
28
27
  "keywords": [
@@ -42,7 +41,7 @@
42
41
  "lib/**"
43
42
  ],
44
43
  "homepage": "https://github.com/AllyMurray/http-client-toolkit#readme",
45
- "version": "0.0.1",
44
+ "version": "0.1.0",
46
45
  "bugs": {
47
46
  "url": "https://github.com/AllyMurray/http-client-toolkit/issues"
48
47
  },