@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 @@
|
|
|
1
|
+
{"version":3,"file":"bind.types.d.ts","names":[],"sources":["../../src/bind/bind.types.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;UA0BiB,UAAA;;;;;EAKf,IAAA;AAAA"}
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Decorator } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/debounce/debounce.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that debounces method calls, ensuring the method only executes
|
|
6
|
+
* after the specified delay has passed since the last call.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the class containing the decorated method
|
|
9
|
+
* @param {number} delayMs - The debounce delay in milliseconds
|
|
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 AutoSave {
|
|
17
|
+
* @debounce(1000)
|
|
18
|
+
* saveDraft(content: string) {
|
|
19
|
+
* // Saves only 1 second after user stops typing
|
|
20
|
+
* localStorage.setItem('draft', content);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* // Usage
|
|
25
|
+
* const autoSave = new AutoSave();
|
|
26
|
+
* autoSave.saveDraft('Hello'); // Won't save yet
|
|
27
|
+
* autoSave.saveDraft('Hello World'); // Resets timer
|
|
28
|
+
* // After 1 second of inactivity, saveDraft executes once
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function debounce<T = unknown>(delayMs: number): Decorator<T>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { debounce };
|
|
34
|
+
//# sourceMappingURL=debounce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.d.ts","names":[],"sources":["../../src/debounce/debounce.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8CgB,QAAA,aAAA,CAAsB,OAAA,WAAkB,SAAA,CAAU,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Method } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/debounce/debounce.fn.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a method to debounce its execution.
|
|
6
|
+
* The method will only execute after the specified delay has passed
|
|
7
|
+
* since the last time it was called.
|
|
8
|
+
*
|
|
9
|
+
* @template D - The return type of the original method
|
|
10
|
+
* @template A - The argument types of the original method
|
|
11
|
+
* @param {Method<D, A>} originalMethod - The method to debounce
|
|
12
|
+
* @param {number} delayMs - The debounce delay in milliseconds
|
|
13
|
+
* @returns {Method<void, A>} The debounced method
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* class SearchService {
|
|
18
|
+
* search(query: string): void {
|
|
19
|
+
* console.log(`Searching for: ${query}`);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* const service = new SearchService();
|
|
24
|
+
* const debouncedSearch = debounceFn(
|
|
25
|
+
* service.search.bind(service),
|
|
26
|
+
* 300
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* // Rapid calls
|
|
30
|
+
* debouncedSearch('a');
|
|
31
|
+
* debouncedSearch('ab');
|
|
32
|
+
* debouncedSearch('abc');
|
|
33
|
+
*
|
|
34
|
+
* // Only "Searching for: abc" is logged after 300ms
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function debounceFn<D = unknown, A extends unknown[] = unknown[]>(originalMethod: Method<D, A>, delayMs: number): Method<void, A>;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { debounceFn };
|
|
40
|
+
//# sourceMappingURL=debounce.fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.fn.d.ts","names":[],"sources":["../../src/debounce/debounce.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmDgB,UAAA,8CAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,OAAA,WACC,MAAA,OAAa,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/debounce/debounce.fn.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a method to debounce its execution.
|
|
4
|
+
* The method will only execute after the specified delay has passed
|
|
5
|
+
* since the last time it was called.
|
|
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 debounce
|
|
10
|
+
* @param {number} delayMs - The debounce delay in milliseconds
|
|
11
|
+
* @returns {Method<void, A>} The debounced method
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class SearchService {
|
|
16
|
+
* search(query: string): void {
|
|
17
|
+
* console.log(`Searching for: ${query}`);
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* const service = new SearchService();
|
|
22
|
+
* const debouncedSearch = debounceFn(
|
|
23
|
+
* service.search.bind(service),
|
|
24
|
+
* 300
|
|
25
|
+
* );
|
|
26
|
+
*
|
|
27
|
+
* // Rapid calls
|
|
28
|
+
* debouncedSearch('a');
|
|
29
|
+
* debouncedSearch('ab');
|
|
30
|
+
* debouncedSearch('abc');
|
|
31
|
+
*
|
|
32
|
+
* // Only "Searching for: abc" is logged after 300ms
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function debounceFn(originalMethod, delayMs) {
|
|
36
|
+
let handler;
|
|
37
|
+
return function(...args) {
|
|
38
|
+
clearTimeout(handler);
|
|
39
|
+
handler = setTimeout(() => {
|
|
40
|
+
originalMethod.apply(this, args);
|
|
41
|
+
}, delayMs);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { debounceFn };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=debounce.fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.fn.js","names":[],"sources":["../../src/debounce/debounce.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 debounce its execution.\n * The method will only execute after the specified delay has passed\n * since the last time it was called.\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 debounce\n * @param {number} delayMs - The debounce delay in milliseconds\n * @returns {Method<void, A>} The debounced method\n *\n * @example\n * ```typescript\n * class SearchService {\n * search(query: string): void {\n * console.log(`Searching for: ${query}`);\n * }\n * }\n *\n * const service = new SearchService();\n * const debouncedSearch = debounceFn(\n * service.search.bind(service),\n * 300\n * );\n *\n * // Rapid calls\n * debouncedSearch('a');\n * debouncedSearch('ab');\n * debouncedSearch('abc');\n *\n * // Only \"Searching for: abc\" is logged after 300ms\n * ```\n */\nexport function debounceFn<D = unknown, A extends unknown[] = unknown[]>(\n originalMethod: Method<D, A>,\n delayMs: number,\n): Method<void, A> {\n let handler: ReturnType<typeof setTimeout> | undefined;\n\n return function (this: unknown, ...args: A): void {\n clearTimeout(handler);\n\n handler = setTimeout(() => {\n originalMethod.apply(this, args);\n }, delayMs);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,SAAgB,WACd,gBACA,SACiB;CACjB,IAAI;AAEJ,QAAO,SAAyB,GAAG,MAAe;AAChD,eAAa,QAAQ;AAErB,YAAU,iBAAiB;AACzB,kBAAe,MAAM,MAAM,KAAK;KAC/B,QAAQ"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { debounceFn } from "./debounce.fn.js";
|
|
2
|
+
//#region src/debounce/debounce.ts
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that debounces method calls, ensuring the method only executes
|
|
5
|
+
* after the specified delay has passed since the last call.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the class containing the decorated method
|
|
8
|
+
* @param {number} delayMs - The debounce delay 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 AutoSave {
|
|
16
|
+
* @debounce(1000)
|
|
17
|
+
* saveDraft(content: string) {
|
|
18
|
+
* // Saves only 1 second after user stops typing
|
|
19
|
+
* localStorage.setItem('draft', content);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Usage
|
|
24
|
+
* const autoSave = new AutoSave();
|
|
25
|
+
* autoSave.saveDraft('Hello'); // Won't save yet
|
|
26
|
+
* autoSave.saveDraft('Hello World'); // Resets timer
|
|
27
|
+
* // After 1 second of inactivity, saveDraft executes once
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function debounce(delayMs) {
|
|
31
|
+
return (_target, _propertyName, descriptor) => {
|
|
32
|
+
if (descriptor.value) {
|
|
33
|
+
const methodsMap = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
const originalMethod = descriptor.value;
|
|
35
|
+
descriptor.value = function(...args) {
|
|
36
|
+
if (!methodsMap.has(this)) methodsMap.set(this, debounceFn(originalMethod, delayMs).bind(this));
|
|
37
|
+
const method = methodsMap.get(this);
|
|
38
|
+
if (method) method(...args);
|
|
39
|
+
};
|
|
40
|
+
return descriptor;
|
|
41
|
+
}
|
|
42
|
+
throw new Error("@debounce is applicable only on a methods.");
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { debounce };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.js","names":[],"sources":["../../src/debounce/debounce.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 { debounceFn } from './debounce.fn.js';\n\n/**\n * Decorator that debounces method calls, ensuring the method only executes\n * after the specified delay has passed since the last call.\n *\n * @template T - The type of the class containing the decorated method\n * @param {number} delayMs - The debounce delay 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 AutoSave {\n * @debounce(1000)\n * saveDraft(content: string) {\n * // Saves only 1 second after user stops typing\n * localStorage.setItem('draft', content);\n * }\n * }\n *\n * // Usage\n * const autoSave = new AutoSave();\n * autoSave.saveDraft('Hello'); // Won't save yet\n * autoSave.saveDraft('Hello World'); // Resets timer\n * // After 1 second of inactivity, saveDraft executes once\n * ```\n */\nexport function debounce<T = unknown>(delayMs: number): Decorator<T> {\n return (\n _target: T,\n _propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<Method<unknown>>,\n ): TypedPropertyDescriptor<Method<unknown>> => {\n if (descriptor.value) {\n const methodsMap = new WeakMap<object, Method<unknown>>();\n const originalMethod = descriptor.value;\n\n descriptor.value = function (this: object, ...args: unknown[]) {\n if (!methodsMap.has(this)) {\n methodsMap.set(this, debounceFn(originalMethod, delayMs).bind(this));\n }\n\n const method = methodsMap.get(this);\n if (method) {\n method(...args);\n }\n };\n\n return descriptor;\n }\n\n throw new Error('@debounce is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,SAAsB,SAA+B;AACnE,SACE,SACA,eACA,eAC6C;AAC7C,MAAI,WAAW,OAAO;GACpB,MAAM,6BAAa,IAAI,SAAkC;GACzD,MAAM,iBAAiB,WAAW;AAElC,cAAW,QAAQ,SAAwB,GAAG,MAAiB;AAC7D,QAAI,CAAC,WAAW,IAAI,KAAK,CACvB,YAAW,IAAI,MAAM,WAAW,gBAAgB,QAAQ,CAAC,KAAK,KAAK,CAAC;IAGtE,MAAM,SAAS,WAAW,IAAI,KAAK;AACnC,QAAI,OACF,QAAO,GAAG,KAAK;;AAInB,UAAO;;AAGT,QAAM,IAAI,MAAM,6CAA6C"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Decorator } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/delay/delay.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that delays the execution of a method by the specified time.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of the class containing the decorated method
|
|
8
|
+
* @param {number} delayMs - The delay time 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 AnimationController {
|
|
16
|
+
* @delay(500)
|
|
17
|
+
* fadeIn(element: HTMLElement) {
|
|
18
|
+
* element.style.opacity = '1';
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @delay(1000)
|
|
22
|
+
* fadeOut(element: HTMLElement) {
|
|
23
|
+
* element.style.opacity = '0';
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* const controller = new AnimationController();
|
|
28
|
+
* controller.fadeIn(element); // Fades in after 500ms
|
|
29
|
+
* controller.fadeOut(element); // Fades out after 1000ms
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function delay<T = unknown>(delayMs: number): Decorator<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { delay };
|
|
35
|
+
//# sourceMappingURL=delay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.d.ts","names":[],"sources":["../../src/delay/delay.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+CgB,KAAA,aAAA,CAAmB,OAAA,WAAkB,SAAA,CAAU,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Method } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/delay/delay.fn.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a method to delay its execution by the specified time.
|
|
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 delay
|
|
10
|
+
* @param {number} delayMs - The delay time in milliseconds
|
|
11
|
+
* @returns {Method<void, A>} The delayed method
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class MessageService {
|
|
16
|
+
* send(message: string): void {
|
|
17
|
+
* console.log(`Sending: ${message}`);
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* const service = new MessageService();
|
|
22
|
+
* const delayedSend = delayFn(
|
|
23
|
+
* service.send.bind(service),
|
|
24
|
+
* 2000
|
|
25
|
+
* );
|
|
26
|
+
*
|
|
27
|
+
* delayedSend('Hello'); // "Sending: Hello" appears after 2 seconds
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function delayFn<D = unknown, A extends unknown[] = unknown[]>(originalMethod: Method<D, A>, delayMs: number): Method<void, A>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { delayFn };
|
|
33
|
+
//# sourceMappingURL=delay.fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.fn.d.ts","names":[],"sources":["../../src/delay/delay.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4CgB,OAAA,8CAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,OAAA,WACC,MAAA,OAAa,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/delay/delay.fn.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a method to delay its execution by the specified time.
|
|
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 delay
|
|
8
|
+
* @param {number} delayMs - The delay time in milliseconds
|
|
9
|
+
* @returns {Method<void, A>} The delayed method
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* class MessageService {
|
|
14
|
+
* send(message: string): void {
|
|
15
|
+
* console.log(`Sending: ${message}`);
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* const service = new MessageService();
|
|
20
|
+
* const delayedSend = delayFn(
|
|
21
|
+
* service.send.bind(service),
|
|
22
|
+
* 2000
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* delayedSend('Hello'); // "Sending: Hello" appears after 2 seconds
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function delayFn(originalMethod, delayMs) {
|
|
29
|
+
return function(...args) {
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
originalMethod.apply(this, args);
|
|
32
|
+
}, delayMs);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { delayFn };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=delay.fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.fn.js","names":[],"sources":["../../src/delay/delay.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 delay its execution by the specified time.\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 delay\n * @param {number} delayMs - The delay time in milliseconds\n * @returns {Method<void, A>} The delayed method\n *\n * @example\n * ```typescript\n * class MessageService {\n * send(message: string): void {\n * console.log(`Sending: ${message}`);\n * }\n * }\n *\n * const service = new MessageService();\n * const delayedSend = delayFn(\n * service.send.bind(service),\n * 2000\n * );\n *\n * delayedSend('Hello'); // \"Sending: Hello\" appears after 2 seconds\n * ```\n */\nexport function delayFn<D = unknown, A extends unknown[] = unknown[]>(\n originalMethod: Method<D, A>,\n delayMs: number,\n): Method<void, A> {\n return function (this: unknown, ...args: A): void {\n setTimeout(() => {\n originalMethod.apply(this, args);\n }, delayMs);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAgB,QACd,gBACA,SACiB;AACjB,QAAO,SAAyB,GAAG,MAAe;AAChD,mBAAiB;AACf,kBAAe,MAAM,MAAM,KAAK;KAC/B,QAAQ"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { delayFn } from "./delay.fn.js";
|
|
2
|
+
//#region src/delay/delay.ts
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that delays the execution of a method by the specified time.
|
|
5
|
+
*
|
|
6
|
+
* @template T - The type of the class containing the decorated method
|
|
7
|
+
* @param {number} delayMs - The delay time 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 AnimationController {
|
|
15
|
+
* @delay(500)
|
|
16
|
+
* fadeIn(element: HTMLElement) {
|
|
17
|
+
* element.style.opacity = '1';
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* @delay(1000)
|
|
21
|
+
* fadeOut(element: HTMLElement) {
|
|
22
|
+
* element.style.opacity = '0';
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* const controller = new AnimationController();
|
|
27
|
+
* controller.fadeIn(element); // Fades in after 500ms
|
|
28
|
+
* controller.fadeOut(element); // Fades out after 1000ms
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function delay(delayMs) {
|
|
32
|
+
return (_target, _propertyName, descriptor) => {
|
|
33
|
+
if (descriptor.value) {
|
|
34
|
+
descriptor.value = delayFn(descriptor.value, delayMs);
|
|
35
|
+
return descriptor;
|
|
36
|
+
}
|
|
37
|
+
throw new Error("@delay is applicable only on a methods.");
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { delay };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=delay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.js","names":[],"sources":["../../src/delay/delay.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 { delayFn } from './delay.fn.js';\n\n/**\n * Decorator that delays the execution of a method by the specified time.\n *\n * @template T - The type of the class containing the decorated method\n * @param {number} delayMs - The delay time 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 AnimationController {\n * @delay(500)\n * fadeIn(element: HTMLElement) {\n * element.style.opacity = '1';\n * }\n *\n * @delay(1000)\n * fadeOut(element: HTMLElement) {\n * element.style.opacity = '0';\n * }\n * }\n *\n * const controller = new AnimationController();\n * controller.fadeIn(element); // Fades in after 500ms\n * controller.fadeOut(element); // Fades out after 1000ms\n * ```\n */\nexport function delay<T = unknown>(delayMs: number): Decorator<T> {\n return (\n _target: T,\n _propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<Method<unknown>>,\n ): TypedPropertyDescriptor<Method<unknown>> => {\n if (descriptor.value) {\n descriptor.value = delayFn(descriptor.value, delayMs);\n\n return descriptor;\n }\n throw new Error('@delay is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,MAAmB,SAA+B;AAChE,SACE,SACA,eACA,eAC6C;AAC7C,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,QAAQ,WAAW,OAAO,QAAQ;AAErD,UAAO;;AAET,QAAM,IAAI,MAAM,0CAA0C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Delegatable } from "./delegate.types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/delegate/delegate.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Decorator that deduplicates concurrent async method calls.
|
|
6
|
+
* Multiple calls with the same arguments will share the same promise
|
|
7
|
+
* until the first one resolves or rejects.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The type of the class containing the decorated method
|
|
10
|
+
* @template D - The return type of the decorated method (wrapped in Promise)
|
|
11
|
+
* @param {(...args: any[]) => string} [keyResolver] - Optional function to generate cache keys from arguments
|
|
12
|
+
* @returns {Delegatable<T, D>} The decorator function
|
|
13
|
+
*
|
|
14
|
+
* @throws {Error} When applied to a non-method property
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* class ApiService {
|
|
19
|
+
* // Basic usage - uses JSON.stringify(args) as key
|
|
20
|
+
* @delegate()
|
|
21
|
+
* async fetchData(id: string): Promise<Data> {
|
|
22
|
+
* return this.http.get(`/data/${id}`);
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* // Custom key resolver for complex arguments
|
|
26
|
+
* @delegate((userId, options) => `${userId}-${options.cacheKey}`)
|
|
27
|
+
* async getUser(userId: string, options: { cacheKey: string }): Promise<User> {
|
|
28
|
+
* return this.http.get(`/users/${userId}`);
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // Usage - concurrent calls with same args share the promise
|
|
33
|
+
* const api = new ApiService();
|
|
34
|
+
*
|
|
35
|
+
* // These share the same underlying promise
|
|
36
|
+
* const [user1, user2] = await Promise.all([
|
|
37
|
+
* api.getUser('123', { cacheKey: 'v1' }),
|
|
38
|
+
* api.getUser('123', { cacheKey: 'v1' }) // Same key, returns cached promise
|
|
39
|
+
* ]);
|
|
40
|
+
*
|
|
41
|
+
* // This creates a new promise (different cache key)
|
|
42
|
+
* const user3 = await api.getUser('123', { cacheKey: 'v2' });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function delegate<T = any, D = any>(keyResolver?: (...args: unknown[]) => string): Delegatable<T, D>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { delegate };
|
|
48
|
+
//# sourceMappingURL=delegate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate.d.ts","names":[],"sources":["../../src/delegate/delegate.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyFgB,QAAA,kBAAA,CACd,WAAA,OAAkB,IAAA,yBACjB,WAAA,CAAY,CAAA,EAAG,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { AsyncMethod } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/delegate/delegate.fn.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Delegate function implementation - wraps an async method to
|
|
6
|
+
* deduplicate concurrent calls with the same arguments.
|
|
7
|
+
*
|
|
8
|
+
* @module @resq/typescript/decorators/delegate.fn
|
|
9
|
+
*
|
|
10
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
11
|
+
* @license MIT
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @fileoverview Delegate function implementation - wraps an async method to
|
|
15
|
+
* deduplicate concurrent calls with the same arguments.
|
|
16
|
+
*
|
|
17
|
+
* @module @resq/typescript/decorators/delegate.fn
|
|
18
|
+
*
|
|
19
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
20
|
+
* @license MIT
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Wraps an async method to deduplicate concurrent calls.
|
|
24
|
+
* Multiple calls with the same key will share the same promise
|
|
25
|
+
* until the first one completes.
|
|
26
|
+
*
|
|
27
|
+
* @template D - The resolved type of the promise
|
|
28
|
+
* @template A - The argument types of the original method
|
|
29
|
+
* @param {AsyncMethod<D, A>} originalMethod - The async method to wrap
|
|
30
|
+
* @param {(...args: A) => string} [keyResolver] - Optional function to generate cache keys
|
|
31
|
+
* @returns {AsyncMethod<D, A>} The delegated method
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* class Service {
|
|
36
|
+
* async fetchData(id: string): Promise<Data> {
|
|
37
|
+
* console.log(`Fetching ${id}`);
|
|
38
|
+
* return fetch(`/api/data/${id}`).then(r => r.json());
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* const service = new Service();
|
|
43
|
+
* const delegated = delegateFn(
|
|
44
|
+
* service.fetchData.bind(service),
|
|
45
|
+
* (id) => id // Use id directly as key
|
|
46
|
+
* );
|
|
47
|
+
*
|
|
48
|
+
* // These share the same promise - "Fetching 123" is logged only once
|
|
49
|
+
* const p1 = delegated('123');
|
|
50
|
+
* const p2 = delegated('123');
|
|
51
|
+
* const [d1, d2] = await Promise.all([p1, p2]);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function delegateFn<D = any, A extends any[] = any[]>(originalMethod: AsyncMethod<D, A>, keyResolver?: (...args: A) => string): AsyncMethod<D, A>;
|
|
55
|
+
//#endregion
|
|
56
|
+
export { delegateFn };
|
|
57
|
+
//# sourceMappingURL=delegate.fn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate.fn.d.ts","names":[],"sources":["../../src/delegate/delegate.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4EgB,UAAA,kCAAA,CACd,cAAA,EAAgB,WAAA,CAAY,CAAA,EAAG,CAAA,GAC/B,WAAA,OAAkB,IAAA,EAAM,CAAA,cACvB,WAAA,CAAY,CAAA,EAAG,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/delegate/delegate.fn.ts
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Delegate function implementation - wraps an async method to
|
|
4
|
+
* deduplicate concurrent calls with the same arguments.
|
|
5
|
+
*
|
|
6
|
+
* @module @resq/typescript/decorators/delegate.fn
|
|
7
|
+
*
|
|
8
|
+
* @copyright Copyright (c) 2026 ResQ
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Wraps an async method to deduplicate concurrent calls.
|
|
13
|
+
* Multiple calls with the same key will share the same promise
|
|
14
|
+
* until the first one completes.
|
|
15
|
+
*
|
|
16
|
+
* @template D - The resolved type of the promise
|
|
17
|
+
* @template A - The argument types of the original method
|
|
18
|
+
* @param {AsyncMethod<D, A>} originalMethod - The async method to wrap
|
|
19
|
+
* @param {(...args: A) => string} [keyResolver] - Optional function to generate cache keys
|
|
20
|
+
* @returns {AsyncMethod<D, A>} The delegated method
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* class Service {
|
|
25
|
+
* async fetchData(id: string): Promise<Data> {
|
|
26
|
+
* console.log(`Fetching ${id}`);
|
|
27
|
+
* return fetch(`/api/data/${id}`).then(r => r.json());
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* const service = new Service();
|
|
32
|
+
* const delegated = delegateFn(
|
|
33
|
+
* service.fetchData.bind(service),
|
|
34
|
+
* (id) => id // Use id directly as key
|
|
35
|
+
* );
|
|
36
|
+
*
|
|
37
|
+
* // These share the same promise - "Fetching 123" is logged only once
|
|
38
|
+
* const p1 = delegated('123');
|
|
39
|
+
* const p2 = delegated('123');
|
|
40
|
+
* const [d1, d2] = await Promise.all([p1, p2]);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
function delegateFn(originalMethod, keyResolver) {
|
|
44
|
+
const delegatedKeysMap = /* @__PURE__ */ new Map();
|
|
45
|
+
const keyGenerator = keyResolver ?? ((...args) => JSON.stringify(args));
|
|
46
|
+
return function(...args) {
|
|
47
|
+
const key = keyGenerator(...args);
|
|
48
|
+
if (!delegatedKeysMap.has(key)) delegatedKeysMap.set(key, originalMethod.apply(this, args).finally(() => delegatedKeysMap.delete(key)));
|
|
49
|
+
return delegatedKeysMap.get(key);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { delegateFn };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=delegate.fn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate.fn.js","names":[],"sources":["../../src/delegate/delegate.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';\n\n/*\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 Delegate function implementation - wraps an async method to\n * deduplicate concurrent calls with the same arguments.\n *\n * @module @resq/typescript/decorators/delegate.fn\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\n/**\n * Wraps an async method to deduplicate concurrent calls.\n * Multiple calls with the same key will share the same promise\n * until the first one completes.\n *\n * @template D - The resolved type of the promise\n * @template A - The argument types of the original method\n * @param {AsyncMethod<D, A>} originalMethod - The async method to wrap\n * @param {(...args: A) => string} [keyResolver] - Optional function to generate cache keys\n * @returns {AsyncMethod<D, A>} The delegated method\n *\n * @example\n * ```typescript\n * class Service {\n * async fetchData(id: string): Promise<Data> {\n * console.log(`Fetching ${id}`);\n * return fetch(`/api/data/${id}`).then(r => r.json());\n * }\n * }\n *\n * const service = new Service();\n * const delegated = delegateFn(\n * service.fetchData.bind(service),\n * (id) => id // Use id directly as key\n * );\n *\n * // These share the same promise - \"Fetching 123\" is logged only once\n * const p1 = delegated('123');\n * const p2 = delegated('123');\n * const [d1, d2] = await Promise.all([p1, p2]);\n * ```\n */\nexport function delegateFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n keyResolver?: (...args: A) => string,\n): AsyncMethod<D, A> {\n const delegatedKeysMap = new Map<string, Promise<D>>();\n const keyGenerator: (...args: unknown[]) => string =\n keyResolver ?? ((...args) => JSON.stringify(args));\n\n return function (this: any, ...args: A): Promise<D> {\n const key = keyGenerator(...args);\n\n if (!delegatedKeysMap.has(key)) {\n delegatedKeysMap.set(\n key,\n originalMethod.apply(this, args).finally(() => delegatedKeysMap.delete(key)),\n );\n }\n\n return delegatedKeysMap.get(key) as Promise<D>;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgB,WACd,gBACA,aACmB;CACnB,MAAM,mCAAmB,IAAI,KAAyB;CACtD,MAAM,eACJ,iBAAiB,GAAG,SAAS,KAAK,UAAU,KAAK;AAEnD,QAAO,SAAqB,GAAG,MAAqB;EAClD,MAAM,MAAM,aAAa,GAAG,KAAK;AAEjC,MAAI,CAAC,iBAAiB,IAAI,IAAI,CAC5B,kBAAiB,IACf,KACA,eAAe,MAAM,MAAM,KAAK,CAAC,cAAc,iBAAiB,OAAO,IAAI,CAAC,CAC7E;AAGH,SAAO,iBAAiB,IAAI,IAAI"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { delegateFn } from "./delegate.fn.js";
|
|
2
|
+
//#region src/delegate/delegate.ts
|
|
3
|
+
/**
|
|
4
|
+
* Decorator that deduplicates concurrent async method calls.
|
|
5
|
+
* Multiple calls with the same arguments will share the same promise
|
|
6
|
+
* until the first one resolves or rejects.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the class containing the decorated method
|
|
9
|
+
* @template D - The return type of the decorated method (wrapped in Promise)
|
|
10
|
+
* @param {(...args: any[]) => string} [keyResolver] - Optional function to generate cache keys from arguments
|
|
11
|
+
* @returns {Delegatable<T, D>} The decorator function
|
|
12
|
+
*
|
|
13
|
+
* @throws {Error} When applied to a non-method property
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* class ApiService {
|
|
18
|
+
* // Basic usage - uses JSON.stringify(args) as key
|
|
19
|
+
* @delegate()
|
|
20
|
+
* async fetchData(id: string): Promise<Data> {
|
|
21
|
+
* return this.http.get(`/data/${id}`);
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* // Custom key resolver for complex arguments
|
|
25
|
+
* @delegate((userId, options) => `${userId}-${options.cacheKey}`)
|
|
26
|
+
* async getUser(userId: string, options: { cacheKey: string }): Promise<User> {
|
|
27
|
+
* return this.http.get(`/users/${userId}`);
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* // Usage - concurrent calls with same args share the promise
|
|
32
|
+
* const api = new ApiService();
|
|
33
|
+
*
|
|
34
|
+
* // These share the same underlying promise
|
|
35
|
+
* const [user1, user2] = await Promise.all([
|
|
36
|
+
* api.getUser('123', { cacheKey: 'v1' }),
|
|
37
|
+
* api.getUser('123', { cacheKey: 'v1' }) // Same key, returns cached promise
|
|
38
|
+
* ]);
|
|
39
|
+
*
|
|
40
|
+
* // This creates a new promise (different cache key)
|
|
41
|
+
* const user3 = await api.getUser('123', { cacheKey: 'v2' });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function delegate(keyResolver) {
|
|
45
|
+
return (target, propertyName, descriptor) => {
|
|
46
|
+
if (descriptor.value) {
|
|
47
|
+
descriptor.value = delegateFn(descriptor.value, keyResolver);
|
|
48
|
+
return descriptor;
|
|
49
|
+
}
|
|
50
|
+
throw new Error("@delegate is applicable only on a methods.");
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { delegate };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=delegate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegate.js","names":[],"sources":["../../src/delegate/delegate.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 Delegate decorator - deduplicates concurrent async method calls\n * with the same arguments. If a method is called while another identical call\n * is in progress, it returns the same promise instead of making a new call.\n *\n * @module @resq/typescript/decorators/delegate\n *\n * @example\n * ```typescript\n * class DataService {\n * @delegate()\n * async fetchUser(userId: string): Promise<User> {\n * return fetch(`/api/users/${userId}`).then(r => r.json());\n * }\n * }\n *\n * const service = new DataService();\n *\n * // These two calls will share the same promise\n * const promise1 = service.fetchUser('123');\n * const promise2 = service.fetchUser('123');\n * console.log(promise1 === promise2); // true\n * ```\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\nimport type { AsyncMethod } from '../types.js';\nimport { delegateFn } from './delegate.fn.js';\nimport type { Delegatable } from './delegate.types.js';\n\n/**\n * Decorator that deduplicates concurrent async method calls.\n * Multiple calls with the same arguments will share the same promise\n * until the first one resolves or rejects.\n *\n * @template T - The type of the class containing the decorated method\n * @template D - The return type of the decorated method (wrapped in Promise)\n * @param {(...args: any[]) => string} [keyResolver] - Optional function to generate cache keys from arguments\n * @returns {Delegatable<T, D>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class ApiService {\n * // Basic usage - uses JSON.stringify(args) as key\n * @delegate()\n * async fetchData(id: string): Promise<Data> {\n * return this.http.get(`/data/${id}`);\n * }\n *\n * // Custom key resolver for complex arguments\n * @delegate((userId, options) => `${userId}-${options.cacheKey}`)\n * async getUser(userId: string, options: { cacheKey: string }): Promise<User> {\n * return this.http.get(`/users/${userId}`);\n * }\n * }\n *\n * // Usage - concurrent calls with same args share the promise\n * const api = new ApiService();\n *\n * // These share the same underlying promise\n * const [user1, user2] = await Promise.all([\n * api.getUser('123', { cacheKey: 'v1' }),\n * api.getUser('123', { cacheKey: 'v1' }) // Same key, returns cached promise\n * ]);\n *\n * // This creates a new promise (different cache key)\n * const user3 = await api.getUser('123', { cacheKey: 'v2' });\n * ```\n */\nexport function delegate<T = any, D = any>(\n keyResolver?: (...args: unknown[]) => string,\n): Delegatable<T, D> {\n return (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<AsyncMethod<D>>,\n ): TypedPropertyDescriptor<AsyncMethod<any>> => {\n if (descriptor.value) {\n descriptor.value = delegateFn(descriptor.value, keyResolver);\n\n return descriptor;\n }\n\n throw new Error('@delegate is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA,SAAgB,SACd,aACmB;AACnB,SACE,QACA,cACA,eAC8C;AAC9C,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,WAAW,WAAW,OAAO,YAAY;AAE5D,UAAO;;AAGT,QAAM,IAAI,MAAM,6CAA6C"}
|