@byloth/core 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +1 -0
  2. package/dist/core.js +18 -8
  3. package/dist/core.js.map +1 -1
  4. package/dist/core.umd.cjs +1 -1
  5. package/dist/core.umd.cjs.map +1 -1
  6. package/package.json +10 -8
  7. package/src/core/types.ts +43 -10
  8. package/src/index.ts +3 -2
  9. package/src/models/aggregators/aggregated-async-iterator.ts +5 -0
  10. package/src/models/aggregators/aggregated-iterator.ts +5 -0
  11. package/src/models/aggregators/reduced-iterator.ts +18 -5
  12. package/src/models/aggregators/types.ts +35 -0
  13. package/src/models/callbacks/callable-object.ts +7 -0
  14. package/src/models/callbacks/publisher.ts +12 -8
  15. package/src/models/callbacks/switchable-callback.ts +9 -1
  16. package/src/models/callbacks/types.ts +32 -0
  17. package/src/models/exceptions/core.ts +9 -0
  18. package/src/models/exceptions/index.ts +40 -1
  19. package/src/models/iterators/smart-async-iterator.ts +5 -0
  20. package/src/models/iterators/smart-iterator.ts +5 -0
  21. package/src/models/iterators/types.ts +79 -1
  22. package/src/models/json/json-storage.ts +3 -0
  23. package/src/models/json/types.ts +1 -1
  24. package/src/models/promises/deferred-promise.ts +5 -0
  25. package/src/models/promises/smart-promise.ts +5 -0
  26. package/src/models/promises/timed-promise.ts +5 -0
  27. package/src/models/promises/types.ts +30 -0
  28. package/src/models/timers/clock.ts +3 -0
  29. package/src/models/timers/countdown.ts +5 -0
  30. package/src/models/timers/game-loop.ts +3 -0
  31. package/src/models/types.ts +1 -1
  32. package/src/utils/async.ts +15 -0
  33. package/src/utils/curve.ts +1 -1
  34. package/src/utils/date.ts +36 -6
  35. package/src/utils/dom.ts +5 -0
  36. package/src/utils/iterator.ts +40 -0
  37. package/src/utils/math.ts +15 -0
  38. package/src/utils/random.ts +4 -0
  39. package/src/utils/string.ts +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byloth/core",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧",
5
5
  "keywords": [
6
6
  "Core",
@@ -48,14 +48,15 @@
48
48
  "types": "src/index.ts",
49
49
  "devDependencies": {
50
50
  "@byloth/eslint-config-typescript": "^3.1.0",
51
- "@eslint/compat": "^1.2.7",
52
- "@types/node": "^22.13.11",
53
- "eslint": "^9.23.0",
51
+ "@eslint/compat": "^1.2.8",
52
+ "@types/node": "^22.14.1",
53
+ "@vitest/coverage-v8": "^3.1.1",
54
+ "eslint": "^9.25.0",
54
55
  "husky": "^9.1.7",
55
- "jsdom": "^26.0.0",
56
- "typescript": "^5.8.2",
57
- "vite": "^6.2.2",
58
- "vitest": "^3.0.9"
56
+ "jsdom": "^26.1.0",
57
+ "typescript": "^5.8.3",
58
+ "vite": "^6.3.2",
59
+ "vitest": "^3.1.1"
59
60
  },
60
61
  "scripts": {
61
62
  "dev": "vite",
@@ -64,6 +65,7 @@
64
65
  "typecheck": "tsc",
65
66
  "lint": "eslint .",
66
67
  "test": "vitest run",
68
+ "test:coverage": "vitest run --coverage",
67
69
  "ci": "pnpm install --frozen-lockfile"
68
70
  }
69
71
  }
