@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
@@ -1,19 +1,162 @@
1
- /* eslint-disable max-len */
2
-
3
1
  import type { MaybePromise } from "../promises/types.js";
4
2
 
3
+ /**
4
+ * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like function
5
+ * with the addition of a `key` parameter, compared to the JavaScript's standard ones.
6
+ * It can be used to transform the elements of an aggregated iterable.
7
+ *
8
+ * ```ts
9
+ * const iteratee: KeyedIteratee<string, number, string> = (key: string, value: number) => `${value}`;
10
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
11
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
12
+ * .map(iteratee);
13
+ *
14
+ * console.log(results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
15
+ * ```
16
+ *
17
+ * @template K The type of the key used to aggregate elements in the iterable.
18
+ * @template T The type of the elements in the iterable.
19
+ * @template R The type of the return value of the iteratee. Default is `void`.
20
+ */
5
21
  export type KeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, value: T, index: number) => R;
22
+
23
+ /**
24
+ * An utility type that represents an asynchronous {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like
25
+ * function with the addition of a `key` parameter.
26
+ * It can be used to transform the elements of an aggregated iterable asynchronously.
27
+ *
28
+ * ```ts
29
+ * const iteratee: AsyncKeyedIteratee<string, number, string> = async (key: string, value: number) => `${value}`;
30
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
31
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
32
+ * .map(iteratee);
33
+ *
34
+ * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
35
+ * ```
36
+ *
37
+ * @template K The type of the key used to aggregate elements in the iterable.
38
+ * @template T The type of the elements in the iterable.
39
+ * @template R The type of the return value of the iteratee. Default is `void`.
40
+ */
6
41
  export type AsyncKeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, value: T, index: number) => Promise<R>;
7
- export type MaybeAsyncKeyedIteratee<K extends PropertyKey, T, R = void> = (key: K, value: T, index: number) => MaybePromise<R>;
8
42
 
9
- export type KeyedTypeGuardIteratee<K extends PropertyKey, T, R extends T> = (key: K, value: T, index: number) => value is R;
43
+ /**
44
+ * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like function
45
+ * with the addition of a `key` parameter that can be either synchronous or asynchronous.
46
+ * It can be used to transform the elements of an aggregated iterable.
47
+ *
48
+ * ```ts
49
+ * const iteratee: AsyncKeyedIteratee<string, number, string> = [async] (key: string, value: number) => `${value}`;
50
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
51
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
52
+ * .map(iteratee);
53
+ *
54
+ * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] }
55
+ * ```
56
+ *
57
+ * @template K The type of the key used to aggregate elements in the iterable.
58
+ * @template T The type of the elements in the iterable.
59
+ * @template R The type of the return value of the iteratee. Default is `void`.
60
+ */
61
+ export type MaybeAsyncKeyedIteratee<K extends PropertyKey, T, R = void> =
62
+ (key: K, value: T, index: number) => MaybePromise<R>;
10
63
 
11
- // @ts-expect-error - This is an asyncronous type guard keyed-iteratee that guarantees the return value is a promise.
12
- export type AsyncKeyedTypeGuardIteratee<K extends PropertyKey, T, R extends T> = (key: K, value: T, index: number) => value is Promise<R>;
64
+ /**
65
+ * An utility type that represents a {@link https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)|predicate}-like
66
+ * function with the addition of a `key` parameter, compared to the JavaScript's standard ones,
67
+ * which act as a
68
+ * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}.
69
+ * It can be used to filter the elements of an aggregated iterable
70
+ * while allowing the type-system to infer them correctly.
71
+ *
72
+ * ```ts
73
+ * const predicate: KeyedTypeGuardPredicate<string, number | string, string> =
74
+ * (key: string, value: number | string): value is string => typeof value === "string";
75
+ *
76
+ * const results = new SmartIterator<number | string>([-3, -1, "0", 2, 3, "5", 6, "8"])
77
+ * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd")
78
+ * .filter(predicate);
79
+ *
80
+ * console.log(results.toObject()); // { odd: ["0", "5", "8"], even: [] }
81
+ * ```
82
+ *
83
+ * @template K The type of the key used to aggregate elements in the iterable.
84
+ * @template T The type of the elements in the iterable.
85
+ * @template R
86
+ * The type of the return value of the predicate.
87
+ * It must be a subtype of `T`. Default is `T`.
88
+ */
89
+ export type KeyedTypeGuardPredicate<K extends PropertyKey, T, R extends T> =
90
+ (key: K, value: T, index: number) => value is R;
13
91
 
