@http-client-toolkit/store-memory 0.0.1 → 0.3.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.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import safeStringify from 'fast-safe-stringify';
2
2
  import { randomUUID } from 'crypto';
3
- import { DEFAULT_RATE_LIMIT, AdaptiveCapacityCalculator } from '@http-client-toolkit/core';
3
+ import {
4
+ DEFAULT_RATE_LIMIT,
5
+ AdaptiveCapacityCalculator,
6
+ } from '@http-client-toolkit/core';
4
7
 
5
8
  var __async = (__this, __arguments, generator) => {
6
9
  return new Promise((resolve, reject) => {
@@ -18,7 +21,10 @@ var __async = (__this, __arguments, generator) => {
18
21
  reject(e);
19
22
  }
20
23
  };
21
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
24
+ var step = (x) =>
25
+ x.done
26
+ ? resolve(x.value)
27
+ : Promise.resolve(x.value).then(fulfilled, rejected);
22
28
  step((generator = generator.apply(__this, __arguments)).next());
23
29
  });
24
30
  };
@@ -28,15 +34,17 @@ var InMemoryCacheStore = class {
28
34
  this.cache = /* @__PURE__ */ new Map();
29
35
  this.totalSize = 0;
30
36
  var _a, _b, _c, _d;
31
- const cleanupIntervalMs = (_a = options.cleanupIntervalMs) != null ? _a : 6e4;
37
+ const cleanupIntervalMs =
38
+ (_a = options.cleanupIntervalMs) != null ? _a : 6e4;
32
39
  this.maxItems = (_b = options.maxItems) != null ? _b : 1e3;
33
- this.maxMemoryBytes = (_c = options.maxMemoryBytes) != null ? _c : 50 * 1024 * 1024;
40
+ this.maxMemoryBytes =
41
+ (_c = options.maxMemoryBytes) != null ? _c : 50 * 1024 * 1024;
34
42
  this.evictionRatio = (_d = options.evictionRatio) != null ? _d : 0.1;
35
43
  if (cleanupIntervalMs > 0) {
36
44
  this.cleanupInterval = setInterval(() => {
37
45
  this.cleanup();
38
46
  }, cleanupIntervalMs);
39
- if (typeof this.cleanupInterval.unref === "function") {
47
+ if (typeof this.cleanupInterval.unref === 'function') {
40
48
  this.cleanupInterval.unref();
41
49
  }
42
50
  }
@@ -71,7 +79,7 @@ var InMemoryCacheStore = class {
71
79
  value,
72
80
  expiresAt,
73
81
  lastAccessed: now,
74
- size: entrySize
82
+ size: entrySize,
75
83
  });
76
84
  this.enforceMemoryLimits();
77
85
  });
@@ -110,7 +118,7 @@ var InMemoryCacheStore = class {
110
118
  maxItems: this.maxItems,
111
119
  maxMemoryBytes: this.maxMemoryBytes,
112
120
  memoryUtilization: memoryUsageBytes / this.maxMemoryBytes,
113
- itemUtilization: this.cache.size / this.maxItems
121
+ itemUtilization: this.cache.size / this.maxItems,
114
122
  };
115
123
  }
116
124
  /**
@@ -144,7 +152,7 @@ var InMemoryCacheStore = class {
144
152
  if (memoryUsage > this.maxMemoryBytes) {
145
153
  const itemsToEvict = Math.max(
146
154
  1,
147
- Math.floor(this.cache.size * this.evictionRatio)
155
+ Math.floor(this.cache.size * this.evictionRatio),
148
156
  );
149
157
  this.evictLRUItems(itemsToEvict);
150
158
  }
@@ -154,7 +162,7 @@ var InMemoryCacheStore = class {
154
162
  */
