@byloth/core 2.0.0-rc.9 → 2.0.0

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 +3371 -608
  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 +13 -10
  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 +765 -21
  10. package/src/models/aggregators/aggregated-iterator.ts +698 -22
  11. package/src/models/aggregators/reduced-iterator.ts +699 -10
  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 +139 -4
  16. package/src/models/callbacks/switchable-callback.ts +138 -4
  17. package/src/models/callbacks/types.ts +16 -0
  18. package/src/models/exceptions/core.ts +112 -3
  19. package/src/models/exceptions/index.ts +340 -13
  20. package/src/models/index.ts +4 -8
  21. package/src/models/iterators/smart-async-iterator.ts +687 -22
  22. package/src/models/iterators/smart-iterator.ts +631 -21
  23. package/src/models/iterators/types.ts +268 -9
  24. package/src/models/json/json-storage.ts +388 -110
  25. package/src/models/json/types.ts +10 -1
  26. package/src/models/promises/deferred-promise.ts +75 -5
  27. package/src/models/promises/index.ts +1 -3
  28. package/src/models/promises/smart-promise.ts +232 -4
  29. package/src/models/promises/timed-promise.ts +38 -1
  30. package/src/models/promises/types.ts +84 -2
  31. package/src/models/timers/clock.ts +91 -19
  32. package/src/models/timers/countdown.ts +152 -22
  33. package/src/models/timers/game-loop.ts +243 -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 +75 -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 +109 -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
@@ -1,6 +1,29 @@
1
- import { SmartIterator } from "../models/index.js";
1
+ import { RangeException, SmartIterator } from "../models/index.js";
2
2
 
3
- export function chain<T>(...iterables: Iterable<T>[]): SmartIterator<T>
3
+ /**
4
+ * An utility function that chains multiple iterables into a single one.
5
+ *
6
+ * Since the iterator is lazy, the chaining process will be
7
+ * executed only once the resulting iterator is materialized.
8
+ *
9
+ * A new iterator will be created, holding the reference to the original one.
10
+ * This means that the original iterator won't be consumed until the
11
+ * new one is and that consuming one of them will consume the other as well.
12
+ *
13
+ * ```ts
14
+ * for (const value of chain([1, 2, 3], [4, 5, 6], [7, 8, 9]))
15
+ * {
16
+ * console.log(value); // 1, 2, 3, 4, 5, 6, 7, 8, 9
17
+ * }
18
+ * ```
19
+ *
20
+ * @template T The type of elements in the iterables.
21
+ *
22
+ * @param iterables The list of iterables to chain.
23
+ *
24
+ * @returns A new {@link SmartIterator} object that chains the iterables into a single one.
25
+ */
26
+ export function chain<T>(...iterables: readonly Iterable<T>[]): SmartIterator<T>
4
27
  {
5
28
  return new SmartIterator<T>(function* ()
6
29
  {
@@ -11,9 +34,26 @@ export function chain<T>(...iterables: Iterable<T>[]): SmartIterator<T>
11
34
  });
12
35
  }
13
36
 
37
+ /**
38
+ * An utility function that counts the number of elements in an iterable.
39
+ *
40
+ * Also note that:
41
+ * - If the iterable isn't an `Array`, it will be consumed entirely in the process.
42
+ * - If the iterable is an infinite generator, the function will never return.
43
+ *
44
+ * ```ts
45
+ * count([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // 10
46
+ * ```
47
+ *
48
+ * @template T The type of elements in the iterable.
49
+ *
50
+ * @param elements The iterable to count.
51
+ *
52
+ * @returns The number of elements in the iterable.
53
+ */
14
54
  export function count<T>(elements: Iterable<T>): number
15
55
  {
16
- if (Array.isArray(elements)) { return elements.length; }
56
+ if (elements instanceof Array) { return elements.length; }
17
57
 
18
58
  let _count = 0;
19
59
  for (const _ of elements) { _count += 1; }
@@ -21,12 +61,35 @@ export function count<T>(elements: Iterable<T>): number
21
61
  return _count;
22
62
  }