14
- // @ts-expect-error - This may be an asyncronous type guard keyed-iteratee that guarantees the return value may be a promise.
15
- export type MaybeAsyncKeyedTypeGuardIteratee<K extends PropertyKey, T, R extends T> = (key: K, value: T, index: number) => value is MaybePromise<R>;
92
+ // These types need this Issue to be solved: https://github.com/microsoft/TypeScript/issues/37681
93
+ //
94
+ // export type AsyncKeyedTypeGuardPredicate<K extends PropertyKey, T, R extends T> =
95
+ // (key: K, value: T, index: number) => value is Promise<R>;
96
+ // export type MaybeAsyncKeyedTypeGuardPredicate<K extends PropertyKey, T, R extends T> =
97
+ // (key: K, value: T, index: number) => value is MaybePromise<R>;
16
98
 
99
+ /**
100
+ * An utility type that represents a reducer-like function.
101
+ * It can be used to reduce the elements of an aggregated iterable into a single value.
102
+ *
103
+ * ```ts
104
+ * const sum: KeyedReducer<string, number, number> =
105
+ * (key: string, accumulator: number, value: number) => accumulator + value;
106
+ *
107
+ * const results = new SmartIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
108
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
109
+ * .reduce(sum);
110
+ *
111
+ * console.log(results.toObject()); // { odd: 4, even: 16 }
112
+ * ```
113
+ *
114
+ * @template K The type of the key used to aggregate elements in the iterable.
115
+ * @template T The type of the elements in the iterable.
116
+ * @template A The type of the accumulator.
117
+ */
17
118
  export type KeyedReducer<K extends PropertyKey, T, A> = (key: K, accumulator: A, value: T, index: number) => A;
18
- export type AsyncKeyedReducer<K extends PropertyKey, T, A> = (key: K, accumulator: A, value: T, index: number) => Promise<A>;
19
- export type MaybeAsyncKeyedReducer<K extends PropertyKey, T, A> = (key: K, accumulator: A, value: T, index: number) => MaybePromise<A>;
119
+
120
+ /**
121
+ * An utility type that represents an asynchronous reducer-like function.
122
+ * It can be used to reduce the elements of an aggregated iterable into a single value.
123
+ *
124
+ * ```ts
125
+ * const sum: AsyncKeyedReducer<string, number, number> =
126
+ * async (key: string, accumulator: number, value: number) => accumulator + value;
127
+ *
128
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
129
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
130
+ * .reduce(sum);
131
+ *
132
+ * console.log(await results.toObject()); // { odd: 4, even: 16 }
133
+ * ```
134
+ *
135
+ * @template K The type of the key used to aggregate elements in the iterable.
136
+ * @template T The type of the elements in the iterable.
137
+ * @template A The type of the accumulator.
138
+ */
139
+ export type AsyncKeyedReducer<K extends PropertyKey, T, A> =
140
+ (key: K, accumulator: A, value: T, index: number) => Promise<A>;
141
+
142
+ /**
143
+ * An utility type that represents a reducer-like function that can be either synchronous or asynchronous.
144
+ * It can be used to reduce the elements of an aggregated iterable into a single value.
145
+ *
146
+ * ```ts
147
+ * const sum: MaybeAsyncKeyedReducer<string, number, number> =
148
+ * [async] (key: string, accumulator: number, value: number) => accumulator + value;
149
+ *
150
+ * const results = new SmartAsyncIterator<number>([-3, -1, 0, 2, 3, 5, 6, 8])
151
+ * .groupBy((value) => value % 2 === 0 ? "even" : "odd")
152
+ * .reduce(sum);
153
+ *
154
+ * console.log(await results.toObject()); // { odd: 4, even: 16 }
155
+ * ```
156
+ *
157
+ * @template K The type of the key used to aggregate elements in the iterable.
158
+ * @template T The type of the elements in the iterable.
159
+ * @template A The type of the accumulator.
160
+ */
161
+ export type MaybeAsyncKeyedReducer<K extends PropertyKey, T, A> =
162
+ (key: K, accumulator: A, value: T, index: number) => MaybePromise<A>;
@@ -2,16 +2,42 @@
2
2
 
3
3
  import type { Callback } from "./types.js";
4
4
 
5
- export const SmartFunction = (Function as unknown) as
6
- new<T extends Callback<any[], any> = () => void>(...args: string[]) =>
7
- (...args: Parameters<T>) => ReturnType<T>;
5
+ const SmartFunction = (Function as unknown) as new<A extends unknown[] = [], R = void>(...args: string[])
6
+ => (...args: A) => R;
8
7
 
