@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.
Files changed (187) hide show
  1. package/README.md +277 -0
  2. package/lib/_utils.d.ts +46 -0
  3. package/lib/_utils.d.ts.map +1 -0
  4. package/lib/_utils.js +91 -0
  5. package/lib/_utils.js.map +1 -0
  6. package/lib/after/after.d.ts +60 -0
  7. package/lib/after/after.d.ts.map +1 -0
  8. package/lib/after/after.fn.d.ts +39 -0
  9. package/lib/after/after.fn.d.ts.map +1 -0
  10. package/lib/after/after.fn.js +59 -0
  11. package/lib/after/after.fn.js.map +1 -0
  12. package/lib/after/after.js +41 -0
  13. package/lib/after/after.js.map +1 -0
  14. package/lib/after/after.types.d.ts +86 -0
  15. package/lib/after/after.types.d.ts.map +1 -0
  16. package/lib/after/after.types.js +0 -0
  17. package/lib/after/index.d.ts +3 -0
  18. package/lib/after/index.js +2 -0
  19. package/lib/before/before.d.ts +61 -0
  20. package/lib/before/before.d.ts.map +1 -0
  21. package/lib/before/before.fn.d.ts +39 -0
  22. package/lib/before/before.fn.d.ts.map +1 -0
  23. package/lib/before/before.fn.js +51 -0
  24. package/lib/before/before.fn.js.map +1 -0
  25. package/lib/before/before.js +40 -0
  26. package/lib/before/before.js.map +1 -0
  27. package/lib/before/before.types.d.ts +48 -0
  28. package/lib/before/before.types.d.ts.map +1 -0
  29. package/lib/before/before.types.js +0 -0
  30. package/lib/before/index.d.ts +3 -0
  31. package/lib/before/index.js +2 -0
  32. package/lib/bind/bind.d.ts +75 -0
  33. package/lib/bind/bind.d.ts.map +1 -0
  34. package/lib/bind/bind.fn.d.ts +46 -0
  35. package/lib/bind/bind.fn.d.ts.map +1 -0
  36. package/lib/bind/bind.fn.js +39 -0
  37. package/lib/bind/bind.fn.js.map +1 -0
  38. package/lib/bind/bind.js +64 -0
  39. package/lib/bind/bind.js.map +1 -0
  40. package/lib/bind/bind.types.d.ts +36 -0
  41. package/lib/bind/bind.types.d.ts.map +1 -0
  42. package/lib/bind/bind.types.js +0 -0
  43. package/lib/bind/index.d.ts +3 -0
  44. package/lib/bind/index.js +2 -0
  45. package/lib/debounce/debounce.d.ts +34 -0
  46. package/lib/debounce/debounce.d.ts.map +1 -0
  47. package/lib/debounce/debounce.fn.d.ts +40 -0
  48. package/lib/debounce/debounce.fn.d.ts.map +1 -0
  49. package/lib/debounce/debounce.fn.js +47 -0
  50. package/lib/debounce/debounce.fn.js.map +1 -0
  51. package/lib/debounce/debounce.js +48 -0
  52. package/lib/debounce/debounce.js.map +1 -0
  53. package/lib/debounce/index.d.ts +2 -0
  54. package/lib/debounce/index.js +2 -0
  55. package/lib/delay/delay.d.ts +35 -0
  56. package/lib/delay/delay.d.ts.map +1 -0
  57. package/lib/delay/delay.fn.d.ts +33 -0
  58. package/lib/delay/delay.fn.d.ts.map +1 -0
  59. package/lib/delay/delay.fn.js +38 -0
  60. package/lib/delay/delay.fn.js.map +1 -0
  61. package/lib/delay/delay.js +43 -0
  62. package/lib/delay/delay.js.map +1 -0
  63. package/lib/delay/index.d.ts +2 -0
  64. package/lib/delay/index.js +2 -0
  65. package/lib/delegate/delegate.d.ts +48 -0
  66. package/lib/delegate/delegate.d.ts.map +1 -0
  67. package/lib/delegate/delegate.fn.d.ts +57 -0
  68. package/lib/delegate/delegate.fn.d.ts.map +1 -0
  69. package/lib/delegate/delegate.fn.js +55 -0
  70. package/lib/delegate/delegate.fn.js.map +1 -0
  71. package/lib/delegate/delegate.js +56 -0
  72. package/lib/delegate/delegate.js.map +1 -0
  73. package/lib/delegate/delegate.types.d.ts +45 -0
  74. package/lib/delegate/delegate.types.d.ts.map +1 -0
  75. package/lib/delegate/delegate.types.js +0 -0
  76. package/lib/delegate/index.d.ts +3 -0
  77. package/lib/delegate/index.js +2 -0
  78. package/lib/exec-time/exec-time.d.ts +42 -0
  79. package/lib/exec-time/exec-time.d.ts.map +1 -0
  80. package/lib/exec-time/exec-time.fn.d.ts +50 -0
  81. package/lib/exec-time/exec-time.fn.d.ts.map +1 -0
  82. package/lib/exec-time/exec-time.fn.js +91 -0
  83. package/lib/exec-time/exec-time.fn.js.map +1 -0
  84. package/lib/exec-time/exec-time.js +55 -0
  85. package/lib/exec-time/exec-time.js.map +1 -0
  86. package/lib/exec-time/exec-time.types.d.ts +70 -0
  87. package/lib/exec-time/exec-time.types.d.ts.map +1 -0
  88. package/lib/exec-time/exec-time.types.js +0 -0
  89. package/lib/exec-time/index.d.ts +4 -0
  90. package/lib/exec-time/index.js +3 -0
  91. package/lib/execute/execute.d.ts +78 -0
  92. package/lib/execute/execute.d.ts.map +1 -0
  93. package/lib/execute/execute.js +82 -0
  94. package/lib/execute/execute.js.map +1 -0
  95. package/lib/execute/index.d.ts +2 -0
  96. package/lib/execute/index.js +2 -0
  97. package/lib/index.d.ts +30 -0
  98. package/lib/index.js +19 -0
  99. package/lib/memoize/index.d.ts +3 -0
  100. package/lib/memoize/index.js +2 -0
  101. package/lib/memoize/memoize.d.ts +67 -0
  102. package/lib/memoize/memoize.d.ts.map +1 -0
  103. package/lib/memoize/memoize.fn.d.ts +69 -0
  104. package/lib/memoize/memoize.fn.d.ts.map +1 -0
  105. package/lib/memoize/memoize.fn.js +43 -0
  106. package/lib/memoize/memoize.fn.js.map +1 -0
  107. package/lib/memoize/memoize.js +40 -0
  108. package/lib/memoize/memoize.js.map +1 -0
  109. package/lib/memoize/memoize.types.d.ts +107 -0
  110. package/lib/memoize/memoize.types.d.ts.map +1 -0
  111. package/lib/memoize/memoize.types.js +0 -0
  112. package/lib/memoize-async/index.d.ts +4 -0
  113. package/lib/memoize-async/index.js +3 -0
  114. package/lib/memoize-async/memoize-async.d.ts +68 -0
  115. package/lib/memoize-async/memoize-async.d.ts.map +1 -0
  116. package/lib/memoize-async/memoize-async.fn.d.ts +69 -0
  117. package/lib/memoize-async/memoize-async.fn.d.ts.map +1 -0
  118. package/lib/memoize-async/memoize-async.fn.js +52 -0
  119. package/lib/memoize-async/memoize-async.fn.js.map +1 -0
  120. package/lib/memoize-async/memoize-async.js +15 -0
  121. package/lib/memoize-async/memoize-async.js.map +1 -0
  122. package/lib/memoize-async/memoize-async.types.d.ts +74 -0
  123. package/lib/memoize-async/memoize-async.types.d.ts.map +1 -0
  124. package/lib/memoize-async/memoize-async.types.js +0 -0
  125. package/lib/observer/index.d.ts +3 -0
  126. package/lib/observer/index.js +2 -0
  127. package/lib/observer/observer.d.ts +54 -0
  128. package/lib/observer/observer.d.ts.map +1 -0
  129. package/lib/observer/observer.js +85 -0
  130. package/lib/observer/observer.js.map +1 -0
  131. package/lib/observer/observer.types.d.ts +41 -0
  132. package/lib/observer/observer.types.d.ts.map +1 -0
  133. package/lib/observer/observer.types.js +0 -0
  134. package/lib/rate-limit/index.d.ts +4 -0
  135. package/lib/rate-limit/index.js +3 -0
  136. package/lib/rate-limit/rate-limit.d.ts +58 -0
  137. package/lib/rate-limit/rate-limit.d.ts.map +1 -0
  138. package/lib/rate-limit/rate-limit.fn.d.ts +43 -0
  139. package/lib/rate-limit/rate-limit.fn.d.ts.map +1 -0
  140. package/lib/rate-limit/rate-limit.fn.js +56 -0
  141. package/lib/rate-limit/rate-limit.fn.js.map +1 -0
  142. package/lib/rate-limit/rate-limit.js +65 -0
  143. package/lib/rate-limit/rate-limit.js.map +1 -0
  144. package/lib/rate-limit/rate-limit.types.d.ts +148 -0
  145. package/lib/rate-limit/rate-limit.types.d.ts.map +1 -0
  146. package/lib/rate-limit/rate-limit.types.js +0 -0
  147. package/lib/rate-limit/simple-rate-limit-counter.d.ts +89 -0
  148. package/lib/rate-limit/simple-rate-limit-counter.d.ts.map +1 -0
  149. package/lib/rate-limit/simple-rate-limit-counter.js +98 -0
  150. package/lib/rate-limit/simple-rate-limit-counter.js.map +1 -0
  151. package/lib/readonly/index.d.ts +3 -0
  152. package/lib/readonly/index.js +2 -0
  153. package/lib/readonly/readonly.d.ts +39 -0
  154. package/lib/readonly/readonly.d.ts.map +1 -0
  155. package/lib/readonly/readonly.js +43 -0
  156. package/lib/readonly/readonly.js.map +1 -0
  157. package/lib/readonly/readonly.types.d.ts +40 -0
  158. package/lib/readonly/readonly.types.d.ts.map +1 -0
  159. package/lib/readonly/readonly.types.js +0 -0
  160. package/lib/throttle/index.d.ts +2 -0
  161. package/lib/throttle/index.js +2 -0
  162. package/lib/throttle/throttle.d.ts +35 -0
  163. package/lib/throttle/throttle.d.ts.map +1 -0
  164. package/lib/throttle/throttle.fn.d.ts +42 -0
  165. package/lib/throttle/throttle.fn.d.ts.map +1 -0
  166. package/lib/throttle/throttle.fn.js +52 -0
  167. package/lib/throttle/throttle.fn.js.map +1 -0
  168. package/lib/throttle/throttle.js +43 -0
  169. package/lib/throttle/throttle.js.map +1 -0
  170. package/lib/throttle-async/index.d.ts +2 -0
  171. package/lib/throttle-async/index.js +2 -0
  172. package/lib/throttle-async/throttle-async-executor.d.ts +79 -0
  173. package/lib/throttle-async/throttle-async-executor.d.ts.map +1 -0
  174. package/lib/throttle-async/throttle-async-executor.js +122 -0
  175. package/lib/throttle-async/throttle-async-executor.js.map +1 -0
  176. package/lib/throttle-async/throttle-async.d.ts +68 -0
  177. package/lib/throttle-async/throttle-async.d.ts.map +1 -0
  178. package/lib/throttle-async/throttle-async.fn.d.ts +41 -0
  179. package/lib/throttle-async/throttle-async.fn.d.ts.map +1 -0
  180. package/lib/throttle-async/throttle-async.fn.js +46 -0
  181. package/lib/throttle-async/throttle-async.fn.js.map +1 -0
  182. package/lib/throttle-async/throttle-async.js +45 -0
  183. package/lib/throttle-async/throttle-async.js.map +1 -0
  184. package/lib/types.d.ts +81 -0
  185. package/lib/types.d.ts.map +1 -0
  186. package/lib/types.js +0 -0
  187. package/package.json +40 -0
