@byloth/core 2.0.0-rc.9 → 2.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.
Files changed (47) hide show
  1. package/dist/core.js +4087 -621
  2. package/dist/core.js.map +1 -1
  3. package/dist/core.umd.cjs +2 -2
  4. package/dist/core.umd.cjs.map +1 -1
  5. package/package.json +17 -13
  6. package/src/core/types.ts +41 -0
  7. package/src/helpers.ts +11 -2
  8. package/src/index.ts +12 -9
  9. package/src/models/aggregators/aggregated-async-iterator.ts +920 -21
  10. package/src/models/aggregators/aggregated-iterator.ts +838 -22
  11. package/src/models/aggregators/reduced-iterator.ts +827 -11
  12. package/src/models/aggregators/types.ts +153 -10
  13. package/src/models/callbacks/callable-object.ts +42 -6
  14. package/src/models/callbacks/index.ts +2 -2
  15. package/src/models/callbacks/publisher.ts +160 -4
  16. package/src/models/callbacks/switchable-callback.ts +230 -23
  17. package/src/models/callbacks/types.ts +16 -0
  18. package/src/models/exceptions/core.ts +132 -3
  19. package/src/models/exceptions/index.ts +405 -13
  20. package/src/models/index.ts +4 -8
  21. package/src/models/iterators/smart-async-iterator.ts +827 -22
  22. package/src/models/iterators/smart-iterator.ts +755 -20
  23. package/src/models/iterators/types.ts +268 -9
  24. package/src/models/json/json-storage.ts +508 -110
  25. package/src/models/json/types.ts +10 -1
  26. package/src/models/promises/deferred-promise.ts +85 -5
  27. package/src/models/promises/index.ts +1 -3
  28. package/src/models/promises/smart-promise.ts +272 -4
  29. package/src/models/promises/timed-promise.ts +43 -1
  30. package/src/models/promises/types.ts +84 -2
  31. package/src/models/timers/clock.ts +109 -19
  32. package/src/models/timers/countdown.ts +176 -21
  33. package/src/models/timers/game-loop.ts +266 -0
  34. package/src/models/timers/index.ts +2 -1
  35. package/src/models/types.ts +6 -5
  36. package/src/utils/async.ts +43 -0
  37. package/src/utils/curve.ts +85 -0
  38. package/src/utils/date.ts +204 -10
  39. package/src/utils/dom.ts +16 -2
  40. package/src/utils/index.ts +3 -2
  41. package/src/utils/iterator.ts +200 -17
  42. package/src/utils/math.ts +55 -3
  43. package/src/utils/random.ts +139 -2
  44. package/src/utils/string.ts +11 -0
  45. package/src/models/game-loop.ts +0 -83
  46. package/src/models/promises/long-running-task.ts +0 -294
  47. package/src/models/promises/thenable.ts +0 -97
@@ -3,58 +3,203 @@ 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
+
8
+ /**
9
+ * A class representing a callback that can be switched between multiple implementations.
10
+ *
11
+ * It can be used to implement different behaviors for the same event handler, allowing
12
+ * it to respond to different states without incurring any overhead during execution.
13
+ *
14
+ * ```ts
15
+ * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>();
16
+ *
17
+ * onPointerMove.register("released", () => { [...] });
18
+ * onPointerMove.register("pressed", () => { [...] });
19
+ *
20
+ * window.addEventListener("pointerdown", () => { onPointerMove.switch("pressed"); });
21
+ * window.addEventListener("pointermove", onPointerMove);
22
+ * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); });
23
+ * ```
24
+ *
25
+ * @template T The type signature of the callback. Default is `(...args: any[]) => any`.
26
+ */
6
27
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
28
  export default class SwitchableCallback<T extends Callback<any[], any> = Callback> extends CallableObject<T>
