@byloth/core 2.0.0 → 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 +900 -187
  3. package/dist/core.js.map +1 -1
  4. package/dist/core.umd.cjs +2 -2
  5. package/dist/core.umd.cjs.map +1 -1
  6. package/package.json +13 -10
  7. package/src/core/types.ts +43 -10
  8. package/src/index.ts +3 -2
  9. package/src/models/aggregators/aggregated-async-iterator.ts +161 -1
  10. package/src/models/aggregators/aggregated-iterator.ts +146 -1
  11. package/src/models/aggregators/reduced-iterator.ts +148 -8
  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 +33 -8
  15. package/src/models/callbacks/switchable-callback.ts +102 -21
  16. package/src/models/callbacks/types.ts +32 -0
  17. package/src/models/exceptions/core.ts +29 -0
  18. package/src/models/exceptions/index.ts +105 -1
  19. package/src/models/iterators/smart-async-iterator.ts +145 -0
  20. package/src/models/iterators/smart-iterator.ts +130 -0
  21. package/src/models/iterators/types.ts +79 -1
  22. package/src/models/json/json-storage.ts +123 -0
  23. package/src/models/json/types.ts +1 -1
  24. package/src/models/promises/deferred-promise.ts +15 -0
  25. package/src/models/promises/smart-promise.ts +45 -0
  26. package/src/models/promises/timed-promise.ts +10 -0
  27. package/src/models/promises/types.ts +30 -0
  28. package/src/models/timers/clock.ts +21 -0
  29. package/src/models/timers/countdown.ts +30 -0
  30. package/src/models/timers/game-loop.ts +26 -0
  31. package/src/models/types.ts +1 -1
  32. package/src/utils/async.ts +15 -0
  33. package/src/utils/curve.ts +11 -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 +34 -0
  39. package/src/utils/string.ts +5 -0
@@ -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,11 +48,14 @@ 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.
51
55
  *
56
+ * ---
57
+ *
58
+ * @example
52
59
  * ```ts
53
60
  * const publisher = new Publisher();
54
61
  * ```
@@ -61,6 +68,9 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
61
68
  /**
62
69
  * Unsubscribes all the subscribers from all the events.
63
70
  *
71
+ * ---
72
+ *
73
+ * @example
64
74
  * ```ts
65
75
  * publisher.subscribe("player:spawn", (evt) => { [...] });
66
76
  * publisher.subscribe("player:move", (coords) => { [...] });
@@ -83,6 +93,9 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
83
93
  /**
84
94
  * Publishes an event to all the subscribers.
85
95
  *
96
+ * ---
97
+ *
98
+ * @example
86
99
  * ```ts
87
100
  * publisher.subscribe("player:move", (coords) => { [...] });
88
101
  * publisher.subscribe("player:move", ({ x, y }) => { [...] });
@@ -91,6 +104,8 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
91
104
  * publisher.publish("player:move", { x: 10, y: 20 });
92
105
  * ```
93
106
  *
107
+ * ---
108
+ *
94
109
  * @template K The key of the map containing the callback signature to publish.
95
110
  *
96
111
  * @param event The name of the event to publish.
@@ -98,7 +113,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
98
113
  *
99
114
  * @returns An array containing the return values of all the subscribers.
100
115
  */
101
- 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]>[]
102
117
  {
103
118
  const subscribers = this._subscribers.get(event);
104
119
  if (!(subscribers)) { return []; }
@@ -110,6 +125,9 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
110
125
  /**
111
126
  * Subscribes a new subscriber to an event.
112
127
  *
128
+ * ---
129
+ *
130
+ * @example
113
131
  * ```ts
114
132
  * let unsubscribe: () => void;
115
133
  * publisher.subscribe("player:death", unsubscribe);
@@ -119,6 +137,8 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
119
137
  * });
120
138
  * ```
121
139
  *
140
+ * ---
141
+ *
122
142
  * @template K The key of the map containing the callback signature to subscribe.
123
143
  *
124
144
  * @param event The name of the event to subscribe to.
@@ -126,7 +146,7 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
126
146
  *
127
147
  * @returns A function that can be used to unsubscribe the subscriber.
128
148
  */
129
- 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
130
150
  {
131
151
  if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); }