package/src/core/types.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  /**
2
- * A utility type that allows to define a class constructor of a specific type.
3
- * Is the counterpart of the native `InstanceType` utility type.
2
+ * An utility type that allows to define a class constructor of a specific type.
3
+ * Is the counterpart of the native {@link InstanceType} utility type.
4
4
  *
5
+ * ---
6
+ *
7
+ * @example
5
8
  * ```ts
6
9
  * function factory<T extends object>(Factory: Constructor<T>): T { [...] }
7
10
  *
@@ -12,14 +15,17 @@
12
15
  export type Constructor<T extends object, P extends unknown[] = any[]> = new (...args: P) => T;
13
16
 
14
17
  /**
15
- * A type that represents the return value of `setInterval` function,
18
+ * A type that represents the return value of {@link setInterval} function,
16
19
  * indipendently from the platform it's currently running on.
17
20
  *
18
- * For instance, in a browser environment, it's a `number` value representing the interval ID.
19
- * In a Node.js environment, on the other hand, it's an object of type `NodeJS.Timeout`.
21
+ * For instance, in a browser environment, it's a {@link Number} value representing the interval ID.
22
+ * In a Node.js environment, on the other hand, it's an object of type {@link NodeJS.Timeout}.
23
+ *
24
+ * This allows to seamlessly use the same code in both environments, without having to deal with the differences.
20
25
  *
21
- * This allows to seamlessly use the same code in both environments, without having to deal with the differences:
26
+ * ---
22
27
  *
28
+ * @example
23
29
  * ```ts
24
30
  * const intervalId: Interval = setInterval(() => { [...] }, 1_000);
25
31
  *
@@ -29,14 +35,17 @@ export type Constructor<T extends object, P extends unknown[] = any[]> = new (..
29
35
  export type Interval = ReturnType<typeof setInterval>;
30
36
 
31
37
  /**
32
- * A type that represents the return value of `setTimeout` function,
38
+ * A type that represents the return value of {@link setTimeout} function,
33
39
  * indipendently from the platform it's currently running on.
34
40
  *
35
- * For instance, in a browser environment, it's a `number` value representing the timeout ID.
36
- * In a Node.js environment, on the other hand, it's an object of type `NodeJS.Timeout`.
41
+ * For instance, in a browser environment, it's a {@link Number} value representing the timeout ID.
42
+ * In a Node.js environment, on the other hand, it's an object of type {@link NodeJS.Timeout}.
37
43
  *
38
- * This allows to seamlessly use the same code in both environments, without having to deal with the differences:
44
+ * This allows to seamlessly use the same code in both environments, without having to deal with the differences.
39
45
  *
46
+ * ---
47
+ *
48
+ * @example
40
49
  * ```ts
41
50
  * const timeoutId: Timeout = setTimeout(() => { [...] }, 1_000);
42
51
  *
@@ -44,3 +53,27 @@ export type Interval = ReturnType<typeof setInterval>;
44
53
  * ```
45
54
  */
46
55
  export type Timeout = ReturnType<typeof setTimeout>;
56
+
57
+ /**
58
+ * An utility type that allows to extract the union of the values of a given type.
59
+ * It can be used to extract the values of all the properties of an object type.
60
+ *
61
+ * ---
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * class MyObject
66
+ * {
67
+ * protected secret = "Sssh! That's a secret!";
68
+ * public answer = 42;
69
+ * public greet() { console.log("Hello, world!"); }
70
+ * }
71
+ *
72
+ * type MyObjectProperties = ValueOf<MyObject>; // number | (() => void)
73
+ * ```
74
+ *
75
+ * ---
76
+ *
77
+ * @template T The type to extract the values from.
78
+ */
79
+ export type ValueOf<T> = T[keyof T];
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- export const VERSION = "2.0.1";
1
+ export const VERSION = "2.0.2";
2
2
 
3
- export type { Constructor, Interval, Timeout } from "./core/types.js";
3
+ export type { Constructor, Interval, Timeout, ValueOf } from "./core/types.js";
4
4
 
5
5
  export { isBrowser, isNode, isWorker } from "./helpers.js";