8
29
  {
30
+ /**
31
+ * The currently selected implementation of the callback.
32
+ */
9
33
  protected _callback: T;
34
+
35
+ /**
36
+ * All the implementations that have been registered for the callback.
37
+ *
38
+ * The keys are the names of the implementations they were registered with.
39
+ * The values are the implementations themselves.
40
+ */
10
41
  protected _callbacks: Map<string, T>;
11
42
 
43
+ /**
44
+ * A flag indicating whether the callback is enabled or not.
45
+ *
46
+ * This protected property is the only one that can be modified directly by the derived classes.
47
+ * If you're looking for the public and readonly property, use
48
+ * the {@link SwitchableCallback.isEnabled} getter instead.
49
+ */
12
50
  protected _isEnabled: boolean;
51
+
52
+ /**
53
+ * A flag indicating whether the callback is enabled or not.
54
+ *
55
+ * It indicates whether the callback is currently able to execute the currently selected implementation.
56
+ * If it's disabled, the callback will be invoked without executing anything.
57
+ */
13
58
  public get isEnabled(): boolean { return this._isEnabled; }
14
59
 
60
+ /**
61
+ * The key that is associated with the currently selected implementation.
62
+ *
63
+ * This protected property is the only one that can be modified directly by the derived classes.
64
+ * If you're looking for the public and readonly property, use the {@link SwitchableCallback.key} getter instead.
65
+ */
15
66
  protected _key: string;
67
+
68
+ /**
69
+ * The key that is associated with the currently selected implementation.
70
+ */
16
71
  public get key(): string { return this._key; }
17
72
 
18
- public readonly invoke: (...args: Parameters<T>) => ReturnType<T>;
73
+ /**
74
+ * The function that will be called by the extended class when the object is invoked as a function.
75
+ */
76
+ protected readonly _invoke: (...args: Parameters<T>) => ReturnType<T>;
19
77
 
20
- public constructor()
21
- {
22
- const _default = () =>
23
- {
24
- throw new NotImplementedException(
25
- "The `SwitchableCallback` has no callback defined yet. " +
26
- "Did you forget to call the `register` method?"
27
- );
28
- };
78
+ /**
79
+ * Initializes a new instance of the {@link SwitchableCallback} class.
80
+ *
81
+ * ---
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>();
86
+ * ```
87
+ */
88
+ public constructor();
29
89
 
90
+ /**
91
+ * Initializes a new instance of the {@link SwitchableCallback}
92
+ * class with the specified callback enabled by default.
93
+ *
94
+ * ---
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>((evt) => { [...] });
99
+ * ```
100
+ *
101
+ * ---
102
+ *
103
+ * @param callback The callback that will be executed when the object is invoked as a function by default.
104
+ * @param key The key that is associated by default to the given callback. Default is `default`.
105
+ */
106
+ public constructor(callback: T, key?: string);
107
+ public constructor(callback?: T, key = "default")
108
+ {
30
109
  super();
31
110
 
32
- this._callback = ((_default) as unknown) as T;
33
111
  this._callbacks = new Map<string, T>();
112
+ this._isEnabled = true;
34
113
 
35
- this._isEnabled = false;
36
- this._key = "";
114
+ if (callback)
115
+ {
116
+ this._callbacks.set(key, callback);
117
+ }
118
+ else
119
+ {
120
+ key = "";
121
+
122
+ callback = ((() =>
123
+ {
124
+ throw new NotImplementedException(
125
+ "The `SwitchableCallback` has no callback defined yet. " +
126
+ "Did you forget to call the `register` method?"
127
+ );
128
+
129
+ }) as unknown) as T;
130
+ }
37
131
 
38
- this.invoke = (...args: Parameters<T>): ReturnType<T> => this._callback(...args);
132
+ this._key = key;
133
+
134
+ this._callback = callback;
135
+ this._invoke = (...args: Parameters<T>): ReturnType<T> => this._callback(...args);
39
136
  }
40
137
 
41
- public enable(): void
138
+ /**
139
+ * Enables the callback, allowing it to execute the currently selected implementation.
140
+ *
141
+ * Also note that:
142
+ * - If any implementation has been registered yet, a {@link KeyException} will be thrown.
143
+ * - If any key is given and it doesn't have any associated
144
+ * implementation yet, a {@link KeyException} will be thrown.
145
+ * - If the callback is already enabled, a {@link RuntimeException} will be thrown.
146
+ *
147
+ * ---
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * window.addEventListener("pointerdown", () => { onPointerMove.enable(); });
152
+ * window.addEventListener("pointermove", onPointerMove);
153
+ * ```
154
+ *
155
+ * @param key
156
+ * The key that is associated with the implementation to enable. Default is the currently selected implementation.
157
+ */
158
+ public enable(key?: string): void
42
159
  {
43
- if (!(this._key))
160
+ if (key === undefined)
161
+ {
162
+ if (!(this._key))
163
+ {
164
+ throw new KeyException(
165
+ "The `SwitchableCallback` has no callback defined yet. " +
166
+ "Did you forget to call the `register` method?"
167
+ );
168
+ }
169
+
170
+ key = this._key;
171
+ }
172
+ else if (!(key))
173
+ {
174
+ throw new KeyException("The key must be a non-empty string.");
175
+ }
176
+ else if (!(this._callbacks.has(key)))
44
177
  {
45
- throw new KeyException(
46
- "The `SwitchableCallback` has no callback defined yet. " +
47
- "Did you forget to call the `register` method?"
48
- );
178
+ throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
49
179
  }
180
+
50
181
  if (this._isEnabled)
51
182
  {
52
183
  throw new RuntimeException("The `SwitchableCallback` is already enabled.");
53
184
  }
54
185
 
55
- this._callback = this._callbacks.get(this._key)!;
186
+ this._callback = this._callbacks.get(key)!;
56
187
  this._isEnabled = true;
57
188
  }
189
+
190
+ /**
191
+ * Disables the callback, allowing it to be invoked without executing any implementation.
192
+ *
193
+ * If the callback is already disabled, a {@link RuntimeException} will be thrown.
194
+ *
195
+ * ---
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * window.addEventListener("pointermove", onPointerMove);
200
+ * window.addEventListener("pointerup", () => { onPointerMove.disable(); });
201
+ * ```
202
+ */
58
203
  public disable(): void
59
204
  {
60
205
  if (!(this._isEnabled))
@@ -62,11 +207,30 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
62
207
  throw new RuntimeException("The `SwitchableCallback` is already disabled.");
63
208
  }
64
209
 
65
- // eslint-disable-next-line @typescript-eslint/no-empty-function
66
- this._callback = (() => { }) as T;
210
+ this._callback = Disabler as T;
67
211
  this._isEnabled = false;
68
212
  }
69
213
 
214
+ /**
215
+ * Registers a new implementation for the callback.
216
+ *
217
+ * Also note that:
218
+ * - If the callback has no other implementation registered yet, this one will be selected as default.
219
+ * - If the key has already been used for another implementation, a {@link KeyException} will be thrown.
220
+ *
221
+ * ---
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * onPointerMove.register("pressed", () => { [...] });
226
+ * onPointerMove.register("released", () => { [...] });
227
+ * ```
228
+ *
229
+ * ---
230
+ *
231
+ * @param key The key that will be associated with the implementation.
232
+ * @param callback The implementation to register.
233
+ */
70
234
  public register(key: string, callback: T): void
71
235
  {
72
236
  if (this._callbacks.size === 0)
@@ -81,8 +245,31 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
81
245
 
82
246
  this._callbacks.set(key, callback);
83
247
  }
248
+
249
+ /**
250
+ * Unregisters an implementation for the callback.
251
+ *
252
+ * Also note that:
253
+ * - If the key is the currently selected implementation, a {@link KeyException} will be thrown.
254
+ * - If the key has no associated implementation yet, a {@link KeyException} will be thrown.
255
+ *
256
+ * ---
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * onPointerMove.unregister("released");
261
+ * ```
262
+ *
263
+ * ---
264
+ *
265
+ * @param key The key that is associated with the implementation to unregister.
266
+ */
84
267
  public unregister(key: string): void
85
268
  {
269
+ if (this._key === key)
270
+ {
271
+ throw new KeyException("Unable to unregister the currently selected callback.");
272
+ }
86
273
  if (!(this._callbacks.has(key)))
87
274
  {
88
275
  throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`);
@@ -91,6 +278,24 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
91
278
  this._callbacks.delete(key);
92
279
  }
93
280
 
281
+ /**
282
+ * Switches the callback to the implementation associated with the given key.
283
+ *
284
+ * If the key has no associated implementation yet, a {@link KeyException} will be thrown.
285
+ *
286
+ * ---
287
+ *
288
+ * @example
289
+ * ```ts
290
+ * window.addEventListener("pointerdown", () => { onPointerMove.switch("pressed"); });
291
+ * window.addEventListener("pointermove", onPointerMove);
292
+ * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); });
293
+ * ```
294
+ *
295
+ * ---
296
+ *
297
+ * @param key The key that is associated with the implementation to switch to.
298
+ */
94
299
  public switch(key: string): void
95
300
  {
96
301
  if (!(this._callbacks.has(key)))
@@ -105,4 +310,6 @@ export default class SwitchableCallback<T extends Callback<any[], any> = Callbac
105
310
  this._callback = this._callbacks.get(key)!;
106
311
  }
107
312
  }
313
+
314
+ public override readonly [Symbol.toStringTag]: string = "SwitchableCallback";
108
315
  }
@@ -1 +1,17 @@
1
+ /**
2
+ * A type that represents a generic function.
3
+ *
4
+ * It can be used to define the signature of a callback, a event handler or any other function.
5
+ * It's simply a shorthand for the `(...args: A) => R` function signature.
6
+ *
7
+ * ```ts
8
+ * const callback: Callback<[PointerEvent]> = (evt: PointerEvent): void => { [...] };
9
+ * ```
10
+ *
11
+ * @template A
12
+ * The type of the arguments that the function accepts.
13
+ * It must be an array of types, even if it's empty. Default is `[]`.
14
+ *
15
+ * @template R The return type of the function. Default is `void`.
16
+ */
1
17
  export type Callback<A extends unknown[] = [], R = void> = (...args: A) => R;
@@ -1,5 +1,50 @@
1
+ /**
2
+ * A class representing an exception, subclass of the native `Error` class.
3
+ * It's the base class for any other further exception.
4
+ *
5
+ * It allows to chain exceptions together, tracking the initial cause of an error and
6
+ * storing its stack trace while providing a clear and friendly message to the user.
7
+ *
8
+ * ```ts
9
+ * try { loadGameSaves(); }
10
+ * catch (error)
11
+ * {
12
+ * throw new Exception("The game saves may be corrupted. Try to restart the game.", error);
13
+ * // Uncaught Exception: The game saves may be corrupted. Try to restart the game.
14
+ * // at /src/game/index.ts:37:15
15
+ * // at /src/main.ts:23:17
16
+ * //
17
+ * // Caused by SyntaxError: Unexpected end of JSON input
18
+ * // at /src/models/saves.ts:47:17
19
+ * // at /src/game/index.ts:12:9
20
+ * // at /src/main.ts:23:17
21
+ * }
22
+ * ```
23
+ */
1
24
  export default class Exception extends Error
2
25
  {
26
+ /**
27
+ * A static method to convert a generic caught error, ensuring it's an instance of the {@link Exception} class.
28
+ *
29
+ * ---
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * try { [...] }
34
+ * catch (error)
35
+ * {
36
+ * const exc = Exception.FromUnknown(error);
37
+ *
38
+ * [...]
39
+ * }
40
+ * ```
41
+ *
42
+ * ---
43
+ *
44
+ * @param error The caught error to convert.
45
+ *
46
+ * @returns An instance of the {@link Exception} class.
47
+ */
3
48
  public static FromUnknown(error: unknown): Exception