132
152
 
@@ -149,6 +169,9 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
149
169
  /**
150
170
  * Unsubscribes a subscriber from an event.
151
171
  *
172
+ * ---
173
+ *
174
+ * @example
152
175
  * ```ts
153
176
  * const onPlayerMove = ({ x, y }: Point) => { [...] };
154
177
  *
@@ -156,12 +179,14 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
156
179
  * publisher.subscribe("player:death", () => publisher.unsubscribe("player:move", onPlayerMove));
157
180
  * ```
158
181
  *
182
+ * ---
183
+ *
159
184
  * @template K The key of the map containing the callback signature to unsubscribe.
160
185
  *
161
186
  * @param event The name of the event to unsubscribe from.
162
187
  * @param subscriber The subscriber to remove from the event.
163
188
  */
164
- 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
165
190
  {
166
191
  const subscribers = this._subscribers.get(event);
167
192
  if (!(subscribers)) { return; }
@@ -3,12 +3,17 @@ import { KeyException, NotImplementedException, RuntimeException } from "../exce
3
3
  import CallableObject from "./callable-object.js";
4
4
  import type { Callback } from "./types.js";
5
5
 
6
+ const Disabler = () => { /* ... */ };
7
+
6
8
  /**
7
9
  * A class representing a callback that can be switched between multiple implementations.
8
10
  *
9
11
  * It can be used to implement different behaviors for the same event handler, allowing
10
12
  * it to respond to different states without incurring any overhead during execution.
11
13
  *
14
+ * ---
15
+ *
16
+ * @example
12
17
  * ```ts
13
18
  * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>();
14
19
  *
@@ -20,6 +25,8 @@ import type { Callback } from "./types.js";
20
25
  * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); });
21
26
  * ```
22
27
  *
28
+ * ---
29
+ *
23
30
  * @template T The type signature of the callback. Default is `(...args: any[]) => any`.
24
31
  */
25
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -76,28 +83,60 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
76
83
  /**
77
84
  * Initializes a new instance of the {@link SwitchableCallback} class.
78
85
  *
86
+ * ---
87
+ *
88
+ * @example
79
89
  * ```ts
80
90
  * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>();
81
91
  * ```
82
92
  */
83
- public constructor()
84
- {
85
- const _default = () =>
86
- {
87
- throw new NotImplementedException(
88
- "The `SwitchableCallback` has no callback defined yet. " +
89
- "Did you forget to call the `register` method?"
90
- );
91
- };
93
+ public constructor();
92
94
 
95
+ /**
96
+ * Initializes a new instance of the {@link SwitchableCallback}
97
+ * class with the specified callback enabled by default.
98
+ *
99
+ * ---
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>((evt) => { [...] });
104
+ * ```
105
+ *
106
+ * ---
107
+ *
108
+ * @param callback The callback that will be executed when the object is invoked as a function by default.
109
+ * @param key The key that is associated by default to the given callback. Default is `default`.
110
+ */
111
+ public constructor(callback: T, key?: string);
112
+ public constructor(callback?: T, key = "default")
113
+ {
93
114
  super();
94
115
 
95
- this._callback = ((_default) as unknown) as T;
96
116
  this._callbacks = new Map<string, T>();
97
-
98
117
  this._isEnabled = true;
99
- this._key = "";
100
118
 
119
+ if (callback)
120
+ {
121
+ this._callbacks.set(key, callback);
122
+ }
123
+ else
124
+ {
125
+ key = "";
126
+
127
+ callback = ((() =>
128
+ {
129
+ throw new NotImplementedException(
130
+ "The `SwitchableCallback` has no callback defined yet. " +
131
+ "Did you forget to call the `register` method?"
132
+ );
133
+
134
+ }) as unknown) as T;
135
+ }
136
+
137
+ this._key = key;
138
+
139
+ this._callback = callback;
101
140
  this._invoke = (...args: Parameters<T>): ReturnType<T> => this._callback(...args);
102
141
  }
103
142
 
@@ -106,28 +145,52 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
106
145
  *
107
146
  * Also note that:
108
147
  * - If any implementation has been registered yet, a {@link KeyException} will be thrown.
148
+ * - If any key is given and it doesn't have any associated
149
+ * implementation yet, a {@link KeyException} will be thrown.
109
150
  * - If the callback is already enabled, a {@link RuntimeException} will be thrown.
110
151
  *
152
+ * ---
153
+ *
154
+ * @example
111
155
  * ```ts
112
156
  * window.addEventListener("pointerdown", () => { onPointerMove.enable(); });
113
157
  * window.addEventListener("pointermove", onPointerMove);
114
158
  * ```
159
+ *
160
+ * ---
161
+ *
162
+ * @param key
163
+ * The key that is associated with the implementation to enable. Default is the currently selected implementation.
115
164
  */
116
- public enable(): void
165
+ public enable(key?: string): void
117
166
  {
118
- if (!(this._key))
167
+ if (key === undefined)
119
168
  {
120
- throw new KeyException(
121
- "The `SwitchableCallback` has no callback defined yet. " +
122
- "Did you forget to call the `register` method?"
123
- );
169
+ if (!(this._key))
170
+ {
171
+ throw new KeyException(
172
+ "The `SwitchableCallback` has no callback defined yet. " +
173
+ "Did you forget to call the `register` method?"
174
+ );
175
+ }
176
+
177
+ key = this._key;
124
178
  }
179
+ else if (!(key))
180
+ {
181
+ throw new KeyException("The key must be a non-empty string.");
182
+ }
183
+ else if (!(this._callbacks.has(key)))
184
+ {
185
+ throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
186
+ }
187
+
125
188
  if (this._isEnabled)
126
189
  {
127
190
  throw new RuntimeException("The `SwitchableCallback` is already enabled.");
128
191
  }
129
192
 
130
- this._callback = this._callbacks.get(this._key)!;
193
+ this._callback = this._callbacks.get(key)!;
131
194
  this._isEnabled = true;
132
195
  }
133
196
 
@@ -136,6 +199,9 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
136
199
  *
137
200
  * If the callback is already disabled, a {@link RuntimeException} will be thrown.
138
201
  *
202
+ * ---
203
+ *
204
+ * @example
139
205
  * ```ts
140
206
  * window.addEventListener("pointermove", onPointerMove);
141
207
  * window.addEventListener("pointerup", () => { onPointerMove.disable(); });
@@ -148,8 +214,7 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
148
214
  throw new RuntimeException("The `SwitchableCallback` is already disabled.");
149
215
  }
150
216
 
151
- // eslint-disable-next-line @typescript-eslint/no-empty-function
152
- this._callback = (() => { }) as T;
217
+ this._callback = Disabler as T;
153
218
  this._isEnabled = false;
154
219
  }
155
220
 
@@ -160,11 +225,16 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
160
225
  * - If the callback has no other implementation registered yet, this one will be selected as default.
161
226
  * - If the key has already been used for another implementation, a {@link KeyException} will be thrown.
162
227
  *
228
+ * ---
229
+ *
230
+ * @example
163
231
  * ```ts
164
232
  * onPointerMove.register("pressed", () => { [...] });
165
233
  * onPointerMove.register("released", () => { [...] });
166
234
  * ```
167
235
  *
236
+ * ---
237
+ *
168
238
  * @param key The key that will be associated with the implementation.
169
239
  * @param callback The implementation to register.
170
240
  */
@@ -190,10 +260,15 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
190
260
  * - If the key is the currently selected implementation, a {@link KeyException} will be thrown.
191
261
  * - If the key has no associated implementation yet, a {@link KeyException} will be thrown.
192
262
  *
263
+ * ---
264
+ *
265
+ * @example
193
266
  * ```ts
194
267
  * onPointerMove.unregister("released");
195
268
  * ```
196
269
  *
270
+ * ---
271
+ *
197
272
  * @param key The key that is associated with the implementation to unregister.
198
273
  */
199
274
  public unregister(key: string): void
@@ -215,12 +290,17 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
215
290
  *
216
291
  * If the key has no associated implementation yet, a {@link KeyException} will be thrown.
217
292
  *
293
+ * ---
294
+ *
295
+ * @example
218
296
  * ```ts
219
297
  * window.addEventListener("pointerdown", () => { onPointerMove.switch("pressed"); });
220
298
  * window.addEventListener("pointermove", onPointerMove);
221
299
  * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); });
