tiny-essentials 1.27.1 → 1.28.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.
Files changed (188) hide show
  1. package/README.md +1 -0
  2. package/changelog/1/28/0.md +16 -0
  3. package/changelog/1/28/1.md +23 -0
  4. package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
  5. package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
  6. package/dist/v1/TinyAnalogClock.min.js +1 -1
  7. package/dist/v1/TinyBasicsEs.min.js +1 -2
  8. package/dist/v1/TinyDayNightCycle.min.js +1 -1
  9. package/dist/v1/TinyDragDropDetector.min.js +1 -1
  10. package/dist/v1/TinyDragger.min.js +1 -1
  11. package/dist/v1/TinyElementObserver.min.js +1 -1
  12. package/dist/v1/TinyEssentials.min.js +1 -2
  13. package/dist/v1/TinyGamepad.min.js +1 -1
  14. package/dist/v1/TinyHtml.min.js +1 -1
  15. package/dist/v1/TinyI18.min.js +1 -1
  16. package/dist/v1/TinyIframeEvents.min.js +1 -1
  17. package/dist/v1/TinyLocalStorage.min.js +1 -1
  18. package/dist/v1/TinyMediaPlayer.min.js +1 -1
  19. package/dist/v1/TinyNewWinEvents.min.js +1 -1
  20. package/dist/v1/TinyNotifyCenter.min.js +1 -1
  21. package/dist/v1/TinyRadioFm.min.js +1 -1
  22. package/dist/v1/TinyRateLimiter.min.js +1 -1
  23. package/dist/v1/TinySmartScroller.min.js +1 -1
  24. package/dist/v1/TinyTextDiffer.min.js +1 -1
  25. package/dist/v1/TinyTextarea.min.js +1 -1
  26. package/dist/v1/TinyTimeout.min.js +1 -1
  27. package/dist/v1/TinyToastNotify.min.js +1 -1
  28. package/dist/v1/TinyUploadClicker.min.js +1 -2
  29. package/dist/v1/UltraRandomMsgGen.min.js +1 -1
  30. package/dist/v1/basics/array.cjs +57 -9
  31. package/dist/v1/basics/array.d.mts +27 -11
  32. package/dist/v1/basics/array.mjs +51 -9
  33. package/dist/v1/basics/clock.cjs +94 -42
  34. package/dist/v1/basics/clock.d.mts +10 -10
  35. package/dist/v1/basics/clock.mjs +91 -54
  36. package/dist/v1/basics/deprecated/html.cjs +246 -0
  37. package/dist/v1/basics/deprecated/html.d.mts +63 -0
  38. package/dist/v1/basics/deprecated/html.mjs +190 -0
  39. package/dist/v1/basics/extendObjType/Buffer.cjs +13 -0
  40. package/dist/v1/basics/extendObjType/Buffer.d.mts +4 -0
  41. package/dist/v1/basics/extendObjType/Buffer.mjs +8 -0
  42. package/dist/v1/basics/html.cjs +149 -130
  43. package/dist/v1/basics/html.d.mts +26 -61
  44. package/dist/v1/basics/html.mjs +113 -105
  45. package/dist/v1/basics/index.cjs +2 -0
  46. package/dist/v1/basics/index.d.mts +3 -1
  47. package/dist/v1/basics/index.mjs +3 -3
  48. package/dist/v1/basics/mediaContent.cjs +51 -9
  49. package/dist/v1/basics/mediaContent.d.mts +0 -87
  50. package/dist/v1/basics/mediaContent.mjs +40 -10
  51. package/dist/v1/basics/objChecker.cjs +7 -5
  52. package/dist/v1/basics/objChecker.d.mts +3 -2
  53. package/dist/v1/basics/objChecker.mjs +6 -6
  54. package/dist/v1/basics/objFilter.cjs +40 -18
  55. package/dist/v1/basics/objFilter.d.mts +6 -6
  56. package/dist/v1/basics/objFilter.mjs +32 -17
  57. package/dist/v1/basics/simpleMath.cjs +81 -25
  58. package/dist/v1/basics/simpleMath.d.mts +2 -2
  59. package/dist/v1/basics/simpleMath.mjs +70 -27
  60. package/dist/v1/basics/text.cjs +76 -31
  61. package/dist/v1/basics/text.d.mts +69 -7
  62. package/dist/v1/basics/text.mjs +62 -31
  63. package/dist/v1/index.cjs +4 -2
  64. package/dist/v1/index.d.mts +4 -2
  65. package/dist/v1/index.mjs +4 -4
  66. package/dist/v1/libs/TinyAdvancedRaffle.cjs +71 -255
  67. package/dist/v1/libs/TinyAdvancedRaffle.d.mts +6 -134
  68. package/dist/v1/libs/TinyAdvancedRaffle.mjs +69 -228
  69. package/dist/v1/libs/TinyAfterScrollWatcher.cjs +17 -1
  70. package/dist/v1/libs/TinyAfterScrollWatcher.d.mts +4 -1
  71. package/dist/v1/libs/TinyAfterScrollWatcher.mjs +15 -1
  72. package/dist/v1/libs/TinyAnalogClock.cjs +60 -0
  73. package/dist/v1/libs/TinyAnalogClock.d.mts +6 -0
  74. package/dist/v1/libs/TinyAnalogClock.mjs +57 -0
  75. package/dist/v1/libs/TinyClipboard.cjs +2 -0
  76. package/dist/v1/libs/TinyClipboard.d.mts +4 -2
  77. package/dist/v1/libs/TinyClipboard.mjs +2 -0
  78. package/dist/v1/libs/TinyColorConverter.cjs +1 -0
  79. package/dist/v1/libs/TinyColorConverter.d.mts +2 -1
  80. package/dist/v1/libs/TinyColorConverter.mjs +1 -0
  81. package/dist/v1/libs/TinyDayNightCycle.cjs +67 -42
  82. package/dist/v1/libs/TinyDayNightCycle.d.mts +2 -7
  83. package/dist/v1/libs/TinyDayNightCycle.mjs +65 -41
  84. package/dist/v1/libs/TinyDragDropDetector.cjs +25 -0
  85. package/dist/v1/libs/TinyDragDropDetector.d.mts +6 -0
  86. package/dist/v1/libs/TinyDragDropDetector.mjs +22 -0
  87. package/dist/v1/libs/TinyDragger.cjs +46 -19
  88. package/dist/v1/libs/TinyDragger.mjs +45 -18
  89. package/dist/v1/libs/TinyElementObserver.cjs +39 -0
  90. package/dist/v1/libs/TinyElementObserver.d.mts +6 -0
  91. package/dist/v1/libs/TinyElementObserver.mjs +36 -0
  92. package/dist/v1/libs/TinyGamepad.cjs +131 -1
  93. package/dist/v1/libs/TinyGamepad.mjs +129 -1
  94. package/dist/v1/libs/TinyHtml.cjs +1 -1
  95. package/dist/v1/libs/TinyHtml.mjs +2 -2
  96. package/dist/v1/libs/TinyI18.cjs +61 -0
  97. package/dist/v1/libs/TinyI18.d.mts +14 -0
  98. package/dist/v1/libs/TinyI18.mjs +55 -0
  99. package/dist/v1/libs/TinyIframeEvents.cjs +26 -196
  100. package/dist/v1/libs/TinyIframeEvents.d.mts +3 -122
  101. package/dist/v1/libs/TinyIframeEvents.mjs +26 -171
  102. package/dist/v1/libs/TinyLocalStorage.cjs +53 -205
  103. package/dist/v1/libs/TinyLocalStorage.d.mts +6 -127
  104. package/dist/v1/libs/TinyLocalStorage.mjs +50 -178
  105. package/dist/v1/libs/TinyMediaPlayer.cjs +74 -2
  106. package/dist/v1/libs/TinyMediaPlayer.d.mts +12 -2
  107. package/dist/v1/libs/TinyMediaPlayer.mjs +68 -2
  108. package/dist/v1/libs/TinyNewWinEvents.cjs +39 -197
  109. package/dist/v1/libs/TinyNewWinEvents.d.mts +6 -119
  110. package/dist/v1/libs/TinyNewWinEvents.mjs +36 -172
  111. package/dist/v1/libs/TinyNotifyCenter.cjs +36 -0
  112. package/dist/v1/libs/TinyNotifyCenter.d.mts +6 -0
  113. package/dist/v1/libs/TinyNotifyCenter.mjs +32 -0
  114. package/dist/v1/libs/TinyRadioFm.cjs +40 -3
  115. package/dist/v1/libs/TinyRadioFm.d.mts +3 -3
  116. package/dist/v1/libs/TinyRadioFm.mjs +37 -3
  117. package/dist/v1/libs/TinyRateLimiter.cjs +52 -0
  118. package/dist/v1/libs/TinyRateLimiter.d.mts +6 -0
  119. package/dist/v1/libs/TinyRateLimiter.mjs +48 -0
  120. package/dist/v1/libs/TinySmartScroller.cjs +56 -205
  121. package/dist/v1/libs/TinySmartScroller.d.mts +19 -152
  122. package/dist/v1/libs/TinySmartScroller.mjs +55 -181
  123. package/dist/v1/libs/TinyTextDiffer.cjs +19 -18
  124. package/dist/v1/libs/TinyTextDiffer.d.mts +1 -0
  125. package/dist/v1/libs/TinyTextDiffer.mjs +16 -17
  126. package/dist/v1/libs/TinyTextarea.cjs +19 -0
  127. package/dist/v1/libs/TinyTextarea.d.mts +6 -0
  128. package/dist/v1/libs/TinyTextarea.mjs +17 -0
  129. package/dist/v1/libs/TinyTimeout.cjs +11 -1
  130. package/dist/v1/libs/TinyTimeout.mjs +9 -1
  131. package/dist/v1/libs/TinyToastNotify.cjs +35 -0
  132. package/dist/v1/libs/TinyToastNotify.d.mts +6 -0
  133. package/dist/v1/libs/TinyToastNotify.mjs +31 -0
  134. package/dist/v1/libs/TinyUploadClicker.cjs +20 -0
  135. package/dist/v1/libs/TinyUploadClicker.d.mts +6 -0
  136. package/dist/v1/libs/TinyUploadClicker.mjs +17 -0
  137. package/dist/v1/libs/UltraRandomMsgGen.cjs +148 -35
  138. package/dist/v1/libs/UltraRandomMsgGen.d.mts +17 -3
  139. package/dist/v1/libs/UltraRandomMsgGen.mjs +133 -34
  140. package/dist/v1/libs/utils.cjs +21 -0
  141. package/dist/v1/libs/utils.d.mts +2 -0
  142. package/dist/v1/libs/utils.mjs +16 -0
  143. package/docs/v1/Exports.md +0 -1
  144. package/docs/v1/README.md +1 -1
  145. package/docs/v1/basics/array.md +2 -2
  146. package/docs/v1/basics/deprecated/html.md +17 -0
  147. package/docs/v1/basics/fullScreen.md +1 -1
  148. package/docs/v1/basics/html.md +12 -26
  149. package/docs/v1/basics/mediaContent.md +1 -1
  150. package/docs/v1/basics/objChecker.md +1 -1
  151. package/docs/v1/basics/objFilter.md +1 -1
  152. package/docs/v1/basics/text.md +2 -2
  153. package/docs/v1/fileManager/main.md +1 -1
  154. package/docs/v1/libs/ColorSafeStringify.md +1 -1
  155. package/docs/v1/libs/TinyAfterScrollWatcher.md +1 -1
  156. package/docs/v1/libs/TinyAnalogClock.md +1 -1
  157. package/docs/v1/libs/TinyArrayComparator.md +1 -1
  158. package/docs/v1/libs/TinyArrayPaginator.md +1 -1
  159. package/docs/v1/libs/TinyClassManager/TinyPluginInliner.md +2 -2
  160. package/docs/v1/libs/TinyClassManager.md +1 -1
  161. package/docs/v1/libs/TinyClipboard.md +1 -1
  162. package/docs/v1/libs/TinyCookieConsent.md +1 -1
  163. package/docs/v1/libs/TinyDomReadyManager.md +1 -1
  164. package/docs/v1/libs/TinyDragDropDetector.md +1 -1
  165. package/docs/v1/libs/TinyDragger.md +1 -1
  166. package/docs/v1/libs/TinyElementObserver.md +1 -1
  167. package/docs/v1/libs/TinyHtml.md +1 -1
  168. package/docs/v1/libs/TinyIframeEvents.md +10 -10
  169. package/docs/v1/libs/TinyLevelUp.md +1 -1
  170. package/docs/v1/libs/TinyLoadingScreen.md +1 -1
  171. package/docs/v1/libs/TinyLocalStorage.md +3 -3
  172. package/docs/v1/libs/TinyNewWinEvents.md +10 -11
  173. package/docs/v1/libs/TinyNotifications.md +1 -1
  174. package/docs/v1/libs/TinyPromiseQueue.md +2 -2
  175. package/docs/v1/libs/TinyTextDiffer.md +1 -1
  176. package/docs/v1/libs/TinyTextarea.md +1 -1
  177. package/package.json +15 -5
  178. package/dist/v1/TinyBasicsEs.min.js.LICENSE.txt +0 -8
  179. package/dist/v1/TinyEssentials.min.js.LICENSE.txt +0 -8
  180. package/dist/v1/TinyEvents.min.js +0 -1
  181. package/dist/v1/TinyUploadClicker.min.js.LICENSE.txt +0 -8
  182. package/dist/v1/build/TinyEvents.cjs +0 -7
  183. package/dist/v1/build/TinyEvents.d.mts +0 -3
  184. package/dist/v1/build/TinyEvents.mjs +0 -2
  185. package/dist/v1/libs/TinyEvents.cjs +0 -395
  186. package/dist/v1/libs/TinyEvents.d.mts +0 -164
  187. package/dist/v1/libs/TinyEvents.mjs +0 -360
  188. package/docs/v1/libs/TinyEvents.md +0 -187
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ var utils = require('./utils.cjs');
4
+
5
+ const checkDestroy = utils.createCheckDestroyed('TinyRateLimiter');
6
+
3
7
  /**
4
8
  * Callback triggered when a group's stored hit history exceeds
5
9
  * the configured memory limit.
@@ -70,6 +74,21 @@
70
74
  * and safe to use in long-running Node.js processes.
71
75
  */