4
49
  {
5
50
  if (error instanceof Exception)
@@ -19,6 +64,22 @@ export default class Exception extends Error
19
64
  return new Exception(`${error}`);
20
65
  }
21
66
 
67
+ /**
68
+ * Initializes a new instance of the {@link Exception} class.
69
+ *
70
+ * ---
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * throw new Exception("An error occurred while processing the request.");
75
+ * ```
76
+ *
77
+ * ---
78
+ *
79
+ * @param message The message that describes the error.
80
+ * @param cause The previous caught error that caused this one, if any.
81
+ * @param name The name of the exception. Default is `"Exception"`.
82
+ */
22
83
  public constructor(message: string, cause?: unknown, name = "Exception")
23
84
  {
24
85
  super(message);
@@ -42,8 +103,43 @@ export default class Exception extends Error
42
103
  public readonly [Symbol.toStringTag]: string = "Exception";
43
104
  }
44
105
 
106
+ /**
107
+ * An utility class representing that kind of situation where the program should never reach.
108
+ * Also commonly used to satisfy the type-system, but not part of a real feasible scenario.
109
+ *
110
+ * It provides a clear and friendly message by default.
111
+ *
112
+ * ```ts
113
+ * function checkCase(value: "A" | "B" | "C"): 1 | 2 | 3
114
+ * {
115
+ * switch (value)
116
+ * {
117
+ * case "A": return 1;
118
+ * case "B": return 2;
119
+ * case "C": return 3;
120
+ * default: throw new FatalErrorException();
121
+ * }
122
+ * }
123
+ * ```
124
+ */
45
125
  export class FatalErrorException extends Exception
