@zeitar/throttle 1.0.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/LICENSE.md +24 -0
- package/README.md +204 -0
- package/dist/CompoundLimiter.d.ts +33 -0
- package/dist/CompoundLimiter.d.ts.map +1 -0
- package/dist/CompoundLimiter.js +62 -0
- package/dist/CompoundLimiter.js.map +1 -0
- package/dist/CompoundRateLimiterFactory.d.ts +19 -0
- package/dist/CompoundRateLimiterFactory.d.ts.map +1 -0
- package/dist/CompoundRateLimiterFactory.js +29 -0
- package/dist/CompoundRateLimiterFactory.js.map +1 -0
- package/dist/LimiterInterface.d.ts +32 -0
- package/dist/LimiterInterface.d.ts.map +1 -0
- package/dist/LimiterInterface.js +3 -0
- package/dist/LimiterInterface.js.map +1 -0
- package/dist/LimiterStateInterface.d.ts +16 -0
- package/dist/LimiterStateInterface.d.ts.map +1 -0
- package/dist/LimiterStateInterface.js +3 -0
- package/dist/LimiterStateInterface.js.map +1 -0
- package/dist/RateLimit.d.ts +43 -0
- package/dist/RateLimit.d.ts.map +1 -0
- package/dist/RateLimit.js +68 -0
- package/dist/RateLimit.js.map +1 -0
- package/dist/RateLimiterFactory.d.ts +83 -0
- package/dist/RateLimiterFactory.d.ts.map +1 -0
- package/dist/RateLimiterFactory.js +115 -0
- package/dist/RateLimiterFactory.js.map +1 -0
- package/dist/RateLimiterFactoryInterface.d.ts +17 -0
- package/dist/RateLimiterFactoryInterface.d.ts.map +1 -0
- package/dist/RateLimiterFactoryInterface.js +3 -0
- package/dist/RateLimiterFactoryInterface.js.map +1 -0
- package/dist/Reservation.d.ts +29 -0
- package/dist/Reservation.d.ts.map +1 -0
- package/dist/Reservation.js +44 -0
- package/dist/Reservation.js.map +1 -0
- package/dist/__tests__/CompoundLimiter.test.d.ts +2 -0
- package/dist/__tests__/CompoundLimiter.test.d.ts.map +1 -0
- package/dist/__tests__/CompoundLimiter.test.js +231 -0
- package/dist/__tests__/CompoundLimiter.test.js.map +1 -0
- package/dist/__tests__/CompoundRateLimiterFactory.test.d.ts +2 -0
- package/dist/__tests__/CompoundRateLimiterFactory.test.d.ts.map +1 -0
- package/dist/__tests__/CompoundRateLimiterFactory.test.js +213 -0
- package/dist/__tests__/CompoundRateLimiterFactory.test.js.map +1 -0
- package/dist/__tests__/RateLimit.test.d.ts +2 -0
- package/dist/__tests__/RateLimit.test.d.ts.map +1 -0
- package/dist/__tests__/RateLimit.test.js +108 -0
- package/dist/__tests__/RateLimit.test.js.map +1 -0
- package/dist/__tests__/RateLimiterFactory.test.d.ts +2 -0
- package/dist/__tests__/RateLimiterFactory.test.d.ts.map +1 -0
- package/dist/__tests__/RateLimiterFactory.test.js +323 -0
- package/dist/__tests__/RateLimiterFactory.test.js.map +1 -0
- package/dist/__tests__/Reservation.test.d.ts +2 -0
- package/dist/__tests__/Reservation.test.d.ts.map +1 -0
- package/dist/__tests__/Reservation.test.js +110 -0
- package/dist/__tests__/Reservation.test.js.map +1 -0
- package/dist/errors/InvalidIntervalError.d.ts +10 -0
- package/dist/errors/InvalidIntervalError.d.ts.map +1 -0
- package/dist/errors/InvalidIntervalError.js +18 -0
- package/dist/errors/InvalidIntervalError.js.map +1 -0
- package/dist/errors/MaxWaitDurationExceededError.d.ts +15 -0
- package/dist/errors/MaxWaitDurationExceededError.d.ts.map +1 -0
- package/dist/errors/MaxWaitDurationExceededError.js +24 -0
- package/dist/errors/MaxWaitDurationExceededError.js.map +1 -0
- package/dist/errors/RateLimitExceededError.d.ts +27 -0
- package/dist/errors/RateLimitExceededError.d.ts.map +1 -0
- package/dist/errors/RateLimitExceededError.js +42 -0
- package/dist/errors/RateLimitExceededError.js.map +1 -0
- package/dist/errors/ReserveNotSupportedError.d.ts +10 -0
- package/dist/errors/ReserveNotSupportedError.d.ts.map +1 -0
- package/dist/errors/ReserveNotSupportedError.js +18 -0
- package/dist/errors/ReserveNotSupportedError.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/policy/FixedWindowLimiter.d.ts +36 -0
- package/dist/policy/FixedWindowLimiter.d.ts.map +1 -0
- package/dist/policy/FixedWindowLimiter.js +105 -0
- package/dist/policy/FixedWindowLimiter.js.map +1 -0
- package/dist/policy/NoLimiter.d.ts +23 -0
- package/dist/policy/NoLimiter.d.ts.map +1 -0
- package/dist/policy/NoLimiter.js +34 -0
- package/dist/policy/NoLimiter.js.map +1 -0
- package/dist/policy/Rate.d.ts +69 -0
- package/dist/policy/Rate.d.ts.map +1 -0
- package/dist/policy/Rate.js +121 -0
- package/dist/policy/Rate.js.map +1 -0
- package/dist/policy/SlidingWindow.d.ts +74 -0
- package/dist/policy/SlidingWindow.d.ts.map +1 -0
- package/dist/policy/SlidingWindow.js +130 -0
- package/dist/policy/SlidingWindow.js.map +1 -0
- package/dist/policy/SlidingWindowLimiter.d.ts +41 -0
- package/dist/policy/SlidingWindowLimiter.d.ts.map +1 -0
- package/dist/policy/SlidingWindowLimiter.js +127 -0
- package/dist/policy/SlidingWindowLimiter.js.map +1 -0
- package/dist/policy/TokenBucket.d.ts +63 -0
- package/dist/policy/TokenBucket.d.ts.map +1 -0
- package/dist/policy/TokenBucket.js +92 -0
- package/dist/policy/TokenBucket.js.map +1 -0
- package/dist/policy/TokenBucketLimiter.d.ts +38 -0
- package/dist/policy/TokenBucketLimiter.d.ts.map +1 -0
- package/dist/policy/TokenBucketLimiter.js +114 -0
- package/dist/policy/TokenBucketLimiter.js.map +1 -0
- package/dist/policy/Window.d.ts +58 -0
- package/dist/policy/Window.d.ts.map +1 -0
- package/dist/policy/Window.js +105 -0
- package/dist/policy/Window.js.map +1 -0
- package/dist/policy/__tests__/FixedWindowLimiter.test.d.ts +2 -0
- package/dist/policy/__tests__/FixedWindowLimiter.test.d.ts.map +1 -0
- package/dist/policy/__tests__/FixedWindowLimiter.test.js +180 -0
- package/dist/policy/__tests__/FixedWindowLimiter.test.js.map +1 -0
- package/dist/policy/__tests__/NoLimiter.test.d.ts +2 -0
- package/dist/policy/__tests__/NoLimiter.test.d.ts.map +1 -0
- package/dist/policy/__tests__/NoLimiter.test.js +40 -0
- package/dist/policy/__tests__/NoLimiter.test.js.map +1 -0
- package/dist/policy/__tests__/Rate.test.d.ts +2 -0
- package/dist/policy/__tests__/Rate.test.d.ts.map +1 -0
- package/dist/policy/__tests__/Rate.test.js +162 -0
- package/dist/policy/__tests__/Rate.test.js.map +1 -0
- package/dist/policy/__tests__/SlidingWindow.test.d.ts +2 -0
- package/dist/policy/__tests__/SlidingWindow.test.d.ts.map +1 -0
- package/dist/policy/__tests__/SlidingWindow.test.js +257 -0
- package/dist/policy/__tests__/SlidingWindow.test.js.map +1 -0
- package/dist/policy/__tests__/SlidingWindowLimiter.test.d.ts +2 -0
- package/dist/policy/__tests__/SlidingWindowLimiter.test.d.ts.map +1 -0
- package/dist/policy/__tests__/SlidingWindowLimiter.test.js +201 -0
- package/dist/policy/__tests__/SlidingWindowLimiter.test.js.map +1 -0
- package/dist/policy/__tests__/TokenBucket.test.d.ts +2 -0
- package/dist/policy/__tests__/TokenBucket.test.d.ts.map +1 -0
- package/dist/policy/__tests__/TokenBucket.test.js +171 -0
- package/dist/policy/__tests__/TokenBucket.test.js.map +1 -0
- package/dist/policy/__tests__/TokenBucketLimiter.test.d.ts +2 -0
- package/dist/policy/__tests__/TokenBucketLimiter.test.d.ts.map +1 -0
- package/dist/policy/__tests__/TokenBucketLimiter.test.js +175 -0
- package/dist/policy/__tests__/TokenBucketLimiter.test.js.map +1 -0
- package/dist/policy/__tests__/Window.test.d.ts +2 -0
- package/dist/policy/__tests__/Window.test.d.ts.map +1 -0
- package/dist/policy/__tests__/Window.test.js +193 -0
- package/dist/policy/__tests__/Window.test.js.map +1 -0
- package/dist/storage/InMemoryStorage.d.ts +34 -0
- package/dist/storage/InMemoryStorage.d.ts.map +1 -0
- package/dist/storage/InMemoryStorage.js +62 -0
- package/dist/storage/InMemoryStorage.js.map +1 -0
- package/dist/storage/LockInterface.d.ts +41 -0
- package/dist/storage/LockInterface.d.ts.map +1 -0
- package/dist/storage/LockInterface.js +19 -0
- package/dist/storage/LockInterface.js.map +1 -0
- package/dist/storage/StorageInterface.d.ts +29 -0
- package/dist/storage/StorageInterface.d.ts.map +1 -0
- package/dist/storage/StorageInterface.js +3 -0
- package/dist/storage/StorageInterface.js.map +1 -0
- package/dist/storage/__tests__/InMemoryStorage.test.d.ts +2 -0
- package/dist/storage/__tests__/InMemoryStorage.test.d.ts.map +1 -0
- package/dist/storage/__tests__/InMemoryStorage.test.js +154 -0
- package/dist/storage/__tests__/InMemoryStorage.test.js.map +1 -0
- package/dist/util/TimeUtil.d.ts +35 -0
- package/dist/util/TimeUtil.d.ts.map +1 -0
- package/dist/util/TimeUtil.js +87 -0
- package/dist/util/TimeUtil.js.map +1 -0
- package/dist/util/__tests__/TimeUtil.test.d.ts +2 -0
- package/dist/util/__tests__/TimeUtil.test.d.ts.map +1 -0
- package/dist/util/__tests__/TimeUtil.test.js +132 -0
- package/dist/util/__tests__/TimeUtil.test.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InMemoryStorage.d.ts","sourceRoot":"","sources":["../../src/storage/InMemoryStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAO3D;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAE3D;;OAEG;IACG,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD;;;;OAIG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAgB9D;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemoryStorage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In-memory storage implementation for rate limiter state.
|
|
6
|
+
*
|
|
7
|
+
* This storage is non-persistent and will be lost when the process restarts.
|
|
8
|
+
* Suitable for single-instance applications or testing.
|
|
9
|
+
*/
|
|
10
|
+
class InMemoryStorage {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.storage = new Map();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Save a state object to memory.
|
|
16
|
+
*/
|
|
17
|
+
async save(state) {
|
|
18
|
+
const expirationTime = state.getExpirationTime();
|
|
19
|
+
const expiresAt = expirationTime ? expirationTime * 1000 : null; // Convert to ms
|
|
20
|
+
this.storage.set(state.getId(), {
|
|
21
|
+
expiresAt,
|
|
22
|
+
state,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch a state object from memory.
|
|
27
|
+
*
|
|
28
|
+
* Automatically cleans up expired entries.
|
|
29
|
+
*/
|
|
30
|
+
async fetch(id) {
|
|
31
|
+
const entry = this.storage.get(id);
|
|
32
|
+
if (!entry) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Check expiration
|
|
36
|
+
if (entry.expiresAt !== null && Date.now() >= entry.expiresAt) {
|
|
37
|
+
this.storage.delete(id);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return entry.state;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Delete a state object from memory.
|
|
44
|
+
*/
|
|
45
|
+
async delete(id) {
|
|
46
|
+
this.storage.delete(id);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Clear all stored state (useful for testing).
|
|
50
|
+
*/
|
|
51
|
+
clear() {
|
|
52
|
+
this.storage.clear();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the number of stored entries (useful for testing).
|
|
56
|
+
*/
|
|
57
|
+
size() {
|
|
58
|
+
return this.storage.size;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.InMemoryStorage = InMemoryStorage;
|
|
62
|
+
//# sourceMappingURL=InMemoryStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InMemoryStorage.js","sourceRoot":"","sources":["../../src/storage/InMemoryStorage.ts"],"names":[],"mappings":";;;AAQA;;;;;GAKG;AACH,MAAa,eAAe;IAA5B;QACmB,YAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAwD7D,CAAC;IAtDC;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,KAA4B;QACrC,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;QACjD,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,gBAAgB;QAEjF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE;YAC9B,SAAS;YACT,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAK,CAAC,EAAU;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,mBAAmB;QACnB,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YAC9D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF;AAzDD,0CAyDC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for distributed locking mechanism.
|
|
3
|
+
*
|
|
4
|
+
* Used to ensure atomic operations on rate limiter state in distributed environments.
|
|
5
|
+
*/
|
|
6
|
+
export interface LockInterface {
|
|
7
|
+
/**
|
|
8
|
+
* Acquire a lock for the given resource.
|
|
9
|
+
*
|
|
10
|
+
* @param key - The resource identifier to lock
|
|
11
|
+
* @param ttl - Time-to-live for the lock in seconds (optional)
|
|
12
|
+
* @returns True if lock was acquired, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
acquire(key: string, ttl?: number): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Release a lock for the given resource.
|
|
17
|
+
*
|
|
18
|
+
* @param key - The resource identifier to unlock
|
|
19
|
+
*/
|
|
20
|
+
release(key: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Execute a callback while holding a lock.
|
|
23
|
+
*
|
|
24
|
+
* Automatically acquires and releases the lock.
|
|
25
|
+
*
|
|
26
|
+
* @param key - The resource identifier to lock
|
|
27
|
+
* @param callback - Function to execute while holding the lock
|
|
28
|
+
* @param ttl - Time-to-live for the lock in seconds (optional)
|
|
29
|
+
* @returns The result of the callback
|
|
30
|
+
*/
|
|
31
|
+
withLock<T>(key: string, callback: () => Promise<T>, ttl?: number): Promise<T>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Simple no-op lock implementation for single-instance applications.
|
|
35
|
+
*/
|
|
36
|
+
export declare class NoLock implements LockInterface {
|
|
37
|
+
acquire(): Promise<boolean>;
|
|
38
|
+
release(): Promise<void>;
|
|
39
|
+
withLock<T>(_key: string, callback: () => Promise<T>): Promise<T>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=LockInterface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LockInterface.d.ts","sourceRoot":"","sources":["../../src/storage/LockInterface.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAErD;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,CAAC,EACR,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC1B,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,MAAO,YAAW,aAAa;IACpC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAI3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,QAAQ,CAAC,CAAC,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC;CAGd"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoLock = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple no-op lock implementation for single-instance applications.
|
|
6
|
+
*/
|
|
7
|
+
class NoLock {
|
|
8
|
+
async acquire() {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
async release() {
|
|
12
|
+
// No-op
|
|
13
|
+
}
|
|
14
|
+
async withLock(_key, callback) {
|
|
15
|
+
return callback();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.NoLock = NoLock;
|
|
19
|
+
//# sourceMappingURL=LockInterface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LockInterface.js","sourceRoot":"","sources":["../../src/storage/LockInterface.ts"],"names":[],"mappings":";;;AAuCA;;GAEG;AACH,MAAa,MAAM;IACjB,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,OAAO;QACX,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,IAAY,EACZ,QAA0B;QAE1B,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;CACF;AAfD,wBAeC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LimiterStateInterface } from '../LimiterStateInterface';
|
|
2
|
+
/**
|
|
3
|
+
* Interface for persisting rate limiter state.
|
|
4
|
+
*
|
|
5
|
+
* Implementations can use in-memory storage, Redis, file system, or any other
|
|
6
|
+
* persistence mechanism.
|
|
7
|
+
*/
|
|
8
|
+
export interface StorageInterface {
|
|
9
|
+
/**
|
|
10
|
+
* Save a state object.
|
|
11
|
+
*
|
|
12
|
+
* @param state - The state object to save
|
|
13
|
+
*/
|
|
14
|
+
save(state: LimiterStateInterface): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch a state object by ID.
|
|
17
|
+
*
|
|
18
|
+
* @param id - The unique identifier for the state
|
|
19
|
+
* @returns The state object, or null if not found or expired
|
|
20
|
+
*/
|
|
21
|
+
fetch(id: string): Promise<LimiterStateInterface | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Delete a state object by ID.
|
|
24
|
+
*
|
|
25
|
+
* @param id - The unique identifier for the state
|
|
26
|
+
*/
|
|
27
|
+
delete(id: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=StorageInterface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageInterface.d.ts","sourceRoot":"","sources":["../../src/storage/StorageInterface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAEzD;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageInterface.js","sourceRoot":"","sources":["../../src/storage/StorageInterface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InMemoryStorage.test.d.ts","sourceRoot":"","sources":["../../../src/storage/__tests__/InMemoryStorage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const InMemoryStorage_1 = require("../InMemoryStorage");
|
|
4
|
+
const TokenBucket_1 = require("../../policy/TokenBucket");
|
|
5
|
+
const Rate_1 = require("../../policy/Rate");
|
|
6
|
+
describe('InMemoryStorage', () => {
|
|
7
|
+
let storage;
|
|
8
|
+
let bucket;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
storage = new InMemoryStorage_1.InMemoryStorage();
|
|
11
|
+
bucket = new TokenBucket_1.TokenBucket('test-bucket', 10, Rate_1.Rate.perSecond(1));
|
|
12
|
+
});
|
|
13
|
+
describe('save and fetch', () => {
|
|
14
|
+
it('should save and fetch a state object', async () => {
|
|
15
|
+
await storage.save(bucket);
|
|
16
|
+
const fetched = await storage.fetch('test-bucket');
|
|
17
|
+
expect(fetched).toBe(bucket);
|
|
18
|
+
expect(fetched?.getId()).toBe('test-bucket');
|
|
19
|
+
});
|
|
20
|
+
it('should return null for non-existent id', async () => {
|
|
21
|
+
const fetched = await storage.fetch('non-existent');
|
|
22
|
+
expect(fetched).toBeNull();
|
|
23
|
+
});
|
|
24
|
+
it('should overwrite existing state with same id', async () => {
|
|
25
|
+
const bucket1 = new TokenBucket_1.TokenBucket('test-id', 10, Rate_1.Rate.perSecond(1), 5);
|
|
26
|
+
const bucket2 = new TokenBucket_1.TokenBucket('test-id', 20, Rate_1.Rate.perSecond(2), 10);
|
|
27
|
+
await storage.save(bucket1);
|
|
28
|
+
await storage.save(bucket2);
|
|
29
|
+
const fetched = await storage.fetch('test-id');
|
|
30
|
+
expect(fetched).toBe(bucket2);
|
|
31
|
+
expect(fetched.getBurstSize()).toBe(20);
|
|
32
|
+
});
|
|
33
|
+
it('should handle multiple different states', async () => {
|
|
34
|
+
const bucket1 = new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1));
|
|
35
|
+
const bucket2 = new TokenBucket_1.TokenBucket('bucket-2', 20, Rate_1.Rate.perSecond(2));
|
|
36
|
+
const bucket3 = new TokenBucket_1.TokenBucket('bucket-3', 30, Rate_1.Rate.perSecond(3));
|
|
37
|
+
await storage.save(bucket1);
|
|
38
|
+
await storage.save(bucket2);
|
|
39
|
+
await storage.save(bucket3);
|
|
40
|
+
expect(await storage.fetch('bucket-1')).toBe(bucket1);
|
|
41
|
+
expect(await storage.fetch('bucket-2')).toBe(bucket2);
|
|
42
|
+
expect(await storage.fetch('bucket-3')).toBe(bucket3);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('delete', () => {
|
|
46
|
+
it('should delete a state object', async () => {
|
|
47
|
+
await storage.save(bucket);
|
|
48
|
+
await storage.delete('test-bucket');
|
|
49
|
+
const fetched = await storage.fetch('test-bucket');
|
|
50
|
+
expect(fetched).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it('should not throw when deleting non-existent id', async () => {
|
|
53
|
+
await expect(storage.delete('non-existent')).resolves.not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
it('should only delete the specified id', async () => {
|
|
56
|
+
const bucket1 = new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1));
|
|
57
|
+
const bucket2 = new TokenBucket_1.TokenBucket('bucket-2', 20, Rate_1.Rate.perSecond(2));
|
|
58
|
+
await storage.save(bucket1);
|
|
59
|
+
await storage.save(bucket2);
|
|
60
|
+
await storage.delete('bucket-1');
|
|
61
|
+
expect(await storage.fetch('bucket-1')).toBeNull();
|
|
62
|
+
expect(await storage.fetch('bucket-2')).toBe(bucket2);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('expiration', () => {
|
|
66
|
+
it('should return null for expired entries', async () => {
|
|
67
|
+
jest.useFakeTimers();
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
jest.setSystemTime(now);
|
|
70
|
+
// Create a bucket that will expire in 10 seconds
|
|
71
|
+
const expirableBucket = new TokenBucket_1.TokenBucket('expirable', 10, Rate_1.Rate.perSecond(1), 0, now / 1000);
|
|
72
|
+
await storage.save(expirableBucket);
|
|
73
|
+
// Should exist before expiration
|
|
74
|
+
expect(await storage.fetch('expirable')).toBe(expirableBucket);
|
|
75
|
+
// Advance time past expiration (bucket expires after it can fully refill)
|
|
76
|
+
const expirationMs = expirableBucket.getExpirationTime() * 1000;
|
|
77
|
+
jest.setSystemTime(expirationMs + 1000);
|
|
78
|
+
// Should return null after expiration
|
|
79
|
+
expect(await storage.fetch('expirable')).toBeNull();
|
|
80
|
+
jest.useRealTimers();
|
|
81
|
+
});
|
|
82
|
+
it('should automatically clean up expired entries on fetch', async () => {
|
|
83
|
+
jest.useFakeTimers();
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
jest.setSystemTime(now);
|
|
86
|
+
const expirableBucket = new TokenBucket_1.TokenBucket('expirable', 10, Rate_1.Rate.perSecond(1), 0, now / 1000);
|
|
87
|
+
await storage.save(expirableBucket);
|
|
88
|
+
expect(storage.size()).toBe(1);
|
|
89
|
+
// Advance time past expiration
|
|
90
|
+
const expirationMs = expirableBucket.getExpirationTime() * 1000;
|
|
91
|
+
jest.setSystemTime(expirationMs + 1000);
|
|
92
|
+
// Fetch should clean up the expired entry
|
|
93
|
+
await storage.fetch('expirable');
|
|
94
|
+
expect(storage.size()).toBe(0);
|
|
95
|
+
jest.useRealTimers();
|
|
96
|
+
});
|
|
97
|
+
it('should handle entries with no expiration (null)', async () => {
|
|
98
|
+
// Create a mock state with no expiration
|
|
99
|
+
const noExpirationState = {
|
|
100
|
+
getId: () => 'no-expiration',
|
|
101
|
+
getExpirationTime: () => null,
|
|
102
|
+
};
|
|
103
|
+
await storage.save(noExpirationState);
|
|
104
|
+
// Should always be fetchable
|
|
105
|
+
expect(await storage.fetch('no-expiration')).toBe(noExpirationState);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe('clear', () => {
|
|
109
|
+
it('should clear all stored states', async () => {
|
|
110
|
+
const bucket1 = new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1));
|
|
111
|
+
const bucket2 = new TokenBucket_1.TokenBucket('bucket-2', 20, Rate_1.Rate.perSecond(2));
|
|
112
|
+
await storage.save(bucket1);
|
|
113
|
+
await storage.save(bucket2);
|
|
114
|
+
storage.clear();
|
|
115
|
+
expect(await storage.fetch('bucket-1')).toBeNull();
|
|
116
|
+
expect(await storage.fetch('bucket-2')).toBeNull();
|
|
117
|
+
expect(storage.size()).toBe(0);
|
|
118
|
+
});
|
|
119
|
+
it('should handle clearing empty storage', () => {
|
|
120
|
+
expect(() => storage.clear()).not.toThrow();
|
|
121
|
+
expect(storage.size()).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('size', () => {
|
|
125
|
+
it('should return 0 for empty storage', () => {
|
|
126
|
+
expect(storage.size()).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
it('should return correct size after adding entries', async () => {
|
|
129
|
+
expect(storage.size()).toBe(0);
|
|
130
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1)));
|
|
131
|
+
expect(storage.size()).toBe(1);
|
|
132
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-2', 20, Rate_1.Rate.perSecond(2)));
|
|
133
|
+
expect(storage.size()).toBe(2);
|
|
134
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-3', 30, Rate_1.Rate.perSecond(3)));
|
|
135
|
+
expect(storage.size()).toBe(3);
|
|
136
|
+
});
|
|
137
|
+
it('should return correct size after deleting entries', async () => {
|
|
138
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1)));
|
|
139
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-2', 20, Rate_1.Rate.perSecond(2)));
|
|
140
|
+
expect(storage.size()).toBe(2);
|
|
141
|
+
await storage.delete('bucket-1');
|
|
142
|
+
expect(storage.size()).toBe(1);
|
|
143
|
+
await storage.delete('bucket-2');
|
|
144
|
+
expect(storage.size()).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
it('should not change size when saving same id', async () => {
|
|
147
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-1', 10, Rate_1.Rate.perSecond(1)));
|
|
148
|
+
expect(storage.size()).toBe(1);
|
|
149
|
+
await storage.save(new TokenBucket_1.TokenBucket('bucket-1', 20, Rate_1.Rate.perSecond(2)));
|
|
150
|
+
expect(storage.size()).toBe(1);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=InMemoryStorage.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InMemoryStorage.test.js","sourceRoot":"","sources":["../../../src/storage/__tests__/InMemoryStorage.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for time calculations.
|
|
3
|
+
*/
|
|
4
|
+
export declare class TimeUtil {
|
|
5
|
+
/**
|
|
6
|
+
* Convert a duration string to seconds.
|
|
7
|
+
*
|
|
8
|
+
* Supports formats like:
|
|
9
|
+
* - "1 second", "2 seconds", "1s"
|
|
10
|
+
* - "1 minute", "2 minutes", "1m"
|
|
11
|
+
* - "1 hour", "2 hours", "1h"
|
|
12
|
+
* - "1 day", "2 days", "1d"
|
|
13
|
+
* - "1 week", "2 weeks", "1w"
|
|
14
|
+
* - "1 month", "2 months" (assumes 30 days)
|
|
15
|
+
* - "1 year", "2 years" (assumes 365 days)
|
|
16
|
+
*
|
|
17
|
+
* @param duration - Duration string to parse
|
|
18
|
+
* @returns Duration in seconds
|
|
19
|
+
* @throws {Error} If the duration format is invalid
|
|
20
|
+
*/
|
|
21
|
+
static durationToSeconds(duration: string): number;
|
|
22
|
+
/**
|
|
23
|
+
* Get the current time in seconds (with millisecond precision).
|
|
24
|
+
*/
|
|
25
|
+
static now(): number;
|
|
26
|
+
/**
|
|
27
|
+
* Convert milliseconds to seconds.
|
|
28
|
+
*/
|
|
29
|
+
static msToSeconds(ms: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Convert seconds to milliseconds.
|
|
32
|
+
*/
|
|
33
|
+
static secondsToMs(seconds: number): number;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=TimeUtil.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeUtil.d.ts","sourceRoot":"","sources":["../../src/util/TimeUtil.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,QAAQ;IACnB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkDlD;;OAEG;IACH,MAAM,CAAC,GAAG,IAAI,MAAM;IAIpB;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAItC;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAG5C"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TimeUtil = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Utility functions for time calculations.
|
|
6
|
+
*/
|
|
7
|
+
class TimeUtil {
|
|
8
|
+
/**
|
|
9
|
+
* Convert a duration string to seconds.
|
|
10
|
+
*
|
|
11
|
+
* Supports formats like:
|
|
12
|
+
* - "1 second", "2 seconds", "1s"
|
|
13
|
+
* - "1 minute", "2 minutes", "1m"
|
|
14
|
+
* - "1 hour", "2 hours", "1h"
|
|
15
|
+
* - "1 day", "2 days", "1d"
|
|
16
|
+
* - "1 week", "2 weeks", "1w"
|
|
17
|
+
* - "1 month", "2 months" (assumes 30 days)
|
|
18
|
+
* - "1 year", "2 years" (assumes 365 days)
|
|
19
|
+
*
|
|
20
|
+
* @param duration - Duration string to parse
|
|
21
|
+
* @returns Duration in seconds
|
|
22
|
+
* @throws {Error} If the duration format is invalid
|
|
23
|
+
*/
|
|
24
|
+
static durationToSeconds(duration) {
|
|
25
|
+
const trimmed = duration.trim().toLowerCase();
|
|
26
|
+
const match = trimmed.match(/^(\d+)\s*(.+)$/);
|
|
27
|
+
if (!match) {
|
|
28
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
29
|
+
}
|
|
30
|
+
const [, amountStr, unit] = match;
|
|
31
|
+
const amount = parseInt(amountStr, 10);
|
|
32
|
+
if (isNaN(amount) || amount < 0) {
|
|
33
|
+
throw new Error(`Invalid amount in duration: ${duration}`);
|
|
34
|
+
}
|
|
35
|
+
// Map unit to seconds
|
|
36
|
+
const unitMap = {
|
|
37
|
+
s: 1,
|
|
38
|
+
sec: 1,
|
|
39
|
+
second: 1,
|
|
40
|
+
seconds: 1,
|
|
41
|
+
m: 60,
|
|
42
|
+
min: 60,
|
|
43
|
+
minute: 60,
|
|
44
|
+
minutes: 60,
|
|
45
|
+
h: 3600,
|
|
46
|
+
hr: 3600,
|
|
47
|
+
hour: 3600,
|
|
48
|
+
hours: 3600,
|
|
49
|
+
d: 86400,
|
|
50
|
+
day: 86400,
|
|
51
|
+
days: 86400,
|
|
52
|
+
w: 604800,
|
|
53
|
+
week: 604800,
|
|
54
|
+
weeks: 604800,
|
|
55
|
+
month: 2592000, // 30 days
|
|
56
|
+
months: 2592000,
|
|
57
|
+
y: 31536000, // 365 days
|
|
58
|
+
year: 31536000,
|
|
59
|
+
years: 31536000,
|
|
60
|
+
};
|
|
61
|
+
const multiplier = unitMap[unit];
|
|
62
|
+
if (multiplier === undefined) {
|
|
63
|
+
throw new Error(`Unknown time unit: ${unit}`);
|
|
64
|
+
}
|
|
65
|
+
return amount * multiplier;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the current time in seconds (with millisecond precision).
|
|
69
|
+
*/
|
|
70
|
+
static now() {
|
|
71
|
+
return Date.now() / 1000;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert milliseconds to seconds.
|
|
75
|
+
*/
|
|
76
|
+
static msToSeconds(ms) {
|
|
77
|
+
return ms / 1000;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Convert seconds to milliseconds.
|
|
81
|
+
*/
|
|
82
|
+
static secondsToMs(seconds) {
|
|
83
|
+
return seconds * 1000;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.TimeUtil = TimeUtil;
|
|
87
|
+
//# sourceMappingURL=TimeUtil.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeUtil.js","sourceRoot":"","sources":["../../src/util/TimeUtil.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAa,QAAQ;IACnB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAEvC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,sBAAsB;QACtB,MAAM,OAAO,GAA2B;YACtC,CAAC,EAAE,CAAC;YACJ,GAAG,EAAE,CAAC;YACN,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;YACV,CAAC,EAAE,EAAE;YACL,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,EAAE;YACX,CAAC,EAAE,IAAI;YACP,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,IAAI;YACX,CAAC,EAAE,KAAK;YACR,GAAG,EAAE,KAAK;YACV,IAAI,EAAE,KAAK;YACX,CAAC,EAAE,MAAM;YACT,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,OAAO,EAAE,UAAU;YAC1B,MAAM,EAAE,OAAO;YACf,CAAC,EAAE,QAAQ,EAAE,WAAW;YACxB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,QAAQ;SAChB,CAAC;QAEF,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,MAAM,GAAG,UAAU,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAG;QACR,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,EAAU;QAC3B,OAAO,EAAE,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,OAAe;QAChC,OAAO,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;CACF;AAvFD,4BAuFC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeUtil.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/TimeUtil.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const TimeUtil_1 = require("../TimeUtil");
|
|
4
|
+
describe('TimeUtil', () => {
|
|
5
|
+
describe('durationToSeconds', () => {
|
|
6
|
+
it('should convert seconds correctly', () => {
|
|
7
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 second')).toBe(1);
|
|
8
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 seconds')).toBe(2);
|
|
9
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('5 s')).toBe(5);
|
|
10
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10s')).toBe(10);
|
|
11
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3 sec')).toBe(3);
|
|
12
|
+
});
|
|
13
|
+
it('should convert minutes correctly', () => {
|
|
14
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 minute')).toBe(60);
|
|
15
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 minutes')).toBe(120);
|
|
16
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('5 m')).toBe(300);
|
|
17
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10m')).toBe(600);
|
|
18
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3 min')).toBe(180);
|
|
19
|
+
});
|
|
20
|
+
it('should convert hours correctly', () => {
|
|
21
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 hour')).toBe(3600);
|
|
22
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 hours')).toBe(7200);
|
|
23
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('5 h')).toBe(18000);
|
|
24
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10h')).toBe(36000);
|
|
25
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3 hr')).toBe(10800);
|
|
26
|
+
});
|
|
27
|
+
it('should convert days correctly', () => {
|
|
28
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 day')).toBe(86400);
|
|
29
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 days')).toBe(172800);
|
|
30
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('7 d')).toBe(604800);
|
|
31
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10d')).toBe(864000);
|
|
32
|
+
});
|
|
33
|
+
it('should convert weeks correctly', () => {
|
|
34
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 week')).toBe(604800);
|
|
35
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 weeks')).toBe(1209600);
|
|
36
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3 w')).toBe(1814400);
|
|
37
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('4w')).toBe(2419200);
|
|
38
|
+
});
|
|
39
|
+
it('should convert months correctly (30 days)', () => {
|
|
40
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 month')).toBe(2592000);
|
|
41
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 months')).toBe(5184000);
|
|
42
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3 month')).toBe(7776000);
|
|
43
|
+
});
|
|
44
|
+
it('should convert years correctly (365 days)', () => {
|
|
45
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 year')).toBe(31536000);
|
|
46
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('2 years')).toBe(63072000);
|
|
47
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 y')).toBe(31536000);
|
|
48
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('3y')).toBe(94608000);
|
|
49
|
+
});
|
|
50
|
+
it('should handle whitespace variations', () => {
|
|
51
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds(' 5 seconds ')).toBe(5);
|
|
52
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10m')).toBe(600);
|
|
53
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 hour')).toBe(3600);
|
|
54
|
+
});
|
|
55
|
+
it('should be case insensitive', () => {
|
|
56
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('5 SECONDS')).toBe(5);
|
|
57
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('10 Minutes')).toBe(600);
|
|
58
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('1 HOUR')).toBe(3600);
|
|
59
|
+
});
|
|
60
|
+
it('should throw error for invalid format', () => {
|
|
61
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('invalid')).toThrow('Invalid duration format');
|
|
62
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('abc seconds')).toThrow();
|
|
63
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('')).toThrow();
|
|
64
|
+
});
|
|
65
|
+
it('should throw error for negative amounts', () => {
|
|
66
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('-5 seconds')).toThrow('Invalid duration format');
|
|
67
|
+
});
|
|
68
|
+
it('should throw error for unknown units', () => {
|
|
69
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('5 fortnights')).toThrow('Unknown time unit');
|
|
70
|
+
expect(() => TimeUtil_1.TimeUtil.durationToSeconds('10 xyz')).toThrow('Unknown time unit');
|
|
71
|
+
});
|
|
72
|
+
it('should handle zero values', () => {
|
|
73
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('0 seconds')).toBe(0);
|
|
74
|
+
expect(TimeUtil_1.TimeUtil.durationToSeconds('0 minutes')).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('now', () => {
|
|
78
|
+
it('should return current time in seconds', () => {
|
|
79
|
+
const before = Date.now() / 1000;
|
|
80
|
+
const now = TimeUtil_1.TimeUtil.now();
|
|
81
|
+
const after = Date.now() / 1000;
|
|
82
|
+
expect(now).toBeGreaterThanOrEqual(before);
|
|
83
|
+
expect(now).toBeLessThanOrEqual(after);
|
|
84
|
+
});
|
|
85
|
+
it('should return a number', () => {
|
|
86
|
+
expect(typeof TimeUtil_1.TimeUtil.now()).toBe('number');
|
|
87
|
+
});
|
|
88
|
+
it('should have decimal precision (milliseconds)', () => {
|
|
89
|
+
const now = TimeUtil_1.TimeUtil.now();
|
|
90
|
+
expect(now % 1).not.toBe(0);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('msToSeconds', () => {
|
|
94
|
+
it('should convert milliseconds to seconds', () => {
|
|
95
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(1000)).toBe(1);
|
|
96
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(5000)).toBe(5);
|
|
97
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(60000)).toBe(60);
|
|
98
|
+
});
|
|
99
|
+
it('should handle fractional seconds', () => {
|
|
100
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(500)).toBe(0.5);
|
|
101
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(1500)).toBe(1.5);
|
|
102
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(100)).toBe(0.1);
|
|
103
|
+
});
|
|
104
|
+
it('should handle zero', () => {
|
|
105
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(0)).toBe(0);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe('secondsToMs', () => {
|
|
109
|
+
it('should convert seconds to milliseconds', () => {
|
|
110
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(1)).toBe(1000);
|
|
111
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(5)).toBe(5000);
|
|
112
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(60)).toBe(60000);
|
|
113
|
+
});
|
|
114
|
+
it('should handle fractional seconds', () => {
|
|
115
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(0.5)).toBe(500);
|
|
116
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(1.5)).toBe(1500);
|
|
117
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(0.1)).toBe(100);
|
|
118
|
+
});
|
|
119
|
+
it('should handle zero', () => {
|
|
120
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(0)).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe('round-trip conversions', () => {
|
|
124
|
+
it('should convert back and forth without loss', () => {
|
|
125
|
+
const ms = 5432;
|
|
126
|
+
expect(TimeUtil_1.TimeUtil.secondsToMs(TimeUtil_1.TimeUtil.msToSeconds(ms))).toBe(ms);
|
|
127
|
+
const seconds = 10.5;
|
|
128
|
+
expect(TimeUtil_1.TimeUtil.msToSeconds(TimeUtil_1.TimeUtil.secondsToMs(seconds))).toBe(seconds);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=TimeUtil.test.js.map
|