155
163
  evictLRUItems(count) {
156
164
  const entries = Array.from(this.cache.entries()).sort(
157
- ([, a], [, b]) => a.lastAccessed - b.lastAccessed
165
+ ([, a], [, b]) => a.lastAccessed - b.lastAccessed,
158
166
  );
159
167
  for (let i = 0; i < count && i < entries.length; i++) {
160
168
  const entry = entries[i];
@@ -175,11 +183,13 @@ var InMemoryCacheStore = class {
175
183
  * Get items sorted by last accessed time (for debugging/monitoring)
176
184
  */
177
185
  getLRUItems(limit = 10) {
178
- const entries = Array.from(this.cache.entries()).sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed).slice(0, limit);
186
+ const entries = Array.from(this.cache.entries())
187
+ .sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed)
188
+ .slice(0, limit);
179
189
  return entries.map(([hash, item]) => ({
180
190
  hash,
181
191
  lastAccessed: new Date(item.lastAccessed),
182
- size: item.size
192
+ size: item.size,
183
193
  }));
184
194
  }
185
195
  /**
@@ -202,7 +212,7 @@ var InMemoryCacheStore = class {
202
212
  size += hash.length * 2;
203
213
  size += 16;
204
214
  try {
205
- const json = (_a = safeStringify(value)) != null ? _a : "";
215
+ const json = (_a = safeStringify(value)) != null ? _a : '';
206
216
  size += json.length * 2;
207
217
  } catch (e) {
208
218
  size += 1024;
@@ -224,7 +234,7 @@ var InMemoryDedupeStore = class {
224
234
  /** Job timeout in milliseconds. Defaults to 5 minutes. */
225
235
  jobTimeoutMs = 3e5,
226
236
  /** Cleanup interval in milliseconds. Defaults to 1 minute. */
227
- cleanupIntervalMs = 6e4
237
+ cleanupIntervalMs = 6e4,
228
238
  } = {}) {
229
239
  this.jobs = /* @__PURE__ */ new Map();
230
240
  this.totalJobsProcessed = 0;
@@ -234,7 +244,7 @@ var InMemoryDedupeStore = class {
234
244
  this.cleanupInterval = setInterval(() => {
235
245
  this.cleanup();
236
246
  }, cleanupIntervalMs);
237
- if (typeof this.cleanupInterval.unref === "function") {
247
+ if (typeof this.cleanupInterval.unref === 'function') {
238
248
  this.cleanupInterval.unref();
239
249
  }
240
250
  }
@@ -249,7 +259,8 @@ var InMemoryDedupeStore = class {
249
259
  if (!job) {
250
260
  return void 0;
251
261
  }
252
- const jobTimedOut = this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
262
+ const jobTimedOut =
263
+ this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
253
264
  if (jobTimedOut) {
254
265
  this.cleanup();
255
266
  return void 0;
@@ -279,7 +290,7 @@ var InMemoryDedupeStore = class {
279
290
  if (existingJob) {
280
291
  return {
281
292
  jobId: existingJob.jobId,
282
- isOwner: false
293
+ isOwner: false,
283
294
  };
284
295
  }
285
296
  const jobId = randomUUID();
@@ -290,13 +301,13 @@ var InMemoryDedupeStore = class {
290
301
  resolve,
291
302
  reject,
292
303
  createdAt: Date.now(),
293
- completed: false
304
+ completed: false,
294
305
  };
295
306
  this.jobs.set(hash, job);
296
307
  this.totalJobsProcessed++;
297
308
  return {
298
309
  jobId,
299
- isOwner: true
310
+ isOwner: true,
300
311
  };
301
312
  });
302
313
  }
@@ -326,7 +337,8 @@ var InMemoryDedupeStore = class {
326
337
  if (!job) {
327
338
  return false;
328
339
  }
329
- const isJobExpired = this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
340
+ const isJobExpired =
341
+ this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
330
342
  if (isJobExpired) {
331
343
  this.cleanup();
332
344
  return false;
@@ -358,7 +370,7 @@ var InMemoryDedupeStore = class {
358
370
  activeJobs,
359
371
  totalJobsProcessed: this.totalJobsProcessed,
360
372
  expiredJobs,
361
- oldestJobAgeMs
373
+ oldestJobAgeMs,
362
374
  };
363
375
  }
364
376
  /**
@@ -375,7 +387,7 @@ var InMemoryDedupeStore = class {
375
387
  if (!job.completed) {
376
388
  job.completed = true;
377
389
  job.error = new Error(
378
- "Job timeout: Request took too long to complete"
390
+ 'Job timeout: Request took too long to complete',
379
391
  );
380
392
  job.reject(job.error);
381
393
  }
@@ -393,7 +405,7 @@ var InMemoryDedupeStore = class {
393
405
  for (const [_hash, job] of this.jobs) {
394
406
  if (!job.completed) {
395
407
  job.completed = true;
396
- job.error = new Error("DedupeStore cleared");
408
+ job.error = new Error('DedupeStore cleared');
397
409
  job.reject(job.error);
398
410
  }
399
411
  }
@@ -418,7 +430,7 @@ var InMemoryRateLimitStore = class {
418
430
  /** Optional per-resource overrides. */
419
431
  resourceConfigs = /* @__PURE__ */ new Map(),
420
432
  /** Cleanup interval in milliseconds. Defaults to 1 minute. */
421
- cleanupIntervalMs = 6e4
433
+ cleanupIntervalMs = 6e4,
422
434
  } = {}) {
423
435
  this.limits = /* @__PURE__ */ new Map();
424
436
  this.resourceConfigs = /* @__PURE__ */ new Map();
@@ -429,7 +441,7 @@ var InMemoryRateLimitStore = class {
429
441
  this.cleanupInterval = setInterval(() => {
430
442
  this.cleanup();
431
443
  }, cleanupIntervalMs);
432
- if (typeof this.cleanupInterval.unref === "function") {
444
+ if (typeof this.cleanupInterval.unref === 'function') {
433
445
  this.cleanupInterval.unref();
434
446
  }
435
447
  }
@@ -461,7 +473,7 @@ var InMemoryRateLimitStore = class {
461
473
  return {
462
474
  remaining: Math.max(0, info.limit - info.requests.length),
463
475
  resetTime: new Date(info.resetTime),
464
- limit: info.limit
476
+ limit: info.limit,
465
477
  };
466
478
  });
467
479
  }
@@ -471,7 +483,7 @@ var InMemoryRateLimitStore = class {
471
483
  if (info) {
472
484
  this.totalRequests = Math.max(
473
485
  0,
474
- this.totalRequests - info.requests.length
486
+ this.totalRequests - info.requests.length,
475
487
  );
476
488
  }
477
489
  this.limits.delete(resource);
@@ -492,7 +504,8 @@ var InMemoryRateLimitStore = class {
492
504
  if (oldestRequest === void 0) {
493
505
  return 0;
494
506
  }
495
- const timeUntilOldestExpires = oldestRequest + config.windowMs - Date.now();
507
+ const timeUntilOldestExpires =
508
+ oldestRequest + config.windowMs - Date.now();
496
509
  return Math.max(0, timeUntilOldestExpires);
497
510
  });
498
511
  }
