@unshared/decorators 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE.md +21 -0
- package/dist/Debounce.cjs +10 -0
- package/dist/Debounce.cjs.map +1 -0
- package/dist/Debounce.d.ts +30 -0
- package/dist/Debounce.js +11 -0
- package/dist/Debounce.js.map +1 -0
- package/dist/Memoize.cjs +10 -0
- package/dist/Memoize.cjs.map +1 -0
- package/dist/Memoize.d.ts +26 -0
- package/dist/Memoize.js +11 -0
- package/dist/Memoize.js.map +1 -0
- package/dist/Once.cjs +10 -0
- package/dist/Once.cjs.map +1 -0
- package/dist/Once.d.ts +23 -0
- package/dist/Once.js +11 -0
- package/dist/Once.js.map +1 -0
- package/dist/Throttle.cjs +10 -0
- package/dist/Throttle.cjs.map +1 -0
- package/dist/Throttle.d.ts +31 -0
- package/dist/Throttle.js +11 -0
- package/dist/Throttle.js.map +1 -0
- package/dist/index.cjs +11 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Stanley Horwood <stanley@hsjm.io>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
var debounce = require("@unshared/functions/debounce");
|
3
|
+
function Debounce(delay) {
|
4
|
+
return (target, propertyName, descriptor) => {
|
5
|
+
const method = descriptor.value;
|
6
|
+
return descriptor.value = debounce.debounce(method, delay), descriptor;
|
7
|
+
};
|
8
|
+
}
|
9
|
+
exports.Debounce = Debounce;
|
10
|
+
//# sourceMappingURL=Debounce.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Debounce.cjs","sources":["../Debounce.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { debounce } from '@unshared/functions/debounce'\n\n/**\n * Debounce a method so that it will only execute after the specified delay. If the method\n * is called multiple times before the delay has passed, the method will only execute once\n * after the delay has passed. The method will be called with the parameters of the last call.\n *\n * **Note:** This decorator will omit the return value of the method and return `undefined`.\n *\n * @param delay The delay in milliseconds to wait before executing the method.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a debounced method.\n * class Greeter {\n * ->@Debounce(100)\n * greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice')\n * instance.greet('Bob')\n * instance.greet('Charlie')\n *\n * // After 100ms the method will be called with the parameters of the last call.\n * // => Hello, Charlie!\n */\nexport function Debounce<T extends Function<void>>(delay: number): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = debounce(method, delay) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n beforeAll(() => {\n vi.useFakeTimers()\n })\n\n test('should not call the function if the delay has not passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(50)\n expect(fn).not.toHaveBeenCalled()\n })\n\n test('should call the function once after the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should call the function once with the parameters of the last call', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn(name: string) { fn(name) } }\n const instance = new Greeter()\n instance.fn('Alice')\n instance.fn('Bob')\n instance.fn('Charlie')\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledWith('Charlie')\n })\n\n test('should call the function multiple times if the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(3)\n })\n\n test('should return undefined', () => {\n const fn = vi.fn(() => 'foobar')\n class Greeter { @Debounce(10) fn() { return fn() } }\n const instance = new Greeter()\n const result = instance.fn()\n expect(result).toBeUndefined()\n })\n}\n"],"names":["debounce"],"mappings":";;AA4BO,SAAS,SAAmC,OAAmC;AAC7E,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQA,SAAAA,SAAS,QAAQ,KAAK,GAClC;AAAA,EAAA;AAEX;;"}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { Function, MethodDecorator } from '@unshared/types';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Debounce a method so that it will only execute after the specified delay. If the method
|
5
|
+
* is called multiple times before the delay has passed, the method will only execute once
|
6
|
+
* after the delay has passed. The method will be called with the parameters of the last call.
|
7
|
+
*
|
8
|
+
* **Note:** This decorator will omit the return value of the method and return `undefined`.
|
9
|
+
*
|
10
|
+
* @param delay The delay in milliseconds to wait before executing the method.
|
11
|
+
* @returns The method descriptor.
|
12
|
+
* @example
|
13
|
+
* // Declare a class with a debounced method.
|
14
|
+
* class Greeter {
|
15
|
+
* ->@Debounce(100)
|
16
|
+
* greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }
|
17
|
+
* }
|
18
|
+
*
|
19
|
+
* // The first call to the method will be executed.
|
20
|
+
* const instance = new Greeter()
|
21
|
+
* instance.greet('Alice')
|
22
|
+
* instance.greet('Bob')
|
23
|
+
* instance.greet('Charlie')
|
24
|
+
*
|
25
|
+
* // After 100ms the method will be called with the parameters of the last call.
|
26
|
+
* // => Hello, Charlie!
|
27
|
+
*/
|
28
|
+
declare function Debounce<T extends Function<void>>(delay: number): MethodDecorator<T>;
|
29
|
+
|
30
|
+
export { Debounce };
|
package/dist/Debounce.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import { debounce } from "@unshared/functions/debounce";
|
2
|
+
function Debounce(delay) {
|
3
|
+
return (target, propertyName, descriptor) => {
|
4
|
+
const method = descriptor.value;
|
5
|
+
return descriptor.value = debounce(method, delay), descriptor;
|
6
|
+
};
|
7
|
+
}
|
8
|
+
export {
|
9
|
+
Debounce
|
10
|
+
};
|
11
|
+
//# sourceMappingURL=Debounce.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Debounce.js","sources":["../Debounce.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { debounce } from '@unshared/functions/debounce'\n\n/**\n * Debounce a method so that it will only execute after the specified delay. If the method\n * is called multiple times before the delay has passed, the method will only execute once\n * after the delay has passed. The method will be called with the parameters of the last call.\n *\n * **Note:** This decorator will omit the return value of the method and return `undefined`.\n *\n * @param delay The delay in milliseconds to wait before executing the method.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a debounced method.\n * class Greeter {\n * ->@Debounce(100)\n * greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice')\n * instance.greet('Bob')\n * instance.greet('Charlie')\n *\n * // After 100ms the method will be called with the parameters of the last call.\n * // => Hello, Charlie!\n */\nexport function Debounce<T extends Function<void>>(delay: number): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = debounce(method, delay) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n beforeAll(() => {\n vi.useFakeTimers()\n })\n\n test('should not call the function if the delay has not passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(50)\n expect(fn).not.toHaveBeenCalled()\n })\n\n test('should call the function once after the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should call the function once with the parameters of the last call', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn(name: string) { fn(name) } }\n const instance = new Greeter()\n instance.fn('Alice')\n instance.fn('Bob')\n instance.fn('Charlie')\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledWith('Charlie')\n })\n\n test('should call the function multiple times if the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Debounce(10) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(3)\n })\n\n test('should return undefined', () => {\n const fn = vi.fn(() => 'foobar')\n class Greeter { @Debounce(10) fn() { return fn() } }\n const instance = new Greeter()\n const result = instance.fn()\n expect(result).toBeUndefined()\n })\n}\n"],"names":[],"mappings":";AA4BO,SAAS,SAAmC,OAAmC;AAC7E,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQ,SAAS,QAAQ,KAAK,GAClC;AAAA,EAAA;AAEX;"}
|
package/dist/Memoize.cjs
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
var memoize = require("@unshared/functions/memoize");
|
3
|
+
function Memoize(options) {
|
4
|
+
return function(_target, _propertyName, descriptor) {
|
5
|
+
const method = descriptor.value;
|
6
|
+
return descriptor.value = memoize.memoize(method, options), descriptor;
|
7
|
+
};
|
8
|
+
}
|
9
|
+
exports.Memoize = Memoize;
|
10
|
+
//# sourceMappingURL=Memoize.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Memoize.cjs","sources":["../Memoize.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { MemoizeOptions, memoize } from '@unshared/functions/memoize'\n\n/**\n * Decorate a method to memoize it's result based on the arguments. Meaning\n * that it will return the same result without executing the method again,\n * **unless the arguments change**.\n *\n * @param options The memoization options.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a memoized method.\n * class Greeter {\n * ->@Memoize()\n * greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Bob') // 'Hello, Bob! - 0.987654321'\n */\nexport function Memoize<T extends Function>(options?: MemoizeOptions<T>): MethodDecorator<T> {\n return function(_target, _propertyName, descriptor) {\n const method = descriptor.value!\n descriptor.value = memoize(method, options) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n test('should memoize the method', () => {\n const fn = vi.fn(Math.random)\n class MyClass { @Memoize() getId() { return fn() } }\n const instance = new MyClass()\n const id1 = instance.getId()\n const id2 = instance.getId()\n expect(id1).toStrictEqual(id2)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith()\n })\n\n test('should memoize the method by parameter', () => {\n const fn = vi.fn((n = 0) => (n as number) + Math.random())\n class MyClass { @Memoize() getId(n: number) { return fn(n) } }\n const instance = new MyClass()\n const id1 = instance.getId(1)\n const id2 = instance.getId(1)\n const id3 = instance.getId(2)\n expect(id1).toStrictEqual(id2)\n expect(id1).not.toStrictEqual(id3)\n expect(fn).toHaveBeenCalledTimes(2)\n expect(fn).toHaveBeenCalledWith(1)\n expect(fn).toHaveBeenCalledWith(2)\n })\n\n test('should preserve the method context', () => {\n class MyClass { value = { foo: 42 }; @Memoize() getValue() { return this?.value } }\n const instance = new MyClass()\n const result1 = instance.getValue()\n const result2 = instance.value\n expect(result1).toBe(result2)\n })\n}\n"],"names":["memoize"],"mappings":";;AAuBO,SAAS,QAA4B,SAAiD;AACpF,SAAA,SAAS,SAAS,eAAe,YAAY;AAClD,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQA,QAAAA,QAAQ,QAAQ,OAAO,GACnC;AAAA,EAAA;AAEX;;"}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { Function, MethodDecorator } from '@unshared/types';
|
2
|
+
import { MemoizeOptions } from '@unshared/functions/memoize';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Decorate a method to memoize it's result based on the arguments. Meaning
|
6
|
+
* that it will return the same result without executing the method again,
|
7
|
+
* **unless the arguments change**.
|
8
|
+
*
|
9
|
+
* @param options The memoization options.
|
10
|
+
* @returns The method descriptor.
|
11
|
+
* @example
|
12
|
+
* // Declare a class with a memoized method.
|
13
|
+
* class Greeter {
|
14
|
+
* ->@Memoize()
|
15
|
+
* greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }
|
16
|
+
* }
|
17
|
+
*
|
18
|
+
* // The first call to the method will be executed.
|
19
|
+
* const instance = new Greeter()
|
20
|
+
* instance.greet('Alice') // 'Hello, Alice! - 0.123456789'
|
21
|
+
* instance.greet('Alice') // 'Hello, Alice! - 0.123456789'
|
22
|
+
* instance.greet('Bob') // 'Hello, Bob! - 0.987654321'
|
23
|
+
*/
|
24
|
+
declare function Memoize<T extends Function>(options?: MemoizeOptions<T>): MethodDecorator<T>;
|
25
|
+
|
26
|
+
export { Memoize };
|
package/dist/Memoize.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import { memoize } from "@unshared/functions/memoize";
|
2
|
+
function Memoize(options) {
|
3
|
+
return function(_target, _propertyName, descriptor) {
|
4
|
+
const method = descriptor.value;
|
5
|
+
return descriptor.value = memoize(method, options), descriptor;
|
6
|
+
};
|
7
|
+
}
|
8
|
+
export {
|
9
|
+
Memoize
|
10
|
+
};
|
11
|
+
//# sourceMappingURL=Memoize.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Memoize.js","sources":["../Memoize.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { MemoizeOptions, memoize } from '@unshared/functions/memoize'\n\n/**\n * Decorate a method to memoize it's result based on the arguments. Meaning\n * that it will return the same result without executing the method again,\n * **unless the arguments change**.\n *\n * @param options The memoization options.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a memoized method.\n * class Greeter {\n * ->@Memoize()\n * greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Bob') // 'Hello, Bob! - 0.987654321'\n */\nexport function Memoize<T extends Function>(options?: MemoizeOptions<T>): MethodDecorator<T> {\n return function(_target, _propertyName, descriptor) {\n const method = descriptor.value!\n descriptor.value = memoize(method, options) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n test('should memoize the method', () => {\n const fn = vi.fn(Math.random)\n class MyClass { @Memoize() getId() { return fn() } }\n const instance = new MyClass()\n const id1 = instance.getId()\n const id2 = instance.getId()\n expect(id1).toStrictEqual(id2)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith()\n })\n\n test('should memoize the method by parameter', () => {\n const fn = vi.fn((n = 0) => (n as number) + Math.random())\n class MyClass { @Memoize() getId(n: number) { return fn(n) } }\n const instance = new MyClass()\n const id1 = instance.getId(1)\n const id2 = instance.getId(1)\n const id3 = instance.getId(2)\n expect(id1).toStrictEqual(id2)\n expect(id1).not.toStrictEqual(id3)\n expect(fn).toHaveBeenCalledTimes(2)\n expect(fn).toHaveBeenCalledWith(1)\n expect(fn).toHaveBeenCalledWith(2)\n })\n\n test('should preserve the method context', () => {\n class MyClass { value = { foo: 42 }; @Memoize() getValue() { return this?.value } }\n const instance = new MyClass()\n const result1 = instance.getValue()\n const result2 = instance.value\n expect(result1).toBe(result2)\n })\n}\n"],"names":[],"mappings":";AAuBO,SAAS,QAA4B,SAAiD;AACpF,SAAA,SAAS,SAAS,eAAe,YAAY;AAClD,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQ,QAAQ,QAAQ,OAAO,GACnC;AAAA,EAAA;AAEX;"}
|
package/dist/Once.cjs
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
var once = require("@unshared/functions/once");
|
3
|
+
function Once() {
|
4
|
+
return (target, propertyName, descriptor) => {
|
5
|
+
const method = descriptor.value;
|
6
|
+
return descriptor.value = once.once(method), descriptor;
|
7
|
+
};
|
8
|
+
}
|
9
|
+
exports.Once = Once;
|
10
|
+
//# sourceMappingURL=Once.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Once.cjs","sources":["../Once.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { once } from '@unshared/functions/once'\n\n/**\n * Decorate a method to memoize it's result. Meaning that if the method is called,\n * it will return the same result without executing the method again, **even if the\n * method is called with different arguments**.\n *\n * @returns The method descriptor.\n * @example\n * // Declare a class with a onced method.\n * class Greeter {\n * ->@Once()\n * greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Bob') // 'Hello, Alice! - 0.123456789'\n */\nexport function Once<T extends Function>(): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = once(method) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n test('should return the same value if no arguments are passed', () => {\n const fn = vi.fn(Math.random)\n class MyClass { @Once() getId() { return fn() } }\n const instance = new MyClass()\n const id1 = instance.getId()\n const id2 = instance.getId()\n expect(id1).toStrictEqual(id2)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith()\n })\n\n test('should return the same values if different arguments are passed', () => {\n const fn = vi.fn((n = 0) => (n as number) + Math.random())\n class MyClass { @Once() getId(n: number) { return fn(n) } }\n const instance = new MyClass()\n const id1 = instance.getId(1)\n const id2 = instance.getId(1)\n const id3 = instance.getId(2)\n expect(id1).toStrictEqual(id2)\n expect(id1).toStrictEqual(id3)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith(1)\n expect(fn).not.toHaveBeenCalledWith(2)\n })\n\n test('should preserve the method context', () => {\n class MyClass { value = { foo: 42 }; @Once() getValue() { return this?.value } }\n const instance = new MyClass()\n const result1 = instance.getValue()\n const result2 = instance.value\n expect(result1).toBe(result2)\n })\n\n test('should have different results for different instances', () => {\n class MyClass { @Once() getValue() { return Math.random() } }\n const instance1 = new MyClass()\n const instance2 = new MyClass()\n const result1 = instance1.getValue()\n const result2 = instance2.getValue()\n expect(result1).not.toStrictEqual(result2)\n })\n}\n"],"names":["once"],"mappings":";;AAqBO,SAAS,OAA+C;AACtD,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AACf,WAAA,WAAA,QAAQA,KAAK,KAAA,MAAM,GACvB;AAAA,EAAA;AAEX;;"}
|
package/dist/Once.d.ts
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
import { Function, MethodDecorator } from '@unshared/types';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Decorate a method to memoize it's result. Meaning that if the method is called,
|
5
|
+
* it will return the same result without executing the method again, **even if the
|
6
|
+
* method is called with different arguments**.
|
7
|
+
*
|
8
|
+
* @returns The method descriptor.
|
9
|
+
* @example
|
10
|
+
* // Declare a class with a onced method.
|
11
|
+
* class Greeter {
|
12
|
+
* ->@Once()
|
13
|
+
* greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }
|
14
|
+
* }
|
15
|
+
*
|
16
|
+
* // The first call to the method will be executed.
|
17
|
+
* const instance = new Greeter()
|
18
|
+
* instance.greet('Alice') // 'Hello, Alice! - 0.123456789'
|
19
|
+
* instance.greet('Bob') // 'Hello, Alice! - 0.123456789'
|
20
|
+
*/
|
21
|
+
declare function Once<T extends Function>(): MethodDecorator<T>;
|
22
|
+
|
23
|
+
export { Once };
|
package/dist/Once.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import { once } from "@unshared/functions/once";
|
2
|
+
function Once() {
|
3
|
+
return (target, propertyName, descriptor) => {
|
4
|
+
const method = descriptor.value;
|
5
|
+
return descriptor.value = once(method), descriptor;
|
6
|
+
};
|
7
|
+
}
|
8
|
+
export {
|
9
|
+
Once
|
10
|
+
};
|
11
|
+
//# sourceMappingURL=Once.js.map
|
package/dist/Once.js.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Once.js","sources":["../Once.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { once } from '@unshared/functions/once'\n\n/**\n * Decorate a method to memoize it's result. Meaning that if the method is called,\n * it will return the same result without executing the method again, **even if the\n * method is called with different arguments**.\n *\n * @returns The method descriptor.\n * @example\n * // Declare a class with a onced method.\n * class Greeter {\n * ->@Once()\n * greet(name: string) { return `Hello, ${name}! - ${Math.random()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice') // 'Hello, Alice! - 0.123456789'\n * instance.greet('Bob') // 'Hello, Alice! - 0.123456789'\n */\nexport function Once<T extends Function>(): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = once(method) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n test('should return the same value if no arguments are passed', () => {\n const fn = vi.fn(Math.random)\n class MyClass { @Once() getId() { return fn() } }\n const instance = new MyClass()\n const id1 = instance.getId()\n const id2 = instance.getId()\n expect(id1).toStrictEqual(id2)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith()\n })\n\n test('should return the same values if different arguments are passed', () => {\n const fn = vi.fn((n = 0) => (n as number) + Math.random())\n class MyClass { @Once() getId(n: number) { return fn(n) } }\n const instance = new MyClass()\n const id1 = instance.getId(1)\n const id2 = instance.getId(1)\n const id3 = instance.getId(2)\n expect(id1).toStrictEqual(id2)\n expect(id1).toStrictEqual(id3)\n expect(fn).toHaveBeenCalledTimes(1)\n expect(fn).toHaveBeenCalledWith(1)\n expect(fn).not.toHaveBeenCalledWith(2)\n })\n\n test('should preserve the method context', () => {\n class MyClass { value = { foo: 42 }; @Once() getValue() { return this?.value } }\n const instance = new MyClass()\n const result1 = instance.getValue()\n const result2 = instance.value\n expect(result1).toBe(result2)\n })\n\n test('should have different results for different instances', () => {\n class MyClass { @Once() getValue() { return Math.random() } }\n const instance1 = new MyClass()\n const instance2 = new MyClass()\n const result1 = instance1.getValue()\n const result2 = instance2.getValue()\n expect(result1).not.toStrictEqual(result2)\n })\n}\n"],"names":[],"mappings":";AAqBO,SAAS,OAA+C;AACtD,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AACf,WAAA,WAAA,QAAQ,KAAK,MAAM,GACvB;AAAA,EAAA;AAEX;"}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
var throttle = require("@unshared/functions/throttle");
|
3
|
+
function Throttle(delay) {
|
4
|
+
return (target, propertyName, descriptor) => {
|
5
|
+
const method = descriptor.value;
|
6
|
+
return descriptor.value = throttle.throttle(method, delay), descriptor;
|
7
|
+
};
|
8
|
+
}
|
9
|
+
exports.Throttle = Throttle;
|
10
|
+
//# sourceMappingURL=Throttle.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Throttle.cjs","sources":["../Throttle.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { throttle } from '@unshared/functions/throttle'\n\n/**\n * Throttle a method so that it will only execute once every specified delay.\n * Useful for implementing spam protection. When the method is called, it will\n * execute immediately and then wait for the specified delay before it can be\n * called again.\n *\n * **Note:** This decorator will omit the return value of the method and return `undefined`.\n *\n * @param delay The delay in milliseconds to wait before executing the method.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a debounced method.\n * class Greeter {\n * ->@Throttle(100)\n * greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice')\n * instance.greet('Bob')\n * instance.greet('Charlie')\n *\n * // The method will be called immediately and can only be called again after 100ms.\n * // => Hello, Alice!\n */\nexport function Throttle<T extends Function<void>>(delay: number): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = throttle(method, delay) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n beforeEach(() => vi.useFakeTimers)\n\n test('should call the function immediately', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should not call the function more than once within the delay', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should pass the parameters of the first call to the function', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(10) fn(name: string) { fn(name) } }\n const instance = new Greeter()\n instance.fn('Alice')\n instance.fn('Bob')\n instance.fn('Charlie')\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledWith('Alice')\n })\n\n test('should call the function again after the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(2)\n })\n\n test('should return undefined', () => {\n const fn = vi.fn(() => 'foobar')\n class Greeter { @Throttle(100) fn() { return fn() } }\n const instance = new Greeter()\n const result = instance.fn()\n expect(result).toBeUndefined()\n })\n}\n"],"names":["throttle"],"mappings":";;AA6BO,SAAS,SAAmC,OAAmC;AAC7E,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQA,SAAAA,SAAS,QAAQ,KAAK,GAClC;AAAA,EAAA;AAEX;;"}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { Function, MethodDecorator } from '@unshared/types';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Throttle a method so that it will only execute once every specified delay.
|
5
|
+
* Useful for implementing spam protection. When the method is called, it will
|
6
|
+
* execute immediately and then wait for the specified delay before it can be
|
7
|
+
* called again.
|
8
|
+
*
|
9
|
+
* **Note:** This decorator will omit the return value of the method and return `undefined`.
|
10
|
+
*
|
11
|
+
* @param delay The delay in milliseconds to wait before executing the method.
|
12
|
+
* @returns The method descriptor.
|
13
|
+
* @example
|
14
|
+
* // Declare a class with a debounced method.
|
15
|
+
* class Greeter {
|
16
|
+
* ->@Throttle(100)
|
17
|
+
* greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }
|
18
|
+
* }
|
19
|
+
*
|
20
|
+
* // The first call to the method will be executed.
|
21
|
+
* const instance = new Greeter()
|
22
|
+
* instance.greet('Alice')
|
23
|
+
* instance.greet('Bob')
|
24
|
+
* instance.greet('Charlie')
|
25
|
+
*
|
26
|
+
* // The method will be called immediately and can only be called again after 100ms.
|
27
|
+
* // => Hello, Alice!
|
28
|
+
*/
|
29
|
+
declare function Throttle<T extends Function<void>>(delay: number): MethodDecorator<T>;
|
30
|
+
|
31
|
+
export { Throttle };
|
package/dist/Throttle.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import { throttle } from "@unshared/functions/throttle";
|
2
|
+
function Throttle(delay) {
|
3
|
+
return (target, propertyName, descriptor) => {
|
4
|
+
const method = descriptor.value;
|
5
|
+
return descriptor.value = throttle(method, delay), descriptor;
|
6
|
+
};
|
7
|
+
}
|
8
|
+
export {
|
9
|
+
Throttle
|
10
|
+
};
|
11
|
+
//# sourceMappingURL=Throttle.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Throttle.js","sources":["../Throttle.ts"],"sourcesContent":["import { Function, MethodDecorator } from '@unshared/types'\nimport { throttle } from '@unshared/functions/throttle'\n\n/**\n * Throttle a method so that it will only execute once every specified delay.\n * Useful for implementing spam protection. When the method is called, it will\n * execute immediately and then wait for the specified delay before it can be\n * called again.\n *\n * **Note:** This decorator will omit the return value of the method and return `undefined`.\n *\n * @param delay The delay in milliseconds to wait before executing the method.\n * @returns The method descriptor.\n * @example\n * // Declare a class with a debounced method.\n * class Greeter {\n * ->@Throttle(100)\n * greet(name: string) { return `Hello, ${name}! - ${Date.now()}` }\n * }\n *\n * // The first call to the method will be executed.\n * const instance = new Greeter()\n * instance.greet('Alice')\n * instance.greet('Bob')\n * instance.greet('Charlie')\n *\n * // The method will be called immediately and can only be called again after 100ms.\n * // => Hello, Alice!\n */\nexport function Throttle<T extends Function<void>>(delay: number): MethodDecorator<T> {\n return (target, propertyName, descriptor) => {\n const method = descriptor.value!\n descriptor.value = throttle(method, delay) as unknown as T\n return descriptor\n }\n}\n\n/* v8 ignore start */\nif (import.meta.vitest) {\n beforeEach(() => vi.useFakeTimers)\n\n test('should call the function immediately', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should not call the function more than once within the delay', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n instance.fn()\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(1)\n })\n\n test('should pass the parameters of the first call to the function', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(10) fn(name: string) { fn(name) } }\n const instance = new Greeter()\n instance.fn('Alice')\n instance.fn('Bob')\n instance.fn('Charlie')\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledWith('Alice')\n })\n\n test('should call the function again after the delay has passed', () => {\n const fn = vi.fn()\n class Greeter { @Throttle(100) fn() { fn() } }\n const instance = new Greeter()\n instance.fn()\n vi.advanceTimersByTime(100)\n instance.fn()\n vi.advanceTimersByTime(100)\n expect(fn).toHaveBeenCalledTimes(2)\n })\n\n test('should return undefined', () => {\n const fn = vi.fn(() => 'foobar')\n class Greeter { @Throttle(100) fn() { return fn() } }\n const instance = new Greeter()\n const result = instance.fn()\n expect(result).toBeUndefined()\n })\n}\n"],"names":[],"mappings":";AA6BO,SAAS,SAAmC,OAAmC;AAC7E,SAAA,CAAC,QAAQ,cAAc,eAAe;AAC3C,UAAM,SAAS,WAAW;AAC1B,WAAA,WAAW,QAAQ,SAAS,QAAQ,KAAK,GAClC;AAAA,EAAA;AAEX;"}
|
package/dist/index.cjs
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
"use strict";
|
2
|
+
var Debounce = require("./Debounce.cjs"), Memoize = require("./Memoize.cjs"), Once = require("./Once.cjs"), Throttle = require("./Throttle.cjs");
|
3
|
+
require("@unshared/functions/debounce");
|
4
|
+
require("@unshared/functions/memoize");
|
5
|
+
require("@unshared/functions/once");
|
6
|
+
require("@unshared/functions/throttle");
|
7
|
+
exports.Debounce = Debounce.Debounce;
|
8
|
+
exports.Memoize = Memoize.Memoize;
|
9
|
+
exports.Once = Once.Once;
|
10
|
+
exports.Throttle = Throttle.Throttle;
|
11
|
+
//# sourceMappingURL=index.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Debounce } from "./Debounce.js";
|
2
|
+
import { Memoize } from "./Memoize.js";
|
3
|
+
import { Once } from "./Once.js";
|
4
|
+
import { Throttle } from "./Throttle.js";
|
5
|
+
import "@unshared/functions/debounce";
|
6
|
+
import "@unshared/functions/memoize";
|
7
|
+
import "@unshared/functions/once";
|
8
|
+
import "@unshared/functions/throttle";
|
9
|
+
export {
|
10
|
+
Debounce,
|
11
|
+
Memoize,
|
12
|
+
Once,
|
13
|
+
Throttle
|
14
|
+
};
|
15
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
|
package/package.json
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
{
|
2
|
+
"name": "@unshared/decorators",
|
3
|
+
"type": "module",
|
4
|
+
"version": "0.0.1",
|
5
|
+
"license": "MIT",
|
6
|
+
"sideEffects": false,
|
7
|
+
"author": "Stanley Horwood <stanley@hsjm.io>",
|
8
|
+
"bugs": "https://github.com/shorwood/unshared/issues",
|
9
|
+
"homepage": "https://github.com/shorwood/unshared#readme",
|
10
|
+
"repository": {
|
11
|
+
"directory": "packages/decorators",
|
12
|
+
"type": "git",
|
13
|
+
"url": "git+ssh://https://github.com/shorwood/unshared"
|
14
|
+
},
|
15
|
+
"main": "./dist/index.cjs",
|
16
|
+
"module": "./dist/index.js",
|
17
|
+
"types": "./dist/index.d.ts",
|
18
|
+
"exports": {
|
19
|
+
"./Debounce": {
|
20
|
+
"require": "./dist/Debounce.cjs",
|
21
|
+
"types": "./dist/Debounce.d.ts",
|
22
|
+
"import": "./dist/Debounce.js"
|
23
|
+
},
|
24
|
+
"./Memoize": {
|
25
|
+
"require": "./dist/Memoize.cjs",
|
26
|
+
"types": "./dist/Memoize.d.ts",
|
27
|
+
"import": "./dist/Memoize.js"
|
28
|
+
},
|
29
|
+
"./Once": {
|
30
|
+
"require": "./dist/Once.cjs",
|
31
|
+
"types": "./dist/Once.d.ts",
|
32
|
+
"import": "./dist/Once.js"
|
33
|
+
},
|
34
|
+
"./Throttle": {
|
35
|
+
"require": "./dist/Throttle.cjs",
|
36
|
+
"types": "./dist/Throttle.d.ts",
|
37
|
+
"import": "./dist/Throttle.js"
|
38
|
+
},
|
39
|
+
".": {
|
40
|
+
"require": "./dist/index.cjs",
|
41
|
+
"types": "./dist/index.d.ts",
|
42
|
+
"import": "./dist/index.js"
|
43
|
+
}
|
44
|
+
},
|
45
|
+
"files": [
|
46
|
+
"dist",
|
47
|
+
"README.md",
|
48
|
+
"LICENSE.md"
|
49
|
+
],
|
50
|
+
"dependencies": {
|
51
|
+
"@unshared/functions": "0.0.1",
|
52
|
+
"@unshared/types": "0.0.1"
|
53
|
+
}
|
54
|
+
}
|