8
+ /**
9
+ * An abstract class that can be used to implement callable objects.
10
+ *
11
+ * ```ts
12
+ * class ActivableCallback extends CallableObject<(evt: PointerEvent) => void>
13
+ * {
14
+ * public enabled = false;
15
+ * protected _invoke(): void
16
+ * {
17
+ * if (this.enabled) { [...] }
18
+ * }
19
+ * }
20
+ *
21
+ * const callback = new ActivableCallback();
22
+ *
23
+ * window.addEventListener("pointerdown", () => { callback.enabled = true; });
24
+ * window.addEventListener("pointermove", callback);
25
+ * window.addEventListener("pointerup", () => { callback.enabled = false; });
26
+ * ```
27
+ *
28
+ * @template T
29
+ * The type signature of the callback function.
30
+ * It must be a function. Default is `(...args: any[]) => any`.
31
+ */
9
32
  export default abstract class CallableObject<T extends Callback<any[], any> = () => void>
10
- extends SmartFunction<T>
33
+ extends SmartFunction<Parameters<T>, ReturnType<T>>
11
34
  {
35
+ /**
36
+ * Initializes a new instance of the {@link CallableObject} class.
37
+ */
12
38
  public constructor()
13
39
  {
14
- super(`return this.invoke(...arguments);`);
40
+ super(`return this._invoke(...arguments);`);
15
41
 
16
42
  const self = this.bind(this);
17
43
  Object.setPrototypeOf(this, self);
@@ -19,5 +45,15 @@ export default abstract class CallableObject<T extends Callback<any[], any> = ()
19
45
  return self as this;
20
46
  }
21
47
 
22
- public abstract invoke(...args: Parameters<T>): ReturnType<T>;
48
+ /**
49
+ * The method that will be called when the object is invoked.
50
+ * It must be implemented by the derived classes.
51
+ *
52
+ * @param args The arguments that have been passed to the object.
53
+ *
54
+ * @returns The return value of the method.
55
+ */
56
+ protected abstract _invoke(...args: Parameters<T>): ReturnType<T>;
57
+
58
+ public readonly [Symbol.toStringTag]: string = "CallableObject";
23
59
  }
@@ -1,5 +1,5 @@
1
- import CallableObject, { SmartFunction } from "./callable-object.js";
1
+ import CallableObject from "./callable-object.js";
2
2
  import Publisher from "./publisher.js";
3
3
  import SwitchableCallback from "./switchable-callback.js";
4
4
 
5
- export { CallableObject, Publisher, SmartFunction, SwitchableCallback };
5
+ export { CallableObject, Publisher, SwitchableCallback };
@@ -2,16 +2,146 @@ import { ReferenceException } from "../exceptions/index.js";
2
2
 
3
3
  import type { Callback } from "./types.js";
4
4
 
5
+ /**
6
+ * A class implementing the
7
+ * {@link https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern|Publish-subscribe} pattern.
8
+ *
9
+ * It can be used to create a simple event system where objects can subscribe
10
+ * to events and receive notifications when the events are published.
11
+ * It's a simple and efficient way to decouple the objects and make them communicate with each other.
12
+ *
13
+ * Using generics, it's also possible to define the type of the events and the callbacks that can be subscribed to them.
14
+ *
15
+ * ```ts
16
+ * interface EventsMap
17
+ * {
18
+ * "player:spawn": (evt: SpawnEvent) => void;
19
+ * "player:move": ({ x, y }: Point) => void;
20
+ * "player:death": () => void;
21
+ * }
22
+ *
23
+ * const publisher = new Publisher<EventsMap>();
24
+ *
25
+ * let unsubscribe: () => void;
26
+ * publisher.subscribe("player:death", unsubscribe);
27
+ * publisher.subscribe("player:spawn", (evt) =>
28
+ * {
29
+ * unsubscribe = publisher.subscribe("player:move", ({ x, y }) => { [...] });
30
+ * });
31
+ * ```
32
+ *
33
+ * @template T
34
+ * A map containing the names of the emittable events and the
35
+ * related callback signatures that can be subscribed to them.
36
+ * Default is `Record<string, () => void>`.
37
+ */
5
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
39
  export default class Publisher<T extends { [K in keyof T]: Callback<any[], any> } = Record<string, Callback>>