@@ -528,7 +541,7 @@ var InMemoryRateLimitStore = class {
528
541
  totalResources: this.limits.size,
529
542
  activeResources,
530
543
  rateLimitedResources,
531
- totalRequests: this.totalRequests
544
+ totalRequests: this.totalRequests,
532
545
  };
533
546
  }
534
547
  /**
@@ -563,7 +576,7 @@ var InMemoryRateLimitStore = class {
563
576
  requests: [],
564
577
  limit: config.limit,
565
578
  windowMs: config.windowMs,
566
- resetTime: Date.now() + config.windowMs
579
+ resetTime: Date.now() + config.windowMs,
567
580
  };
568
581
  this.limits.set(resource, info);
569
582
  }
@@ -589,48 +602,51 @@ var AdaptiveRateLimitStore = class {
589
602
  this.lastCapacityUpdate = /* @__PURE__ */ new Map();
590
603
  this.cachedCapacity = /* @__PURE__ */ new Map();
591
604
  var _a, _b;
592
- this.defaultConfig = (_a = options.defaultConfig) != null ? _a : DEFAULT_RATE_LIMIT;
593
- this.resourceConfigs = (_b = options.resourceConfigs) != null ? _b : /* @__PURE__ */ new Map();
605
+ this.defaultConfig =
606
+ (_a = options.defaultConfig) != null ? _a : DEFAULT_RATE_LIMIT;
607
+ this.resourceConfigs =
608
+ (_b = options.resourceConfigs) != null ? _b : /* @__PURE__ */ new Map();
594
609
  this.capacityCalculator = new AdaptiveCapacityCalculator(
595
- options.adaptiveConfig
610
+ options.adaptiveConfig,
596
611
  );
597
612
  }