6
6
 
@@ -47,6 +47,7 @@ export type {
47
47
  AsyncKeyedReducer,
48
48
  AsyncReducer,
49
49
  Callback,
50
+ CallbackMap,
50
51
  FulfilledHandler,
51
52
  GeneratorFunction,
52
53
  Iteratee,
@@ -30,6 +30,9 @@ import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js
30
30
  * This is particularly useful when you need to group elements and
31
31
  * then perform specific operations on the groups themselves.
32
32
  *
33
+ * ---
34
+ *
35
+ * @example
33
36
  * ```ts
34
37
  * const elements = fetch([...]); // Promise<[-3, -1, 0, 2, 3, 5, 6, 8]>;
35
38
  * const results = new SmartAsyncIterator(elements)
@@ -39,6 +42,8 @@ import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js
39
42
  * console.log(await results.toObject()); // { odd: 4, even: 4 }
40
43
  * ```
41
44
  *
45
+ * ---
46
+ *
42
47
  * @template K The type of the keys used to group the elements.
43
48
  * @template T The type of the elements to aggregate.
44
49
  */
@@ -22,6 +22,9 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ
22
22
  * This is particularly useful when you need to group elements and
23
23
  * then perform specific operations on the groups themselves.
24
24
  *
25
+ * ---
26
+ *
27
+ * @example
25
28
  * ```ts
26
29
  * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
27
30
  * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
@@ -30,6 +33,8 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ
30
33
  * console.log(results.toObject()); // { odd: 4, even: 4 }
31
34
  * ```
32
35
  *
36
+ * ---
37
+ *
33
38
  * @template K The type of the keys used to group the elements.
34
39
  * @template T The type of the elements to aggregate.
35
40
  */
@@ -24,6 +24,9 @@ import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./typ
24
24
  * This is particularly useful when you have group elements and
25
25
  * need perform specific operations on the reduced elements.
26
26
  *
27
+ * ---
28
+ *
29
+ * @example
27
30
  * ```ts
28
31
  * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
29
32
  * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
@@ -32,6 +35,8 @@ import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./typ
32
35
  * console.log(results.toObject()); // { odd: 4, even: 4 }
33
36
  * ```
34
37
  *
38
+ * ---
39
+ *
35
40
  * @template K The type of the key used to group the elements.
36
41
  * @template T The type of the elements in the iterator.
37
42
  */
@@ -508,7 +513,9 @@ export default class ReducedIterator<K extends PropertyKey, T>
508
513
  *
509
514
  * console.log(results.toObject()); // { even: [0, 2, 6, 8] }
510
515
  * ```
511
- *
516
+ *
517
+ * ---
518
+ *
512
519
  * @param count The number of elements to drop.
513
520
  *
514
521
  * @returns A new {@link ReducedIterator} containing the remaining elements.
@@ -597,9 +604,12 @@ export default class ReducedIterator<K extends PropertyKey, T>
597
604
  * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
598
605
  * .reduce((key, accumulator, value) => accumulator + value)
599
606
  * .find((key, value) => value > 0);
600
- *
607
+ *
601
608
  * console.log(results); // 16
602
- *
609
+ * ```
610
+ *
611
+ * ---
612
+ *
603
613
  * @param predicate The condition to check for each element of the iterator.
604
614
  *
605
615
  * @returns The first element that satisfies the condition, `undefined` otherwise.
@@ -628,8 +638,11 @@ export default class ReducedIterator<K extends PropertyKey, T>
628
638
  * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
629
639
  * .reduce((key, accumulator, value) => accumulator + value)
630
640
  * .find<number>((key, value) => typeof value === "number");
631
- *
641
+ *
632
642
  * console.log(results); // 16
643
+ * ```
644
+ *
645
+ * ---
633
646
  *
634
647
  * @template S
635
648
  * The type of the elements that satisfy the condition.
@@ -771,7 +784,7 @@ export default class ReducedIterator<K extends PropertyKey, T>
771
784
  * const reduced = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
772
785
  * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
773
786
  * .reduce((key, accumulator, value) => accumulator + value);
774
- *
787
+ *
775
788
  * reduced.forEach((key, value, index) =>
776
789
  * {
777
790
  * console.log(`#${index} - ${key}: ${value}`); // "#0 - odd: 4", "#1 - even: 16"
@@ -5,6 +5,9 @@ import type { MaybePromise } from "../promises/types.js";
5
5
  * with the addition of a `key` parameter, compared to the JavaScript's standard ones.
6
6
  * It can be used to transform the elements of an aggregated iterable.
7
7
  *
8
+ * ---
9
+ *
10
+ * @example
8
11
  * ```ts
9
12
  * const iteratee: KeyedIteratee<string, number, string> = (key: string, value: number) => `${value}`;
10
13
  * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
@@ -14,6 +17,8 @@ import type { MaybePromise } from "../promises/types.js";
14
17
  * console.log(results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
15
18
  * ```
16
19
  *
20
+ * ---
21
+ *
17
22
  * @template K The type of the key used to aggregate elements in the iterable.
18
23
  * @template T The type of the elements in the iterable.
19
24
  * @template R The type of the return value of the iteratee. Default is `void`.
@@ -25,6 +30,9 @@ export type KeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, value:
25
30
  * function with the addition of a `key` parameter.
26
31
  * It can be used to transform the elements of an aggregated iterable asynchronously.
27
32
  *
33
+ * ---
34
+ *
35
+ * @example
28
36
  * ```ts
29
37
  * const iteratee: AsyncKeyedIteratee<string, number, string> = async (key: string, value: number) => `${value}`;
30
38
  * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
@@ -34,6 +42,8 @@ export type KeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, value:
34
42
  * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
35
43
  * ```
36
44
  *
45
+ * ---
46
+ *
37
47
  * @template K The type of the key used to aggregate elements in the iterable.
38
48
  * @template T The type of the elements in the iterable.
39
49
  * @template R The type of the return value of the iteratee. Default is `void`.
@@ -45,6 +55,9 @@ export type AsyncKeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, va
45
55
  * with the addition of a `key` parameter that can be either synchronous or asynchronous.
46
56
  * It can be used to transform the elements of an aggregated iterable.
47
57
  *
58
+ * ---
59
+ *
60
+ * @example
48
61
  * ```ts
49
62
  * const iteratee: AsyncKeyedIteratee<string, number, string> = [async] (key: string, value: number) => `${value}`;
50
63
  * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
@@ -54,6 +67,8 @@ export type AsyncKeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, va
54
67
  * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
55
68
  * ```
56
69
  *
70
+ * ---
71
+ *
57
72
  * @template K The type of the key used to aggregate elements in the iterable.
58
73
  * @template T The type of the elements in the iterable.
59
74
  * @template R The type of the return value of the iteratee. Default is `void`.
@@ -69,6 +84,9 @@ export type MaybeAsyncKeyedIteratee<K extends PropertyKey, T, R = void> =
69
84
  * It can be used to filter the elements of an aggregated iterable
70
85
  * while allowing the type-system to infer them correctly.
71
86
  *
87
+ * ---
88
+ *
89
+ * @example
72
90
  * ```ts
73
91
  * const predicate: KeyedTypeGuardPredicate<string, number | string, string> =
74
92
  * (key: string, value: number | string): value is string => typeof value === "string";
@@ -80,6 +98,8 @@ export type MaybeAsyncKeyedIteratee<K extends PropertyKey, T, R = void> =
80
98
  * console.log(results.toObject()); // { odd: ["0", "5", "8"], even: [] }
81
99
  * ```
82
100
  *
101
+ * ---
102
+ *
83
103
  * @template K The type of the key used to aggregate elements in the iterable.
84
104
  * @template T The type of the elements in the iterable.
85
105
  * @template R
@@ -100,6 +120,9 @@ export type KeyedTypeGuardPredicate<K extends PropertyKey, T, R extends T> =
100
120
  * An utility type that represents a reducer-like function.
101
121
  * It can be used to reduce the elements of an aggregated iterable into a single value.
102
122
  *
123
+ * ---
124
+ *
125
+ * @example
103
126
  * ```ts
104
127
  * const sum: KeyedReducer<string, number, number> =
105
128
  * (key: string, accumulator: number, value: number) => accumulator + value;
@@ -111,6 +134,8 @@ export type KeyedTypeGuardPredicate<K extends PropertyKey, T, R extends T> =
111
134
  * console.log(results.toObject()); // { odd: 4, even: 16 }
112
135
  * ```
113
136
  *
137
+ * ---
138
+ *
114
139
  * @template K The type of the key used to aggregate elements in the iterable.
115
140
  * @template T The type of the elements in the iterable.
116
141
  * @template A The type of the accumulator.
@@ -121,6 +146,9 @@ export type KeyedReducer<K extends PropertyKey, T, A> = (key: K, accumulator: A,
121
146
  * An utility type that represents an asynchronous reducer-like function.
122
147
  * It can be used to reduce the elements of an aggregated iterable into a single value.
123
148
  *
149
+ * ---
150
+ *
151
+ * @example
124
152
  * ```ts
125
153
  * const sum: AsyncKeyedReducer<string, number, number> =
126
154
  * async (key: string, accumulator: number, value: number) => accumulator + value;
@@ -132,6 +160,8 @@ export type KeyedReducer<K extends PropertyKey, T, A> = (key: K, accumulator: A,
132
160
  * console.log(await results.toObject()); // { odd: 4, even: 16 }
133
161
  * ```
134
162
  *
163
+ * ---
164
+ *
135
165
  * @template K The type of the key used to aggregate elements in the iterable.
136
166
  * @template T The type of the elements in the iterable.
137
167
  * @template A The type of the accumulator.
@@ -143,6 +173,9 @@ export type AsyncKeyedReducer<K extends PropertyKey, T, A> =
143
173
  * An utility type that represents a reducer-like function that can be either synchronous or asynchronous.
144
174
  * It can be used to reduce the elements of an aggregated iterable into a single value.
145
175
  *
176
+ * ---
177
+ *
178
+ * @example
146
179
  * ```ts
147
180
  * const sum: MaybeAsyncKeyedReducer<string, number, number> =
148
181
  * [async] (key: string, accumulator: number, value: number) => accumulator + value;
@@ -154,6 +187,8 @@ export type AsyncKeyedReducer<K extends PropertyKey, T, A> =
154
187
  * console.log(await results.toObject()); // { odd: 4, even: 16 }
155
188
  * ```
156
189
  *
190
+ * ---
191
+ *
157
192
  * @template K The type of the key used to aggregate elements in the iterable.
158
193
  * @template T The type of the elements in the iterable.
159
194
  * @template A The type of the accumulator.
@@ -8,6 +8,9 @@ const SmartFunction = (Function as unknown) as new<A extends unknown[] = [], R =
8
8
  /**
9
9
  * An abstract class that can be used to implement callable objects.
10
10
  *
11
+ * ---
12
+ *
13
+ * @example
11
14
  * ```ts
12
15
  * class ActivableCallback extends CallableObject<(evt: PointerEvent) => void>
13
16
  * {
@@ -25,6 +28,8 @@ const SmartFunction = (Function as unknown) as new<A extends unknown[] = [], R =
25
28
  * window.addEventListener("pointerup", () => { callback.enabled = false; });
26
29
  * ```
27
30
  *
31
+ * ---
32
+ *
28
33
  * @template T
29
34
  * The type signature of the callback function.
30
35
  * It must be a function. Default is `(...args: any[]) => any`.
@@ -49,6 +54,8 @@ export default abstract class CallableObject<T extends Callback<any[], any> = ()
49
54
  * The method that will be called when the object is invoked.
50
55
  * It must be implemented by the derived classes.
51
56
  *
57
+ * ---
58
+ *
52
59
  * @param args The arguments that have been passed to the object.
53
60
  *
54
61
  * @returns The return value of the method.
@@ -1,6 +1,6 @@
1
1
  import { ReferenceException } from "../exceptions/index.js";
2
2
 
3
- import type { Callback } from "./types.js";
3
+ import type { Callback, CallbackMap } from "./types.js";
4
4
 
5
5
  /**
6
6
  * A class implementing the
@@ -12,6 +12,9 @@ import type { Callback } from "./types.js";
12
12
  *
13
13
  * Using generics, it's also possible to define the type of the events and the callbacks that can be subscribed to them.
14
14
  *
15
+ * ---
16
+ *
17
+ * @example
15
18
  * ```ts
16
19
  * interface EventsMap
17
20
  * {
@@ -30,13 +33,14 @@ import type { Callback } from "./types.js";
30
33
  * });
31
34
  * ```
32
35
  *
36
+ * ---
37
+ *
33
38
  * @template T
34
39
  * A map containing the names of the emittable events and the
35
40
  * related callback signatures that can be subscribed to them.
36
- * Default is `Record<string, () => void>`.
41
+ * Default is `Record<string, (...args: unknown[]) => unknown>`.
37
42
  */
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- export default class Publisher<T extends { [K in keyof T]: Callback<any[], any> } = Record<string, Callback>>
43
+ export default class Publisher<T extends CallbackMap<T> = CallbackMap>
40
44
  {
41
45
  /**
42
46
  * A map containing all the subscribers for each event.
@@ -44,7 +48,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
44
48
  * The keys are the names of the events they are subscribed to.
45
49
  * The values are the arrays of the subscribers themselves.
46
50
  */
47
- protected _subscribers: Map<keyof T, Callback<unknown[], unknown>[]>;
51
+ protected _subscribers: Map<string, Callback<unknown[], unknown>[]>;
48
52
 
49
53
  /**
50
54
  * Initializes a new instance of the {@link Publisher} class.
@@ -109,7 +113,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
109
113
  *
110
114
  * @returns An array containing the return values of all the subscribers.
111
115
  */
112
- public publish<K extends keyof T>(event: K, ...args: Parameters<T[K]>): ReturnType<T[K]>[]
116
+ public publish<K extends keyof T>(event: K & string, ...args: Parameters<T[K]>): ReturnType<T[K]>[]
113
117
  {
114
118
  const subscribers = this._subscribers.get(event);
115
119
  if (!(subscribers)) { return []; }
@@ -142,7 +146,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
142
146
  *
143
147
  * @returns A function that can be used to unsubscribe the subscriber.
144
148
  */
145
- public subscribe<K extends keyof T>(event: K, subscriber: T[K]): () => void
149
+ public subscribe<K extends keyof T>(event: K & string, subscriber: T[K]): () => void
146
150
  {
147
151
  if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); }
148
152
 
@@ -182,7 +186,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
182
186
  * @param event The name of the event to unsubscribe from.
183
187
  * @param subscriber The subscriber to remove from the event.
184
188
  */
185
- public unsubscribe<K extends keyof T>(event: K, subscriber: T[K]): void
189
+ public unsubscribe<K extends keyof T>(event: K & string, subscriber: T[K]): void
186
190
  {
187
191
  const subscribers = this._subscribers.get(event);
188
192
  if (!(subscribers)) { return; }
@@ -11,6 +11,9 @@ const Disabler = () => { /* ... */ };
11
11
  * It can be used to implement different behaviors for the same event handler, allowing
12
12
  * it to respond to different states without incurring any overhead during execution.
13
13
  *
14
+ * ---
15
+ *
16
+ * @example
14
17
  * ```ts
15
18
  * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>();
16
19
  *
@@ -22,6 +25,8 @@ const Disabler = () => { /* ... */ };
22
25
  * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); });
23
26
  * ```
24
27
  *
28
+ * ---
29
+ *
25
30
  * @template T The type signature of the callback. Default is `(...args: any[]) => any`.
26
31
  */
27
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -151,7 +156,9 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
151
156
  * window.addEventListener("pointerdown", () => { onPointerMove.enable(); });
152
157
  * window.addEventListener("pointermove", onPointerMove);
153
158
  * ```
154
- *
159
+ *
160
+ * ---
161
+ *
155
162
  * @param key
156
163
  * The key that is associated with the implementation to enable. Default is the currently selected implementation.
157
164
  */
@@ -303,6 +310,7 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
303
310
  throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
304
311
  }
305
312
 
313
+ if (this._key === key) { return; }
306
314
  this._key = key;
307
315
 
308
316
  if (this._isEnabled)
@@ -4,10 +4,15 @@
4
4
  * It can be used to define the signature of a callback, a event handler or any other function.
5
5
  * It's simply a shorthand for the `(...args: A) => R` function signature.
6
6
  *
7
+ * ---
8
+ *
9
+ * @example
7
10
  * ```ts
8
11
  * const callback: Callback<[PointerEvent]> = (evt: PointerEvent): void => { [...] };
9
12
  * ```
10
13
  *
14
+ * ---
15
+ *
11
16
  * @template A
12
17
  * The type of the arguments that the function accepts.
13
18
  * It must be an array of types, even if it's empty. Default is `[]`.
@@ -15,3 +20,30 @@
15
20
  * @template R The return type of the function. Default is `void`.
16
21
  */
17
22
  export type Callback<A extends unknown[] = [], R = void> = (...args: A) => R;
23
+
24
+ /**
25
+ * An utility type that is required to represents a map of callbacks.
26
+ *
27
+ * It is used for type inheritance on the `Publisher` class signature.
28
+ * Whenever you'll need to extend that class, you may need to use this type too.
29
+ *
30
+ * ---
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * interface EventsMap
35
+ * {
36
+ * "player:spawn": (evt: SpawnEvent) => void;
37
+ * "player:move": ({ x, y }: Point) => void;
38
+ * "player:death": () => void;
39
+ * }
40
+ *
41
+ * class EventManager<T extends CallbackMap<T> = { }> extends Publisher<T> { [...] }
42
+ * ```
43
+ *
44
+ * ---
45
+ *
46
+ * @template T The interface defining the map of callbacks. Default is `Record<string, Callback<unknown[], unknown>>`.
47
+ */
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ export type CallbackMap<T = Record<string, Callback<unknown[], unknown>>> = { [K in keyof T]: Callback<any[], any> };
@@ -5,6 +5,9 @@
5
5
  * It allows to chain exceptions together, tracking the initial cause of an error and
6
6
  * storing its stack trace while providing a clear and friendly message to the user.
7
7
  *
8
+ * ---
9
+ *
10
+ * @example
8
11
  * ```ts
9
12
  * try { loadGameSaves(); }
10
13
  * catch (error)
@@ -109,6 +112,9 @@ export default class Exception extends Error
109
112
  *
110
113
  * It provides a clear and friendly message by default.
111
114
  *
115
+ * ---
116
+ *
117
+ * @example
112
118
  * ```ts
113
119
  * function checkCase(value: "A" | "B" | "C"): 1 | 2 | 3
114
120
  * {
@@ -160,6 +166,9 @@ export class FatalErrorException extends Exception
160
166
  *
161
167
  * It provides a clear and friendly message by default.
162
168
  *
169
+ * ---
170
+ *
171
+ * @example
163
172
  * ```ts
164
173
  * class Database
165
174
  * {