7
40
  {
41
+ /**
42
+ * A map containing all the subscribers for each event.
43
+ *
44
+ * The keys are the names of the events they are subscribed to.
45
+ * The values are the arrays of the subscribers themselves.
46
+ */
8
47
  protected _subscribers: Map<keyof T, Callback<unknown[], unknown>[]>;
9
48
 
49
+ /**
50
+ * Initializes a new instance of the {@link Publisher} class.
51
+ *
52
+ * ---
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const publisher = new Publisher();
57
+ * ```
58
+ */
10
59
  public constructor()
11
60
  {
12
61
  this._subscribers = new Map();
13
62
  }
14
63
 
64
+ /**
65
+ * Unsubscribes all the subscribers from all the events.
66
+ *
67
+ * ---
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * publisher.subscribe("player:spawn", (evt) => { [...] });
72
+ * publisher.subscribe("player:move", (coords) => { [...] });
73
+ * publisher.subscribe("player:move", () => { [...] });
74
+ * publisher.subscribe("player:move", ({ x, y }) => { [...] });
75
+ * publisher.subscribe("player:death", () => { [...] });
76
+ *
77
+ * // All these subscribers are working fine...
78
+ *
79
+ * publisher.clear();
80
+ *
81
+ * // ... but now they're all gone!
82
+ * ```
83
+ */
84
+ public clear(): void
85
+ {
86
+ this._subscribers.clear();
87
+ }
88
+
89
+ /**
90
+ * Publishes an event to all the subscribers.
91
+ *
92
+ * ---
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * publisher.subscribe("player:move", (coords) => { [...] });
97
+ * publisher.subscribe("player:move", ({ x, y }) => { [...] });
98
+ * publisher.subscribe("player:move", (evt) => { [...] });
99
+ *
100
+ * publisher.publish("player:move", { x: 10, y: 20 });
101
+ * ```
102
+ *
103
+ * ---
104
+ *
105
+ * @template K The key of the map containing the callback signature to publish.
106
+ *
107
+ * @param event The name of the event to publish.
108
+ * @param args The arguments to pass to the subscribers.
109
+ *
110
+ * @returns An array containing the return values of all the subscribers.
111
+ */
112
+ public publish<K extends keyof T>(event: K, ...args: Parameters<T[K]>): ReturnType<T[K]>[]
113
+ {
114
+ const subscribers = this._subscribers.get(event);
115
+ if (!(subscribers)) { return []; }
116
+
117
+ return subscribers.slice()
118
+ .map((subscriber) => subscriber(...args)) as ReturnType<T[K]>[];
119
+ }
120
+
121
+ /**
122
+ * Subscribes a new subscriber to an event.
123
+ *
124
+ * ---
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * let unsubscribe: () => void;
129
+ * publisher.subscribe("player:death", unsubscribe);
130
+ * publisher.subscribe("player:spawn", (evt) =>
131
+ * {
132
+ * unsubscribe = publisher.subscribe("player:move", ({ x, y }) => { [...] });
133
+ * });
134
+ * ```
135
+ *
136
+ * ---
137
+ *
138
+ * @template K The key of the map containing the callback signature to subscribe.
139
+ *
140
+ * @param event The name of the event to subscribe to.
141
+ * @param subscriber The subscriber to add to the event.
142
+ *
143
+ * @returns A function that can be used to unsubscribe the subscriber.
144
+ */
15
145
  public subscribe<K extends keyof T>(event: K, subscriber: T[K]): () => void
16
146
  {
17
147
  if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); }
@@ -32,13 +162,39 @@ export default class Publisher<T extends { [K in keyof T]: Callback<any[], any>
32
162
  };
33
163
  }
34
164
 
35
- public publish<K extends keyof T>(event: K, ...args: Parameters<T[K]>): ReturnType<T[K]>[]
165
+ /**
166
+ * Unsubscribes a subscriber from an event.
167
+ *
168
+ * ---
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * const onPlayerMove = ({ x, y }: Point) => { [...] };
173
+ *
174
+ * publisher.subscribe("player:spawn", (evt) => publisher.subscribe("player:move", onPlayerMove));
175
+ * publisher.subscribe("player:death", () => publisher.unsubscribe("player:move", onPlayerMove));
176
+ * ```
177
+ *
178
+ * ---
179
+ *
180
+ * @template K The key of the map containing the callback signature to unsubscribe.
181
+ *
182
+ * @param event The name of the event to unsubscribe from.
183
+ * @param subscriber The subscriber to remove from the event.
184
+ */
185
+ public unsubscribe<K extends keyof T>(event: K, subscriber: T[K]): void
36
186
  {
37
187
  const subscribers = this._subscribers.get(event);
38
- if (!(subscribers)) { return []; }
188
+ if (!(subscribers)) { return; }
39
189
 
40
- return subscribers.slice()
41
- .map((subscriber) => subscriber(...args)) as ReturnType<T[K]>[];
190
+ const index = subscribers.indexOf(subscriber);
191
+ if (index < 0)
192
+ {
193
+ throw new ReferenceException("Unable to unsubscribe the required subscriber. " +
194
+ "The subscription was already unsubscribed or was never subscribed.");
195
+ }
196
+
197
+ subscribers.splice(index, 1);
42
198
  }
43
199
 
44
200
  public readonly [Symbol.toStringTag]: string = "Publisher";