46
126
  {
127
+ /**
128
+ * Initializes a new instance of the {@link FatalErrorException} class.
129
+ *
130
+ * ---
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * throw new FatalErrorException("This error should never happen. Please, contact the support team.");
135
+ * ```
136
+ *
137
+ * ---
138
+ *
139
+ * @param message The message that describes the error.
140
+ * @param cause The previous caught error that caused this one, if any.
141
+ * @param name The name of the exception. Default is `"FatalErrorException"`.
142
+ */
47
143
  public constructor(message?: string, cause?: unknown, name = "FatalErrorException")
48
144
  {
49
145
  if (message === undefined)
@@ -55,19 +151,52 @@ export class FatalErrorException extends Exception
55
151
  super(message, cause, name);
56
152
  }
57
153
 
58
- public readonly [Symbol.toStringTag]: string = "FatalErrorException";
154
+ public override readonly [Symbol.toStringTag]: string = "FatalErrorException";
59
155
  }
156
+
157
+ /**
158
+ * An utility class representing a situation where a feature isn't implemented yet.
159
+ * It's commonly used as a placeholder for future implementations.
160
+ *
161
+ * It provides a clear and friendly message by default.
162
+ *
163
+ * ```ts
164
+ * class Database
165
+ * {
166
+ * public async connect(): Promise<void>
167
+ * {
168
+ * throw new NotImplementedException();
169
+ * }
170
+ * }
171
+ * ```
172
+ */
60
173
  export class NotImplementedException extends FatalErrorException
61
174
  {
175
+ /**
176
+ * Initializes a new instance of the {@link NotImplementedException} class.
177
+ *
178
+ * ---
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * throw new NotImplementedException("This method hasn't been implemented yet. Check back later.");
183
+ * ```
184
+ *
185
+ * ---
186
+ *
187
+ * @param message The message that describes the error.
188
+ * @param cause The previous caught error that caused this one, if any.
189
+ * @param name The name of the exception. Default is `"NotImplementedException"`.
190
+ */
62
191
  public constructor(message?: string, cause?: unknown, name = "NotImplementedException")
63
192
  {
64
193
  if (message === undefined)
65
194
  {
66
- message = "This feature is not implemented yet. Please, try again later.";
195
+ message = "This feature isn't implemented yet. Please, try again later.";
67
196
  }
68
197
 
69
198
  super(message, cause, name);
70
199
  }
71
200
 
72
- public readonly [Symbol.toStringTag]: string = "NotImplementedException";
201
+ public override readonly [Symbol.toStringTag]: string = "NotImplementedException";
73
202
  }