@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,74 @@
1
+ import { Cache, KeyResolver, Memoizable } from "../memoize/memoize.types.js";
2
+
3
+ //#region src/memoize-async/memoize-async.types.d.ts
4
+ /**
5
+ * Type for the @memoizeAsync decorator function.
6
+ * Similar to Memoizable but for async methods.
7
+ *
8
+ * @template T - The type of the class containing the decorated method
9
+ * @template D - The resolved type of the async method
10
+ */
11
+ type AsyncMemoizable<T, D> = Memoizable<T, Promise<D>>;
12
+ /**
13
+ * Interface for async cache implementations used by the memoizeAsync decorator.
14
+ *
15
+ * @interface AsyncCache
16
+ * @template D - The type of values stored in the cache
17
+ * @property {(key: string, value: D) => Promise<void>} set - Store a value in the cache asynchronously
18
+ * @property {(key: string) => Promise<D> | Promise<null>} get - Retrieve a value from the cache asynchronously
19
+ * @property {(key: string) => Promise<void>} delete - Remove a value from the cache asynchronously
20
+ * @property {(key: string) => Promise<boolean>} has - Check if a key exists in the cache asynchronously
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const redisCache: AsyncCache<User> = {
25
+ * set: async (key, value) => await redis.set(key, JSON.stringify(value)),
26
+ * get: async (key) => {
27
+ * const data = await redis.get(key);
28
+ * return data ? JSON.parse(data) : null;
29
+ * },
30
+ * delete: async (key) => await redis.del(key),
31
+ * has: async (key) => await redis.exists(key) > 0
32
+ * };
33
+ * ```
34
+ */
35
+ interface AsyncCache<D> {
36
+ /** Store a value in the cache asynchronously */
37
+ set: (key: string, value: D) => Promise<void>;
38
+ /** Retrieve a value from the cache asynchronously */
39
+ get: (key: string) => Promise<D> | Promise<null>;
40
+ /** Remove a value from the cache asynchronously */
41
+ delete: (key: string) => Promise<void>;
42
+ /** Check if a key exists in the cache asynchronously */
43
+ has: (key: string) => Promise<boolean>;
44
+ }
45
+ /**
46
+ * Configuration options for the @memoizeAsync decorator.
47
+ *
48
+ * @interface AsyncMemoizeConfig
49
+ * @template T - The type of the class containing the decorated method
50
+ * @template D - The resolved type of the async method
51
+ * @property {Cache<D> | AsyncCache<D>} [cache] - Custom cache implementation (sync or async)
52
+ * @property {KeyResolver | keyof T} [keyResolver] - Function or method name for generating cache keys
53
+ * @property {number} [expirationTimeMs] - Time in milliseconds after which cached values expire
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const config: AsyncMemoizeConfig<ApiService, User> = {
58
+ * cache: redisCache,
59
+ * keyResolver: (userId) => `user:${userId}`,
60
+ * expirationTimeMs: 300000
61
+ * };
62
+ * ```
63
+ */
64
+ interface AsyncMemoizeConfig<T, D> {
65
+ /** Custom cache implementation (sync or async) */
66
+ cache?: Cache<D> | AsyncCache<D>;
67
+ /** Function or method name for generating cache keys */
68
+ keyResolver?: KeyResolver | keyof T;
69
+ /** Time in milliseconds after which cached values expire */
70
+ expirationTimeMs?: number;
71
+ }
72
+ //#endregion
73
+ export { AsyncCache, AsyncMemoizable, AsyncMemoizeConfig };
74
+ //# sourceMappingURL=memoize-async.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoize-async.types.d.ts","names":[],"sources":["../../src/memoize-async/memoize-async.types.ts"],"mappings":";;;;;;;AAkDA;;;KAzBY,eAAA,SAAwB,UAAA,CAAW,CAAA,EAAG,OAAA,CAAQ,CAAA;;;;;;;;;;;;;;;;;;;;;;;;UAyBzC,UAAA;EAQO;EANtB,GAAA,GAAM,GAAA,UAAa,KAAA,EAAO,CAAA,KAAM,OAAA;EAMH;EAJ7B,GAAA,GAAM,GAAA,aAAgB,OAAA,CAAQ,CAAA,IAAK,OAAA;EA0BF;EAxBjC,MAAA,GAAS,GAAA,aAAgB,OAAA;EA0BX;EAxBd,GAAA,GAAM,GAAA,aAAgB,OAAA;AAAA;;;;;;;;;;;;;;;;;;;;UAsBP,kBAAA;;EAEf,KAAA,GAAQ,KAAA,CAAM,CAAA,IAAK,UAAA,CAAW,CAAA;;EAE9B,WAAA,GAAc,WAAA,SAAoB,CAAA;;EAElC,gBAAA;AAAA"}
File without changes
@@ -0,0 +1,3 @@
1
+ import { observe } from "./observer.js";
2
+ import { ObserverCallback } from "./observer.types.js";
3
+ export { ObserverCallback, observe };
@@ -0,0 +1,2 @@
1
+ import { observe } from "./observer.js";
2
+ export { observe };
@@ -0,0 +1,54 @@
1
+ import { ObserverCallback } from "./observer.types.js";
2
+
3
+ //#region src/observer/observer.d.ts
4
+ /**
5
+ * Observe all changes of a property. All assignments will be logged to the console.
6
+ *
7
+ * @param {object} target - The class prototype
8
+ * @param {string | symbol} propertyKey - The property key
9
+ * @returns {void}
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * class Counter {
14
+ * @observe
15
+ * value: number = 0;
16
+ * }
17
+ *
18
+ * const counter = new Counter();
19
+ * counter.value = 5; // Logs: "setting property Counter#value = 5"
20
+ * counter.value = 10; // Logs: "setting property Counter#value = 10"
21
+ * ```
22
+ */
23
+ declare function observe(target: object, propertyKey: string | symbol): void;
24
+ /**
25
+ * Observe all changes of a property and invoke a provided callback on each assignment.
26
+ *
27
+ * @template T - The type of the property value
28
+ * @param {ObserverCallback<T>} cb - Callback to execute on assignment of observed variable
29
+ * @returns {PropertyDecorator} The property decorator
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * class User {
34
+ * @observe((value) => {
35
+ * console.log('Email changed:', value);
36
+ * validateEmail(value);
37
+ * })
38
+ * email: string = '';
39
+ *
40
+ * @observe((value) => {
41
+ * metrics.gauge('user.age', value);
42
+ * })
43
+ * age: number = 0;
44
+ * }
45
+ *
46
+ * const user = new User();
47
+ * user.email = 'test@example.com'; // Logs and validates
48
+ * user.age = 25; // Records metric
49
+ * ```
50
+ */
51
+ declare function observe<T>(cb: ObserverCallback<T>): PropertyDecorator;
52
+ //#endregion
53
+ export { observe };
54
+ //# sourceMappingURL=observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.d.ts","names":[],"sources":["../../src/observer/observer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBA4FgB,OAAA,CAAQ,MAAA,UAAgB,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BxB,OAAA,GAAA,CAAW,EAAA,EAAI,gBAAA,CAAiB,CAAA,IAAK,iBAAA"}
@@ -0,0 +1,85 @@
1
+ import { isFunction } from "../_utils.js";
2
+ //#region src/observer/observer.ts
3
+ /**
4
+ * Copyright 2026 ResQ
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ /**
19
+ * @fileoverview Observer decorator - observes property changes and invokes
20
+ * a callback when the property value changes.
21
+ *
22
+ * @module @resq/typescript/decorators/observer
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * class Component {
27
+ * @observe
28
+ * count: number = 0;
29
+ *
30
+ * @observe((newValue) => {
31
+ * console.log('Name changed to:', newValue);
32
+ * })
33
+ * name: string = '';
34
+ * }
35
+ *
36
+ * const comp = new Component();
37
+ * comp.count = 5; // Logs: "setting property Component#count = 5"
38
+ * comp.name = 'John'; // Logs: "Name changed to: John"
39
+ * ```
40
+ *
41
+ * @copyright Copyright (c) 2026 ResQ
42
+ * @license MIT
43
+ */
44
+ /**
45
+ * Creates a property decorator factory that observes property changes.
46
+ *
47
+ * @template T - The type of the property value
48
+ * @param {ObserverCallback<T>} [cb] - Optional callback to invoke on changes
49
+ * @returns {PropertyDecorator} The property decorator
50
+ */
51
+ function factory(cb) {
52
+ return (target, propertyKey) => {
53
+ let value;
54
+ const { name } = target.constructor;
55
+ Object.defineProperty(target, propertyKey, {
56
+ set(newValue) {
57
+ value = newValue;
58
+ if (cb) cb(newValue);
59
+ else console.log(`setting property ${name}#${String(propertyKey)} = ${newValue}`);
60
+ },
61
+ get() {
62
+ return value;
63
+ }
64
+ });
65
+ };
66
+ }
67
+ /**
68
+ * Overloaded function for observing property changes.
69
+ * Can be used with or without a custom callback.
70
+ *
71
+ * @param {object | ObserverCallback<T>} targetOrCb - Either the class prototype or a callback function
72
+ * @param {string | symbol} [propertyKey] - The property key (when used without callback)
73
+ * @returns {void | PropertyDecorator} Either void or the decorator function
74
+ *
75
+ * @throws {TypeError} When used with incorrect parameters
76
+ */
77
+ function observe(targetOrCb, propertyKey) {
78
+ if (propertyKey && !isFunction(targetOrCb)) return factory()(targetOrCb, propertyKey);
79
+ if (isFunction(targetOrCb)) return factory(targetOrCb);
80
+ throw new TypeError("@observe not used with correct parameters!");
81
+ }
82
+ //#endregion
83
+ export { observe };
84
+
85
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","names":[],"sources":["../../src/observer/observer.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 Observer decorator - observes property changes and invokes\n * a callback when the property value changes.\n *\n * @module @resq/typescript/decorators/observer\n *\n * @example\n * ```typescript\n * class Component {\n * @observe\n * count: number = 0;\n *\n * @observe((newValue) => {\n * console.log('Name changed to:', newValue);\n * })\n * name: string = '';\n * }\n *\n * const comp = new Component();\n * comp.count = 5; // Logs: \"setting property Component#count = 5\"\n * comp.name = 'John'; // Logs: \"Name changed to: John\"\n * ```\n *\n * @copyright Copyright (c) 2026 ResQ\n * @license MIT\n */\n\nimport { isFunction } from '../_utils.js';\nimport type { ObserverCallback } from './index.js';\n\n/**\n * Creates a property decorator factory that observes property changes.\n *\n * @template T - The type of the property value\n * @param {ObserverCallback<T>} [cb] - Optional callback to invoke on changes\n * @returns {PropertyDecorator} The property decorator\n */\nfunction factory<T>(cb?: ObserverCallback<T>): PropertyDecorator {\n return (target: object, propertyKey: string | symbol) => {\n let value: T;\n const { name } = target.constructor;\n Object.defineProperty(target, propertyKey, {\n set(newValue: T) {\n value = newValue;\n if (cb) {\n cb(newValue);\n } else {\n console.log(`setting property ${name}#${String(propertyKey)} = ${newValue}`);\n }\n },\n get() {\n return value;\n },\n });\n };\n}\n\n/**\n * Observe all changes of a property. All assignments will be logged to the console.\n *\n * @param {object} target - The class prototype\n * @param {string | symbol} propertyKey - The property key\n * @returns {void}\n *\n * @example\n * ```typescript\n * class Counter {\n * @observe\n * value: number = 0;\n * }\n *\n * const counter = new Counter();\n * counter.value = 5; // Logs: \"setting property Counter#value = 5\"\n * counter.value = 10; // Logs: \"setting property Counter#value = 10\"\n * ```\n */\nexport function observe(target: object, propertyKey: string | symbol): void;\n\n/**\n * Observe all changes of a property and invoke a provided callback on each assignment.\n *\n * @template T - The type of the property value\n * @param {ObserverCallback<T>} cb - Callback to execute on assignment of observed variable\n * @returns {PropertyDecorator} The property decorator\n *\n * @example\n * ```typescript\n * class User {\n * @observe((value) => {\n * console.log('Email changed:', value);\n * validateEmail(value);\n * })\n * email: string = '';\n *\n * @observe((value) => {\n * metrics.gauge('user.age', value);\n * })\n * age: number = 0;\n * }\n *\n * const user = new User();\n * user.email = 'test@example.com'; // Logs and validates\n * user.age = 25; // Records metric\n * ```\n */\nexport function observe<T>(cb: ObserverCallback<T>): PropertyDecorator;\n\n/**\n * Overloaded function for observing property changes.\n * Can be used with or without a custom callback.\n *\n * @param {object | ObserverCallback<T>} targetOrCb - Either the class prototype or a callback function\n * @param {string | symbol} [propertyKey] - The property key (when used without callback)\n * @returns {void | PropertyDecorator} Either void or the decorator function\n *\n * @throws {TypeError} When used with incorrect parameters\n */\nexport function observe<T>(\n targetOrCb: object | ObserverCallback<T>,\n propertyKey?: string | symbol,\n) {\n if (propertyKey && !isFunction(targetOrCb)) {\n const decorator = factory();\n return decorator(targetOrCb, propertyKey);\n }\n if (isFunction(targetOrCb)) {\n return factory(targetOrCb as ObserverCallback<T>);\n }\n throw new TypeError('@observe not used with correct parameters!');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAS,QAAW,IAA6C;AAC/D,SAAQ,QAAgB,gBAAiC;EACvD,IAAI;EACJ,MAAM,EAAE,SAAS,OAAO;AACxB,SAAO,eAAe,QAAQ,aAAa;GACzC,IAAI,UAAa;AACf,YAAQ;AACR,QAAI,GACF,IAAG,SAAS;QAEZ,SAAQ,IAAI,oBAAoB,KAAK,GAAG,OAAO,YAAY,CAAC,KAAK,WAAW;;GAGhF,MAAM;AACJ,WAAO;;GAEV,CAAC;;;;;;;;;;;;;AAgEN,SAAgB,QACd,YACA,aACA;AACA,KAAI,eAAe,CAAC,WAAW,WAAW,CAExC,QADkB,SAAS,CACV,YAAY,YAAY;AAE3C,KAAI,WAAW,WAAW,CACxB,QAAO,QAAQ,WAAkC;AAEnD,OAAM,IAAI,UAAU,6CAA6C"}
@@ -0,0 +1,41 @@
1
+ //#region src/observer/observer.types.d.ts
2
+ /**
3
+ * Copyright 2026 ResQ
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * Callback function type for property observers.
19
+ * Called whenever the observed property value changes.
20
+ *
21
+ * @template T - The type of the property value
22
+ * @param {T} value - The new value of the property
23
+ * @returns {unknown} Can return any value (typically void)
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const onCountChange: ObserverCallback<number> = (newValue) => {
28
+ * console.log(`Count is now: ${newValue}`);
29
+ * };
30
+ *
31
+ * const onNameChange: ObserverCallback<string> = (newValue) => {
32
+ * if (newValue.length < 3) {
33
+ * console.warn('Name is too short!');
34
+ * }
35
+ * };
36
+ * ```
37
+ */
38
+ type ObserverCallback<T> = (value: T) => unknown;
39
+ //#endregion
40
+ export { ObserverCallback };
41
+ //# sourceMappingURL=observer.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.types.d.ts","names":[],"sources":["../../src/observer/observer.types.ts"],"mappings":";;AAqCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAAY,gBAAA,OAAuB,KAAA,EAAO,CAAA"}
File without changes
@@ -0,0 +1,4 @@
1
+ import { RateLimitAsyncCounter, RateLimitConfigs, RateLimitCounter, RateLimitable } from "./rate-limit.types.js";
2
+ import { rateLimit } from "./rate-limit.js";
3
+ import { SimpleRateLimitCounter } from "./simple-rate-limit-counter.js";
4
+ export { RateLimitAsyncCounter, RateLimitConfigs, RateLimitCounter, RateLimitable, SimpleRateLimitCounter, rateLimit };
@@ -0,0 +1,3 @@
1
+ import { SimpleRateLimitCounter } from "./simple-rate-limit-counter.js";
2
+ import { rateLimit } from "./rate-limit.js";
3
+ export { SimpleRateLimitCounter, rateLimit };
@@ -0,0 +1,58 @@
1
+ import { Method } from "../types.js";
2
+ import { RateLimitConfigs } from "./rate-limit.types.js";
3
+
4
+ //#region src/rate-limit/rate-limit.d.ts
5
+ /**
6
+ * Decorator that rate limits method calls.
7
+ * Only allows a specified number of calls within a time window.
8
+ *
9
+ * @template T - The type of the class containing the decorated method
10
+ * @param {RateLimitConfigs<T>} config - Rate limit configuration
11
+ * @returns {(
12
+ * target: T,
13
+ * propertyName: keyof T,
14
+ * descriptor: TypedPropertyDescriptor<Method<unknown>>
15
+ * ) => TypedPropertyDescriptor<Method<unknown>>} The decorator function
16
+ *
17
+ * @throws {Error} When applied to a non-method property
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * class Api {
22
+ * @rateLimit({
23
+ * timeSpanMs: 1000, // 1 second
24
+ * allowedCalls: 5, // Max 5 calls
25
+ * exceedHandler: () => console.warn('Rate limit exceeded!')
26
+ * })
27
+ * fetchData() {
28
+ * // Only 5 calls allowed per second
29
+ * }
30
+ *
31
+ * // With custom key resolver for per-user limiting
32
+ * @rateLimit({
33
+ * timeSpanMs: 60000, // 1 minute
34
+ * allowedCalls: 100, // Max 100 calls per user per minute
35
+ * keyResolver: (userId) => userId // Limit per user
36
+ * })
37
+ * getUserData(userId: string) {
38
+ * return database.getUser(userId);
39
+ * }
40
+ * }
41
+ *
42
+ * // With custom counter implementation
43
+ * class DistributedApi {
44
+ * @rateLimit({
45
+ * timeSpanMs: 1000,
46
+ * allowedCalls: 10,
47
+ * rateLimitCounter: new RedisRateLimitCounter() // Distributed counter
48
+ * })
49
+ * async heavyOperation(): Promise<void> {
50
+ * // Rate limited across all instances
51
+ * }
52
+ * }
53
+ * ```
54
+ */
55
+ declare function rateLimit<T = unknown>(config: RateLimitConfigs<T>): (target: T, propertyName: keyof T, descriptor: TypedPropertyDescriptor<Method<unknown>>) => TypedPropertyDescriptor<Method<unknown>>;
56
+ //#endregion
57
+ export { rateLimit };
58
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","names":[],"sources":["../../src/rate-limit/rate-limit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsEgB,SAAA,aAAA,CACd,MAAA,EAAQ,gBAAA,CAAiB,CAAA,KAEzB,MAAA,EAAQ,CAAA,EACR,YAAA,QAAoB,CAAA,EACpB,UAAA,EAAY,uBAAA,CAAwB,MAAA,eACjC,uBAAA,CAAwB,MAAA"}
@@ -0,0 +1,43 @@
1
+ import { Method } from "../types.js";
2
+ import { RateLimitConfigs } from "./rate-limit.types.js";
3
+
4
+ //#region src/rate-limit/rate-limit.fn.d.ts
5
+ /**
6
+ * Creates a rate-limited version of a method.
7
+ *
8
+ * @template D - The return type of the original method
9
+ * @template A - The argument types of the original method
10
+ * @param {Method<D, A>} originalMethod - The method to rate limit
11
+ * @param {RateLimitConfigs} config - The rate limit configuration
12
+ * @returns {Method<D | undefined, A>} A rate-limited method
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * class ApiService {
17
+ * async fetchData(id: string): Promise<Data> {
18
+ * return await fetch(`/api/data/${id}`).then(r => r.json());
19
+ * }
20
+ * }
21
+ *
22
+ * const service = new ApiService();
23
+ *
24
+ * // Rate limit to 3 calls per 5 seconds
25
+ * const limited = rateLimitFn(
26
+ * service.fetchData.bind(service),
27
+ * {
28
+ * timeSpanMs: 5000,
29
+ * allowedCalls: 3,
30
+ * exceedHandler: () => console.warn('Too many requests!')
31
+ * }
32
+ * );
33
+ *
34
+ * await limited('1'); // Executes
35
+ * await limited('2'); // Executes
36
+ * await limited('3'); // Executes
37
+ * const result = await limited('4'); // Returns undefined, logs warning
38
+ * ```
39
+ */
40
+ declare function rateLimitFn<D = unknown, A extends unknown[] = unknown[]>(originalMethod: Method<D, A>, config: RateLimitConfigs): Method<D | undefined, A>;
41
+ //#endregion
42
+ export { rateLimitFn };
43
+ //# sourceMappingURL=rate-limit.fn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.fn.d.ts","names":[],"sources":["../../src/rate-limit/rate-limit.fn.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuDgB,WAAA,8CAAA,CACd,cAAA,EAAgB,MAAA,CAAO,CAAA,EAAG,CAAA,GAC1B,MAAA,EAAQ,gBAAA,GACP,MAAA,CAAO,CAAA,cAAe,CAAA"}
@@ -0,0 +1,56 @@
1
+ import { SimpleRateLimitCounter } from "./simple-rate-limit-counter.js";
2
+ //#region src/rate-limit/rate-limit.fn.ts
3
+ /**
4
+ * Creates a rate-limited version of a method.
5
+ *
6
+ * @template D - The return type of the original method
7
+ * @template A - The argument types of the original method
8
+ * @param {Method<D, A>} originalMethod - The method to rate limit
9
+ * @param {RateLimitConfigs} config - The rate limit configuration
10
+ * @returns {Method<D | undefined, A>} A rate-limited method
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * class ApiService {
15
+ * async fetchData(id: string): Promise<Data> {
16
+ * return await fetch(`/api/data/${id}`).then(r => r.json());
17
+ * }
18
+ * }
19
+ *
20
+ * const service = new ApiService();
21
+ *
22
+ * // Rate limit to 3 calls per 5 seconds
23
+ * const limited = rateLimitFn(
24
+ * service.fetchData.bind(service),
25
+ * {
26
+ * timeSpanMs: 5000,
27
+ * allowedCalls: 3,
28
+ * exceedHandler: () => console.warn('Too many requests!')
29
+ * }
30
+ * );
31
+ *
32
+ * await limited('1'); // Executes
33
+ * await limited('2'); // Executes
34
+ * await limited('3'); // Executes
35
+ * const result = await limited('4'); // Returns undefined, logs warning
36
+ * ```
37
+ */
38
+ function rateLimitFn(originalMethod, config) {
39
+ const counter = config.rateLimitCounter ?? new SimpleRateLimitCounter();
40
+ return function(...args) {
41
+ const key = typeof config.keyResolver === "function" ? config.keyResolver(...args) : "default";
42
+ if (counter.getCount(key) >= config.allowedCalls) {
43
+ config.exceedHandler?.();
44
+ return;
45
+ }
46
+ counter.inc(key);
47
+ setTimeout(() => {
48
+ counter.dec(key);
49
+ }, config.timeSpanMs);
50
+ return originalMethod.apply(this, args);
51
+ };
52
+ }
53
+ //#endregion
54
+ export { rateLimitFn };
55
+
56
+ //# sourceMappingURL=rate-limit.fn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.fn.js","names":[],"sources":["../../src/rate-limit/rate-limit.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';\nimport type { RateLimitConfigs, RateLimitCounter } from './rate-limit.types.js';\nimport { SimpleRateLimitCounter } from './simple-rate-limit-counter.js';\n\n/**\n * Creates a rate-limited version of a method.\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 rate limit\n * @param {RateLimitConfigs} config - The rate limit configuration\n * @returns {Method<D | undefined, A>} A rate-limited method\n *\n * @example\n * ```typescript\n * class ApiService {\n * async fetchData(id: string): Promise<Data> {\n * return await fetch(`/api/data/${id}`).then(r => r.json());\n * }\n * }\n *\n * const service = new ApiService();\n *\n * // Rate limit to 3 calls per 5 seconds\n * const limited = rateLimitFn(\n * service.fetchData.bind(service),\n * {\n * timeSpanMs: 5000,\n * allowedCalls: 3,\n * exceedHandler: () => console.warn('Too many requests!')\n * }\n * );\n *\n * await limited('1'); // Executes\n * await limited('2'); // Executes\n * await limited('3'); // Executes\n * const result = await limited('4'); // Returns undefined, logs warning\n * ```\n */\nexport function rateLimitFn<D = unknown, A extends unknown[] = unknown[]>(\n originalMethod: Method<D, A>,\n config: RateLimitConfigs,\n): Method<D | undefined, A> {\n const counter: RateLimitCounter = config.rateLimitCounter ?? new SimpleRateLimitCounter();\n\n return function (this: unknown, ...args: A): D | undefined {\n const key = typeof config.keyResolver === 'function' ? config.keyResolver(...args) : 'default';\n\n const currentCount = counter.getCount(key);\n\n if (currentCount >= config.allowedCalls) {\n config.exceedHandler?.();\n return undefined;\n }\n\n counter.inc(key);\n\n // Schedule decrement after time span\n setTimeout(() => {\n counter.dec(key);\n }, config.timeSpanMs);\n\n return originalMethod.apply(this, args);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAgB,YACd,gBACA,QAC0B;CAC1B,MAAM,UAA4B,OAAO,oBAAoB,IAAI,wBAAwB;AAEzF,QAAO,SAAyB,GAAG,MAAwB;EACzD,MAAM,MAAM,OAAO,OAAO,gBAAgB,aAAa,OAAO,YAAY,GAAG,KAAK,GAAG;AAIrF,MAFqB,QAAQ,SAAS,IAAI,IAEtB,OAAO,cAAc;AACvC,UAAO,iBAAiB;AACxB;;AAGF,UAAQ,IAAI,IAAI;AAGhB,mBAAiB;AACf,WAAQ,IAAI,IAAI;KACf,OAAO,WAAW;AAErB,SAAO,eAAe,MAAM,MAAM,KAAK"}
@@ -0,0 +1,65 @@
1
+ import { rateLimitFn } from "./rate-limit.fn.js";
2
+ //#region src/rate-limit/rate-limit.ts
3
+ /**
4
+ * Decorator that rate limits method calls.
5
+ * Only allows a specified number of calls within a time window.
6
+ *
7
+ * @template T - The type of the class containing the decorated method
8
+ * @param {RateLimitConfigs<T>} config - Rate limit configuration
9
+ * @returns {(
10
+ * target: T,
11
+ * propertyName: keyof T,
12
+ * descriptor: TypedPropertyDescriptor<Method<unknown>>
13
+ * ) => TypedPropertyDescriptor<Method<unknown>>} The decorator function
14
+ *
15
+ * @throws {Error} When applied to a non-method property
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * class Api {
20
+ * @rateLimit({
21
+ * timeSpanMs: 1000, // 1 second
22
+ * allowedCalls: 5, // Max 5 calls
23
+ * exceedHandler: () => console.warn('Rate limit exceeded!')
24
+ * })
25
+ * fetchData() {
26
+ * // Only 5 calls allowed per second
27
+ * }
28
+ *
29
+ * // With custom key resolver for per-user limiting
30
+ * @rateLimit({
31
+ * timeSpanMs: 60000, // 1 minute
32
+ * allowedCalls: 100, // Max 100 calls per user per minute
33
+ * keyResolver: (userId) => userId // Limit per user
34
+ * })
35
+ * getUserData(userId: string) {
36
+ * return database.getUser(userId);
37
+ * }
38
+ * }
39
+ *
40
+ * // With custom counter implementation
41
+ * class DistributedApi {
42
+ * @rateLimit({
43
+ * timeSpanMs: 1000,
44
+ * allowedCalls: 10,
45
+ * rateLimitCounter: new RedisRateLimitCounter() // Distributed counter
46
+ * })
47
+ * async heavyOperation(): Promise<void> {
48
+ * // Rate limited across all instances
49
+ * }
50
+ * }
51
+ * ```
52
+ */
53
+ function rateLimit(config) {
54
+ return (_target, _propertyName, descriptor) => {
55
+ if (descriptor.value) {
56
+ descriptor.value = rateLimitFn(descriptor.value, config);
57
+ return descriptor;
58
+ }
59
+ throw new Error("@rateLimit is applicable only on methods.");
60
+ };
61
+ }
62
+ //#endregion
63
+ export { rateLimit };
64
+
65
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","names":[],"sources":["../../src/rate-limit/rate-limit.ts"],"sourcesContent":["/**\n * Copyright 2026 ResQ\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Method } from '../types.js';\nimport { rateLimitFn } from './rate-limit.fn.js';\nimport type { RateLimitConfigs } from './rate-limit.types.js';\n\n/**\n * Decorator that rate limits method calls.\n * Only allows a specified number of calls within a time window.\n *\n * @template T - The type of the class containing the decorated method\n * @param {RateLimitConfigs<T>} config - Rate limit configuration\n * @returns {(\n * target: T,\n * propertyName: keyof T,\n * descriptor: TypedPropertyDescriptor<Method<unknown>>\n * ) => TypedPropertyDescriptor<Method<unknown>>} The decorator function\n *\n * @throws {Error} When applied to a non-method property\n *\n * @example\n * ```typescript\n * class Api {\n * @rateLimit({\n * timeSpanMs: 1000, // 1 second\n * allowedCalls: 5, // Max 5 calls\n * exceedHandler: () => console.warn('Rate limit exceeded!')\n * })\n * fetchData() {\n * // Only 5 calls allowed per second\n * }\n *\n * // With custom key resolver for per-user limiting\n * @rateLimit({\n * timeSpanMs: 60000, // 1 minute\n * allowedCalls: 100, // Max 100 calls per user per minute\n * keyResolver: (userId) => userId // Limit per user\n * })\n * getUserData(userId: string) {\n * return database.getUser(userId);\n * }\n * }\n *\n * // With custom counter implementation\n * class DistributedApi {\n * @rateLimit({\n * timeSpanMs: 1000,\n * allowedCalls: 10,\n * rateLimitCounter: new RedisRateLimitCounter() // Distributed counter\n * })\n * async heavyOperation(): Promise<void> {\n * // Rate limited across all instances\n * }\n * }\n * ```\n */\nexport function rateLimit<T = unknown>(\n config: RateLimitConfigs<T>,\n): (\n target: T,\n propertyName: keyof T,\n descriptor: TypedPropertyDescriptor<Method<unknown>>,\n) => TypedPropertyDescriptor<Method<unknown>> {\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 = rateLimitFn(descriptor.value, config);\n return descriptor;\n }\n\n throw new Error('@rateLimit is applicable only on methods.');\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,SAAgB,UACd,QAK4C;AAC5C,SACE,SACA,eACA,eAC6C;AAC7C,MAAI,WAAW,OAAO;AACpB,cAAW,QAAQ,YAAY,WAAW,OAAO,OAAO;AACxD,UAAO;;AAGT,QAAM,IAAI,MAAM,4CAA4C"}