222
300
  * ```
223
301
  *
302
+ * ---
303
+ *
224
304
  * @param key The key that is associated with the implementation to switch to.
225
305
  */
226
306
  public switch(key: string): void
@@ -230,6 +310,7 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
230
310
  throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
231
311
  }
232
312
 
313
+ if (this._key === key) { return; }
233
314
  this._key = key;
234
315
 
235
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)
@@ -26,6 +29,9 @@ export default class Exception extends Error
26
29
  /**
27
30
  * A static method to convert a generic caught error, ensuring it's an instance of the {@link Exception} class.
28
31
  *
32
+ * ---
33
+ *
34
+ * @example
29
35
  * ```ts
30
36
  * try { [...] }
31
37
  * catch (error)
@@ -36,6 +42,8 @@ export default class Exception extends Error
36
42
  * }
37
43
  * ```
38
44
  *
45
+ * ---
46
+ *
39
47
  * @param error The caught error to convert.
40
48
  *
41
49
  * @returns An instance of the {@link Exception} class.
@@ -62,10 +70,15 @@ export default class Exception extends Error
62
70
  /**
63
71
  * Initializes a new instance of the {@link Exception} class.
64
72
  *
73
+ * ---
74
+ *
75
+ * @example
65
76
  * ```ts
66
77
  * throw new Exception("An error occurred while processing the request.");
