@resq-sw/decorators 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -0
- package/lib/_utils.d.ts +46 -0
- package/lib/_utils.d.ts.map +1 -0
- package/lib/_utils.js +91 -0
- package/lib/_utils.js.map +1 -0
- package/lib/after/after.d.ts +60 -0
- package/lib/after/after.d.ts.map +1 -0
- package/lib/after/after.fn.d.ts +39 -0
- package/lib/after/after.fn.d.ts.map +1 -0
- package/lib/after/after.fn.js +59 -0
- package/lib/after/after.fn.js.map +1 -0
- package/lib/after/after.js +41 -0
- package/lib/after/after.js.map +1 -0
- package/lib/after/after.types.d.ts +86 -0
- package/lib/after/after.types.d.ts.map +1 -0
- package/lib/after/after.types.js +0 -0
- package/lib/after/index.d.ts +3 -0
- package/lib/after/index.js +2 -0
- package/lib/before/before.d.ts +61 -0
- package/lib/before/before.d.ts.map +1 -0
- package/lib/before/before.fn.d.ts +39 -0
- package/lib/before/before.fn.d.ts.map +1 -0
- package/lib/before/before.fn.js +51 -0
- package/lib/before/before.fn.js.map +1 -0
- package/lib/before/before.js +40 -0
- package/lib/before/before.js.map +1 -0
- package/lib/before/before.types.d.ts +48 -0
- package/lib/before/before.types.d.ts.map +1 -0
- package/lib/before/before.types.js +0 -0
- package/lib/before/index.d.ts +3 -0
- package/lib/before/index.js +2 -0
- package/lib/bind/bind.d.ts +75 -0
- package/lib/bind/bind.d.ts.map +1 -0
- package/lib/bind/bind.fn.d.ts +46 -0
- package/lib/bind/bind.fn.d.ts.map +1 -0
- package/lib/bind/bind.fn.js +39 -0
- package/lib/bind/bind.fn.js.map +1 -0
- package/lib/bind/bind.js +64 -0
- package/lib/bind/bind.js.map +1 -0
- package/lib/bind/bind.types.d.ts +36 -0
- package/lib/bind/bind.types.d.ts.map +1 -0
- package/lib/bind/bind.types.js +0 -0
- package/lib/bind/index.d.ts +3 -0
- package/lib/bind/index.js +2 -0
- package/lib/debounce/debounce.d.ts +34 -0
- package/lib/debounce/debounce.d.ts.map +1 -0
- package/lib/debounce/debounce.fn.d.ts +40 -0
- package/lib/debounce/debounce.fn.d.ts.map +1 -0
- package/lib/debounce/debounce.fn.js +47 -0
- package/lib/debounce/debounce.fn.js.map +1 -0
- package/lib/debounce/debounce.js +48 -0
- package/lib/debounce/debounce.js.map +1 -0
- package/lib/debounce/index.d.ts +2 -0
- package/lib/debounce/index.js +2 -0
- package/lib/delay/delay.d.ts +35 -0
- package/lib/delay/delay.d.ts.map +1 -0
- package/lib/delay/delay.fn.d.ts +33 -0
- package/lib/delay/delay.fn.d.ts.map +1 -0
- package/lib/delay/delay.fn.js +38 -0
- package/lib/delay/delay.fn.js.map +1 -0
- package/lib/delay/delay.js +43 -0
- package/lib/delay/delay.js.map +1 -0
- package/lib/delay/index.d.ts +2 -0
- package/lib/delay/index.js +2 -0
- package/lib/delegate/delegate.d.ts +48 -0
- package/lib/delegate/delegate.d.ts.map +1 -0
- package/lib/delegate/delegate.fn.d.ts +57 -0
- package/lib/delegate/delegate.fn.d.ts.map +1 -0
- package/lib/delegate/delegate.fn.js +55 -0
- package/lib/delegate/delegate.fn.js.map +1 -0
- package/lib/delegate/delegate.js +56 -0
- package/lib/delegate/delegate.js.map +1 -0
- package/lib/delegate/delegate.types.d.ts +45 -0
- package/lib/delegate/delegate.types.d.ts.map +1 -0
- package/lib/delegate/delegate.types.js +0 -0
- package/lib/delegate/index.d.ts +3 -0
- package/lib/delegate/index.js +2 -0
- package/lib/exec-time/exec-time.d.ts +42 -0
- package/lib/exec-time/exec-time.d.ts.map +1 -0
- package/lib/exec-time/exec-time.fn.d.ts +50 -0
- package/lib/exec-time/exec-time.fn.d.ts.map +1 -0
- package/lib/exec-time/exec-time.fn.js +91 -0
- package/lib/exec-time/exec-time.fn.js.map +1 -0
- package/lib/exec-time/exec-time.js +55 -0
- package/lib/exec-time/exec-time.js.map +1 -0
- package/lib/exec-time/exec-time.types.d.ts +70 -0
- package/lib/exec-time/exec-time.types.d.ts.map +1 -0
- package/lib/exec-time/exec-time.types.js +0 -0
- package/lib/exec-time/index.d.ts +4 -0
- package/lib/exec-time/index.js +3 -0
- package/lib/execute/execute.d.ts +78 -0
- package/lib/execute/execute.d.ts.map +1 -0
- package/lib/execute/execute.js +82 -0
- package/lib/execute/execute.js.map +1 -0
- package/lib/execute/index.d.ts +2 -0
- package/lib/execute/index.js +2 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.js +19 -0
- package/lib/memoize/index.d.ts +3 -0
- package/lib/memoize/index.js +2 -0
- package/lib/memoize/memoize.d.ts +67 -0
- package/lib/memoize/memoize.d.ts.map +1 -0
- package/lib/memoize/memoize.fn.d.ts +69 -0
- package/lib/memoize/memoize.fn.d.ts.map +1 -0
- package/lib/memoize/memoize.fn.js +43 -0
- package/lib/memoize/memoize.fn.js.map +1 -0
- package/lib/memoize/memoize.js +40 -0
- package/lib/memoize/memoize.js.map +1 -0
- package/lib/memoize/memoize.types.d.ts +107 -0
- package/lib/memoize/memoize.types.d.ts.map +1 -0
- package/lib/memoize/memoize.types.js +0 -0
- package/lib/memoize-async/index.d.ts +4 -0
- package/lib/memoize-async/index.js +3 -0
- package/lib/memoize-async/memoize-async.d.ts +68 -0
- package/lib/memoize-async/memoize-async.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.fn.d.ts +69 -0
- package/lib/memoize-async/memoize-async.fn.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.fn.js +52 -0
- package/lib/memoize-async/memoize-async.fn.js.map +1 -0
- package/lib/memoize-async/memoize-async.js +15 -0
- package/lib/memoize-async/memoize-async.js.map +1 -0
- package/lib/memoize-async/memoize-async.types.d.ts +74 -0
- package/lib/memoize-async/memoize-async.types.d.ts.map +1 -0
- package/lib/memoize-async/memoize-async.types.js +0 -0
- package/lib/observer/index.d.ts +3 -0
- package/lib/observer/index.js +2 -0
- package/lib/observer/observer.d.ts +54 -0
- package/lib/observer/observer.d.ts.map +1 -0
- package/lib/observer/observer.js +85 -0
- package/lib/observer/observer.js.map +1 -0
- package/lib/observer/observer.types.d.ts +41 -0
- package/lib/observer/observer.types.d.ts.map +1 -0
- package/lib/observer/observer.types.js +0 -0
- package/lib/rate-limit/index.d.ts +4 -0
- package/lib/rate-limit/index.js +3 -0
- package/lib/rate-limit/rate-limit.d.ts +58 -0
- package/lib/rate-limit/rate-limit.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.fn.d.ts +43 -0
- package/lib/rate-limit/rate-limit.fn.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.fn.js +56 -0
- package/lib/rate-limit/rate-limit.fn.js.map +1 -0
- package/lib/rate-limit/rate-limit.js +65 -0
- package/lib/rate-limit/rate-limit.js.map +1 -0
- package/lib/rate-limit/rate-limit.types.d.ts +148 -0
- package/lib/rate-limit/rate-limit.types.d.ts.map +1 -0
- package/lib/rate-limit/rate-limit.types.js +0 -0
- package/lib/rate-limit/simple-rate-limit-counter.d.ts +89 -0
- package/lib/rate-limit/simple-rate-limit-counter.d.ts.map +1 -0
- package/lib/rate-limit/simple-rate-limit-counter.js +98 -0
- package/lib/rate-limit/simple-rate-limit-counter.js.map +1 -0
- package/lib/readonly/index.d.ts +3 -0
- package/lib/readonly/index.js +2 -0
- package/lib/readonly/readonly.d.ts +39 -0
- package/lib/readonly/readonly.d.ts.map +1 -0
- package/lib/readonly/readonly.js +43 -0
- package/lib/readonly/readonly.js.map +1 -0
- package/lib/readonly/readonly.types.d.ts +40 -0
- package/lib/readonly/readonly.types.d.ts.map +1 -0
- package/lib/readonly/readonly.types.js +0 -0
- package/lib/throttle/index.d.ts +2 -0
- package/lib/throttle/index.js +2 -0
- package/lib/throttle/throttle.d.ts +35 -0
- package/lib/throttle/throttle.d.ts.map +1 -0
- package/lib/throttle/throttle.fn.d.ts +42 -0
- package/lib/throttle/throttle.fn.d.ts.map +1 -0
- package/lib/throttle/throttle.fn.js +52 -0
- package/lib/throttle/throttle.fn.js.map +1 -0
- package/lib/throttle/throttle.js +43 -0
- package/lib/throttle/throttle.js.map +1 -0
- package/lib/throttle-async/index.d.ts +2 -0
- package/lib/throttle-async/index.js +2 -0
- package/lib/throttle-async/throttle-async-executor.d.ts +79 -0
- package/lib/throttle-async/throttle-async-executor.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async-executor.js +122 -0
- package/lib/throttle-async/throttle-async-executor.js.map +1 -0
- package/lib/throttle-async/throttle-async.d.ts +68 -0
- package/lib/throttle-async/throttle-async.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async.fn.d.ts +41 -0
- package/lib/throttle-async/throttle-async.fn.d.ts.map +1 -0
- package/lib/throttle-async/throttle-async.fn.js +46 -0
- package/lib/throttle-async/throttle-async.fn.js.map +1 -0
- package/lib/throttle-async/throttle-async.js +45 -0
- package/lib/throttle-async/throttle-async.js.map +1 -0
- package/lib/types.d.ts +81 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +0 -0
- package/package.json +40 -0
|
@@ -0,0 +1,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,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,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"}
|