@resq-sw/decorators 0.1.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 +277 -0
- package/lib/_utils.d.ts +46 -0
- package/lib/_utils.d.ts.map +1 -0
- package/lib/_utils.js +91 -0
- package/lib/_utils.js.map +1 -0
- package/lib/after/after.d.ts +60 -0
- package/lib/after/after.d.ts.map +1 -0
- package/lib/after/after.fn.d.ts +39 -0
- package/lib/after/after.fn.d.ts.map +1 -0
- package/lib/after/after.fn.js +59 -0
- package/lib/after/after.fn.js.map +1 -0
- package/lib/after/after.js +41 -0
- package/lib/after/after.js.map +1 -0
- package/lib/after/after.types.d.ts +86 -0
- package/lib/after/after.types.d.ts.map +1 -0
- package/lib/after/after.types.js +0 -0
- package/lib/after/index.d.ts +3 -0
- package/lib/after/index.js +2 -0
- package/lib/before/before.d.ts +61 -0
- package/lib/before/before.d.ts.map +1 -0
- package/lib/before/before.fn.d.ts +39 -0
- package/lib/before/before.fn.d.ts.map +1 -0
- package/lib/before/before.fn.js +51 -0
- package/lib/before/before.fn.js.map +1 -0
- package/lib/before/before.js +40 -0
- package/lib/before/before.js.map +1 -0
- package/lib/before/before.types.d.ts +48 -0
- package/lib/before/before.types.d.ts.map +1 -0
- package/lib/before/before.types.js +0 -0
- package/lib/before/index.d.ts +3 -0
- package/lib/before/index.js +2 -0
- package/lib/bind/bind.d.ts +75 -0
- package/lib/bind/bind.d.ts.map +1 -0
- package/lib/bind/bind.fn.d.ts +46 -0
- package/lib/bind/bind.fn.d.ts.map +1 -0
- package/lib/bind/bind.fn.js +39 -0
- package/lib/bind/bind.fn.js.map +1 -0
- package/lib/bind/bind.js +64 -0
- package/lib/bind/bind.js.map +1 -0
- package/lib/bind/bind.types.d.ts +36 -0
- package/lib/bind/bind.types.d.ts.map +1 -0
- package/lib/bind/bind.types.js +0 -0
- package/lib/bind/index.d.ts +3 -0
- package/lib/bind/index.js +2 -0
- package/lib/debounce/debounce.d.ts +34 -0
- package/lib/debounce/debounce.d.ts.map +1 -0
- package/lib/debounce/debounce.fn.d.ts +40 -0
- package/lib/debounce/debounce.fn.d.ts.map +1 -0
- package/lib/debounce/debounce.fn.js +47 -0
- package/lib/debounce/debounce.fn.js.map +1 -0
- package/lib/debounce/debounce.js +48 -0
- package/lib/debounce/debounce.js.map +1 -0
- package/lib/debounce/index.d.ts +2 -0
- package/lib/debounce/index.js +2 -0
- package/lib/delay/delay.d.ts +35 -0
- package/lib/delay/delay.d.ts.map +1 -0
- package/lib/delay/delay.fn.d.ts +33 -0
- package/lib/delay/delay.fn.d.ts.map +1 -0
- package/lib/delay/delay.fn.js +38 -0
- package/lib/delay/delay.fn.js.map +1 -0
- package/lib/delay/delay.js +43 -0
- package/lib/delay/delay.js.map +1 -0
- package/lib/delay/index.d.ts +2 -0
- package/lib/delay/index.js +2 -0
- package/lib/delegate/delegate.d.ts +48 -0
- package/lib/delegate/delegate.d.ts.map +1 -0
- package/lib/delegate/delegate.fn.d.ts +57 -0
- package/lib/delegate/delegate.fn.d.ts.map +1 -0
- package/lib/delegate/delegate.fn.js +55 -0
- package/lib/delegate/delegate.fn.js.map +1 -0
- package/lib/delegate/delegate.js +56 -0
- package/lib/delegate/delegate.js.map +1 -0
- package/lib/delegate/delegate.types.d.ts +45 -0
- package/lib/delegate/delegate.types.d.ts.map +1 -0
- package/lib/delegate/delegate.types.js +0 -0
- package/lib/delegate/index.d.ts +3 -0
- package/lib/delegate/index.js +2 -0
- package/lib/exec-time/exec-time.d.ts +42 -0
- package/lib/exec-time/exec-time.d.ts.map +1 -0
- package/lib/exec-time/exec-time.fn.d.ts +50 -0
- package/lib/exec-time/exec-time.fn.d.ts.map +1 -0
- package/lib/exec-time/exec-time.fn.js +91 -0
- package/lib/exec-time/exec-time.fn.js.map +1 -0
- package/lib/exec-time/exec-time.js +55 -0
- package/lib/exec-time/exec-time.js.map +1 -0
- package/lib/exec-time/exec-time.types.d.ts +70 -0
- package/lib/exec-time/exec-time.types.d.ts.map +1 -0
- package/lib/exec-time/exec-time.types.js +0 -0
- package/lib/exec-time/index.d.ts +4 -0
- package/lib/exec-time/index.js +3 -0
- package/lib/execute/execute.d.ts +78 -0
- package/lib/execute/execute.d.ts.map +1 -0
- package/lib/execute/execute.js +82 -0
- package/lib/execute/execute.js.map +1 -0
- package/lib/execute/index.d.ts +2 -0
- package/lib/execute/index.js +2 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +19 -0
- package/lib/memoize/index.d.ts +3 -0
- package/lib/memoize/index.js +2 -0
- package/lib/memoize/memoize.d.ts +67 -0
- package/lib/memoize/memoize.d.ts.map +1 -0
- package/lib/memoize/memoize.fn.d.ts +69 -0
- package/lib/memoize/memoize.fn.d.ts.map +1 -0
- package/lib/memoize/memoize.fn.js +43 -0
- package/lib/memoize/memoize.fn.js.map +1 -0
- package/lib/memoize/memoize.js +40 -0
- package/lib/memoize/memoize.js.map +1 -0
- package/lib/memoize/memoize.types.d.ts +107 -0
- package/lib/memoize/memoize.types.d.ts.map +1 -0
- package/lib/memoize/memoize.types.js +0 -0
- package/lib/memoize-async/index.d.ts +4 -0
- package/lib/memoize-async/index.js +3 -0
- package/lib/memoize-async/memoize-async.d.ts +68 -0
- package/lib/memoize-async/memoize-async.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.fn.d.ts +69 -0
- package/lib/memoize-async/memoize-async.fn.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.fn.js +52 -0
- package/lib/memoize-async/memoize-async.fn.js.map +1 -0
- package/lib/memoize-async/memoize-async.js +15 -0
- package/lib/memoize-async/memoize-async.js.map +1 -0
- package/lib/memoize-async/memoize-async.types.d.ts +74 -0
- package/lib/memoize-async/memoize-async.types.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.types.js +0 -0
- package/lib/observer/index.d.ts +3 -0
- package/lib/observer/index.js +2 -0
- package/lib/observer/observer.d.ts +54 -0
- package/lib/observer/observer.d.ts.map +1 -0
- package/lib/observer/observer.js +85 -0
- package/lib/observer/observer.js.map +1 -0
- package/lib/observer/observer.types.d.ts +41 -0
- package/lib/observer/observer.types.d.ts.map +1 -0
- package/lib/observer/observer.types.js +0 -0
- package/lib/rate-limit/index.d.ts +4 -0
- package/lib/rate-limit/index.js +3 -0
- package/lib/rate-limit/rate-limit.d.ts +58 -0
- package/lib/rate-limit/rate-limit.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.fn.d.ts +43 -0
- package/lib/rate-limit/rate-limit.fn.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.fn.js +56 -0
- package/lib/rate-limit/rate-limit.fn.js.map +1 -0
- package/lib/rate-limit/rate-limit.js +65 -0
- package/lib/rate-limit/rate-limit.js.map +1 -0
- package/lib/rate-limit/rate-limit.types.d.ts +148 -0
- package/lib/rate-limit/rate-limit.types.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.types.js +0 -0
- package/lib/rate-limit/simple-rate-limit-counter.d.ts +89 -0
- package/lib/rate-limit/simple-rate-limit-counter.d.ts.map +1 -0
- package/lib/rate-limit/simple-rate-limit-counter.js +98 -0
- package/lib/rate-limit/simple-rate-limit-counter.js.map +1 -0
- package/lib/readonly/index.d.ts +3 -0
- package/lib/readonly/index.js +2 -0
- package/lib/readonly/readonly.d.ts +39 -0
- package/lib/readonly/readonly.d.ts.map +1 -0
- package/lib/readonly/readonly.js +43 -0
- package/lib/readonly/readonly.js.map +1 -0
- package/lib/readonly/readonly.types.d.ts +40 -0
- package/lib/readonly/readonly.types.d.ts.map +1 -0
- package/lib/readonly/readonly.types.js +0 -0
- package/lib/throttle/index.d.ts +2 -0
- package/lib/throttle/index.js +2 -0
- package/lib/throttle/throttle.d.ts +35 -0
- package/lib/throttle/throttle.d.ts.map +1 -0
- package/lib/throttle/throttle.fn.d.ts +42 -0
- package/lib/throttle/throttle.fn.d.ts.map +1 -0
- package/lib/throttle/throttle.fn.js +52 -0
- package/lib/throttle/throttle.fn.js.map +1 -0
- package/lib/throttle/throttle.js +43 -0
- package/lib/throttle/throttle.js.map +1 -0
- package/lib/throttle-async/index.d.ts +2 -0
- package/lib/throttle-async/index.js +2 -0
- package/lib/throttle-async/throttle-async-executor.d.ts +79 -0
- package/lib/throttle-async/throttle-async-executor.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async-executor.js +122 -0
- package/lib/throttle-async/throttle-async-executor.js.map +1 -0
- package/lib/throttle-async/throttle-async.d.ts +68 -0
- package/lib/throttle-async/throttle-async.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async.fn.d.ts +41 -0
- package/lib/throttle-async/throttle-async.fn.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async.fn.js +46 -0
- package/lib/throttle-async/throttle-async.fn.js.map +1 -0
- package/lib/throttle-async/throttle-async.js +45 -0
- package/lib/throttle-async/throttle-async.js.map +1 -0
- package/lib/types.d.ts +81 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +0 -0
- package/package.json +40 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Method } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/rate-limit/rate-limit.types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Type definitions for the RateLimit decorator.
|
|
6
|
+
* Provides types for rate limiting configuration and counter implementations.
|
|
7
|
+
*
|
|
8
|
+
* @module @resq/typescript/decorators/rate-limit/types
|
|
9
|
+
*
|
|
10
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
11
|
+
* @license MIT
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @fileoverview Type definitions for the RateLimit decorator.
|
|
15
|
+
* Provides types for rate limiting configuration and counter implementations.
|
|
16
|
+
*
|
|
17
|
+
* @module @resq/typescript/decorators/rate-limit/types
|
|
18
|
+
*
|
|
19
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
20
|
+
* @license MIT
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Configuration options for rate limiting.
|
|
24
|
+
*
|
|
25
|
+
* @interface RateLimitConfigs
|
|
26
|
+
* @template T - The type of the class containing the decorated method
|
|
27
|
+
* @property {number} timeSpanMs - The time window in milliseconds
|
|
28
|
+
* @property {number} allowedCalls - Maximum number of calls allowed in the time window
|
|
29
|
+
* @property {((...args: any[]) => string) | keyof T} [keyResolver] - Function to generate rate limit keys (for per-user/entity limiting)
|
|
30
|
+
* @property {RateLimitCounter} [rateLimitCounter] - Custom counter implementation
|
|
31
|
+
* @property {RateLimitAsyncCounter} [rateLimitAsyncCounter] - Async counter implementation
|
|
32
|
+
* @property {() => void} [exceedHandler] - Handler called when rate limit is exceeded
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const config: RateLimitConfigs<ApiService> = {
|
|
37
|
+
* timeSpanMs: 60000, // 1 minute
|
|
38
|
+
* allowedCalls: 100, // 100 calls per minute
|
|
39
|
+
* keyResolver: (userId) => `user-${userId}`,
|
|
40
|
+
* exceedHandler: () => { throw new Error('Rate limit exceeded'); }
|
|
41
|
+
* };
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
interface RateLimitConfigs<T = any> {
|
|
45
|
+
/** The time window in milliseconds */
|
|
46
|
+
timeSpanMs: number;
|
|
47
|
+
/** Maximum number of calls allowed in the time window */
|
|
48
|
+
allowedCalls: number;
|
|
49
|
+
/** Function to generate rate limit keys (for per-user/entity limiting) */
|
|
50
|
+
keyResolver?: ((...args: unknown[]) => string) | keyof T;
|
|
51
|
+
/** Custom counter implementation */
|
|
52
|
+
rateLimitCounter?: RateLimitCounter;
|
|
53
|
+
/** Async counter implementation */
|
|
54
|
+
rateLimitAsyncCounter?: RateLimitAsyncCounter;
|
|
55
|
+
/** Handler called when rate limit is exceeded */
|
|
56
|
+
exceedHandler?: () => void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Interface for rate limit counter implementations.
|
|
60
|
+
* Used to track call counts within time windows.
|
|
61
|
+
*
|
|
62
|
+
* @interface RateLimitCounter
|
|
63
|
+
* @property {(key: string) => void} inc - Increment the count for a key
|
|
64
|
+
* @property {(key: string) => void} dec - Decrement the count for a key
|
|
65
|
+
* @property {(key: string) => number} getCount - Get the current count for a key
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* class InMemoryCounter implements RateLimitCounter {
|
|
70
|
+
* private counts = new Map<string, number>();
|
|
71
|
+
*
|
|
72
|
+
* inc(key: string): void {
|
|
73
|
+
* this.counts.set(key, (this.counts.get(key) ?? 0) + 1);
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* dec(key: string): void {
|
|
77
|
+
* const count = this.counts.get(key) ?? 0;
|
|
78
|
+
* if (count <= 1) {
|
|
79
|
+
* this.counts.delete(key);
|
|
80
|
+
* } else {
|
|
81
|
+
* this.counts.set(key, count - 1);
|
|
82
|
+
* }
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* getCount(key: string): number {
|
|
86
|
+
* return this.counts.get(key) ?? 0;
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
interface RateLimitCounter {
|
|
92
|
+
/** Increment the count for a key */
|
|
93
|
+
inc: (key: string) => void;
|
|
94
|
+
/** Decrement the count for a key */
|
|
95
|
+
dec: (key: string) => void;
|
|
96
|
+
/** Get the current count for a key */
|
|
97
|
+
getCount: (key: string) => number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Interface for async rate limit counter implementations.
|
|
101
|
+
* Use this when the counter needs to perform async operations (e.g., Redis, database).
|
|
102
|
+
*
|
|
103
|
+
* @interface RateLimitAsyncCounter
|
|
104
|
+
* @property {(key: string) => Promise<void>} inc - Increment the count for a key asynchronously
|
|
105
|
+
* @property {(key: string) => Promise<void>} dec - Decrement the count for a key asynchronously
|
|
106
|
+
* @property {(key: string) => Promise<number>} getCount - Get the current count for a key asynchronously
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* class RedisCounter implements RateLimitAsyncCounter {
|
|
111
|
+
* async inc(key: string): Promise<void> {
|
|
112
|
+
* await redis.incr(`ratelimit:${key}`);
|
|
113
|
+
* }
|
|
114
|
+
*
|
|
115
|
+
* async dec(key: string): Promise<void> {
|
|
116
|
+
* await redis.decr(`ratelimit:${key}`);
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* async getCount(key: string): Promise<number> {
|
|
120
|
+
* const count = await redis.get(`ratelimit:${key}`);
|
|
121
|
+
* return parseInt(count ?? '0', 10);
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
interface RateLimitAsyncCounter {
|
|
127
|
+
/** Increment the count for a key asynchronously */
|
|
128
|
+
inc: (key: string) => Promise<void>;
|
|
129
|
+
/** Decrement the count for a key asynchronously */
|
|
130
|
+
dec: (key: string) => Promise<void>;
|
|
131
|
+
/** Get the current count for a key asynchronously */
|
|
132
|
+
getCount: (key: string) => Promise<number>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Type for the @rateLimit decorator function.
|
|
136
|
+
*
|
|
137
|
+
* @template T - The type of the class containing the decorated method
|
|
138
|
+
* @template D - The return type of the decorated method
|
|
139
|
+
*
|
|
140
|
+
* @param {T} target - The class prototype
|
|
141
|
+
* @param {keyof T} propertyName - The name of the method being decorated
|
|
142
|
+
* @param {TypedPropertyDescriptor<Method<D>>} descriptor - The property descriptor
|
|
143
|
+
* @returns {TypedPropertyDescriptor<Method<D>>} The modified descriptor
|
|
144
|
+
*/
|
|
145
|
+
type RateLimitable<T, D> = (target: T, propertyName: keyof T, descriptor: TypedPropertyDescriptor<Method<D>>) => TypedPropertyDescriptor<Method<D>>;
|
|
146
|
+
//#endregion
|
|
147
|
+
export { RateLimitAsyncCounter, RateLimitConfigs, RateLimitCounter, RateLimitable };
|
|
148
|
+
//# sourceMappingURL=rate-limit.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.types.d.ts","names":[],"sources":["../../src/rate-limit/rate-limit.types.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHA;;;;;;;;;;;;AAoCA;;UApFiB,gBAAA;EAsFO;EApFtB,UAAA;EAwF2B;EAtF3B,YAAA;EAsFkC;EApFlC,WAAA,QAAmB,IAAA,gCAAoC,CAAA;EAgFjD;EA9EN,gBAAA,GAAmB,gBAAA;EAgFnB;EA9EA,qBAAA,GAAwB,qBAAA;EA8EF;EA5EtB,aAAA;AAAA;;;;AA4FF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAxDiB,gBAAA;;EAEf,GAAA,GAAM,GAAA;;EAEN,GAAA,GAAM,GAAA;;EAEN,QAAA,GAAW,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BI,qBAAA;;EAEf,GAAA,GAAM,GAAA,aAAgB,OAAA;;EAEtB,GAAA,GAAM,GAAA,aAAgB,OAAA;;EAEtB,QAAA,GAAW,GAAA,aAAgB,OAAA;AAAA;;;;;;;;;;;;KAcjB,aAAA,UACV,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,uBAAA,CAAwB,MAAA,CAAO,CAAA,OACxC,uBAAA,CAAwB,MAAA,CAAO,CAAA"}
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { RateLimitCounter } from "./rate-limit.types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/rate-limit/simple-rate-limit-counter.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Simple in-memory implementation of RateLimitCounter.
|
|
6
|
+
* Uses a Map to store counts for each key.
|
|
7
|
+
*
|
|
8
|
+
* @class SimpleRateLimitCounter
|
|
9
|
+
* @implements {RateLimitCounter}
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const counter = new SimpleRateLimitCounter();
|
|
14
|
+
*
|
|
15
|
+
* // Track API calls per user
|
|
16
|
+
* counter.inc('user-1');
|
|
17
|
+
* counter.inc('user-1');
|
|
18
|
+
* counter.inc('user-2');
|
|
19
|
+
*
|
|
20
|
+
* console.log(counter.getCount('user-1')); // 2
|
|
21
|
+
* console.log(counter.getCount('user-2')); // 1
|
|
22
|
+
* console.log(counter.getCount('user-3')); // 0
|
|
23
|
+
*
|
|
24
|
+
* // After some time, decrement
|
|
25
|
+
* counter.dec('user-1');
|
|
26
|
+
* console.log(counter.getCount('user-1')); // 1
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare class SimpleRateLimitCounter implements RateLimitCounter {
|
|
30
|
+
private readonly counterMap;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new SimpleRateLimitCounter instance.
|
|
33
|
+
*
|
|
34
|
+
* @param {Map<string, number>} [counterMap=new Map()] - Optional existing Map to use for storage
|
|
35
|
+
*/
|
|
36
|
+
constructor(counterMap?: Map<string, number>);
|
|
37
|
+
/**
|
|
38
|
+
* Gets the current count for a key.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} key - The key to get count for
|
|
41
|
+
* @returns {number} The current count (0 if key doesn't exist)
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const counter = new SimpleRateLimitCounter();
|
|
46
|
+
* console.log(counter.getCount('key')); // 0
|
|
47
|
+
* counter.inc('key');
|
|
48
|
+
* console.log(counter.getCount('key')); // 1
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
getCount(key: string): number;
|
|
52
|
+
/**
|
|
53
|
+
* Increments the count for a key.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} key - The key to increment
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const counter = new SimpleRateLimitCounter();
|
|
61
|
+
* counter.inc('user-123');
|
|
62
|
+
* counter.inc('user-123');
|
|
63
|
+
* console.log(counter.getCount('user-123')); // 2
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
inc(key: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Decrements the count for a key.
|
|
69
|
+
* Removes the key from the map if count reaches 0.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} key - The key to decrement
|
|
72
|
+
* @returns {void}
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const counter = new SimpleRateLimitCounter();
|
|
77
|
+
* counter.inc('key');
|
|
78
|
+
* counter.inc('key');
|
|
79
|
+
* counter.dec('key');
|
|
80
|
+
* console.log(counter.getCount('key')); // 1
|
|
81
|
+
* counter.dec('key');
|
|
82
|
+
* console.log(counter.getCount('key')); // 0 (key removed from map)
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
dec(key: string): void;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { SimpleRateLimitCounter };
|
|
89
|
+
//# sourceMappingURL=simple-rate-limit-counter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simple-rate-limit-counter.d.ts","names":[],"sources":["../../src/rate-limit/simple-rate-limit-counter.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2Ca,sBAAA,YAAkC,gBAAA;EAAA,iBAMhB,UAAA;;;;;;cAAA,UAAA,GAAU,GAAA;;;;;;;;;;;;;;;EAgBvC,QAAA,CAAS,GAAA;;;;;;;;;;;;;;;EAkBT,GAAA,CAAI,GAAA;;;;;;;;;;;;;;;;;;;EA0BJ,GAAA,CAAI,GAAA;AAAA"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
//#region src/rate-limit/simple-rate-limit-counter.ts
|
|
2
|
+
/**
|
|
3
|
+
* Simple in-memory implementation of RateLimitCounter.
|
|
4
|
+
* Uses a Map to store counts for each key.
|
|
5
|
+
*
|
|
6
|
+
* @class SimpleRateLimitCounter
|
|
7
|
+
* @implements {RateLimitCounter}
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const counter = new SimpleRateLimitCounter();
|
|
12
|
+
*
|
|
13
|
+
* // Track API calls per user
|
|
14
|
+
* counter.inc('user-1');
|
|
15
|
+
* counter.inc('user-1');
|
|
16
|
+
* counter.inc('user-2');
|
|
17
|
+
*
|
|
18
|
+
* console.log(counter.getCount('user-1')); // 2
|
|
19
|
+
* console.log(counter.getCount('user-2')); // 1
|
|
20
|
+
* console.log(counter.getCount('user-3')); // 0
|
|
21
|
+
*
|
|
22
|
+
* // After some time, decrement
|
|
23
|
+
* counter.dec('user-1');
|
|
24
|
+
* console.log(counter.getCount('user-1')); // 1
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
var SimpleRateLimitCounter = class {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new SimpleRateLimitCounter instance.
|
|
30
|
+
*
|
|
31
|
+
* @param {Map<string, number>} [counterMap=new Map()] - Optional existing Map to use for storage
|
|
32
|
+
*/
|
|
33
|
+
constructor(counterMap = /* @__PURE__ */ new Map()) {
|
|
34
|
+
this.counterMap = counterMap;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Gets the current count for a key.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} key - The key to get count for
|
|
40
|
+
* @returns {number} The current count (0 if key doesn't exist)
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const counter = new SimpleRateLimitCounter();
|
|
45
|
+
* console.log(counter.getCount('key')); // 0
|
|
46
|
+
* counter.inc('key');
|
|
47
|
+
* console.log(counter.getCount('key')); // 1
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
getCount(key) {
|
|
51
|
+
return this.counterMap.get(key) ?? 0;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Increments the count for a key.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} key - The key to increment
|
|
57
|
+
* @returns {void}
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const counter = new SimpleRateLimitCounter();
|
|
62
|
+
* counter.inc('user-123');
|
|
63
|
+
* counter.inc('user-123');
|
|
64
|
+
* console.log(counter.getCount('user-123')); // 2
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
inc(key) {
|
|
68
|
+
if (!this.counterMap.has(key)) this.counterMap.set(key, 0);
|
|
69
|
+
this.counterMap.set(key, (this.counterMap.get(key) ?? 0) + 1);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decrements the count for a key.
|
|
73
|
+
* Removes the key from the map if count reaches 0.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} key - The key to decrement
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const counter = new SimpleRateLimitCounter();
|
|
81
|
+
* counter.inc('key');
|
|
82
|
+
* counter.inc('key');
|
|
83
|
+
* counter.dec('key');
|
|
84
|
+
* console.log(counter.getCount('key')); // 1
|
|
85
|
+
* counter.dec('key');
|
|
86
|
+
* console.log(counter.getCount('key')); // 0 (key removed from map)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
dec(key) {
|
|
90
|
+
const currentCount = this.counterMap.get(key);
|
|
91
|
+
if (currentCount !== void 0) if (currentCount <= 1) this.counterMap.delete(key);
|
|
92
|
+
else this.counterMap.set(key, currentCount - 1);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { SimpleRateLimitCounter };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=simple-rate-limit-counter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simple-rate-limit-counter.js","names":[],"sources":["../../src/rate-limit/simple-rate-limit-counter.ts"],"sourcesContent":["/**\n * Copyright 2026 ResQ\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { RateLimitCounter } from './index.js';\n\n/**\n * Simple in-memory implementation of RateLimitCounter.\n * Uses a Map to store counts for each key.\n *\n * @class SimpleRateLimitCounter\n * @implements {RateLimitCounter}\n *\n * @example\n * ```typescript\n * const counter = new SimpleRateLimitCounter();\n *\n * // Track API calls per user\n * counter.inc('user-1');\n * counter.inc('user-1');\n * counter.inc('user-2');\n *\n * console.log(counter.getCount('user-1')); // 2\n * console.log(counter.getCount('user-2')); // 1\n * console.log(counter.getCount('user-3')); // 0\n *\n * // After some time, decrement\n * counter.dec('user-1');\n * console.log(counter.getCount('user-1')); // 1\n * ```\n */\nexport class SimpleRateLimitCounter implements RateLimitCounter {\n /**\n * Creates a new SimpleRateLimitCounter instance.\n *\n * @param {Map<string, number>} [counterMap=new Map()] - Optional existing Map to use for storage\n */\n constructor(private readonly counterMap = new Map<string, number>()) {}\n\n /**\n * Gets the current count for a key.\n *\n * @param {string} key - The key to get count for\n * @returns {number} The current count (0 if key doesn't exist)\n *\n * @example\n * ```typescript\n * const counter = new SimpleRateLimitCounter();\n * console.log(counter.getCount('key')); // 0\n * counter.inc('key');\n * console.log(counter.getCount('key')); // 1\n * ```\n */\n getCount(key: string): number {\n return this.counterMap.get(key) ?? 0;\n }\n\n /**\n * Increments the count for a key.\n *\n * @param {string} key - The key to increment\n * @returns {void}\n *\n * @example\n * ```typescript\n * const counter = new SimpleRateLimitCounter();\n * counter.inc('user-123');\n * counter.inc('user-123');\n * console.log(counter.getCount('user-123')); // 2\n * ```\n */\n inc(key: string): void {\n if (!this.counterMap.has(key)) {\n this.counterMap.set(key, 0);\n }\n\n this.counterMap.set(key, (this.counterMap.get(key) ?? 0) + 1);\n }\n\n /**\n * Decrements the count for a key.\n * Removes the key from the map if count reaches 0.\n *\n * @param {string} key - The key to decrement\n * @returns {void}\n *\n * @example\n * ```typescript\n * const counter = new SimpleRateLimitCounter();\n * counter.inc('key');\n * counter.inc('key');\n * counter.dec('key');\n * console.log(counter.getCount('key')); // 1\n * counter.dec('key');\n * console.log(counter.getCount('key')); // 0 (key removed from map)\n * ```\n */\n dec(key: string): void {\n const currentCount = this.counterMap.get(key);\n\n if (currentCount !== undefined) {\n if (currentCount <= 1) {\n this.counterMap.delete(key);\n } else {\n this.counterMap.set(key, currentCount - 1);\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,yBAAb,MAAgE;;;;;;CAM9D,YAAY,6BAA8B,IAAI,KAAqB,EAAE;AAAxC,OAAA,aAAA;;;;;;;;;;;;;;;;CAgB7B,SAAS,KAAqB;AAC5B,SAAO,KAAK,WAAW,IAAI,IAAI,IAAI;;;;;;;;;;;;;;;;CAiBrC,IAAI,KAAmB;AACrB,MAAI,CAAC,KAAK,WAAW,IAAI,IAAI,CAC3B,MAAK,WAAW,IAAI,KAAK,EAAE;AAG7B,OAAK,WAAW,IAAI,MAAM,KAAK,WAAW,IAAI,IAAI,IAAI,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;CAqB/D,IAAI,KAAmB;EACrB,MAAM,eAAe,KAAK,WAAW,IAAI,IAAI;AAE7C,MAAI,iBAAiB,KAAA,EACnB,KAAI,gBAAgB,EAClB,MAAK,WAAW,OAAO,IAAI;MAE3B,MAAK,WAAW,IAAI,KAAK,eAAe,EAAE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Readonlyable } from "./readonly.types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/readonly/readonly.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that makes a method read-only (non-writable).
|
|
6
|
+
* Prevents the method from being reassigned after class instantiation.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the class containing the decorated method
|
|
9
|
+
* @returns {Readonlyable<T>} The decorator function
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* class SecureApi {
|
|
14
|
+
* @readonly()
|
|
15
|
+
* authenticate(): Promise<AuthToken> {
|
|
16
|
+
* return this.performAuth();
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* @readonly()
|
|
20
|
+
* getBaseUrl(): string {
|
|
21
|
+
* return 'https://api.example.com';
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* const api = new SecureApi();
|
|
26
|
+
*
|
|
27
|
+
* // These will throw TypeError
|
|
28
|
+
* // api.authenticate = () => Promise.resolve(fakeToken);
|
|
29
|
+
* // api.getBaseUrl = () => 'https://evil.com';
|
|
30
|
+
*
|
|
31
|
+
* // Calling the methods works normally
|
|
32
|
+
* const token = await api.authenticate();
|
|
33
|
+
* const url = api.getBaseUrl();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function readonly<T = any>(): Readonlyable<T>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { readonly };
|
|
39
|
+
//# sourceMappingURL=readonly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readonly.d.ts","names":[],"sources":["../../src/readonly/readonly.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmDgB,QAAA,SAAA,CAAA,GAAqB,YAAA,CAAa,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/readonly/readonly.ts
|
|
2
|
+
/**
|
|
3
|
+
* Decorator that makes a method read-only (non-writable).
|
|
4
|
+
* Prevents the method from being reassigned after class instantiation.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type of the class containing the decorated method
|
|
7
|
+
* @returns {Readonlyable<T>} The decorator function
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* class SecureApi {
|
|
12
|
+
* @readonly()
|
|
13
|
+
* authenticate(): Promise<AuthToken> {
|
|
14
|
+
* return this.performAuth();
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* @readonly()
|
|
18
|
+
* getBaseUrl(): string {
|
|
19
|
+
* return 'https://api.example.com';
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* const api = new SecureApi();
|
|
24
|
+
*
|
|
25
|
+
* // These will throw TypeError
|
|
26
|
+
* // api.authenticate = () => Promise.resolve(fakeToken);
|
|
27
|
+
* // api.getBaseUrl = () => 'https://evil.com';
|
|
28
|
+
*
|
|
29
|
+
* // Calling the methods works normally
|
|
30
|
+
* const token = await api.authenticate();
|
|
31
|
+
* const url = api.getBaseUrl();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function readonly() {
|
|
35
|
+
return (target, key, descriptor) => {
|
|
36
|
+
descriptor.writable = false;
|
|
37
|
+
return descriptor;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { readonly };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=readonly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readonly.js","names":[],"sources":["../../src/readonly/readonly.ts"],"sourcesContent":["/**\n * Copyright 2026 ResQ\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Method } from '../types.js';\nimport type { Readonlyable } from './index.js';\n\n/**\n * Decorator that makes a method read-only (non-writable).\n * Prevents the method from being reassigned after class instantiation.\n *\n * @template T - The type of the class containing the decorated method\n * @returns {Readonlyable<T>} The decorator function\n *\n * @example\n * ```typescript\n * class SecureApi {\n * @readonly()\n * authenticate(): Promise<AuthToken> {\n * return this.performAuth();\n * }\n *\n * @readonly()\n * getBaseUrl(): string {\n * return 'https://api.example.com';\n * }\n * }\n *\n * const api = new SecureApi();\n *\n * // These will throw TypeError\n * // api.authenticate = () => Promise.resolve(fakeToken);\n * // api.getBaseUrl = () => 'https://evil.com';\n *\n * // Calling the methods works normally\n * const token = await api.authenticate();\n * const url = api.getBaseUrl();\n * ```\n */\nexport function readonly<T = any>(): Readonlyable<T> {\n return (\n target: T,\n key: keyof T,\n descriptor: TypedPropertyDescriptor<Method<any>>,\n ): TypedPropertyDescriptor<Method<any>> => {\n descriptor.writable = false;\n\n return descriptor;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,WAAqC;AACnD,SACE,QACA,KACA,eACyC;AACzC,aAAW,WAAW;AAEtB,SAAO"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/readonly/readonly.types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 ResQ
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Type for decorators that make methods read-only.
|
|
19
|
+
*
|
|
20
|
+
* @template T - The type of the class containing the decorated method
|
|
21
|
+
*
|
|
22
|
+
* @param {T} target - The class prototype
|
|
23
|
+
* @param {keyof T} propertyName - The name of the method being decorated
|
|
24
|
+
* @param {PropertyDescriptor} descriptor - The property descriptor
|
|
25
|
+
* @returns {PropertyDescriptor} The modified descriptor with writable set to false
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* type ReadonlyMethod = Readonlyable<MyClass>;
|
|
30
|
+
*
|
|
31
|
+
* const decorator: ReadonlyMethod = (target, key, descriptor) => {
|
|
32
|
+
* descriptor.writable = false;
|
|
33
|
+
* return descriptor;
|
|
34
|
+
* };
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
type Readonlyable<T = any> = (target: T, propertyName: keyof T, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { Readonlyable };
|
|
40
|
+
//# sourceMappingURL=readonly.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readonly.types.d.ts","names":[],"sources":["../../src/readonly/readonly.types.ts"],"mappings":";;AAoCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,YAAA,aACV,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,kBAAA,KACT,kBAAA"}
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Decorator } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/throttle/throttle.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that throttles method calls to once per specified time period.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the class containing the decorated method
|
|
8
|
+
* @param {number} delayMs - The throttle interval in milliseconds
|
|
9
|
+
* @returns {Decorator<T>} The decorator function
|
|
10
|
+
*
|
|
11
|
+
* @throws {Error} When applied to a non-method property
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class ResizeHandler {
|
|
16
|
+
* private width = window.innerWidth;
|
|
17
|
+
* private height = window.innerHeight;
|
|
18
|
+
*
|
|
19
|
+
* @throttle(200)
|
|
20
|
+
* handleResize(): void {
|
|
21
|
+
* this.width = window.innerWidth;
|
|
22
|
+
* this.height = window.innerHeight;
|
|
23
|
+
* this.render();
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* const handler = new ResizeHandler();
|
|
28
|
+
* window.addEventListener('resize', () => handler.handleResize());
|
|
29
|
+
* // handleResize executes at most once every 200ms during resize
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function throttle<T = any>(delayMs: number): Decorator<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { throttle };
|
|
35
|
+
//# sourceMappingURL=throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.d.ts","names":[],"sources":["../../src/throttle/throttle.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+CgB,QAAA,SAAA,CAAkB,OAAA,WAAkB,SAAA,CAAU,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Method } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/throttle/throttle.fn.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a method to throttle its execution to once per time period.
|
|
6
|
+
*
|
|
7
|
+
* @template D - The return type of the original method
|
|
8
|
+
* @template A - The argument types of the original method
|
|
9
|
+
* @param {Method<D, A>} originalMethod - The method to throttle
|
|
10
|
+
* @param {number} delayMs - The throttle interval in milliseconds
|
|
11
|
+
* @returns {Method<void, A>} The throttled method
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class ScrollTracker {
|
|
16
|
+
* scrollY = 0;
|
|
17
|
+
*
|
|
18
|
+
* updatePosition(y: number): void {
|
|
19
|
+
* this.scrollY = y;
|
|
20
|
+
* console.log('Position updated:', y);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* const tracker = new ScrollTracker();
|
|
25
|
+
*
|
|
26
|
+
* // Throttle to once per 100ms
|
|
27
|
+
* const throttledUpdate = throttleFn(
|
|
28
|
+
* tracker.updatePosition.bind(tracker),
|
|
29
|
+
* 100
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* // Rapid scroll events
|
|
33
|
+
* window.addEventListener('scroll', (e) => {
|
|
34
|
+
* throttledUpdate(window.scrollY);
|
|
35
|
+
* // Only logs once every 100ms even during rapid scrolling
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function throttleFn<D = any, A extends any[] = any[]>(originalMethod: Method<D, A>, delayMs: number): Method<void, A>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { throttleFn };
|
|
42
|
+
//# sourceMappingURL=throttle.fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.fn.d.ts","names":[],"sources":["../../src/throttle/throttle.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqDgB,UAAA,kCAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,OAAA,WACC,MAAA,OAAa,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/throttle/throttle.fn.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a method to throttle its execution to once per time period.
|
|
4
|
+
*
|
|
5
|
+
* @template D - The return type of the original method
|
|
6
|
+
* @template A - The argument types of the original method
|
|
7
|
+
* @param {Method<D, A>} originalMethod - The method to throttle
|
|
8
|
+
* @param {number} delayMs - The throttle interval in milliseconds
|
|
9
|
+
* @returns {Method<void, A>} The throttled method
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* class ScrollTracker {
|
|
14
|
+
* scrollY = 0;
|
|
15
|
+
*
|
|
16
|
+
* updatePosition(y: number): void {
|
|
17
|
+
* this.scrollY = y;
|
|
18
|
+
* console.log('Position updated:', y);
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* const tracker = new ScrollTracker();
|
|
23
|
+
*
|
|
24
|
+
* // Throttle to once per 100ms
|
|
25
|
+
* const throttledUpdate = throttleFn(
|
|
26
|
+
* tracker.updatePosition.bind(tracker),
|
|
27
|
+
* 100
|
|
28
|
+
* );
|
|
29
|
+
*
|
|
30
|
+
* // Rapid scroll events
|
|
31
|
+
* window.addEventListener('scroll', (e) => {
|
|
32
|
+
* throttledUpdate(window.scrollY);
|
|
33
|
+
* // Only logs once every 100ms even during rapid scrolling
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function throttleFn(originalMethod, delayMs) {
|
|
38
|
+
let throttling = false;
|
|
39
|
+
return function(...args) {
|
|
40
|
+
if (!throttling) {
|
|
41
|
+
throttling = true;
|
|
42
|
+
originalMethod.apply(this, args);
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
throttling = false;
|
|
45
|
+
}, delayMs);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { throttleFn };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=throttle.fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.fn.js","names":[],"sources":["../../src/throttle/throttle.fn.ts"],"sourcesContent":["/**\n * Copyright 2026 ResQ\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Method } from '../types.js';\n\n/**\n * Wraps a method to throttle its execution to once per time period.\n *\n * @template D - The return type of the original method\n * @template A - The argument types of the original method\n * @param {Method<D, A>} originalMethod - The method to throttle\n * @param {number} delayMs - The throttle interval in milliseconds\n * @returns {Method<void, A>} The throttled method\n *\n * @example\n * ```typescript\n * class ScrollTracker {\n * scrollY = 0;\n *\n * updatePosition(y: number): void {\n * this.scrollY = y;\n * console.log('Position updated:', y);\n * }\n * }\n *\n * const tracker = new ScrollTracker();\n *\n * // Throttle to once per 100ms\n * const throttledUpdate = throttleFn(\n * tracker.updatePosition.bind(tracker),\n * 100\n * );\n *\n * // Rapid scroll events\n * window.addEventListener('scroll', (e) => {\n * throttledUpdate(window.scrollY);\n * // Only logs once every 100ms even during rapid scrolling\n * });\n * ```\n */\nexport function throttleFn<D = any, A extends any[] = any[]>(\n originalMethod: Method<D, A>,\n delayMs: number,\n): Method<void, A> {\n let throttling = false;\n return function (this: any, ...args: A): void {\n if (!throttling) {\n throttling = true;\n originalMethod.apply(this, args);\n\n setTimeout(() => {\n throttling = false;\n }, delayMs);\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,WACd,gBACA,SACiB;CACjB,IAAI,aAAa;AACjB,QAAO,SAAqB,GAAG,MAAe;AAC5C,MAAI,CAAC,YAAY;AACf,gBAAa;AACb,kBAAe,MAAM,MAAM,KAAK;AAEhC,oBAAiB;AACf,iBAAa;MACZ,QAAQ"}
|