72
76
  class TinyRateLimiter {
77
+ /**
78
+ * Tracks whether the instance has been destroyed.
79
+ * @type {boolean}
80
+ */
81
+ #destroyed = false;
82
+
83
+ /**
84
+ * Gets the current destruction status of the instance.
85
+ *
86
+ * @returns {boolean} True if the instance is destroyed, false otherwise.
87
+ */
88
+ get destroyed() {
89
+ return this.#destroyed;
90
+ }
91
+
73
92
  /**
74
93
  * Maximum number of timestamps stored per group.
75
94
  * When exceeded, older entries are discarded.
@@ -186,6 +205,7 @@ class TinyRateLimiter {
186
205
  * @param {OnMemoryExceeded} callback
187
206
  */
188
207
  setOnMemoryExceeded(callback) {
208
+ checkDestroy(this.#destroyed);
189
209
  if (typeof callback !== 'function') throw new Error('onMemoryExceeded must be a function');
190
210
  this.#onMemoryExceeded = callback;
191
211
  }
@@ -194,6 +214,7 @@ class TinyRateLimiter {
194
214
  * Removes the memory-exceeded callback.
195
215
  */
196
216
  clearOnMemoryExceeded() {
217
+ checkDestroy(this.#destroyed);
197
218
  this.#onMemoryExceeded = null;
198
219
  }
199
220
 
@@ -213,6 +234,7 @@ class TinyRateLimiter {
213
234
  * @param {OnGroupExpired} callback - A function that receives the expired groupId.
214
235
  */
215
236
  setOnGroupExpired(callback) {
237
+ checkDestroy(this.#destroyed);
216
238
  if (typeof callback !== 'function') throw new Error('onGroupExpired must be a function');
217
239
  this.#onGroupExpired = callback;
218
240
  }
@@ -221,6 +243,7 @@ class TinyRateLimiter {
221
243
  * Removes the group-expiration callback.
222
244
  */
223
245
  clearOnGroupExpired() {
246
+ checkDestroy(this.#destroyed);
224
247
  this.#onGroupExpired = null;
225
248
  }
226
249
 
@@ -285,6 +308,7 @@ class TinyRateLimiter {
285
308
  * @returns {boolean}
286
309
  */
287
310
  isGroupId(id) {
311
+ checkDestroy(this.#destroyed);
288
312
  const result = this.groupFlags.get(id);
289
313
  return typeof result === 'boolean' ? result : false;
290
314
  }
@@ -295,6 +319,7 @@ class TinyRateLimiter {
295
319
  * @returns {string[]}
296
320
  */
297
321
  getUsersInGroup(groupId) {
322
+ checkDestroy(this.#destroyed);
298
323
  const users = [];
299
324
  for (const [userId, assignedGroup] of this.userToGroup.entries()) {
300
325
  if (assignedGroup === groupId) {
@@ -311,6 +336,7 @@ class TinyRateLimiter {
311
336
  * @param {number} ttl
312
337
  */
313
338
  setGroupTTL(groupId, ttl) {
339
+ checkDestroy(this.#destroyed);
314
340
  if (typeof ttl !== 'number' || !Number.isFinite(ttl) || ttl <= 0)
315
341
  throw new Error('TTL must be a positive number in milliseconds');
316
342
  this.groupTTL.set(groupId, ttl);
@@ -322,6 +348,7 @@ class TinyRateLimiter {
322
348
  * @returns {number|null}
323
349
  */
324
350
  getGroupTTL(groupId) {
351
+ checkDestroy(this.#destroyed);
325
352
  return this.groupTTL.get(groupId) ?? null;
326
353
  }
327
354
 
@@ -330,6 +357,7 @@ class TinyRateLimiter {
330
357
  * @param {string} groupId
331
358
  */
332
359
  deleteGroupTTL(groupId) {
360
+ checkDestroy(this.#destroyed);
333
361
  this.groupTTL.delete(groupId);
334
362
  }
335
363
 
@@ -344,6 +372,7 @@ class TinyRateLimiter {
344
372
  * @throws {Error} If the user belongs to another group
345
373
  */
346
374
  assignToGroup(userId, groupId) {
375
+ checkDestroy(this.#destroyed);
347
376
  const existingGroup = this.userToGroup.get(userId);
348
377
  if (existingGroup && existingGroup !== groupId)
349
378
  throw new Error(`User ${userId} is already assigned to group ${existingGroup}`);
@@ -391,6 +420,7 @@ class TinyRateLimiter {
391
420
  * @returns {string}
392
421
  */
393
422
  getGroupId(userId) {
423
+ checkDestroy(this.#destroyed);
394
424
  return this.userToGroup.get(userId) || userId; // fallback: use userId as own group
395
425
  }
396
426
 
@@ -417,6 +447,7 @@ class TinyRateLimiter {
417
447
  * @param {string} userId
418
448
  */
419
449
  hit(userId) {
450
+ checkDestroy(this.#destroyed);
420
451
  const groupId = this.getGroupId(userId);
421
452
  const now = Date.now();
422
453
 
@@ -470,6 +501,7 @@ class TinyRateLimiter {
470
501
  * @returns {boolean}
471
502
  */
472
503
  isRateLimited(userId) {
504
+ checkDestroy(this.#destroyed);
473
505
  const groupId = this.getGroupId(userId);
474
506
  if (!this.groupData.has(groupId)) return false;
475
507
 
@@ -501,6 +533,7 @@ class TinyRateLimiter {
501
533
  * @param {string} groupId
502
534
  */
503
535
  resetGroup(groupId) {
536
+ checkDestroy(this.#destroyed);
504
537
  this.groupFlags.delete(groupId);
505
538
  this.groupData.delete(groupId);
506
539
  this.lastSeen.delete(groupId);
@@ -512,6 +545,7 @@ class TinyRateLimiter {
512
545
  * @param {string} userId
513
546
  */
514
547
  resetUserGroup(userId) {
548
+ checkDestroy(this.#destroyed);
515
549
  this.userToGroup.delete(userId);
516
550
  }
517
551
 
@@ -521,6 +555,7 @@ class TinyRateLimiter {
521
555
  * @param {number[]} timestamps
522
556
  */
523
557
  setData(groupId, timestamps) {
558
+ checkDestroy(this.#destroyed);
524
559
  if (!Array.isArray(timestamps)) throw new Error('timestamps must be an array of numbers.');
525
560
  for (const t of timestamps) {
526
561
  if (typeof t !== 'number' || !Number.isFinite(t)) {
@@ -538,6 +573,7 @@ class TinyRateLimiter {
538
573
  * @returns {boolean}
539
574
  */
540
575
  hasData(groupId) {
576
+ checkDestroy(this.#destroyed);
541
577
  return this.groupData.has(groupId);
542
578
  }
543
579
 
@@ -547,6 +583,7 @@ class TinyRateLimiter {
547
583
  * @returns {number[]}
548
584
  */
549
585
  getData(groupId) {
586
+ checkDestroy(this.#destroyed);
550
587
  return this.groupData.get(groupId) || [];
551
588
  }
552
589
 
@@ -555,6 +592,7 @@ class TinyRateLimiter {
555
592
  * @returns {number}
556
593
  */
557
594
  getMaxIdle() {
595
+ checkDestroy(this.#destroyed);
558
596
  if (typeof this.#maxIdle !== 'number' || !Number.isFinite(this.#maxIdle) || this.#maxIdle < 0) {
559
597
  throw new Error("'maxIdle' must be a non-negative finite number.");
560
598
  }
@@ -566,6 +604,7 @@ class TinyRateLimiter {
566
604
  * @param {number} ms
567
605
  */
568
606
  setMaxIdle(ms) {
607
+ checkDestroy(this.#destroyed);
569
608
  if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) {
570
609
  throw new Error("'maxIdle' must be a non-negative finite number.");
571
610
  }
@@ -599,6 +638,7 @@ class TinyRateLimiter {
599
638
  * @returns {string[]}
600
639
  */
601
640
  getActiveGroups() {
641
+ checkDestroy(this.#destroyed);
602
642
  return Array.from(this.groupData.keys());
603
643
  }
604
644
 
@@ -607,6 +647,7 @@ class TinyRateLimiter {
607
647
  * @returns {Record<string, string>}
608
648
  */
609
649
  getAllUserMappings() {
650
+ checkDestroy(this.#destroyed);
610
651
  return Object.fromEntries(this.userToGroup);
611
652
  }
612
653
 
@@ -619,6 +660,7 @@ class TinyRateLimiter {
619
660
  * @returns {number}
620
661
  */
621
662
  getInterval() {
663
+ checkDestroy(this.#destroyed);
622
664
  if (typeof this.#interval !== 'number' || !Number.isFinite(this.#interval)) {
623
665
  throw new Error("'interval' is not a valid finite number.");
624
666
  }
@@ -630,6 +672,7 @@ class TinyRateLimiter {
630
672
  * @returns {number}
631
673
  */
632
674
  getMaxHits() {
675
+ checkDestroy(this.#destroyed);
633
676
  if (typeof this.#maxHits !== 'number' || !Number.isFinite(this.#maxHits)) {
634
677
  throw new Error("'maxHits' is not a valid finite number.");
635
678
  }
@@ -642,6 +685,7 @@ class TinyRateLimiter {
642
685
  * @returns {number}
643
686
  */
644
687
  getTotalHits(groupId) {
688
+ checkDestroy(this.#destroyed);
645
689
  const history = this.groupData.get(groupId);
646
690
  return Array.isArray(history) ? history.length : 0;
647
691
  }
@@ -652,6 +696,7 @@ class TinyRateLimiter {
652
696
  * @returns {number|null}
653
697
  */
654
698
  getLastHit(groupId) {
699
+ checkDestroy(this.#destroyed);
655
700
  const history = this.groupData.get(groupId);
656
701
  return history?.length ? history[history.length - 1] : null;
657
702
  }
@@ -662,6 +707,7 @@ class TinyRateLimiter {
662
707
  * @returns {number|null}
663
708
  */
664
709
  getTimeSinceLastHit(groupId) {
710
+ checkDestroy(this.#destroyed);
665
711
  const last = this.getLastHit(groupId);
666
712
  return last !== null ? Date.now() - last : null;
667
713
  }
@@ -687,6 +733,7 @@ class TinyRateLimiter {
687
733
  * @returns {number|null}
688
734
  */
689
735
  getAverageHitSpacing(groupId) {
736
+ checkDestroy(this.#destroyed);
690
737
  return this._calculateAverageSpacing(this.groupData.get(groupId));
691
738
  }
692
739
 
@@ -701,6 +748,7 @@ class TinyRateLimiter {
701
748
  * }}
702
749
  */
703
750
  getMetrics(groupId) {
751
+ checkDestroy(this.#destroyed);
704
752
  const history = this.groupData.get(groupId);
705
753
 
706
754
  if (!Array.isArray(history) || history.length === 0) {
@@ -729,6 +777,7 @@ class TinyRateLimiter {
729
777
  * Destroy the rate limiter, stopping cleanup and clearing data
730
778
  */
731
779
  destroy() {
780
+ if (this.destroyed) return;
732
781
  if (this.#cleanupTimer) clearInterval(this.#cleanupTimer);
733
782
  this._cleanup();
734
783
  this.groupData.clear();
@@ -736,6 +785,9 @@ class TinyRateLimiter {
736
785
  this.userToGroup.clear();
737
786
  this.groupTTL.clear();
738
787
  this.groupFlags.clear();
788
+
789
+ // Lock the instance
790
+ this.#destroyed = true;
739
791
  }
740
792
  }
741
793
 
@@ -106,6 +106,12 @@ declare class TinyRateLimiter {
106
106
  cleanupInterval?: number | undefined;
107
107
  maxIdle?: number | undefined;
108
108
  });
109
+ /**
110
+ * Gets the current destruction status of the instance.
111
+ *
112
+ * @returns {boolean} True if the instance is destroyed, false otherwise.
113
+ */
114
+ get destroyed(): boolean;
109
115
  /**
110
116
  * Stores hit timestamps per group.
111
117
  *
@@ -1,3 +1,5 @@
1
+ import { createCheckDestroyed } from './utils.mjs';
2
+ const checkDestroy = createCheckDestroyed('TinyRateLimiter');
1
3
  /**
2
4
  * Callback triggered when a group's stored hit history exceeds
3
5
  * the configured memory limit.
@@ -66,6 +68,19 @@
66
68
  * and safe to use in long-running Node.js processes.
67
69
  */
68
70
  class TinyRateLimiter {
71
+ /**
72
+ * Tracks whether the instance has been destroyed.
73
+ * @type {boolean}
74
+ */
75
+ #destroyed = false;
76
+ /**
77
+ * Gets the current destruction status of the instance.
78
+ *
79
+ * @returns {boolean} True if the instance is destroyed, false otherwise.
80
+ */
81
+ get destroyed() {
82
+ return this.#destroyed;
83
+ }
69
84
  /**
70
85
  * Maximum number of timestamps stored per group.
71
86
  * When exceeded, older entries are discarded.
@@ -170,6 +185,7 @@ class TinyRateLimiter {
170
185
  * @param {OnMemoryExceeded} callback
171
186
  */
172
187
  setOnMemoryExceeded(callback) {
188
+ checkDestroy(this.#destroyed);
173
189
  if (typeof callback !== 'function')
174
190
  throw new Error('onMemoryExceeded must be a function');
175
191
  this.#onMemoryExceeded = callback;
@@ -178,6 +194,7 @@ class TinyRateLimiter {
178
194
  * Removes the memory-exceeded callback.
179
195
  */
180
196
  clearOnMemoryExceeded() {
197
+ checkDestroy(this.#destroyed);
181
198
  this.#onMemoryExceeded = null;
182
199
  }
183
200
  /**
@@ -195,6 +212,7 @@ class TinyRateLimiter {
195
212
  * @param {OnGroupExpired} callback - A function that receives the expired groupId.
196
213
  */
197
214
  setOnGroupExpired(callback) {
215
+ checkDestroy(this.#destroyed);
198
216
  if (typeof callback !== 'function')
199
217
  throw new Error('onGroupExpired must be a function');
200
218
  this.#onGroupExpired = callback;
@@ -203,6 +221,7 @@ class TinyRateLimiter {
203
221
  * Removes the group-expiration callback.
204
222
  */
205
223
  clearOnGroupExpired() {
224
+ checkDestroy(this.#destroyed);
206
225
  this.#onGroupExpired = null;
207
226
  }
208
227
  /**
@@ -262,6 +281,7 @@ class TinyRateLimiter {
262
281
  * @returns {boolean}
263
282
  */
264
283
  isGroupId(id) {
284
+ checkDestroy(this.#destroyed);
265
285
  const result = this.groupFlags.get(id);
266
286
  return typeof result === 'boolean' ? result : false;
267
287
  }
@@ -271,6 +291,7 @@ class TinyRateLimiter {
271
291
  * @returns {string[]}
272
292
  */
273
293
  getUsersInGroup(groupId) {
294
+ checkDestroy(this.#destroyed);
274
295
  const users = [];
275
296
  for (const [userId, assignedGroup] of this.userToGroup.entries()) {
276
297
  if (assignedGroup === groupId) {
@@ -285,6 +306,7 @@ class TinyRateLimiter {
285
306
  * @param {number} ttl
286
307
  */
287
308
  setGroupTTL(groupId, ttl) {
309
+ checkDestroy(this.#destroyed);
288
310
  if (typeof ttl !== 'number' || !Number.isFinite(ttl) || ttl <= 0)
289
311
  throw new Error('TTL must be a positive number in milliseconds');
290
312
  this.groupTTL.set(groupId, ttl);
@@ -295,6 +317,7 @@ class TinyRateLimiter {
295
317
  * @returns {number|null}
296
318
  */
297
319
  getGroupTTL(groupId) {
320
+ checkDestroy(this.#destroyed);
298
321
  return this.groupTTL.get(groupId) ?? null;
299
322
  }
300
323
  /**
@@ -302,6 +325,7 @@ class TinyRateLimiter {
302
325
  * @param {string} groupId
303
326
  */
304
327
  deleteGroupTTL(groupId) {
328
+ checkDestroy(this.#destroyed);
305
329
  this.groupTTL.delete(groupId);
306
330
  }
307
331
  /**
@@ -315,6 +339,7 @@ class TinyRateLimiter {
315
339
  * @throws {Error} If the user belongs to another group
316
340
  */
317
341
  assignToGroup(userId, groupId) {
342
+ checkDestroy(this.#destroyed);
318
343
  const existingGroup = this.userToGroup.get(userId);
319
344
  if (existingGroup && existingGroup !== groupId)
320
345
  throw new Error(`User ${userId} is already assigned to group ${existingGroup}`);
@@ -363,6 +388,7 @@ class TinyRateLimiter {
363
388
  * @returns {string}
364
389
  */
365
390
  getGroupId(userId) {
391
+ checkDestroy(this.#destroyed);
366
392
  return this.userToGroup.get(userId) || userId; // fallback: use userId as own group
367
393
  }
368
394
  /**
@@ -388,6 +414,7 @@ class TinyRateLimiter {
388
414
  * @param {string} userId
389
415
  */
390
416
  hit(userId) {
417
+ checkDestroy(this.#destroyed);
391
418
  const groupId = this.getGroupId(userId);
392
419
  const now = Date.now();
393
420
  if (!this.groupData.has(groupId)) {
@@ -437,6 +464,7 @@ class TinyRateLimiter {
437
464
  * @returns {boolean}
438
465
  */
439
466
  isRateLimited(userId) {
467
+ checkDestroy(this.#destroyed);
440
468
  const groupId = this.getGroupId(userId);
441
469
  if (!this.groupData.has(groupId))
442
470
  return false;
@@ -468,6 +496,7 @@ class TinyRateLimiter {
468
496
  * @param {string} groupId
469
497
  */
470
498
  resetGroup(groupId) {
499
+ checkDestroy(this.#destroyed);
471
500
  this.groupFlags.delete(groupId);
472
501
  this.groupData.delete(groupId);
473
502
  this.lastSeen.delete(groupId);
@@ -478,6 +507,7 @@ class TinyRateLimiter {
478
507
  * @param {string} userId
479
508
  */
480
509
  resetUserGroup(userId) {
510
+ checkDestroy(this.#destroyed);
481
511
  this.userToGroup.delete(userId);
482
512
  }
483
513
  /**
@@ -486,6 +516,7 @@ class TinyRateLimiter {
486
516
  * @param {number[]} timestamps
487
517
  */
488
518
  setData(groupId, timestamps) {
519
+ checkDestroy(this.#destroyed);
489
520
  if (!Array.isArray(timestamps))
490
521
  throw new Error('timestamps must be an array of numbers.');
491
522
  for (const t of timestamps) {
@@ -504,6 +535,7 @@ class TinyRateLimiter {
504
535
  * @returns {boolean}
505
536
  */
506
537
  hasData(groupId) {
538
+ checkDestroy(this.#destroyed);
507
539
  return this.groupData.has(groupId);
508
540
  }
509
541
  /**
@@ -512,6 +544,7 @@ class TinyRateLimiter {
512
544
  * @returns {number[]}
513
545
  */
514
546
  getData(groupId) {
547
+ checkDestroy(this.#destroyed);
515
548
  return this.groupData.get(groupId) || [];
516
549
  }
517
550
  /**
@@ -519,6 +552,7 @@ class TinyRateLimiter {
519
552
  * @returns {number}
520
553
  */
521
554
  getMaxIdle() {
555
+ checkDestroy(this.#destroyed);
522
556
  if (typeof this.#maxIdle !== 'number' || !Number.isFinite(this.#maxIdle) || this.#maxIdle < 0) {
523
557
  throw new Error("'maxIdle' must be a non-negative finite number.");
524
558
  }
@@ -529,6 +563,7 @@ class TinyRateLimiter {
529
563
  * @param {number} ms
530
564
  */
531
565
  setMaxIdle(ms) {
566
+ checkDestroy(this.#destroyed);
532
567
  if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) {
533
568
  throw new Error("'maxIdle' must be a non-negative finite number.");
534
569
  }
@@ -559,6 +594,7 @@ class TinyRateLimiter {
559
594
  * @returns {string[]}
560
595
  */
561
596
  getActiveGroups() {
597
+ checkDestroy(this.#destroyed);
562
598
  return Array.from(this.groupData.keys());
563
599
  }
564
600
  /**
@@ -566,6 +602,7 @@ class TinyRateLimiter {
566
602
  * @returns {Record<string, string>}
567
603
  */
568
604
  getAllUserMappings() {
605
+ checkDestroy(this.#destroyed);
569
606
  return Object.fromEntries(this.userToGroup);
570
607
  }
571
608
  /**
@@ -577,6 +614,7 @@ class TinyRateLimiter {
577
614
  * @returns {number}
578
615
  */
579
616
  getInterval() {
617
+ checkDestroy(this.#destroyed);
580
618
  if (typeof this.#interval !== 'number' || !Number.isFinite(this.#interval)) {
581
619
  throw new Error("'interval' is not a valid finite number.");
582
620
  }
@@ -587,6 +625,7 @@ class TinyRateLimiter {
587
625
  * @returns {number}
588
626
  */
589
627
  getMaxHits() {
628
+ checkDestroy(this.#destroyed);
590
629
  if (typeof this.#maxHits !== 'number' || !Number.isFinite(this.#maxHits)) {
591
630
  throw new Error("'maxHits' is not a valid finite number.");
592
631
  }
@@ -598,6 +637,7 @@ class TinyRateLimiter {
598
637
  * @returns {number}
599
638
  */
600
639
  getTotalHits(groupId) {
640
+ checkDestroy(this.#destroyed);
601
641
  const history = this.groupData.get(groupId);
602
642
  return Array.isArray(history) ? history.length : 0;
603
643
  }
@@ -607,6 +647,7 @@ class TinyRateLimiter {
607
647
  * @returns {number|null}
608
648
  */
609
649
  getLastHit(groupId) {
650
+ checkDestroy(this.#destroyed);
610
651
  const history = this.groupData.get(groupId);
611
652
  return history?.length ? history[history.length - 1] : null;
612
653
  }
@@ -616,6 +657,7 @@ class TinyRateLimiter {
616
657
  * @returns {number|null}
617
658
  */
618
659
  getTimeSinceLastHit(groupId) {
660
+ checkDestroy(this.#destroyed);
619
661
  const last = this.getLastHit(groupId);
620
662
  return last !== null ? Date.now() - last : null;
621
663
  }
@@ -640,6 +682,7 @@ class TinyRateLimiter {
640
682
  * @returns {number|null}
641
683
  */
642
684
  getAverageHitSpacing(groupId) {
685
+ checkDestroy(this.#destroyed);
643
686
  return this._calculateAverageSpacing(this.groupData.get(groupId));
644
687
  }
645
688
  /**
@@ -653,6 +696,7 @@ class TinyRateLimiter {
653
696
  * }}
654
697
  */
655
698
  getMetrics(groupId) {
699
+ checkDestroy(this.#destroyed);
656
700
  const history = this.groupData.get(groupId);
657
701
  if (!Array.isArray(history) || history.length === 0) {
658
702
  return {
@@ -677,6 +721,8 @@ class TinyRateLimiter {
677
721
  * Destroy the rate limiter, stopping cleanup and clearing data
678
722
  */
679
723
  destroy() {
724
+ if (this.destroyed)
725
+ return;
680
726
  if (this.#cleanupTimer)
681
727
  clearInterval(this.#cleanupTimer);
682
728
  this._cleanup();
@@ -685,6 +731,8 @@ class TinyRateLimiter {
685
731
  this.userToGroup.clear();
686
732
  this.groupTTL.clear();
687
733
  this.groupFlags.clear();
734
+ // Lock the instance
735
+ this.#destroyed = true;
688
736
  }
689
737
  }
690
738
  export default TinyRateLimiter;