@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,43 @@
|
|
|
1
|
+
import { throttleFn } from "./throttle.fn.js";
|
|
2
|
+
//#region src/throttle/throttle.ts
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that throttles method calls to once per specified time period.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type of the class containing the decorated method
|
|
7
|
+
* @param {number} delayMs - The throttle interval in milliseconds
|
|
8
|
+
* @returns {Decorator<T>} The decorator function
|
|
9
|
+
*
|
|
10
|
+
* @throws {Error} When applied to a non-method property
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* class ResizeHandler {
|
|
15
|
+
* private width = window.innerWidth;
|
|
16
|
+
* private height = window.innerHeight;
|
|
17
|
+
*
|
|
18
|
+
* @throttle(200)
|
|
19
|
+
* handleResize(): void {
|
|
20
|
+
* this.width = window.innerWidth;
|
|
21
|
+
* this.height = window.innerHeight;
|
|
22
|
+
* this.render();
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* const handler = new ResizeHandler();
|
|
27
|
+
* window.addEventListener('resize', () => handler.handleResize());
|
|
28
|
+
* // handleResize executes at most once every 200ms during resize
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function throttle(delayMs) {
|
|
32
|
+
return (target, propertyName, descriptor) => {
|
|
33
|
+
if (descriptor.value) {
|
|
34
|
+
descriptor.value = throttleFn(descriptor.value, delayMs);
|
|
35
|
+
return descriptor;
|
|
36
|
+
}
|
|
37
|
+
throw new Error("@throttle is applicable only on a methods.");
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { throttle };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=throttle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.js","names":[],"sources":["../../src/throttle/throttle.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 { Decorator, Method } from '../types.js';\nimport { throttleFn } from './throttle.fn.js';\n\n/**\n * Decorator that throttles method calls to once per specified time period.\n *\n * @template T - The type of the class containing the decorated method\n * @param {number} delayMs - The throttle interval in milliseconds\n * @returns {Decorator<T>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class ResizeHandler {\n * private width = window.innerWidth;\n * private height = window.innerHeight;\n *\n * @throttle(200)\n * handleResize(): void {\n * this.width = window.innerWidth;\n * this.height = window.innerHeight;\n * this.render();\n * }\n * }\n *\n * const handler = new ResizeHandler();\n * window.addEventListener('resize', () => handler.handleResize());\n * // handleResize executes at most once every 200ms during resize\n * ```\n */\nexport function throttle<T = any>(delayMs: number): Decorator<T> {\n return (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<Method<any>>,\n ): TypedPropertyDescriptor<Method<any>> => {\n if (descriptor.value) {\n descriptor.value = throttleFn(descriptor.value, delayMs);\n\n return descriptor;\n }\n\n throw new Error('@throttle is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,SAAkB,SAA+B;AAC/D,SACE,QACA,cACA,eACyC;AACzC,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,WAAW,WAAW,OAAO,QAAQ;AAExD,UAAO;;AAGT,QAAM,IAAI,MAAM,6CAA6C"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AsyncMethod } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/throttle-async/throttle-async-executor.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Manages the queue and execution of throttled async method calls.
|
|
6
|
+
* Ensures that only a specified number of calls run concurrently,
|
|
7
|
+
* queueing additional calls until slots become available.
|
|
8
|
+
*
|
|
9
|
+
* @class ThrottleAsyncExecutor
|
|
10
|
+
* @template D - The resolved type of the async method
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const executor = new ThrottleAsyncExecutor(
|
|
15
|
+
* async (data) => await fetchData(data),
|
|
16
|
+
* 3 // Max 3 concurrent calls
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* // Execute multiple calls
|
|
20
|
+
* const promises = [
|
|
21
|
+
* executor.exec(this, ['arg1']),
|
|
22
|
+
* executor.exec(this, ['arg2']),
|
|
23
|
+
* executor.exec(this, ['arg3']),
|
|
24
|
+
* executor.exec(this, ['arg4']), // Queued
|
|
25
|
+
* executor.exec(this, ['arg5']), // Queued
|
|
26
|
+
* ];
|
|
27
|
+
*
|
|
28
|
+
* const results = await Promise.all(promises);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare class ThrottleAsyncExecutor<D> {
|
|
32
|
+
private readonly fun;
|
|
33
|
+
private readonly parallelCalls;
|
|
34
|
+
/**
|
|
35
|
+
* Number of calls currently executing.
|
|
36
|
+
* @private
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
private onGoingCallsCount;
|
|
40
|
+
/**
|
|
41
|
+
* Queue of pending calls waiting to execute.
|
|
42
|
+
* @private
|
|
43
|
+
* @type {Queue<CallArgs<D>>}
|
|
44
|
+
*/
|
|
45
|
+
private readonly callsToRun;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a new ThrottleAsyncExecutor instance.
|
|
48
|
+
*
|
|
49
|
+
* @param {AsyncMethod<D>} fun - The async method to throttle
|
|
50
|
+
* @param {number} parallelCalls - Maximum number of concurrent calls allowed
|
|
51
|
+
*/
|
|
52
|
+
constructor(fun: AsyncMethod<D>, parallelCalls: number);
|
|
53
|
+
/**
|
|
54
|
+
* Queues a method call for execution.
|
|
55
|
+
*
|
|
56
|
+
* @param {any} context - The `this` context for the method call
|
|
57
|
+
* @param {any[]} args - The arguments to pass to the method
|
|
58
|
+
* @returns {Promise<D>} A promise that resolves with the method result
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const executor = new ThrottleAsyncExecutor(myAsyncMethod, 2);
|
|
63
|
+
*
|
|
64
|
+
* // Queue a call
|
|
65
|
+
* const result = await executor.exec(this, ['arg1', 'arg2']);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
exec(context: unknown, args: unknown[]): Promise<D>;
|
|
69
|
+
/**
|
|
70
|
+
* Attempts to execute the next queued call if capacity allows.
|
|
71
|
+
*
|
|
72
|
+
* @private
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
private tryCall;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { ThrottleAsyncExecutor };
|
|
79
|
+
//# sourceMappingURL=throttle-async-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async-executor.d.ts","names":[],"sources":["../../src/throttle-async/throttle-async-executor.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8Ca,qBAAA;EAAA,iBAsBQ,GAAA;EAAA,iBACA,aAAA;;;;;;UAjBX,iBAAA;;;;;;mBAOS,UAAA;;;;;;;cASE,GAAA,EAAK,WAAA,CAAY,CAAA,GACjB,aAAA;;;;;;;;;;;;;;;;EAkBnB,IAAA,CAAK,OAAA,WAAkB,IAAA,cAAkB,OAAA,CAAQ,CAAA;;;;;;;UAsBzC,OAAA;AAAA"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Queue } from "../_utils.js";
|
|
2
|
+
//#region src/throttle-async/throttle-async-executor.ts
|
|
3
|
+
/**
|
|
4
|
+
* Copyright 2026 ResQ
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Manages the queue and execution of throttled async method calls.
|
|
20
|
+
* Ensures that only a specified number of calls run concurrently,
|
|
21
|
+
* queueing additional calls until slots become available.
|
|
22
|
+
*
|
|
23
|
+
* @class ThrottleAsyncExecutor
|
|
24
|
+
* @template D - The resolved type of the async method
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const executor = new ThrottleAsyncExecutor(
|
|
29
|
+
* async (data) => await fetchData(data),
|
|
30
|
+
* 3 // Max 3 concurrent calls
|
|
31
|
+
* );
|
|
32
|
+
*
|
|
33
|
+
* // Execute multiple calls
|
|
34
|
+
* const promises = [
|
|
35
|
+
* executor.exec(this, ['arg1']),
|
|
36
|
+
* executor.exec(this, ['arg2']),
|
|
37
|
+
* executor.exec(this, ['arg3']),
|
|
38
|
+
* executor.exec(this, ['arg4']), // Queued
|
|
39
|
+
* executor.exec(this, ['arg5']), // Queued
|
|
40
|
+
* ];
|
|
41
|
+
*
|
|
42
|
+
* const results = await Promise.all(promises);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
var ThrottleAsyncExecutor = class {
|
|
46
|
+
/**
|
|
47
|
+
* Number of calls currently executing.
|
|
48
|
+
* @private
|
|
49
|
+
* @type {number}
|
|
50
|
+
*/
|
|
51
|
+
onGoingCallsCount = 0;
|
|
52
|
+
/**
|
|
53
|
+
* Queue of pending calls waiting to execute.
|
|
54
|
+
* @private
|
|
55
|
+
* @type {Queue<CallArgs<D>>}
|
|
56
|
+
*/
|
|
57
|
+
callsToRun = new Queue();
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new ThrottleAsyncExecutor instance.
|
|
60
|
+
*
|
|
61
|
+
* @param {AsyncMethod<D>} fun - The async method to throttle
|
|
62
|
+
* @param {number} parallelCalls - Maximum number of concurrent calls allowed
|
|
63
|
+
*/
|
|
64
|
+
constructor(fun, parallelCalls) {
|
|
65
|
+
this.fun = fun;
|
|
66
|
+
this.parallelCalls = parallelCalls;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Queues a method call for execution.
|
|
70
|
+
*
|
|
71
|
+
* @param {any} context - The `this` context for the method call
|
|
72
|
+
* @param {any[]} args - The arguments to pass to the method
|
|
73
|
+
* @returns {Promise<D>} A promise that resolves with the method result
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const executor = new ThrottleAsyncExecutor(myAsyncMethod, 2);
|
|
78
|
+
*
|
|
79
|
+
* // Queue a call
|
|
80
|
+
* const result = await executor.exec(this, ['arg1', 'arg2']);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
exec(context, args) {
|
|
84
|
+
const callArgs = {
|
|
85
|
+
context,
|
|
86
|
+
args,
|
|
87
|
+
resolve: null,
|
|
88
|
+
reject: null
|
|
89
|
+
};
|
|
90
|
+
this.callsToRun.enqueue(callArgs);
|
|
91
|
+
const proms = new Promise((resolve, reject) => {
|
|
92
|
+
callArgs.resolve = resolve;
|
|
93
|
+
callArgs.reject = reject;
|
|
94
|
+
});
|
|
95
|
+
this.tryCall();
|
|
96
|
+
proms.hell = args[0];
|
|
97
|
+
return proms;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Attempts to execute the next queued call if capacity allows.
|
|
101
|
+
*
|
|
102
|
+
* @private
|
|
103
|
+
* @returns {void}
|
|
104
|
+
*/
|
|
105
|
+
tryCall() {
|
|
106
|
+
if (this.callsToRun.getSize() > 0 && this.onGoingCallsCount < this.parallelCalls) {
|
|
107
|
+
const callArgs = this.callsToRun.dequeue();
|
|
108
|
+
if (callArgs) {
|
|
109
|
+
const { context, args, resolve, reject } = callArgs;
|
|
110
|
+
this.onGoingCallsCount += 1;
|
|
111
|
+
this.fun.apply(context, args).then(resolve).catch(reject).finally(() => {
|
|
112
|
+
this.onGoingCallsCount -= 1;
|
|
113
|
+
this.tryCall();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { ThrottleAsyncExecutor };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=throttle-async-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async-executor.js","names":[],"sources":["../../src/throttle-async/throttle-async-executor.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 { Queue } from '../_utils.js';\nimport type { AsyncMethod } from '../types.js';\n\n/**\n * Manages the queue and execution of throttled async method calls.\n * Ensures that only a specified number of calls run concurrently,\n * queueing additional calls until slots become available.\n *\n * @class ThrottleAsyncExecutor\n * @template D - The resolved type of the async method\n *\n * @example\n * ```typescript\n * const executor = new ThrottleAsyncExecutor(\n * async (data) => await fetchData(data),\n * 3 // Max 3 concurrent calls\n * );\n *\n * // Execute multiple calls\n * const promises = [\n * executor.exec(this, ['arg1']),\n * executor.exec(this, ['arg2']),\n * executor.exec(this, ['arg3']),\n * executor.exec(this, ['arg4']), // Queued\n * executor.exec(this, ['arg5']), // Queued\n * ];\n *\n * const results = await Promise.all(promises);\n * ```\n */\nexport class ThrottleAsyncExecutor<D> {\n /**\n * Number of calls currently executing.\n * @private\n * @type {number}\n */\n private onGoingCallsCount = 0;\n\n /**\n * Queue of pending calls waiting to execute.\n * @private\n * @type {Queue<CallArgs<D>>}\n */\n private readonly callsToRun = new Queue<CallArgs<D>>();\n\n /**\n * Creates a new ThrottleAsyncExecutor instance.\n *\n * @param {AsyncMethod<D>} fun - The async method to throttle\n * @param {number} parallelCalls - Maximum number of concurrent calls allowed\n */\n constructor(\n private readonly fun: AsyncMethod<D>,\n private readonly parallelCalls: number,\n ) {}\n\n /**\n * Queues a method call for execution.\n *\n * @param {any} context - The `this` context for the method call\n * @param {any[]} args - The arguments to pass to the method\n * @returns {Promise<D>} A promise that resolves with the method result\n *\n * @example\n * ```typescript\n * const executor = new ThrottleAsyncExecutor(myAsyncMethod, 2);\n *\n * // Queue a call\n * const result = await executor.exec(this, ['arg1', 'arg2']);\n * ```\n */\n exec(context: unknown, args: unknown[]): Promise<D> {\n const callArgs: CallArgs<D> = { context, args, resolve: null!, reject: null! };\n this.callsToRun.enqueue(callArgs);\n\n const proms = new Promise<D>((resolve, reject) => {\n callArgs.resolve = resolve;\n callArgs.reject = reject;\n });\n\n this.tryCall();\n\n (proms as unknown as { hell: unknown }).hell = args[0];\n\n return proms;\n }\n\n /**\n * Attempts to execute the next queued call if capacity allows.\n *\n * @private\n * @returns {void}\n */\n private tryCall(): void {\n if (this.callsToRun.getSize() > 0 && this.onGoingCallsCount < this.parallelCalls) {\n const callArgs = this.callsToRun.dequeue();\n if (callArgs) {\n const { context, args, resolve, reject } = callArgs;\n this.onGoingCallsCount += 1;\n this.fun\n .apply(context, args)\n .then(resolve)\n .catch(reject)\n .finally(() => {\n this.onGoingCallsCount -= 1;\n this.tryCall();\n });\n }\n }\n }\n}\n\n/**\n * Arguments for a queued async call.\n *\n * @interface CallArgs\n * @template T - The resolved type of the async call\n * @property {unknown} context - The `this` context\n * @property {unknown[]} args - The method arguments\n * @property {(value: T | PromiseLike<T>) => void} resolve - Promise resolve function\n * @property {(reason?: unknown) => void} reject - Promise reject function\n */\ninterface CallArgs<T> {\n context: unknown;\n args: unknown[];\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,IAAa,wBAAb,MAAsC;;;;;;CAMpC,oBAA4B;;;;;;CAO5B,aAA8B,IAAI,OAAoB;;;;;;;CAQtD,YACE,KACA,eACA;AAFiB,OAAA,MAAA;AACA,OAAA,gBAAA;;;;;;;;;;;;;;;;;CAkBnB,KAAK,SAAkB,MAA6B;EAClD,MAAM,WAAwB;GAAE;GAAS;GAAM,SAAS;GAAO,QAAQ;GAAO;AAC9E,OAAK,WAAW,QAAQ,SAAS;EAEjC,MAAM,QAAQ,IAAI,SAAY,SAAS,WAAW;AAChD,YAAS,UAAU;AACnB,YAAS,SAAS;IAClB;AAEF,OAAK,SAAS;AAEb,QAAuC,OAAO,KAAK;AAEpD,SAAO;;;;;;;;CAST,UAAwB;AACtB,MAAI,KAAK,WAAW,SAAS,GAAG,KAAK,KAAK,oBAAoB,KAAK,eAAe;GAChF,MAAM,WAAW,KAAK,WAAW,SAAS;AAC1C,OAAI,UAAU;IACZ,MAAM,EAAE,SAAS,MAAM,SAAS,WAAW;AAC3C,SAAK,qBAAqB;AAC1B,SAAK,IACF,MAAM,SAAS,KAAK,CACpB,KAAK,QAAQ,CACb,MAAM,OAAO,CACb,cAAc;AACb,UAAK,qBAAqB;AAC1B,UAAK,SAAS;MACd"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Decorator } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/throttle-async/throttle-async.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview ThrottleAsync decorator - limits the number of concurrent
|
|
6
|
+
* async method calls. Additional calls are queued and executed when slots
|
|
7
|
+
* become available.
|
|
8
|
+
*
|
|
9
|
+
* @module @resq/typescript/decorators/throttle-async
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* class ApiService {
|
|
14
|
+
* @throttleAsync(3) // Max 3 concurrent requests
|
|
15
|
+
* async fetchData(endpoint: string): Promise<Data> {
|
|
16
|
+
* return fetch(endpoint).then(r => r.json());
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* const api = new ApiService();
|
|
21
|
+
*
|
|
22
|
+
* // These will execute 3 at a time
|
|
23
|
+
* const results = await Promise.all([
|
|
24
|
+
* api.fetchData('/api/data1'),
|
|
25
|
+
* api.fetchData('/api/data2'),
|
|
26
|
+
* api.fetchData('/api/data3'),
|
|
27
|
+
* api.fetchData('/api/data4'), // Queued until a slot frees up
|
|
28
|
+
* api.fetchData('/api/data5'), // Queued until a slot frees up
|
|
29
|
+
* ]);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
33
|
+
* @license MIT
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Decorator that limits concurrent async method calls.
|
|
37
|
+
* Excess calls are queued and executed when slots become available.
|
|
38
|
+
*
|
|
39
|
+
* @template T - The type of the class containing the decorated method
|
|
40
|
+
* @template D - The resolved type of the async method
|
|
41
|
+
* @param {number} [parallelCalls=1] - Maximum number of concurrent calls allowed
|
|
42
|
+
* @returns {Decorator<T>} The decorator function
|
|
43
|
+
*
|
|
44
|
+
* @throws {Error} When applied to a non-method property
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* class BatchProcessor {
|
|
49
|
+
* // Process up to 5 items concurrently
|
|
50
|
+
* @throttleAsync(5)
|
|
51
|
+
* async processItem(item: Item): Promise<Result> {
|
|
52
|
+
* return await this.performHeavyProcessing(item);
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* const processor = new BatchProcessor();
|
|
57
|
+
* const items = Array.from({ length: 100 }, (_, i) => ({ id: i }));
|
|
58
|
+
*
|
|
59
|
+
* // Process 100 items, 5 at a time
|
|
60
|
+
* const results = await Promise.all(
|
|
61
|
+
* items.map(item => processor.processItem(item))
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare function throttleAsync<T = any, D = any>(parallelCalls?: number): Decorator<T>;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { throttleAsync };
|
|
68
|
+
//# sourceMappingURL=throttle-async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async.d.ts","names":[],"sources":["../../src/throttle-async/throttle-async.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiFgB,aAAA,kBAAA,CAAgC,aAAA,YAAyB,SAAA,CAAU,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AsyncMethod } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/throttle-async/throttle-async.fn.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wraps an async method to limit concurrent executions.
|
|
6
|
+
*
|
|
7
|
+
* @template D - The resolved type of the async method
|
|
8
|
+
* @template A - The argument types of the original method
|
|
9
|
+
* @param {AsyncMethod<D, A>} originalMethod - The async method to throttle
|
|
10
|
+
* @param {number} [parallelCalls=1] - Maximum number of concurrent calls
|
|
11
|
+
* @returns {AsyncMethod<D, A>} The throttled async method
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class ApiClient {
|
|
16
|
+
* async fetchUser(userId: string): Promise<User> {
|
|
17
|
+
* return fetch(`/api/users/${userId}`).then(r => r.json());
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* const client = new ApiClient();
|
|
22
|
+
*
|
|
23
|
+
* // Limit to 2 concurrent requests
|
|
24
|
+
* const throttledFetch = throttleAsyncFn(
|
|
25
|
+
* client.fetchUser.bind(client),
|
|
26
|
+
* 2
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* // Execute multiple calls, only 2 run concurrently
|
|
30
|
+
* const users = await Promise.all([
|
|
31
|
+
* throttledFetch('1'), // Starts immediately
|
|
32
|
+
* throttledFetch('2'), // Starts immediately
|
|
33
|
+
* throttledFetch('3'), // Queued, starts when 1 or 2 completes
|
|
34
|
+
* throttledFetch('4'), // Queued, starts when slot available
|
|
35
|
+
* ]);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare function throttleAsyncFn<D = any, A extends any[] = any[]>(originalMethod: AsyncMethod<D, A>, parallelCalls?: number): AsyncMethod<D, A>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { throttleAsyncFn };
|
|
41
|
+
//# sourceMappingURL=throttle-async.fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async.fn.d.ts","names":[],"sources":["../../src/throttle-async/throttle-async.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqDgB,eAAA,kCAAA,CACd,cAAA,EAAgB,WAAA,CAAY,CAAA,EAAG,CAAA,GAC/B,aAAA,YACC,WAAA,CAAY,CAAA,EAAG,CAAA"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ThrottleAsyncExecutor } from "./throttle-async-executor.js";
|
|
2
|
+
//#region src/throttle-async/throttle-async.fn.ts
|
|
3
|
+
/**
|
|
4
|
+
* Wraps an async method to limit concurrent executions.
|
|
5
|
+
*
|
|
6
|
+
* @template D - The resolved type of the async method
|
|
7
|
+
* @template A - The argument types of the original method
|
|
8
|
+
* @param {AsyncMethod<D, A>} originalMethod - The async method to throttle
|
|
9
|
+
* @param {number} [parallelCalls=1] - Maximum number of concurrent calls
|
|
10
|
+
* @returns {AsyncMethod<D, A>} The throttled async method
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* class ApiClient {
|
|
15
|
+
* async fetchUser(userId: string): Promise<User> {
|
|
16
|
+
* return fetch(`/api/users/${userId}`).then(r => r.json());
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* const client = new ApiClient();
|
|
21
|
+
*
|
|
22
|
+
* // Limit to 2 concurrent requests
|
|
23
|
+
* const throttledFetch = throttleAsyncFn(
|
|
24
|
+
* client.fetchUser.bind(client),
|
|
25
|
+
* 2
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* // Execute multiple calls, only 2 run concurrently
|
|
29
|
+
* const users = await Promise.all([
|
|
30
|
+
* throttledFetch('1'), // Starts immediately
|
|
31
|
+
* throttledFetch('2'), // Starts immediately
|
|
32
|
+
* throttledFetch('3'), // Queued, starts when 1 or 2 completes
|
|
33
|
+
* throttledFetch('4'), // Queued, starts when slot available
|
|
34
|
+
* ]);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function throttleAsyncFn(originalMethod, parallelCalls = 1) {
|
|
38
|
+
const executor = new ThrottleAsyncExecutor(originalMethod, parallelCalls);
|
|
39
|
+
return function(...args) {
|
|
40
|
+
return executor.exec(this, args);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { throttleAsyncFn };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=throttle-async.fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async.fn.js","names":[],"sources":["../../src/throttle-async/throttle-async.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 { AsyncMethod } from '../types.js';\nimport { ThrottleAsyncExecutor } from './throttle-async-executor.js';\n\n/**\n * Wraps an async method to limit concurrent executions.\n *\n * @template D - The resolved type of the async method\n * @template A - The argument types of the original method\n * @param {AsyncMethod<D, A>} originalMethod - The async method to throttle\n * @param {number} [parallelCalls=1] - Maximum number of concurrent calls\n * @returns {AsyncMethod<D, A>} The throttled async method\n *\n * @example\n * ```typescript\n * class ApiClient {\n * async fetchUser(userId: string): Promise<User> {\n * return fetch(`/api/users/${userId}`).then(r => r.json());\n * }\n * }\n *\n * const client = new ApiClient();\n *\n * // Limit to 2 concurrent requests\n * const throttledFetch = throttleAsyncFn(\n * client.fetchUser.bind(client),\n * 2\n * );\n *\n * // Execute multiple calls, only 2 run concurrently\n * const users = await Promise.all([\n * throttledFetch('1'), // Starts immediately\n * throttledFetch('2'), // Starts immediately\n * throttledFetch('3'), // Queued, starts when 1 or 2 completes\n * throttledFetch('4'), // Queued, starts when slot available\n * ]);\n * ```\n */\nexport function throttleAsyncFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n parallelCalls = 1,\n): AsyncMethod<D, A> {\n const executor = new ThrottleAsyncExecutor(originalMethod, parallelCalls);\n\n return function (this: any, ...args: A): Promise<D> {\n return executor.exec(this, args);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,gBACd,gBACA,gBAAgB,GACG;CACnB,MAAM,WAAW,IAAI,sBAAsB,gBAAgB,cAAc;AAEzE,QAAO,SAAqB,GAAG,MAAqB;AAClD,SAAO,SAAS,KAAK,MAAM,KAAK"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { throttleAsyncFn } from "./throttle-async.fn.js";
|
|
2
|
+
//#region src/throttle-async/throttle-async.ts
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that limits concurrent async method calls.
|
|
5
|
+
* Excess calls are queued and executed when slots become available.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the class containing the decorated method
|
|
8
|
+
* @template D - The resolved type of the async method
|
|
9
|
+
* @param {number} [parallelCalls=1] - Maximum number of concurrent calls allowed
|
|
10
|
+
* @returns {Decorator<T>} The decorator function
|
|
11
|
+
*
|
|
12
|
+
* @throws {Error} When applied to a non-method property
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* class BatchProcessor {
|
|
17
|
+
* // Process up to 5 items concurrently
|
|
18
|
+
* @throttleAsync(5)
|
|
19
|
+
* async processItem(item: Item): Promise<Result> {
|
|
20
|
+
* return await this.performHeavyProcessing(item);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* const processor = new BatchProcessor();
|
|
25
|
+
* const items = Array.from({ length: 100 }, (_, i) => ({ id: i }));
|
|
26
|
+
*
|
|
27
|
+
* // Process 100 items, 5 at a time
|
|
28
|
+
* const results = await Promise.all(
|
|
29
|
+
* items.map(item => processor.processItem(item))
|
|
30
|
+
* );
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
function throttleAsync(parallelCalls) {
|
|
34
|
+
return (target, propertyName, descriptor) => {
|
|
35
|
+
if (descriptor.value) {
|
|
36
|
+
descriptor.value = throttleAsyncFn(descriptor.value, parallelCalls);
|
|
37
|
+
return descriptor;
|
|
38
|
+
}
|
|
39
|
+
throw new Error("@throttleAsync is applicable only on a methods.");
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { throttleAsync };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=throttle-async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle-async.js","names":[],"sources":["../../src/throttle-async/throttle-async.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\n/**\n * @fileoverview ThrottleAsync decorator - limits the number of concurrent\n * async method calls. Additional calls are queued and executed when slots\n * become available.\n *\n * @module @resq/typescript/decorators/throttle-async\n *\n * @example\n * ```typescript\n * class ApiService {\n * @throttleAsync(3) // Max 3 concurrent requests\n * async fetchData(endpoint: string): Promise<Data> {\n * return fetch(endpoint).then(r => r.json());\n * }\n * }\n *\n * const api = new ApiService();\n *\n * // These will execute 3 at a time\n * const results = await Promise.all([\n * api.fetchData('/api/data1'),\n * api.fetchData('/api/data2'),\n * api.fetchData('/api/data3'),\n * api.fetchData('/api/data4'), // Queued until a slot frees up\n * api.fetchData('/api/data5'), // Queued until a slot frees up\n * ]);\n * ```\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\nimport type { AsyncMethod, Decorator } from '../types.js';\nimport { throttleAsyncFn } from './throttle-async.fn.js';\n\n/**\n * Decorator that limits concurrent async method calls.\n * Excess calls are queued and executed when slots become available.\n *\n * @template T - The type of the class containing the decorated method\n * @template D - The resolved type of the async method\n * @param {number} [parallelCalls=1] - Maximum number of concurrent calls allowed\n * @returns {Decorator<T>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class BatchProcessor {\n * // Process up to 5 items concurrently\n * @throttleAsync(5)\n * async processItem(item: Item): Promise<Result> {\n * return await this.performHeavyProcessing(item);\n * }\n * }\n *\n * const processor = new BatchProcessor();\n * const items = Array.from({ length: 100 }, (_, i) => ({ id: i }));\n *\n * // Process 100 items, 5 at a time\n * const results = await Promise.all(\n * items.map(item => processor.processItem(item))\n * );\n * ```\n */\nexport function throttleAsync<T = any, D = any>(parallelCalls?: number): Decorator<T> {\n return (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<AsyncMethod<any>>,\n ): TypedPropertyDescriptor<AsyncMethod<D>> => {\n if (descriptor.value) {\n descriptor.value = throttleAsyncFn(descriptor.value, parallelCalls);\n\n return descriptor;\n }\n\n throw new Error('@throttleAsync is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,SAAgB,cAAgC,eAAsC;AACpF,SACE,QACA,cACA,eAC4C;AAC5C,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,gBAAgB,WAAW,OAAO,cAAc;AAEnE,UAAO;;AAGT,QAAM,IAAI,MAAM,kDAAkD"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region src/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
|
+
* A generic method type used throughout decorators.
|
|
19
|
+
*
|
|
20
|
+
* @template D - The return type of the method
|
|
21
|
+
* @template A - The argument types of the method (as an array)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const myMethod: Method<number, [string, boolean]> = (name, active) => {
|
|
26
|
+
* return active ? name.length : 0;
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
type Method<D = any, A extends any[] = any[]> = (...args: A) => D;
|
|
31
|
+
/**
|
|
32
|
+
* A generic decorator type for method decorators.
|
|
33
|
+
*
|
|
34
|
+
* @template T - The class type containing the method
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const myDecorator: Decorator<MyClass> = (target, propertyName, descriptor) => {
|
|
39
|
+
* // Decorator implementation
|
|
40
|
+
* return descriptor;
|
|
41
|
+
* };
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
type Decorator<T = any> = (target: T, propertyName: keyof T, descriptor: TypedPropertyDescriptor<Method<any>>) => TypedPropertyDescriptor<Method<any>>;
|
|
45
|
+
/**
|
|
46
|
+
* A generic async method type.
|
|
47
|
+
*
|
|
48
|
+
* @template D - The resolved type of the Promise
|
|
49
|
+
* @template A - The argument types of the method (as an array)
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const fetchData: AsyncMethod<User, [string]> = async (userId) => {
|
|
54
|
+
* return await api.getUser(userId);
|
|
55
|
+
* };
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
type AsyncMethod<D = any, A extends any[] = any[]> = (...args: A) => Promise<D>;
|
|
59
|
+
/**
|
|
60
|
+
* A decorator type specifically for async methods.
|
|
61
|
+
*
|
|
62
|
+
* @template T - The class type containing the method
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const asyncDecorator: AsyncDecorator<MyClass> = (target, propertyName, descriptor) => {
|
|
67
|
+
* const original = descriptor.value!;
|
|
68
|
+
* descriptor.value = async function(...args) {
|
|
69
|
+
* console.log('Before async call');
|
|
70
|
+
* const result = await original.apply(this, args);
|
|
71
|
+
* console.log('After async call');
|
|
72
|
+
* return result;
|
|
73
|
+
* };
|
|
74
|
+
* return descriptor;
|
|
75
|
+
* };
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
type AsyncDecorator<T = any> = (target: T, propertyName: keyof T, descriptor: TypedPropertyDescriptor<AsyncMethod<any>>) => TypedPropertyDescriptor<AsyncMethod<any>>;
|
|
79
|
+
//#endregion
|
|
80
|
+
export { AsyncDecorator, AsyncMethod, Decorator, Method };
|
|
81
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;AA8BA;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;;KAhBY,MAAA,yCAA+C,IAAA,EAAM,CAAA,KAAM,CAAA;;;;;;;AAoCvE;;;;;;;KApBY,SAAA,aACV,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,uBAAA,CAAwB,MAAA,WACjC,uBAAA,CAAwB,MAAA;;;;;;;;;AAsC7B;;;;;KAtBY,WAAA,yCAAoD,IAAA,EAAM,CAAA,KAAM,OAAA,CAAQ,CAAA;;;;;;;;;;;;;;;;;;;;KAsBxE,cAAA,aACV,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,uBAAA,CAAwB,WAAA,WACjC,uBAAA,CAAwB,WAAA"}
|
package/lib/types.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@resq-sw/decorators",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript method and class decorators for caching, rate limiting, control flow, and observability",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./lib/index.d.ts",
|
|
10
|
+
"import": "./lib/index.js",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/index.d.ts",
|
|
16
|
+
"files": ["lib", "README.md"],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsdown",
|
|
19
|
+
"test": "vitest run"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsdown": "^0.21.7",
|
|
23
|
+
"typescript": "5.9.3",
|
|
24
|
+
"vitest": "3.2.4"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"provenance": true,
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/resq-software/npm.git",
|
|
34
|
+
"directory": "packages/decorators"
|
|
35
|
+
},
|
|
36
|
+
"keywords": ["decorators", "memoize", "throttle", "debounce", "rate-limit", "typescript"],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20.19.0"
|
|
39
|
+
}
|
|
40
|
+
}
|