@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.
Files changed (163) hide show
  1. package/LICENSE.md +24 -0
  2. package/README.md +204 -0
  3. package/dist/CompoundLimiter.d.ts +33 -0
  4. package/dist/CompoundLimiter.d.ts.map +1 -0
  5. package/dist/CompoundLimiter.js +62 -0
  6. package/dist/CompoundLimiter.js.map +1 -0
  7. package/dist/CompoundRateLimiterFactory.d.ts +19 -0
  8. package/dist/CompoundRateLimiterFactory.d.ts.map +1 -0
  9. package/dist/CompoundRateLimiterFactory.js +29 -0
  10. package/dist/CompoundRateLimiterFactory.js.map +1 -0
  11. package/dist/LimiterInterface.d.ts +32 -0
  12. package/dist/LimiterInterface.d.ts.map +1 -0
  13. package/dist/LimiterInterface.js +3 -0
  14. package/dist/LimiterInterface.js.map +1 -0
  15. package/dist/LimiterStateInterface.d.ts +16 -0
  16. package/dist/LimiterStateInterface.d.ts.map +1 -0
  17. package/dist/LimiterStateInterface.js +3 -0
  18. package/dist/LimiterStateInterface.js.map +1 -0
  19. package/dist/RateLimit.d.ts +43 -0
  20. package/dist/RateLimit.d.ts.map +1 -0
  21. package/dist/RateLimit.js +68 -0
  22. package/dist/RateLimit.js.map +1 -0
  23. package/dist/RateLimiterFactory.d.ts +83 -0
  24. package/dist/RateLimiterFactory.d.ts.map +1 -0
  25. package/dist/RateLimiterFactory.js +115 -0
  26. package/dist/RateLimiterFactory.js.map +1 -0
  27. package/dist/RateLimiterFactoryInterface.d.ts +17 -0
  28. package/dist/RateLimiterFactoryInterface.d.ts.map +1 -0
  29. package/dist/RateLimiterFactoryInterface.js +3 -0
  30. package/dist/RateLimiterFactoryInterface.js.map +1 -0
  31. package/dist/Reservation.d.ts +29 -0
  32. package/dist/Reservation.d.ts.map +1 -0
  33. package/dist/Reservation.js +44 -0
  34. package/dist/Reservation.js.map +1 -0
  35. package/dist/__tests__/CompoundLimiter.test.d.ts +2 -0
  36. package/dist/__tests__/CompoundLimiter.test.d.ts.map +1 -0
  37. package/dist/__tests__/CompoundLimiter.test.js +231 -0
  38. package/dist/__tests__/CompoundLimiter.test.js.map +1 -0
  39. package/dist/__tests__/CompoundRateLimiterFactory.test.d.ts +2 -0
  40. package/dist/__tests__/CompoundRateLimiterFactory.test.d.ts.map +1 -0
  41. package/dist/__tests__/CompoundRateLimiterFactory.test.js +213 -0
  42. package/dist/__tests__/CompoundRateLimiterFactory.test.js.map +1 -0
  43. package/dist/__tests__/RateLimit.test.d.ts +2 -0
  44. package/dist/__tests__/RateLimit.test.d.ts.map +1 -0
  45. package/dist/__tests__/RateLimit.test.js +108 -0
  46. package/dist/__tests__/RateLimit.test.js.map +1 -0
  47. package/dist/__tests__/RateLimiterFactory.test.d.ts +2 -0
  48. package/dist/__tests__/RateLimiterFactory.test.d.ts.map +1 -0
  49. package/dist/__tests__/RateLimiterFactory.test.js +323 -0
  50. package/dist/__tests__/RateLimiterFactory.test.js.map +1 -0
  51. package/dist/__tests__/Reservation.test.d.ts +2 -0
  52. package/dist/__tests__/Reservation.test.d.ts.map +1 -0
  53. package/dist/__tests__/Reservation.test.js +110 -0
  54. package/dist/__tests__/Reservation.test.js.map +1 -0
  55. package/dist/errors/InvalidIntervalError.d.ts +10 -0
  56. package/dist/errors/InvalidIntervalError.d.ts.map +1 -0
  57. package/dist/errors/InvalidIntervalError.js +18 -0
  58. package/dist/errors/InvalidIntervalError.js.map +1 -0
  59. package/dist/errors/MaxWaitDurationExceededError.d.ts +15 -0
  60. package/dist/errors/MaxWaitDurationExceededError.d.ts.map +1 -0
  61. package/dist/errors/MaxWaitDurationExceededError.js +24 -0
  62. package/dist/errors/MaxWaitDurationExceededError.js.map +1 -0
  63. package/dist/errors/RateLimitExceededError.d.ts +27 -0
  64. package/dist/errors/RateLimitExceededError.d.ts.map +1 -0
  65. package/dist/errors/RateLimitExceededError.js +42 -0
  66. package/dist/errors/RateLimitExceededError.js.map +1 -0
  67. package/dist/errors/ReserveNotSupportedError.d.ts +10 -0
  68. package/dist/errors/ReserveNotSupportedError.d.ts.map +1 -0
  69. package/dist/errors/ReserveNotSupportedError.js +18 -0
  70. package/dist/errors/ReserveNotSupportedError.js.map +1 -0
  71. package/dist/index.d.ts +35 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +58 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/policy/FixedWindowLimiter.d.ts +36 -0
  76. package/dist/policy/FixedWindowLimiter.d.ts.map +1 -0
  77. package/dist/policy/FixedWindowLimiter.js +105 -0
  78. package/dist/policy/FixedWindowLimiter.js.map +1 -0
  79. package/dist/policy/NoLimiter.d.ts +23 -0
  80. package/dist/policy/NoLimiter.d.ts.map +1 -0
  81. package/dist/policy/NoLimiter.js +34 -0
  82. package/dist/policy/NoLimiter.js.map +1 -0
  83. package/dist/policy/Rate.d.ts +69 -0
  84. package/dist/policy/Rate.d.ts.map +1 -0
  85. package/dist/policy/Rate.js +121 -0
  86. package/dist/policy/Rate.js.map +1 -0
  87. package/dist/policy/SlidingWindow.d.ts +74 -0
  88. package/dist/policy/SlidingWindow.d.ts.map +1 -0
  89. package/dist/policy/SlidingWindow.js +130 -0
  90. package/dist/policy/SlidingWindow.js.map +1 -0
  91. package/dist/policy/SlidingWindowLimiter.d.ts +41 -0
  92. package/dist/policy/SlidingWindowLimiter.d.ts.map +1 -0
  93. package/dist/policy/SlidingWindowLimiter.js +127 -0
  94. package/dist/policy/SlidingWindowLimiter.js.map +1 -0
  95. package/dist/policy/TokenBucket.d.ts +63 -0
  96. package/dist/policy/TokenBucket.d.ts.map +1 -0
  97. package/dist/policy/TokenBucket.js +92 -0
  98. package/dist/policy/TokenBucket.js.map +1 -0
  99. package/dist/policy/TokenBucketLimiter.d.ts +38 -0
  100. package/dist/policy/TokenBucketLimiter.d.ts.map +1 -0
  101. package/dist/policy/TokenBucketLimiter.js +114 -0
  102. package/dist/policy/TokenBucketLimiter.js.map +1 -0
  103. package/dist/policy/Window.d.ts +58 -0
  104. package/dist/policy/Window.d.ts.map +1 -0
  105. package/dist/policy/Window.js +105 -0
  106. package/dist/policy/Window.js.map +1 -0
  107. package/dist/policy/__tests__/FixedWindowLimiter.test.d.ts +2 -0
  108. package/dist/policy/__tests__/FixedWindowLimiter.test.d.ts.map +1 -0
  109. package/dist/policy/__tests__/FixedWindowLimiter.test.js +180 -0
  110. package/dist/policy/__tests__/FixedWindowLimiter.test.js.map +1 -0
  111. package/dist/policy/__tests__/NoLimiter.test.d.ts +2 -0
  112. package/dist/policy/__tests__/NoLimiter.test.d.ts.map +1 -0
  113. package/dist/policy/__tests__/NoLimiter.test.js +40 -0
  114. package/dist/policy/__tests__/NoLimiter.test.js.map +1 -0
  115. package/dist/policy/__tests__/Rate.test.d.ts +2 -0
  116. package/dist/policy/__tests__/Rate.test.d.ts.map +1 -0
  117. package/dist/policy/__tests__/Rate.test.js +162 -0
  118. package/dist/policy/__tests__/Rate.test.js.map +1 -0
  119. package/dist/policy/__tests__/SlidingWindow.test.d.ts +2 -0
  120. package/dist/policy/__tests__/SlidingWindow.test.d.ts.map +1 -0
  121. package/dist/policy/__tests__/SlidingWindow.test.js +257 -0
  122. package/dist/policy/__tests__/SlidingWindow.test.js.map +1 -0
  123. package/dist/policy/__tests__/SlidingWindowLimiter.test.d.ts +2 -0
  124. package/dist/policy/__tests__/SlidingWindowLimiter.test.d.ts.map +1 -0
  125. package/dist/policy/__tests__/SlidingWindowLimiter.test.js +201 -0
  126. package/dist/policy/__tests__/SlidingWindowLimiter.test.js.map +1 -0
  127. package/dist/policy/__tests__/TokenBucket.test.d.ts +2 -0
  128. package/dist/policy/__tests__/TokenBucket.test.d.ts.map +1 -0
  129. package/dist/policy/__tests__/TokenBucket.test.js +171 -0
  130. package/dist/policy/__tests__/TokenBucket.test.js.map +1 -0
  131. package/dist/policy/__tests__/TokenBucketLimiter.test.d.ts +2 -0
  132. package/dist/policy/__tests__/TokenBucketLimiter.test.d.ts.map +1 -0
  133. package/dist/policy/__tests__/TokenBucketLimiter.test.js +175 -0
  134. package/dist/policy/__tests__/TokenBucketLimiter.test.js.map +1 -0
  135. package/dist/policy/__tests__/Window.test.d.ts +2 -0
  136. package/dist/policy/__tests__/Window.test.d.ts.map +1 -0
  137. package/dist/policy/__tests__/Window.test.js +193 -0
  138. package/dist/policy/__tests__/Window.test.js.map +1 -0
  139. package/dist/storage/InMemoryStorage.d.ts +34 -0
  140. package/dist/storage/InMemoryStorage.d.ts.map +1 -0
  141. package/dist/storage/InMemoryStorage.js +62 -0
  142. package/dist/storage/InMemoryStorage.js.map +1 -0
  143. package/dist/storage/LockInterface.d.ts +41 -0
  144. package/dist/storage/LockInterface.d.ts.map +1 -0
  145. package/dist/storage/LockInterface.js +19 -0
  146. package/dist/storage/LockInterface.js.map +1 -0
  147. package/dist/storage/StorageInterface.d.ts +29 -0
  148. package/dist/storage/StorageInterface.d.ts.map +1 -0
  149. package/dist/storage/StorageInterface.js +3 -0
  150. package/dist/storage/StorageInterface.js.map +1 -0
  151. package/dist/storage/__tests__/InMemoryStorage.test.d.ts +2 -0
  152. package/dist/storage/__tests__/InMemoryStorage.test.d.ts.map +1 -0
  153. package/dist/storage/__tests__/InMemoryStorage.test.js +154 -0
  154. package/dist/storage/__tests__/InMemoryStorage.test.js.map +1 -0
  155. package/dist/util/TimeUtil.d.ts +35 -0
  156. package/dist/util/TimeUtil.d.ts.map +1 -0
  157. package/dist/util/TimeUtil.js +87 -0
  158. package/dist/util/TimeUtil.js.map +1 -0
  159. package/dist/util/__tests__/TimeUtil.test.d.ts +2 -0
  160. package/dist/util/__tests__/TimeUtil.test.d.ts.map +1 -0
  161. package/dist/util/__tests__/TimeUtil.test.js +132 -0
  162. package/dist/util/__tests__/TimeUtil.test.js.map +1 -0
  163. 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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=StorageInterface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageInterface.js","sourceRoot":"","sources":["../../src/storage/StorageInterface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=InMemoryStorage.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TimeUtil.test.d.ts.map
@@ -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