@unshared/decorators 0.0.1

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/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 };
@@ -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;"}
@@ -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 };
@@ -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
@@ -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 };
@@ -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":";;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ export { Debounce } from './Debounce.js';
2
+ export { Memoize } from './Memoize.js';
3
+ export { Once } from './Once.js';
4
+ export { Throttle } from './Throttle.js';
5
+ import '@unshared/types';
6
+ import '@unshared/functions/memoize';
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
+ }