67
78
  * ```
68
79
  *
80
+ * ---
81
+ *
69
82
  * @param message The message that describes the error.
70
83
  * @param cause The previous caught error that caused this one, if any.
71
84
  * @param name The name of the exception. Default is `"Exception"`.
@@ -99,6 +112,9 @@ export default class Exception extends Error
99
112
  *
100
113
  * It provides a clear and friendly message by default.
101
114
  *
115
+ * ---
116
+ *
117
+ * @example
102
118
  * ```ts
103
119
  * function checkCase(value: "A" | "B" | "C"): 1 | 2 | 3
104
120
  * {
@@ -117,10 +133,15 @@ export class FatalErrorException extends Exception
117
133
  /**
118
134
  * Initializes a new instance of the {@link FatalErrorException} class.
119
135
  *
136
+ * ---
137
+ *
138
+ * @example
120
139
  * ```ts
121
140
  * throw new FatalErrorException("This error should never happen. Please, contact the support team.");
122
141
  * ```
123
142
  *
143
+ * ---
144
+ *
124
145
  * @param message The message that describes the error.
125
146
  * @param cause The previous caught error that caused this one, if any.
126
147
  * @param name The name of the exception. Default is `"FatalErrorException"`.
@@ -145,6 +166,9 @@ export class FatalErrorException extends Exception
145
166
  *
146
167
  * It provides a clear and friendly message by default.
147
168
  *
169
+ * ---
170
+ *
171
+ * @example
148
172
  * ```ts
149
173
  * class Database
150
174
  * {
@@ -160,10 +184,15 @@ export class NotImplementedException extends FatalErrorException
160
184
  /**
161
185
  * Initializes a new instance of the {@link NotImplementedException} class.
162
186
  *
187
+ * ---
188
+ *
189
+ * @example
163
190
  * ```ts
164
191
  * throw new NotImplementedException("This method hasn't been implemented yet. Check back later.");
165
192
  * ```
166
193
  *
194
+ * ---
195
+ *
167
196
  * @param message The message that describes the error.
168
197
  * @param cause The previous caught error that caused this one, if any.
169
198
  * @param name The name of the exception. Default is `"NotImplementedException"`.