23
63
 
64
+ /**
65
+ * An utility function that enumerates the elements of an iterable.
66
+ * Each element is paired with its index in a new iterator.
67
+ *
68
+ * Since the iterator is lazy, the enumeration process will
69
+ * be executed once the resulting iterator is materialized.
70
+ *
71
+ * A new iterator will be created, holding the reference to the original one.
72
+ * This means that the original iterator won't be consumed until the
73
+ * new one is and that consuming one of them will consume the other as well.
74
+ *
75
+ * ```ts
76
+ * for (const [index, value] of enumerate(["A", "M", "N", "Z"]))
77
+ * {
78
+ * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z"
79
+ * }
80
+ * ```
81
+ *
82
+ * @template T The type of elements in the iterable.
83
+ *
84
+ * @param elements The iterable to enumerate.
85
+ *
86
+ * @returns A new {@link SmartIterator} object containing the enumerated elements.
87
+ */
24
88
  export function enumerate<T>(elements: Iterable<T>): SmartIterator<[number, T]>
25
89
  {
26
90
  return new SmartIterator<[number, T]>(function* ()
27
91
  {
28
92
  let index = 0;
29
-
30
93
  for (const element of elements)
31
94
  {
32
95
  yield [index, element];
@@ -36,25 +99,109 @@ export function enumerate<T>(elements: Iterable<T>): SmartIterator<[number, T]>
36
99
  });
37
100
  }
38
101
 
102
+ /**
103
+ * An utility function that generates an iterator over a range of numbers.
104
+ * The values are included between `0` (included) and `end` (excluded).
105
+ *
106
+ * The default step between the numbers is `1`.
107
+ *
108
+ * ```ts
109
+ * for (const number of range(5))
110
+ * {
111
+ * console.log(number); // 0, 1, 2, 3, 4
112
+ * }
113
+ * ```
114
+ *
115
+ * @param end
116
+ * The end value (excluded).
117
+ *
118
+ * If the `end` value is negative, the step will be `-1` leading to generate the numbers in reverse order.
119
+ *
120
+ * @returns A {@link SmartIterator} object that generates the numbers in the range.
121
+ */
39
122
  export function range(end: number): SmartIterator<number>;
40
- export function range(start: number, end: number): SmartIterator<number>;
41
- export function range(start: number, end: number, step: number): SmartIterator<number>;
123
+
124
+ /**
125
+ * An utility function that generates an iterator over a range of numbers.
126
+ * The values are included between `start` (included) and `end` (excluded).
127
+ *
128
+ * The step between the numbers can be specified with a custom value. Default is `1`.
129
+ *
130
+ * ```ts
131
+ * for (const number of range(2, 7))
132
+ * {
133
+ * console.log(number); // 2, 3, 4, 5, 6
134
+ * }
135
+ * ```
136
+ *
137
+ * @param start
138
+ * The start value (included).
139
+ *
140
+ * If the `start` value is greater than the `end` value, the iterator will generate the numbers in reverse order.
141
+ *
142
+ * @param end
143
+ * The end value (excluded).
144
+ *
145
+ * If the `end` value is less than the `start` value, the iterator will generate the numbers in reverse order.
146
+ *
147
+ * @param step
148
+ * The step between the numbers. Default is `1`.
149
+ *
150
+ * Must be a positive number. Otherwise, a {@link RangeError} will be thrown.
151
+ *
152
+ * @returns A {@link SmartIterator} object that generates the numbers in the range.
153
+ */
154
+ export function range(start: number, end: number, step?: number): SmartIterator<number>;
42
155
  export function range(start: number, end?: number, step = 1): SmartIterator<number>
43
156
  {
44
- return new SmartIterator<number>(function* ()
157
+ if (step <= 0)
45
158
  {
46
- if (end === undefined)
47
- {
48
- end = start;
49
- start = 0;
50
- }
159
+ throw new RangeException(
160
+ "Step must be always a positive number, even when generating numbers in reverse order."
161
+ );
162
+ }
51
163
 
52
- if (start > end) { step = step ?? -1; }
164
+ if (end === undefined)
165
+ {
166
+ end = start;
167
+ start = 0;
168
+ }
53
169
 
170
+ if (start > end)
171
+ {
172
+ return new SmartIterator<number>(function* ()
173
+ {
174
+ for (let index = start; index > end; index -= step) { yield index; }
175
+ });
176
+ }
177
+
178
+ return new SmartIterator<number>(function* ()
179
+ {
54
180
  for (let index = start; index < end; index += step) { yield index; }
55
181
  });
56
182
  }
57
183
 
184
+ /**
185
+ * An utility function shuffles the elements of a given iterable.
186
+ *
187
+ * The function uses the {@link https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle|Fisher-Yates}
188
+ * algorithm to shuffle the elements.
189
+ *
190
+ * Also note that:
191
+ * - If the iterable is an `Array`, it won't be modified since the shuffling isn't done in-place.
192
+ * - If the iterable isn't an `Array`, it will be consumed entirely in the process.
193
+ * - If the iterable is an infinite generator, the function will never return.
194
+ *
195
+ * ```ts
196
+ * shuffle([1, 2, 3, 4, 5]); // [3, 1, 5, 2, 4]
197
+ * ```
198
+ *
199
+ * @template T The type of elements in the iterable.
200
+ *
201
+ * @param iterable The iterable to shuffle.
202
+ *
203
+ * @returns A new `Array` containing the shuffled elements of the given iterable.
204
+ */
58
205
  export function shuffle<T>(iterable: Iterable<T>): T[]
59
206
  {
60
207
  const array = Array.from(iterable);
@@ -69,12 +216,27 @@ export function shuffle<T>(iterable: Iterable<T>): T[]
69
216
  return array;
70
217
  }
71
218
 
219
+ /**
220
+ * An utility function that filters the elements of an iterable ensuring that they are all unique.
221
+ *
222
+ * ```ts
223
+ * for (const value of unique([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]))
224
+ * {
225
+ * console.log(value); // 1, 2, 3, 4, 5
226
+ * }
227
+ * ```
228
+ *
229
+ * @template T The type of elements in the iterable.
230
+ *
231
+ * @param elements The iterable to filter.
232
+ *
233
+ * @returns A {@link SmartIterator} object that iterates over the unique elements of the given iterable.
234
+ */
72
235
  export function unique<T>(elements: Iterable<T>): SmartIterator<T>
73
236
  {
74
237
  return new SmartIterator<T>(function* ()
75
238
  {
76
239
  const values = new Set<T>();
77
-
78
240
  for (const element of elements)
79
241
  {
80
242
  if (values.has(element)) { continue; }
@@ -86,13 +248,34 @@ export function unique<T>(elements: Iterable<T>): SmartIterator<T>
86
248
  });
87
249
  }
88
250
 
251
+ /**
252
+ * An utility function that zips two iterables into a single one.
253
+ * The resulting iterable will contain the elements of the two iterables paired together.
254
+ *
255
+ * The function will stop when one of the two iterables is exhausted.
256
+ *
257
+ * ```ts
258
+ * for (const [number, char] of zip([1, 2, 3, 4], ["A", "M", "N" "Z"]))
259
+ * {
260
+ * console.log(`${number} - ${char}`); // "1 - A", "2 - M", "3 - N", "4 - Z"
261
+ * }
262
+ * ```
263
+ *
264
+ * @template T The type of elements in the first iterable.
265
+ * @template U The type of elements in the second iterable.
266
+ *
267
+ * @param first The first iterable to zip.
268
+ * @param second The second iterable to zip.
269
+ *
270
+ * @returns A {@link SmartIterator} object that iterates over the zipped elements of the two given iterables.
271
+ */
89
272
  export function zip<T, U>(first: Iterable<T>, second: Iterable<U>): SmartIterator<[T, U]>
90
273
  {
274
+ const firstIterator = first[Symbol.iterator]();
275
+ const secondIterator = second[Symbol.iterator]();
276
+
91
277
  return new SmartIterator<[T, U]>(function* ()
92
278
  {
93
- const firstIterator = first[Symbol.iterator]();
94
- const secondIterator = second[Symbol.iterator]();
95
-
96
279
  while (true)
97
280
  {
98
281
  const firstResult = firstIterator.next();
package/src/utils/math.ts CHANGED
@@ -1,8 +1,31 @@
1
1
  import { ValueException } from "../models/exceptions/index.js";
2
2
  import { zip } from "./iterator.js";
3
3
 
4
- export function average<T extends number>(values: Iterable<T>): number;
5
- export function average<T extends number>(values: Iterable<T>, weights: Iterable<number>): number;
4
+ /**
5
+ * Computes the average of a given list of values.
6
+ * The values can be weighted using an additional list of weights.
7
+ *
8
+ * ```ts
9
+ * average([1, 2, 3, 4, 5]); // 3
10
+ * average([6, 8.5, 4], [3, 2, 1]); // 6.5
11
+ * ```
12
+ *
13
+ * @template T The type of the values in the list. It must be or extend a `number` object.
14
+ *
15
+ * @param values
16
+ * The list of values to compute the average.
17
+ *
18
+ * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
19
+ *
20
+ * @param weights
21
+ * The list of weights to apply to the values.
22
+ * It should contain the same number of elements as the values list or
23
+ * the smaller number of elements between the two lists will be considered.
24
+ *
25
+ * The sum of the weights must be greater than zero. Otherwise, a {@link ValueException} will be thrown.
26
+ *
27
+ * @returns The average of the specified values.
28
+ */
6
29
  export function average<T extends number>(values: Iterable<T>, weights?: Iterable<number>): number
7
30
  {
8
31
  if (weights === undefined)
@@ -38,11 +61,27 @@ export function average<T extends number>(values: Iterable<T>, weights?: Iterabl
38
61
  }
39
62
 
40
63
  if (_index === 0) { throw new ValueException("You must provide at least one value and weight."); }
41
- if (_count > 0) { throw new ValueException("The sum of weights must be greater than zero."); }
64
+ if (_count <= 0) { throw new ValueException("The sum of weights must be greater than zero."); }
42
65
 
43
66
  return _sum / _count;
44
67
  }
45
68
 
69
+ /**
70
+ * An utility function to compute the hash of a given string.
71
+ *
72
+ * The hash is computed using a simple variation of the
73
+ * {@link http://www.cse.yorku.ca/~oz/hash.html#djb2|djb2} algorithm.
74
+ * However, the hash is garanteed to be a 32-bit signed integer.
75
+ *
76
+ * ```ts
77
+ * hash("Hello, world!"); // -1880044555
78
+ * hash("How are you?"); // 1761539132
79
+ * ```
80
+ *
81
+ * @param value The string to hash.
82
+ *
83
+ * @returns The hash of the specified string.
84
+ */
46
85
  export function hash(value: string): number
47
86
  {
48
87
  let hashedValue = 0;
@@ -57,6 +96,19 @@ export function hash(value: string): number
57
96
  return hashedValue;
58
97
  }
59
98
 
99
+ /**
100
+ * Sums all the values of a given list.
101
+ *
102
+ * ```ts
103
+ * sum([1, 2, 3, 4, 5]); // 15
104
+ * ```
105
+ *
106
+ * @template T The type of the values in the list. It must be or extend a `number` object.
107
+ *
108
+ * @param values The list of values to sum.
109
+ *
110
+ * @returns The sum of the specified values.
111
+ */
60
112
  export function sum<T extends number>(values: Iterable<T>): number
61
113
  {
62
114
  let _sum = 0;
@@ -1,13 +1,61 @@
1
1
  import { ValueException } from "../models/index.js";
2
2
 
3
+ /**
4
+ * A wrapper class around the native {@link Math.random} function that
5
+ * provides a set of methods to generate random values more easily.
6
+ * It can be used to generate random numbers, booleans and other different values.
7
+ *
8
+ * It cannot be instantiated directly.
9
+ */
3
10
  export default class Random
4
11
  {
12
+ /**
13
+ * Generates a random boolean value.
14
+ *
15
+ * ```ts
16
+ * if (Random.Boolean())
17
+ * {
18
+ * // Do something...
19
+ * }
20
+ * ```
21
+ *
22
+ * @param ratio
23
+ * The probability of generating `true`.
24
+ *
25
+ * It must be included between `0` and `1`. Default is `0.5`.
26
+ *
27
+ * @returns A random boolean value.
28
+ */
5
29
  public static Boolean(ratio = 0.5): boolean
6
30
  {
7
31
  return (Math.random() < ratio);
8
32
  }
9
33
 
34
+ /**
35
+ * Generates a random integer value between `0` (included) and `max` (excluded).
36
+ *
37
+ * ```ts
38
+ * Random.Integer(5); // 0, 1, 2, 3, 4
39
+ * ```
40
+ *
41
+ * @param max The maximum value (excluded).
42
+ *
43
+ * @returns A random integer value.
44
+ */
10
45
  public static Integer(max: number): number;
46
+
47
+ /**
48
+ * Generates a random integer value between `min` (included) and `max` (excluded).
49
+ *
50
+ * ```ts
51
+ * Random.Integer(2, 7); // 2, 3, 4, 5, 6
52
+ * ```
53
+ *
54
+ * @param min The minimum value (included).
55
+ * @param max The maximum value (excluded).
56
+ *
57
+ * @returns A random integer value.
58
+ */
11
59
  public static Integer(min: number, max: number): number;
12
60
  public static Integer(min: number, max?: number): number
13
61
  {
@@ -16,8 +64,42 @@ export default class Random
16
64
  return Math.floor(Math.random() * (max - min) + min);
17
65
  }
18
66
 
67
+ /**
68
+ * Generates a random decimal value between `0` (included) and `1` (excluded).
69
+ *
70
+ * ```ts
71
+ * Random.Decimal(); // 0.123456789
72
+ * ```
73
+ *
74
+ * @returns A random decimal value.
75
+ */
19
76
  public static Decimal(): number;
77
+
78
+ /**
79
+ * Generates a random decimal value between `0` (included) and `max` (excluded).
80
+ *
81
+ * ```ts
82
+ * Random.Decimal(5); // 2.3456789
83
+ * ```
84
+ *
85
+ * @param max The maximum value (excluded).
86
+ *
87
+ * @returns A random decimal value.
88
+ */
20
89
  public static Decimal(max: number): number;
90
+
91
+ /**
92
+ * Generates a random decimal value between `min` (included) and `max` (excluded).
93
+ *
94
+ * ```ts
95
+ * Random.Decimal(2, 7); // 4.56789
96
+ * ```
97
+ *
98
+ * @param min The minimum value (included).
99
+ * @param max The maximum value (excluded).
100
+ *
101
+ * @returns A random decimal value
102
+ */
21
103
  public static Decimal(min: number, max: number): number;
22
104
  public static Decimal(min?: number, max?: number): number
23
105
  {
@@ -27,13 +109,38 @@ export default class Random
27
109
  return (Math.random() * (max - min) + min);
28
110
  }
29
111
 
30
- public static Index<T>(elements: T[]): number
112
+ /**
113
+ * Picks a random valid index from a given array of elements.
114
+ *
115
+ * @template T The type of the elements in the array.
116
+ *
117
+ * @param elements
118
+ * The array of elements to pick from.
119
+ *
120
+ * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
121
+ *
122
+ * @returns A valid random index from the given array.
123
+ */
124
+ public static Index<T>(elements: readonly T[]): number
31
125
  {
32
126
  if (elements.length === 0) { throw new ValueException("You must provide at least one element."); }
33
127
 
34
128
  return this.Integer(elements.length);
35
129
  }
36
- public static Choice<T>(elements: T[]): T
130
+
131
+ /**
132
+ * Picks a random element from a given array of elements.
133
+ *
134
+ * @template T The type of the elements in the array.
135
+ *
136
+ * @param elements
137
+ * The array of elements to pick from.
138
+ *
139
+ * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
140
+ *
141
+ * @returns A random element from the given array.
142
+ */
143
+ public static Choice<T>(elements: readonly T[]): T
37
144
  {
38
145
  return elements[Random.Index(elements)];
39
146
  }
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Capitalize the first letter of a string.
3
+ *
4
+ * ```ts
5
+ * capitalize('hello'); // 'Hello'
6
+ * ```
7
+ *
8
+ * @param value The string to capitalize.
9
+ *
10
+ * @returns The capitalized string.
11
+ */
1
12
  export function capitalize(value: string): string
2
13
  {
3
14
  return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
@@ -1,83 +0,0 @@
1
- import type { Interval } from "../core/types.js";
2
- import { isBrowser } from "../helpers.js";
3
-
4
- import { FatalErrorException, RuntimeException } from "./exceptions/index.js";
5
-
6
- export default class GameLoop
7
- {
8
- protected _handle?: number | Interval;
9
-
10
- protected _startTime: number;
11
- public get startTime(): number
12
- {
13
- return this._startTime;
14
- }
15
-
16
- protected _isRunning: boolean;
17
- public get isRunning(): boolean
18
- {
19
- return this._isRunning;
20
- }
21
-
22
- public get elapsedTime(): number
23
- {
24
- return performance.now() - this._startTime;
25
- }
26
-
27
- protected _start: () => void;
28
- protected _stop: () => void;
29
-
30
- public constructor(callback: FrameRequestCallback, msIfNotBrowser = 40)
31
- {
32
- this._startTime = 0;
33
- this._isRunning = false;
34
-
35
- if (isBrowser)
36
- {
37
- this._start = () =>
38
- {
39
- callback(this.elapsedTime);
40
-
41
- this._handle = window.requestAnimationFrame(this._start);
42
- };
43
-
44
- this._stop = () => window.cancelAnimationFrame(this._handle as number);
45
- }
46
- else
47
- {
48
- // eslint-disable-next-line no-console
49
- console.warn(
50
- "Not a browser environment detected. " +
51
- `Using setInterval@${msIfNotBrowser}ms instead of requestAnimationFrame...`
52
- );
53
-
54
- this._start = () =>
55
- {
56
- this._handle = setInterval(() => callback(this.elapsedTime), msIfNotBrowser);
57
- };
58
-
59
- this._stop = () => clearInterval(this._handle as Interval);
60
- }
61
- }
62
-
63
- public start(elapsedTime = 0): void
64
- {
65
- if (this._isRunning) { throw new RuntimeException("The game loop has already been started."); }
66
-
67
- this._startTime = performance.now() - elapsedTime;
68
- this._start();
69
- this._isRunning = true;
70
- }
71
-
72
- public stop(): void
73
- {
74
- if (!(this._isRunning)) { throw new RuntimeException("The game loop hadn't yet started."); }
75
- if (!(this._handle)) { throw new FatalErrorException(); }
76
-
77
- this._stop();
78
- this._handle = undefined;
79
- this._isRunning = false;
80
- }
81
-
82
- public readonly [Symbol.toStringTag]: string = "GameLoop";
83
- }