598
- canProceed(resource, priority = "background") {
613
+ canProceed(resource, priority = 'background') {
599
614
  return __async(this, null, function* () {
600
615
  const metrics = this.getOrCreateActivityMetrics(resource);
601
616
  const capacity = this.calculateCurrentCapacity(resource, metrics);
602
- if (priority === "background" && capacity.backgroundPaused) {
617
+ if (priority === 'background' && capacity.backgroundPaused) {
603
618
  return false;
604
619
  }
605
620
  const currentUserRequests = this.getCurrentUsage(
606
621
  resource,
607
- metrics.recentUserRequests
622
+ metrics.recentUserRequests,
608
623
  );
609
624
  const currentBackgroundRequests = this.getCurrentUsage(
610
625
  resource,
611
- metrics.recentBackgroundRequests
626
+ metrics.recentBackgroundRequests,
612
627
  );
613
- if (priority === "user") {
628
+ if (priority === 'user') {
614
629
  return currentUserRequests < capacity.userReserved;
615
630
  } else {
616
631
  return currentBackgroundRequests < capacity.backgroundMax;
617
632
  }
618
633
  });
619
634
  }
620
- record(resource, priority = "background") {
635
+ record(resource, priority = 'background') {
621
636
  return __async(this, null, function* () {
622
637
  const metrics = this.getOrCreateActivityMetrics(resource);
623
638
  const now = Date.now();
624
- if (priority === "user") {
639
+ if (priority === 'user') {
625
640
  metrics.recentUserRequests.push(now);
626
641
  this.cleanupOldRequests(metrics.recentUserRequests);
627
642
  } else {
628
643
  metrics.recentBackgroundRequests.push(now);
629
644
  this.cleanupOldRequests(metrics.recentBackgroundRequests);
630
645
  }
631
- metrics.userActivityTrend = this.capacityCalculator.calculateActivityTrend(
632
- metrics.recentUserRequests
633
- );
646
+ metrics.userActivityTrend =
647
+ this.capacityCalculator.calculateActivityTrend(
648
+ metrics.recentUserRequests,
649
+ );
634
650
  });
635
651
  }
636
652
  getStatus(resource) {
@@ -640,15 +656,21 @@ var AdaptiveRateLimitStore = class {
640
656
  const capacity = this.calculateCurrentCapacity(resource, metrics);
641
657
  const currentUserUsage = this.getCurrentUsage(
642
658
  resource,
643
- metrics.recentUserRequests
659
+ metrics.recentUserRequests,
644
660
  );
645
661
  const currentBackgroundUsage = this.getCurrentUsage(
646
662
  resource,
647
- metrics.recentBackgroundRequests
663
+ metrics.recentBackgroundRequests,
648
664
  );
649
- const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
665
+ const config =
666
+ (_a = this.resourceConfigs.get(resource)) != null
667
+ ? _a
668
+ : this.defaultConfig;
650
669
  return {
651
- remaining: capacity.userReserved - currentUserUsage + (capacity.backgroundMax - currentBackgroundUsage),
670
+ remaining:
671
+ capacity.userReserved -
672
+ currentUserUsage +
673
+ (capacity.backgroundMax - currentBackgroundUsage),
652
674
  resetTime: new Date(Date.now() + config.windowMs),
653
675
  limit: this.getResourceLimit(resource),
654
676
  adaptive: {
@@ -656,10 +678,10 @@ var AdaptiveRateLimitStore = class {
656
678
  backgroundMax: capacity.backgroundMax,
657
679
  backgroundPaused: capacity.backgroundPaused,
658
680
  recentUserActivity: this.capacityCalculator.getRecentActivity(
659
- metrics.recentUserRequests
681
+ metrics.recentUserRequests,
660
682
  ),
661
- reason: capacity.reason
662
- }
683
+ reason: capacity.reason,
684
+ },
663
685
  };
664
686
  });
665
687
  }
@@ -670,7 +692,7 @@ var AdaptiveRateLimitStore = class {
670
692
  this.lastCapacityUpdate.delete(resource);
671
693
  });
672
694
  }
673
- getWaitTime(resource, priority = "background") {
695
+ getWaitTime(resource, priority = 'background') {
674
696
  return __async(this, null, function* () {
675
697
  const canProceed = yield this.canProceed(resource, priority);
676
698
  if (canProceed) {
@@ -678,13 +700,18 @@ var AdaptiveRateLimitStore = class {
678
700
  }
679
701
  const metrics = this.getOrCreateActivityMetrics(resource);
680
702
  const capacity = this.calculateCurrentCapacity(resource, metrics);
681
- if (priority === "background" && capacity.backgroundPaused) {
703
+ if (priority === 'background' && capacity.backgroundPaused) {
682
704
  const lastUpdate = this.lastCapacityUpdate.get(resource) || 0;
683
- const nextUpdate = lastUpdate + this.capacityCalculator.config.recalculationIntervalMs;
705
+ const nextUpdate =
706
+ lastUpdate + this.capacityCalculator.config.recalculationIntervalMs;
684
707
  return Math.max(0, nextUpdate - Date.now());
685
708
  }
686
- const monitoringWindow = this.capacityCalculator.config.monitoringWindowMs;
687
- const requests = priority === "user" ? metrics.recentUserRequests : metrics.recentBackgroundRequests;
709
+ const monitoringWindow =
710
+ this.capacityCalculator.config.monitoringWindowMs;
711
+ const requests =
712
+ priority === 'user'
713
+ ? metrics.recentUserRequests
714
+ : metrics.recentBackgroundRequests;
688
715
  if (requests.length === 0) {
689
716
  return 0;
690
717
  }
@@ -695,15 +722,18 @@ var AdaptiveRateLimitStore = class {
695
722
  }
696
723
  calculateCurrentCapacity(resource, metrics) {
697
724
  const lastUpdate = this.lastCapacityUpdate.get(resource) || 0;
698
- const recalcInterval = this.capacityCalculator.config.recalculationIntervalMs;
725
+ const recalcInterval =
726
+ this.capacityCalculator.config.recalculationIntervalMs;
699
727
  if (Date.now() - lastUpdate < recalcInterval) {
700
- return this.cachedCapacity.get(resource) || this.getDefaultCapacity(resource);
728
+ return (
729
+ this.cachedCapacity.get(resource) || this.getDefaultCapacity(resource)
730
+ );
701
731
  }
702
732
  const totalLimit = this.getResourceLimit(resource);
703
733
  const capacity = this.capacityCalculator.calculateDynamicCapacity(
704
734
  resource,
705
735
  totalLimit,
706
- metrics
736
+ metrics,
707
737
  );
708
738
  this.cachedCapacity.set(resource, capacity);
709
739
  this.lastCapacityUpdate.set(resource, Date.now());
@@ -714,26 +744,33 @@ var AdaptiveRateLimitStore = class {
714
744
  this.activityMetrics.set(resource, {
715
745
  recentUserRequests: [],
716
746
  recentBackgroundRequests: [],
717
- userActivityTrend: "none"
747
+ userActivityTrend: 'none',
718
748
  });
719
749
  }
720
750
  return this.activityMetrics.get(resource);
721
751
  }
722
752
  getCurrentUsage(resource, requests) {
723
753
  var _a;
724
- const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
754
+ const config =
755
+ (_a = this.resourceConfigs.get(resource)) != null
756
+ ? _a
757
+ : this.defaultConfig;
725
758
  const windowStart = Date.now() - config.windowMs;
726
759
  return requests.filter((timestamp) => timestamp > windowStart).length;
727
760
  }
728
761
  cleanupOldRequests(requests) {
729
- const cutoff = Date.now() - this.capacityCalculator.config.monitoringWindowMs;
762
+ const cutoff =
763
+ Date.now() - this.capacityCalculator.config.monitoringWindowMs;
730
764
  while (requests.length > 0 && requests[0] < cutoff) {
731
765
  requests.shift();
732
766
  }
733
767
  }
734
768
  getResourceLimit(resource) {
735
769
  var _a;
736
- const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
770
+ const config =
771
+ (_a = this.resourceConfigs.get(resource)) != null
772
+ ? _a
773
+ : this.defaultConfig;
737
774
  return config.limit;
738
775
  }
739
776
  getDefaultCapacity(resource) {
@@ -742,11 +779,16 @@ var AdaptiveRateLimitStore = class {
742
779
  userReserved: Math.floor(totalLimit * 0.3),
743
780
  backgroundMax: Math.floor(totalLimit * 0.7),
744
781
  backgroundPaused: false,
745
- reason: "Default capacity allocation"
782
+ reason: 'Default capacity allocation',
746
783
  };
747
784
  }
748
785
  };
749
786
 
750
- export { AdaptiveRateLimitStore, InMemoryCacheStore, InMemoryDedupeStore, InMemoryRateLimitStore };
787
+ export {
788
+ AdaptiveRateLimitStore,
789
+ InMemoryCacheStore,
790
+ InMemoryDedupeStore,
791
+ InMemoryRateLimitStore,
792
+ };
793
+ //# sourceMappingURL=index.js.map
751
794
  //# sourceMappingURL=index.js.map
752
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "fast-safe-stringify": "^2.1.1",
27
27
  "zod": "^3.24.1",
28
- "@http-client-toolkit/core": "0.0.1"
28
+ "@http-client-toolkit/core": "0.3.0"
29
29
  },
30
30
  "keywords": [
31
31
  "http-client",
@@ -44,7 +44,7 @@
44
44
  "lib/**"
45
45
  ],
46
46
  "homepage": "https://github.com/AllyMurray/http-client-toolkit#readme",
47
- "version": "0.0.1",
47
+ "version": "0.3.0",
48
48
  "bugs": {
49
49
  "url": "https://github.com/AllyMurray/http-client-toolkit/issues"
50
50
  },