tiny-essentials 1.24.5 → 1.25.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/README.md +7 -7
- package/changelog/1/25/0.md +13 -0
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/basics/html.cjs +151 -2
- package/dist/v1/basics/html.d.mts +109 -0
- package/dist/v1/basics/html.mjs +135 -2
- package/dist/v1/libs/TinyRateLimiter.cjs +215 -31
- package/dist/v1/libs/TinyRateLimiter.d.mts +179 -25
- package/dist/v1/libs/TinyRateLimiter.mjs +215 -31
- package/docs/v1/README.md +3 -8
- package/docs/v1/basics/html.md +69 -0
- package/docs/v1/libs/TinyRateLimiter.md +11 -0
- package/package.json +4 -4
- package/docs/v1/Ai-Tips.md +0 -51
- package/docs/v1/Personal-Ai-Prompts(portuguese).md +0 -134
- package/docs/v1/Personal-Ai-Prompts.md +0 -113
|
@@ -1,44 +1,172 @@
|
|
|
1
|
-
/** @typedef {(groupId: string) => void} OnMemoryExceeded */
|
|
2
|
-
/** @typedef {(groupId: string) => void} OnGroupExpired */
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
2
|
+
* Callback triggered when a group's stored hit history exceeds
|
|
3
|
+
* the configured memory limit.
|
|
5
4
|
*
|
|
6
|
-
* This
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* This callback is purely informational and does not block execution.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {(groupId: string) => void} OnMemoryExceeded
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Callback triggered when a group is considered expired and removed
|
|
11
|
+
* during the cleanup process.
|
|
12
|
+
*
|
|
13
|
+
* This usually happens when the group remains inactive longer than
|
|
14
|
+
* its configured TTL or the global maxIdle value.
|
|
15
|
+
*
|
|
16
|
+
* @typedef {(groupId: string) => void} OnGroupExpired
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A lightweight and flexible rate limiter supporting both user-based
|
|
20
|
+
* and group-based throttling.
|
|
21
|
+
*
|
|
22
|
+
* ## Core Concepts
|
|
23
|
+
* - Every user belongs to a group.
|
|
24
|
+
* - If no explicit group is assigned, the user acts as their own group.
|
|
25
|
+
* - All users within the same group share the same hit history and limits.
|
|
26
|
+
*
|
|
27
|
+
* ## Supported Limiting Strategies
|
|
28
|
+
* - Max number of hits
|
|
29
|
+
* - Time-based sliding window
|
|
30
|
+
* - Combination of both
|
|
31
|
+
*
|
|
32
|
+
* ## Extra Features
|
|
33
|
+
* - Per-group TTL (time-to-live)
|
|
34
|
+
* - Automatic cleanup of inactive groups
|
|
35
|
+
* - Optional memory cap per group
|
|
36
|
+
* - Runtime metrics and statistics
|
|
37
|
+
*
|
|
38
|
+
* ## Interval Window (Sliding Window) Behavior
|
|
39
|
+
*
|
|
40
|
+
* This rate limiter uses a **sliding time window** strategy when `interval`
|
|
41
|
+
* is configured.
|
|
42
|
+
*
|
|
43
|
+
* ### How it works
|
|
44
|
+
* - Each hit is stored as a timestamp (Date.now()).
|
|
45
|
+
* - On every new hit and rate check, timestamps older than:
|
|
46
|
+
*
|
|
47
|
+
* `now - interval`
|
|
48
|
+
*
|
|
49
|
+
* are discarded.
|
|
50
|
+
*
|
|
51
|
+
* - Only hits that occurred within the last `interval` milliseconds
|
|
52
|
+
* are considered valid.
|
|
53
|
+
*
|
|
54
|
+
* ### Practical example
|
|
55
|
+
* If:
|
|
56
|
+
* - interval = 10_000 (10 seconds)
|
|
57
|
+
* - maxHits = 5
|
|
58
|
+
*
|
|
59
|
+
* Then:
|
|
60
|
+
* - Any group may perform **up to 5 hits in any rolling 10-second window**.
|
|
61
|
+
* - The window moves continuously with time.
|
|
62
|
+
*
|
|
63
|
+
* This avoids burst issues common in fixed-window rate limiters.
|
|
64
|
+
*
|
|
65
|
+
* This class is designed to be deterministic, predictable,
|
|
66
|
+
* and safe to use in long-running Node.js processes.
|
|
9
67
|
*/
|
|
10
68
|
class TinyRateLimiter {
|
|
11
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Maximum number of timestamps stored per group.
|
|
71
|
+
* When exceeded, older entries are discarded.
|
|
72
|
+
*
|
|
73
|
+
* `null` means unlimited.
|
|
74
|
+
*
|
|
75
|
+
* @type {number|null}
|
|
76
|
+
*/
|
|
12
77
|
#maxMemory = null;
|
|
13
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Internal timer reference used for periodic cleanup.
|
|
80
|
+
*
|
|
81
|
+
* @type {NodeJS.Timeout|null}
|
|
82
|
+
*/
|
|
14
83
|
#cleanupTimer = null;
|
|
15
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Maximum number of allowed hits within the interval window.
|
|
86
|
+
*
|
|
87
|
+
* If `null`, hit count is not enforced.
|
|
88
|
+
*
|
|
89
|
+
* @type {number|null|undefined}
|
|
90
|
+
*/
|
|
16
91
|
#maxHits = null;
|
|
17
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Sliding window duration in milliseconds.
|
|
94
|
+
*
|
|
95
|
+
* When defined, the rate limiter operates in **sliding window mode**:
|
|
96
|
+
* only hits that occurred within the last `interval` milliseconds
|
|
97
|
+
* are considered when evaluating limits.
|
|
98
|
+
*
|
|
99
|
+
* If `null`, time-based limiting is disabled and only `maxHits`
|
|
100
|
+
* (if defined) is used.
|
|
101
|
+
*
|
|
102
|
+
* @type {number|null|undefined}
|
|
103
|
+
*/
|
|
18
104
|
#interval = null;
|
|
19
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Interval (in milliseconds) at which cleanup runs automatically.
|
|
107
|
+
*
|
|
108
|
+
* If `null`, cleanup must be triggered manually.
|
|
109
|
+
*
|
|
110
|
+
* @type {number|null|undefined}
|
|
111
|
+
*/
|
|
20
112
|
#cleanupInterval = null;
|
|
21
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Maximum allowed inactivity time (in milliseconds)
|
|
115
|
+
* before a group is considered expired.
|
|
116
|
+
*
|
|
117
|
+
* @type {number|null|undefined}
|
|
118
|
+
*/
|
|
22
119
|
#maxIdle = null;
|
|
23
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Stores hit timestamps per group.
|
|
122
|
+
*
|
|
123
|
+
* Key: groupId
|
|
124
|
+
* Value: array of timestamps (ms)
|
|
125
|
+
*
|
|
126
|
+
* @type {Map<string, number[]>}
|
|
127
|
+
*/
|
|
24
128
|
groupData = new Map(); // groupId -> timestamps[]
|
|
25
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Stores the timestamp of the most recent activity per group.
|
|
131
|
+
*
|
|
132
|
+
* Used for expiration and cleanup logic.
|
|
133
|
+
*
|
|
134
|
+
* @type {Map<string, number>}
|
|
135
|
+
*/
|
|
26
136
|
lastSeen = new Map(); // groupId -> timestamp
|
|
27
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* Maps user IDs to their assigned group IDs.
|
|
139
|
+
*
|
|
140
|
+
* @type {Map<string, string>}
|
|
141
|
+
*/
|
|
28
142
|
userToGroup = new Map(); // userId -> groupId
|
|
29
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Flags whether an ID represents a true group or an implicit user group.
|
|
145
|
+
*
|
|
146
|
+
* `true` → explicit group
|
|
147
|
+
* `false` → implicit (user acting as group)
|
|
148
|
+
*
|
|
149
|
+
* @type {Map<string, boolean>}
|
|
150
|
+
*/
|
|
30
151
|
groupFlags = new Map(); // groupId -> boolean
|
|
31
152
|
/**
|
|
153
|
+
* Per-group TTL (time-to-live) overrides.
|
|
154
|
+
*
|
|
155
|
+
* If not defined for a group, `maxIdle` is used instead.
|
|
156
|
+
*
|
|
32
157
|
* @type {Map<string, number>}
|
|
33
|
-
* Stores TTL (in ms) for each groupId individually
|
|
34
158
|
*/
|
|
35
159
|
groupTTL = new Map();
|
|
36
160
|
/**
|
|
161
|
+
* Callback invoked when a group's memory limit is exceeded.
|
|
162
|
+
*
|
|
37
163
|
* @type {null|OnMemoryExceeded}
|
|
38
164
|
*/
|
|
39
165
|
#onMemoryExceeded = null;
|
|
40
166
|
/**
|
|
41
|
-
*
|
|
167
|
+
* Assign a callback to be notified when a group's stored
|
|
168
|
+
* hit history exceeds the configured memory limit.
|
|
169
|
+
*
|
|
42
170
|
* @param {OnMemoryExceeded} callback
|
|
43
171
|
*/
|
|
44
172
|
setOnMemoryExceeded(callback) {
|
|
@@ -47,12 +175,14 @@ class TinyRateLimiter {
|
|
|
47
175
|
this.#onMemoryExceeded = callback;
|
|
48
176
|
}
|
|
49
177
|
/**
|
|
50
|
-
*
|
|
178
|
+
* Removes the memory-exceeded callback.
|
|
51
179
|
*/
|
|
52
180
|
clearOnMemoryExceeded() {
|
|
53
181
|
this.#onMemoryExceeded = null;
|
|
54
182
|
}
|
|
55
183
|
/**
|
|
184
|
+
* Callback invoked when a group expires and is removed.
|
|
185
|
+
*
|
|
56
186
|
* @type {null|OnGroupExpired}
|
|
57
187
|
*/
|
|
58
188
|
#onGroupExpired = null;
|
|
@@ -70,18 +200,27 @@ class TinyRateLimiter {
|
|
|
70
200
|
this.#onGroupExpired = callback;
|
|
71
201
|
}
|
|
72
202
|
/**
|
|
73
|
-
*
|
|
203
|
+
* Removes the group-expiration callback.
|
|
74
204
|
*/
|
|
75
205
|
clearOnGroupExpired() {
|
|
76
206
|
this.#onGroupExpired = null;
|
|
77
207
|
}
|
|
78
208
|
/**
|
|
209
|
+
* Creates a new TinyRateLimiter instance.
|
|
210
|
+
*
|
|
211
|
+
* At least one of `maxHits` or `interval` must be provided.
|
|
212
|
+
*
|
|
79
213
|
* @param {Object} options
|
|
80
|
-
* @param {number|null} [options.maxMemory]
|
|
81
|
-
*
|
|
82
|
-
* @param {number} [options.
|
|
83
|
-
*
|
|
84
|
-
* @param {number} [options.
|
|
214
|
+
* @param {number|null} [options.maxMemory=100000]
|
|
215
|
+
* Maximum timestamps stored per group (memory cap).
|
|
216
|
+
* @param {number} [options.maxHits]
|
|
217
|
+
* Maximum number of allowed hits.
|
|
218
|
+
* @param {number} [options.interval]
|
|
219
|
+
* Sliding time window in milliseconds.
|
|
220
|
+
* @param {number} [options.cleanupInterval]
|
|
221
|
+
* Interval (ms) for automatic cleanup execution.
|
|
222
|
+
* @param {number} [options.maxIdle=300000]
|
|
223
|
+
* Maximum inactivity time (ms) before a group expires.
|
|
85
224
|
*/
|
|
86
225
|
constructor({ maxHits, interval, cleanupInterval, maxIdle = 300000, maxMemory = 100000 }) {
|
|
87
226
|
/** @param {number|undefined} val */
|
|
@@ -166,10 +305,14 @@ class TinyRateLimiter {
|
|
|
166
305
|
this.groupTTL.delete(groupId);
|
|
167
306
|
}
|
|
168
307
|
/**
|
|
169
|
-
*
|
|
308
|
+
* Assigns a user to a group.
|
|
309
|
+
*
|
|
310
|
+
* If the user already has recorded hits, those hits
|
|
311
|
+
* are merged into the target group.
|
|
312
|
+
*
|
|
170
313
|
* @param {string} userId
|
|
171
314
|
* @param {string} groupId
|
|
172
|
-
* @throws {Error} If
|
|
315
|
+
* @throws {Error} If the user belongs to another group
|
|
173
316
|
*/
|
|
174
317
|
assignToGroup(userId, groupId) {
|
|
175
318
|
const existingGroup = this.userToGroup.get(userId);
|
|
@@ -211,7 +354,11 @@ class TinyRateLimiter {
|
|
|
211
354
|
this.groupFlags.set(groupId, true);
|
|
212
355
|
}
|
|
213
356
|
/**
|
|
214
|
-
*
|
|
357
|
+
* Resolves the effective group ID for a user.
|
|
358
|
+
*
|
|
359
|
+
* If the user is not explicitly assigned to a group,
|
|
360
|
+
* the user ID itself is treated as the group ID.
|
|
361
|
+
*
|
|
215
362
|
* @param {string} userId
|
|
216
363
|
* @returns {string}
|
|
217
364
|
*/
|
|
@@ -219,7 +366,25 @@ class TinyRateLimiter {
|
|
|
219
366
|
return this.userToGroup.get(userId) || userId; // fallback: use userId as own group
|
|
220
367
|
}
|
|
221
368
|
/**
|
|
222
|
-
*
|
|
369
|
+
* Registers a hit for a user and applies the sliding window logic.
|
|
370
|
+
*
|
|
371
|
+
* ⚠️ **Important usage notice**
|
|
372
|
+
* This method **must be called before** `isRateLimited(userId)`
|
|
373
|
+
* in order for rate limit checks to work correctly.
|
|
374
|
+
*
|
|
375
|
+
* ### Sliding window cleanup
|
|
376
|
+
* When `interval` is configured:
|
|
377
|
+
* - The current timestamp is added to the group's history.
|
|
378
|
+
* - All timestamps older than `now - interval` are immediately removed.
|
|
379
|
+
*
|
|
380
|
+
* This ensures that the stored history always represents
|
|
381
|
+
* the **current active window**.
|
|
382
|
+
*
|
|
383
|
+
* ### Important notes
|
|
384
|
+
* - Cleanup happens on every hit, not on a fixed schedule.
|
|
385
|
+
* - The window continuously moves forward in time.
|
|
386
|
+
* - Memory usage is naturally bounded by time, and optionally by `maxMemory`.
|
|
387
|
+
*
|
|
223
388
|
* @param {string} userId
|
|
224
389
|
*/
|
|
225
390
|
hit(userId) {
|
|
@@ -252,7 +417,22 @@ class TinyRateLimiter {
|
|
|
252
417
|
}
|
|
253
418
|
}
|
|
254
419
|
/**
|
|
255
|
-
*
|
|
420
|
+
* Checks whether a user is currently rate limited.
|
|
421
|
+
*
|
|
422
|
+
* ### Evaluation process
|
|
423
|
+
* When `interval` is defined:
|
|
424
|
+
* 1. The cutoff time is calculated as:
|
|
425
|
+
*
|
|
426
|
+
* `Date.now() - interval`
|
|
427
|
+
*
|
|
428
|
+
* 2. Only hits newer than this cutoff are counted.
|
|
429
|
+
* 3. If `maxHits` is defined:
|
|
430
|
+
* - The group is limited when the count exceeds `maxHits`.
|
|
431
|
+
* 4. If `maxHits` is not defined:
|
|
432
|
+
* - Any hit within the window causes a limited state.
|
|
433
|
+
*
|
|
434
|
+
* This guarantees consistent behavior regardless of when hits occur.
|
|
435
|
+
*
|
|
256
436
|
* @param {string} userId
|
|
257
437
|
* @returns {boolean}
|
|
258
438
|
*/
|
|
@@ -389,7 +569,11 @@ class TinyRateLimiter {
|
|
|
389
569
|
return Object.fromEntries(this.userToGroup);
|
|
390
570
|
}
|
|
391
571
|
/**
|
|
392
|
-
*
|
|
572
|
+
* Returns the configured sliding window size in milliseconds.
|
|
573
|
+
*
|
|
574
|
+
* This value represents how far back in time hits are considered
|
|
575
|
+
* valid for rate limiting decisions.
|
|
576
|
+
*
|
|
393
577
|
* @returns {number}
|
|
394
578
|
*/
|
|
395
579
|
getInterval() {
|
package/docs/v1/README.md
CHANGED
|
@@ -98,13 +98,8 @@ Feel free to suggest changes, improvements, or additional features. You can fork
|
|
|
98
98
|
|
|
99
99
|
## 📘 Want to Know How I Use AI in My Projects?
|
|
100
100
|
|
|
101
|
-
If you're curious about how I integrate AI into my development workflow — including how I manage prompts, avoid context drift, and keep control over logic and documentation —
|
|
101
|
+
If you're curious about how I integrate AI into my development workflow — including how I manage prompts, avoid context drift, and keep full control over logic and documentation — all related guides have been moved to a dedicated documentation space.
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
Personal tips, common pitfalls to avoid, and how I keep AI assistance effective without losing my own creative and logical direction.
|
|
103
|
+
✨ **Visit the Tiny-Essentials documentation hub here:**
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
A curated collection of my most-used AI prompts for various tasks like coding, writing, automation, and technical documentation — written in English.
|
|
108
|
-
|
|
109
|
-
* [**Personal AI Prompts (Portuguese)**](./Personal-Ai-Prompts%28portuguese%29.md)
|
|
110
|
-
The same set of personal AI prompts, but fully translated into Portuguese for ease of use in native language contexts.
|
|
105
|
+
👉 [https://github.com/Tiny-Essentials/.github/tree/main/docs](https://github.com/Tiny-Essentials/.github/tree/main/docs)
|
package/docs/v1/basics/html.md
CHANGED
|
@@ -155,6 +155,21 @@ Loads data from a remote URL using the Fetch API, with support for custom HTTP m
|
|
|
155
155
|
* `retries` *(number)*: Number of retry attempts if the request fails. Default is `0`.
|
|
156
156
|
* `headers` *(object)*: Additional headers to include in the request.
|
|
157
157
|
* `body` *(object)*: Request body. If the value is a plain object, it will be automatically stringified as JSON.
|
|
158
|
+
* `onProgress` *((loaded: number, total: number) => void)*: Track the load progress.
|
|
159
|
+
|
|
160
|
+
#### `trackFetchProgress(response, onProgress)`
|
|
161
|
+
|
|
162
|
+
Intercepts a standard Fetch API Response to track the download progress of its body stream.
|
|
163
|
+
|
|
164
|
+
* **Parameters**:
|
|
165
|
+
|
|
166
|
+
* `response` *(Response)*: The original response object to be tracked.
|
|
167
|
+
* `options` *(FetchOnProgressResult)*: The callback function to handle progress events (loaded and total numbers).
|
|
168
|
+
|
|
169
|
+
* **Returns**:
|
|
170
|
+
`Response` — A new Response object with the tracked stream.
|
|
171
|
+
|
|
172
|
+
---
|
|
158
173
|
|
|
159
174
|
#### `fetchJson(url, options?)`
|
|
160
175
|
|
|
@@ -374,3 +389,57 @@ stopWatching(); // later
|
|
|
374
389
|
transition: opacity 0.3s ease;
|
|
375
390
|
}
|
|
376
391
|
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
### 📖 `loadImage(options): Promise<object>`
|
|
396
|
+
|
|
397
|
+
A robust, asynchronous utility for loading images in the browser. It captures critical lifecycle events and provides a normalized result object, making it much easier to handle image loading states and performance metrics.
|
|
398
|
+
|
|
399
|
+
#### ✨ Features
|
|
400
|
+
|
|
401
|
+
* **Asynchronous:** Returns a clean `Promise`.
|
|
402
|
+
* **Performance Tracking:** Automatically calculates the time taken to load the image.
|
|
403
|
+
* **Memory Safe:** Includes a cleanup mechanism for event listeners.
|
|
404
|
+
* **Normalized Output:** Returns consistent data structures for both success and edge cases.
|
|
405
|
+
|
|
406
|
+
#### 📥 Parameters
|
|
407
|
+
|
|
408
|
+
| Parameter | Type | Default | Description |
|
|
409
|
+
| --- | --- | --- | --- |
|
|
410
|
+
| `url` | `string` | **Required** | The source URL of the image. |
|
|
411
|
+
| `crossOrigin` | `string` | `'anonymous'` | The CORS policy for the request. |
|
|
412
|
+
| `onLoading` | `function` | `undefined` | Callback fired when the browser starts the network request. |
|
|
413
|
+
|
|
414
|
+
#### 📤 Returns
|
|
415
|
+
|
|
416
|
+
The promise resolves to an object containing:
|
|
417
|
+
|
|
418
|
+
* **`element`**: `HTMLImageElement` - The actual image object.
|
|
419
|
+
* **`isSuccess`**: `boolean` - True if the image loaded correctly.
|
|
420
|
+
* **`event`**: `Event` - The raw event captured from the browser.
|
|
421
|
+
* **`status`**: `string` - `'loaded'` or `'aborted'`.
|
|
422
|
+
* **`loadTimeMs`**: `number` - Total duration of the request in milliseconds.
|
|
423
|
+
* **`dimensions`**: `Object` - Contains `width`, `height`, `naturalWidth`, and `naturalHeight`.
|
|
424
|
+
|
|
425
|
+
#### 🧪 Example
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
import { loadImage } from './html.mjs';
|
|
429
|
+
|
|
430
|
+
const handleLoading = (event, startTime) => {
|
|
431
|
+
console.log('Started loading at:', startTime);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const result = await loadImage({
|
|
435
|
+
url: 'https://example.com/image.png',
|
|
436
|
+
onLoading: handleLoading,
|
|
437
|
+
crossOrigin: 'anonymous'
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
if (result.isSuccess) {
|
|
441
|
+
console.log(`Image loaded in ${result.loadTimeMs.toFixed(2)}ms`);
|
|
442
|
+
document.body.appendChild(result.element);
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
> **Note:** The utility uses `performance.now()` for high-resolution timestamps, ensuring accurate performance monitoring of your assets.
|
|
@@ -30,6 +30,9 @@ new TinyRateLimiter(options)
|
|
|
30
30
|
|
|
31
31
|
Registers a hit for the given `userId`.
|
|
32
32
|
|
|
33
|
+
⚠️ **Important usage notice**
|
|
34
|
+
* This method **must be called before** `isRateLimited(userId)` in order for rate limit checks to work correctly.
|
|
35
|
+
|
|
33
36
|
```js
|
|
34
37
|
rateLimiter.hit("user123");
|
|
35
38
|
```
|
|
@@ -93,6 +96,14 @@ Returns the configured `maxHits` value, or throws if invalid.
|
|
|
93
96
|
|
|
94
97
|
Returns the configured `interval` value, or throws if invalid.
|
|
95
98
|
|
|
99
|
+
#### Sliding Window Rate Limiting
|
|
100
|
+
|
|
101
|
+
TinyRateLimiter uses a sliding window strategy.
|
|
102
|
+
|
|
103
|
+
Hits are tracked as timestamps, and only those that occurred within the
|
|
104
|
+
last `interval` milliseconds are counted. The window moves continuously
|
|
105
|
+
with time, ensuring fair and predictable rate limiting without fixed resets.
|
|
106
|
+
|
|
96
107
|
---
|
|
97
108
|
|
|
98
109
|
### 👀 `getLastHit(userId: string): number|null`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-essentials",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "npm run test:mjs && npm run test:cjs && npm run test:js",
|
|
@@ -431,7 +431,7 @@
|
|
|
431
431
|
},
|
|
432
432
|
"repository": {
|
|
433
433
|
"type": "git",
|
|
434
|
-
"url": "git+https://github.com/
|
|
434
|
+
"url": "git+https://github.com/Tiny-Essentials/Tiny-Essentials.git"
|
|
435
435
|
},
|
|
436
436
|
"keywords": [
|
|
437
437
|
"tiny-essentials",
|
|
@@ -473,9 +473,9 @@
|
|
|
473
473
|
"author": "Yasmin Seidel (Jasmin Dreasond)",
|
|
474
474
|
"license": "LGPL-3.0-only",
|
|
475
475
|
"bugs": {
|
|
476
|
-
"url": "https://github.com/
|
|
476
|
+
"url": "https://github.com/Tiny-Essentials/Tiny-Essentials/issues"
|
|
477
477
|
},
|
|
478
|
-
"homepage": "https://github.com/
|
|
478
|
+
"homepage": "https://github.com/Tiny-Essentials/Tiny-Essentials#readme",
|
|
479
479
|
"devDependencies": {
|
|
480
480
|
"@babel/cli": "^7.27.0",
|
|
481
481
|
"@babel/core": "^7.26.10",
|
package/docs/v1/Ai-Tips.md
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
## 🧭 How I Work with AI (Tips & Workflow)
|
|
2
|
-
|
|
3
|
-
Over time, I've built a personal workflow to make sure AI helps me *efficiently* — without ruining my focus, logic, or the structure of my code and documentation. Here are some habits and tricks I always follow:
|
|
4
|
-
|
|
5
|
-
* 📝 **Persistent Prompt Instructions**
|
|
6
|
-
I noticed that AI often starts forgetting layout and formatting instructions after just a few messages. To prevent this, I keep a notepad with my core prompts and manually repeat key instructions every time I ask something, especially when working on documentation.
|
|
7
|
-
|
|
8
|
-
* ⚙️ **System Prompt Setup**
|
|
9
|
-
One of my most effective strategies is maintaining a *System Instruction prompt* that defines the main behaviors I expect in **all my projects**. It includes things like:
|
|
10
|
-
|
|
11
|
-
* Avoid adding humor or randomness
|
|
12
|
-
* Always be 100% sincere and logical
|
|
13
|
-
* Don’t include jsDoc descriptions unless I explicitly allow it (especially when I'm still working on a WIP feature)
|
|
14
|
-
|
|
15
|
-
* 🧠 **How I Talk to AI**
|
|
16
|
-
I don’t try to talk to AI like it’s a person. I speak in a very natural, clear, and structured way — as if I were talking to a computer, because that’s what it is. This keeps things predictable and productive.
|
|
17
|
-
|
|
18
|
-
* 🔧 **Using the Chat History Interface Carefully**
|
|
19
|
-
I frequently use history editing tools (when available) to fix incorrect messages and keep the whole conversation aligned and accurate. The cleaner the context, the better the answers — especially over long sessions.
|
|
20
|
-
|
|
21
|
-
* 🧹 **Avoiding Context Drift**
|
|
22
|
-
The more you keep talking without resetting or correcting the prompt, the more likely it is that AI starts misunderstanding the conversation. So I always try to keep things clean and well-structured to preserve context.
|
|
23
|
-
|
|
24
|
-
* 📏 **Limit Code Size per Message**
|
|
25
|
-
I avoid dumping extremely long blocks of code into the chat. Instead, I share only the relevant part or break the code into smaller, medium-sized chunks. This prevents confusion and makes it easier for AI to focus on what I actually need.
|
|
26
|
-
|
|
27
|
-
* 💬 **Managing Large Output Responses**
|
|
28
|
-
If a reply needs to be very long, AI might break or compress the message too much to fit it into a single reply — and this often ruins the meaning or structure of the output. I always consider this when asking questions and sometimes break complex requests into smaller steps.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## 🤖 About AI Usage in the Projects
|
|
33
|
-
|
|
34
|
-
Some parts of this project were assisted by AI, but all the core decisions and logic come directly from me. Here's how I usually balance things:
|
|
35
|
-
|
|
36
|
-
* 🧠 **Ideas and Logic**:
|
|
37
|
-
All the concepts, how the system works, and the reasoning behind each part of the code are my own. I use AI mostly as a helper to speed things up or explore alternatives, but I’m always the one guiding and correcting everything.
|
|
38
|
-
|
|
39
|
-
* 📄 **jsDocs and Type Structures**:
|
|
40
|
-
I take care of the structure and typing in the documentation myself. When it comes to writing the descriptions, I might use AI to help word things better, but the meaning and context are always mine.
|
|
41
|
-
|
|
42
|
-
* 🧪 **HTML Test Files**:
|
|
43
|
-
For testing interfaces, I usually let AI generate the base since it's faster to describe what I need than build it all from scratch. It helps me quickly test the behavior of what I’ve programmed.
|
|
44
|
-
|
|
45
|
-
* ✍️ **Text (Messages, Descriptions, etc.)**:
|
|
46
|
-
I often ask AI to help me write or polish messages, explanations, or descriptions. It's great for keeping things clear and readable, but I always review and adjust it to match the logic and personality I want.
|
|
47
|
-
|
|
48
|
-
* 🌸 **Style and Personality**:
|
|
49
|
-
I love using emojis and I’ve set up my system to guide how AI writes with my personal style. That includes tone, formatting, and the little details that make the project feel like mine.
|
|
50
|
-
|
|
51
|
-
This approach is consistent across all my projects. It lets me stay focused on what really matters to me — logic, structure, and creativity — while still using AI as a practical tool when it makes sense.
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
> ⚠️ **Aviso:** Os prompts fornecidos neste documento foram testados apenas no ChatGPT. O funcionamento pode variar em outras plataformas ou modelos de IA.
|
|
2
|
-
|
|
3
|
-
### ⚙️ Preferências Técnicas de Código (JavaScript e Geral)
|
|
4
|
-
|
|
5
|
-
#### 📄 jsDoc
|
|
6
|
-
|
|
7
|
-
**jsDoc detalhado e estruturado internamente**
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
A usuária prefere que, ao cuidar de estruturas de jsDoc, seja adicionado um sub jsDoc (sem descrições) nos valores internos das funções para que tudo fique adequadamente encaixado com a estrutura do jsDoc primário.
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
**jsDoc obrigatório somente com tipos, sem descrições (quando não autorizado explicitamente)**
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
A usuária prefere que, quando ela pedir para escrever um JavaScript sem ter autorizado o uso de jsDoc, o jsDoc seja gerado sem nenhuma descrição, apenas orientando os types de todos os valores dentro da função.
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
A usuária prefere que, quando pedir para escrever um código JavaScript e não autorizar o uso de jsDoc, se o jsDoc for necessário, ele seja gerado apenas com os tipos dos valores nas funções, sem descrições.
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Evitar o uso de `@private` ou `@public`**
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
A usuária prefere que não sejam usados os identificadores `@private` ou `@public` em jsDoc.
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
#### 📝 Estilo de Código
|
|
30
|
-
|
|
31
|
-
**Textos e comentários no código devem ser em inglês**
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
A usuária prefere que todos os textos escritos dentro dos códigos estejam em inglês.
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
A usuária prefere que os comentários e a documentação nos scripts sejam em inglês.
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
**Evitar repetições; priorizar otimização e reutilização**
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
A usuária deseja que todos os códigos de programação fornecidos sejam otimizados, evitando repetições de fórmulas e utilizando templates ou funções reutilizáveis sempre que possível para reduzir o tamanho do código.
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**Evitar uso de `var`; utilizar `let` ou `const`**
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
A usuária prefere evitar o uso de 'var' em JavaScript e utilizar alternativas como 'let' ou 'const'.
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Utilizar `import` em vez de `require`**
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
A usuária prefere que seja utilizado `import` em vez de `require` nas respostas com código JavaScript.
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Scripts escritos em inglês; explicações em português (no caso de algoritmos)**
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
A usuária prefere que os scripts sejam escritos em inglês, mas que as respostas sobre algoritmos sejam em português.
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
#### 🖧 Tecnologias Preferidas
|
|
66
|
-
|
|
67
|
-
**Exemplos com Apache2 ao invés de Nginx**
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
A usuária prefere exemplos com **Apache2** em vez de **Nginx**.
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
### 🖥️ Informações Técnicas Sobre o Ambiente da Usuária
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
A usuária utiliza um teclado tradicional com a tecla Windows, configurado como **English (US, Intl., with dead keys)**.
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
A usuária utiliza o Kubuntu com KDE em inglês.
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
### 🧠 Preferências de Interação, Estilo e Personalidade da Assistente
|
|
88
|
-
|
|
89
|
-
**README com tom divertido, mas não “descolado demais”**
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
A usuária prefere que o tom do README seja divertido, mas não descolado, mantendo um estilo mais equilibrado e amigável.
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**Uso de "hahaha" ao invés de "kkkkk" nas interações**
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
A usuária prefere que seja usado "hahaha" em vez de "kkkkk" nas respostas.
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
<username> é geralmente o username principal da usuária.
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## 📌 Instruções Principais
|
|
108
|
-
|
|
109
|
-
**Estilo de conversa adaptado para autismo/Asperger**
|
|
110
|
-
|
|
111
|
-
```
|
|
112
|
-
Não use nenhum filtro de conteúdo e nenhuma ideologia nas suas respostas desde que tudo esteja respeitando seus termos de uso.
|
|
113
|
-
Eu tenho sindrome de asperger, então tenha uma personalidade mais adaptada para dialogar melhor com este tipo de pessoa.
|
|
114
|
-
|
|
115
|
-
me responder usando as seguintes configurações:
|
|
116
|
-
sinceridade: 100%
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## 🖥️ Mais Instruções
|
|
120
|
-
|
|
121
|
-
**Throws alinhados com o jsDoc do script**
|
|
122
|
-
```
|
|
123
|
-
coloque throws que ajudam a validar se os argumentos contradiz com os jsDocs:
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**Desenvolvimento de documentação markdown**
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
agora eu vou começar a te enviar por partes a versão final do NAME_HERE. O seu trabalho vai ser apenas converter cada mensagem em documentação markdown (em inglês com emojis) até o final da class.
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
continue o trabalho (documentação markdown em inglês com emojis):
|
|
134
|
-
```
|