tiny-essentials 1.8.5 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/TinyEssentials.js +351 -28
- package/dist/TinyEssentials.min.js +1 -1
- package/dist/TinyLevelUp.js +125 -15
- package/dist/TinyLevelUp.min.js +1 -1
- package/dist/TinyPromiseQueue.js +27 -13
- package/dist/TinyPromiseQueue.min.js +1 -1
- package/dist/TinyRateLimiter.js +236 -0
- package/dist/TinyRateLimiter.min.js +1 -0
- package/dist/legacy/libs/userLevel.cjs +125 -15
- package/dist/legacy/libs/userLevel.d.mts +86 -59
- package/dist/legacy/libs/userLevel.mjs +123 -15
- package/dist/v1/build/TinyRateLimiter.cjs +7 -0
- package/dist/v1/build/TinyRateLimiter.d.mts +3 -0
- package/dist/v1/build/TinyRateLimiter.mjs +2 -0
- package/dist/v1/index.cjs +2 -0
- package/dist/v1/index.d.mts +2 -1
- package/dist/v1/index.mjs +2 -1
- package/dist/v1/libs/TinyPromiseQueue.cjs +27 -13
- package/dist/v1/libs/TinyPromiseQueue.d.mts +38 -0
- package/dist/v1/libs/TinyPromiseQueue.mjs +26 -13
- package/dist/v1/libs/TinyRateLimit.cjs +196 -0
- package/dist/v1/libs/TinyRateLimit.d.mts +86 -0
- package/dist/v1/libs/TinyRateLimit.mjs +171 -0
- package/dist/v1/libs/TinyRateLimiter.cjs +197 -0
- package/dist/v1/libs/TinyRateLimiter.d.mts +86 -0
- package/dist/v1/libs/TinyRateLimiter.mjs +173 -0
- package/docs/README.md +1 -0
- package/docs/libs/TinyLevelUp.md +208 -68
- package/docs/libs/TinyRateLimiter.md +156 -0
- package/package.json +3 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export default TinyRateLimiter;
|
|
2
|
+
/**
|
|
3
|
+
* Class representing a flexible rate limiter per user.
|
|
4
|
+
*
|
|
5
|
+
* This rate limiter can be configured by maximum number of hits,
|
|
6
|
+
* time interval, or a combination of both. It supports automatic
|
|
7
|
+
* cleanup of inactive users to optimize memory usage.
|
|
8
|
+
*
|
|
9
|
+
* @class
|
|
10
|
+
*/
|
|
11
|
+
declare class TinyRateLimiter {
|
|
12
|
+
/**
|
|
13
|
+
* @param {Object} options
|
|
14
|
+
* @param {number} [options.maxHits] - Max interactions allowed
|
|
15
|
+
* @param {number} [options.interval] - Time window in milliseconds
|
|
16
|
+
* @param {number} [options.cleanupInterval] - Interval for automatic cleanup (ms)
|
|
17
|
+
* @param {number} [options.maxIdle=300000] - Max idle time for a user before being cleaned (ms)
|
|
18
|
+
*/
|
|
19
|
+
constructor({ maxHits, interval, cleanupInterval, maxIdle }: {
|
|
20
|
+
maxHits?: number | undefined;
|
|
21
|
+
interval?: number | undefined;
|
|
22
|
+
cleanupInterval?: number | undefined;
|
|
23
|
+
maxIdle?: number | undefined;
|
|
24
|
+
});
|
|
25
|
+
maxHits: number | null | undefined;
|
|
26
|
+
interval: number | null | undefined;
|
|
27
|
+
cleanupInterval: number | null | undefined;
|
|
28
|
+
maxIdle: number;
|
|
29
|
+
/** @type {Map<string, number[]>} */
|
|
30
|
+
userData: Map<string, number[]>;
|
|
31
|
+
/** @type {Map<string, number>} */
|
|
32
|
+
lastSeen: Map<string, number>;
|
|
33
|
+
_cleanupTimer: NodeJS.Timeout | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Get the interval window in milliseconds.
|
|
36
|
+
*
|
|
37
|
+
* @returns {number} The interval value.
|
|
38
|
+
* @throws {Error} If interval is not a valid finite number.
|
|
39
|
+
*/
|
|
40
|
+
getInterval(): number;
|
|
41
|
+
/**
|
|
42
|
+
* Get the maximum number of allowed hits.
|
|
43
|
+
*
|
|
44
|
+
* @returns {number} The maxHits value.
|
|
45
|
+
* @throws {Error} If maxHits is not a valid finite number.
|
|
46
|
+
*/
|
|
47
|
+
getMaxHits(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Register a hit for a specific user
|
|
50
|
+
* @param {string} userId
|
|
51
|
+
*/
|
|
52
|
+
hit(userId: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Check if the user is currently rate limited
|
|
55
|
+
* @param {string} userId
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
isRateLimited(userId: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Manually reset user data
|
|
61
|
+
* @param {string} userId
|
|
62
|
+
*/
|
|
63
|
+
reset(userId: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Set hit timestamps for a user
|
|
66
|
+
* @param {string} userId
|
|
67
|
+
* @param {number[]} timestamps
|
|
68
|
+
*/
|
|
69
|
+
setData(userId: string, timestamps: number[]): void;
|
|
70
|
+
/**
|
|
71
|
+
* Get timestamps from user
|
|
72
|
+
* @param {string} userId
|
|
73
|
+
* @returns {number[]}
|
|
74
|
+
*/
|
|
75
|
+
getData(userId: string): number[];
|
|
76
|
+
/**
|
|
77
|
+
* Cleanup old/inactive users
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
private _cleanup;
|
|
81
|
+
/**
|
|
82
|
+
* Destroy the rate limiter, stopping all intervals
|
|
83
|
+
*/
|
|
84
|
+
destroy(): void;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=TinyRateLimiter.d.mts.map
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class representing a flexible rate limiter per user.
|
|
3
|
+
*
|
|
4
|
+
* This rate limiter can be configured by maximum number of hits,
|
|
5
|
+
* time interval, or a combination of both. It supports automatic
|
|
6
|
+
* cleanup of inactive users to optimize memory usage.
|
|
7
|
+
*
|
|
8
|
+
* @class
|
|
9
|
+
*/
|
|
10
|
+
class TinyRateLimiter {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @param {number} [options.maxHits] - Max interactions allowed
|
|
14
|
+
* @param {number} [options.interval] - Time window in milliseconds
|
|
15
|
+
* @param {number} [options.cleanupInterval] - Interval for automatic cleanup (ms)
|
|
16
|
+
* @param {number} [options.maxIdle=300000] - Max idle time for a user before being cleaned (ms)
|
|
17
|
+
*/
|
|
18
|
+
constructor({ maxHits, interval, cleanupInterval, maxIdle = 300000 }) {
|
|
19
|
+
/** @param {number|undefined} val */
|
|
20
|
+
const isPositiveInteger = (val) => typeof val === 'number' && Number.isFinite(val) && val >= 1 && Number.isInteger(val);
|
|
21
|
+
const isMaxHitsValid = isPositiveInteger(maxHits);
|
|
22
|
+
const isIntervalValid = isPositiveInteger(interval);
|
|
23
|
+
const isCleanupValid = isPositiveInteger(cleanupInterval);
|
|
24
|
+
const isMaxIdleValid = isPositiveInteger(maxIdle);
|
|
25
|
+
if (!isMaxHitsValid && !isIntervalValid)
|
|
26
|
+
throw new Error("RateLimiter requires at least one valid option: 'maxHits' or 'interval'.");
|
|
27
|
+
if (maxHits !== undefined && !isMaxHitsValid)
|
|
28
|
+
throw new Error("'maxHits' must be a positive integer if defined.");
|
|
29
|
+
if (interval !== undefined && !isIntervalValid)
|
|
30
|
+
throw new Error("'interval' must be a positive integer in milliseconds if defined.");
|
|
31
|
+
if (cleanupInterval !== undefined && !isCleanupValid)
|
|
32
|
+
throw new Error("'cleanupInterval' must be a positive integer in milliseconds if defined.");
|
|
33
|
+
if (!isMaxIdleValid)
|
|
34
|
+
throw new Error("'maxIdle' must be a positive integer in milliseconds.");
|
|
35
|
+
this.maxHits = isMaxHitsValid ? maxHits : null;
|
|
36
|
+
this.interval = isIntervalValid ? interval : null;
|
|
37
|
+
this.cleanupInterval = isCleanupValid ? cleanupInterval : null;
|
|
38
|
+
this.maxIdle = maxIdle;
|
|
39
|
+
/** @type {Map<string, number[]>} */
|
|
40
|
+
this.userData = new Map();
|
|
41
|
+
/** @type {Map<string, number>} */
|
|
42
|
+
this.lastSeen = new Map();
|
|
43
|
+
// Start automatic cleanup only if cleanupInterval is valid
|
|
44
|
+
if (this.cleanupInterval !== null)
|
|
45
|
+
this._cleanupTimer = setInterval(() => this._cleanup(), this.cleanupInterval);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the interval window in milliseconds.
|
|
49
|
+
*
|
|
50
|
+
* @returns {number} The interval value.
|
|
51
|
+
* @throws {Error} If interval is not a valid finite number.
|
|
52
|
+
*/
|
|
53
|
+
getInterval() {
|
|
54
|
+
if (typeof this.interval !== 'number' || !Number.isFinite(this.interval))
|
|
55
|
+
throw new Error("'interval' is not a valid finite number.");
|
|
56
|
+
return this.interval;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the maximum number of allowed hits.
|
|
60
|
+
*
|
|
61
|
+
* @returns {number} The maxHits value.
|
|
62
|
+
* @throws {Error} If maxHits is not a valid finite number.
|
|
63
|
+
*/
|
|
64
|
+
getMaxHits() {
|
|
65
|
+
if (typeof this.maxHits !== 'number' || !Number.isFinite(this.maxHits)) {
|
|
66
|
+
throw new Error("'maxHits' is not a valid finite number.");
|
|
67
|
+
}
|
|
68
|
+
return this.maxHits;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Register a hit for a specific user
|
|
72
|
+
* @param {string} userId
|
|
73
|
+
*/
|
|
74
|
+
hit(userId) {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
if (!this.userData.has(userId)) {
|
|
77
|
+
this.userData.set(userId, []);
|
|
78
|
+
}
|
|
79
|
+
const history = this.userData.get(userId);
|
|
80
|
+
if (!history)
|
|
81
|
+
throw new Error(`No data found for userId: ${userId}`);
|
|
82
|
+
history.push(now);
|
|
83
|
+
this.lastSeen.set(userId, now);
|
|
84
|
+
// Clean up old entries
|
|
85
|
+
if (this.interval !== null) {
|
|
86
|
+
const interval = this.getInterval();
|
|
87
|
+
const cutoff = now - interval;
|
|
88
|
+
while (history.length && history[0] < cutoff) {
|
|
89
|
+
history.shift();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Optional: keep only the last N entries for memory optimization
|
|
93
|
+
if (this.maxHits !== null) {
|
|
94
|
+
const maxHits = this.getMaxHits();
|
|
95
|
+
if (history.length > maxHits) {
|
|
96
|
+
history.splice(0, history.length - maxHits);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if the user is currently rate limited
|
|
102
|
+
* @param {string} userId
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
isRateLimited(userId) {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
if (!this.userData.has(userId))
|
|
108
|
+
return false;
|
|
109
|
+
const history = this.userData.get(userId);
|
|
110
|
+
if (!history)
|
|
111
|
+
throw new Error(`No data found for userId: ${userId}`);
|
|
112
|
+
if (this.interval !== null) {
|
|
113
|
+
const interval = this.getInterval();
|
|
114
|
+
const recent = history.filter((t) => t > now - interval);
|
|
115
|
+
if (this.maxHits !== null) {
|
|
116
|
+
return recent.length >= this.getMaxHits();
|
|
117
|
+
}
|
|
118
|
+
return recent.length > 0;
|
|
119
|
+
}
|
|
120
|
+
if (this.maxHits !== null) {
|
|
121
|
+
return history.length >= this.getMaxHits();
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Manually reset user data
|
|
127
|
+
* @param {string} userId
|
|
128
|
+
*/
|
|
129
|
+
reset(userId) {
|
|
130
|
+
this.userData.delete(userId);
|
|
131
|
+
this.lastSeen.delete(userId);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Set hit timestamps for a user
|
|
135
|
+
* @param {string} userId
|
|
136
|
+
* @param {number[]} timestamps
|
|
137
|
+
*/
|
|
138
|
+
setData(userId, timestamps) {
|
|
139
|
+
this.userData.set(userId, timestamps);
|
|
140
|
+
this.lastSeen.set(userId, Date.now());
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get timestamps from user
|
|
144
|
+
* @param {string} userId
|
|
145
|
+
* @returns {number[]}
|
|
146
|
+
*/
|
|
147
|
+
getData(userId) {
|
|
148
|
+
return this.userData.get(userId) || [];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Cleanup old/inactive users
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
_cleanup() {
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
for (const [userId, last] of this.lastSeen.entries()) {
|
|
157
|
+
if (now - last > this.maxIdle) {
|
|
158
|
+
this.userData.delete(userId);
|
|
159
|
+
this.lastSeen.delete(userId);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Destroy the rate limiter, stopping all intervals
|
|
165
|
+
*/
|
|
166
|
+
destroy() {
|
|
167
|
+
if (this._cleanupTimer)
|
|
168
|
+
clearInterval(this._cleanupTimer);
|
|
169
|
+
this.userData.clear();
|
|
170
|
+
this.lastSeen.clear();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export default TinyRateLimiter;
|
package/docs/README.md
CHANGED
|
@@ -23,6 +23,7 @@ This folder contains the core scripts we have worked on so far. Each file is a m
|
|
|
23
23
|
- 🗂️ **[TinyPromiseQueue](./libs/TinyPromiseQueue.md)** — A class that allows sequential execution of asynchronous tasks, supporting task delays, cancellation, and queue management.
|
|
24
24
|
- 🏅 **[TinyLevelUp](./libs/TinyLevelUp.md)** — A class to manage user level-up logic based on experience points, providing methods for experience validation, addition, removal, and calculation.
|
|
25
25
|
- 🎨 **[ColorSafeStringify](./libs/ColorSafeStringify.md)** — A utility for applying customizable ANSI colors to JSON strings in terminal outputs, supporting presets and fine-grained type-based highlighting.
|
|
26
|
+
- 🚦 **[TinyRateLimiter](./libs/TinyRateLimiter.md)** — A flexible per-user rate limiter supporting time windows, hit caps, and automatic cleanup of inactive users.
|
|
26
27
|
|
|
27
28
|
---
|
|
28
29
|
|
package/docs/libs/TinyLevelUp.md
CHANGED
|
@@ -1,108 +1,248 @@
|
|
|
1
|
+
# 📚 TinyLevelUp Documentation
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
A lightweight XP-based leveling system for managing user experience and levels. Great for games, communities, or reward-based applications! 🎮✨
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📐 Type Definitions
|
|
8
|
+
|
|
9
|
+
### `UserEditor` ✍️
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
Represents the structure of a user object used in the leveling system.
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
```ts
|
|
14
|
+
{
|
|
15
|
+
exp: number; // Current experience points of the user
|
|
16
|
+
level: number; // Current level of the user
|
|
17
|
+
totalExp: number; // Total accumulated experience
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
10
22
|
|
|
11
|
-
|
|
12
|
-
- `expLevel` (`number`): The base experience required to level up for each level. 📈
|
|
23
|
+
## 🏗️ Class: `TinyLevelUp`
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
Handles experience logic, leveling up/down, validation, and XP generation.
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
Validates and adjusts the user's level based on their current experience. If the user's experience is above or below the required threshold, their level is adjusted accordingly. ⚖️
|
|
27
|
+
### 🆕 Constructor
|
|
18
28
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
```ts
|
|
30
|
+
new TinyLevelUp(giveExp: number, expLevel: number)
|
|
31
|
+
```
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
* `giveExp`: Base XP value for random XP generation 🎲
|
|
34
|
+
* `expLevel`: Base XP required per level up 🔺
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **Returns**: `number` - The total experience of the user. 🔢
|
|
36
|
+
---
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
Generates random experience points based on a configured multiplier. 🎲
|
|
38
|
+
## 🧰 Methods
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
### ➕ `createUser()`
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
Creates a fresh user at level 1 with no XP.
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
```ts
|
|
45
|
+
createUser(): UserEditor
|
|
46
|
+
```
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
---
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
### 🔎 `validateUser(user)`
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
An alias for `progress`. Returns the experience points required to reach the next level. ⏩
|
|
52
|
+
Throws an error if the user object is malformed or contains invalid values.
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
```ts
|
|
55
|
+
validateUser(user: UserEditor): void
|
|
56
|
+
```
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
🚨 Throws if:
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
Sets the user's experience value and adjusts their level if necessary. 📝
|
|
60
|
+
* `exp`, `level`, or `totalExp` are not valid numbers.
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
- `value` (`number`): The new experience value to set for the user. 💡
|
|
62
|
+
---
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
### ✅ `isValidUser(user)`
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
Adds experience to the user and adjusts their level if necessary. Experience can be added with or without a multiplier. ➕
|
|
66
|
+
Checks if the given user object is valid (without throwing errors).
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- `multi` (`number`): Multiplier for experience generation. Default is `1`. 💥
|
|
68
|
+
```ts
|
|
69
|
+
isValidUser(user: UserEditor): boolean
|
|
70
|
+
```
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
---
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
Removes experience from the user and adjusts their level if necessary. Experience can be removed with or without a multiplier. ➖
|
|
74
|
+
### 🎁 `getGiveExpBase()`
|
|
71
75
|
|
|
72
|
-
|
|
73
|
-
- `extraExp` (`number`): Additional experience to be removed. 💣
|
|
74
|
-
- `type` (`'add' | 'extra'`): Type of experience removal. `'add'` removes random experience, while `'extra'` removes specified experience. 🔧
|
|
75
|
-
- `multi` (`number`): Multiplier for experience generation. Default is `1`. 💥
|
|
76
|
+
Returns the base XP value used for generating XP.
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
```ts
|
|
79
|
+
getGiveExpBase(): number
|
|
80
|
+
```
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
---
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
Represents the structure of a user object after level validation. 🧑💻
|
|
84
|
+
### 🧮 `getExpLevelBase()`
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
- `level` (`number`): The user's current level. 🏆
|
|
86
|
-
- `totalExp` (`number`): The user's total experience. 📊
|
|
86
|
+
Returns the base XP required per level.
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
```ts
|
|
89
|
+
getExpLevelBase(): number
|
|
90
|
+
```
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
- `level` (`number`): The user's current level. 🏆
|
|
93
|
-
- `totalExp` (`any`): The user's total experience (can be calculated using `getTotalExp`). 📊
|
|
92
|
+
---
|
|
94
93
|
|
|
95
|
-
###
|
|
94
|
+
### ⚖️ `expValidator(user)`
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
const user = { exp: 50, level: 1, totalExp: 50 };
|
|
99
|
-
const levelUpSystem = new TinyLevelUp(100, 1000);
|
|
96
|
+
Validates and adjusts user level based on current XP.
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log(user); // { exp: 112, level: 1, totalExp: 112 }
|
|
98
|
+
```ts
|
|
99
|
+
expValidator(user: UserEditor): UserEditor
|
|
104
100
|
```
|
|
105
101
|
|
|
102
|
+
* Levels up/down the user if needed.
|
|
103
|
+
* Applies "extra" XP appropriately.
|
|
104
|
+
|
|
106
105
|
---
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
### 📊 `getTotalExp(user)`
|
|
108
|
+
|
|
109
|
+
Calculates total accumulated XP (including current level and progress).
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
getTotalExp(user: UserEditor): number
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### 🎲 `expGenerator(multi = 1)`
|
|
118
|
+
|
|
119
|
+
Generates a random XP value based on the multiplier.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
expGenerator(multi?: number): number
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### ⏩ `progress(user)`
|
|
128
|
+
|
|
129
|
+
Returns XP required to reach the next level.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
progress(user: UserEditor): number
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### 📈 `getProgress(user)`
|
|
138
|
+
|
|
139
|
+
Alias for `progress()`. Returns XP needed to reach the next level.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
getProgress(user: UserEditor): number
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### ✍️ `set(user, value)`
|
|
148
|
+
|
|
149
|
+
Sets the current XP for the user and updates their level if necessary.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
set(user: UserEditor, value: number): UserEditor
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### ⬆️ `give(user, extraExp?, type?, multi?)`
|
|
158
|
+
|
|
159
|
+
Adds experience to the user and auto-levels if needed.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
give(
|
|
163
|
+
user: UserEditor,
|
|
164
|
+
extraExp?: number,
|
|
165
|
+
type?: 'add' | 'extra',
|
|
166
|
+
multi?: number
|
|
167
|
+
): UserEditor
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
* `type = 'add'`: Adds generated XP + extra
|
|
171
|
+
* `type = 'extra'`: Adds only extra
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### ⬇️ `remove(user, extraExp?, type?, multi?)`
|
|
176
|
+
|
|
177
|
+
Removes experience from the user and updates their level if necessary.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
remove(
|
|
181
|
+
user: UserEditor,
|
|
182
|
+
extraExp?: number,
|
|
183
|
+
type?: 'add' | 'extra',
|
|
184
|
+
multi?: number
|
|
185
|
+
): UserEditor
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
* `type = 'add'`: Removes generated XP + extra
|
|
189
|
+
* `type = 'extra'`: Removes only extra
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### 🔍 `getMissingExp(user)`
|
|
194
|
+
|
|
195
|
+
**Description:**
|
|
196
|
+
Calculates how much experience is missing for the user to reach the next level.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
**Parameters:**
|
|
201
|
+
|
|
202
|
+
* `user` (UserEditor) — The user object containing experience and level properties.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
**Returns:**
|
|
207
|
+
|
|
208
|
+
* `number` — The amount of experience points still needed to level up.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
**Throws:**
|
|
213
|
+
|
|
214
|
+
* `Error` — If any property (`exp`, `level`, or `totalExp`) in the `user` object is not a valid number. ⚠️
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
**Example usage:**
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
const missing = tinyLevelUp.getMissingExp(user);
|
|
222
|
+
console.log(`You need ${missing} more XP to level up! 🚀`);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🌟 Example Usage
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import TinyLevelUp from './TinyLevelUp.js';
|
|
231
|
+
|
|
232
|
+
const levelSystem = new TinyLevelUp(15, 10);
|
|
233
|
+
const user = levelSystem.createUser();
|
|
234
|
+
|
|
235
|
+
levelSystem.give(user); // Adds random XP
|
|
236
|
+
console.log(user);
|
|
237
|
+
|
|
238
|
+
levelSystem.set(user, 50); // Manually set XP
|
|
239
|
+
console.log(user.level);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 💬 Notes
|
|
245
|
+
|
|
246
|
+
* All methods throw if invalid values are passed ❗
|
|
247
|
+
* You can use `isValidUser()` for safe checks without exceptions 🛡️
|
|
248
|
+
* This system is deterministic and extendable!
|