@http-client-toolkit/store-memory 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,752 @@
1
+ import safeStringify from 'fast-safe-stringify';
2
+ import { randomUUID } from 'crypto';
3
+ import { DEFAULT_RATE_LIMIT, AdaptiveCapacityCalculator } from '@http-client-toolkit/core';
4
+
5
+ var __async = (__this, __arguments, generator) => {
6
+ return new Promise((resolve, reject) => {
7
+ var fulfilled = (value) => {
8
+ try {
9
+ step(generator.next(value));
10
+ } catch (e) {
11
+ reject(e);
12
+ }
13
+ };
14
+ var rejected = (value) => {
15
+ try {
16
+ step(generator.throw(value));
17
+ } catch (e) {
18
+ reject(e);
19
+ }
20
+ };
21
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
22
+ step((generator = generator.apply(__this, __arguments)).next());
23
+ });
24
+ };
25
+ var InMemoryCacheStore = class {
26
+ // running total of memory usage in bytes
27
+ constructor(options = {}) {
28
+ this.cache = /* @__PURE__ */ new Map();
29
+ this.totalSize = 0;
30
+ var _a, _b, _c, _d;
31
+ const cleanupIntervalMs = (_a = options.cleanupIntervalMs) != null ? _a : 6e4;
32
+ this.maxItems = (_b = options.maxItems) != null ? _b : 1e3;
33
+ this.maxMemoryBytes = (_c = options.maxMemoryBytes) != null ? _c : 50 * 1024 * 1024;
34
+ this.evictionRatio = (_d = options.evictionRatio) != null ? _d : 0.1;
35
+ if (cleanupIntervalMs > 0) {
36
+ this.cleanupInterval = setInterval(() => {
37
+ this.cleanup();
38
+ }, cleanupIntervalMs);
39
+ if (typeof this.cleanupInterval.unref === "function") {
40
+ this.cleanupInterval.unref();
41
+ }
42
+ }
43
+ }
44
+ get(hash) {
45
+ return __async(this, null, function* () {
46
+ const item = this.cache.get(hash);
47
+ if (!item) {
48
+ return void 0;
49
+ }
50
+ if (item.expiresAt > 0 && Date.now() > item.expiresAt) {
51
+ this.totalSize -= item.size;
52
+ this.cache.delete(hash);
53
+ return void 0;
54
+ }
55
+ item.lastAccessed = Date.now();
56
+ this.cache.set(hash, item);
57
+ return item.value;
58
+ });
59
+ }
60
+ set(hash, value, ttlSeconds) {
61
+ return __async(this, null, function* () {
62
+ const now = Date.now();
63
+ const expiresAt = ttlSeconds === 0 ? 0 : now + ttlSeconds * 1e3;
64
+ const existing = this.cache.get(hash);
65
+ if (existing) {
66
+ this.totalSize -= existing.size;
67
+ }
68
+ const entrySize = this.estimateEntrySize(hash, value);
69
+ this.totalSize += entrySize;
70
+ this.cache.set(hash, {
71
+ value,
72
+ expiresAt,
73
+ lastAccessed: now,
74
+ size: entrySize
75
+ });
76
+ this.enforceMemoryLimits();
77
+ });
78
+ }
79
+ delete(hash) {
80
+ return __async(this, null, function* () {
81
+ const existing = this.cache.get(hash);
82
+ if (existing) {
83
+ this.totalSize -= existing.size;
84
+ }
85
+ this.cache.delete(hash);
86
+ });
87
+ }
88
+ clear() {
89
+ return __async(this, null, function* () {
90
+ this.cache.clear();
91
+ this.totalSize = 0;
92
+ });
93
+ }
94
+ /**
95
+ * Get statistics about cache usage
96
+ */
97
+ getStats() {
98
+ const now = Date.now();
99
+ let expired = 0;
100
+ for (const [_hash, item] of this.cache) {
101
+ if (item.expiresAt > 0 && now > item.expiresAt) {
102
+ expired++;
103
+ }
104
+ }
105
+ const memoryUsageBytes = this.calculateMemoryUsage();
106
+ return {
107
+ totalItems: this.cache.size,
108
+ expired,
109
+ memoryUsageBytes,
110
+ maxItems: this.maxItems,
111
+ maxMemoryBytes: this.maxMemoryBytes,
112
+ memoryUtilization: memoryUsageBytes / this.maxMemoryBytes,
113
+ itemUtilization: this.cache.size / this.maxItems
114
+ };
115
+ }
116
+ /**
117
+ * Clean up expired cache entries
118
+ */
119
+ cleanup() {
120
+ const now = Date.now();
121
+ const toDelete = [];
122
+ for (const [hash, item] of this.cache) {
123
+ if (item.expiresAt > 0 && now > item.expiresAt) {
124
+ toDelete.push(hash);
125
+ }
126
+ }
127
+ for (const hash of toDelete) {
128
+ const item = this.cache.get(hash);
129
+ if (item) {
130
+ this.totalSize -= item.size;
131
+ }
132
+ this.cache.delete(hash);
133
+ }
134
+ }
135
+ /**
136
+ * Enforce memory and item count limits using LRU eviction
137
+ */
138
+ enforceMemoryLimits() {
139
+ if (this.cache.size > this.maxItems) {
140
+ this.evictLRUItems(this.cache.size - this.maxItems);
141
+ return;
142
+ }
143
+ const memoryUsage = this.totalSize;
144
+ if (memoryUsage > this.maxMemoryBytes) {
145
+ const itemsToEvict = Math.max(
146
+ 1,
147
+ Math.floor(this.cache.size * this.evictionRatio)
148
+ );
149
+ this.evictLRUItems(itemsToEvict);
150
+ }
151
+ }
152
+ /**
153
+ * Evict the least recently used items
154
+ */
155
+ evictLRUItems(count) {
156
+ const entries = Array.from(this.cache.entries()).sort(
157
+ ([, a], [, b]) => a.lastAccessed - b.lastAccessed
158
+ );
159
+ for (let i = 0; i < count && i < entries.length; i++) {
160
+ const entry = entries[i];
161
+ if (entry) {
162
+ const [key, item] = entry;
163
+ this.totalSize -= item.size;
164
+ this.cache.delete(key);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Calculate rough memory usage
170
+ */
171
+ calculateMemoryUsage() {
172
+ return this.totalSize;
173
+ }
174
+ /**
175
+ * Get items sorted by last accessed time (for debugging/monitoring)
176
+ */
177
+ getLRUItems(limit = 10) {
178
+ const entries = Array.from(this.cache.entries()).sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed).slice(0, limit);
179
+ return entries.map(([hash, item]) => ({
180
+ hash,
181
+ lastAccessed: new Date(item.lastAccessed),
182
+ size: item.size
183
+ }));
184
+ }
185
+ /**
186
+ * Destroy the cache and cleanup resources
187
+ */
188
+ destroy() {
189
+ if (this.cleanupInterval) {
190
+ clearInterval(this.cleanupInterval);
191
+ this.cleanupInterval = void 0;
192
+ }
193
+ this.cache.clear();
194
+ this.totalSize = 0;
195
+ }
196
+ /**
197
+ * Estimate the size in bytes of a cache entry (key + value + metadata)
198
+ */
199
+ estimateEntrySize(hash, value) {
200
+ var _a;
201
+ let size = 0;
202
+ size += hash.length * 2;
203
+ size += 16;
204
+ try {
205
+ const json = (_a = safeStringify(value)) != null ? _a : "";
206
+ size += json.length * 2;
207
+ } catch (e) {
208
+ size += 1024;
209
+ }
210
+ return size;
211
+ }
212
+ };
213
+ function deferred() {
214
+ let resolve;
215
+ let reject;
216
+ const promise = new Promise((res, rej) => {
217
+ resolve = res;
218
+ reject = rej;
219
+ });
220
+ return { promise, resolve, reject };
221
+ }
222
+ var InMemoryDedupeStore = class {
223
+ constructor({
224
+ /** Job timeout in milliseconds. Defaults to 5 minutes. */
225
+ jobTimeoutMs = 3e5,
226
+ /** Cleanup interval in milliseconds. Defaults to 1 minute. */
227
+ cleanupIntervalMs = 6e4
228
+ } = {}) {
229
+ this.jobs = /* @__PURE__ */ new Map();
230
+ this.totalJobsProcessed = 0;
231
+ this.destroyed = false;
232
+ this.jobTimeoutMs = jobTimeoutMs;
233
+ if (cleanupIntervalMs > 0) {
234
+ this.cleanupInterval = setInterval(() => {
235
+ this.cleanup();
236
+ }, cleanupIntervalMs);
237
+ if (typeof this.cleanupInterval.unref === "function") {
238
+ this.cleanupInterval.unref();
239
+ }
240
+ }
241
+ }
242
+ waitFor(hash) {
243
+ return __async(this, null, function* () {
244
+ var _a;
245
+ if (this.destroyed) {
246
+ return void 0;
247
+ }
248
+ const job = this.jobs.get(hash);
249
+ if (!job) {
250
+ return void 0;
251
+ }
252
+ const jobTimedOut = this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
253
+ if (jobTimedOut) {
254
+ this.cleanup();
255
+ return void 0;
256
+ }
257
+ if (job.completed) {
258
+ if (job.error) {
259
+ return void 0;
260
+ }
261
+ return (_a = job.result) != null ? _a : void 0;
262
+ }
263
+ try {
264
+ return yield job.promise;
265
+ } catch (e) {
266
+ return void 0;
267
+ }
268
+ });
269
+ }
270
+ register(hash) {
271
+ return __async(this, null, function* () {
272
+ const registration = yield this.registerOrJoin(hash);
273
+ return registration.jobId;
274
+ });
275
+ }
276
+ registerOrJoin(hash) {
277
+ return __async(this, null, function* () {
278
+ const existingJob = this.jobs.get(hash);
279
+ if (existingJob) {
280
+ return {
281
+ jobId: existingJob.jobId,
282
+ isOwner: false
283
+ };
284
+ }
285
+ const jobId = randomUUID();
286
+ const { promise, resolve, reject } = deferred();
287
+ const job = {
288
+ jobId,
289
+ promise,
290
+ resolve,
291
+ reject,
292
+ createdAt: Date.now(),
293
+ completed: false
294
+ };
295
+ this.jobs.set(hash, job);
296
+ this.totalJobsProcessed++;
297
+ return {
298
+ jobId,
299
+ isOwner: true
300
+ };
301
+ });
302
+ }
303
+ complete(hash, value) {
304
+ return __async(this, null, function* () {
305
+ const job = this.jobs.get(hash);
306
+ if (job && !job.completed) {
307
+ job.completed = true;
308
+ job.result = value;
309
+ job.resolve(value);
310
+ }
311
+ });
312
+ }
313
+ fail(hash, error) {
314
+ return __async(this, null, function* () {
315
+ const job = this.jobs.get(hash);
316
+ if (job && !job.completed) {
317
+ job.completed = true;
318
+ job.error = error;
319
+ job.reject(error);
320
+ }
321
+ });
322
+ }
323
+ isInProgress(hash) {
324
+ return __async(this, null, function* () {
325
+ const job = this.jobs.get(hash);
326
+ if (!job) {
327
+ return false;
328
+ }
329
+ const isJobExpired = this.jobTimeoutMs > 0 && Date.now() - job.createdAt > this.jobTimeoutMs;
330
+ if (isJobExpired) {
331
+ this.cleanup();
332
+ return false;
333
+ }
334
+ return !job.completed;
335
+ });
336
+ }
337
+ /**
338
+ * Get statistics about current dedupe jobs
339
+ */
340
+ getStats() {
341
+ const now = Date.now();
342
+ let expiredJobs = 0;
343
+ let oldestJobAgeMs = 0;
344
+ let activeJobs = 0;
345
+ for (const [_hash, job] of this.jobs) {
346
+ const ageMs = now - job.createdAt;
347
+ if (this.jobTimeoutMs > 0 && ageMs > this.jobTimeoutMs) {
348
+ expiredJobs++;
349
+ }
350
+ if (ageMs > oldestJobAgeMs) {
351
+ oldestJobAgeMs = ageMs;
352
+ }
353
+ if (!job.completed) {
354
+ activeJobs++;
355
+ }
356
+ }
357
+ return {
358
+ activeJobs,
359
+ totalJobsProcessed: this.totalJobsProcessed,
360
+ expiredJobs,
361
+ oldestJobAgeMs
362
+ };
363
+ }
364
+ /**
365
+ * Clean up expired jobs
366
+ */
367
+ cleanup() {
368
+ if (this.jobTimeoutMs <= 0) {
369
+ return;
370
+ }
371
+ const now = Date.now();
372
+ const toDelete = [];
373
+ for (const [hash, job] of this.jobs) {
374
+ if (now - job.createdAt > this.jobTimeoutMs) {
375
+ if (!job.completed) {
376
+ job.completed = true;
377
+ job.error = new Error(
378
+ "Job timeout: Request took too long to complete"
379
+ );
380
+ job.reject(job.error);
381
+ }
382
+ toDelete.push(hash);
383
+ }
384
+ }
385
+ for (const hash of toDelete) {
386
+ this.jobs.delete(hash);
387
+ }
388
+ }
389
+ /**
390
+ * Clear all jobs
391
+ */
392
+ clear() {
393
+ for (const [_hash, job] of this.jobs) {
394
+ if (!job.completed) {
395
+ job.completed = true;
396
+ job.error = new Error("DedupeStore cleared");
397
+ job.reject(job.error);
398
+ }
399
+ }
400
+ this.jobs.clear();
401
+ }
402
+ /**
403
+ * Destroy the store and clean up resources
404
+ */
405
+ destroy() {
406
+ if (this.cleanupInterval) {
407
+ clearInterval(this.cleanupInterval);
408
+ this.cleanupInterval = void 0;
409
+ }
410
+ this.clear();
411
+ this.destroyed = true;
412
+ }
413
+ };
414
+ var InMemoryRateLimitStore = class {
415
+ constructor({
416
+ /** Global/default rate-limit config applied when a resource-specific override is not provided. */
417
+ defaultConfig = DEFAULT_RATE_LIMIT,
418
+ /** Optional per-resource overrides. */
419
+ resourceConfigs = /* @__PURE__ */ new Map(),
420
+ /** Cleanup interval in milliseconds. Defaults to 1 minute. */
421
+ cleanupIntervalMs = 6e4
422
+ } = {}) {
423
+ this.limits = /* @__PURE__ */ new Map();
424
+ this.resourceConfigs = /* @__PURE__ */ new Map();
425
+ this.totalRequests = 0;
426
+ this.defaultConfig = defaultConfig;
427
+ this.resourceConfigs = resourceConfigs;
428
+ if (cleanupIntervalMs > 0) {
429
+ this.cleanupInterval = setInterval(() => {
430
+ this.cleanup();
431
+ }, cleanupIntervalMs);
432
+ if (typeof this.cleanupInterval.unref === "function") {
433
+ this.cleanupInterval.unref();
434
+ }
435
+ }
436
+ }
437
+ canProceed(resource) {
438
+ return __async(this, null, function* () {
439
+ const config = this.resourceConfigs.get(resource) || this.defaultConfig;
440
+ const info = this.getOrCreateRateLimitInfo(resource, config);
441
+ this.cleanupExpiredRequests(info);
442
+ return info.requests.length < info.limit;
443
+ });
444
+ }
445
+ record(resource) {
446
+ return __async(this, null, function* () {
447
+ const config = this.resourceConfigs.get(resource) || this.defaultConfig;
448
+ const info = this.getOrCreateRateLimitInfo(resource, config);
449
+ this.cleanupExpiredRequests(info);
450
+ const now = Date.now();
451
+ info.requests.push(now);
452
+ this.totalRequests++;
453
+ info.resetTime = now + config.windowMs;
454
+ });
455
+ }
456
+ getStatus(resource) {
457
+ return __async(this, null, function* () {
458
+ const config = this.resourceConfigs.get(resource) || this.defaultConfig;
459
+ const info = this.getOrCreateRateLimitInfo(resource, config);
460
+ this.cleanupExpiredRequests(info);
461
+ return {
462
+ remaining: Math.max(0, info.limit - info.requests.length),
463
+ resetTime: new Date(info.resetTime),
464
+ limit: info.limit
465
+ };
466
+ });
467
+ }
468
+ reset(resource) {
469
+ return __async(this, null, function* () {
470
+ const info = this.limits.get(resource);
471
+ if (info) {
472
+ this.totalRequests = Math.max(
473
+ 0,
474
+ this.totalRequests - info.requests.length
475
+ );
476
+ }
477
+ this.limits.delete(resource);
478
+ });
479
+ }
480
+ getWaitTime(resource) {
481
+ return __async(this, null, function* () {
482
+ const config = this.resourceConfigs.get(resource) || this.defaultConfig;
483
+ const info = this.getOrCreateRateLimitInfo(resource, config);
484
+ this.cleanupExpiredRequests(info);
485
+ if (info.limit === 0) {
486
+ return Math.max(0, info.resetTime - Date.now());
487
+ }
488
+ if (info.requests.length < info.limit) {
489
+ return 0;
490
+ }
491
+ const oldestRequest = info.requests[0];
492
+ if (oldestRequest === void 0) {
493
+ return 0;
494
+ }
495
+ const timeUntilOldestExpires = oldestRequest + config.windowMs - Date.now();
496
+ return Math.max(0, timeUntilOldestExpires);
497
+ });
498
+ }
499
+ /**
500
+ * Set rate limit configuration for a specific resource
501
+ */
502
+ setResourceConfig(resource, config) {
503
+ this.resourceConfigs.set(resource, config);
504
+ this.limits.delete(resource);
505
+ }
506
+ /**
507
+ * Get rate limit configuration for a resource
508
+ */
509
+ getResourceConfig(resource) {
510
+ return this.resourceConfigs.get(resource) || this.defaultConfig;
511
+ }
512
+ /**
513
+ * Get statistics for all resources
514
+ */
515
+ getStats() {
516
+ let activeResources = 0;
517
+ let rateLimitedResources = 0;
518
+ for (const [_resource, info] of this.limits) {
519
+ this.cleanupExpiredRequests(info);
520
+ if (info.requests.length > 0) {
521
+ activeResources++;
522
+ if (info.requests.length >= info.limit) {
523
+ rateLimitedResources++;
524
+ }
525
+ }
526
+ }
527
+ return {
528
+ totalResources: this.limits.size,
529
+ activeResources,
530
+ rateLimitedResources,
531
+ totalRequests: this.totalRequests
532
+ };
533
+ }
534
+ /**
535
+ * Clear all rate limit data
536
+ */
537
+ clear() {
538
+ this.limits.clear();
539
+ this.totalRequests = 0;
540
+ }
541
+ /**
542
+ * Clean up expired requests for all resources
543
+ */
544
+ cleanup() {
545
+ for (const [_resource, info] of this.limits) {
546
+ this.cleanupExpiredRequests(info);
547
+ }
548
+ }
549
+ /**
550
+ * Destroy the store and clean up resources
551
+ */
552
+ destroy() {
553
+ if (this.cleanupInterval) {
554
+ clearInterval(this.cleanupInterval);
555
+ this.cleanupInterval = void 0;
556
+ }
557
+ this.clear();
558
+ }
559
+ getOrCreateRateLimitInfo(resource, config) {
560
+ let info = this.limits.get(resource);
561
+ if (!info) {
562
+ info = {
563
+ requests: [],
564
+ limit: config.limit,
565
+ windowMs: config.windowMs,
566
+ resetTime: Date.now() + config.windowMs
567
+ };
568
+ this.limits.set(resource, info);
569
+ }
570
+ return info;
571
+ }
572
+ cleanupExpiredRequests(info) {
573
+ const now = Date.now();
574
+ const cutoff = now - info.windowMs;
575
+ const initialLength = info.requests.length;
576
+ while (info.requests.length > 0 && info.requests[0] < cutoff) {
577
+ info.requests.shift();
578
+ }
579
+ const expiredCount = initialLength - info.requests.length;
580
+ this.totalRequests = Math.max(0, this.totalRequests - expiredCount);
581
+ if (info.requests.length === 0) {
582
+ info.resetTime = now + info.windowMs;
583
+ }
584
+ }
585
+ };
586
+ var AdaptiveRateLimitStore = class {
587
+ constructor(options = {}) {
588
+ this.activityMetrics = /* @__PURE__ */ new Map();
589
+ this.lastCapacityUpdate = /* @__PURE__ */ new Map();
590
+ this.cachedCapacity = /* @__PURE__ */ new Map();
591
+ 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();
594
+ this.capacityCalculator = new AdaptiveCapacityCalculator(
595
+ options.adaptiveConfig
596
+ );
597
+ }
598
+ canProceed(resource, priority = "background") {
599
+ return __async(this, null, function* () {
600
+ const metrics = this.getOrCreateActivityMetrics(resource);
601
+ const capacity = this.calculateCurrentCapacity(resource, metrics);
602
+ if (priority === "background" && capacity.backgroundPaused) {
603
+ return false;
604
+ }
605
+ const currentUserRequests = this.getCurrentUsage(
606
+ resource,
607
+ metrics.recentUserRequests
608
+ );
609
+ const currentBackgroundRequests = this.getCurrentUsage(
610
+ resource,
611
+ metrics.recentBackgroundRequests
612
+ );
613
+ if (priority === "user") {
614
+ return currentUserRequests < capacity.userReserved;
615
+ } else {
616
+ return currentBackgroundRequests < capacity.backgroundMax;
617
+ }
618
+ });
619
+ }
620
+ record(resource, priority = "background") {
621
+ return __async(this, null, function* () {
622
+ const metrics = this.getOrCreateActivityMetrics(resource);
623
+ const now = Date.now();
624
+ if (priority === "user") {
625
+ metrics.recentUserRequests.push(now);
626
+ this.cleanupOldRequests(metrics.recentUserRequests);
627
+ } else {
628
+ metrics.recentBackgroundRequests.push(now);
629
+ this.cleanupOldRequests(metrics.recentBackgroundRequests);
630
+ }
631
+ metrics.userActivityTrend = this.capacityCalculator.calculateActivityTrend(
632
+ metrics.recentUserRequests
633
+ );
634
+ });
635
+ }
636
+ getStatus(resource) {
637
+ return __async(this, null, function* () {
638
+ var _a;
639
+ const metrics = this.getOrCreateActivityMetrics(resource);
640
+ const capacity = this.calculateCurrentCapacity(resource, metrics);
641
+ const currentUserUsage = this.getCurrentUsage(
642
+ resource,
643
+ metrics.recentUserRequests
644
+ );
645
+ const currentBackgroundUsage = this.getCurrentUsage(
646
+ resource,
647
+ metrics.recentBackgroundRequests
648
+ );
649
+ const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
650
+ return {
651
+ remaining: capacity.userReserved - currentUserUsage + (capacity.backgroundMax - currentBackgroundUsage),
652
+ resetTime: new Date(Date.now() + config.windowMs),
653
+ limit: this.getResourceLimit(resource),
654
+ adaptive: {
655
+ userReserved: capacity.userReserved,
656
+ backgroundMax: capacity.backgroundMax,
657
+ backgroundPaused: capacity.backgroundPaused,
658
+ recentUserActivity: this.capacityCalculator.getRecentActivity(
659
+ metrics.recentUserRequests
660
+ ),
661
+ reason: capacity.reason
662
+ }
663
+ };
664
+ });
665
+ }
666
+ reset(resource) {
667
+ return __async(this, null, function* () {
668
+ this.activityMetrics.delete(resource);
669
+ this.cachedCapacity.delete(resource);
670
+ this.lastCapacityUpdate.delete(resource);
671
+ });
672
+ }
673
+ getWaitTime(resource, priority = "background") {
674
+ return __async(this, null, function* () {
675
+ const canProceed = yield this.canProceed(resource, priority);
676
+ if (canProceed) {
677
+ return 0;
678
+ }
679
+ const metrics = this.getOrCreateActivityMetrics(resource);
680
+ const capacity = this.calculateCurrentCapacity(resource, metrics);
681
+ if (priority === "background" && capacity.backgroundPaused) {
682
+ const lastUpdate = this.lastCapacityUpdate.get(resource) || 0;
683
+ const nextUpdate = lastUpdate + this.capacityCalculator.config.recalculationIntervalMs;
684
+ return Math.max(0, nextUpdate - Date.now());
685
+ }
686
+ const monitoringWindow = this.capacityCalculator.config.monitoringWindowMs;
687
+ const requests = priority === "user" ? metrics.recentUserRequests : metrics.recentBackgroundRequests;
688
+ if (requests.length === 0) {
689
+ return 0;
690
+ }
691
+ const oldestRequest = Math.min(...requests);
692
+ const waitTime = oldestRequest + monitoringWindow - Date.now();
693
+ return Math.max(0, waitTime);
694
+ });
695
+ }
696
+ calculateCurrentCapacity(resource, metrics) {
697
+ const lastUpdate = this.lastCapacityUpdate.get(resource) || 0;
698
+ const recalcInterval = this.capacityCalculator.config.recalculationIntervalMs;
699
+ if (Date.now() - lastUpdate < recalcInterval) {
700
+ return this.cachedCapacity.get(resource) || this.getDefaultCapacity(resource);
701
+ }
702
+ const totalLimit = this.getResourceLimit(resource);
703
+ const capacity = this.capacityCalculator.calculateDynamicCapacity(
704
+ resource,
705
+ totalLimit,
706
+ metrics
707
+ );
708
+ this.cachedCapacity.set(resource, capacity);
709
+ this.lastCapacityUpdate.set(resource, Date.now());
710
+ return capacity;
711
+ }
712
+ getOrCreateActivityMetrics(resource) {
713
+ if (!this.activityMetrics.has(resource)) {
714
+ this.activityMetrics.set(resource, {
715
+ recentUserRequests: [],
716
+ recentBackgroundRequests: [],
717
+ userActivityTrend: "none"
718
+ });
719
+ }
720
+ return this.activityMetrics.get(resource);
721
+ }
722
+ getCurrentUsage(resource, requests) {
723
+ var _a;
724
+ const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
725
+ const windowStart = Date.now() - config.windowMs;
726
+ return requests.filter((timestamp) => timestamp > windowStart).length;
727
+ }
728
+ cleanupOldRequests(requests) {
729
+ const cutoff = Date.now() - this.capacityCalculator.config.monitoringWindowMs;
730
+ while (requests.length > 0 && requests[0] < cutoff) {
731
+ requests.shift();
732
+ }
733
+ }
734
+ getResourceLimit(resource) {
735
+ var _a;
736
+ const config = (_a = this.resourceConfigs.get(resource)) != null ? _a : this.defaultConfig;
737
+ return config.limit;
738
+ }
739
+ getDefaultCapacity(resource) {
740
+ const totalLimit = this.getResourceLimit(resource);
741
+ return {
742
+ userReserved: Math.floor(totalLimit * 0.3),
743
+ backgroundMax: Math.floor(totalLimit * 0.7),
744
+ backgroundPaused: false,
745
+ reason: "Default capacity allocation"
746
+ };
747
+ }
748
+ };
749
+
750
+ export { AdaptiveRateLimitStore, InMemoryCacheStore, InMemoryDedupeStore, InMemoryRateLimitStore };
751
+ //# sourceMappingURL=index.js.map
752
+ //# sourceMappingURL=index.js.map