@@ -0,0 +1,67 @@
1
+ import { Memoizable, MemoizeConfig } from "./memoize.types.js";
2
+
3
+ //#region src/memoize/memoize.d.ts
4
+ /**
5
+ * Decorator that caches method results based on their arguments.
6
+ * Subsequent calls with the same arguments return the cached result.
7
+ *
8
+ * @overload
9
+ * @template T - The type of the class containing the decorated method
10
+ * @template D - The return type of the decorated method
11
+ * @returns {Memoizable<T, D>} The decorator function
12
+ *
13
+ * @overload
14
+ * @template T - The type of the class containing the decorated method
15
+ * @template D - The return type of the decorated method
16
+ * @param {MemoizeConfig<T, D>} config - Configuration for memoization
17
+ * @returns {Memoizable<T, D>} The decorator function
18
+ *
19
+ * @overload
20
+ * @template T - The type of the class containing the decorated method
21
+ * @template D - The return type of the decorated method
22
+ * @param {number} expirationTimeMs - Cache expiration time in milliseconds
23
+ * @returns {Memoizable<T, D>} The decorator function
24
+ *
25
+ * @throws {Error} When applied to a non-method property
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * class DataService {
30
+ * // Basic usage - caches indefinitely
31
+ * @memoize()
32
+ * getUser(id: string): User {
33
+ * return this.database.findUser(id);
34
+ * }
35
+ *
36
+ * // With TTL (time to live)
37
+ * @memoize(60000) // Cache for 60 seconds
38
+ * getConfig(): Config {
39
+ * return this.loadConfig();
40
+ * }
41
+ *
42
+ * // With custom cache and key resolver
43
+ * @memoize({
44
+ * cache: new LRUCache<string, User>(100),
45
+ * keyResolver: (userId, includeDetails) => `${userId}-${includeDetails}`,
46
+ * expirationTimeMs: 300000 // 5 minutes
47
+ * })
48
+ * getUserWithDetails(userId: string, includeDetails: boolean): User {
49
+ * return this.fetchUser(userId, includeDetails);
50
+ * }
51
+ * }
52
+ *
53
+ * const service = new DataService();
54
+ *
55
+ * // First call executes the method
56
+ * const user1 = service.getUser('123');
57
+ *
58
+ * // Second call with same argument returns cached result
59
+ * const user2 = service.getUser('123'); // Instant, no database query
60
+ * ```
61
+ */
62
+ declare function memoize<T = any, D = any>(): Memoizable<T, D>;
63
+ declare function memoize<T = any, D = any>(config: MemoizeConfig<T, D>): Memoizable<T, D>;
64
+ declare function memoize<T = any, D = any>(expirationTimeMs: number): Memoizable<T, D>;
65
+ //#endregion
66
+ export { memoize };
67
+ //# sourceMappingURL=memoize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.d.ts","names":[],"sources":["../../src/memoize/memoize.ts"],"mappings":";;;;;AAyHA;;;;;;;;;;;;;;;;;;;;;;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAFgB,OAAA,kBAAA,CAAA,GAA6B,UAAA,CAAW,CAAA,EAAG,CAAA;AAAA,iBAC3C,OAAA,kBAAA,CAA0B,MAAA,EAAQ,aAAA,CAAc,CAAA,EAAG,CAAA,IAAK,UAAA,CAAW,CAAA,EAAG,CAAA;AAAA,iBACtE,OAAA,kBAAA,CAA0B,gBAAA,WAA2B,UAAA,CAAW,CAAA,EAAG,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { Method } from "../types.js";
2
+ import { MemoizeConfig } from "./memoize.types.js";
3
+
4
+ //#region src/memoize/memoize.fn.d.ts
5
+ /**
6
+ * Wraps a method to cache its results based on arguments.
7
+ *
8
+ * @overload
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 memoize
12
+ * @returns {Method<D, A>} The memoized method
13
+ *
14
+ * @overload
15
+ * @template D - The return type of the original method
16
+ * @template A - The argument types of the original method
17
+ * @param {Method<D, A>} originalMethod - The method to memoize
18
+ * @param {MemoizeConfig<any, D>} config - Configuration for memoization
19
+ * @returns {Method<D, A>} The memoized method
20
+ *
21
+ * @overload
22
+ * @template D - The return type of the original method
23
+ * @template A - The argument types of the original method
24
+ * @param {Method<D, A>} originalMethod - The method to memoize
25
+ * @param {number} expirationTimeMs - Cache expiration time in milliseconds
26
+ * @returns {Method<D, A>} The memoized method
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * class ExpensiveOperations {
31
+ * calculatePrimes(max: number): number[] {
32
+ * const primes = [];
33
+ * for (let i = 2; i <= max; i++) {
34
+ * if (this.isPrime(i)) primes.push(i);
35
+ * }
36
+ * return primes;
37
+ * }
38
+ * }
39
+ *
40
+ * const ops = new ExpensiveOperations();
41
+ *
42
+ * // Basic memoization
43
+ * const memoized = memoizeFn(ops.calculatePrimes.bind(ops));
44
+ * const primes1 = memoized(1000); // Computes
45
+ * const primes2 = memoized(1000); // Returns cached result
46
+ *
47
+ * // With TTL
48
+ * const withTTL = memoizeFn(
49
+ * ops.calculatePrimes.bind(ops),
50
+ * 60000 // Cache for 60 seconds
51
+ * );
52
+ *
53
+ * // With custom config
54
+ * const withConfig = memoizeFn(
55
+ * ops.calculatePrimes.bind(ops),
56
+ * {
57
+ * cache: new Map(),
58
+ * keyResolver: (max) => `primes-${max}`,
59
+ * expirationTimeMs: 300000
60
+ * }
61
+ * );
62
+ * ```
63
+ */
64
+ declare function memoizeFn<D = any, A extends any[] = any[]>(originalMethod: Method<D, A>): Method<D, A>;
65
+ declare function memoizeFn<D = any, A extends any[] = any[]>(originalMethod: Method<D, A>, config: MemoizeConfig<any, D>): Method<D, A>;
66
+ declare function memoizeFn<D = any, A extends any[] = any[]>(originalMethod: Method<D, A>, expirationTimeMs: number): Method<D, A>;
67
+ //#endregion
68
+ export { memoizeFn };
69
+ //# sourceMappingURL=memoize.fn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.fn.d.ts","names":[],"sources":["../../src/memoize/memoize.fn.ts"],"mappings":";;;;;;;;;;;;;AAkFA;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA;;;;;;;;;;;;;;;;;;;;;;;iBAPgB,SAAA,kCAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,IACzB,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBACG,SAAA,kCAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,MAAA,EAAQ,aAAA,MAAmB,CAAA,IAC1B,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBACG,SAAA,kCAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,gBAAA,WACC,MAAA,CAAO,CAAA,EAAG,CAAA"}
@@ -0,0 +1,43 @@
1
+ import { TaskExec } from "../_utils.js";
2
+ //#region src/memoize/memoize.fn.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
+ function memoizeFn(originalMethod, input) {
19
+ const defaultConfig = { cache: /* @__PURE__ */ new Map() };
20
+ const runner = new TaskExec();
21
+ let resolvedConfig = { ...defaultConfig };
22
+ if (typeof input === "number") resolvedConfig.expirationTimeMs = input;
23
+ else resolvedConfig = {
24
+ ...resolvedConfig,
25
+ ...input
26
+ };
27
+ return function(...args) {
28
+ const keyResolver = typeof resolvedConfig.keyResolver === "string" ? this[resolvedConfig.keyResolver].bind(this) : resolvedConfig.keyResolver;
29
+ const key = keyResolver ? keyResolver(...args) : JSON.stringify(args);
30
+ if (resolvedConfig.cache && !resolvedConfig.cache.has(key)) {
31
+ const response = originalMethod.apply(this, args);
32
+ if (resolvedConfig.expirationTimeMs !== void 0) runner.exec(() => {
33
+ resolvedConfig.cache?.delete(key);
34
+ }, resolvedConfig.expirationTimeMs);
35
+ resolvedConfig.cache.set(key, response);
36
+ }
37
+ return resolvedConfig.cache?.get(key);
38
+ };
39
+ }
40
+ //#endregion
41
+ export { memoizeFn };
42
+
43
+ //# sourceMappingURL=memoize.fn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.fn.js","names":[],"sources":["../../src/memoize/memoize.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 { TaskExec } from '../_utils.js';\nimport type { Method } from '../types.js';\nimport type { MemoizeConfig } from './memoize.types.js';\n\n/**\n * Wraps a method to cache its results based on arguments.\n *\n * @overload\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 memoize\n * @returns {Method<D, A>} The memoized method\n *\n * @overload\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 memoize\n * @param {MemoizeConfig<any, D>} config - Configuration for memoization\n * @returns {Method<D, A>} The memoized method\n *\n * @overload\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 memoize\n * @param {number} expirationTimeMs - Cache expiration time in milliseconds\n * @returns {Method<D, A>} The memoized method\n *\n * @example\n * ```typescript\n * class ExpensiveOperations {\n * calculatePrimes(max: number): number[] {\n * const primes = [];\n * for (let i = 2; i <= max; i++) {\n * if (this.isPrime(i)) primes.push(i);\n * }\n * return primes;\n * }\n * }\n *\n * const ops = new ExpensiveOperations();\n *\n * // Basic memoization\n * const memoized = memoizeFn(ops.calculatePrimes.bind(ops));\n * const primes1 = memoized(1000); // Computes\n * const primes2 = memoized(1000); // Returns cached result\n *\n * // With TTL\n * const withTTL = memoizeFn(\n * ops.calculatePrimes.bind(ops),\n * 60000 // Cache for 60 seconds\n * );\n *\n * // With custom config\n * const withConfig = memoizeFn(\n * ops.calculatePrimes.bind(ops),\n * {\n * cache: new Map(),\n * keyResolver: (max) => `primes-${max}`,\n * expirationTimeMs: 300000\n * }\n * );\n * ```\n */\nexport function memoizeFn<D = any, A extends any[] = any[]>(\n originalMethod: Method<D, A>,\n): Method<D, A>;\nexport function memoizeFn<D = any, A extends any[] = any[]>(\n originalMethod: Method<D, A>,\n config: MemoizeConfig<any, D>,\n): Method<D, A>;\nexport function memoizeFn<D = any, A extends any[] = any[]>(\n originalMethod: Method<D, A>,\n expirationTimeMs: number,\n): Method<D, A>;\nexport function memoizeFn<D = any, A extends any[] = any[]>(\n originalMethod: Method<D, A>,\n input?: MemoizeConfig<any, D> | number,\n): Method<D, A> {\n const defaultConfig: MemoizeConfig<any, D> = {\n cache: new Map<string, D>(),\n };\n\n const runner = new TaskExec();\n let resolvedConfig = {\n ...defaultConfig,\n } as MemoizeConfig<any, D>;\n\n if (typeof input === 'number') {\n resolvedConfig.expirationTimeMs = input;\n } else {\n resolvedConfig = {\n ...resolvedConfig,\n ...input,\n };\n }\n\n return function (this: any, ...args: A): D {\n const keyResolver =\n typeof resolvedConfig.keyResolver === 'string'\n ? this[resolvedConfig.keyResolver].bind(this)\n : resolvedConfig.keyResolver;\n\n const key = keyResolver ? keyResolver(...args) : JSON.stringify(args);\n\n if (resolvedConfig.cache && !resolvedConfig.cache.has(key)) {\n const response = originalMethod.apply(this, args);\n\n if (resolvedConfig.expirationTimeMs !== undefined) {\n runner.exec(() => {\n resolvedConfig.cache?.delete(key);\n }, resolvedConfig.expirationTimeMs);\n }\n\n resolvedConfig.cache.set(key, response);\n }\n\n return resolvedConfig.cache?.get(key) as D;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0FA,SAAgB,UACd,gBACA,OACc;CACd,MAAM,gBAAuC,EAC3C,uBAAO,IAAI,KAAgB,EAC5B;CAED,MAAM,SAAS,IAAI,UAAU;CAC7B,IAAI,iBAAiB,EACnB,GAAG,eACJ;AAED,KAAI,OAAO,UAAU,SACnB,gBAAe,mBAAmB;KAElC,kBAAiB;EACf,GAAG;EACH,GAAG;EACJ;AAGH,QAAO,SAAqB,GAAG,MAAY;EACzC,MAAM,cACJ,OAAO,eAAe,gBAAgB,WAClC,KAAK,eAAe,aAAa,KAAK,KAAK,GAC3C,eAAe;EAErB,MAAM,MAAM,cAAc,YAAY,GAAG,KAAK,GAAG,KAAK,UAAU,KAAK;AAErE,MAAI,eAAe,SAAS,CAAC,eAAe,MAAM,IAAI,IAAI,EAAE;GAC1D,MAAM,WAAW,eAAe,MAAM,MAAM,KAAK;AAEjD,OAAI,eAAe,qBAAqB,KAAA,EACtC,QAAO,WAAW;AAChB,mBAAe,OAAO,OAAO,IAAI;MAChC,eAAe,iBAAiB;AAGrC,kBAAe,MAAM,IAAI,KAAK,SAAS;;AAGzC,SAAO,eAAe,OAAO,IAAI,IAAI"}
@@ -0,0 +1,40 @@
1
+ import { memoizeFn } from "./memoize.fn.js";
2
+ //#region src/memoize/memoize.ts
3
+ /**
4
+ * @fileoverview Memoize decorator - caches method results based on arguments.
5
+ * Subsequent calls with the same arguments return the cached result instead of
6
+ * re-executing the method.
7
+ *
8
+ * @module @resq/typescript/decorators/memoize
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * class Calculator {
13
+ * @memoize()
14
+ * fibonacci(n: number): number {
15
+ * if (n <= 1) return n;
16
+ * return this.fibonacci(n - 1) + this.fibonacci(n - 2);
17
+ * }
18
+ * }
19
+ *
20
+ * const calc = new Calculator();
21
+ * console.log(calc.fibonacci(40)); // Computes and caches
22
+ * console.log(calc.fibonacci(40)); // Returns cached result instantly
23
+ * ```
24
+ *
25
+ * @copyright Copyright (c) 2026 ResQ
26
+ * @license MIT
27
+ */
28
+ function memoize(input) {
29
+ return (target, propertyName, descriptor) => {
30
+ if (descriptor.value) {
31
+ descriptor.value = input === void 0 ? memoizeFn(descriptor.value) : memoizeFn(descriptor.value, input);
32
+ return descriptor;
33
+ }
34
+ throw new Error("@memoize is applicable only on a methods.");
35
+ };
36
+ }
37
+ //#endregion
38
+ export { memoize };
39
+
40
+ //# sourceMappingURL=memoize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.js","names":[],"sources":["../../src/memoize/memoize.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 * 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 Memoize decorator - caches method results based on arguments.\n * Subsequent calls with the same arguments return the cached result instead of\n * re-executing the method.\n *\n * @module @resq/typescript/decorators/memoize\n *\n * @example\n * ```typescript\n * class Calculator {\n * @memoize()\n * fibonacci(n: number): number {\n * if (n <= 1) return n;\n * return this.fibonacci(n - 1) + this.fibonacci(n - 2);\n * }\n * }\n *\n * const calc = new Calculator();\n * console.log(calc.fibonacci(40)); // Computes and caches\n * console.log(calc.fibonacci(40)); // Returns cached result instantly\n * ```\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\nimport { memoizeFn } from './memoize.fn.js';\nimport type { Memoizable, MemoizeConfig } from './memoize.types.js';\n\n/**\n * Decorator that caches method results based on their arguments.\n * Subsequent calls with the same arguments return the cached result.\n *\n * @overload\n * @template T - The type of the class containing the decorated method\n * @template D - The return type of the decorated method\n * @returns {Memoizable<T, D>} The decorator function\n *\n * @overload\n * @template T - The type of the class containing the decorated method\n * @template D - The return type of the decorated method\n * @param {MemoizeConfig<T, D>} config - Configuration for memoization\n * @returns {Memoizable<T, D>} The decorator function\n *\n * @overload\n * @template T - The type of the class containing the decorated method\n * @template D - The return type of the decorated method\n * @param {number} expirationTimeMs - Cache expiration time in milliseconds\n * @returns {Memoizable<T, D>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class DataService {\n * // Basic usage - caches indefinitely\n * @memoize()\n * getUser(id: string): User {\n * return this.database.findUser(id);\n * }\n *\n * // With TTL (time to live)\n * @memoize(60000) // Cache for 60 seconds\n * getConfig(): Config {\n * return this.loadConfig();\n * }\n *\n * // With custom cache and key resolver\n * @memoize({\n * cache: new LRUCache<string, User>(100),\n * keyResolver: (userId, includeDetails) => `${userId}-${includeDetails}`,\n * expirationTimeMs: 300000 // 5 minutes\n * })\n * getUserWithDetails(userId: string, includeDetails: boolean): User {\n * return this.fetchUser(userId, includeDetails);\n * }\n * }\n *\n * const service = new DataService();\n *\n * // First call executes the method\n * const user1 = service.getUser('123');\n *\n * // Second call with same argument returns cached result\n * const user2 = service.getUser('123'); // Instant, no database query\n * ```\n */\nexport function memoize<T = any, D = any>(): Memoizable<T, D>;\nexport function memoize<T = any, D = any>(config: MemoizeConfig<T, D>): Memoizable<T, D>;\nexport function memoize<T = any, D = any>(expirationTimeMs: number): Memoizable<T, D>;\nexport function memoize<T = any, D = any>(input?: MemoizeConfig<T, D> | number): Memoizable<T, D> {\n return (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<Method<D>>,\n ): TypedPropertyDescriptor<Method<D>> => {\n if (descriptor.value) {\n descriptor.value = input === undefined ? memoizeFn(descriptor.value) : memoizeFn(descriptor.value, input as any);\n\n return descriptor;\n }\n throw new Error('@memoize is applicable only on a methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA2HA,SAAgB,QAA0B,OAAwD;AAChG,SACE,QACA,cACA,eACuC;AACvC,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,UAAU,KAAA,IAAY,UAAU,WAAW,MAAM,GAAG,UAAU,WAAW,OAAO,MAAa;AAEhH,UAAO;;AAET,QAAM,IAAI,MAAM,4CAA4C"}
@@ -0,0 +1,107 @@
1
+ import { Method } from "../types.js";
2
+
3
+ //#region src/memoize/memoize.types.d.ts
4
+ /**
5
+ * @fileoverview Type definitions for the Memoize decorator.
6
+ * Provides types for caching method results.
7
+ *
8
+ * @module @resq/typescript/decorators/memoize/types
9
+ *
10
+ * @copyright Copyright (c) 2026 ResQ
11
+ * @license MIT
12
+ */
13
+ /**
14
+ * @fileoverview Type definitions for the Memoize decorator.
15
+ * Provides types for caching method results.
16
+ *
17
+ * @module @resq/typescript/decorators/memoize/types
18
+ *
19
+ * @copyright Copyright (c) 2026 ResQ
20
+ * @license MIT
21
+ */
22
+ /**
23
+ * Function type for resolving cache keys from method arguments.
24
+ *
25
+ * @param {...any[]} args - The method arguments
26
+ * @returns {string} The cache key string
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const keyResolver: KeyResolver = (userId, includeDetails) => {
31
+ * return `${userId}-${includeDetails}`;
32
+ * };
33
+ * ```
34
+ */
35
+ type KeyResolver = (...args: unknown[]) => string;
36
+ /**
37
+ * Interface for cache implementations used by the memoize decorator.
38
+ *
39
+ * @interface Cache
40
+ * @template D - The type of values stored in the cache
41
+ * @property {(key: string, value: D) => void} set - Store a value in the cache
42
+ * @property {(key: string) => D | null | undefined} get - Retrieve a value from the cache
43
+ * @property {(key: string) => void} delete - Remove a value from the cache
44
+ * @property {(key: string) => boolean} has - Check if a key exists in the cache
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const cache: Cache<User> = {
49
+ * set: (key, value) => storage.set(key, value),
50
+ * get: (key) => storage.get(key),
51
+ * delete: (key) => storage.delete(key),
52
+ * has: (key) => storage.has(key)
53
+ * };
54
+ * ```
55
+ */
56
+ interface Cache<D> {
57
+ /** Store a value in the cache */
58
+ set: (key: string, value: D) => void;
59
+ /** Retrieve a value from the cache */
60
+ get: (key: string) => D | null | undefined;
61
+ /** Remove a value from the cache */
62
+ delete: (key: string) => void;
63
+ /** Check if a key exists in the cache */
64
+ has: (key: string) => boolean;
65
+ }
66
+ /**
67
+ * Configuration options for the @memoize decorator.
68
+ *
69
+ * @interface MemoizeConfig
70
+ * @template T - The type of the class containing the decorated method
71
+ * @template D - The return type of the decorated method
72
+ * @property {Cache<D>} [cache] - Custom cache implementation (defaults to Map)
73
+ * @property {KeyResolver | keyof T} [keyResolver] - Function or method name for generating cache keys
74
+ * @property {number} [expirationTimeMs] - Time in milliseconds after which cached values expire
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const config: MemoizeConfig<MyService, User> = {
79
+ * cache: new LRUCache<string, User>(100),
80
+ * keyResolver: (id) => `user-${id}`,
81
+ * expirationTimeMs: 300000 // 5 minutes
82
+ * };
83
+ * ```
84
+ */
85
+ interface MemoizeConfig<T, D> {
86
+ /** Custom cache implementation (defaults to Map) */
87
+ cache?: Cache<D>;
88
+ /** Function or method name for generating cache keys */
89
+ keyResolver?: KeyResolver | keyof T;
90
+ /** Time in milliseconds after which cached values expire */
91
+ expirationTimeMs?: number;
92
+ }
93
+ /**
94
+ * Type for the @memoize decorator function.
95
+ *
96
+ * @template T - The type of the class containing the decorated method
97
+ * @template D - The return type of the decorated method
98
+ *
99
+ * @param {T} target - The class prototype
100
+ * @param {keyof T} propertyName - The name of the method being decorated
101
+ * @param {TypedPropertyDescriptor<Method<D>>} descriptor - The property descriptor
102
+ * @returns {TypedPropertyDescriptor<Method<D>>} The modified descriptor
103
+ */
104
+ type Memoizable<T, D> = (target: T, propertyName: keyof T, descriptor: TypedPropertyDescriptor<Method<D>>) => TypedPropertyDescriptor<Method<D>>;
105
+ //#endregion
106
+ export { Cache, KeyResolver, Memoizable, MemoizeConfig };
107
+ //# sourceMappingURL=memoize.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize.types.d.ts","names":[],"sources":["../../src/memoize/memoize.types.ts"],"mappings":";;;;;;;;;;;;;AA8EA;;;;;;;;;;;;;;;;;;AA8BA;;;KApDY,WAAA,OAAkB,IAAA;;;;;;;;;;;;;;;;;AAwE9B;;;;UAlDiB,KAAA;EAqD4B;EAnD3C,GAAA,GAAM,GAAA,UAAa,KAAA,EAAO,CAAA;EAmDd;EAjDZ,GAAA,GAAM,GAAA,aAAgB,CAAA;EAkDK;EAhD3B,MAAA,GAAS,GAAA;EAgDiB;EA9C1B,GAAA,GAAM,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;UAsBS,aAAA;;EAEf,KAAA,GAAQ,KAAA,CAAM,CAAA;;EAEd,WAAA,GAAc,WAAA,SAAoB,CAAA;;EAElC,gBAAA;AAAA;;;;;;;;;;;;KAcU,UAAA,UACV,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,uBAAA,CAAwB,MAAA,CAAO,CAAA,OACxC,uBAAA,CAAwB,MAAA,CAAO,CAAA"}
File without changes
@@ -0,0 +1,4 @@
1
+ import { AsyncCache, AsyncMemoizable, AsyncMemoizeConfig } from "./memoize-async.types.js";
2
+ import { memoizeAsyncFn } from "./memoize-async.fn.js";
3
+ import { memoizeAsync } from "./memoize-async.js";
4
+ export { AsyncCache, AsyncMemoizable, AsyncMemoizeConfig, memoizeAsync, memoizeAsyncFn };
@@ -0,0 +1,3 @@
1
+ import { memoizeAsyncFn } from "./memoize-async.fn.js";
2
+ import { memoizeAsync } from "./memoize-async.js";
3
+ export { memoizeAsync, memoizeAsyncFn };
@@ -0,0 +1,68 @@
1
+ import { AsyncMemoizable, AsyncMemoizeConfig } from "./memoize-async.types.js";
2
+
3
+ //#region src/memoize-async/memoize-async.d.ts
4
+ /**
5
+ * Decorator that caches async method results based on their arguments.
6
+ * Prevents duplicate concurrent requests by returning the same promise
7
+ * for identical calls until the first one resolves.
8
+ *
9
+ * @overload
10
+ * @template T - The type of the class containing the decorated method
11
+ * @template D - The resolved type of the async method
12
+ * @returns {AsyncMemoizable<T, D>} The decorator function
13
+ *
14
+ * @overload
15
+ * @template T - The type of the class containing the decorated method
16
+ * @template D - The resolved type of the async method
17
+ * @param {AsyncMemoizeConfig<T, D>} config - Configuration for memoization
18
+ * @returns {AsyncMemoizable<T, D>} The decorator function
19
+ *
20
+ * @overload
21
+ * @template T - The type of the class containing the decorated method
22
+ * @template D - The resolved type of the async method
23
+ * @param {number} expirationTimeMs - Cache expiration time in milliseconds
24
+ * @returns {AsyncMemoizable<T, D>} The decorator function
25
+ *
26
+ * @throws {Error} When applied to a non-method property
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * class DataService {
31
+ * // Basic usage - caches indefinitely
32
+ * @memoizeAsync()
33
+ * async fetchConfig(): Promise<Config> {
34
+ * return fetch('/api/config').then(r => r.json());
35
+ * }
36
+ *
37
+ * // With TTL
38
+ * @memoizeAsync(60000) // Cache for 60 seconds
39
+ * async getExchangeRates(): Promise<Rates> {
40
+ * return fetch('/api/rates').then(r => r.json());
41
+ * }
42
+ *
43
+ * // With custom cache and key resolver
44
+ * @memoizeAsync({
45
+ * cache: new RedisCache<string, Product>(),
46
+ * keyResolver: (productId, includeDetails) => `product-${productId}-${includeDetails}`,
47
+ * expirationTimeMs: 300000
48
+ * })
49
+ * async getProduct(productId: string, includeDetails: boolean): Promise<Product> {
50
+ * return this.fetchProduct(productId, includeDetails);
51
+ * }
52
+ * }
53
+ *
54
+ * const service = new DataService();
55
+ *
56
+ * // Concurrent calls with same args share the same promise
57
+ * const [product1, product2] = await Promise.all([
58
+ * service.getProduct('123', true),
59
+ * service.getProduct('123', true) // Same promise as above
60
+ * ]);
61
+ * ```
62
+ */
63
+ declare function memoizeAsync<T = any, D = any>(): AsyncMemoizable<T, D>;
64
+ declare function memoizeAsync<T = any, D = any>(config: AsyncMemoizeConfig<T, D>): AsyncMemoizable<T, D>;
65
+ declare function memoizeAsync<T = any, D = any>(expirationTimeMs: number): AsyncMemoizable<T, D>;
66
+ //#endregion
67
+ export { memoizeAsync };
68
+ //# sourceMappingURL=memoize-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize-async.d.ts","names":[],"sources":["../../src/memoize-async/memoize-async.ts"],"mappings":";;;;;AAyGA;;;;;;;;;;;;;;;;;;;;;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAJgB,YAAA,kBAAA,CAAA,GAAkC,eAAA,CAAgB,CAAA,EAAG,CAAA;AAAA,iBACrD,YAAA,kBAAA,CACd,MAAA,EAAQ,kBAAA,CAAmB,CAAA,EAAG,CAAA,IAC7B,eAAA,CAAgB,CAAA,EAAG,CAAA;AAAA,iBACN,YAAA,kBAAA,CAA+B,gBAAA,WAA2B,eAAA,CAAgB,CAAA,EAAG,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { AsyncMethod } from "../types.js";
2
+ import { AsyncMemoizeConfig } from "./memoize-async.types.js";
3
+
4
+ //#region src/memoize-async/memoize-async.fn.d.ts
5
+ /**
6
+ * Wraps an async method to cache its results and deduplicate concurrent calls.
7
+ *
8
+ * @overload
9
+ * @template D - The resolved type of the async method
10
+ * @template A - The argument types of the original method
11
+ * @param {AsyncMethod<D, A>} originalMethod - The async method to memoize
12
+ * @returns {AsyncMethod<D, A>} The memoized method
13
+ *
14
+ * @overload
15
+ * @template D - The resolved type of the async method
16
+ * @template A - The argument types of the original method
17
+ * @param {AsyncMethod<D, A>} originalMethod - The async method to memoize
18
+ * @param {AsyncMemoizeConfig<any, D>} config - Configuration for memoization
19
+ * @returns {AsyncMethod<D, A>} The memoized method
20
+ *
21
+ * @overload
22
+ * @template D - The resolved type of the async method
23
+ * @template A - The argument types of the original method
24
+ * @param {AsyncMethod<D, A>} originalMethod - The async method to memoize
25
+ * @param {number} expirationTimeMs - Cache expiration time in milliseconds
26
+ * @returns {AsyncMethod<D, A>} The memoized method
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * class ApiClient {
31
+ * async fetchData(endpoint: string): Promise<Data> {
32
+ * const response = await fetch(endpoint);
33
+ * return response.json();
34
+ * }
35
+ * }
36
+ *
37
+ * const client = new ApiClient();
38
+ *
39
+ * // Basic memoization
40
+ * const memoized = memoizeAsyncFn(client.fetchData.bind(client));
41
+ *
42
+ * // Concurrent calls share the same promise
43
+ * const promise1 = memoized('/api/data');
44
+ * const promise2 = memoized('/api/data'); // Same promise as above
45
+ * const [data1, data2] = await Promise.all([promise1, promise2]);
46
+ *
47
+ * // With TTL
48
+ * const withTTL = memoizeAsyncFn(
49
+ * client.fetchData.bind(client),
50
+ * 60000 // Cache for 60 seconds
51
+ * );
52
+ *
53
+ * // With custom config
54
+ * const withConfig = memoizeAsyncFn(
55
+ * client.fetchData.bind(client),
56
+ * {
57
+ * cache: new Map(),
58
+ * keyResolver: (endpoint) => endpoint,
59
+ * expirationTimeMs: 300000
60
+ * }
61
+ * );
62
+ * ```
63
+ */
64
+ declare function memoizeAsyncFn<D = any, A extends any[] = any[]>(originalMethod: AsyncMethod<D, A>): AsyncMethod<D, A>;
65
+ declare function memoizeAsyncFn<D = any, A extends any[] = any[]>(originalMethod: AsyncMethod<D, A>, config: AsyncMemoizeConfig<any, D>): AsyncMethod<D, A>;
66
+ declare function memoizeAsyncFn<D = any, A extends any[] = any[]>(originalMethod: AsyncMethod<D, A>, expirationTimeMs: number): AsyncMethod<D, A>;
67
+ //#endregion
68
+ export { memoizeAsyncFn };
69
+ //# sourceMappingURL=memoize-async.fn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize-async.fn.d.ts","names":[],"sources":["../../src/memoize-async/memoize-async.fn.ts"],"mappings":";;;;;;;;;;;;;AAkFA;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA;;;;;;;;;;;;;;;;;;;;;;;iBAPgB,cAAA,kCAAA,CACd,cAAA,EAAgB,WAAA,CAAY,CAAA,EAAG,CAAA,IAC9B,WAAA,CAAY,CAAA,EAAG,CAAA;AAAA,iBACF,cAAA,kCAAA,CACd,cAAA,EAAgB,WAAA,CAAY,CAAA,EAAG,CAAA,GAC/B,MAAA,EAAQ,kBAAA,MAAwB,CAAA,IAC/B,WAAA,CAAY,CAAA,EAAG,CAAA;AAAA,iBACF,cAAA,kCAAA,CACd,cAAA,EAAgB,WAAA,CAAY,CAAA,EAAG,CAAA,GAC/B,gBAAA,WACC,WAAA,CAAY,CAAA,EAAG,CAAA"}
@@ -0,0 +1,52 @@
1
+ import { TaskExec, isNumber, isString } from "../_utils.js";
2
+ //#region src/memoize-async/memoize-async.fn.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
+ function memoizeAsyncFn(originalMethod, input) {
19
+ const defaultConfig = { cache: /* @__PURE__ */ new Map() };
20
+ const runner = new TaskExec();
21
+ const promCache = /* @__PURE__ */ new Map();
22
+ let resolvedConfig = { ...defaultConfig };
23
+ if (isNumber(input)) resolvedConfig.expirationTimeMs = input;
24
+ else resolvedConfig = {
25
+ ...resolvedConfig,
26
+ ...input
27
+ };
28
+ return async function(...args) {
29
+ const keyResolver = isString(resolvedConfig.keyResolver) ? this[resolvedConfig.keyResolver].bind(this) : resolvedConfig.keyResolver;
30
+ let key;
31
+ if (keyResolver) key = keyResolver(...args);
32
+ else key = JSON.stringify(args);
33
+ if (promCache.has(key)) return promCache.get(key);
34
+ const prom = (async () => {
35
+ if (await resolvedConfig.cache?.has(key) ?? false) return await resolvedConfig.cache?.get(key);
36
+ const data = await originalMethod.apply(this, args);
37
+ await resolvedConfig.cache?.set(key, data);
38
+ if (resolvedConfig.expirationTimeMs !== void 0) runner.exec(() => {
39
+ resolvedConfig.cache?.delete(key);
40
+ }, resolvedConfig.expirationTimeMs);
41
+ return data;
42
+ })().finally(() => {
43
+ promCache.delete(key);
44
+ });
45
+ promCache.set(key, prom);
46
+ return prom;
47
+ };
48
+ }
49
+ //#endregion
50
+ export { memoizeAsyncFn };
51
+
52
+ //# sourceMappingURL=memoize-async.fn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize-async.fn.js","names":[],"sources":["../../src/memoize-async/memoize-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 { isNumber, isString, TaskExec } from '../_utils.js';\nimport type { AsyncMethod } from '../types.js';\nimport type { AsyncMemoizeConfig } from './memoize-async.types.js';\n\n/**\n * Wraps an async method to cache its results and deduplicate concurrent calls.\n *\n * @overload\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 memoize\n * @returns {AsyncMethod<D, A>} The memoized method\n *\n * @overload\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 memoize\n * @param {AsyncMemoizeConfig<any, D>} config - Configuration for memoization\n * @returns {AsyncMethod<D, A>} The memoized method\n *\n * @overload\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 memoize\n * @param {number} expirationTimeMs - Cache expiration time in milliseconds\n * @returns {AsyncMethod<D, A>} The memoized method\n *\n * @example\n * ```typescript\n * class ApiClient {\n * async fetchData(endpoint: string): Promise<Data> {\n * const response = await fetch(endpoint);\n * return response.json();\n * }\n * }\n *\n * const client = new ApiClient();\n *\n * // Basic memoization\n * const memoized = memoizeAsyncFn(client.fetchData.bind(client));\n *\n * // Concurrent calls share the same promise\n * const promise1 = memoized('/api/data');\n * const promise2 = memoized('/api/data'); // Same promise as above\n * const [data1, data2] = await Promise.all([promise1, promise2]);\n *\n * // With TTL\n * const withTTL = memoizeAsyncFn(\n * client.fetchData.bind(client),\n * 60000 // Cache for 60 seconds\n * );\n *\n * // With custom config\n * const withConfig = memoizeAsyncFn(\n * client.fetchData.bind(client),\n * {\n * cache: new Map(),\n * keyResolver: (endpoint) => endpoint,\n * expirationTimeMs: 300000\n * }\n * );\n * ```\n */\nexport function memoizeAsyncFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n): AsyncMethod<D, A>;\nexport function memoizeAsyncFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n config: AsyncMemoizeConfig<any, D>,\n): AsyncMethod<D, A>;\nexport function memoizeAsyncFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n expirationTimeMs: number,\n): AsyncMethod<D, A>;\n\nexport function memoizeAsyncFn<D = any, A extends any[] = any[]>(\n originalMethod: AsyncMethod<D, A>,\n input?: AsyncMemoizeConfig<any, D> | number,\n): AsyncMethod<D, A> {\n const defaultConfig: AsyncMemoizeConfig<any, D> = {\n cache: new Map<string, D>(),\n };\n const runner = new TaskExec();\n const promCache = new Map<string, Promise<D>>();\n let resolvedConfig = {\n ...defaultConfig,\n } as AsyncMemoizeConfig<any, D>;\n\n if (isNumber(input)) {\n resolvedConfig.expirationTimeMs = input;\n } else {\n resolvedConfig = {\n ...resolvedConfig,\n ...input,\n };\n }\n\n return async function (this: any, ...args: A): Promise<D> {\n const keyResolver = isString(resolvedConfig.keyResolver)\n ? this[resolvedConfig.keyResolver].bind(this)\n : resolvedConfig.keyResolver;\n\n let key;\n\n if (keyResolver) {\n key = keyResolver(...args);\n } else {\n key = JSON.stringify(args);\n }\n\n if (promCache.has(key)) {\n return promCache.get(key) as Promise<D>;\n }\n\n const prom = (async (): Promise<D> => {\n const inCache = (await resolvedConfig.cache?.has(key)) ?? false;\n\n if (inCache) {\n return (await resolvedConfig.cache?.get(key)) as D;\n }\n\n const data = await originalMethod.apply(this, args);\n await resolvedConfig.cache?.set(key, data);\n\n if (resolvedConfig.expirationTimeMs !== undefined) {\n runner.exec(() => {\n resolvedConfig.cache?.delete(key);\n }, resolvedConfig.expirationTimeMs);\n }\n\n return data;\n })().finally(() => {\n promCache.delete(key);\n });\n\n promCache.set(key, prom);\n\n return prom;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2FA,SAAgB,eACd,gBACA,OACmB;CACnB,MAAM,gBAA4C,EAChD,uBAAO,IAAI,KAAgB,EAC5B;CACD,MAAM,SAAS,IAAI,UAAU;CAC7B,MAAM,4BAAY,IAAI,KAAyB;CAC/C,IAAI,iBAAiB,EACnB,GAAG,eACJ;AAED,KAAI,SAAS,MAAM,CACjB,gBAAe,mBAAmB;KAElC,kBAAiB;EACf,GAAG;EACH,GAAG;EACJ;AAGH,QAAO,eAA2B,GAAG,MAAqB;EACxD,MAAM,cAAc,SAAS,eAAe,YAAY,GACpD,KAAK,eAAe,aAAa,KAAK,KAAK,GAC3C,eAAe;EAEnB,IAAI;AAEJ,MAAI,YACF,OAAM,YAAY,GAAG,KAAK;MAE1B,OAAM,KAAK,UAAU,KAAK;AAG5B,MAAI,UAAU,IAAI,IAAI,CACpB,QAAO,UAAU,IAAI,IAAI;EAG3B,MAAM,QAAQ,YAAwB;AAGpC,OAFiB,MAAM,eAAe,OAAO,IAAI,IAAI,IAAK,MAGxD,QAAQ,MAAM,eAAe,OAAO,IAAI,IAAI;GAG9C,MAAM,OAAO,MAAM,eAAe,MAAM,MAAM,KAAK;AACnD,SAAM,eAAe,OAAO,IAAI,KAAK,KAAK;AAE1C,OAAI,eAAe,qBAAqB,KAAA,EACtC,QAAO,WAAW;AAChB,mBAAe,OAAO,OAAO,IAAI;MAChC,eAAe,iBAAiB;AAGrC,UAAO;MACL,CAAC,cAAc;AACjB,aAAU,OAAO,IAAI;IACrB;AAEF,YAAU,IAAI,KAAK,KAAK;AAExB,SAAO"}
@@ -0,0 +1,15 @@
1
+ import { memoizeAsyncFn } from "./memoize-async.fn.js";
2
+ //#region src/memoize-async/memoize-async.ts
3
+ function memoizeAsync(input) {
4
+ return (target, propertyName, descriptor) => {
5
+ if (descriptor.value) {
6
+ descriptor.value = input === void 0 ? memoizeAsyncFn(descriptor.value) : memoizeAsyncFn(descriptor.value, input);
7
+ return descriptor;
8
+ }
9
+ throw new Error("@memoizeAsync is applicable only on a methods.");
10
+ };
11
+ }
12
+ //#endregion
13
+ export { memoizeAsync };
14
+
15
+ //# sourceMappingURL=memoize-async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize-async.js","names":[],"sources":["../../src/memoize-async/memoize-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 MemoizeAsync decorator - caches async method results based on arguments.\n * Similar to @memoize but designed for async methods with support for promise deduplication.\n *\n * @module @resq/typescript/decorators/memoize-async\n *\n * @example\n * ```typescript\n * class ApiService {\n * @memoizeAsync()\n * async fetchUser(userId: string): Promise<User> {\n * const response = await fetch(`/api/users/${userId}`);\n * return response.json();\n * }\n * }\n *\n * const api = new ApiService();\n * const user1 = await api.fetchUser('123'); // Fetches from API\n * const user2 = await api.fetchUser('123'); // Returns cached promise\n * ```\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\nimport type { AsyncMethod } from '../types.js';\nimport { memoizeAsyncFn } from './memoize-async.fn.js';\nimport type { AsyncMemoizable, AsyncMemoizeConfig } from './memoize-async.types.js';\n\n/**\n * Decorator that caches async method results based on their arguments.\n * Prevents duplicate concurrent requests by returning the same promise\n * for identical calls until the first one resolves.\n *\n * @overload\n * @template T - The type of the class containing the decorated method\n * @template D - The resolved type of the async method\n * @returns {AsyncMemoizable<T, D>} The decorator function\n *\n * @overload\n * @template T - The type of the class containing the decorated method\n * @template D - The resolved type of the async method\n * @param {AsyncMemoizeConfig<T, D>} config - Configuration for memoization\n * @returns {AsyncMemoizable<T, D>} The decorator function\n *\n * @overload\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} expirationTimeMs - Cache expiration time in milliseconds\n * @returns {AsyncMemoizable<T, D>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class DataService {\n * // Basic usage - caches indefinitely\n * @memoizeAsync()\n * async fetchConfig(): Promise<Config> {\n * return fetch('/api/config').then(r => r.json());\n * }\n *\n * // With TTL\n * @memoizeAsync(60000) // Cache for 60 seconds\n * async getExchangeRates(): Promise<Rates> {\n * return fetch('/api/rates').then(r => r.json());\n * }\n *\n * // With custom cache and key resolver\n * @memoizeAsync({\n * cache: new RedisCache<string, Product>(),\n * keyResolver: (productId, includeDetails) => `product-${productId}-${includeDetails}`,\n * expirationTimeMs: 300000\n * })\n * async getProduct(productId: string, includeDetails: boolean): Promise<Product> {\n * return this.fetchProduct(productId, includeDetails);\n * }\n * }\n *\n * const service = new DataService();\n *\n * // Concurrent calls with same args share the same promise\n * const [product1, product2] = await Promise.all([\n * service.getProduct('123', true),\n * service.getProduct('123', true) // Same promise as above\n * ]);\n * ```\n */\nexport function memoizeAsync<T = any, D = any>(): AsyncMemoizable<T, D>;\nexport function memoizeAsync<T = any, D = any>(\n config: AsyncMemoizeConfig<T, D>,\n): AsyncMemoizable<T, D>;\nexport function memoizeAsync<T = any, D = any>(expirationTimeMs: number): AsyncMemoizable<T, D>;\nexport function memoizeAsync<T = any, D = any>(\n input?: AsyncMemoizeConfig<T, D> | number,\n): AsyncMemoizable<T, D> {\n return (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<AsyncMethod<D>>,\n ): TypedPropertyDescriptor<AsyncMethod<D>> => {\n if (descriptor.value) {\n descriptor.value = input === undefined ? memoizeAsyncFn(descriptor.value) : memoizeAsyncFn(descriptor.value, input as any);\n\n return descriptor;\n }\n\n throw new Error('@memoizeAsync is applicable only on a methods.');\n };\n}\n"],"mappings":";;AA6GA,SAAgB,aACd,OACuB;AACvB,SACE,QACA,cACA,eAC4C;AAC5C,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,UAAU,KAAA,IAAY,eAAe,WAAW,MAAM,GAAG,eAAe,WAAW,OAAO,MAAa;AAE1H,UAAO;;AAGT,QAAM,IAAI,MAAM,iDAAiD"}