From 8d3b1ae20061a369d3207196668c559a7d89cfff Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Wed, 18 Dec 2024 22:20:30 +0100 Subject: [PATCH 01/32] wip: Some JSDoc drafts & tests... --- eslint.config.mjs | 1 + src/models/json/json-storage.ts | 209 +++++++++++++++++++------------- 2 files changed, 125 insertions(+), 85 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 8ce942c..fbb46c4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -10,6 +10,7 @@ const gitignorePath = path.resolve(__dirname, ".gitignore"); export default [includeIgnoreFile(gitignorePath), ...eslintTs, { rules: { + "no-trailing-spaces": ["error", { "ignoreComments": true }], "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/unified-signatures": "off" } diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index e6cf334..661cbdb 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -5,10 +5,11 @@ import { EnvironmentException } from "../exceptions/index.js"; import type { JSONValue } from "./types.js"; /** - * A wrapper around the `Storage` API to store and retrieve JSON values. + * A wrapper around the `Storage` API to better store and easily retrieve JSON values + * using the classical key-value pair storage system. * * It allows to handle either the `sessionStorage` or the `localStorage` - * storage at the same time, depending on the required use case. + * storage at the same time, depending on what's your required use case. */ export default class JSONStorage { @@ -17,10 +18,22 @@ export default class JSONStorage protected _volatile: Storage; protected _persistent: Storage; + /** + * Initializes a new instance of the `JSONStorage` class. + * It cannot be instantiated outside of a browser environment or an `EnvironmentException` is thrown. + * + * ```ts + * const jsonStorage = new JSONStorage(); + * ``` + * + * --- + * + * @param preferPersistence + * Whether to prefer the `localStorage` over the `sessionStorage` when calling an ambivalent method. + * If omitted, it defaults to `true` to prefer the persistent storage. + */ public constructor(preferPersistence = true) { - this._preferPersistence = preferPersistence; - if (!(isBrowser)) { throw new EnvironmentException( @@ -28,227 +41,253 @@ export default class JSONStorage ); } + this._preferPersistence = preferPersistence; + this._volatile = window.sessionStorage; this._persistent = window.localStorage; } - protected _get(storage: Storage, propertyName: string): T | undefined; - protected _get(storage: Storage, propertyName: string, defaultValue: T): T; - protected _get(storage: Storage, propertyName: string, defaultValue?: T): T | undefined; - protected _get(storage: Storage, propertyName: string, defaultValue?: T): T | undefined + protected _get(storage: Storage, key: string): T | undefined; + protected _get(storage: Storage, key: string, defaultValue: T): T; + protected _get(storage: Storage, key: string, defaultValue?: T): T | undefined; + protected _get(storage: Storage, key: string, defaultValue?: T): T | undefined { - const propertyValue = storage.getItem(propertyName); - if (propertyValue) + const value = storage.getItem(key); + if (value) { try { - return JSON.parse(propertyValue); + return JSON.parse(value); } catch { // eslint-disable-next-line no-console console.warn( - `The "${propertyValue}" value for "${propertyName}"` + + `The "${value}" value for "${key}"` + " property cannot be parsed. Clearing the storage..."); - storage.removeItem(propertyName); + storage.removeItem(key); } } return defaultValue; } - protected _set(storage: Storage, propertyName: string, newValue?: T): void + protected _set(storage: Storage, key: string, newValue?: T): void { const encodedValue = JSON.stringify(newValue); if (encodedValue) { - storage.setItem(propertyName, encodedValue); + storage.setItem(key, encodedValue); } else { - storage.removeItem(propertyName); + storage.removeItem(key); } } /** - * Retrieves the value with the specified name from the corresponding storage. + * Ambivalent getter method that retrieves the value with the specified key from the default storage. + * + * ```ts + * const value: TValue = jsonStorage.get("key"); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * + * @returns The value with the specified key or `undefined` if the property doesn't exist. + */ + public get(key: string): T | undefined; + + /** + * Retrieves the value with the specified name from the default storage. + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the property doesn't exist. + * @param persistent Whether to override the default storage preference. + * + * @returns The value of the property or the default value if the property doesn't exist. + */ + public get(key: string, defaultValue: T, persistent?: boolean): T; + + /** + * Retrieves the value with the specified name from the default storage. * - * @param propertyName The name of the property to retrieve. - * @param defaultValue The default value to return if the property does not exist. - * @param persistent Whether to use the persistent `localStorage` or the volatile `sessionStorage`. + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the property doesn't exist. + * @param persistent Whether to override the default storage preference. * - * @returns The value of the property or the default value if the property does not exist. + * @returns The value of the property or the default value if the property doesn't exist. */ - public get(propertyName: string, defaultValue: undefined, persistent?: boolean): T | undefined; - public get(propertyName: string, defaultValue: T, persistent?: boolean): T ; - public get(propertyName: string, defaultValue?: T, persistent?: boolean): T | undefined; - public get(propertyName: string, defaultValue?: T, persistent = this._preferPersistence) + public get(key: string, defaultValue?: T, persistent?: boolean): T | undefined; + public get(key: string, defaultValue?: T, persistent = this._preferPersistence) : T | undefined { const storage = persistent ? this._persistent : this._volatile; - return this._get(storage, propertyName, defaultValue); + return this._get(storage, key, defaultValue); } /** * Retrieves the value with the specified name from the volatile `sessionStorage`. * - * @param propertyName The name of the property to retrieve. - * @param defaultValue The default value to return if the property does not exist. + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property does not exist. + * @returns The value of the property or the default value if the property doesn't exist. */ - public recall(propertyName: string): T | undefined; - public recall(propertyName: string, defaultValue: T): T; - public recall(propertyName: string, defaultValue?: T): T | undefined; - public recall(propertyName: string, defaultValue?: T): T | undefined + public recall(key: string): T | undefined; + public recall(key: string, defaultValue: T): T; + public recall(key: string, defaultValue?: T): T | undefined; + public recall(key: string, defaultValue?: T): T | undefined { - return this._get(this._volatile, propertyName, defaultValue); + return this._get(this._volatile, key, defaultValue); } /** * Retrieves the value with the specified name looking first in the * volatile `sessionStorage` and then in the persistent `localStorage`. * - * @param propertyName The name of the property to retrieve. - * @param defaultValue The default value to return if the property does not exist. + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property does not exist. + * @returns The value of the property or the default value if the property doesn't exist. */ - public retrieve(propertyName: string): T | undefined; - public retrieve(propertyName: string, defaultValue: T): T; - public retrieve(propertyName: string, defaultValue?: T): T | undefined; - public retrieve(propertyName: string, defaultValue?: T): T | undefined + public retrieve(key: string): T | undefined; + public retrieve(key: string, defaultValue: T): T; + public retrieve(key: string, defaultValue?: T): T | undefined; + public retrieve(key: string, defaultValue?: T): T | undefined { - return this.recall(propertyName) ?? this.read(propertyName, defaultValue); + return this.recall(key) ?? this.read(key, defaultValue); } /** * Retrieves the value with the specified name from the persistent `localStorage`. * - * @param propertyName The name of the property to retrieve. - * @param defaultValue The default value to return if the property does not exist. + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property does not exist. + * @returns The value of the property or the default value if the property doesn't exist. */ - public read(propertyName: string): T | undefined; - public read(propertyName: string, defaultValue: T): T; - public read(propertyName: string, defaultValue?: T): T | undefined; - public read(propertyName: string, defaultValue?: T): T | undefined + public read(key: string): T | undefined; + public read(key: string, defaultValue: T): T; + public read(key: string, defaultValue?: T): T | undefined; + public read(key: string, defaultValue?: T): T | undefined { - return this._get(this._persistent, propertyName, defaultValue); + return this._get(this._persistent, key, defaultValue); } /** - * Checks whether the property with the specified name exists in the corresponding storage. + * Checks whether the property with the specified name exists in the default storage. * - * @param propertyName The name of the property to check. - * @param persistent Whether to use the persistent `localStorage` or the volatile `sessionStorage`. + * @param key The key of the value to check. + * @param persistent Whether to override the default storage preference. * * @returns `true` if the property exists, `false` otherwise. */ - public has(propertyName: string, persistent?: boolean): boolean + public has(key: string, persistent?: boolean): boolean { const storage = persistent ? this._persistent : this._volatile; - return storage.getItem(propertyName) !== null; + return storage.getItem(key) !== null; } /** * Checks whether the property with the specified name exists in the volatile `sessionStorage`. * - * @param propertyName The name of the property to check. + * @param key The key of the value to check. * * @returns `true` if the property exists, `false` otherwise. */ - public knows(propertyName: string): boolean + public knows(key: string): boolean { - return this._volatile.getItem(propertyName) !== null; + return this._volatile.getItem(key) !== null; } /** * Checks whether the property with the specified name exists looking first in the * volatile `sessionStorage` and then in the persistent `localStorage`. * - * @param propertyName The name of the property to check. + * @param key The key of the value to check. * * @returns `true` if the property exists, `false` otherwise. */ - public find(propertyName: string): boolean + public find(key: string): boolean { - return this.knows(propertyName) ?? this.exists(propertyName); + return this.knows(key) ?? this.exists(key); } /** * Checks whether the property with the specified name exists in the persistent `localStorage`. * - * @param propertyName The name of the property to check. + * @param key The key of the value to check. * * @returns `true` if the property exists, `false` otherwise. */ - public exists(propertyName: string): boolean + public exists(key: string): boolean { - return this._persistent.getItem(propertyName) !== null; + return this._persistent.getItem(key) !== null; } /** - * Sets the value with the specified name in the corresponding storage. + * Sets the value with the specified name in the default storage. * If the value is `undefined`, the property is removed from the storage. * - * @param propertyName The name of the property to set. + * @param key The key of the value to set. * @param newValue The new value to set. - * @param persistent Whether to use the persistent `localStorage` or the volatile `sessionStorage`. + * @param persistent Whether to override the default storage preference. */ - public set(propertyName: string, newValue?: T, persistent = this._preferPersistence): void + public set(key: string, newValue?: T, persistent = this._preferPersistence): void { const storage = persistent ? this._persistent : this._volatile; - this._set(storage, propertyName, newValue); + this._set(storage, key, newValue); } /** * Sets the value with the specified name in the volatile `sessionStorage`. * If the value is `undefined`, the property is removed from the storage. * - * @param propertyName The name of the property to set. + * @param key The key of the value to set. * @param newValue The new value to set. */ - public remember(propertyName: string, newValue?: T): void + public remember(key: string, newValue?: T): void { - this._set(this._volatile, propertyName, newValue); + this._set(this._volatile, key, newValue); } /** * Sets the value with the specified name in the persistent `localStorage`. * If the value is `undefined`, the property is removed from the storage. * - * @param propertyName The name of the property to set. + * @param key The key of the value to set. * @param newValue The new value to set. */ - public write(propertyName: string, newValue?: T): void + public write(key: string, newValue?: T): void { - this._set(this._persistent, propertyName, newValue); + this._set(this._persistent, key, newValue); } /** * Removes the value with the specified name from the volatile `sessionStorage`. * - * @param propertyName The name of the property to remove. + * @param key The key of the value to remove. */ - public forget(propertyName: string): void + public forget(key: string): void { - this._volatile.removeItem(propertyName); + this._volatile.removeItem(key); } /** * Removes the value with the specified name from the persistent `localStorage`. * - * @param propertyName The name of the property to remove. + * @param key The key of the value to remove. */ - public erase(propertyName: string): void + public erase(key: string): void { - this._persistent.removeItem(propertyName); + this._persistent.removeItem(key); } /** * Removes the value with the specified name from all the storages. * - * @param propertyName The name of the property to remove. + * @param key The key of the value to remove. */ - public clear(propertyName: string): void + public clear(key: string): void { - this._volatile.removeItem(propertyName); - this._persistent.removeItem(propertyName); + this._volatile.removeItem(key); + this._persistent.removeItem(key); } public readonly [Symbol.toStringTag]: string = "JSONStorage"; From 97f88c50ebb19fb3ef3f926905df988122a65eb4 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 22 Dec 2024 15:07:34 +0100 Subject: [PATCH 02/32] add: JSDoc for `JSONStorage` class. --- src/models/json/json-storage.ts | 334 +++++++++++++++++++++++++++----- 1 file changed, 285 insertions(+), 49 deletions(-) diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index 661cbdb..05aeecb 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -1,15 +1,14 @@ - import { isBrowser } from "../../helpers.js"; import { EnvironmentException } from "../exceptions/index.js"; import type { JSONValue } from "./types.js"; /** - * A wrapper around the `Storage` API to better store and easily retrieve JSON values - * using the classical key-value pair storage system. + * A wrapper around the `Storage` API to better store and easily retrieve + * typed JSON values using the classical key-value pair storage system. * - * It allows to handle either the `sessionStorage` or the `localStorage` - * storage at the same time, depending on what's your required use case. + * It allows to handle either the volatile `sessionStorage` or the persistent + * `localStorage` at the same time, depending on what's your required use case. */ export default class JSONStorage { @@ -29,7 +28,7 @@ export default class JSONStorage * --- * * @param preferPersistence - * Whether to prefer the `localStorage` over the `sessionStorage` when calling an ambivalent method. + * Whether to prefer the `localStorage` over the `sessionStorage` when calling an ambivalent method. * If omitted, it defaults to `true` to prefer the persistent storage. */ public constructor(preferPersistence = true) @@ -86,7 +85,7 @@ export default class JSONStorage } /** - * Ambivalent getter method that retrieves the value with the specified key from the default storage. + * Retrieves the value with the specified key from the default storage. * * ```ts * const value: TValue = jsonStorage.get("key"); @@ -96,29 +95,45 @@ export default class JSONStorage * * @param key The key of the value to retrieve. * - * @returns The value with the specified key or `undefined` if the property doesn't exist. + * @returns The value with the specified key or `undefined` if the key doesn't exist. */ public get(key: string): T | undefined; /** - * Retrieves the value with the specified name from the default storage. + * Retrieves the value with the specified key from the default storage. + * + * ```ts + * const value: TValue = jsonStorage.get("key", defaultValue); + * ``` + * + * --- * * @param key The key of the value to retrieve. - * @param defaultValue The default value to return if the property doesn't exist. - * @param persistent Whether to override the default storage preference. + * @param defaultValue The default value to return if the key doesn't exist. + * @param persistent + * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * If omitted, it defaults to the `preferPersistence` value set in the constructor. * - * @returns The value of the property or the default value if the property doesn't exist. + * @returns The value with the specified key or the provided default value if the key doesn't exist. */ public get(key: string, defaultValue: T, persistent?: boolean): T; /** - * Retrieves the value with the specified name from the default storage. + * Retrieves the value with the specified key from the default storage. + * + * ```ts + * const value: TValue = jsonStorage.get("key", obj?.value); + * ``` + * + * --- * * @param key The key of the value to retrieve. - * @param defaultValue The default value to return if the property doesn't exist. - * @param persistent Whether to override the default storage preference. + * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. + * @param persistent + * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * If omitted, it defaults to the `preferPersistence` value set in the constructor. * - * @returns The value of the property or the default value if the property doesn't exist. + * @returns The value with the specified key or the default value if the key doesn't exist. */ public get(key: string, defaultValue?: T, persistent?: boolean): T | undefined; public get(key: string, defaultValue?: T, persistent = this._preferPersistence) @@ -128,47 +143,157 @@ export default class JSONStorage return this._get(storage, key, defaultValue); } + /** - * Retrieves the value with the specified name from the volatile `sessionStorage`. + * Retrieves the value with the specified key from the volatile `sessionStorage`. + * + * ```ts + * const value: TValue = jsonStorage.recall("key"); + * ``` + * + * --- * * @param key The key of the value to retrieve. - * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property doesn't exist. + * @returns The value with the specified key or `undefined` if the key doesn't exist. */ public recall(key: string): T | undefined; + + /** + * Retrieves the value with the specified key from the volatile `sessionStorage`. + * + * ```ts + * const value: TValue = jsonStorage.recall("key", defaultValue); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public recall(key: string, defaultValue: T): T; + + /** + * Retrieves the value with the specified key from the volatile `sessionStorage`. + * + * ```ts + * const value: TValue = jsonStorage.recall("key", obj?.value); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public recall(key: string, defaultValue?: T): T | undefined; public recall(key: string, defaultValue?: T): T | undefined { return this._get(this._volatile, key, defaultValue); } + /** - * Retrieves the value with the specified name looking first in the - * volatile `sessionStorage` and then in the persistent `localStorage`. + * Retrieves the value with the specified key looking first in the volatile + * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.retrieve("key"); + * ``` + * + * --- * * @param key The key of the value to retrieve. - * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property doesn't exist. + * @returns The value with the specified key or `undefined` if the key doesn't exist. */ public retrieve(key: string): T | undefined; + + /** + * Retrieves the value with the specified key looking first in the volatile + * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.retrieve("key", defaultValue); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public retrieve(key: string, defaultValue: T): T; + + /** + * Retrieves the value with the specified key looking first in the volatile + * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.retrieve("key", obj?.value); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public retrieve(key: string, defaultValue?: T): T | undefined; public retrieve(key: string, defaultValue?: T): T | undefined { return this.recall(key) ?? this.read(key, defaultValue); } + /** - * Retrieves the value with the specified name from the persistent `localStorage`. + * Retrieves the value with the specified key from the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.read("key"); + * ``` + * + * --- * * @param key The key of the value to retrieve. - * @param defaultValue The default value to return if the property doesn't exist. * - * @returns The value of the property or the default value if the property doesn't exist. + * @returns The value with the specified key or `undefined` if the key doesn't exist. */ public read(key: string): T | undefined; + + /** + * Retrieves the value with the specified key from the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.read("key", defaultValue); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public read(key: string, defaultValue: T): T; + + /** + * Retrieves the value with the specified key from the persistent `localStorage`. + * + * ```ts + * const value: TValue = jsonStorage.read("key", obj?.value); + * ``` + * + * --- + * + * @param key The key of the value to retrieve. + * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. + * + * @returns The value with the specified key or the default value if the key doesn't exist. + */ public read(key: string, defaultValue?: T): T | undefined; public read(key: string, defaultValue?: T): T | undefined { @@ -176,12 +301,23 @@ export default class JSONStorage } /** - * Checks whether the property with the specified name exists in the default storage. + * Checks whether the value with the specified key exists within the default storage. + * + * ```ts + * if (jsonStorage.has("key")) + * { + * // The key exists. Do something... + * } + * ``` + * + * --- * * @param key The key of the value to check. - * @param persistent Whether to override the default storage preference. + * @param persistent + * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * If omitted, it defaults to the `preferPersistence` value set in the constructor. * - * @returns `true` if the property exists, `false` otherwise. + * @returns `true` if the key exists, `false` otherwise. */ public has(key: string, persistent?: boolean): boolean { @@ -189,35 +325,65 @@ export default class JSONStorage return storage.getItem(key) !== null; } + /** - * Checks whether the property with the specified name exists in the volatile `sessionStorage`. + * Checks whether the value with the specified key exists within the volatile `sessionStorage`. + * + * ```ts + * if (jsonStorage.knows("key")) + * { + * // The key exists. Do something... + * } + * ``` + * + * --- * * @param key The key of the value to check. * - * @returns `true` if the property exists, `false` otherwise. + * @returns `true` if the key exists, `false` otherwise. */ public knows(key: string): boolean { return this._volatile.getItem(key) !== null; } + /** - * Checks whether the property with the specified name exists looking first in the - * volatile `sessionStorage` and then in the persistent `localStorage`. + * Checks whether the value with the specified key exists looking first in the + * volatile `sessionStorage` and then, if not found, in the persistent `localStorage`. + * + * ```ts + * if (jsonStorage.find("key")) + * { + * // The key exists. Do something... + * } + * ``` + * + * --- * * @param key The key of the value to check. * - * @returns `true` if the property exists, `false` otherwise. + * @returns `true` if the key exists, `false` otherwise. */ public find(key: string): boolean { return this.knows(key) ?? this.exists(key); } + /** - * Checks whether the property with the specified name exists in the persistent `localStorage`. + * Checks whether the value with the specified key exists within the persistent `localStorage`. + * + * ```ts + * if (jsonStorage.exists("key")) + * { + * // The key exists. Do something... + * } + * ``` + * + * --- * * @param key The key of the value to check. * - * @returns `true` if the property exists, `false` otherwise. + * @returns `true` if the key exists, `false` otherwise. */ public exists(key: string): boolean { @@ -225,12 +391,22 @@ export default class JSONStorage } /** - * Sets the value with the specified name in the default storage. - * If the value is `undefined`, the property is removed from the storage. + * Sets the value with the specified key in the default storage. + * If the value is `undefined` or omitted, the key is removed from the storage. + * + * ```ts + * jsonStorage.set("key"); + * jsonStorage.set("key", value); + * jsonStorage.set("key", obj?.value); + * ``` + * + * --- * * @param key The key of the value to set. - * @param newValue The new value to set. - * @param persistent Whether to override the default storage preference. + * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. + * @param persistent + * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * If omitted, it defaults to the `preferPersistence` value set in the constructor. */ public set(key: string, newValue?: T, persistent = this._preferPersistence): void { @@ -238,23 +414,41 @@ export default class JSONStorage this._set(storage, key, newValue); } + /** - * Sets the value with the specified name in the volatile `sessionStorage`. - * If the value is `undefined`, the property is removed from the storage. + * Sets the value with the specified key in the volatile `sessionStorage`. + * If the value is `undefined` or omitted, the key is removed from the storage. + * + * ```ts + * jsonStorage.remember("key"); + * jsonStorage.remember("key", value); + * jsonStorage.remember("key", obj?.value); + * ``` + * + * --- * * @param key The key of the value to set. - * @param newValue The new value to set. + * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. */ public remember(key: string, newValue?: T): void { this._set(this._volatile, key, newValue); } + /** - * Sets the value with the specified name in the persistent `localStorage`. - * If the value is `undefined`, the property is removed from the storage. + * Sets the value with the specified key in the persistent `localStorage`. + * If the value is `undefined` or omitted, the key is removed from the storage. + * + * ```ts + * jsonStorage.write("key"); + * jsonStorage.write("key", value); + * jsonStorage.write("key", obj?.value); + * ``` + * + * --- * * @param key The key of the value to set. - * @param newValue The new value to set. + * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. */ public write(key: string, newValue?: T): void { @@ -262,7 +456,34 @@ export default class JSONStorage } /** - * Removes the value with the specified name from the volatile `sessionStorage`. + * Removes the value with the specified key from the default storage. + * + * ```ts + * jsonStorage.delete("key"); + * ``` + * + * --- + * + * @param key The key of the value to remove. + * @param persistent + * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * If omitted, it defaults to the `preferPersistence` value set in the constructor. + */ + public delete(key: string, persistent?: boolean): void + { + const storage = persistent ? this._persistent : this._volatile; + + storage.removeItem(key); + } + + /** + * Removes the value with the specified key from the volatile `sessionStorage`. + * + * ```ts + * jsonStorage.forget("key"); + * ``` + * + * --- * * @param key The key of the value to remove. */ @@ -270,8 +491,15 @@ export default class JSONStorage { this._volatile.removeItem(key); } + /** - * Removes the value with the specified name from the persistent `localStorage`. + * Removes the value with the specified key from the persistent `localStorage`. + * + * ```ts + * jsonStorage.erase("key"); + * ``` + * + * --- * * @param key The key of the value to remove. */ @@ -279,8 +507,16 @@ export default class JSONStorage { this._persistent.removeItem(key); } + /** - * Removes the value with the specified name from all the storages. + * Removes the value with the specified key from both the + * volatile `sessionStorage` and the persistent `localStorage`. + * + * ```ts + * jsonStorage.clear("key"); + * ``` + * + * --- * * @param key The key of the value to remove. */ From a197ab884d9a7646e9258595c16f082c973ae56d Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 22 Dec 2024 18:41:05 +0100 Subject: [PATCH 03/32] add: Added JSDoc for `core`, `models/json`, `helpers` & almost all `utils` files. --- src/core/types.ts | 41 ++++++++++++ src/helpers.ts | 11 ++- src/index.ts | 2 + src/models/json/types.ts | 10 +++ src/utils/async.ts | 49 ++++++++++++++ src/utils/curve.ts | 58 +++++++++++++++- src/utils/date.ts | 141 +++++++++++++++++++++++++++++++++++++++ src/utils/dom.ts | 20 +++++- src/utils/index.ts | 2 +- src/utils/random.ts | 114 +++++++++++++++++++++++++++++++ src/utils/string.ts | 13 ++++ 11 files changed, 454 insertions(+), 7 deletions(-) diff --git a/src/core/types.ts b/src/core/types.ts index beec26c..0cc568e 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,5 +1,46 @@ +/** + * A utility type that allows to define a class constructor of a specific type. + * Is the counterpart of the native `InstanceType` utility type. + * + * ```ts + * function factory(Factory: Constructor): T { [...] } + * + * const instance: MyObject = factory(MyObject); + * ``` + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Constructor = new (...args: P) => T; +/** + * A type representing the return value of `setInterval` function, + * indipendently from the platform it's currently running on. + * + * For instance, in a browser environment, it's a `number` value representing the interval ID. + * In a Node.js environment, on the other hand, it's an object of type `NodeJS.Timeout`. + * + * This allows to seamlessly use the same code in both environments, without having to deal with the differences: + * + * ```ts + * const intervalId: Interval = setInterval(() => { [...] }, 1000); + * + * clearInterval(intervalId); + * ``` + */ export type Interval = ReturnType; + +/** + * A type representing the return value of `setTimeout` function, + * indipendently from the platform it's currently running on. + * + * For instance, in a browser environment, it's a `number` value representing the timeout ID. + * In a Node.js environment, on the other hand, it's an object of type `NodeJS.Timeout`. + * + * This allows to seamlessly use the same code in both environments, without having to deal with the differences: + * + * ```ts + * const timeoutId: Timeout = setTimeout(() => { [...] }, 1000); + * + * clearTimeout(timeoutId); + * ``` + */ export type Timeout = ReturnType; diff --git a/src/helpers.ts b/src/helpers.ts index 23d1075..d36906d 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,10 +1,19 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +/** + * An utility constant that indicates whether the current environment is a browser. + */ // @ts-ignore export const isBrowser = ((typeof window !== "undefined") && (typeof window.document !== "undefined")); +/** + * An utility constant that indicates whether the current environment is a Node.js runtime. + */ // @ts-ignore -export const isNode = ((typeof process !== "undefined") && (process.versions?.node)); +export const isNode = ((typeof process !== "undefined") && !!(process.versions?.node)); +/** + * An utility constant that indicates whether the current environment is a Web Worker. + */ // @ts-ignore export const isWebWorker = ((typeof self === "object") && (self.constructor?.name === "DedicatedWorkerGlobalScope")); diff --git a/src/index.ts b/src/index.ts index 01aca3b..2af1569 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,6 +85,7 @@ export { dateRound, TimeUnit, enumerate, + getWeek, hash, loadScript, nextAnimationFrame, @@ -93,6 +94,7 @@ export { shuffle, sum, unique, + WeekDay, yieldToEventLoop, zip diff --git a/src/models/json/types.ts b/src/models/json/types.ts index a98b5f1..d8d7315 100644 --- a/src/models/json/types.ts +++ b/src/models/json/types.ts @@ -1,5 +1,15 @@ +/** + * A type representing a JSON array. + */ export type JSONArray = JSONValue[]; +/** + * A type representing a JSON object. + */ // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style export interface JSONObject { [key: string]: JSONValue } + +/** + * A type representing all the possible values of a JSON value. + */ export type JSONValue = boolean | number | string | null | JSONObject | JSONArray; diff --git a/src/utils/async.ts b/src/utils/async.ts index 749f35b..0d18a6d 100644 --- a/src/utils/async.ts +++ b/src/utils/async.ts @@ -1,13 +1,62 @@ +/** + * Returns a promise that resolves after a certain number of milliseconds. + * It can be used to pause or delay the execution of an asynchronous function. + * + * ```ts + * doSomething(); + * await delay(1000); + * doSomethingElse(); + * ``` + * + * --- + * + * @param milliseconds The number of milliseconds to wait before resolving the promise. + * + * @returns A promise that resolves after the specified number of milliseconds. + */ export function delay(milliseconds: number): Promise { return new Promise((resolve) => setTimeout(resolve, milliseconds)); } +/** + * Returns a promise that resolves on the next animation frame. + * It can be used to synchronize operations with the browser's rendering cycle. + * + * ```ts + * const $el = document.querySelector(".element"); + * + * $el.classList.add("animate"); + * await nextAnimationFrame(); + * $el.style.opacity = "1"; + * ``` + * + * --- + * + * @returns A promise that resolves on the next animation frame. + */ export function nextAnimationFrame(): Promise { return new Promise((resolve) => requestAnimationFrame(() => resolve())); } +/** + * Returns a promise that resolves on the next microtask. + * It can be used to yield to the event loop in long-running operations to prevent blocking the main thread. + * + * ```ts + * for (let i = 0; i < 100_000_000; i += 1) + * { + * doSomething(i); + * + * if (i % 100 === 0) await yieldToEventLoop(); + * } + * ``` + * + * --- + * + * @returns A promise that resolves on the next microtask. + */ export function yieldToEventLoop(): Promise { return new Promise((resolve) => setTimeout(resolve)); diff --git a/src/utils/curve.ts b/src/utils/curve.ts index 122b7c9..476f7c2 100644 --- a/src/utils/curve.ts +++ b/src/utils/curve.ts @@ -1,18 +1,70 @@ -import { SmartIterator } from "../models/index.js"; +import { SmartIterator, ValueException } from "../models/index.js"; +/** + * A utility class that provides a set of methods to generate sequences of numbers following specific curves. + * It can be used to generate sequences of values that can be + * used in animations, transitions and other different scenarios. + * + * It cannot be instantiated directly. + */ export default class Curve { + /** + * Generates a given number of values following a linear curve. + * The values are equally spaced and normalized between 0 and 1. + * + * ```ts + * for (const value of Curve.Linear(5)) + * { + * console.log(value); // 0, 0.25, 0.5, 0.75, 1 + * } + * ``` + * + * --- + * + * @param values The number of values to generate. + * + * @returns A `SmartIterator` object that generates the values following a linear curve. + */ public static Linear(values: number): SmartIterator { - const step = (1 / values); + const steps = (values - 1); return new SmartIterator(function* () { - for (let index = 0; index < values; index += 1) { yield index * step; } + for (let index = 0; index < values; index += 1) { yield index / steps; } }); } + + /** + * Generates a given number of values following an exponential curve. + * The values are equally spaced and normalized between 0 and 1. + * + * ```ts + * for (const value of Curve.Exponential(6)) + * { + * console.log(value); // 0, 0.04, 0.16, 0.36, 0.64, 1 + * } + * ``` + * + * --- + * + * @param values The number of values to generate. + * @param base + * The base of the exponential curve. Default is `2`. + * + * Also note that: + * - If it's equal to `1`, the curve will be linear. + * - If it's included between `0` and `1`, the curve will be logarithmic. + * + * The base cannot be negative. If so, a `ValueException` will be thrown. + * + * @returns A `SmartIterator` object that generates the values following an exponential curve. + */ public static Exponential(values: number, base = 2): SmartIterator { + if (base < 0) { throw new ValueException("The base of the exponential curve cannot be negative."); } + const steps = (values - 1); return new SmartIterator(function* () diff --git a/src/utils/date.ts b/src/utils/date.ts index becb158..a157a1a 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -1,20 +1,129 @@ import { SmartIterator } from "../models/index.js"; +/** + * An enumeration that represents the time units and their conversion factors. + * It can be used as utility to express time values in a more + * readable way or to convert time values between different units. + * + * ```ts + * setTimeout(() => { [...] }, 5 * TimeUnit.Minute); + * ``` + */ export enum TimeUnit { /* eslint-disable @typescript-eslint/prefer-literal-enum-member */ + /** + * A millisecond: the base time unit. + */ Millisecond = 1, + + /** + * A second: 1000 milliseconds. + */ Second = 1000, + + /** + * A minute: 60 seconds. + */ Minute = 60 * Second, + + /** + * An hour: 60 minutes. + */ Hour = 60 * Minute, + + /** + * A day: 24 hours. + */ Day = 24 * Hour, + + /** + * A week: 7 days. + */ Week = 7 * Day, + + /** + * A month: 30 days. + */ Month = 30 * Day, + + /** + * A year: 365 days. + */ Year = 365 * Day } +/** + * An enumeration that represents the days of the week. + * It can be used as utility to identify the days of the week when working with dates. + * + * ```ts + * const today = new Date(); + * if (today.getUTCDay() === WeekDay.Sunday) + * { + * // Today is Sunday. Do something... + * } + * ``` + */ +export enum WeekDay +{ + /** + * Sunday + */ + Sunday = 0, + + /** + * Monday + */ + Monday = 1, + + /** + * Tuesday + */ + Tuesday = 2, + + /** + * Wednesday + */ + Wednesday = 3, + + /** + * Thursday + */ + Thursday = 4, + + /** + * Friday + */ + Friday = 5, + + /** + * Saturday + */ + Saturday = 6 +} + +/** + * An utility function that calculates the difference between two dates. + * The difference can be expressed in different time units. + * + * ```ts + * const start = new Date("2025-01-01"); + * const end = new Date("2025-01-31"); + * + * dateDifference(start, end, TimeUnit.Minute); // 43200 + * ``` + * + * --- + * + * @param start The start date. + * @param end The end date. + * @param unit The time unit to express the difference. `TimeUnit.Day` by default. + * + * @returns The difference between the two dates in the specified time unit. + */ export function dateDifference(start: string | Date, end: string | Date, unit = TimeUnit.Day): number { start = new Date(start); @@ -23,6 +132,27 @@ export function dateDifference(start: string | Date, end: string | Date, unit = return Math.floor((end.getTime() - start.getTime()) / unit); } +/** + * An utility function that generates a range of dates between two dates. + * The range can be expressed in different time units. + * + * ```ts + * const start = new Date("2025-01-01"); + * const end = new Date("2025-01-31"); + * + * for (const date of dateRange(start, end, TimeUnit.Week)) + * { + * date.toISOString().slice(8, 10); // "01", "08", "15", "22", "29" + * } + * ``` + * + * --- + * + * @param start + * @param end + * @param offset + * @returns + */ export function dateRange(start: string | Date, end: string | Date, offset = TimeUnit.Day): SmartIterator { start = new Date(start); @@ -48,3 +178,14 @@ export function dateRound(date: string | Date, unit = TimeUnit.Day): Date return new Date(Math.floor(date.getTime() / unit) * unit); } + +export function getWeek(date: string | Date, firstDay = WeekDay.Sunday): Date +{ + date = new Date(date); + + const startCorrector = 7 - firstDay; + const weekDayIndex = (date.getUTCDay() + startCorrector) % 7; + const firstDayTime = date.getTime() - (TimeUnit.Day * weekDayIndex); + + return dateRound(new Date(firstDayTime)); +} diff --git a/src/utils/dom.ts b/src/utils/dom.ts index 7604a1e..2bbf6a9 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -1,3 +1,19 @@ +/** + * Appends a script element to the document body. + * It can be used to load external scripts dynamically. + * + * ```ts + * await loadScript("https://analytics.service/script.js?id=0123456789"); + * ``` + * + * --- + * + * @param scriptUrl The URL of the script to load. + * @param scriptType The type of the script to load. Default is `"text/javascript"`. + * + * @returns + * A promise that resolves when the script has been loaded successfully or rejects if an error occurs. + */ export function loadScript(scriptUrl: string, scriptType = "text/javascript"): Promise { return new Promise((resolve, reject) => @@ -9,8 +25,8 @@ export function loadScript(scriptUrl: string, scriptType = "text/javascript"): P script.src = scriptUrl; script.type = scriptType; - script.onload = () => resolve(); - script.onerror = () => reject(); + script.onload = (evt) => resolve(); + script.onerror = (reason) => reject(reason); document.body.appendChild(script); }); diff --git a/src/utils/index.ts b/src/utils/index.ts index e10c144..52162ed 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,7 +2,7 @@ import Curve from "./curve.js"; import Random from "./random.js"; export { delay, nextAnimationFrame, yieldToEventLoop } from "./async.js"; -export { dateDifference, dateRange, dateRound, TimeUnit } from "./date.js"; +export { dateDifference, dateRange, dateRound, getWeek, TimeUnit, WeekDay } from "./date.js"; export { loadScript } from "./dom.js"; export { chain, count, enumerate, range, shuffle, unique, zip } from "./iterator.js"; export { average, hash, sum } from "./math.js"; diff --git a/src/utils/random.ts b/src/utils/random.ts index 73eb9c1..99a1bc0 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -1,13 +1,66 @@ import { ValueException } from "../models/index.js"; +/** + * A utility class that provides a set of methods to generate random values. + * It can be used to generate random numbers, booleans and other different values. + * + * It cannot be instantiated directly. + */ export default class Random { + /** + * Generates a random boolean value. + * + * ```ts + * if (Random.Boolean()) + * { + * // Do something... + * } + * ``` + * + * --- + * + * @param ratio + * The probability of generating `true`. + * + * It must be included between `0` and `1`. Default is `0.5`. + * + * @returns A random boolean value. + */ public static Boolean(ratio = 0.5): boolean { return (Math.random() < ratio); } + /** + * Generates a random integer value between `0` and `max` (excluded). + * + * ```ts + * Random.Integer(5); // 0, 1, 2, 3, 4 + * ``` + * + * --- + * + * @param max The maximum value (excluded). + * + * @returns A random integer value. + */ public static Integer(max: number): number; + + /** + * Generates a random integer value between `min` (included) and `max` (excluded). + * + * ```ts + * Random.Integer(2, 7); // 2, 3, 4, 5, 6 + * ``` + * + * --- + * + * @param min The minimum value (included). + * @param max The maximum value (excluded). + * + * @returns A random integer value. + */ public static Integer(min: number, max: number): number; public static Integer(min: number, max?: number): number { @@ -16,8 +69,48 @@ export default class Random return Math.floor(Math.random() * (max - min) + min); } + /** + * Generates a random decimal value between `0` (included) and `1` (excluded). + * + * ```ts + * Random.Decimal(); // 0.123456789 + * ``` + * + * --- + * + * @returns A random decimal value. + */ public static Decimal(): number; + + /** + * Generates a random decimal value between `0` (included) and `max` (excluded). + * + * ```ts + * Random.Decimal(5); // 2.3456789 + * ``` + * + * --- + * + * @param max The maximum value (excluded). + * + * @returns A random decimal value. + */ public static Decimal(max: number): number; + + /** + * Generates a random decimal value between `min` (included) and `max` (excluded). + * + * ```ts + * Random.Decimal(2, 7); // 4.56789 + * ``` + * + * --- + * + * @param min The minimum value (included). + * @param max The maximum value (excluded). + * + * @returns A random decimal value + */ public static Decimal(min: number, max: number): number; public static Decimal(min?: number, max?: number): number { @@ -27,12 +120,33 @@ export default class Random return (Math.random() * (max - min) + min); } + /** + * Picks a random valid index from a given array of elements. + * + * @param elements + * The array of elements to pick from. + * + * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * + * @returns A valid random index from the given array. + */ public static Index(elements: T[]): number { if (elements.length === 0) { throw new ValueException("You must provide at least one element."); } return this.Integer(elements.length); } + + /** + * Picks a random element from a given array of elements. + * + * @param elements + * The array of elements to pick from. + * + * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * + * @returns A random element from the given array. + */ public static Choice(elements: T[]): T { return elements[Random.Index(elements)]; diff --git a/src/utils/string.ts b/src/utils/string.ts index 4b425cb..424cb47 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -1,3 +1,16 @@ +/** + * Capitalize the first letter of a string. + * + * ```ts + * capitalize('hello'); // 'Hello' + * ``` + * + * --- + * + * @param value The string to capitalize. + * + * @returns The capitalized string. + */ export function capitalize(value: string): string { return `${value.charAt(0).toUpperCase()}${value.slice(1)}`; From f7a6a33a5071af6cd5e5786b340a44940c0a3472 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Wed, 25 Dec 2024 01:53:11 +0100 Subject: [PATCH 04/32] upd: Completed JSDoc for `utils` directory. --- src/utils/curve.ts | 4 +- src/utils/date.ts | 58 +++++++++++---- src/utils/iterator.ts | 161 +++++++++++++++++++++++++++++++++++++++++- src/utils/math.ts | 58 ++++++++++++++- src/utils/random.ts | 6 +- 5 files changed, 265 insertions(+), 22 deletions(-) diff --git a/src/utils/curve.ts b/src/utils/curve.ts index 476f7c2..414da71 100644 --- a/src/utils/curve.ts +++ b/src/utils/curve.ts @@ -24,7 +24,7 @@ export default class Curve * * @param values The number of values to generate. * - * @returns A `SmartIterator` object that generates the values following a linear curve. + * @returns A {@link SmartIterator} object that generates the values following a linear curve. */ public static Linear(values: number): SmartIterator { @@ -59,7 +59,7 @@ export default class Curve * * The base cannot be negative. If so, a `ValueException` will be thrown. * - * @returns A `SmartIterator` object that generates the values following an exponential curve. + * @returns A {@link SmartIterator} object that generates the values following an exponential curve. */ public static Exponential(values: number, base = 2): SmartIterator { diff --git a/src/utils/date.ts b/src/utils/date.ts index a157a1a..14a7067 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -133,8 +133,8 @@ export function dateDifference(start: string | Date, end: string | Date, unit = } /** - * An utility function that generates a range of dates between two dates. - * The range can be expressed in different time units. + * An utility function that generates an iterator over a range of dates. + * The step between the dates can be expressed in different time units. * * ```ts * const start = new Date("2025-01-01"); @@ -148,30 +148,45 @@ export function dateDifference(start: string | Date, end: string | Date, unit = * * --- * - * @param start - * @param end - * @param offset - * @returns + * @param start The start date (included). + * @param end The end date (excluded). + * @param step The time unit to express the step between the dates. `TimeUnit.Day` by default. + * + * @returns A {@link SmartIterator} object that generates the dates in the range. */ -export function dateRange(start: string | Date, end: string | Date, offset = TimeUnit.Day): SmartIterator +export function dateRange(start: string | Date, end: string | Date, step = TimeUnit.Day): SmartIterator { - start = new Date(start); - end = new Date(end); - return new SmartIterator(function* () { - const endTime = end.getTime(); + const endTime = new Date(end).getTime(); - let unixTime: number = start.getTime(); + let unixTime: number = new Date(start).getTime(); while (unixTime < endTime) { yield new Date(unixTime); - unixTime += offset; + unixTime += step; } }); } +/** + * An utility function that rounds a date to the nearest time unit. + * The rounding can be expressed in different time units. + * + * ```ts + * const date = new Date("2025-01-01T12:34:56.789Z"); + * + * dateRound(date, TimeUnit.Hour); // 2025-01-01T12:00:00.000Z + * ``` + * + * --- + * + * @param date The date to round. + * @param unit The time unit to express the rounding. `TimeUnit.Day` by default. + * + * @returns The rounded date. + */ export function dateRound(date: string | Date, unit = TimeUnit.Day): Date { date = new Date(date); @@ -179,6 +194,23 @@ export function dateRound(date: string | Date, unit = TimeUnit.Day): Date return new Date(Math.floor(date.getTime() / unit) * unit); } +/** + * An utility function that gets the week of a date. + * The first day of the week can be optionally specified. + * + * ```ts + * const date = new Date("2025-01-01"); + * + * getWeek(date, WeekDay.Monday); // 2024-12-30 + * ``` + * + * --- + * + * @param date The date to get the week of. + * @param firstDay The first day of the week. `WeekDay.Sunday` by default. + * + * @returns The first day of the week of the specified date. + */ export function getWeek(date: string | Date, firstDay = WeekDay.Sunday): Date { date = new Date(date); diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index 4af24fe..fe279fe 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -1,5 +1,21 @@ import { SmartIterator } from "../models/index.js"; +/** + * An utility function that chains multiple iterables into a single one. + * + * ```ts + * for (const value of chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) + * { + * console.log(value); // 1, 2, 3, 4, 5, 6, 7, 8, 9 + * } + * ``` + * + * --- + * + * @param iterables The list of iterables to chain. + * + * @returns A {@link SmartIterator} object that chains the iterables into a single one. + */ export function chain(...iterables: Iterable[]): SmartIterator { return new SmartIterator(function* () @@ -11,6 +27,23 @@ export function chain(...iterables: Iterable[]): SmartIterator }); } +/** + * An utility function that counts the number of elements in an iterable. + * + * Also note that: + * - If the iterable isn't an `Array`, it will be consumed entirely in the process. + * - If the iterable is an infinite generator, the function will never return. + * + * ```ts + * count([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // 10 + * ``` + * + * --- + * + * @param elements The iterable to count. + * + * @returns The number of elements in the iterable. + */ export function count(elements: Iterable): number { if (Array.isArray(elements)) { return elements.length; } @@ -21,6 +54,22 @@ export function count(elements: Iterable): number return _count; } +/** + * An utility function that enumerates the elements of an iterable. + * + * ```ts + * for (const [index, value] of enumerate(["A", "M", "N", "Z"])) + * { + * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" + * } + * ``` + * + * --- + * + * @param elements The iterable to enumerate. + * + * @returns A {@link SmartIterator} object that enumerates the elements of the given iterable. + */ export function enumerate(elements: Iterable): SmartIterator<[number, T]> { return new SmartIterator<[number, T]>(function* () @@ -36,9 +85,60 @@ export function enumerate(elements: Iterable): SmartIterator<[number, T]> }); } +/** + * An utility function that generates an iterator over a range of numbers. + * The values are included between `0` (included) and `end` (excluded). + * + * The default step between the numbers is `1`. + * + * ```ts + * for (const number of range(5)) + * { + * console.log(number); // 0, 1, 2, 3, 4 + * } + * ``` + * + * --- + * + * @param end + * The end value (excluded). + * + * If the `end` value is negative, the step will be `-1` leading to generate the numbers in reverse order. + * + * @returns A {@link SmartIterator} object that generates the numbers in the range. + */ export function range(end: number): SmartIterator; -export function range(start: number, end: number): SmartIterator; -export function range(start: number, end: number, step: number): SmartIterator; + +/** + * An utility function that generates an iterator over a range of numbers. + * The values are included between `start` (included) and `end` (excluded). + * + * The step between the numbers can be specified with a custom value. Default is `1`. + * + * ```ts + * for (const number of range(2, 7)) + * { + * console.log(number); // 2, 3, 4, 5, 6 + * } + * ``` + * + * --- + * + * @param start + * The start value (included). + * + * If the `start` value is greater than the `end` value, the iterator will generate the numbers in reverse order. + * + * @param end + * The end value (excluded). + * + * If the `end` value is less than the `start` value, the iterator will generate the numbers in reverse order. + * + * @param step The step between the numbers. Default is `1`. + * + * @returns A {@link SmartIterator} object that generates the numbers in the range. + */ +export function range(start: number, end: number, step?: number): SmartIterator; export function range(start: number, end?: number, step = 1): SmartIterator { return new SmartIterator(function* () @@ -55,6 +155,27 @@ export function range(start: number, end?: number, step = 1): SmartIterator(iterable: Iterable): T[] { const array = Array.from(iterable); @@ -69,6 +190,22 @@ export function shuffle(iterable: Iterable): T[] return array; } +/** + * An utility function that filters the elements of an iterable ensuring that they are all unique. + * + * ```ts + * for (const value of unique([1, 1, 2, 3, 2, 3, 4, 5, 5, 4])) + * { + * console.log(value); // 1, 2, 3, 4, 5 + * } + * ``` + * + * --- + * + * @param elements The iterable to filter. + * + * @returns A {@link SmartIterator} object that iterates over the unique elements of the given iterable. + */ export function unique(elements: Iterable): SmartIterator { return new SmartIterator(function* () @@ -86,6 +223,26 @@ export function unique(elements: Iterable): SmartIterator }); } +/** + * An utility function that zips two iterables into a single one. + * The resulting iterable will contain the elements of the two iterables paired together. + * + * The function will stop when one of the two iterables is exhausted. + * + * ```ts + * for (const [number, char] of zip([1, 2, 3, 4], ["A", "M", "N" "Z"])) + * { + * console.log(`${number} - ${char}`); // "1 - A", "2 - M", "3 - N", "4 - Z" + * } + * ``` + * + * --- + * + * @param first The first iterable to zip. + * @param second The second iterable to zip. + * + * @returns A {@link SmartIterator} object that iterates over the zipped elements of the two given iterables. + */ export function zip(first: Iterable, second: Iterable): SmartIterator<[T, U]> { return new SmartIterator<[T, U]>(function* () diff --git a/src/utils/math.ts b/src/utils/math.ts index 0e7de44..d1177ab 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -1,8 +1,31 @@ import { ValueException } from "../models/exceptions/index.js"; import { zip } from "./iterator.js"; -export function average(values: Iterable): number; -export function average(values: Iterable, weights: Iterable): number; +/** + * Computes the average of a given list of values. + * The values can be weighted using an additional list of weights. + * + * ```ts + * average([1, 2, 3, 4, 5]); // 3 + * average([6, 8.5, 4], [3, 2, 1]); // 6.5 + * ``` + * + * --- + * + * @param values + * The list of values to compute the average. + * + * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * + * @param weights + * The list of weights to apply to the values. + * It should contain the same number of elements as the values list or + * the smaller number of elements between the two lists will be considered. + * + * The sum of the weights must be greater than zero. Otherwise, a `ValueException` will be thrown. + * + * @returns The average of the specified values. + */ export function average(values: Iterable, weights?: Iterable): number { if (weights === undefined) @@ -43,6 +66,24 @@ export function average(values: Iterable, weights?: Iterabl return _sum / _count; } +/** + * An utility function to compute the hash of a given string. + * + * The hash is computed using a simple variation of the + * {@link http://www.cse.yorku.ca/~oz/hash.html#djb2|djb2} algorithm. + * However, the hash is garanteed to be a 32-bit signed integer. + * + * ```ts + * hash("Hello, world!"); // -1880044555 + * hash("How are you?"); // 1761539132 + * ``` + * + * --- + * + * @param value The string to hash. + * + * @returns The hash of the specified string. + */ export function hash(value: string): number { let hashedValue = 0; @@ -57,6 +98,19 @@ export function hash(value: string): number return hashedValue; } +/** + * Sums all the values of a given list. + * + * ```ts + * sum([1, 2, 3, 4, 5]); // 15 + * ``` + * + * --- + * + * @param values The list of values to sum. + * + * @returns The sum of the specified values. + */ export function sum(values: Iterable): number { let _sum = 0; diff --git a/src/utils/random.ts b/src/utils/random.ts index 99a1bc0..540d380 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -33,7 +33,7 @@ export default class Random } /** - * Generates a random integer value between `0` and `max` (excluded). + * Generates a random integer value between `0` (included) and `max` (excluded). * * ```ts * Random.Integer(5); // 0, 1, 2, 3, 4 @@ -130,7 +130,7 @@ export default class Random * * @returns A valid random index from the given array. */ - public static Index(elements: T[]): number + public static Index(elements: readonly T[]): number { if (elements.length === 0) { throw new ValueException("You must provide at least one element."); } @@ -147,7 +147,7 @@ export default class Random * * @returns A random element from the given array. */ - public static Choice(elements: T[]): T + public static Choice(elements: readonly T[]): T { return elements[Random.Index(elements)]; } From 9d1727cccf123e81943eae7bbc165ebff67d7646 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 28 Dec 2024 10:03:41 +0100 Subject: [PATCH 05/32] add: Added JSDoc for part of `models/callbacks` files. --- src/models/callbacks/callable-object.ts | 30 ++++++- src/models/callbacks/index.ts | 4 +- src/models/callbacks/switchable-callback.ts | 99 +++++++++++++++++++++ src/models/index.ts | 2 +- src/models/json/json-storage.ts | 62 +++++++------ src/utils/curve.ts | 2 +- src/utils/math.ts | 4 +- src/utils/random.ts | 7 +- 8 files changed, 173 insertions(+), 37 deletions(-) diff --git a/src/models/callbacks/callable-object.ts b/src/models/callbacks/callable-object.ts index 7cd7646..f882ae9 100644 --- a/src/models/callbacks/callable-object.ts +++ b/src/models/callbacks/callable-object.ts @@ -2,9 +2,29 @@ import type { Callback } from "./types.js"; -export const SmartFunction = (Function as unknown) as new(...args: string[]) +const SmartFunction = (Function as unknown) as new(...args: string[]) => (...args: A) => R; +/** + * An abstract class that can be used to implement callable objects. + * + * ```ts + * class EnableableCallback extends CallableObject<(evt: PointerEvent) => void> + * { + * public enabled = false; + * protected _invoke(): void + * { + * if (this.enabled) { [...] } + * } + * } + * + * const callback = new EnableableCallback(); + * + * window.addEventListener("pointerdown", () => { callback.enabled = true; }); + * window.addEventListener("pointermove", callback); + * window.addEventListener("pointerup", () => { callback.enabled = false; }); + * ``` + */ export default abstract class CallableObject = () => void> extends SmartFunction, ReturnType> { @@ -18,6 +38,14 @@ export default abstract class CallableObject = () return self as this; } + /** + * The method that will be called when the object is invoked. + * It must be implemented by the derived classes. + * + * @param args The arguments that have been passed to the object. + * + * @returns The return value of the method. + */ protected abstract _invoke(...args: Parameters): ReturnType; public readonly [Symbol.toStringTag]: string = "CallableObject"; diff --git a/src/models/callbacks/index.ts b/src/models/callbacks/index.ts index 437e8fd..af6180d 100644 --- a/src/models/callbacks/index.ts +++ b/src/models/callbacks/index.ts @@ -1,5 +1,5 @@ -import CallableObject, { SmartFunction } from "./callable-object.js"; +import CallableObject from "./callable-object.js"; import Publisher from "./publisher.js"; import SwitchableCallback from "./switchable-callback.js"; -export { CallableObject, Publisher, SmartFunction, SwitchableCallback }; +export { CallableObject, Publisher, SwitchableCallback }; diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index 4712ae0..86da233 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -3,6 +3,23 @@ import { KeyException, NotImplementedException, RuntimeException } from "../exce import CallableObject from "./callable-object.js"; import type { Callback } from "./types.js"; +/** + * A class representing a callback that can be switched between multiple implementations. + * + * It can be used to implement different behaviors for the same event handler, allowing + * it to respond to different states without incurring any overhead during execution. + * + * ```ts + * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>(); + * + * onPointerMove.register("released", () => { [...] }); + * onPointerMove.register("pressed", () => { [...] }); + * + * window.addEventListener("pointerdown", () => { onPointerMove.switch("pressed"); }); + * window.addEventListener("pointermove", onPointerMove); + * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); }); + * ``` + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class SwitchableCallback = Callback> extends CallableObject { @@ -17,6 +34,13 @@ export default class SwitchableCallback = Callbac protected readonly _invoke: (...args: Parameters) => ReturnType; + /** + * Initializes a new instance of the {@link SwitchableCallback} class. + * + * ```ts + * const onPointerMove = new SwitchableCallback<(evt: PointerEvent) => void>(); + * ``` + */ public constructor() { const _default = () => @@ -38,6 +62,18 @@ export default class SwitchableCallback = Callbac this._invoke = (...args: Parameters): ReturnType => this._callback(...args); } + /** + * Enables the callback, allowing it to execute the currently selected implementation. + * + * Also note that: + * - If any implementation has been registered yet, a {@link KeyException} will be thrown. + * - If the callback is already enabled, a {@link RuntimeException} will be thrown. + * + * ```ts + * window.addEventListener("pointerdown", () => { onPointerMove.enable(); }); + * window.addEventListener("pointermove", onPointerMove); + * ``` + */ public enable(): void { if (!(this._key)) @@ -55,6 +91,17 @@ export default class SwitchableCallback = Callbac this._callback = this._callbacks.get(this._key)!; this._isEnabled = true; } + + /** + * Disables the callback, allowing it to be invoked without executing any implementation. + * + * If the callback is already disabled, a {@link RuntimeException} will be thrown. + * + * ```ts + * window.addEventListener("pointermove", onPointerMove); + * window.addEventListener("pointerup", () => { onPointerMove.disable(); }); + * ``` + */ public disable(): void { if (!(this._isEnabled)) @@ -67,6 +114,23 @@ export default class SwitchableCallback = Callbac this._isEnabled = false; } + /** + * Registers a new implementation for the callback. + * + * Also note that: + * - If the callback has no other implementation registered yet, this one will be selected as default. + * - If the key has already been used for another implementation, a {@link KeyException} will be thrown. + * + * ```ts + * onPointerMove.register("pressed", () => { [...] }); + * onPointerMove.register("released", () => { [...] }); + * ``` + * + * --- + * + * @param key The key that will be associated with the implementation. + * @param callback The implementation to register. + */ public register(key: string, callback: T): void { if (this._callbacks.size === 0) @@ -81,8 +145,28 @@ export default class SwitchableCallback = Callbac this._callbacks.set(key, callback); } + + /** + * Unregisters an implementation for the callback. + * + * Also note that: + * - If the key is the currently selected implementation, a {@link KeyException} will be thrown. + * - If the key has no associated implementation yet, a {@link KeyException} will be thrown. + * + * ```ts + * onPointerMove.unregister("released"); + * ``` + * + * --- + * + * @param key The key that is associated with the implementation to unregister. + */ public unregister(key: string): void { + if (this._key === key) + { + throw new KeyException("Unable to unregister the currently selected callback."); + } if (!(this._callbacks.has(key))) { throw new KeyException(`The key '${key}' doesn't yet have any associated callback.`); @@ -91,6 +175,21 @@ export default class SwitchableCallback = Callbac this._callbacks.delete(key); } + /** + * Switches the callback to the implementation associated with the given key. + * + * If the key has no associated implementation yet, a {@link KeyException} will be thrown. + * + * ```ts + * window.addEventListener("pointerdown", () => { onPointerMove.switch("pressed"); }); + * window.addEventListener("pointermove", onPointerMove); + * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); }); + * ``` + * + * --- + * + * @param key The key that is associated with the implementation to switch to. + */ public switch(key: string): void { if (!(this._callbacks.has(key))) diff --git a/src/models/index.ts b/src/models/index.ts index a05a4c2..f21c791 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -5,7 +5,7 @@ export { } from "./aggregators/index.js"; -export { CallableObject, Publisher, SmartFunction, SwitchableCallback } from "./callbacks/index.js"; +export { CallableObject, Publisher, SwitchableCallback } from "./callbacks/index.js"; export { Exception, FatalErrorException, diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index 05aeecb..131f6f8 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -7,8 +7,16 @@ import type { JSONValue } from "./types.js"; * A wrapper around the `Storage` API to better store and easily retrieve * typed JSON values using the classical key-value pair storage system. * - * It allows to handle either the volatile `sessionStorage` or the persistent - * `localStorage` at the same time, depending on what's your required use case. + * It allows to handle either the volatile {@link sessionStorage} or the persistent + * {@link localStorage} at the same time, depending on what's your required use case. + * + * ```ts + * const jsonStorage = new JSONStorage(); + * + * jsonStorage.write("user:cookieAck", { value: true, version: "2023-02-15" }); + * // ... between sessions ... + * const cookieAck = jsonStorage.read<{ value: boolean; version: string; }>("user:cookieAck"); + * ``` */ export default class JSONStorage { @@ -18,8 +26,8 @@ export default class JSONStorage protected _persistent: Storage; /** - * Initializes a new instance of the `JSONStorage` class. - * It cannot be instantiated outside of a browser environment or an `EnvironmentException` is thrown. + * Initializes a new instance of the {@link JSONStorage} class. + * It cannot be instantiated outside of a browser environment or an {@link EnvironmentException} is thrown. * * ```ts * const jsonStorage = new JSONStorage(); @@ -28,7 +36,7 @@ export default class JSONStorage * --- * * @param preferPersistence - * Whether to prefer the `localStorage` over the `sessionStorage` when calling an ambivalent method. + * Whether to prefer the {@link localStorage} over the {@link sessionStorage} when calling an ambivalent method. * If omitted, it defaults to `true` to prefer the persistent storage. */ public constructor(preferPersistence = true) @@ -111,7 +119,7 @@ export default class JSONStorage * @param key The key of the value to retrieve. * @param defaultValue The default value to return if the key doesn't exist. * @param persistent - * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * Whether to prefer the persistent {@link localStorage} over the volatile {@link sessionStorage}. * If omitted, it defaults to the `preferPersistence` value set in the constructor. * * @returns The value with the specified key or the provided default value if the key doesn't exist. @@ -130,7 +138,7 @@ export default class JSONStorage * @param key The key of the value to retrieve. * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. * @param persistent - * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * Whether to prefer the persistent {@link localStorage} over the volatile {@link sessionStorage}. * If omitted, it defaults to the `preferPersistence` value set in the constructor. * * @returns The value with the specified key or the default value if the key doesn't exist. @@ -145,7 +153,7 @@ export default class JSONStorage } /** - * Retrieves the value with the specified key from the volatile `sessionStorage`. + * Retrieves the value with the specified key from the volatile {@link sessionStorage}. * * ```ts * const value: TValue = jsonStorage.recall("key"); @@ -160,7 +168,7 @@ export default class JSONStorage public recall(key: string): T | undefined; /** - * Retrieves the value with the specified key from the volatile `sessionStorage`. + * Retrieves the value with the specified key from the volatile {@link sessionStorage}. * * ```ts * const value: TValue = jsonStorage.recall("key", defaultValue); @@ -176,7 +184,7 @@ export default class JSONStorage public recall(key: string, defaultValue: T): T; /** - * Retrieves the value with the specified key from the volatile `sessionStorage`. + * Retrieves the value with the specified key from the volatile {@link sessionStorage}. * * ```ts * const value: TValue = jsonStorage.recall("key", obj?.value); @@ -197,7 +205,7 @@ export default class JSONStorage /** * Retrieves the value with the specified key looking first in the volatile - * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * {@link sessionStorage} and then, if not found, in the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.retrieve("key"); @@ -213,7 +221,7 @@ export default class JSONStorage /** * Retrieves the value with the specified key looking first in the volatile - * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * {@link sessionStorage} and then, if not found, in the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.retrieve("key", defaultValue); @@ -230,7 +238,7 @@ export default class JSONStorage /** * Retrieves the value with the specified key looking first in the volatile - * `sessionStorage` and then, if not found, in the persistent `localStorage`. + * {@link sessionStorage} and then, if not found, in the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.retrieve("key", obj?.value); @@ -250,7 +258,7 @@ export default class JSONStorage } /** - * Retrieves the value with the specified key from the persistent `localStorage`. + * Retrieves the value with the specified key from the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.read("key"); @@ -265,7 +273,7 @@ export default class JSONStorage public read(key: string): T | undefined; /** - * Retrieves the value with the specified key from the persistent `localStorage`. + * Retrieves the value with the specified key from the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.read("key", defaultValue); @@ -281,7 +289,7 @@ export default class JSONStorage public read(key: string, defaultValue: T): T; /** - * Retrieves the value with the specified key from the persistent `localStorage`. + * Retrieves the value with the specified key from the persistent {@link localStorage}. * * ```ts * const value: TValue = jsonStorage.read("key", obj?.value); @@ -314,7 +322,7 @@ export default class JSONStorage * * @param key The key of the value to check. * @param persistent - * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * Whether to prefer the persistent {@link localStorage} over the volatile {@link sessionStorage}. * If omitted, it defaults to the `preferPersistence` value set in the constructor. * * @returns `true` if the key exists, `false` otherwise. @@ -327,7 +335,7 @@ export default class JSONStorage } /** - * Checks whether the value with the specified key exists within the volatile `sessionStorage`. + * Checks whether the value with the specified key exists within the volatile {@link sessionStorage}. * * ```ts * if (jsonStorage.knows("key")) @@ -349,7 +357,7 @@ export default class JSONStorage /** * Checks whether the value with the specified key exists looking first in the - * volatile `sessionStorage` and then, if not found, in the persistent `localStorage`. + * volatile {@link sessionStorage} and then, if not found, in the persistent {@link localStorage}. * * ```ts * if (jsonStorage.find("key")) @@ -370,7 +378,7 @@ export default class JSONStorage } /** - * Checks whether the value with the specified key exists within the persistent `localStorage`. + * Checks whether the value with the specified key exists within the persistent {@link localStorage}. * * ```ts * if (jsonStorage.exists("key")) @@ -405,7 +413,7 @@ export default class JSONStorage * @param key The key of the value to set. * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. * @param persistent - * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * Whether to prefer the persistent {@link localStorage} over the volatile {@link sessionStorage}. * If omitted, it defaults to the `preferPersistence` value set in the constructor. */ public set(key: string, newValue?: T, persistent = this._preferPersistence): void @@ -416,7 +424,7 @@ export default class JSONStorage } /** - * Sets the value with the specified key in the volatile `sessionStorage`. + * Sets the value with the specified key in the volatile {@link sessionStorage}. * If the value is `undefined` or omitted, the key is removed from the storage. * * ```ts @@ -436,7 +444,7 @@ export default class JSONStorage } /** - * Sets the value with the specified key in the persistent `localStorage`. + * Sets the value with the specified key in the persistent {@link localStorage}. * If the value is `undefined` or omitted, the key is removed from the storage. * * ```ts @@ -466,7 +474,7 @@ export default class JSONStorage * * @param key The key of the value to remove. * @param persistent - * Whether to prefer the persistent `localStorage` over the volatile `sessionStorage`. + * Whether to prefer the persistent {@link localStorage} over the volatile {@link sessionStorage}. * If omitted, it defaults to the `preferPersistence` value set in the constructor. */ public delete(key: string, persistent?: boolean): void @@ -477,7 +485,7 @@ export default class JSONStorage } /** - * Removes the value with the specified key from the volatile `sessionStorage`. + * Removes the value with the specified key from the volatile {@link sessionStorage}. * * ```ts * jsonStorage.forget("key"); @@ -493,7 +501,7 @@ export default class JSONStorage } /** - * Removes the value with the specified key from the persistent `localStorage`. + * Removes the value with the specified key from the persistent {@link localStorage}. * * ```ts * jsonStorage.erase("key"); @@ -510,7 +518,7 @@ export default class JSONStorage /** * Removes the value with the specified key from both the - * volatile `sessionStorage` and the persistent `localStorage`. + * volatile {@link sessionStorage} and the persistent {@link localStorage}. * * ```ts * jsonStorage.clear("key"); diff --git a/src/utils/curve.ts b/src/utils/curve.ts index 414da71..ee3c3b1 100644 --- a/src/utils/curve.ts +++ b/src/utils/curve.ts @@ -57,7 +57,7 @@ export default class Curve * - If it's equal to `1`, the curve will be linear. * - If it's included between `0` and `1`, the curve will be logarithmic. * - * The base cannot be negative. If so, a `ValueException` will be thrown. + * The base cannot be negative. If so, a {@link ValueException} will be thrown. * * @returns A {@link SmartIterator} object that generates the values following an exponential curve. */ diff --git a/src/utils/math.ts b/src/utils/math.ts index d1177ab..0ddf62a 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -15,14 +15,14 @@ import { zip } from "./iterator.js"; * @param values * The list of values to compute the average. * - * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. * * @param weights * The list of weights to apply to the values. * It should contain the same number of elements as the values list or * the smaller number of elements between the two lists will be considered. * - * The sum of the weights must be greater than zero. Otherwise, a `ValueException` will be thrown. + * The sum of the weights must be greater than zero. Otherwise, a {@link ValueException} will be thrown. * * @returns The average of the specified values. */ diff --git a/src/utils/random.ts b/src/utils/random.ts index 540d380..8b60ce7 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -1,7 +1,8 @@ import { ValueException } from "../models/index.js"; /** - * A utility class that provides a set of methods to generate random values. + * A wrapper class around the native {@link Math.random} function that + * provides a set of methods to generate random values more easily. * It can be used to generate random numbers, booleans and other different values. * * It cannot be instantiated directly. @@ -126,7 +127,7 @@ export default class Random * @param elements * The array of elements to pick from. * - * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. * * @returns A valid random index from the given array. */ @@ -143,7 +144,7 @@ export default class Random * @param elements * The array of elements to pick from. * - * It must contain at least one element. Otherwise, a `ValueException` will be thrown. + * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. * * @returns A random element from the given array. */ From def0de9fb942c7c450841c654f69658dcfd11a39 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 28 Dec 2024 11:41:36 +0100 Subject: [PATCH 06/32] add: Completed JSDoc for `models/callbacks` files. --- src/models/callbacks/callable-object.ts | 7 +- src/models/callbacks/publisher.ts | 107 ++++++++++++++++++++ src/models/callbacks/switchable-callback.ts | 36 +++++++ src/models/callbacks/types.ts | 10 ++ src/models/json/json-storage.ts | 13 +++ 5 files changed, 171 insertions(+), 2 deletions(-) diff --git a/src/models/callbacks/callable-object.ts b/src/models/callbacks/callable-object.ts index f882ae9..bb9f2e7 100644 --- a/src/models/callbacks/callable-object.ts +++ b/src/models/callbacks/callable-object.ts @@ -9,7 +9,7 @@ const SmartFunction = (Function as unknown) as new void> + * class ActivableCallback extends CallableObject<(evt: PointerEvent) => void> * { * public enabled = false; * protected _invoke(): void @@ -18,7 +18,7 @@ const SmartFunction = (Function as unknown) as new { callback.enabled = true; }); * window.addEventListener("pointermove", callback); @@ -28,6 +28,9 @@ const SmartFunction = (Function as unknown) as new = () => void> extends SmartFunction, ReturnType> { + /** + * Initializes a new instance of the {@link CallableObject} class. + */ public constructor() { super(`return this._invoke(...arguments);`); diff --git a/src/models/callbacks/publisher.ts b/src/models/callbacks/publisher.ts index 65cce03..a6f9cdd 100644 --- a/src/models/callbacks/publisher.ts +++ b/src/models/callbacks/publisher.ts @@ -2,21 +2,93 @@ import { ReferenceException } from "../exceptions/index.js"; import type { Callback } from "./types.js"; +/** + * A class implementing the + * {@link https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern|Publish-subscribe} pattern. + * + * It can be used to create a simple event system where objects can subscribe + * to events and receive notifications when the events are published. + * It's a simple and efficient way to decouple the objects and make them communicate with each other. + * + * Using generics, it's also possible to define the type of the events and the callbacks that can be subscribed to them. + * + * ```ts + * interface EventsMap + * { + * "player:spawn": (evt: SpawnEvent) => void; + * "player:move": ({ x, y }: Point) => void; + * "player:death": () => void; + * } + * + * const publisher = new Publisher(); + * + * let unsubscribe: () => void; + * publisher.subscribe("player:death", unsubscribe); + * publisher.subscribe("player:spawn", (evt) => + * { + * unsubscribe = publisher.subscribe("player:move", ({ x, y }) => { [...] }); + * }); + * ``` + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class Publisher } = Record> { + /** + * A map containing all the subscribers for each event. + * + * The keys are the names of the events they are subscribed to. + * The values are the arrays of the subscribers themselves. + */ protected _subscribers: Map[]>; + /** + * Initializes a new instance of the {@link Publisher} class. + */ public constructor() { this._subscribers = new Map(); } + /** + * Unsubscribes all the subscribers from all the events. + * + * ```ts + * publisher.subscribe("player:spawn", (evt) => { [...] }); + * publisher.subscribe("player:move", (coords) => { [...] }); + * publisher.subscribe("player:move", () => { [...] }); + * publisher.subscribe("player:move", ({ x, y }) => { [...] }); + * publisher.subscribe("player:death", () => { [...] }); + * + * // All these subscribers are working fine... + * + * publisher.clear(); + * + * // ... but now they're all gone! + * ``` + */ public clear(): void { this._subscribers.clear(); } + /** + * Publishes an event to all the subscribers. + * + * ```ts + * publisher.subscribe("player:move", (coords) => { [...] }); + * publisher.subscribe("player:move", ({ x, y }) => { [...] }); + * publisher.subscribe("player:move", (evt) => { [...] }); + * + * publisher.publish("player:move", { x: 10, y: 20 }); + * ``` + * + * --- + * + * @param event The name of the event to publish. + * @param args The arguments to pass to the subscribers. + * + * @returns An array containing the return values of all the subscribers. + */ public publish(event: K, ...args: Parameters): ReturnType[] { const subscribers = this._subscribers.get(event); @@ -26,6 +98,25 @@ export default class Publisher .map((subscriber) => subscriber(...args)) as ReturnType[]; } + /** + * Subscribes a new subscriber to an event. + * + * ```ts + * let unsubscribe: () => void; + * publisher.subscribe("player:death", unsubscribe); + * publisher.subscribe("player:spawn", (evt) => + * { + * unsubscribe = publisher.subscribe("player:move", ({ x, y }) => { [...] }); + * }); + * ``` + * + * --- + * + * @param event The name of the event to subscribe to. + * @param subscriber The subscriber to add to the event. + * + * @returns A function that can be used to unsubscribe the subscriber. + */ public subscribe(event: K, subscriber: T[K]): () => void { if (!(this._subscribers.has(event))) { this._subscribers.set(event, []); } @@ -45,6 +136,22 @@ export default class Publisher subscribers.splice(index, 1); }; } + + /** + * Unsubscribes a subscriber from an event. + * + * ```ts + * const onPlayerMove = ({ x, y }: Point) => { [...] }; + * + * publisher.subscribe("player:spawn", (evt) => publisher.subscribe("player:move", onPlayerMove)); + * publisher.subscribe("player:death", () => publisher.unsubscribe("player:move", onPlayerMove)); + * ``` + * + * --- + * + * @param event The name of the event to unsubscribe from. + * @param subscriber The subscriber to remove from the event. + */ public unsubscribe(event: K, subscriber: T[K]): void { const subscribers = this._subscribers.get(event); diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index 86da233..b5c0501 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -23,15 +23,51 @@ import type { Callback } from "./types.js"; // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class SwitchableCallback = Callback> extends CallableObject { + /** + * The currently selected implementation of the callback. + */ protected _callback: T; + + /** + * All the implementations that have been registered for the callback. + * + * The keys are the names of the implementations they were registered with. + * The values are the implementations themselves. + */ protected _callbacks: Map; + /** + * A flag indicating whether the callback is enabled or not. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link isEnabled} getter instead. + */ protected _isEnabled: boolean; + + /** + * A flag indicating whether the callback is enabled or not. + * + * It indicates whether the callback is currently able to execute the currently selected implementation. + * If it's disabled, the callback will be invoked without executing anything. + */ public get isEnabled(): boolean { return this._isEnabled; } + /** + * The key that is associated with the currently selected implementation. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link key} getter instead. + */ protected _key: string; + + /** + * The key that is associated with the currently selected implementation. + */ public get key(): string { return this._key; } + /** + * The function that will be called by the extended class when the object is invoked as a function. + */ protected readonly _invoke: (...args: Parameters) => ReturnType; /** diff --git a/src/models/callbacks/types.ts b/src/models/callbacks/types.ts index a010aa9..b6631ec 100644 --- a/src/models/callbacks/types.ts +++ b/src/models/callbacks/types.ts @@ -1 +1,11 @@ +/** + * A type representing a generic function. + * + * It can be used to define the signature of a callback, a event handler or any other function. + * It's simply a shorthand for the `(...args: A) => R` function signature. + * + * ```ts + * const callback: Callback<[PointerEvent]> = (evt: PointerEvent): void => { [...] }; + * ``` + */ export type Callback = (...args: A) => R; diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index 131f6f8..bdcc968 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -20,9 +20,22 @@ import type { JSONValue } from "./types.js"; */ export default class JSONStorage { + /** + * Whether to prefer the {@link localStorage} over the {@link sessionStorage} when calling an ambivalent method. + * + * If `true`, the persistent storage is preferred. If `false`, the volatile storage is preferred. + * Default is `true`. + */ protected _preferPersistence: boolean; + /** + * A reference to the volatile {@link sessionStorage} storage. + */ protected _volatile: Storage; + + /** + * A reference to the persistent {@link localStorage} storage. + */ protected _persistent: Storage; /** From 4e60b8b5c6823055e138fd5dddfa54378b92b83f Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 28 Dec 2024 15:28:04 +0100 Subject: [PATCH 07/32] add: Completed JSDoc for `models/exceptions` files. --- src/models/exceptions/core.ts | 101 +++++++++++- src/models/exceptions/index.ts | 274 +++++++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+), 1 deletion(-) diff --git a/src/models/exceptions/core.ts b/src/models/exceptions/core.ts index 6b3c264..5e18b58 100644 --- a/src/models/exceptions/core.ts +++ b/src/models/exceptions/core.ts @@ -1,5 +1,47 @@ +/** + * A class representing an exception, subclass of the native `Error` class. + * It's the base class for any other further exception. + * + * It allows to chain exceptions together, tracking the initial cause of an error and + * storing its stack trace while providing a clear and friendly message to the user. + * + * ```ts + * try { loadGameSaves(); } + * catch (error) + * { + * throw new Exception("The game saves may be corrupted. Try to restart the game.", error); + * // Uncaught Exception: The game saves may be corrupted. Try to restart the game. + * // at /src/game/index.ts:37:15 + * // at /src/main.ts:23:17 + * // + * // Caused by SyntaxError: Unexpected end of JSON input + * // at /src/models/saves.ts:47:17 + * // at /src/game/index.ts:12:9 + * // at /src/main.ts:23:17 + * } + * ``` + */ export default class Exception extends Error { + /** + * A static method to convert a generic caught error, ensuring it's an instance of the {@link Exception} class. + * + * ```ts + * try { [...] } + * catch (error) + * { + * const exc = Exception.FromUnknown(error); + * + * [...] + * } + * ``` + * + * --- + * + * @param error The caught error to convert. + * + * @returns An instance of the {@link Exception} class. + */ public static FromUnknown(error: unknown): Exception { if (error instanceof Exception) @@ -19,6 +61,13 @@ export default class Exception extends Error return new Exception(`${error}`); } + /** + * Initializes a new instance of the {@link Exception} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"Exception"`. + */ public constructor(message: string, cause?: unknown, name = "Exception") { super(message); @@ -42,8 +91,34 @@ export default class Exception extends Error public readonly [Symbol.toStringTag]: string = "Exception"; } +/** + * An utility class representing that kind of situation where the program should never reach. + * Also commonly used to satisfy the type-system, but not part of a real feasible scenario. + * + * It provides a clear and friendly message by default. + * + * ```ts + * function checkCase(value: "A" | "B" | "C"): 1 | 2 | 3 + * { + * switch (value) + * { + * case "A": return 1; + * case "B": return 2; + * case "C": return 3; + * default: throw new FatalErrorException(); + * } + * } + * ``` + */ export class FatalErrorException extends Exception { + /** + * Initializes a new instance of the {@link FatalErrorException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"FatalErrorException"`. + */ public constructor(message?: string, cause?: unknown, name = "FatalErrorException") { if (message === undefined) @@ -57,13 +132,37 @@ export class FatalErrorException extends Exception public override readonly [Symbol.toStringTag]: string = "FatalErrorException"; } + +/** + * An utility class representing a situation where a feature isn't implemented yet. + * It's commonly used as a placeholder for future implementations. + * + * It provides a clear and friendly message by default. + * + * ```ts + * class Database + * { + * public async connect(): Promise + * { + * throw new NotImplementedException(); + * } + * } + * ``` + */ export class NotImplementedException extends FatalErrorException { + /** + * Initializes a new instance of the {@link NotImplementedException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"NotImplementedException"`. + */ public constructor(message?: string, cause?: unknown, name = "NotImplementedException") { if (message === undefined) { - message = "This feature is not implemented yet. Please, try again later."; + message = "This feature isn't implemented yet. Please, try again later."; } super(message, cause, name); diff --git a/src/models/exceptions/index.ts b/src/models/exceptions/index.ts index 0971dc8..4e2dc54 100644 --- a/src/models/exceptions/index.ts +++ b/src/models/exceptions/index.ts @@ -1,7 +1,31 @@ import Exception from "./core.js"; +/** + * A class representing a generic exception that can be thrown when a file + * operation fails, such as reading, writing, copying, moving, deleting, etc... + * + * It can also be used to catch all file-related exceptions at once. + * + * ```ts + * try { [...] } + * catch (error) + * { + * if (error instanceof FileException) + * { + * // A file-related exception occurred. Handle it... + * } + * } + * ``` + */ export class FileException extends Exception { + /** + * Initializes a new instance of the {@link FileException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"FileException"`. + */ public constructor(message: string, cause?: unknown, name = "FileException") { super(message, cause, name); @@ -9,8 +33,28 @@ export class FileException extends Exception public override readonly [Symbol.toStringTag]: string = "FileException"; } + +/** + * A class representing an exception that can be thrown when a file already exists. + * + * ```ts + * import { existsSync } from "node:fs"; + * + * if (existsSync("file.txt")) + * { + * throw new FileExistsException("The file named 'file.txt' already exists."); + * } + * ``` + */ export class FileExistsException extends FileException { + /** + * Initializes a new instance of the {@link FileExistsException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"FileExistsException"`. + */ public constructor(message: string, cause?: unknown, name = "FileExistsException") { super(message, cause, name); @@ -18,8 +62,28 @@ export class FileExistsException extends FileException public override readonly [Symbol.toStringTag]: string = "FileExistsException"; } + +/** + * A class representing an exception that can be thrown when a file isn't found. + * + * ```ts + * import { existsSync } from "node:fs"; + * + * if (!existsSync("file.txt")) + * { + * throw new FileNotFoundException("The file named 'file.txt' wasn't found."); + * } + * ``` + */ export class FileNotFoundException extends FileException { + /** + * Initializes a new instance of the {@link FileNotFoundException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"FileNotFoundException"`. + */ public constructor(message: string, cause?: unknown, name = "FileNotFoundException") { super(message, cause, name); @@ -28,8 +92,27 @@ export class FileNotFoundException extends FileException public override readonly [Symbol.toStringTag]: string = "FileNotFoundException"; } +/** + * A class representing an exception that can be thrown when a key is invalid or not found. + * It's commonly used when working with dictionaries, maps, objects, sets, etc... + * + * ```ts + * const map = new Map(); + * if (!map.has("hash")) + * { + * throw new KeyException("The key 'hash' wasn't found in the collection."); + * } + * ``` + */ export class KeyException extends Exception { + /** + * Initializes a new instance of the {@link KeyException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"KeyException"`. + */ public constructor(message: string, cause?: unknown, name = "KeyException") { super(message, cause, name); @@ -37,8 +120,36 @@ export class KeyException extends Exception public override readonly [Symbol.toStringTag]: string = "KeyException"; } + +/** + * A class representing an exception that can be thrown when a network operation fails. + * It's commonly used when it's unable to connect to a server or when a request times out. + * + * ```ts + * import axios, { isAxiosError } from "axios"; + * + * try { await axios.get("https://api.example.com/data"); } + * catch (error) + * { + * if (isAxiosError(error) && !error.response) + * { + * throw new NetworkException( + * "Unable to establish a connection to the server. " + + * "Please, check your internet connection and try again." + * ); + * } + * } + * ``` + */ export class NetworkException extends Exception { + /** + * Initializes a new instance of the {@link NetworkException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"NetworkException"`. + */ public constructor(message: string, cause?: unknown, name = "NetworkException") { super(message, cause, name); @@ -46,8 +157,28 @@ export class NetworkException extends Exception public override readonly [Symbol.toStringTag]: string = "NetworkException"; } + +/** + * A class representing an exception that can be thrown when a permission is denied. + * It's commonly used when a user tries to access a restricted resource or perform a forbidden action. + * + * ```ts + * const $user = useUserStore(); + * if (!$user.isAdmin) + * { + * throw new PermissionException("You don't have permission to perform this action."); + * } + * ``` + */ export class PermissionException extends Exception { + /** + * Initializes a new instance of the {@link PermissionException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"PermissionException"`. + */ public constructor(message: string, cause?: unknown, name = "PermissionException") { super(message, cause, name); @@ -55,8 +186,28 @@ export class PermissionException extends Exception public override readonly [Symbol.toStringTag]: string = "PermissionException"; } + +/** + * A class representing an exception that can be thrown when a reference is invalid or not found. + * It's commonly used when a variable is `null`, `undefined` or when an object doesn't exist. + * + * ```ts + * const $el = document.getElementById("app"); + * if ($el === null) + * { + * throw new ReferenceException("The element with the ID 'app' wasn't found in the document."); + * } + * ``` + */ export class ReferenceException extends Exception { + /** + * Initializes a new instance of the {@link ReferenceException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"ReferenceException"`. + */ public constructor(message: string, cause?: unknown, name = "ReferenceException") { super(message, cause, name); @@ -65,8 +216,29 @@ export class ReferenceException extends Exception public override readonly [Symbol.toStringTag]: string = "ReferenceException"; } +/** + * A class representing an exception that can be thrown when a runtime error occurs. + * It's commonly used when an unexpected condition is encountered during the execution of a program. + * + * ```ts + * let status: "enabled" | "disabled" = "enabled"; + * + * function enable(): void + * { + * if (status === "enabled") { throw new RuntimeException("The feature is already enabled."); } + * status = "enabled"; + * } + * ``` + */ export class RuntimeException extends Exception { + /** + * Initializes a new instance of the {@link RuntimeException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"RuntimeException"`. + */ public constructor(message: string, cause?: unknown, name = "RuntimeException") { super(message, cause, name); @@ -74,8 +246,27 @@ export class RuntimeException extends Exception public override readonly [Symbol.toStringTag]: string = "RuntimeException"; } + +/** + * A class representing an exception that can be thrown when an environment + * isn't properly configured or when a required variable isn't set. + * It can also be used when the environment on which the program is running is unsupported. + * + * ```ts + * if (!navigator.geolocation) + * { + * throw new EnvironmentException("The Geolocation API isn't supported in this environment."); + * } + */ export class EnvironmentException extends RuntimeException { + /** + * Initializes a new instance of the {@link EnvironmentException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"EnvironmentException"`. + */ public constructor(message: string, cause?: unknown, name = "EnvironmentException") { super(message, cause, name); @@ -84,8 +275,26 @@ export class EnvironmentException extends RuntimeException public override readonly [Symbol.toStringTag]: string = "EnvironmentException"; } +/** + * A class representing an exception that can be thrown when a timeout occurs. + * It's commonly used when a task takes too long to complete or when a request times out. + * + * ```ts + * const timeoutId = setTimeout(() => { throw new TimeoutException("The request timed out."); }, 5000); + * const response = await fetch("https://api.example.com/data"); + * + * clearTimeout(timeoutId); + * ``` + */ export class TimeoutException extends Exception { + /** + * Initializes a new instance of the {@link TimeoutException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"TimeoutException"`. + */ public constructor(message: string, cause?: unknown, name = "TimeoutException") { super(message, cause, name); @@ -93,8 +302,30 @@ export class TimeoutException extends Exception public override readonly [Symbol.toStringTag]: string = "TimeoutException"; } + +/** + * A class representing an exception that can be thrown when a type is invalid or not supported. + * It's commonly used when a function receives an unexpected type of argument. + * + * ```ts + * function greet(name: string): void + * { + * if (typeof name !== "string") + * { + * throw new TypeException("The 'name' argument must be a string."); + * } + * } + * ``` + */ export class TypeException extends Exception { + /** + * Initializes a new instance of the {@link TypeException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"TypeException"`. + */ public constructor(message: string, cause?: unknown, name = "TypeException") { super(message, cause, name); @@ -103,8 +334,29 @@ export class TypeException extends Exception public override readonly [Symbol.toStringTag]: string = "TypeException"; } +/** + * A class representing an exception that can be thrown when a value is invalid. + * It's commonly used when a function receives an unexpected value as an argument. + * + * ```ts + * function setVolume(value: number): void + * { + * if (value < 0) + * { + * throw new ValueException("The 'value' argument must be greater than or equal to 0."); + * } + * } + * ``` + */ export class ValueException extends Exception { + /** + * Initializes a new instance of the {@link ValueException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"ValueException"`. + */ public constructor(message: string, cause?: unknown, name = "ValueException") { super(message, cause, name); @@ -112,8 +364,30 @@ export class ValueException extends Exception public override readonly [Symbol.toStringTag]: string = "ValueException"; } + +/** + * A class representing an exception that can be thrown when a value is out of range. + * It's commonly used when a function receives an unexpected value as an argument. + * + * ```ts + * function setVolume(value: number): void + * { + * if ((value < 0) || (value > 100)) + * { + * throw new RangeException("The 'value' argument must be between 0 and 100."); + * } + * } + * ``` + */ export class RangeException extends ValueException { + /** + * Initializes a new instance of the {@link RangeException} class. + * + * @param message The message that describes the error. + * @param cause The previous caught error that caused this one, if any. + * @param name The name of the exception. Default is `"RangeException"`. + */ public constructor(message: string, cause?: unknown, name = "RangeException") { super(message, cause, name); From eff694b7720611ee1553a8bd5ced527ea9c35f47 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 28 Dec 2024 18:49:12 +0100 Subject: [PATCH 08/32] =?UTF-8?q?add:=20Completed=20JSDoc=20for=20`models/?= =?UTF-8?q?timers`=20files.=20+=20Some=20clean-up.=20=F0=9F=A7=BD=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/types.ts | 6 +- src/models/callbacks/publisher.ts | 6 +- src/models/exceptions/core.ts | 22 +++- src/models/exceptions/index.ts | 80 +++++++++++++- src/models/game-loop.ts | 170 ++++++++++++++++++++++++++++- src/models/json/json-storage.ts | 18 ++-- src/models/timers/clock.ts | 107 +++++++++++++++--- src/models/timers/countdown.ts | 174 ++++++++++++++++++++++++++---- src/utils/async.ts | 2 +- src/utils/iterator.ts | 2 +- src/utils/math.ts | 2 +- src/utils/random.ts | 6 +- 12 files changed, 538 insertions(+), 57 deletions(-) diff --git a/src/core/types.ts b/src/core/types.ts index 0cc568e..c4a61f4 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -4,7 +4,7 @@ * * ```ts * function factory(Factory: Constructor): T { [...] } - * + * * const instance: MyObject = factory(MyObject); * ``` */ @@ -22,7 +22,7 @@ export type Constructor = new (.. * * ```ts * const intervalId: Interval = setInterval(() => { [...] }, 1000); - * + * * clearInterval(intervalId); * ``` */ @@ -39,7 +39,7 @@ export type Interval = ReturnType; * * ```ts * const timeoutId: Timeout = setTimeout(() => { [...] }, 1000); - * + * * clearTimeout(timeoutId); * ``` */ diff --git a/src/models/callbacks/publisher.ts b/src/models/callbacks/publisher.ts index a6f9cdd..23af894 100644 --- a/src/models/callbacks/publisher.ts +++ b/src/models/callbacks/publisher.ts @@ -11,7 +11,7 @@ import type { Callback } from "./types.js"; * It's a simple and efficient way to decouple the objects and make them communicate with each other. * * Using generics, it's also possible to define the type of the events and the callbacks that can be subscribed to them. - * + * * ```ts * interface EventsMap * { @@ -43,6 +43,10 @@ export default class Publisher /** * Initializes a new instance of the {@link Publisher} class. + * + * ```ts + * const publisher = new Publisher(); + * ``` */ public constructor() { diff --git a/src/models/exceptions/core.ts b/src/models/exceptions/core.ts index 5e18b58..eec1467 100644 --- a/src/models/exceptions/core.ts +++ b/src/models/exceptions/core.ts @@ -31,7 +31,7 @@ export default class Exception extends Error * catch (error) * { * const exc = Exception.FromUnknown(error); - * + * * [...] * } * ``` @@ -64,6 +64,12 @@ export default class Exception extends Error /** * Initializes a new instance of the {@link Exception} class. * + * ```ts + * throw new Exception("An error occurred while processing the request."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"Exception"`. @@ -96,7 +102,7 @@ export default class Exception extends Error * Also commonly used to satisfy the type-system, but not part of a real feasible scenario. * * It provides a clear and friendly message by default. - * + * * ```ts * function checkCase(value: "A" | "B" | "C"): 1 | 2 | 3 * { @@ -115,6 +121,12 @@ export class FatalErrorException extends Exception /** * Initializes a new instance of the {@link FatalErrorException} class. * + * ```ts + * throw new FatalErrorException("This error should never happen. Please, contact the support team."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"FatalErrorException"`. @@ -154,6 +166,12 @@ export class NotImplementedException extends FatalErrorException /** * Initializes a new instance of the {@link NotImplementedException} class. * + * ```ts + * throw new NotImplementedException("This method hasn't been implemented yet. Check back later."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"NotImplementedException"`. diff --git a/src/models/exceptions/index.ts b/src/models/exceptions/index.ts index 4e2dc54..a15a56a 100644 --- a/src/models/exceptions/index.ts +++ b/src/models/exceptions/index.ts @@ -22,6 +22,12 @@ export class FileException extends Exception /** * Initializes a new instance of the {@link FileException} class. * + * ```ts + * throw new FileException("An error occurred while trying to read the file."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"FileException"`. @@ -51,6 +57,12 @@ export class FileExistsException extends FileException /** * Initializes a new instance of the {@link FileExistsException} class. * + * ```ts + * throw new FileExistsException("The file named 'data.json' already exists on the server."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"FileExistsException"`. @@ -80,6 +92,12 @@ export class FileNotFoundException extends FileException /** * Initializes a new instance of the {@link FileNotFoundException} class. * + * ```ts + * throw new FileNotFoundException("The file named 'data.json' wasn't found on the server."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"FileNotFoundException"`. @@ -109,6 +127,12 @@ export class KeyException extends Exception /** * Initializes a new instance of the {@link KeyException} class. * + * ```ts + * throw new KeyException("The 'id' key wasn't found in the dictionary."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"KeyException"`. @@ -146,6 +170,12 @@ export class NetworkException extends Exception /** * Initializes a new instance of the {@link NetworkException} class. * + * ```ts + * throw new NetworkException("Couldn't connect to the server. Please, try again later."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"NetworkException"`. @@ -175,6 +205,12 @@ export class PermissionException extends Exception /** * Initializes a new instance of the {@link PermissionException} class. * + * ```ts + * throw new PermissionException("You don't have permission to access this resource."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"PermissionException"`. @@ -204,6 +240,12 @@ export class ReferenceException extends Exception /** * Initializes a new instance of the {@link ReferenceException} class. * + * ```ts + * throw new ReferenceException("The 'canvas' element wasn't found in the document."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"ReferenceException"`. @@ -235,6 +277,12 @@ export class RuntimeException extends Exception /** * Initializes a new instance of the {@link RuntimeException} class. * + * ```ts + * throw new RuntimeException("The received input seems to be malformed or corrupted."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"RuntimeException"`. @@ -263,6 +311,12 @@ export class EnvironmentException extends RuntimeException /** * Initializes a new instance of the {@link EnvironmentException} class. * + * ```ts + * throw new EnvironmentException("The required environment variable 'API_KEY' isn't set."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"EnvironmentException"`. @@ -291,6 +345,12 @@ export class TimeoutException extends Exception /** * Initializes a new instance of the {@link TimeoutException} class. * + * ```ts + * throw new TimeoutException("The task took too long to complete."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"TimeoutException"`. @@ -312,7 +372,7 @@ export class TimeoutException extends Exception * { * if (typeof name !== "string") * { - * throw new TypeException("The 'name' argument must be a string."); + * throw new TypeException("The 'name' argument must be a valid string."); * } * } * ``` @@ -322,6 +382,12 @@ export class TypeException extends Exception /** * Initializes a new instance of the {@link TypeException} class. * + * ```ts + * throw new TypeException("The 'username' argument must be a valid string."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"TypeException"`. @@ -353,6 +419,12 @@ export class ValueException extends Exception /** * Initializes a new instance of the {@link ValueException} class. * + * ```ts + * throw new ValueException("The 'grade' argument cannot be negative."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"ValueException"`. @@ -384,6 +456,12 @@ export class RangeException extends ValueException /** * Initializes a new instance of the {@link RangeException} class. * + * ```ts + * throw new RangeException("The 'percentage' argument must be between 0 and 100."); + * ``` + * + * --- + * * @param message The message that describes the error. * @param cause The previous caught error that caused this one, if any. * @param name The name of the exception. Default is `"RangeException"`. diff --git a/src/models/game-loop.ts b/src/models/game-loop.ts index 372e11a..d8fd726 100644 --- a/src/models/game-loop.ts +++ b/src/models/game-loop.ts @@ -1,32 +1,131 @@ import type { Interval } from "../core/types.js"; import { isBrowser } from "../helpers.js"; +import Publisher from "./callbacks/publisher.js"; import { FatalErrorException, RuntimeException } from "./exceptions/index.js"; +import type { Callback } from "./types.js"; +interface GameLoopEventMap +{ + start: () => void; + stop: () => void; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: Callback; +} + +/** + * A class representing a {@link https://en.wikipedia.org/wiki/Video_game_programming#Game_structure|game loop} pattern + * that allows to run a function at a specific frame rate. + * + * In a browser environment, it uses the native {@link requestAnimationFrame} + * function to run the callback at the refresh rate of the screen. + * In a non-browser environment, however, it uses the {@link setInterval} + * function to run the callback at the specified fixed interval of time. + * + * Every time the callback is executed, it receives the + * elapsed time since the start of the game loop. + * It's also possible to subscribe to the `start` and `stop` events to receive notifications when they occur. + * + * ```ts + * const loop = new GameLoop((elapsedTime: number) => + * { + * console.log(`The game loop has been running for ${elapsedTime}ms.`); + * }); + * + * loop.onStart(() => { console.log("The game loop has started."); }); + * loop.onStop(() => { console.log("The game loop has stopped."); }); + * + * loop.start(); + * ``` + */ export default class GameLoop { + /** + * The handle of the interval or the animation frame, depending on the environment. + * It's used to stop the game loop when the {@link _stop} method is called. + */ protected _handle?: number | Interval; + /** + * The time when the game loop has started. + * In addition to indicating the {@link https://en.wikipedia.org/wiki/Unix_time|Unix timestamp} + * of the start of the game loop, it's also used to calculate the elapsed time. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link startTime} getter instead. + */ protected _startTime: number; + + /** + * The time when the game loop has started. + * In addition to indicating the {@link https://en.wikipedia.org/wiki/Unix_time|Unix timestamp} + * of the start of the game loop, it's also used to calculate the elapsed time. + */ public get startTime(): number { return this._startTime; } + /** + * A boolean value indicating whether the game loop is currently running or not. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link isRunning} getter instead. + */ protected _isRunning: boolean; + + /** + * A boolean value indicating whether the game loop is currently running or not. + */ public get isRunning(): boolean { return this._isRunning; } + /** + * The elapsed time since the start of the game loop. + * It's calculated as the difference between the current time and the {@link startTime}. + */ public get elapsedTime(): number { return performance.now() - this._startTime; } + /** + * The {@link Publisher} object that will be used to publish the events of the game loop. + */ + protected _publisher: Publisher; + + /** + * The internal method actually responsible for starting the game loop. + * + * Depending on the current environment, it could use the + * {@link requestAnimationFrame} or the {@link setInterval} function. + */ protected _start: () => void; + + /** + * The internal method actually responsible for stopping the game loop. + * + * Depending on the current environment, it could use the + * {@link cancelAnimationFrame} or the {@link clearInterval} function. + */ protected _stop: () => void; + /** + * Initializes a new instance of the {@link GameLoop} class. + * + * ```ts + * const loop = new GameLoop((elapsedTime: number) => { [...] }); + * ``` + * + * --- + * + * @param callback The function that will be executed at each iteration of the game loop. + * @param msIfNotBrowser + * The interval in milliseconds that will be used if the current environment isn't a browser. Default is `40`. + */ public constructor(callback: FrameRequestCallback, msIfNotBrowser = 40) { this._startTime = 0; @@ -58,8 +157,24 @@ export default class GameLoop this._stop = () => clearInterval(this._handle as Interval); } + + this._publisher = new Publisher(); } + /** + * Starts the execution of the game loop. + * + * If the game loop is already running, a {@link RuntimeException} will be thrown. + * + * ```ts + * loop.onStart(() => { [...] }); // This callback will be executed. + * loop.start(); + * ``` + * + * --- + * + * @param elapsedTime The elapsed time to set as default when the game loop starts. Default is `0`. + */ public start(elapsedTime = 0): void { if (this._isRunning) { throw new RuntimeException("The game loop has already been started."); } @@ -67,16 +182,69 @@ export default class GameLoop this._startTime = performance.now() - elapsedTime; this._start(); this._isRunning = true; + + this._publisher.publish("start"); } + /** + * Stops the execution of the game loop. + * + * If the game loop hasn't yet started, a {@link RuntimeException} will be thrown. + * + * ```ts + * loop.onStop(() => { [...] }); // This callback will be executed. + * loop.stop(); + * ``` + */ public stop(): void { - if (!(this._isRunning)) { throw new RuntimeException("The game loop hadn't yet started."); } + if (!(this._isRunning)) + { + throw new RuntimeException("The game loop had already stopped or hadn't yet started."); + } if (!(this._handle)) { throw new FatalErrorException(); } this._stop(); this._handle = undefined; this._isRunning = false; + + this._publisher.publish("stop"); + } + + /** + * Subscribes to the `start` event of the game loop. + * + * ```ts + * loop.onStart(() => { console.log("The game loop has started."); }); + * ``` + * + * --- + * + * @param callback The function that will be executed when the game loop starts. + * + * @returns A function that can be used to unsubscribe from the event. + */ + public onStart(callback: () => void): () => void + { + return this._publisher.subscribe("start", callback); + } + + /** + * Subscribes to the `stop` event of the game loop. + * + * ```ts + * loop.onStop(() => { console.log("The game loop has stopped."); }); + * ``` + * + * --- + * + * @param callback The function that will be executed when the game loop stops. + * + * @returns A function that can be used to unsubscribe from the event. + */ + public onStop(callback: () => void): () => void + { + return this._publisher.subscribe("stop", callback); } public readonly [Symbol.toStringTag]: string = "GameLoop"; diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index bdcc968..054ead8 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -47,7 +47,7 @@ export default class JSONStorage * ``` * * --- - * + * * @param preferPersistence * Whether to prefer the {@link localStorage} over the {@link sessionStorage} when calling an ambivalent method. * If omitted, it defaults to `true` to prefer the persistent storage. @@ -323,14 +323,14 @@ export default class JSONStorage /** * Checks whether the value with the specified key exists within the default storage. - * + * * ```ts * if (jsonStorage.has("key")) * { * // The key exists. Do something... * } * ``` - * + * * --- * * @param key The key of the value to check. @@ -349,14 +349,14 @@ export default class JSONStorage /** * Checks whether the value with the specified key exists within the volatile {@link sessionStorage}. - * + * * ```ts * if (jsonStorage.knows("key")) * { * // The key exists. Do something... * } * ``` - * + * * --- * * @param key The key of the value to check. @@ -371,14 +371,14 @@ export default class JSONStorage /** * Checks whether the value with the specified key exists looking first in the * volatile {@link sessionStorage} and then, if not found, in the persistent {@link localStorage}. - * + * * ```ts * if (jsonStorage.find("key")) * { * // The key exists. Do something... * } * ``` - * + * * --- * * @param key The key of the value to check. @@ -392,14 +392,14 @@ export default class JSONStorage /** * Checks whether the value with the specified key exists within the persistent {@link localStorage}. - * + * * ```ts * if (jsonStorage.exists("key")) * { * // The key exists. Do something... * } * ``` - * + * * --- * * @param key The key of the value to check. diff --git a/src/models/timers/clock.ts b/src/models/timers/clock.ts index f72b7a2..6d2c03f 100644 --- a/src/models/timers/clock.ts +++ b/src/models/timers/clock.ts @@ -1,20 +1,56 @@ import { TimeUnit } from "../../utils/date.js"; -import { RangeException, RuntimeException } from "../exceptions/index.js"; import Publisher from "../callbacks/publisher.js"; +import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js"; import GameLoop from "../game-loop.js"; +import type { Callback } from "../types.js"; interface ClockEventMap { start: () => void; stop: () => void; tick: (elapsedTime: number) => void; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: Callback; } +/** + * A class representing a clock. + * + * It can be started, stopped and, when running, it ticks at a specific frame rate. + * It's possible to subscribe to these events to receive notifications when they occur. + * + * ```ts + * const clock = new Clock(); + * + * clock.onStart(() => { console.log("The clock has started."); }); + * clock.onTick((elapsedTime) => { console.log(`The clock has ticked at ${elapsedTime}ms.`); }); + * clock.onStop(() => { console.log("The clock has stopped."); }); + * + * clock.start(); + * ``` + */ export default class Clock extends GameLoop { - protected _publisher: Publisher; - + /** + * The {@link Publisher} object that will be used to publish the events of the clock. + */ + protected override _publisher: Publisher; + + /** + * Initializes a new instance of the {@link Clock} class. + * + * ```ts + * const clock = new Clock(); + * ``` + * + * --- + * + * @param msIfNotBrowser + * The interval in milliseconds at which the clock will tick if the environment is not a browser. + * `TimeUnit.Second` by default. + */ public constructor(msIfNotBrowser: number = TimeUnit.Second) { super((elapsedTime) => this._publisher.publish("tick", elapsedTime), msIfNotBrowser); @@ -22,33 +58,74 @@ export default class Clock extends GameLoop this._publisher = new Publisher(); } + /** + * Starts the execution of the clock. + * + * If the clock is already running, a {@link RuntimeException} will be thrown. + * + * ```ts + * clock.onStart(() => { [...] }); // This callback will be executed. + * clock.start(); + * ``` + * + * --- + * + * @param elapsedTime The elapsed time to set as default when the clock starts. Default is `0`. + */ public override start(elapsedTime = 0): void { if (this._isRunning) { throw new RuntimeException("The clock has already been started."); } - super.start(elapsedTime); + this._startTime = performance.now() - elapsedTime; + this._start(); + this._isRunning = true; this._publisher.publish("start"); } + /** + * Stops the execution of the clock. + * + * If the clock hasn't yet started, a {@link RuntimeException} will be thrown. + * + * ```ts + * clock.onStop(() => { [...] }); // This callback will be executed. + * clock.stop(); + * ``` + */ public override stop(): void { - if (!(this._isRunning)) { throw new RuntimeException("The clock hadn't yet started."); } + if (!(this._isRunning)) { throw new RuntimeException("The clock had already stopped or hadn't yet started."); } + if (!(this._handle)) { throw new FatalErrorException(); } - super.stop(); + this._stop(); + this._handle = undefined; + this._isRunning = false; this._publisher.publish("stop"); } - public onStart(callback: () => void): () => void - { - return this._publisher.subscribe("start", callback); - } - public onStop(callback: () => void): () => void - { - return this._publisher.subscribe("stop", callback); - } - + /** + * Subscribes to the `tick` event of the clock. + * + * ```ts + * clock.onTick((elapsedTime) => { [...] }); // This callback will be executed. + * clock.start(); + * ``` + * + * --- + * + * @param callback The callback that will be executed when the clock ticks. + * @param tickStep + * The minimum time in milliseconds that must pass from the previous execution of the callback to the next one. + * + * - If it's a positive number, the callback will be executed only if the + * time passed from the previous execution is greater than this number. + * - If it's `0`, the callback will be executed every tick without even checking for the time. + * - If it's a negative number, a {@link RangeException} will be thrown. + * + * @returns A function that can be used to unsubscribe from the event. + */ public onTick(callback: (elapsedTime: number) => void, tickStep = 0): () => void { if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); } diff --git a/src/models/timers/countdown.ts b/src/models/timers/countdown.ts index 74d57a6..221e971 100644 --- a/src/models/timers/countdown.ts +++ b/src/models/timers/countdown.ts @@ -1,10 +1,10 @@ import { TimeUnit } from "../../utils/date.js"; -import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js"; -import { DeferredPromise, SmartPromise } from "../promises/index.js"; - import Publisher from "../callbacks/publisher.js"; +import { FatalErrorException, RangeException, RuntimeException } from "../exceptions/index.js"; import GameLoop from "../game-loop.js"; +import { DeferredPromise, SmartPromise } from "../promises/index.js"; +import type { Callback } from "../types.js"; interface CountdownEventMap { @@ -12,37 +12,97 @@ interface CountdownEventMap stop: (reason: unknown) => void; tick: (remainingTime: number) => void; expire: () => void; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: Callback; } +/** + * A class representing a countdown. + * + * It can be started, stopped, when running it ticks at a specific frame rate and it expires when the time's up. + * It's possible to subscribe to these events to receive notifications when they occur. + * + * ```ts + * const countdown = new Countdown(10000); + * + * countdown.onStart(() => { console.log("The countdown has started."); }); + * countdown.onTick((remainingTime) => { console.log(`The countdown has ${remainingTime}ms remaining.`); }); + * countdown.onStop((reason) => { console.log(`The countdown has stopped because of ${reason}.`); }); + * countdown.onExpire(() => { console.log("The countdown has expired."); }); + * + * countdown.start(); + * ``` + */ export default class Countdown extends GameLoop { - protected _deferrer?: DeferredPromise; - protected _publisher: Publisher; - + /** + * The {@link Publisher} object that will be used to publish the events of the countdown. + */ + protected override _publisher: Publisher; + + /** + * The total duration of the countdown in milliseconds. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link duration} getter instead. + */ protected _duration: number; + + /** + * The total duration of the countdown in milliseconds. + */ public get duration(): number { return this._duration; } + /** + * The remaining time of the countdown in milliseconds. + * It's calculated as the difference between the total duration and the elapsed time. + */ public get remainingTime(): number { return this._duration - this.elapsedTime; } + /** + * The {@link DeferredPromise} that will be resolved or rejected when the countdown expires or stops. + */ + protected _deferrer?: DeferredPromise; + + /** + * Initializes a new instance of the {@link Countdown} class. + * + * ```ts + * const countdown = new Countdown(10000); + * ``` + * + * --- + * + * @param duration + * The total duration of the countdown in milliseconds. + * + * @param msIfNotBrowser + * The interval in milliseconds at which the countdown will tick if the environment is not a browser. + * `TimeUnit.Second` by default. + */ public constructor(duration: number, msIfNotBrowser: number = TimeUnit.Second) { const callback = () => { const remainingTime = this.remainingTime; - this._publisher.publish("tick", remainingTime); - if (remainingTime <= 0) { this._deferrerStop(); + this._publisher.publish("tick", 0); this._publisher.publish("expire"); } + else + { + this._publisher.publish("tick", remainingTime); + } }; super(callback, msIfNotBrowser); @@ -51,12 +111,24 @@ export default class Countdown extends GameLoop this._duration = duration; } + /** + * The internal method actually responsible for stopping the + * countdown and resolving or rejecting the {@link _deferrer} promise. + * + * @param reason + * The reason why the countdown has stopped. + * + * - If it's `undefined`, the promise will be resolved. + * - If it's a value, the promise will be rejected with that value. + */ protected _deferrerStop(reason?: unknown): void { if (!(this._isRunning)) { throw new RuntimeException("The countdown hadn't yet started."); } if (!(this._deferrer)) { throw new FatalErrorException(); } - super.stop(); + this._stop(); + this._handle = undefined; + this._isRunning = false; if (reason !== undefined) { this._deferrer.reject(reason); } else { this._deferrer.resolve(); } @@ -64,9 +136,26 @@ export default class Countdown extends GameLoop this._deferrer = undefined; } + /** + * Starts the execution of the countdown. + * + * If the countdown is already running, a {@link RuntimeException} will be thrown. + * + * ```ts + * countdown.onStart(() => { [...] }); // This callback will be executed. + * countdown.start(); + * ``` + * + * --- + * + * @param remainingTime + * The remaining time to set as default when the countdown starts. Default is the {@link duration} itself. + * + * @returns A {@link SmartPromise} that will be resolved or rejected when the countdown expires or stops. + */ public override start(remainingTime: number = this.duration): SmartPromise { - if (this._isRunning) { throw new RuntimeException("The countdown has already been started."); } + if (this._isRunning) { throw new RuntimeException("The countdown had already stopped or hadn't yet started."); } if (this._deferrer) { throw new FatalErrorException(); } this._deferrer = new DeferredPromise(); @@ -76,27 +165,74 @@ export default class Countdown extends GameLoop return this._deferrer; } + + /** + * Stops the execution of the countdown. + * + * If the countdown hasn't yet started, a {@link RuntimeException} will be thrown. + * + * ```ts + * countdown.onStop(() => { [...] }); // This callback will be executed. + * countdown.stop(); + * ``` + * + * --- + * + * @param reason + * The reason why the countdown has stopped. + * + * - If it's `undefined`, the promise will be resolved. + * - If it's a value, the promise will be rejected with that value. + */ public override stop(reason?: unknown): void { + // TODO: Once solved Issues #6 & #10, make the `reason` parameter required. + // - https://github.com/Byloth/core/issues/6 + // - https://github.com/Byloth/core/issues/10 + // this._deferrerStop(reason); this._publisher.publish("stop", reason); } + /** + * Subscribes to the `expire` event of the countdown. + * + * ```ts + * countdown.onExpire(() => { [...] }); // This callback will be executed once the countdown has expired. + * countdown.start(); + * ``` + * + * @param callback The callback that will be executed when the countdown expires. + * + * @returns A function that can be used to unsubscribe from the event. + */ public onExpire(callback: () => void): () => void { return this._publisher.subscribe("expire", callback); } - public onStart(callback: () => void): () => void - { - return this._publisher.subscribe("start", callback); - } - public onStop(callback: (reason?: unknown) => void): () => void - { - return this._publisher.subscribe("stop", callback); - } - + /** + * Subscribes to the `tick` event of the countdown. + * + * ```ts + * countdown.onTick((remainingTime) => { [...] }); // This callback will be executed. + * countdown.start(); + * ``` + * + * --- + * + * @param callback The callback that will be executed when the countdown ticks. + * @param tickStep + * The minimum time in milliseconds that must pass from the previous execution of the callback to the next one. + * + * - If it's a positive number, the callback will be executed only if the + * time passed from the previous execution is greater than this number. + * - If it's `0`, the callback will be executed every tick without even checking for the time. + * - If it's a negative number, a {@link RangeException} will be thrown. + * + * @returns A function that can be used to unsubscribe from the event. + */ public onTick(callback: (remainingTime: number) => void, tickStep = 0): () => void { if (tickStep < 0) { throw new RangeException("The tick step must be a non-negative number."); } diff --git a/src/utils/async.ts b/src/utils/async.ts index 0d18a6d..15a80c4 100644 --- a/src/utils/async.ts +++ b/src/utils/async.ts @@ -48,7 +48,7 @@ export function nextAnimationFrame(): Promise * for (let i = 0; i < 100_000_000; i += 1) * { * doSomething(i); - * + * * if (i % 100 === 0) await yieldToEventLoop(); * } * ``` diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index fe279fe..90c0542 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -135,7 +135,7 @@ export function range(end: number): SmartIterator; * If the `end` value is less than the `start` value, the iterator will generate the numbers in reverse order. * * @param step The step between the numbers. Default is `1`. - * + * * @returns A {@link SmartIterator} object that generates the numbers in the range. */ export function range(start: number, end: number, step?: number): SmartIterator; diff --git a/src/utils/math.ts b/src/utils/math.ts index 0ddf62a..b301385 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -14,7 +14,7 @@ import { zip } from "./iterator.js"; * * @param values * The list of values to compute the average. - * + * * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. * * @param weights diff --git a/src/utils/random.ts b/src/utils/random.ts index 8b60ce7..6722dfb 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -4,7 +4,7 @@ import { ValueException } from "../models/index.js"; * A wrapper class around the native {@link Math.random} function that * provides a set of methods to generate random values more easily. * It can be used to generate random numbers, booleans and other different values. - * + * * It cannot be instantiated directly. */ export default class Random @@ -128,7 +128,7 @@ export default class Random * The array of elements to pick from. * * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. - * + * * @returns A valid random index from the given array. */ public static Index(elements: readonly T[]): number @@ -145,7 +145,7 @@ export default class Random * The array of elements to pick from. * * It must contain at least one element. Otherwise, a {@link ValueException} will be thrown. - * + * * @returns A random element from the given array. */ public static Choice(elements: readonly T[]): T From 10a9dc278ff40c1aeba526364c78fc93930b28f7 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 28 Dec 2024 19:59:26 +0100 Subject: [PATCH 09/32] add: Completed some JSDoc for `models/promises` files. --- src/models/exceptions/index.ts | 1 + src/models/game-loop.ts | 4 +- src/models/promises/smart-promise.ts | 222 ++++++++++++++++++++++++++- 3 files changed, 221 insertions(+), 6 deletions(-) diff --git a/src/models/exceptions/index.ts b/src/models/exceptions/index.ts index a15a56a..e4bc297 100644 --- a/src/models/exceptions/index.ts +++ b/src/models/exceptions/index.ts @@ -305,6 +305,7 @@ export class RuntimeException extends Exception * { * throw new EnvironmentException("The Geolocation API isn't supported in this environment."); * } + * ``` */ export class EnvironmentException extends RuntimeException { diff --git a/src/models/game-loop.ts b/src/models/game-loop.ts index d8fd726..842357b 100644 --- a/src/models/game-loop.ts +++ b/src/models/game-loop.ts @@ -68,7 +68,7 @@ export default class GameLoop } /** - * A boolean value indicating whether the game loop is currently running or not. + * A flag indicating whether the game loop is currently running or not. * * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public & readonly property, use the {@link isRunning} getter instead. @@ -76,7 +76,7 @@ export default class GameLoop protected _isRunning: boolean; /** - * A boolean value indicating whether the game loop is currently running or not. + * A flag indicating whether the game loop is currently running or not. */ public get isRunning(): boolean { diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 4bdea2a..a9cda28 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -1,18 +1,119 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types.js"; +/** + * A wrapper class representing an enhanced version of the native {@link Promise} object. + * + * It provides additional properties that allow you to check the state of the promise itself. + * The state can be either `pending`, `fulfilled` or `rejected` and you can access it via the + * {@link isPending}, {@link isFulfilled} and {@link isRejected} properties. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), 1000); + * }); + * + * console.log(promise.isPending); // true + * console.log(await promise); // "Hello, World!" + * console.log(promise.isFulfilled); // true + * ``` + */ export default class SmartPromise implements Promise { + /** + * Wraps a new {@link SmartPromise} object around an existing native {@link Promise} object. + * + * ```ts + * const request = fetch("https://api.example.com/data"); + * const smartRequest = SmartPromise.FromPromise(request); + * + * console.log(request.isPending); // Throws an error: `isPending` is not a property of `Promise`. + * console.log(smartRequest.isPending); // true + * + * const response = await request; + * console.log(smartRequest.isFulfilled); // true + * ``` + * + * --- + * + * @param promise The promise to wrap. + * + * @returns A new {@link SmartPromise} object that wraps the provided promise. + */ public static FromPromise(promise: Promise): SmartPromise { return new SmartPromise((resolve, reject) => promise.then(resolve, reject)); } + /** + * A flag indicating whether the promise is still pending or not. + * + * The protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link isPending} getter instead. + */ protected _isPending: boolean; + + /** + * A flag indicating whether the promise is still pending or not. + */ + public get isPending(): boolean + { + return this._isPending; + } + + /** + * A flag indicating whether the promise has been fulfilled or not. + * + * The protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link isFulfilled} getter instead. + */ protected _isFulfilled: boolean; + + /** + * A flag indicating whether the promise has been fulfilled or not. + */ + public get isFulfilled(): boolean + { + return this._isFulfilled; + } + + /** + * A flag indicating whether the promise has been rejected or not. + * + * The protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link isRejected} getter instead. + */ protected _isRejected: boolean; + /** + * A flag indicating whether the promise has been rejected or not. + */ + public get isRejected(): boolean + { + return this._isRejected; + } + + /** + * The native {@link Promise} object that is wrapped by this {@link SmartPromise} itself. + */ protected _promise: Promise; + /** + * Initializes a new instance of the {@link SmartPromise} class. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), 1000); + * }); + * ``` + * + * --- + * + * @param executor + * The function that is responsible for eventually resolving or rejecting the promise. + * Just ike for the native {@link Promise}, it will be immediately executed after the promise has been created. + */ public constructor(executor: PromiseExecutor) { this._isPending = true; @@ -38,12 +139,65 @@ export default class SmartPromise implements Promise .then(_onFulfilled, _onRejected); } - public get isPending(): boolean { return this._isPending; } - public get isFulfilled(): boolean { return this._isFulfilled; } - public get isRejected(): boolean { return this._isRejected; } - + /** + * Creates a new {@link Promise} object identical to the one that is wrapped by this {@link SmartPromise}. + * Just with a different reference so you won't be able to compare or modify the original one. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), 1000); + * }); + * + * console.log(await promise.then()); // "Hello, World!" + * ``` + * + * --- + * + * @returns A new {@link Promise} object that's identical to the original one. + */ public then(onFulfilled?: null): Promise; + + /** + * Attaches a callback to the promise that will be called right after the promise has been fulfilled. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), 1000); + * }); + * + * promise.then((result) => console.log(result)); // "Hello, World!" + * ``` + * + * --- + * + * @param onFulfilled The callback that will be called when the promise has been fulfilled. + * + * @returns A new {@link Promise} object that will be resolved with the return value of the callback. + */ public then(onFulfilled: FulfilledHandler, onRejected?: null): Promise; + + /** + * Attaches callbacks to the promise that will be called right after the promise has been fulfilled or rejected. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(resolve, Math.random() * 1000); + * setTimeout(reject, Math.random() * 1000); + * }); + * + * promise.then(() => console.log("OK!"), () => console.log("KO!")); // "OK!" or "KO!" + * ``` + * + * --- + * + * @param onFulfilled The callback that will be called when the promise has been fulfilled. + * @param onRejected The callback that will be called when the promise has been rejected. + * + * @returns A new {@link Promise} object that will be resolved with the return value of the callback. + */ public then(onFulfilled: FulfilledHandler, onRejected: RejectedHandler) : Promise; public then( @@ -53,12 +207,72 @@ export default class SmartPromise implements Promise return this._promise.then(onFulfilled, onRejected); } + /** + * Creates a new {@link Promise} object identical to the one that is wrapped by this {@link SmartPromise}. + * Just with a different reference so you won't be able to compare or modify the original one. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => reject(new Error("An unknown error occurred.")), 1000); + * }); + * + * promise.catch(); // Uncaught Error: An unknown error occurred. + * ``` + * + * --- + * + * @returns A new {@link Promise} object that's identical to the original one. + */ public catch(onRejected?: null): Promise; + + /** + * Attaches a callback to the promise that will be called right after the promise has been rejected. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(() => reject(new Error("An unknown error occurred.")), 1000); + * }); + * + * promise.catch((reason) => console.error(reason)); // "Error: An unknown error occurred." + * ``` + * + * --- + * + * @param onRejected The callback that will be called when the promise has been rejected. + * + * @returns + * A new {@link Promise} object that will catch the error and resolve with the return value of the callback. + */ public catch(onRejected: RejectedHandler): Promise; public catch(onRejected?: RejectedHandler | null): Promise { return this._promise.catch(onRejected); } + + /** + * Attaches a callback to the promise that will be called right after the promise has been settled. + * + * ```ts + * const promise = new SmartPromise((resolve, reject) => + * { + * setTimeout(resolve, Math.random() * 1000); + * setTimeout(reject, Math.random() * 1000); + * }); + * + * + * promise.then(() => console.log("OK!")) // First will only prints "OK!" is the promise is resolved. + * .catch(() => console.log("KO!")) // First will only prints "KO!" is the promise is rejected. + * .finally(() => console.log("Done!")); // Then will always prints "Done!" in any case. + * ``` + * + * --- + * + * @param onFinally The callback that will be called when the promise has been settled. + * + * @returns A new {@link Promise} object that will always execute the callback at the end. + */ public finally(onFinally?: (() => void) | null): Promise { return this._promise.finally(onFinally); From 58a97551151b1640e807d3d2cb0ea947947e483a Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 29 Dec 2024 01:00:10 +0100 Subject: [PATCH 10/32] fix: Minor fixes. --- src/models/promises/smart-promise.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index a9cda28..ca24a7b 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -262,9 +262,9 @@ export default class SmartPromise implements Promise * }); * * - * promise.then(() => console.log("OK!")) // First will only prints "OK!" is the promise is resolved. - * .catch(() => console.log("KO!")) // First will only prints "KO!" is the promise is rejected. - * .finally(() => console.log("Done!")); // Then will always prints "Done!" in any case. + * promise.then(() => console.log("Yup!")) // First will only prints "Yup!" is the promise is resolved. + * .catch(() => console.log("Nope!")) // First will only prints "Nope!" is the promise is rejected. + * .finally(() => console.log("What?")); // Then will always prints "What?" in any case. * ``` * * --- From c6b77ea40f7e5b99601c5db25cbbc33779f1160e Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 29 Dec 2024 02:42:39 +0100 Subject: [PATCH 11/32] add: A better `SmartPromise` documentation. --- src/models/promises/smart-promise.ts | 86 +++++++++++++++++----------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index ca24a7b..2e3df98 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -3,9 +3,9 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types /** * A wrapper class representing an enhanced version of the native {@link Promise} object. * - * It provides additional properties that allow you to check the state of the promise itself. - * The state can be either `pending`, `fulfilled` or `rejected` and you can access it via the - * {@link isPending}, {@link isFulfilled} and {@link isRejected} properties. + * It provides additional properties to check the state of the promise itself. + * The state can be either `pending`, `fulfilled` or `rejected` and is accessible through + * the {@link isPending}, {@link isFulfilled} and {@link isRejected} properties. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -14,7 +14,11 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types * }); * * console.log(promise.isPending); // true + * console.log(promise.isFulfilled); // false + * * console.log(await promise); // "Hello, World!" + * + * console.log(promise.isPending); // false * console.log(promise.isFulfilled); // true * ``` */ @@ -48,7 +52,7 @@ export default class SmartPromise implements Promise /** * A flag indicating whether the promise is still pending or not. * - * The protected property is the only one that can be modified directly by the derived classes. + * The protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public & readonly property, use the {@link isPending} getter instead. */ protected _isPending: boolean; @@ -64,7 +68,7 @@ export default class SmartPromise implements Promise /** * A flag indicating whether the promise has been fulfilled or not. * - * The protected property is the only one that can be modified directly by the derived classes. + * The protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public & readonly property, use the {@link isFulfilled} getter instead. */ protected _isFulfilled: boolean; @@ -80,7 +84,7 @@ export default class SmartPromise implements Promise /** * A flag indicating whether the promise has been rejected or not. * - * The protected property is the only one that can be modified directly by the derived classes. + * The protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public & readonly property, use the {@link isRejected} getter instead. */ protected _isRejected: boolean; @@ -94,7 +98,7 @@ export default class SmartPromise implements Promise } /** - * The native {@link Promise} object that is wrapped by this {@link SmartPromise} itself. + * The native {@link Promise} object wrapped by this {@link SmartPromise} instance. */ protected _promise: Promise; @@ -111,8 +115,8 @@ export default class SmartPromise implements Promise * --- * * @param executor - * The function that is responsible for eventually resolving or rejecting the promise. - * Just ike for the native {@link Promise}, it will be immediately executed after the promise has been created. + * The function responsible for eventually resolving or rejecting the promise. + * Similarly to the native {@link Promise} object, it's immediately executed after the promise is created. */ public constructor(executor: PromiseExecutor) { @@ -140,8 +144,8 @@ export default class SmartPromise implements Promise } /** - * Creates a new {@link Promise} object identical to the one that is wrapped by this {@link SmartPromise}. - * Just with a different reference so you won't be able to compare or modify the original one. + * Creates a new {@link Promise} identical to the one wrapped by + * this {@link SmartPromise} instance, with a different reference. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -154,12 +158,15 @@ export default class SmartPromise implements Promise * * --- * - * @returns A new {@link Promise} object that's identical to the original one. + * @returns A new {@link Promise} identical to the original one. */ public then(onFulfilled?: null): Promise; /** - * Attaches a callback to the promise that will be called right after the promise has been fulfilled. + * Attaches a callback that executes right after the promise is fulfilled. + * + * The previous result of the promise is passed as the argument to the callback. + * The callback's return value is considered the new promise's result instead. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -172,14 +179,24 @@ export default class SmartPromise implements Promise * * --- * - * @param onFulfilled The callback that will be called when the promise has been fulfilled. + * @param onFulfilled The callback to execute once the promise is fulfilled. * - * @returns A new {@link Promise} object that will be resolved with the return value of the callback. + * @returns A new {@link Promise} resolved with the return value of the callback. */ public then(onFulfilled: FulfilledHandler, onRejected?: null): Promise; /** - * Attaches callbacks to the promise that will be called right after the promise has been fulfilled or rejected. + * Attaches callbacks that executes right after the promise is fulfilled or rejected. + * + * The previous result of the promise is passed as the argument to the fulfillment callback. + * The fulfillment callback's return value is considered the new promise's result instead. + * + * If an error is thrown during execution, the rejection callback is then executed instead. + * + * Also note that: + * - If the rejection callback runs properly, the error is considered handled. + * The rejection callback's return value is considered the new promise's result. + * - If the rejection callback throws an error, the new promise is rejected with that error. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -193,10 +210,10 @@ export default class SmartPromise implements Promise * * --- * - * @param onFulfilled The callback that will be called when the promise has been fulfilled. - * @param onRejected The callback that will be called when the promise has been rejected. + * @param onFulfilled The callback to execute once the promise is fulfilled. + * @param onRejected The callback to execute once the promise is rejected. * - * @returns A new {@link Promise} object that will be resolved with the return value of the callback. + * @returns A new {@link Promise} resolved or rejected based on the callbacks. */ public then(onFulfilled: FulfilledHandler, onRejected: RejectedHandler) : Promise; @@ -208,8 +225,8 @@ export default class SmartPromise implements Promise } /** - * Creates a new {@link Promise} object identical to the one that is wrapped by this {@link SmartPromise}. - * Just with a different reference so you won't be able to compare or modify the original one. + * Creates a new {@link Promise} identical to the one wrapped by + * this {@link SmartPromise} instance, with a different reference. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -222,12 +239,18 @@ export default class SmartPromise implements Promise * * --- * - * @returns A new {@link Promise} object that's identical to the original one. + * @returns A new {@link Promise} identical to the original one. */ public catch(onRejected?: null): Promise; /** - * Attaches a callback to the promise that will be called right after the promise has been rejected. + * Attaches a callback to handle the potential rejection of the promise. + * If it happens, the callback is then executed. + * + * Also note that: + * - If the callback runs properly, the error is considered handled. + * The callback's return value is considered the new promise's result. + * - If the callback throws an error, the new promise is rejected with that error. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -240,10 +263,9 @@ export default class SmartPromise implements Promise * * --- * - * @param onRejected The callback that will be called when the promise has been rejected. + * @param onRejected The callback to execute once the promise is rejected. * - * @returns - * A new {@link Promise} object that will catch the error and resolve with the return value of the callback. + * @returns A new {@link Promise} able to catch and handle the potential error. */ public catch(onRejected: RejectedHandler): Promise; public catch(onRejected?: RejectedHandler | null): Promise @@ -252,7 +274,7 @@ export default class SmartPromise implements Promise } /** - * Attaches a callback to the promise that will be called right after the promise has been settled. + * Attaches a callback that executes right after the promise is settled, regardless of the outcome. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -262,16 +284,16 @@ export default class SmartPromise implements Promise * }); * * - * promise.then(() => console.log("Yup!")) // First will only prints "Yup!" is the promise is resolved. - * .catch(() => console.log("Nope!")) // First will only prints "Nope!" is the promise is rejected. - * .finally(() => console.log("What?")); // Then will always prints "What?" in any case. + * promise.then(() => console.log("OK!")) + * .catch(() => console.log("KO!")) + * .finally(() => console.log("Done!")); // Always logs "Done!" * ``` * * --- * - * @param onFinally The callback that will be called when the promise has been settled. + * @param onFinally The callback to execute when once promise is settled. * - * @returns A new {@link Promise} object that will always execute the callback at the end. + * @returns A new {@link Promise} that executes the callback once the promise is settled. */ public finally(onFinally?: (() => void) | null): Promise { From 4a5db3a3240eb67f410e6b2c7d3dfefab801540c Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Mon, 30 Dec 2024 19:02:40 +0100 Subject: [PATCH 12/32] add: Completed JSDoc for `models/promises` files. --- src/core/types.ts | 4 +- src/models/exceptions/index.ts | 2 +- src/models/promises/deferred-promise.ts | 72 +++++++++++++++++++++++-- src/models/promises/smart-promise.ts | 35 ++++++------ src/models/promises/timed-promise.ts | 37 +++++++++++++ src/models/timers/countdown.ts | 4 +- src/utils/async.ts | 2 +- src/utils/date.ts | 2 +- 8 files changed, 131 insertions(+), 27 deletions(-) diff --git a/src/core/types.ts b/src/core/types.ts index c4a61f4..bbbed79 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -21,7 +21,7 @@ export type Constructor = new (.. * This allows to seamlessly use the same code in both environments, without having to deal with the differences: * * ```ts - * const intervalId: Interval = setInterval(() => { [...] }, 1000); + * const intervalId: Interval = setInterval(() => { [...] }, 1_000); * * clearInterval(intervalId); * ``` @@ -38,7 +38,7 @@ export type Interval = ReturnType; * This allows to seamlessly use the same code in both environments, without having to deal with the differences: * * ```ts - * const timeoutId: Timeout = setTimeout(() => { [...] }, 1000); + * const timeoutId: Timeout = setTimeout(() => { [...] }, 1_000); * * clearTimeout(timeoutId); * ``` diff --git a/src/models/exceptions/index.ts b/src/models/exceptions/index.ts index e4bc297..bd0e59b 100644 --- a/src/models/exceptions/index.ts +++ b/src/models/exceptions/index.ts @@ -335,7 +335,7 @@ export class EnvironmentException extends RuntimeException * It's commonly used when a task takes too long to complete or when a request times out. * * ```ts - * const timeoutId = setTimeout(() => { throw new TimeoutException("The request timed out."); }, 5000); + * const timeoutId = setTimeout(() => { throw new TimeoutException("The request timed out."); }, 5_000); * const response = await fetch("https://api.example.com/data"); * * clearTimeout(timeoutId); diff --git a/src/models/promises/deferred-promise.ts b/src/models/promises/deferred-promise.ts index a6e93b8..d6aa3fd 100644 --- a/src/models/promises/deferred-promise.ts +++ b/src/models/promises/deferred-promise.ts @@ -2,11 +2,63 @@ import type { PromiseResolver, PromiseRejecter, FulfilledHandler, RejectedHandle import SmartPromise from "./smart-promise.js"; +/** + * A class representing a promise that can be resolved or rejected from the "outside". + * The `resolve` and `reject` methods are exposed to allow the promise to be settled from another context. + * + * It's particularly useful in scenarios where the promise is created and needs to be awaited in one place, + * while being resolved or rejected in another (e.g. an event handler for an user interaction). + * + * This is a change in the approach to promises: instead of defining how the promise will be resolved (or rejected), + * you define how to handle the resolution (or rejection) when it occurs. + * + * ```ts + * const promise = new DeferredPromise((value: string) => value.split(" ")); + * + * promise.then((result) => console.log(result)); // ["Hello,", "World!"] + * promise.resolve("Hello, World!"); + * ``` + */ export default class DeferredPromise extends SmartPromise { + /** + * The exposed function that allows to resolve the promise. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link resolve} getter instead. + */ protected _resolve: PromiseResolver; + + /** + * The exposed function that allows to reject the promise. + */ + public get resolve(): PromiseResolver { return this._resolve; } + + /** + * The exposed function that allows to reject the promise. + * + * This protected property is the only one that can be modified directly by the derived classes. + * If you're looking for the public & readonly property, use the {@link reject} getter instead. + */ protected _reject: PromiseRejecter; + /** + * The exposed function that allows to reject the promise. + */ + public get reject(): PromiseRejecter { return this._reject; } + + /** + * Initializes a new instance of the {@link DeferredPromise} class. + * + * ```ts + * const promise = new DeferredPromise((value: string) => value.split(" ")); + * ``` + * + * --- + * + * @param onFulfilled The callback to execute once the promise is fulfilled. + * @param onRejected The callback to execute once the promise is rejected. + */ public constructor(onFulfilled?: FulfilledHandler | null, onRejected?: RejectedHandler | null) { let _resolve: PromiseResolver; @@ -27,9 +79,23 @@ export default class DeferredPromise extends SmartPr this._reject = _reject!; } - public get resolve(): PromiseResolver { return this._resolve; } - public get reject(): PromiseRejecter { return this._reject; } - + /** + * Watches another promise and resolves or rejects this promise when the other one is settled. + * + * ```ts + * const promise = new Promise((resolve) => setTimeout(() => resolve("Hello, World!"), 1_000)); + * const deferred = new DeferredPromise((value: string) => value.split(" ")); + * + * deferred.then((result) => console.log(result)); // ["Hello,", "World!"] + * deferred.watch(promise); + * ``` + * + * --- + * + * @param otherPromise The promise to watch. + * + * @returns The current instance of the {@link DeferredPromise} class. + */ public watch(otherPromise: PromiseLike): this { otherPromise.then(this.resolve, this.reject); diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 2e3df98..96079eb 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -8,9 +8,9 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types * the {@link isPending}, {@link isFulfilled} and {@link isRejected} properties. * * ```ts - * const promise = new SmartPromise((resolve, reject) => + * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => resolve("Hello, World!"), 1000); + * setTimeout(() => resolve("Hello, World!"), 1_000); * }); * * console.log(promise.isPending); // true @@ -106,9 +106,9 @@ export default class SmartPromise implements Promise * Initializes a new instance of the {@link SmartPromise} class. * * ```ts - * const promise = new SmartPromise((resolve, reject) => + * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => resolve("Hello, World!"), 1000); + * setTimeout(() => resolve("Hello, World!"), 1_000); * }); * ``` * @@ -148,9 +148,9 @@ export default class SmartPromise implements Promise * this {@link SmartPromise} instance, with a different reference. * * ```ts - * const promise = new SmartPromise((resolve, reject) => + * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => resolve("Hello, World!"), 1000); + * setTimeout(() => resolve("Hello, World!"), 1_000); * }); * * console.log(await promise.then()); // "Hello, World!" @@ -169,9 +169,9 @@ export default class SmartPromise implements Promise * The callback's return value is considered the new promise's result instead. * * ```ts - * const promise = new SmartPromise((resolve, reject) => + * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => resolve("Hello, World!"), 1000); + * setTimeout(() => resolve("Hello, World!"), 1_000); * }); * * promise.then((result) => console.log(result)); // "Hello, World!" @@ -201,8 +201,8 @@ export default class SmartPromise implements Promise * ```ts * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(resolve, Math.random() * 1000); - * setTimeout(reject, Math.random() * 1000); + * setTimeout(resolve, Math.random() * 1_000); + * setTimeout(reject, Math.random() * 1_000); * }); * * promise.then(() => console.log("OK!"), () => console.log("KO!")); // "OK!" or "KO!" @@ -231,7 +231,7 @@ export default class SmartPromise implements Promise * ```ts * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => reject(new Error("An unknown error occurred.")), 1000); + * setTimeout(() => reject(new Error("An unknown error occurred.")), 1_000); * }); * * promise.catch(); // Uncaught Error: An unknown error occurred. @@ -255,7 +255,7 @@ export default class SmartPromise implements Promise * ```ts * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(() => reject(new Error("An unknown error occurred.")), 1000); + * setTimeout(() => reject(new Error("An unknown error occurred.")), 1_000); * }); * * promise.catch((reason) => console.error(reason)); // "Error: An unknown error occurred." @@ -279,14 +279,15 @@ export default class SmartPromise implements Promise * ```ts * const promise = new SmartPromise((resolve, reject) => * { - * setTimeout(resolve, Math.random() * 1000); - * setTimeout(reject, Math.random() * 1000); + * setTimeout(resolve, Math.random() * 1_000); + * setTimeout(reject, Math.random() * 1_000); * }); * * - * promise.then(() => console.log("OK!")) - * .catch(() => console.log("KO!")) - * .finally(() => console.log("Done!")); // Always logs "Done!" + * promise + * .then(() => console.log("OK!")) // Logs "OK!" if the promise is fulfilled. + * .catch(() => console.log("KO!")) // Logs "KO!" if the promise is rejected. + * .finally(() => console.log("Done!")); // Always logs "Done!". * ``` * * --- diff --git a/src/models/promises/timed-promise.ts b/src/models/promises/timed-promise.ts index 5bbaa5a..92436f9 100644 --- a/src/models/promises/timed-promise.ts +++ b/src/models/promises/timed-promise.ts @@ -3,8 +3,45 @@ import { TimeoutException } from "../exceptions/index.js"; import SmartPromise from "./smart-promise.js"; import type { MaybePromise, PromiseExecutor } from "./types.js"; +/** + * A class representing a {@link SmartPromise} that rejects automatically after a given time. + * It's useful for operations that must be completed within a certain time frame. + * + * If the operation takes longer than the specified time, the promise is rejected with a {@link TimeoutException}. + * + * ```ts + * const promise = new TimedPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), Math.random() * 10_000); + * + * }, 5_000); + * + * promise + * .then((result) => console.log(result)) // "Hello, World!" + * .catch((error) => console.error(error)); // TimeoutException: The operation has timed out. + * ``` + */ export default class TimedPromise extends SmartPromise { + /** + * Initializes a new instance of the {@link TimedPromise} class. + * + * ```ts + * const promise = new TimedPromise((resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), Math.random() * 10_000); + * + * }, 5_000); + * ``` + * + * --- + * + * @param executor + * The function responsible for eventually resolving or rejecting the promise. + * Similarly to the native {@link Promise} object, it's immediately executed after the promise is created. + * + * @param timeout The maximum time in milliseconds that the operation can take before timing out. + */ public constructor(executor: PromiseExecutor, timeout?: number) { super((resolve, reject) => diff --git a/src/models/timers/countdown.ts b/src/models/timers/countdown.ts index 221e971..b53efd4 100644 --- a/src/models/timers/countdown.ts +++ b/src/models/timers/countdown.ts @@ -24,7 +24,7 @@ interface CountdownEventMap * It's possible to subscribe to these events to receive notifications when they occur. * * ```ts - * const countdown = new Countdown(10000); + * const countdown = new Countdown(10_000); * * countdown.onStart(() => { console.log("The countdown has started."); }); * countdown.onTick((remainingTime) => { console.log(`The countdown has ${remainingTime}ms remaining.`); }); @@ -75,7 +75,7 @@ export default class Countdown extends GameLoop * Initializes a new instance of the {@link Countdown} class. * * ```ts - * const countdown = new Countdown(10000); + * const countdown = new Countdown(10_000); * ``` * * --- diff --git a/src/utils/async.ts b/src/utils/async.ts index 15a80c4..a655a8c 100644 --- a/src/utils/async.ts +++ b/src/utils/async.ts @@ -4,7 +4,7 @@ * * ```ts * doSomething(); - * await delay(1000); + * await delay(1_000); * doSomethingElse(); * ``` * diff --git a/src/utils/date.ts b/src/utils/date.ts index 14a7067..a3f95e8 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -22,7 +22,7 @@ export enum TimeUnit /** * A second: 1000 milliseconds. */ - Second = 1000, + Second = 1_000, /** * A minute: 60 seconds. From 5d170cf82056d579c1b95c18027ed263f1aa3dd0 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Mon, 30 Dec 2024 19:18:48 +0100 Subject: [PATCH 13/32] add: Missing JSDoc for `models/promises/types` file. --- src/models/promises/types.ts | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/models/promises/types.ts b/src/models/promises/types.ts index 22618a4..cbf7f8d 100644 --- a/src/models/promises/types.ts +++ b/src/models/promises/types.ts @@ -1,8 +1,77 @@ +/** + * An utility type that represents a value that can be either a value or a promise of that value. + * This is useful when you want to handle both synchronous and asynchronous values in the same way. + * + * ```ts + * async function splitWords(value: MaybePromise): Promise + * { + * return (await value).split(" "); + * } + * ``` + */ export type MaybePromise = T | PromiseLike; +/** + * An utility type that represents the callback that is executed when a promise is fulfilled. + * It's compatible with the `onFulfilled` parameter of the `then` method of the native {@link Promise} object. + * + * ```ts + * const onFulfilled: FulfilledHandler = (value) => value.split(" "); + * + * await new Promise((resolve) => resolve("Hello, World!")) + * .then(onFulfilled); + * ``` + */ export type FulfilledHandler = (value: T) => MaybePromise; + +/** + * An utility type that represents the callback that is executed when a promise is rejected. + * It's compatible with the `onRejected` parameter of the `then`/`catch` methods of the native {@link Promise} object. + * + * ```ts + * const onRejected: RejectedHandler = (reason) => String(reason); + * + * await new Promise((_, reject) => reject(new Error("An error occurred."))) + * .catch(onRejected); + * ``` + */ export type RejectedHandler = (reason: E) => MaybePromise; +/** + * An utility type that represents a function that can be used to resolve a promise. + * It's compatible with the `resolve` parameter of the native {@link Promise} executor. + * + * ```ts + * let _resolve: PromiseResolver = (result) => console.log(result); + * + * await new Promise((resolve) => { _resolve = resolve; }); + * ``` + */ export type PromiseResolver = (result: MaybePromise) => void; + +/** + * An utility type that represents a function that can be used to reject a promise. + * It's compatible with the `reject` parameter of the native {@link Promise} executor. + * + * ```ts + * let _reject: PromiseRejecter = (reason) => console.error(reason); + * + * await new Promise((_, reject) => { _reject = reject; }); + * ``` + */ export type PromiseRejecter = (reason?: MaybePromise) => void; + +/** + * An utility type that represents the function that will be executed by the promise. + * It's compatible with the `executor` parameter of the native {@link Promise} object. + * + * ```ts + * const executor: PromiseExecutor = (resolve, reject) => + * { + * setTimeout(() => resolve("Hello, World!"), 1_000); + * }; + * + * await new Promise(executor); + * ``` + */ export type PromiseExecutor = (resolve: PromiseResolver, reject: PromiseRejecter) => void; From f4da6234f76e011649687f65daa439b845d44c2a Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 31 Dec 2024 14:34:00 +0100 Subject: [PATCH 14/32] add: Added all the documentation for the used generic types. --- src/models/callbacks/callable-object.ts | 6 + src/models/callbacks/publisher.ts | 13 ++ src/models/callbacks/switchable-callback.ts | 4 + src/models/callbacks/types.ts | 8 ++ src/models/iterators/smart-async-iterator.ts | 18 ++- src/models/iterators/smart-iterator.ts | 132 ++++++++++++++++++- src/models/json/json-storage.ts | 30 +++++ src/models/promises/deferred-promise.ts | 12 +- src/models/promises/smart-promise.ts | 19 ++- src/models/promises/timed-promise.ts | 4 + src/models/promises/types.ts | 27 ++++ src/utils/iterator.ts | 13 ++ src/utils/math.ts | 4 + src/utils/random.ts | 4 + 14 files changed, 276 insertions(+), 18 deletions(-) diff --git a/src/models/callbacks/callable-object.ts b/src/models/callbacks/callable-object.ts index bb9f2e7..41e7443 100644 --- a/src/models/callbacks/callable-object.ts +++ b/src/models/callbacks/callable-object.ts @@ -24,6 +24,12 @@ const SmartFunction = (Function as unknown) as new { callback.enabled = false; }); * ``` + * + * --- + * + * @template T + * The type signature of the callback function. + * It must be a function. Default is `(...args: any[]) => any`. */ export default abstract class CallableObject = () => void> extends SmartFunction, ReturnType> diff --git a/src/models/callbacks/publisher.ts b/src/models/callbacks/publisher.ts index 23af894..f02ff9e 100644 --- a/src/models/callbacks/publisher.ts +++ b/src/models/callbacks/publisher.ts @@ -29,6 +29,13 @@ import type { Callback } from "./types.js"; * unsubscribe = publisher.subscribe("player:move", ({ x, y }) => { [...] }); * }); * ``` + * + * --- + * + * @template T + * A map containing the names of the emittable events and the + * related callback signatures that can be subscribed to them. + * Default is `Record void>`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class Publisher } = Record> @@ -88,6 +95,8 @@ export default class Publisher * * --- * + * @template K The key of the map containing the callback signature to publish. + * * @param event The name of the event to publish. * @param args The arguments to pass to the subscribers. * @@ -116,6 +125,8 @@ export default class Publisher * * --- * + * @template K The key of the map containing the callback signature to subscribe. + * * @param event The name of the event to subscribe to. * @param subscriber The subscriber to add to the event. * @@ -153,6 +164,8 @@ export default class Publisher * * --- * + * @template K The key of the map containing the callback signature to unsubscribe. + * * @param event The name of the event to unsubscribe from. * @param subscriber The subscriber to remove from the event. */ diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index b5c0501..4e9b37e 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -19,6 +19,10 @@ import type { Callback } from "./types.js"; * window.addEventListener("pointermove", onPointerMove); * window.addEventListener("pointerup", () => { onPointerMove.switch("released"); }); * ``` + * + * --- + * + * @template T The type signature of the callback. Default is `(...args: any[]) => any`. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export default class SwitchableCallback = Callback> extends CallableObject diff --git a/src/models/callbacks/types.ts b/src/models/callbacks/types.ts index b6631ec..5e95db0 100644 --- a/src/models/callbacks/types.ts +++ b/src/models/callbacks/types.ts @@ -7,5 +7,13 @@ * ```ts * const callback: Callback<[PointerEvent]> = (evt: PointerEvent): void => { [...] }; * ``` + * + * --- + * + * @template A + * The type of the arguments that the function accepts. + * It must be an array of types, even if it's empty. Default is `[]`. + * + * @template R The return type of the function. Default is `void`. */ export type Callback = (...args: A) => R; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 1702588..122aaa6 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -17,9 +17,6 @@ export default class SmartAsyncIterator implements A { protected _iterator: AsyncIterator; - public return?: (value?: R) => Promise>; - public throw?: (error?: unknown) => Promise>; - public constructor(iterable: Iterable); public constructor(iterable: AsyncIterable); public constructor(iterator: Iterator); @@ -88,9 +85,6 @@ export default class SmartAsyncIterator implements A })(); } - - if (this._iterator.return) { this.return = (value?: R) => this._iterator.return!(value); } - if (this._iterator.throw) { this.throw = (error?: unknown) => this._iterator.throw!(error); } } public async every(predicate: MaybeAsyncIteratee): Promise @@ -332,6 +326,18 @@ export default class SmartAsyncIterator implements A { return this._iterator.next(...values); } + public async return(value?: R): Promise> + { + if (this._iterator.return) { return this._iterator.return(value); } + + return { done: true, value: value as R }; + } + public throw(error: unknown): Promise> + { + if (this._iterator.throw) { return this._iterator.throw(error); } + + throw error; + } public groupBy(iteratee: MaybeAsyncIteratee): AggregatedAsyncIterator { diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 86503e2..5a8b191 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -3,16 +3,88 @@ import { ValueException } from "../exceptions/index.js"; import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorLike } from "./types.js"; +/** + * A wrapper class representing an enhanced & instantiable version + * of the native {@link Iterable} & {@link Iterator} interfaces. + * + * It provides a set of utility methods to better manipulate and + * transform iterators in a functional and highly performant way. + * It takes inspiration from the native {@link Array} methods like `map`, `filter`, `reduce`, etc... + * + * @template T The type of elements in the iterator. + * @template R The type of the final result of the iterator. Default is `void`. + * @template N The type of the argument required by the `next` method. Default is `undefined`. + */ export default class SmartIterator implements Iterator { + /** + * The native {@link Iterator} object that is wrapped by this instance. + */ protected _iterator: Iterator; - public return?: (value?: R) => IteratorResult; - public throw?: (error?: unknown) => IteratorResult; - + /** + * Initializes a new instance of the {@link SmartIterator} class. + * + * ```ts + * const iterator = new SmartIterator(["A", "B", "C"]); + * ``` + * + * --- + * + * @param iterable The iterable to wrap. + */ public constructor(iterable: Iterable); + + /** + * Initializes a new instance of the {@link SmartIterator} class. + * + * ```ts + * const iterator = new SmartIterator({ + * _sum: 0, _count: 0, + * + * next: function (value: number) + * { + * this._sum += value; + * this._count += 1; + * + * return { done: false, value: this._sum / this._count }; + * } + * }) + * ``` + * + * --- + * + * @param iterator The iterator to wrap. + */ public constructor(iterator: Iterator); + + /** + * Initializes a new instance of the {@link SmartIterator} class. + * + * ```ts + * const iterator = new SmartIterator(function* () + * { + * for (let i = 2; i < 65_536; i *= 2) { yield (i - 1); } + * }); + * ``` + * + * --- + * + * @param generatorFn The generator function to wrap. + */ public constructor(generatorFn: GeneratorFunction); + + /** + * Initializes a new instance of the {@link SmartIterator} class. + * + * ```ts + * const iterator = new SmartIterator(values); + * ``` + * + * --- + * + * @param argument The iterable, iterator or generator function to wrap. + */ public constructor(argument: IteratorLike | GeneratorFunction); public constructor(argument: IteratorLike | GeneratorFunction) { @@ -28,11 +100,27 @@ export default class SmartIterator implements Iterat { this._iterator = argument; } - - if (this._iterator.return) { this.return = (value) => this._iterator.return!(value); } - if (this._iterator.throw) { this.throw = (error) => this._iterator.throw!(error); } } + /** + * Determines whether all the elements of the iterator satisfy a condition. + * + * Also note that: + * - The iterator will be consumed entirely in the process. + * - If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * const result = iterator.every((value) => value > 0); + * + * console.log(result); // true + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * @returns `true` if all elements satisfy the condition, `false` otherwise. + */ public every(predicate: Iteratee): boolean { let index = 0; @@ -47,6 +135,26 @@ export default class SmartIterator implements Iterat index += 1; } } + + /** + * Determines whether any element of the iterator satisfies a condition. + * + * Also note that: + * - The iterator will be consumed entirely in the process. + * - If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * const result = iterator.some((value) => value > 3); + * + * console.log(result); // true + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * @returns `true` if any element satisfies the condition, `false` otherwise. + */ public some(predicate: Iteratee): boolean { let index = 0; @@ -272,6 +380,18 @@ export default class SmartIterator implements Iterat { return this._iterator.next(...values); } + public return(value?: R): IteratorResult + { + if (this._iterator.return) { return this._iterator.return(value); } + + return { done: true, value: value as R }; + } + public throw(error: unknown): IteratorResult + { + if (this._iterator.throw) { return this._iterator.throw(error); } + + throw error; + } public groupBy(iteratee: Iteratee): AggregatedIterator { diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index 054ead8..38d000c 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -114,6 +114,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * * @returns The value with the specified key or `undefined` if the key doesn't exist. @@ -129,6 +131,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return if the key doesn't exist. * @param persistent @@ -148,6 +152,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. * @param persistent @@ -174,6 +180,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * * @returns The value with the specified key or `undefined` if the key doesn't exist. @@ -189,6 +197,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return if the key doesn't exist. * @@ -205,6 +215,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. * @@ -226,6 +238,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * * @returns The value with the specified key or `undefined` if the key doesn't exist. @@ -242,6 +256,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return if the key doesn't exist. * @@ -259,6 +275,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. * @@ -279,6 +297,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * * @returns The value with the specified key or `undefined` if the key doesn't exist. @@ -294,6 +314,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return if the key doesn't exist. * @@ -310,6 +332,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to retrieve. + * * @param key The key of the value to retrieve. * @param defaultValue The default value to return (which may be `undefined`) if the key doesn't exist. * @@ -423,6 +447,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to set. + * * @param key The key of the value to set. * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. * @param persistent @@ -448,6 +474,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to set. + * * @param key The key of the value to set. * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. */ @@ -468,6 +496,8 @@ export default class JSONStorage * * --- * + * @template T The type of the value to set. + * * @param key The key of the value to set. * @param newValue The new value to set. If it's `undefined` or omitted, the key is removed instead. */ diff --git a/src/models/promises/deferred-promise.ts b/src/models/promises/deferred-promise.ts index d6aa3fd..71d8b55 100644 --- a/src/models/promises/deferred-promise.ts +++ b/src/models/promises/deferred-promise.ts @@ -3,7 +3,7 @@ import type { PromiseResolver, PromiseRejecter, FulfilledHandler, RejectedHandle import SmartPromise from "./smart-promise.js"; /** - * A class representing a promise that can be resolved or rejected from the "outside". + * A class representing a {@link SmartPromise} that can be resolved or rejected from the "outside". * The `resolve` and `reject` methods are exposed to allow the promise to be settled from another context. * * It's particularly useful in scenarios where the promise is created and needs to be awaited in one place, @@ -18,6 +18,16 @@ import SmartPromise from "./smart-promise.js"; * promise.then((result) => console.log(result)); // ["Hello,", "World!"] * promise.resolve("Hello, World!"); * ``` + * + * --- + * + * @template T The type of value the promise expects to initially be resolved with. Default is `void`. + * @template F + * The type of value returned by the `onFulfilled` callback. + * This will be the actual type of value the promise will eventually resolve to. Default is `T`. + * @template R + * The type of value possibly returned by the `onRejected` callback. + * This will be coupled with the type of value the promise will eventually resolve to, if provided. Default is `never`. */ export default class DeferredPromise extends SmartPromise { diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 96079eb..508f586 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -21,6 +21,10 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types * console.log(promise.isPending); // false * console.log(promise.isFulfilled); // true * ``` + * + * --- + * + * @template T The type of value the promise will eventually resolve to. Default is `void`. */ export default class SmartPromise implements Promise { @@ -98,7 +102,7 @@ export default class SmartPromise implements Promise } /** - * The native {@link Promise} object wrapped by this {@link SmartPromise} instance. + * The native {@link Promise} object wrapped by this instance. */ protected _promise: Promise; @@ -144,8 +148,7 @@ export default class SmartPromise implements Promise } /** - * Creates a new {@link Promise} identical to the one wrapped by - * this {@link SmartPromise} instance, with a different reference. + * Creates a new {@link Promise} identical to the one wrapped by this instance, with a different reference. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -179,6 +182,8 @@ export default class SmartPromise implements Promise * * --- * + * @template F The type of value the new promise will eventually resolve to. Default is `T`. + * * @param onFulfilled The callback to execute once the promise is fulfilled. * * @returns A new {@link Promise} resolved with the return value of the callback. @@ -210,6 +215,9 @@ export default class SmartPromise implements Promise * * --- * + * @template F The type of value the new promise will eventually resolve to. Default is `T`. + * @template R The type of value the new promise will eventually resolve to. Default is `never`. + * * @param onFulfilled The callback to execute once the promise is fulfilled. * @param onRejected The callback to execute once the promise is rejected. * @@ -225,8 +233,7 @@ export default class SmartPromise implements Promise } /** - * Creates a new {@link Promise} identical to the one wrapped by - * this {@link SmartPromise} instance, with a different reference. + * Creates a new {@link Promise} identical to the one wrapped by this instance, with a different reference. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -263,6 +270,8 @@ export default class SmartPromise implements Promise * * --- * + * @template R The type of value the new promise will eventually resolve to. Default is `T`. + * * @param onRejected The callback to execute once the promise is rejected. * * @returns A new {@link Promise} able to catch and handle the potential error. diff --git a/src/models/promises/timed-promise.ts b/src/models/promises/timed-promise.ts index 92436f9..684eb2a 100644 --- a/src/models/promises/timed-promise.ts +++ b/src/models/promises/timed-promise.ts @@ -20,6 +20,10 @@ import type { MaybePromise, PromiseExecutor } from "./types.js"; * .then((result) => console.log(result)) // "Hello, World!" * .catch((error) => console.error(error)); // TimeoutException: The operation has timed out. * ``` + * + * --- + * + * @template T The type of value the promise will eventually resolve to. Default is `void`. */ export default class TimedPromise extends SmartPromise { diff --git a/src/models/promises/types.ts b/src/models/promises/types.ts index cbf7f8d..24d3b0a 100644 --- a/src/models/promises/types.ts +++ b/src/models/promises/types.ts @@ -8,6 +8,10 @@ * return (await value).split(" "); * } * ``` + * + * --- + * + * @template T The type of the value. */ export type MaybePromise = T | PromiseLike; @@ -21,6 +25,11 @@ export type MaybePromise = T | PromiseLike; * await new Promise((resolve) => resolve("Hello, World!")) * .then(onFulfilled); * ``` + * + * --- + * + * @template T The type of value accepted by the function. Default is `void`. + * @template R The type of value returned by the function. Default is `T`. */ export type FulfilledHandler = (value: T) => MaybePromise; @@ -34,6 +43,11 @@ export type FulfilledHandler = (value: T) => MaybePromise; * await new Promise((_, reject) => reject(new Error("An error occurred."))) * .catch(onRejected); * ``` + * + * --- + * + * @template E The type of value accepted by the function. Default is `unknown`. + * @template R The type of value returned by the function. Default is `never`. */ export type RejectedHandler = (reason: E) => MaybePromise; @@ -46,6 +60,10 @@ export type RejectedHandler = (reason: E) => MaybePromis * * await new Promise((resolve) => { _resolve = resolve; }); * ``` + * + * --- + * + * @template T The type of the value accepted by the function. Default is `void`. */ export type PromiseResolver = (result: MaybePromise) => void; @@ -58,6 +76,10 @@ export type PromiseResolver = (result: MaybePromise) => void; * * await new Promise((_, reject) => { _reject = reject; }); * ``` + * + * --- + * + * @template E The type of the value accepted by the function. Default is `unknown`. */ export type PromiseRejecter = (reason?: MaybePromise) => void; @@ -73,5 +95,10 @@ export type PromiseRejecter = (reason?: MaybePromise) => void; * * await new Promise(executor); * ``` + * + * --- + * + * @template T The type of value accepted by the `resolve` function. Default is `void`. + * @template E The type of value accepted by the `reject` function. Default is `unknown`. */ export type PromiseExecutor = (resolve: PromiseResolver, reject: PromiseRejecter) => void; diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index 90c0542..fccc91d 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -12,6 +12,8 @@ import { SmartIterator } from "../models/index.js"; * * --- * + * @template T The type of elements in the iterables. + * * @param iterables The list of iterables to chain. * * @returns A {@link SmartIterator} object that chains the iterables into a single one. @@ -40,6 +42,8 @@ export function chain(...iterables: Iterable[]): SmartIterator * * --- * + * @template T The type of elements in the iterable. + * * @param elements The iterable to count. * * @returns The number of elements in the iterable. @@ -66,6 +70,8 @@ export function count(elements: Iterable): number * * --- * + * @template T The type of elements in the iterable. + * * @param elements The iterable to enumerate. * * @returns A {@link SmartIterator} object that enumerates the elements of the given iterable. @@ -172,6 +178,8 @@ export function range(start: number, end?: number, step = 1): SmartIterator(iterable: Iterable): T[] * * --- * + * @template T The type of elements in the iterable. + * * @param elements The iterable to filter. * * @returns A {@link SmartIterator} object that iterates over the unique elements of the given iterable. @@ -238,6 +248,9 @@ export function unique(elements: Iterable): SmartIterator * * --- * + * @template T The type of elements in the first iterable. + * @template U The type of elements in the second iterable. + * * @param first The first iterable to zip. * @param second The second iterable to zip. * diff --git a/src/utils/math.ts b/src/utils/math.ts index b301385..277f482 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -12,6 +12,8 @@ import { zip } from "./iterator.js"; * * --- * + * @template T The type of the values in the list. It must be or extend a `number` object. + * * @param values * The list of values to compute the average. * @@ -107,6 +109,8 @@ export function hash(value: string): number * * --- * + * @template T The type of the values in the list. It must be or extend a `number` object. + * * @param values The list of values to sum. * * @returns The sum of the specified values. diff --git a/src/utils/random.ts b/src/utils/random.ts index 6722dfb..2da829b 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -124,6 +124,8 @@ export default class Random /** * Picks a random valid index from a given array of elements. * + * @template T The type of the elements in the array. + * * @param elements * The array of elements to pick from. * @@ -141,6 +143,8 @@ export default class Random /** * Picks a random element from a given array of elements. * + * @template T The type of the elements in the array. + * * @param elements * The array of elements to pick from. * From d614fe1a28c87af702cfab9e028260e8cd897965 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 31 Dec 2024 20:01:24 +0100 Subject: [PATCH 15/32] add: Almost completed JSDoc for `models/iterators` files. --- src/models/callbacks/switchable-callback.ts | 5 +- src/models/game-loop.ts | 8 +- src/models/iterators/smart-async-iterator.ts | 680 +++++++++++++++++++ src/models/iterators/smart-iterator.ts | 543 ++++++++++++++- src/models/promises/deferred-promise.ts | 4 +- src/models/promises/smart-promise.ts | 8 +- src/models/timers/countdown.ts | 7 +- src/utils/iterator.ts | 17 +- 8 files changed, 1239 insertions(+), 33 deletions(-) diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index 4e9b37e..d9ed151 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -44,7 +44,8 @@ export default class SwitchableCallback = Callbac * A flag indicating whether the callback is enabled or not. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link isEnabled} getter instead. + * If you're looking for the public & readonly property, use + * the {@link SwitchableCallback.isEnabled} getter instead. */ protected _isEnabled: boolean; @@ -60,7 +61,7 @@ export default class SwitchableCallback = Callbac * The key that is associated with the currently selected implementation. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link key} getter instead. + * If you're looking for the public & readonly property, use the {@link SwitchableCallback.key} getter instead. */ protected _key: string; diff --git a/src/models/game-loop.ts b/src/models/game-loop.ts index 842357b..b7e8bac 100644 --- a/src/models/game-loop.ts +++ b/src/models/game-loop.ts @@ -43,7 +43,7 @@ export default class GameLoop { /** * The handle of the interval or the animation frame, depending on the environment. - * It's used to stop the game loop when the {@link _stop} method is called. + * It's used to stop the game loop when the {@link GameLoop._stop} method is called. */ protected _handle?: number | Interval; @@ -53,7 +53,7 @@ export default class GameLoop * of the start of the game loop, it's also used to calculate the elapsed time. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link startTime} getter instead. + * If you're looking for the public & readonly property, use the {@link GameLoop.startTime} getter instead. */ protected _startTime: number; @@ -71,7 +71,7 @@ export default class GameLoop * A flag indicating whether the game loop is currently running or not. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link isRunning} getter instead. + * If you're looking for the public & readonly property, use the {@link GameLoop.isRunning} getter instead. */ protected _isRunning: boolean; @@ -85,7 +85,7 @@ export default class GameLoop /** * The elapsed time since the start of the game loop. - * It's calculated as the difference between the current time and the {@link startTime}. + * It's calculated as the difference between the current time and the {@link GameLoop.startTime}. */ public get elapsedTime(): number { diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 122aaa6..9aecf39 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -13,16 +13,141 @@ import type { } from "./types.js"; +/** + * A wrapper class representing an enhanced & instantiable version + * of the native {@link AsyncIterable} & {@link AsyncIterator} interfaces. + * + * It provides a set of utility methods to better manipulate and transform + * asynchronous iterators in a functional and highly performant way. + * It takes inspiration from the native {@link Array} methods like + * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc... + * + * @template T The type of elements in the iterator. + * @template R The type of the final result of the iterator. Default is `void`. + * @template N The type of the argument passed to the `next` method. Default is `undefined`. + */ export default class SmartAsyncIterator implements AsyncIterator { + /** + * The native {@link AsyncIterator} object that is being wrapped by this instance. + */ protected _iterator: AsyncIterator; + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator(["A", "B", "C"]); + * ``` + * + * --- + * + * @param iterable The iterable object to wrap. + */ public constructor(iterable: Iterable); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); + * ``` + * + * --- + * + * @param iterable The asynchronous iterable object to wrap. + */ public constructor(iterable: AsyncIterable); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator({ + * _sum: 0, _count: 0, + * + * next: function (value: number) + * { + * this._sum += value; + * this._count += 1; + * + * return { done: false, value: this._sum / this._count }; + * } + * }) + * ``` + * + * --- + * + * @param iterator The iterator object to wrap. + */ public constructor(iterator: Iterator); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator({ + * _sum: 0, _count: 0, + * + * next: async function (value: number) + * { + * this._sum += value; + * this._count += 1; + * + * return { done: false, value: this._sum / this._count }; + * } + * }) + * ``` + * + * --- + * + * @param iterator The asynchronous iterator object to wrap. + */ public constructor(iterator: AsyncIterator); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator(function* () + * { + * for (let i = 2; i < 65_536; i *= 2) { yield (i - 1); } + * }); + * ``` + * + * --- + * + * @param generatorFn The generator function to wrap. + */ public constructor(generatorFn: GeneratorFunction); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator(async function* () + * { + * for await (let i = 2; i < 65_536; i *= 2) { yield (i - 1); } + * }); + * ``` + * + * --- + * + * @param generatorFn The asynchronous generator function to wrap. + */ public constructor(generatorFn: AsyncGeneratorFunction); + + /** + * Initializes a new instance of the {@link SmartAsyncIterator} class. + * + * ```ts + * const iterator = new SmartAsyncIterator(values); + * ``` + * + * --- + * + * @param argument The synchronous or asynchronous iterable, iterator or generator function to wrap. + */ public constructor(argument: MaybeAsyncIteratorLike | MaybeAsyncGeneratorFunction); public constructor(argument: MaybeAsyncIteratorLike | MaybeAsyncGeneratorFunction) { @@ -87,6 +212,32 @@ export default class SmartAsyncIterator implements A } } + /** + * Determines whether all elements of the iterator satisfy a given condition. + * See also {@link SmartAsyncIterator.some}. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element doesn't satisfy the condition, the method will return `false` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link SmartAsyncIterator.find} instead. + * + * If the iterator is infinite and every element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = await iterator.every(async (value) => value < 0); + * + * console.log(result); // false + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A promise that will resolve to `true` if all elements satisfy the condition, `false` otherwise. + */ public async every(predicate: MaybeAsyncIteratee): Promise { let index = 0; @@ -101,6 +252,33 @@ export default class SmartAsyncIterator implements A index += 1; } } + + /** + * Determines whether any element of the iterator satisfies a given condition. + * See also {@link SmartAsyncIterator.every}. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element satisfies the condition, the method will return `true` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link SmartAsyncIterator.find} instead. + * + * If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = await iterator.some(async (value) => value > 0); + * + * console.log(result); // true + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A promise that will resolve to `true` if any element satisfies the condition, `false` otherwise. + */ public async some(predicate: MaybeAsyncIteratee): Promise { let index = 0; @@ -116,7 +294,58 @@ export default class SmartAsyncIterator implements A } } + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = iterator.filter(async (value) => value < 0); + * + * console.log(await result.toArray()); // [-2, -1] + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator; + + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); + * const result = iterator.filter(async (value) => typeof value === "number"); + * + * console.log(await result.toArray()); // [-2, 1] + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: MaybeAsyncTypeGuardIteratee): SmartAsyncIterator; public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator { @@ -137,6 +366,31 @@ export default class SmartAsyncIterator implements A } }); } + + /** + * Maps the elements of the iterator using a given transformation function. + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = iterator.map(async (value) => Math.abs(value)); + * + * console.log(await result.toArray()); // [2, 1, 0, 1, 2] + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link SmartAsyncIterator} containing the transformed elements. + */ public map(iteratee: MaybeAsyncIteratee): SmartAsyncIterator { const iterator = this._iterator; @@ -156,7 +410,64 @@ export default class SmartAsyncIterator implements A } }); } + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the first element of the iterator. + * The last accumulator value will be the final result of the reduction. + * + * Also note that: + * - If an empty iterator is provided, a {@link ValueException} will be thrown. + * - If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); + * const result = await iterator.reduce(async (acc, value) => acc + value); + * + * console.log(result); // 15 + * ``` + * + * --- + * + * @param reducer The reducer function to apply to each element of the iterator. + * + * @returns A promise that will resolve to the final result of the reduction. + */ public async reduce(reducer: MaybeAsyncReducer): Promise; + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the initial value provided. + * The last accumulator value will be the final result of the reduction. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); + * const result = await iterator.reduce(async (acc, value) => acc + value, 10); + * + * console.log(result); // 25 + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the type of the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The initial value of the accumulator. + * + * @returns A promise that will resolve to the final result of the reduction. + */ public async reduce(reducer: MaybeAsyncReducer, initialValue: A): Promise; public async reduce(reducer: MaybeAsyncReducer, initialValue?: A): Promise { @@ -182,6 +493,30 @@ export default class SmartAsyncIterator implements A } } + /** + * Flattens the elements of the iterator using a given transformation function. + * Since the iterator is lazy, the flattening process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); + * const result = iterator.flatMap(async (value) => value); + * + * console.log(await result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5] + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link SmartAsyncIterator} containing the flattened elements. + */ public flatMap(iteratee: MaybeAsyncIteratee>): SmartAsyncIterator { const iterator = this._iterator; @@ -207,6 +542,33 @@ export default class SmartAsyncIterator implements A }); } + /** + * Drops a given number of elements at the beginning of the iterator. + * The remaining elements will be returned in a new iterator. + * + * Since the iterator is lazy, the dropping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * Only the dropped elements will be consumed in the process. + * The rest of the iterator will be consumed only once the new one is. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = iterator.drop(3); + * + * console.log(await result.toArray()); // [1, 2] + * ``` + * + * --- + * + * @param count The number of elements to drop. + * + * @returns A new {@link SmartAsyncIterator} containing the remaining elements. + */ public drop(count: number): SmartAsyncIterator { const iterator = this._iterator; @@ -232,6 +594,35 @@ export default class SmartAsyncIterator implements A } }); } + + /** + * Takes a given number of elements at the beginning of the iterator. + * These elements will be returned in a new iterator. + * + * Since the iterator is lazy, the taking process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * Only the taken elements will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = iterator.take(3); + * + * console.log(await result.toArray()); // [-2, -1, 0] + * console.log(await iterator.toArray()); // [1, 2] + * ``` + * + * --- + * + * @param limit The number of elements to take. + * + * @returns A new {@link SmartAsyncIterator} containing the taken elements. + */ public take(limit: number): SmartAsyncIterator { const iterator = this._iterator; @@ -254,6 +645,69 @@ export default class SmartAsyncIterator implements A }); } + /** + * Finds the first element of the iterator that satisfies a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * The first element that satisfies the condition will be returned immediately. + * + * Only the elements that are necessary to find the first + * satisfying one will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * Also note that: + * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. + * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); + * const result = await iterator.find(async (value) => value > 0); + * + * console.log(result); // 1 + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. + */ + public async find(predicate: MaybeAsyncIteratee): Promise; + + /** + * Finds the first element of the iterator that satisfies a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * The first element that satisfies the condition will be returned immediately. + * + * Only the elements that are necessary to find the first + * satisfying one will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * Also note that: + * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. + * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); + * const result = await iterator.find(async (value) => typeof value === "number"); + * + * console.log(result); // -2 + * ``` + * + * --- + * + * @template S + * The type of the element that satisfies the condition. + * This allows the type-system to infer the correct type of the result. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. + */ + public async find(predicate: MaybeAsyncTypeGuardIteratee): Promise; public async find(predicate: MaybeAsyncIteratee): Promise { let index = 0; @@ -269,10 +723,58 @@ export default class SmartAsyncIterator implements A } } + /** + * Enumerates the elements of the iterator. + * Each element is be paired with its index in a new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator(["A", "M", "N", "Z"]); + * const result = iterator.enumerate(); + * + * for await (const [index, value] of result) + * { + * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" + * } + * ``` + * + * --- + * + * @returns A new {@link SmartAsyncIterator} containing the enumerated elements. + */ public enumerate(): SmartAsyncIterator<[number, T], R> { return this.map((value, index) => [index, value]); } + + /** + * Removes all duplicate elements from the iterator. + * The first occurrence of each element will be kept. + * + * Since the iterator is lazy, the deduplication process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]); + * const result = iterator.unique(); + * + * console.log(await result.toArray()); // [1, 2, 3, 4, 5] + * ``` + * + * --- + * + * @returns A new {@link SmartAsyncIterator} containing only the unique elements. + */ public unique(): SmartAsyncIterator { const iterator = this._iterator; @@ -295,6 +797,23 @@ export default class SmartAsyncIterator implements A }); } + /** + * Counts the number of elements in the iterator. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); + * const result = await iterator.count(); + * + * console.log(result); // 5 + * ``` + * + * --- + * + * @returns A promise that will resolve to the number of elements in the iterator. + */ public async count(): Promise { let index = 0; @@ -307,6 +826,27 @@ export default class SmartAsyncIterator implements A index += 1; } } + + /** + * Iterates over all elements of the iterator applying a given function. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator(["A", "M", "N", "Z"]); + * await iterator.forEach(async (value, index) => + * { + * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" + * } + * ``` + * + * --- + * + * @param iteratee The function to apply to each element of the iterator. + * + * @returns A promise that will resolve once the iteration is complete. + */ public async forEach(iteratee: MaybeAsyncIteratee): Promise { let index = 0; @@ -322,16 +862,111 @@ export default class SmartAsyncIterator implements A } } + /** + * Advances the iterator to the next element and returns the result. + * If the iterator requires it, a value must be provided to be passed to the next element. + * + * Once the iterator is done, the method will return an object with the `done` property set to `true`. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); + * + * let result = await iterator.next(); + * while (!result.done) + * { + * console.log(result.value); // 1, 2, 3, 4, 5 + * + * result = await iterator.next(); + * } + * + * console.log(result); // { done: true, value: undefined } + * ``` + * + * --- + * + * @param values The value to pass to the next element, if required. + * + * @returns A promise that will resolve to the result of the iteration, containing the value of the operation. + */ public next(...values: N extends undefined ? [] : [N]): Promise> { return this._iterator.next(...values); } + + /** + * An utility method that may be used to close the iterator gracefully, + * free the resources and perform any cleanup operation. + * It may also be used to signal the end or to compute a specific final result of the iteration process. + * + * ```ts + * const iterator = new SmartAsyncIterator({ + * _index: 0, + * next: async function() + * { + * return { done: false, value: this._index += 1 }; + * }, + * return: async function() { console.log("Closing the iterator..."); } + * }); + * + * for await (const value of iterator) + * { + * if (value > 5) { break; } // Closing the iterator... + * + * console.log(value); // 1, 2, 3, 4, 5 + * } + * ``` + * + * --- + * + * @param value The final value of the iterator. + * + * @returns A promise that will resolve to the final result of the iterator. + */ public async return(value?: R): Promise> { if (this._iterator.return) { return this._iterator.return(value); } return { done: true, value: value as R }; } + + /** + * An utility method that may be used to close the iterator due to an error, + * free the resources and perform any cleanup operation. + * It may also be used to signal that an error occurred during the iteration process or to handle it. + * + * ```ts + * const iterator = new SmartAsyncIterator({ + * _index: 0, + * next: async function() + * { + * return { done: this._index > 10, value: this._index += 1 }; + * }, + * throw: async function(error) + * { + * console.warn(error.message); + * + * this._index = 0; + * } + * }); + * + * for await (const value of iterator) // 1, 2, 3, 4, 5, "The index is too high.", 1, 2, 3, 4, 5, ... + * { + * try + * { + * if (value > 5) { throw new Error("The index is too high."); } + * + * console.log(value); // 1, 2, 3, 4, 5 + * } + * catch (error) { await iterator.throw(error); } + * } + * ``` + * + * --- + * + * @param error The error to throw into the iterator. + * + * @returns A promise that will resolve to the final result of the iterator. + */ public throw(error: unknown): Promise> { if (this._iterator.throw) { return this._iterator.throw(error); } @@ -339,6 +974,33 @@ export default class SmartAsyncIterator implements A throw error; } + /** + * An utility method that aggregates the elements of the iterator using a given key function. + * The elements will be grouped by the resulting keys in a new specialized iterator. + * See {@link AggregatedAsyncIterator}. + * + * Since the iterator is lazy, the grouping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * the new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + * const result = iterator.groupBy(async (value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(await result.toObject()); // { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8, 10] } + * ``` + * + * --- + * + * @template K The type of the keys used to group the elements. + * + * @param iteratee The key function to apply to each element of the iterator. + * + * @returns A new instance of the {@link AggregatedAsyncIterator} class containing the grouped elements. + */ public groupBy(iteratee: MaybeAsyncIteratee): AggregatedAsyncIterator { return new AggregatedAsyncIterator(this.map(async (element, index) => @@ -349,6 +1011,24 @@ export default class SmartAsyncIterator implements A })); } + /** + * Materializes the iterator into an array. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator(async function* () + * { + * for (let i = 0; i < 5; i += 1) { yield i; } + * }); + * const result = await iterator.toArray(); + * + * console.log(result); // [0, 1, 2, 3, 4] + * ``` + * + * @returns A promise that will resolve to an array containing all elements of the iterator. + */ public toArray(): Promise { return Array.fromAsync(this as AsyncIterable); diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 5a8b191..51143e7 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -9,7 +9,8 @@ import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorL * * It provides a set of utility methods to better manipulate and * transform iterators in a functional and highly performant way. - * It takes inspiration from the native {@link Array} methods like `map`, `filter`, `reduce`, etc... + * It takes inspiration from the native {@link Array} methods like + * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc... * * @template T The type of elements in the iterator. * @template R The type of the final result of the iterator. Default is `void`. @@ -18,7 +19,7 @@ import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorL export default class SmartIterator implements Iterator { /** - * The native {@link Iterator} object that is wrapped by this instance. + * The native {@link Iterator} object that is being wrapped by this instance. */ protected _iterator: Iterator; @@ -31,7 +32,7 @@ export default class SmartIterator implements Iterat * * --- * - * @param iterable The iterable to wrap. + * @param iterable The iterable object to wrap. */ public constructor(iterable: Iterable); @@ -54,7 +55,7 @@ export default class SmartIterator implements Iterat * * --- * - * @param iterator The iterator to wrap. + * @param iterator The iterator object to wrap. */ public constructor(iterator: Iterator); @@ -103,22 +104,28 @@ export default class SmartIterator implements Iterat } /** - * Determines whether all the elements of the iterator satisfy a condition. + * Determines whether all elements of the iterator satisfy a given condition. See also {@link SmartIterator.some}. * - * Also note that: - * - The iterator will be consumed entirely in the process. - * - If the iterator is infinite, the function will never return. + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element doesn't satisfy the condition, the method will return `false` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link SmartIterator.find} instead. + * + * If the iterator is infinite and every element satisfies the condition, the function will never return. * * ```ts - * const iterator = new SmartIterator([1, 2, 3, 4, 5]); - * const result = iterator.every((value) => value > 0); + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.every((value) => value < 0); * - * console.log(result); // true + * console.log(result); // false * ``` * * --- * * @param predicate The condition to check for each element of the iterator. + * * @returns `true` if all elements satisfy the condition, `false` otherwise. */ public every(predicate: Iteratee): boolean @@ -137,15 +144,20 @@ export default class SmartIterator implements Iterat } /** - * Determines whether any element of the iterator satisfies a condition. + * Determines whether any element of the iterator satisfies a given condition. See also {@link SmartIterator.every}. * - * Also note that: - * - The iterator will be consumed entirely in the process. - * - If the iterator is infinite, the function will never return. + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element satisfies the condition, the method will return `true` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link SmartIterator.find} instead. + * + * If the iterator is infinite and no element satisfies the condition, the function will never return. * * ```ts - * const iterator = new SmartIterator([1, 2, 3, 4, 5]); - * const result = iterator.some((value) => value > 3); + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.some((value) => value < 0); * * console.log(result); // true * ``` @@ -153,6 +165,7 @@ export default class SmartIterator implements Iterat * --- * * @param predicate The condition to check for each element of the iterator. + * * @returns `true` if any element satisfies the condition, `false` otherwise. */ public some(predicate: Iteratee): boolean @@ -170,7 +183,58 @@ export default class SmartIterator implements Iterat } } + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.filter((value) => value < 0); + * + * console.log(result.toArray()); // [-2, -1] + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: Iteratee): SmartIterator; + + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([-2, "-1", "0", 1, "2"]); + * const result = iterator.filter((value) => typeof value === "number"); + * + * console.log(result.toArray()); // [-2, 1] + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: TypeGuardIteratee): SmartIterator; public filter(predicate: Iteratee): SmartIterator { @@ -191,6 +255,31 @@ export default class SmartIterator implements Iterat } }); } + + /** + * Maps the elements of the iterator using a given transformation function. + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.map((value) => Math.abs(value)); + * + * console.log(result.toArray()); // [2, 1, 0, 1, 2] + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link SmartIterator} containing the transformed elements. + */ public map(iteratee: Iteratee): SmartIterator { const iterator = this._iterator; @@ -210,7 +299,64 @@ export default class SmartIterator implements Iterat } }); } + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the first element of the iterator. + * The last accumulator value will be the final result of the reduction. + * + * Also note that: + * - If an empty iterator is provided, a {@link ValueException} will be thrown. + * - If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * const result = iterator.reduce((acc, value) => acc + value); + * + * console.log(result); // 15 + * ``` + * + * --- + * + * @param reducer The reducer function to apply to each element of the iterator. + * + * @returns The final result of the reduction. + */ public reduce(reducer: Reducer): T; + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the initial value provided. + * The last accumulator value will be the final result of the reduction. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * const result = iterator.reduce((acc, value) => acc + value, 10); + * + * console.log(result); // 25 + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the type of the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The initial value of the accumulator. + * + * @returns The final result of the reduction. + */ public reduce(reducer: Reducer, initialValue: A): A; public reduce(reducer: Reducer, initialValue?: A): A { @@ -236,6 +382,30 @@ export default class SmartIterator implements Iterat } } + /** + * Flattens the elements of the iterator using a given transformation function. + * Since the iterator is lazy, the flattening process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); + * const result = iterator.flatMap((value) => value); + * + * console.log(result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5] + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link SmartIterator} containing the flattened elements. + */ public flatMap(iteratee: Iteratee>): SmartIterator { const iterator = this._iterator; @@ -260,6 +430,33 @@ export default class SmartIterator implements Iterat }); } + /** + * Drops a given number of elements at the beginning of the iterator. + * The remaining elements will be returned in a new iterator. + * + * Since the iterator is lazy, the dropping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * Only the dropped elements will be consumed in the process. + * The rest of the iterator will be consumed only once the new one is. + * + * ```ts + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.drop(3); + * + * console.log(result.toArray()); // [1, 2] + * ``` + * + * --- + * + * @param count The number of elements to drop. + * + * @returns A new {@link SmartIterator} containing the remaining elements. + */ public drop(count: number): SmartIterator { const iterator = this._iterator; @@ -284,6 +481,35 @@ export default class SmartIterator implements Iterat } }); } + + /** + * Takes a given number of elements at the beginning of the iterator. + * These elements will be returned in a new iterator. + * + * Since the iterator is lazy, the taking process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * Only the taken elements will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * ```ts + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.take(3); + * + * console.log(result.toArray()); // [-2, -1, 0] + * console.log(iterator.toArray()); // [1, 2] + * ``` + * + * --- + * + * @param limit The number of elements to take. + * + * @returns A new {@link SmartIterator} containing the taken elements. + */ public take(limit: number): SmartIterator { const iterator = this._iterator; @@ -305,7 +531,68 @@ export default class SmartIterator implements Iterat }); } + /** + * Finds the first element of the iterator that satisfies a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * The first element that satisfies the condition will be returned immediately. + * + * Only the elements that are necessary to find the first + * satisfying one will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * Also note that: + * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. + * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); + * const result = iterator.find((value) => value > 0); + * + * console.log(result); // 1 + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns The first element that satisfies the condition, `undefined` otherwise. + */ public find(predicate: Iteratee): T | undefined; + + /** + * Finds the first element of the iterator that satisfies a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * The first element that satisfies the condition will be returned immediately. + * + * Only the elements that are necessary to find the first + * satisfying one will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * Also note that: + * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. + * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([-2, "-1", "0", 1, "2"]); + * const result = iterator.find((value) => typeof value === "number"); + * + * console.log(result); // -2 + * ``` + * + * --- + * + * @template S + * The type of the element that satisfies the condition. + * This allows the type-system to infer the correct type of the result. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns The first element that satisfies the condition, `undefined` otherwise. + */ public find(predicate: TypeGuardIteratee): S | undefined; public find(predicate: Iteratee): T | undefined { @@ -322,10 +609,58 @@ export default class SmartIterator implements Iterat } } + /** + * Enumerates the elements of the iterator. + * Each element is be paired with its index in a new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator(["A", "M", "N", "Z"]); + * const result = iterator.enumerate(); + * + * for (const [index, value] of result) + * { + * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" + * } + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing the enumerated elements. + */ public enumerate(): SmartIterator<[number, T], R> { return this.map((value, index) => [index, value]); } + + /** + * Removes all duplicate elements from the iterator. + * The first occurrence of each element will be kept. + * + * Since the iterator is lazy, the deduplication process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]); + * const result = iterator.unique(); + * + * console.log(result.toArray()); // [1, 2, 3, 4, 5] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing only the unique elements. + */ public unique(): SmartIterator { const iterator = this._iterator; @@ -348,6 +683,23 @@ export default class SmartIterator implements Iterat }); } + /** + * Counts the number of elements in the iterator. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * const result = iterator.count(); + * + * console.log(result); // 5 + * ``` + * + * --- + * + * @returns The number of elements in the iterator. + */ public count(): number { let index = 0; @@ -361,6 +713,24 @@ export default class SmartIterator implements Iterat } } + /** + * Iterates over all elements of the iterator applying a given function. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator(["A", "M", "N", "Z"]); + * iterator.forEach((value, index) => + * { + * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" + * } + * ``` + * + * --- + * + * @param iteratee The function to apply to each element of the iterator. + */ public forEach(iteratee: Iteratee): void { let index = 0; @@ -376,16 +746,111 @@ export default class SmartIterator implements Iterat } } + /** + * Advances the iterator to the next element and returns the result. + * If the iterator requires it, a value must be provided to be passed to the next element. + * + * Once the iterator is done, the method will return an object with the `done` property set to `true`. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5]); + * + * let result = iterator.next(); + * while (!result.done) + * { + * console.log(result.value); // 1, 2, 3, 4, 5 + * + * result = iterator.next(); + * } + * + * console.log(result); // { done: true, value: undefined } + * ``` + * + * --- + * + * @param values The value to pass to the next element, if required. + * + * @returns The result of the iteration, containing the value of the operation. + */ public next(...values: N extends undefined ? [] : [N]): IteratorResult { return this._iterator.next(...values); } + + /** + * An utility method that may be used to close the iterator gracefully, + * free the resources and perform any cleanup operation. + * It may also be used to signal the end or to compute a specific final result of the iteration process. + * + * ```ts + * const iterator = new SmartIterator({ + * _index: 0, + * next: function() + * { + * return { done: false, value: this._index += 1 }; + * }, + * return: function() { console.log("Closing the iterator..."); } + * }); + * + * for (const value of iterator) + * { + * if (value > 5) { break; } // Closing the iterator... + * + * console.log(value); // 1, 2, 3, 4, 5 + * } + * ``` + * + * --- + * + * @param value The final value of the iterator. + * + * @returns The result of the iterator. + */ public return(value?: R): IteratorResult { if (this._iterator.return) { return this._iterator.return(value); } return { done: true, value: value as R }; } + + /** + * An utility method that may be used to close the iterator due to an error, + * free the resources and perform any cleanup operation. + * It may also be used to signal that an error occurred during the iteration process or to handle it. + * + * ```ts + * const iterator = new SmartIterator({ + * _index: 0, + * next: function() + * { + * return { done: this._index > 10, value: this._index += 1 }; + * }, + * throw: function(error) + * { + * console.warn(error.message); + * + * this._index = 0; + * } + * }); + * + * for (const value of iterator) // 1, 2, 3, 4, 5, "The index is too high.", 1, 2, 3, 4, 5, ... + * { + * try + * { + * if (value > 5) { throw new Error("The index is too high."); } + * + * console.log(value); // 1, 2, 3, 4, 5 + * } + * catch (error) { iterator.throw(error); } + * } + * ``` + * + * --- + * + * @param error The error to throw into the iterator. + * + * @returns The final result of the iterator. + */ public throw(error: unknown): IteratorResult { if (this._iterator.throw) { return this._iterator.throw(error); } @@ -393,6 +858,32 @@ export default class SmartIterator implements Iterat throw error; } + /** + * An utility method that aggregates the elements of the iterator using a given key function. + * The elements will be grouped by the resulting keys in a new specialized iterator. See {@link AggregatedIterator}. + * + * Since the iterator is lazy, the grouping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * the new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartIterator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + * const result = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(result.toObject()); // { odd: [1, 3, 5, 7, 9], even: [2, 4, 6, 8, 10] } + * ``` + * + * --- + * + * @template K The type of the keys used to group the elements. + * + * @param iteratee The key function to apply to each element of the iterator. + * + * @returns A new instance of the {@link AggregatedIterator} class containing the grouped elements. + */ public groupBy(iteratee: Iteratee): AggregatedIterator { return new AggregatedIterator(this.map((element, index) => @@ -403,6 +894,24 @@ export default class SmartIterator implements Iterat })); } + /** + * Materializes the iterator into an array. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the function will never return. + * + * ```ts + * const iterator = new SmartIterator(function* () + * { + * for (let i = 0; i < 5; i += 1) { yield i; } + * }); + * const result = iterator.toArray(); + * + * console.log(result); // [0, 1, 2, 3, 4] + * ``` + * + * @returns The array containing all elements of the iterator. + */ public toArray(): T[] { return Array.from(this as Iterable); diff --git a/src/models/promises/deferred-promise.ts b/src/models/promises/deferred-promise.ts index 71d8b55..23b78cc 100644 --- a/src/models/promises/deferred-promise.ts +++ b/src/models/promises/deferred-promise.ts @@ -35,7 +35,7 @@ export default class DeferredPromise extends SmartPr * The exposed function that allows to resolve the promise. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link resolve} getter instead. + * If you're looking for the public & readonly property, use the {@link DeferredPromise.resolve} getter instead. */ protected _resolve: PromiseResolver; @@ -48,7 +48,7 @@ export default class DeferredPromise extends SmartPr * The exposed function that allows to reject the promise. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link reject} getter instead. + * If you're looking for the public & readonly property, use the {@link DeferredPromise.reject} getter instead. */ protected _reject: PromiseRejecter; diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 508f586..37918bc 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -5,7 +5,7 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types * * It provides additional properties to check the state of the promise itself. * The state can be either `pending`, `fulfilled` or `rejected` and is accessible through - * the {@link isPending}, {@link isFulfilled} and {@link isRejected} properties. + * the {@link SmartPromise.isPending}, {@link SmartPromise.isFulfilled} and {@link SmartPromise.isRejected} properties. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -57,7 +57,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise is still pending or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link isPending} getter instead. + * If you're looking for the public & readonly property, use the {@link SmartPromise.isPending} getter instead. */ protected _isPending: boolean; @@ -73,7 +73,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise has been fulfilled or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link isFulfilled} getter instead. + * If you're looking for the public & readonly property, use the {@link SmartPromise.isFulfilled} getter instead. */ protected _isFulfilled: boolean; @@ -89,7 +89,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise has been rejected or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link isRejected} getter instead. + * If you're looking for the public & readonly property, use the {@link SmartPromise.isRejected} getter instead. */ protected _isRejected: boolean; diff --git a/src/models/timers/countdown.ts b/src/models/timers/countdown.ts index b53efd4..bdcf482 100644 --- a/src/models/timers/countdown.ts +++ b/src/models/timers/countdown.ts @@ -45,7 +45,7 @@ export default class Countdown extends GameLoop * The total duration of the countdown in milliseconds. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link duration} getter instead. + * If you're looking for the public & readonly property, use the {@link Countdown.duration} getter instead. */ protected _duration: number; @@ -113,7 +113,7 @@ export default class Countdown extends GameLoop /** * The internal method actually responsible for stopping the - * countdown and resolving or rejecting the {@link _deferrer} promise. + * countdown and resolving or rejecting the {@link Countdown._deferrer} promise. * * @param reason * The reason why the countdown has stopped. @@ -149,7 +149,8 @@ export default class Countdown extends GameLoop * --- * * @param remainingTime - * The remaining time to set as default when the countdown starts. Default is the {@link duration} itself. + * The remaining time to set as default when the countdown starts. + * Default is the {@link Countdown.duration} itself. * * @returns A {@link SmartPromise} that will be resolved or rejected when the countdown expires or stops. */ diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index fccc91d..c8f16cd 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -3,6 +3,13 @@ import { SmartIterator } from "../models/index.js"; /** * An utility function that chains multiple iterables into a single one. * + * Since the iterator is lazy, the chaining process will be + * executed only once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * * ```ts * for (const value of chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) * { @@ -59,7 +66,15 @@ export function count(elements: Iterable): number } /** - * An utility function that enumerates the elements of an iterable. + * An utility function that enumerates the elements of an iterable. + * Each element is paired with its index in a new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. * * ```ts * for (const [index, value] of enumerate(["A", "M", "N", "Z"])) From 80ee774c50613e267b21ad9613515a40083f9381 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 4 Jan 2025 14:18:11 +0100 Subject: [PATCH 16/32] wip: Added some minor docs. + Commented out async type-guards. They're not working. --- src/index.ts | 10 +- .../aggregators/aggregated-async-iterator.ts | 6 +- src/models/aggregators/aggregated-iterator.ts | 6 +- src/models/aggregators/reduced-iterator.ts | 4 +- src/models/aggregators/types.ts | 26 ++- src/models/iterators/smart-async-iterator.ts | 68 +----- src/models/iterators/smart-iterator.ts | 6 +- src/models/iterators/types.ts | 194 +++++++++++++++++- src/models/types.ts | 10 +- 9 files changed, 224 insertions(+), 106 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9c9bccd..2739fea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,11 @@ export { export type { AsyncGeneratorFunction, + AsyncIteratee, AsyncIteratorLike, + AsyncKeyedIteratee, + AsyncKeyedReducer, + AsyncReducer, Callback, FulfilledHandler, GeneratorFunction, @@ -51,22 +55,20 @@ export type { JSONValue, KeyedIteratee, KeyedReducer, - KeyedTypeGuardIteratee, + KeyedTypeGuardPredicate, MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer, - MaybeAsyncKeyedTypeGuardIteratee, MaybeAsyncGeneratorFunction, MaybeAsyncIteratee, MaybeAsyncIteratorLike, MaybeAsyncReducer, - MaybeAsyncTypeGuardIteratee, MaybePromise, PromiseExecutor, PromiseRejecter, PromiseResolver, Reducer, RejectedHandler, - TypeGuardIteratee + TypeGuardPredicate } from "./models/types.js"; diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index b6ae933..89e749c 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -9,7 +9,7 @@ import type { import type { MaybePromise } from "../types.js"; import ReducedIterator from "./reduced-iterator.js"; -import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedTypeGuardIteratee, MaybeAsyncKeyedReducer } from "./types.js"; +import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js"; export default class AggregatedAsyncIterator { @@ -65,7 +65,6 @@ export default class AggregatedAsyncIterator } public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator; - public filter(predicate: MaybeAsyncKeyedTypeGuardIteratee): AggregatedAsyncIterator; public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator { const elements = this._elements; @@ -200,9 +199,6 @@ export default class AggregatedAsyncIterator } public async find(predicate: MaybeAsyncKeyedIteratee): Promise>; - public async find(predicate: MaybeAsyncKeyedTypeGuardIteratee) - : Promise>; - public async find(predicate: MaybeAsyncKeyedIteratee): Promise> { const values = new Map(); diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index bb5fed1..348c8ee 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -2,7 +2,7 @@ import { SmartIterator } from "../iterators/index.js"; import type { GeneratorFunction, IteratorLike } from "../iterators/types.js"; import ReducedIterator from "./reduced-iterator.js"; -import type { KeyedIteratee, KeyedTypeGuardIteratee, KeyedReducer } from "./types.js"; +import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./types.js"; export default class AggregatedIterator { @@ -55,7 +55,7 @@ export default class AggregatedIterator } public filter(predicate: KeyedIteratee): AggregatedIterator; - public filter(predicate: KeyedTypeGuardIteratee): AggregatedIterator; + public filter(predicate: KeyedTypeGuardPredicate): AggregatedIterator; public filter(predicate: KeyedIteratee): AggregatedIterator { const elements = this._elements; @@ -188,7 +188,7 @@ export default class AggregatedIterator } public find(predicate: KeyedIteratee): ReducedIterator; - public find(predicate: KeyedTypeGuardIteratee): ReducedIterator; + public find(predicate: KeyedTypeGuardPredicate): ReducedIterator; public find(predicate: KeyedIteratee): ReducedIterator { const values = new Map(); diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index d30b2eb..84bb08d 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -3,7 +3,7 @@ import { SmartIterator } from "../iterators/index.js"; import type { GeneratorFunction } from "../iterators/types.js"; import AggregatedIterator from "./aggregated-iterator.js"; -import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardIteratee } from "./types.js"; +import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./types.js"; export default class ReducedIterator { @@ -38,7 +38,7 @@ export default class ReducedIterator } public filter(predicate: KeyedIteratee): ReducedIterator; - public filter(predicate: KeyedTypeGuardIteratee): ReducedIterator; + public filter(predicate: KeyedTypeGuardPredicate): ReducedIterator; public filter(predicate: KeyedIteratee): ReducedIterator { const elements = this._elements.enumerate(); diff --git a/src/models/aggregators/types.ts b/src/models/aggregators/types.ts index 7236482..f82aad7 100644 --- a/src/models/aggregators/types.ts +++ b/src/models/aggregators/types.ts @@ -1,19 +1,23 @@ -/* eslint-disable max-len */ - import type { MaybePromise } from "../promises/types.js"; export type KeyedIteratee = (key: K, value: T, index: number) => R; export type AsyncKeyedIteratee = (key: K, value: T, index: number) => Promise; -export type MaybeAsyncKeyedIteratee = (key: K, value: T, index: number) => MaybePromise; - -export type KeyedTypeGuardIteratee = (key: K, value: T, index: number) => value is R; +export type MaybeAsyncKeyedIteratee = + (key: K, value: T, index: number) => MaybePromise; -// @ts-expect-error - This is an asyncronous type guard keyed-iteratee that guarantees the return value is a promise. -export type AsyncKeyedTypeGuardIteratee = (key: K, value: T, index: number) => value is Promise; +export type KeyedTypeGuardPredicate = + (key: K, value: T, index: number) => value is R; -// @ts-expect-error - This may be an asyncronous type guard keyed-iteratee that guarantees the return value may be a promise. -export type MaybeAsyncKeyedTypeGuardIteratee = (key: K, value: T, index: number) => value is MaybePromise; +// These types need this Issue to be solved: https://github.com/microsoft/TypeScript/issues/37681 +// +// export type AsyncKeyedTypeGuardPredicate = +// (key: K, value: T, index: number) => value is Promise; +// export type MaybeAsyncKeyedTypeGuardPredicate = +// (key: K, value: T, index: number) => value is MaybePromise; export type KeyedReducer = (key: K, accumulator: A, value: T, index: number) => A; -export type AsyncKeyedReducer = (key: K, accumulator: A, value: T, index: number) => Promise; -export type MaybeAsyncKeyedReducer = (key: K, accumulator: A, value: T, index: number) => MaybePromise; +export type AsyncKeyedReducer = + (key: K, accumulator: A, value: T, index: number) => Promise; + +export type MaybeAsyncKeyedReducer = + (key: K, accumulator: A, value: T, index: number) => MaybePromise; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 9aecf39..83de7c2 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -8,8 +8,7 @@ import type { MaybeAsyncIteratee, MaybeAsyncReducer, MaybeAsyncIterable, - MaybeAsyncIteratorLike, - MaybeAsyncTypeGuardIteratee + MaybeAsyncIteratorLike } from "./types.js"; @@ -317,36 +316,6 @@ export default class SmartAsyncIterator implements A * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. */ public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator; - - /** - * Filters the elements of the iterator using a given condition. - * Since the iterator is lazy, the filtering process will - * be executed once the resulting iterator is materialized. - * - * A new iterator will be created, holding the reference to the original one. - * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. - * - * ```ts - * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); - * const result = iterator.filter(async (value) => typeof value === "number"); - * - * console.log(await result.toArray()); // [-2, 1] - * ``` - * - * --- - * - * @template S - * The type of the elements that satisfy the condition. - * This allows the type-system to infer the correct type of the new iterator. - * - * It must be a subtype of the original type of the iterator. - * - * @param predicate The condition to check for each element of the iterator. - * - * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. - */ - public filter(predicate: MaybeAsyncTypeGuardIteratee): SmartAsyncIterator; public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator { const iterator = this._iterator; @@ -673,41 +642,6 @@ export default class SmartAsyncIterator implements A * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. */ public async find(predicate: MaybeAsyncIteratee): Promise; - - /** - * Finds the first element of the iterator that satisfies a given condition. - * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. - * The first element that satisfies the condition will be returned immediately. - * - * Only the elements that are necessary to find the first - * satisfying one will be consumed from the original iterator. - * The rest of the original iterator will be available for further consumption. - * - * Also note that: - * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. - * - If the iterator is infinite and no element satisfies the condition, the function will never return. - * - * ```ts - * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); - * const result = await iterator.find(async (value) => typeof value === "number"); - * - * console.log(result); // -2 - * ``` - * - * --- - * - * @template S - * The type of the element that satisfies the condition. - * This allows the type-system to infer the correct type of the result. - * - * It must be a subtype of the original type of the iterator. - * - * @param predicate The condition to check for each element of the iterator. - * - * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. - */ - public async find(predicate: MaybeAsyncTypeGuardIteratee): Promise; public async find(predicate: MaybeAsyncIteratee): Promise { let index = 0; diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 51143e7..afad72f 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -1,7 +1,7 @@ import AggregatedIterator from "../aggregators/aggregated-iterator.js"; import { ValueException } from "../exceptions/index.js"; -import type { GeneratorFunction, Iteratee, TypeGuardIteratee, Reducer, IteratorLike } from "./types.js"; +import type { GeneratorFunction, Iteratee, TypeGuardPredicate, Reducer, IteratorLike } from "./types.js"; /** * A wrapper class representing an enhanced & instantiable version @@ -235,7 +235,7 @@ export default class SmartIterator implements Iterat * * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition. */ - public filter(predicate: TypeGuardIteratee): SmartIterator; + public filter(predicate: TypeGuardPredicate): SmartIterator; public filter(predicate: Iteratee): SmartIterator { const iterator = this._iterator; @@ -593,7 +593,7 @@ export default class SmartIterator implements Iterat * * @returns The first element that satisfies the condition, `undefined` otherwise. */ - public find(predicate: TypeGuardIteratee): S | undefined; + public find(predicate: TypeGuardPredicate): S | undefined; public find(predicate: Iteratee): T | undefined { let index = 0; diff --git a/src/models/iterators/types.ts b/src/models/iterators/types.ts index dd7e761..91a02c1 100644 --- a/src/models/iterators/types.ts +++ b/src/models/iterators/types.ts @@ -1,25 +1,205 @@ - import type { MaybePromise } from "../promises/types.js"; +/** + * An utility type that represents an iterable object that can be either synchronous or asynchronous. + * + * ```ts + * const iterable: MaybeAsyncIterable = [...]; + * for await (const value of iterable) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + */ export type MaybeAsyncIterable = Iterable | AsyncIterable; + +/** + * An utility type that represents an iterator object that can be either synchronous or asynchronous. + * + * ```ts + * const iterator: MaybeAsyncIterator = { ... }; + * for await (const value of iterator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterator. + */ export type MaybeAsyncIterator = Iterator | AsyncIterator; + +/** + * An utility type that represents a generator object that can be either synchronous or asynchronous. + * + * ```ts + * const generator: MaybeAsyncGenerator = [async] function*() { ... }; + * for await (const value of generator) + * { + * console.log(value); + * } + */ export type MaybeAsyncGenerator = Generator | AsyncGenerator; +/** + * An utility type that represents a function that returns a generator object. + * It differs from the native `GeneratorFunction` type by allowing to specify the types of the returned generator. + * + * ```ts + * const generatorFn: GeneratorFunction = function*() { ... }; + * const generator: Generator = generatorFn(); + * for (const value of generator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements generated by the generator. + * @template R The type of the return value of the generator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ export type GeneratorFunction = () => Generator; + +/** + * An utility type that represents a function that returns an asynchronous generator object. + * It differs from the native `AsyncGeneratorFunction` type by allowing to specify the types of the returned generator. + * + * ```ts + * const asyncGeneratorFn: AsyncGeneratorFunction = async function*() { ... }; + * const generator: AsyncGenerator = asyncGeneratorFn(); + * for await (const value of generator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements generated by the generator. + * @template R The type of the return value of the generator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ export type AsyncGeneratorFunction = () => AsyncGenerator; + +/** + * An utility type that represents a function that returns a + * generator object that can be either synchronous or asynchronous. + * + * ```ts + * const generatorFn: MaybeAsyncGeneratorFunction = [async] function*() { ... }; + * const generator: MaybeAsyncGenerator = generatorFn(); + * for await (const value of generator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements generated by the generator. + * @template R The type of the return value of the generator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ export type MaybeAsyncGeneratorFunction = () => MaybeAsyncGenerator; +/** + * An utility type that represents the standard JavaScript's + * {@link https://en.wikipedia.org/wiki/Iteratee|iteratee} function. + * It can be used to transform the elements of an iterable. + * + * ```ts + * const iteratee: Iteratee = (value: number) => `${value}`; + * const values: string[] = [1, 2, 3, 4, 5].map(iteratee); + * for (const value of values) + * { + * console.log(value); // "1", "2", "3", "4", "5" + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ export type Iteratee = (value: T, index: number) => R; + +/** + * An utility type that represents an asynchronous {@link https://en.wikipedia.org/wiki/Iteratee|iteratee} function. + * It can be used to transform the elements of an iterable asynchronously. + * + * ```ts + * const iteratee: AsyncIteratee = async (value: number) => `${value}`; + * const values: Promise[] = [1, 2, 3, 4, 5].map(iteratee); + * for (const value of values) + * { + * console.log(await value); // "1", "2", "3", "4", "5" + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ export type AsyncIteratee = (value: T, index: number) => Promise; -export type MaybeAsyncIteratee = (value: T, index: number) => MaybePromise; -export type TypeGuardIteratee = (value: T, index: number) => value is R; +/** + * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee} + * function that can be either synchronous or asynchronous. + * It can be used to transform the elements of an iterable. + * + * ```ts + * const iteratee: MaybeAsyncIteratee = [async] (value: number) => `${value}`; + * const values: Promise[] = [1, 2, 3, 4, 5].map(iteratee); + * for (const value of values) + * { + * console.log(await value); // "1", "2", "3", "4", "5" + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ +export type MaybeAsyncIteratee = (value: T, index: number) => MaybePromise; -// @ts-expect-error - This is an asyncronous type guard iteratee that guarantees the return value is a promise. -export type AsyncTypeGuardIteratee = (value: T, index: number) => value is Promise; +/** + * An utility type that represents a {@link https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)|predicate} + * which acts as a + * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}. + * It can be used to ensure the type of the elements of an iterable + * while allowing the type-system to infer them correctly. + * + * ```ts + * const iteratee: TypeGuardPredicate = (value): value is string => typeof value === "string"; + * const values: string[] = [1, "2", 3, "4", 5].filter(iteratee); + * for (const value of values) + * { + * console.log(value); // "2", "4" + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R + * The type of the elements that pass the type guard. + * It must be a subtype of `T`. Default is `T`. + */ +export type TypeGuardPredicate = (value: T, index: number) => value is R; -// @ts-expect-error - This may be an asyncronous type guard iteratee that guarantees the return value may be a promise. -export type MaybeAsyncTypeGuardIteratee = (value: T, index: number) => value is MaybePromise; +// These types need this Issue to be solved: https://github.com/microsoft/TypeScript/issues/37681 +// +// export type AsyncTypeGuardPredicate = (value: T, index: number) => value is Promise; +// export type MaybeAsyncTypeGuardPredicate = (value: T, index: number) => value is MaybePromise; export type Reducer = (accumulator: A, value: T, index: number) => A; export type AsyncReducer = (accumulator: A, value: T, index: number) => Promise; diff --git a/src/models/types.ts b/src/models/types.ts index 63a7729..a7cc230 100644 --- a/src/models/types.ts +++ b/src/models/types.ts @@ -1,9 +1,10 @@ export type { KeyedIteratee, + AsyncKeyedIteratee, MaybeAsyncKeyedIteratee, - KeyedTypeGuardIteratee, - MaybeAsyncKeyedTypeGuardIteratee, + KeyedTypeGuardPredicate, KeyedReducer, + AsyncKeyedReducer, MaybeAsyncKeyedReducer } from "./aggregators/types.js"; @@ -13,10 +14,11 @@ export type { AsyncGeneratorFunction, MaybeAsyncGeneratorFunction, Iteratee, + AsyncIteratee, MaybeAsyncIteratee, - TypeGuardIteratee, - MaybeAsyncTypeGuardIteratee, + TypeGuardPredicate, Reducer, + AsyncReducer, MaybeAsyncReducer, IteratorLike, AsyncIteratorLike, From 9f67f7f8a97451953c49a04018bb05dc43f31624 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 5 Jan 2025 09:37:33 +0100 Subject: [PATCH 17/32] add: Missing JSDoc for `models/iterators/types` file. + Reintroduced (but edited) some previously removed overrides. --- .../aggregators/aggregated-async-iterator.ts | 4 + src/models/iterators/smart-async-iterator.ts | 65 +++++++++ src/models/iterators/types.ts | 130 ++++++++++++++++-- src/models/promises/types.ts | 2 +- 4 files changed, 190 insertions(+), 11 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index 89e749c..6144252 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -65,6 +65,7 @@ export default class AggregatedAsyncIterator } public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator; + public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator; public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator { const elements = this._elements; @@ -199,6 +200,9 @@ export default class AggregatedAsyncIterator } public async find(predicate: MaybeAsyncKeyedIteratee): Promise>; + public async find(predicate: MaybeAsyncKeyedIteratee) + : Promise>; + public async find(predicate: MaybeAsyncKeyedIteratee): Promise> { const values = new Map(); diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 83de7c2..081c1e8 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -316,6 +316,36 @@ export default class SmartAsyncIterator implements A * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. */ public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator; + + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume also the other. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); + * const result = iterator.filter(async (value) => typeof value === "number"); + * + * console.log(await result.toArray()); // [-2, 1] + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. + */ + public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator; public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator { const iterator = this._iterator; @@ -642,6 +672,41 @@ export default class SmartAsyncIterator implements A * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. */ public async find(predicate: MaybeAsyncIteratee): Promise; + + /** + * Finds the first element of the iterator that satisfies a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * The first element that satisfies the condition will be returned immediately. + * + * Only the elements that are necessary to find the first + * satisfying one will be consumed from the original iterator. + * The rest of the original iterator will be available for further consumption. + * + * Also note that: + * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. + * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * + * ```ts + * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); + * const result = await iterator.find(async (value) => typeof value === "number"); + * + * console.log(result); // -2 + * ``` + * + * --- + * + * @template S + * The type of the element that satisfies the condition. + * This allows the type-system to infer the correct type of the result. + * + * It must be a subtype of the original type of the iterator. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. + */ + public async find(predicate: MaybeAsyncIteratee): Promise; public async find(predicate: MaybeAsyncIteratee): Promise { let index = 0; diff --git a/src/models/iterators/types.ts b/src/models/iterators/types.ts index 91a02c1..dd71bfd 100644 --- a/src/models/iterators/types.ts +++ b/src/models/iterators/types.ts @@ -1,7 +1,7 @@ import type { MaybePromise } from "../promises/types.js"; /** - * An utility type that represents an iterable object that can be either synchronous or asynchronous. + * An union type that represents an iterable object that can be either synchronous or asynchronous. * * ```ts * const iterable: MaybeAsyncIterable = [...]; @@ -18,7 +18,7 @@ import type { MaybePromise } from "../promises/types.js"; export type MaybeAsyncIterable = Iterable | AsyncIterable; /** - * An utility type that represents an iterator object that can be either synchronous or asynchronous. + * An union type that represents an iterator object that can be either synchronous or asynchronous. * * ```ts * const iterator: MaybeAsyncIterator = { ... }; @@ -35,7 +35,7 @@ export type MaybeAsyncIterable = Iterable | export type MaybeAsyncIterator = Iterator | AsyncIterator; /** - * An utility type that represents a generator object that can be either synchronous or asynchronous. + * An union type that represents a generator object that can be either synchronous or asynchronous. * * ```ts * const generator: MaybeAsyncGenerator = [async] function*() { ... }; @@ -137,9 +137,9 @@ export type Iteratee = (value: T, index: number) => R; * ```ts * const iteratee: AsyncIteratee = async (value: number) => `${value}`; * const values: Promise[] = [1, 2, 3, 4, 5].map(iteratee); - * for (const value of values) + * for await (const value of values) * { - * console.log(await value); // "1", "2", "3", "4", "5" + * console.log(value); // "1", "2", "3", "4", "5" * } * ``` * @@ -158,9 +158,9 @@ export type AsyncIteratee = (value: T, index: number) => Promise * ```ts * const iteratee: MaybeAsyncIteratee = [async] (value: number) => `${value}`; * const values: Promise[] = [1, 2, 3, 4, 5].map(iteratee); - * for (const value of values) + * for await (const value of values) * { - * console.log(await value); // "1", "2", "3", "4", "5" + * console.log(value); // "1", "2", "3", "4", "5" * } * ``` * @@ -173,7 +173,7 @@ export type MaybeAsyncIteratee = (value: T, index: number) => Maybe /** * An utility type that represents a {@link https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)|predicate} - * which acts as a + * function which acts as a * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}. * It can be used to ensure the type of the elements of an iterable * while allowing the type-system to infer them correctly. @@ -201,10 +201,120 @@ export type TypeGuardPredicate = (value: T, index: number) => va // export type AsyncTypeGuardPredicate = (value: T, index: number) => value is Promise; // export type MaybeAsyncTypeGuardPredicate = (value: T, index: number) => value is MaybePromise; +/** + * An utility type that represents a reducer function. + * It can be used to reduce the elements of an iterable into a single value. + * + * ```ts + * const sum: Reducer = (accumulator, value) => accumulator + value; + * const total: number = [1, 2, 3, 4, 5].reduce(sum); + * + * console.log(total); // 15 + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type Reducer = (accumulator: A, value: T, index: number) => A; + +/** + * An utility type that represents an asynchronous reducer function. + * It can be used to reduce the elements of an iterable into a single value. + * + * ```ts + * const sum: AsyncReducer = async (accumulator, value) => accumulator + value; + * const result = await new SmartAsyncIterator([1, 2, 3, 4, 5]).reduce(sum); + * + * console.log(result); // 15 + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type AsyncReducer = (accumulator: A, value: T, index: number) => Promise; + +/** + * An utility type that represents a reducer function that can be either synchronous or asynchronous. + * It can be used to reduce the elements of an iterable into a single value. + * + * ```ts + * const sum: MaybeAsyncReducer = [async] (accumulator, value) => accumulator + value; + * const result = await new SmartAsyncIterator([1, 2, 3, 4, 5]).reduce(sum); + * + * console.log(result); // 15 + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type MaybeAsyncReducer = (accumulator: A, value: T, index: number) => MaybePromise; -export type IteratorLike = Iterable | Iterator; -export type AsyncIteratorLike = AsyncIterable | AsyncIterator; +/** + * An union type that represents either an iterable or an iterator object. + * More in general, it represents an object that can be looped over in one way or another. + * + * ```ts + * const elements: IteratorLike = { ... }; + * const iterator: SmartIterator = new SmartIterator(elements); + * for (const value of iterator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iterator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ +export type IteratorLike = Iterable | Iterator; + +/** + * An union type that represents either an iterable or an iterator object that can be asynchronous. + * More in general, it represents an object that can be looped over in one way or another. + * + * ```ts + * const elements: AsyncIteratorLike = { ... }; + * const iterator: SmartAsyncIterator = new SmartAsyncIterator(elements); + * for await (const value of iterator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iterator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ +export type AsyncIteratorLike = AsyncIterable | AsyncIterator; + +/** + * An union type that represents either an iterable or an iterator + * object that can be either synchronous or asynchronous. + * More in general, it represents an object that can be looped over in one way or another. + * + * ```ts + * const elements: MaybeAsyncIteratorLike = { ... }; + * const iterator: SmartAsyncIterator = new SmartAsyncIterator(elements); + * for await (const value of iterator) + * { + * console.log(value); + * } + * ``` + * + * --- + * + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iterator. Default is `void`. + * @template N The type of the `next` method argument. Default is `undefined`. + */ export type MaybeAsyncIteratorLike = IteratorLike | AsyncIteratorLike; diff --git a/src/models/promises/types.ts b/src/models/promises/types.ts index 24d3b0a..92c5c08 100644 --- a/src/models/promises/types.ts +++ b/src/models/promises/types.ts @@ -1,5 +1,5 @@ /** - * An utility type that represents a value that can be either a value or a promise of that value. + * An union type that represents a value that can be either a value or a promise of that value. * This is useful when you want to handle both synchronous and asynchronous values in the same way. * * ```ts From 9fb59b745b7e6d19d9fd72c0084e01008867fd5f Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 5 Jan 2025 09:38:51 +0100 Subject: [PATCH 18/32] upd: Updated dependencies. --- package.json | 12 +- pnpm-lock.yaml | 395 ++++++++++++++++++++++++++----------------------- 2 files changed, 218 insertions(+), 189 deletions(-) diff --git a/package.json b/package.json index 22a03fd..ec5b6e0 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,12 @@ "exports": { ".": { "import": { - "default": "./dist/core.js", - "types": "./src/index.ts" + "types": "./src/index.ts", + "default": "./dist/core.js" }, "require": { - "default": "./dist/core.umd.cjs", - "types": "./src/index.ts" + "types": "./src/index.ts", + "default": "./dist/core.umd.cjs" } } }, @@ -58,10 +58,10 @@ }, "devDependencies": { "@byloth/eslint-config-typescript": "^3.0.3", - "@types/node": "^22.10.2", + "@types/node": "^22.10.5", "husky": "^9.1.7", "typescript": "^5.7.2", - "vite": "^5.4.11" + "vite": "^6.0.7" }, "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6705b8..df69685 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^3.0.3 version: 3.0.3(eslint@9.17.0)(typescript@5.7.2) '@types/node': - specifier: ^22.10.2 - version: 22.10.2 + specifier: ^22.10.5 + version: 22.10.5 husky: specifier: ^9.1.7 version: 9.1.7 @@ -21,8 +21,8 @@ importers: specifier: ^5.7.2 version: 5.7.2 vite: - specifier: ^5.4.11 - version: 5.4.11(@types/node@22.10.2) + specifier: ^6.0.7 + version: 6.0.7(@types/node@22.10.5) packages: @@ -32,141 +32,153 @@ packages: '@byloth/eslint-config@3.0.3': resolution: {integrity: sha512-fXpIxZByU2Ux+95jGcEEweKYb4bxS571xaMZDJ24wsasrDuczjld63EGAulnm9yRAJiLpScruvV0mWLow+16tg==} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -346,54 +358,54 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.10.2': - resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} - '@typescript-eslint/eslint-plugin@8.18.1': - resolution: {integrity: sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==} + '@typescript-eslint/eslint-plugin@8.19.0': + resolution: {integrity: sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.18.1': - resolution: {integrity: sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==} + '@typescript-eslint/parser@8.19.0': + resolution: {integrity: sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.18.1': - resolution: {integrity: sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==} + '@typescript-eslint/scope-manager@8.19.0': + resolution: {integrity: sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.18.1': - resolution: {integrity: sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==} + '@typescript-eslint/type-utils@8.19.0': + resolution: {integrity: sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.18.1': - resolution: {integrity: sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==} + '@typescript-eslint/types@8.19.0': + resolution: {integrity: sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.18.1': - resolution: {integrity: sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==} + '@typescript-eslint/typescript-estree@8.19.0': + resolution: {integrity: sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.18.1': - resolution: {integrity: sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==} + '@typescript-eslint/utils@8.19.0': + resolution: {integrity: sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.18.1': - resolution: {integrity: sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==} + '@typescript-eslint/visitor-keys@8.19.0': + resolution: {integrity: sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -463,9 +475,9 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} hasBin: true escape-string-regexp@4.0.0: @@ -527,8 +539,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.18.0: + resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} @@ -774,22 +786,27 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - vite@5.4.11: - resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.0.7: + resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -804,6 +821,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -823,8 +844,8 @@ snapshots: '@byloth/eslint-config-typescript@3.0.3(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@byloth/eslint-config': 3.0.3 - '@typescript-eslint/eslint-plugin': 8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) transitivePeerDependencies: - eslint - jiti @@ -841,73 +862,79 @@ snapshots: - jiti - supports-color - '@esbuild/aix-ppc64@0.21.5': + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.24.2': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-x64@0.24.2': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/darwin-arm64@0.24.2': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/darwin-x64@0.24.2': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.24.2': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/freebsd-x64@0.24.2': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/linux-arm64@0.24.2': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/linux-arm@0.24.2': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-ia32@0.24.2': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-loong64@0.24.2': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-mips64el@0.24.2': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-ppc64@0.24.2': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-riscv64@0.24.2': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-s390x@0.24.2': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-x64@0.24.2': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/netbsd-arm64@0.24.2': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/openbsd-arm64@0.24.2': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-x64@0.24.2': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/sunos-x64@0.24.2': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/win32-arm64@0.24.2': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-ia32@0.24.2': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/win32-x64@0.24.2': optional: true '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0)': @@ -978,7 +1005,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + fastq: 1.18.0 '@rollup/rollup-android-arm-eabi@4.29.1': optional: true @@ -1041,18 +1068,18 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.10.2': + '@types/node@22.10.5': dependencies: undici-types: 6.20.0 - '@typescript-eslint/eslint-plugin@8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.18.1(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/type-utils': 8.18.1(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.1(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/type-utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.19.0 eslint: 9.17.0 graphemer: 1.4.0 ignore: 5.3.2 @@ -1062,27 +1089,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.18.1(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.19.0 debug: 4.4.0 eslint: 9.17.0 typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.18.1': + '@typescript-eslint/scope-manager@8.19.0': dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/visitor-keys': 8.19.0 - '@typescript-eslint/type-utils@8.18.1(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/type-utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) - '@typescript-eslint/utils': 8.18.1(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) + '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) debug: 4.4.0 eslint: 9.17.0 ts-api-utils: 1.4.3(typescript@5.7.2) @@ -1090,12 +1117,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.18.1': {} + '@typescript-eslint/types@8.19.0': {} - '@typescript-eslint/typescript-estree@8.18.1(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.19.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/visitor-keys': 8.18.1 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/visitor-keys': 8.19.0 debug: 4.4.0 fast-glob: 3.3.2 is-glob: 4.0.3 @@ -1106,20 +1133,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.18.1(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0) - '@typescript-eslint/scope-manager': 8.18.1 - '@typescript-eslint/types': 8.18.1 - '@typescript-eslint/typescript-estree': 8.18.1(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.19.0 + '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) eslint: 9.17.0 typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.18.1': + '@typescript-eslint/visitor-keys@8.19.0': dependencies: - '@typescript-eslint/types': 8.18.1 + '@typescript-eslint/types': 8.19.0 eslint-visitor-keys: 4.2.0 acorn-jsx@5.3.2(acorn@8.14.0): @@ -1183,31 +1210,33 @@ snapshots: deep-is@0.1.4: {} - esbuild@0.21.5: + esbuild@0.24.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 escape-string-regexp@4.0.0: {} @@ -1291,7 +1320,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.17.1: + fastq@1.18.0: dependencies: reusify: 1.0.4 @@ -1511,13 +1540,13 @@ snapshots: dependencies: punycode: 2.3.1 - vite@5.4.11(@types/node@22.10.2): + vite@6.0.7(@types/node@22.10.5): dependencies: - esbuild: 0.21.5 + esbuild: 0.24.2 postcss: 8.4.49 rollup: 4.29.1 optionalDependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 fsevents: 2.3.3 which@2.0.2: From b5472f7d0d7b30232b1ed370d350aa4bc7a5ab6b Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sun, 5 Jan 2025 09:40:43 +0100 Subject: [PATCH 19/32] fix: Fixed a no longer required `eslint-disable` rule. --- src/models/json/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/json/types.ts b/src/models/json/types.ts index d8d7315..a19a856 100644 --- a/src/models/json/types.ts +++ b/src/models/json/types.ts @@ -6,7 +6,6 @@ export type JSONArray = JSONValue[]; /** * A type representing a JSON object. */ -// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style export interface JSONObject { [key: string]: JSONValue } /** From 26936ebbbf6096844f4d546f3e0e680d14137be3 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Wed, 8 Jan 2025 03:00:43 +0100 Subject: [PATCH 20/32] wip: A lot of things... --- .../aggregators/aggregated-async-iterator.ts | 19 +++ src/models/aggregators/aggregated-iterator.ts | 151 ++++++++++++++++++ src/models/aggregators/reduced-iterator.ts | 13 ++ src/models/callbacks/switchable-callback.ts | 4 +- src/models/game-loop.ts | 6 +- src/models/iterators/smart-async-iterator.ts | 20 ++- src/models/iterators/smart-iterator.ts | 20 ++- src/models/promises/deferred-promise.ts | 4 +- src/models/promises/smart-promise.ts | 8 +- src/models/timers/countdown.ts | 2 +- 10 files changed, 233 insertions(+), 14 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index 6144252..51f460b 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -276,6 +276,25 @@ export default class AggregatedAsyncIterator } } + public rekey(iteratee: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator + { + const elements = this._elements; + + return new AggregatedAsyncIterator(async function* (): AsyncGenerator<[J, T]> + { + const indexes = new Map(); + + for await (const [key, element] of elements) + { + const index = indexes.get(key) ?? 0; + + yield [await iteratee(key, element, index), element]; + + indexes.set(key, index + 1); + } + }); + } + public keys(): SmartAsyncIterator { const elements = this._elements; diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 348c8ee..a503ee9 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -4,19 +4,151 @@ import type { GeneratorFunction, IteratorLike } from "../iterators/types.js"; import ReducedIterator from "./reduced-iterator.js"; import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./types.js"; +/** + * A class representing an iterator that aggregates elements in a lazy and optimized way. + * + * It's part of the {@link SmartIterator} implementation, providing a way to group elements of an iterable by key. + * For this reason, it isn't recommended to instantiate this class directly + * (although it's still possible), but rather use the {@link SmartIterator.groupBy} method. + * + * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate. + * See the {@link AggregatedIterator.keys}, {@link AggregatedIterator.items} + * & {@link AggregatedIterator.values} methods. + * It does, however, provides the same set of methods to perform + * operations and transformation on the elements of the iterator, + * having also the knowledge and context of the groups to which + * they belong, allowing to handle them in a grouped manner. + * + * + * This is particularly useful when you need to group elements and + * then perform specific operations on the groups themselves. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator: SmartIterator = range(10).map(() => Random.Integer(10) + 1); + * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .count() + * .toObject(); + * + * if (odd > even) { console.log("There are more odd numbers."); } + * else { console.log("There are more even numbers."); } + * ``` + * + * --- + * + * @template K The type of the keys of the elements. + * @template T The type of the elements. + */ export default class AggregatedIterator { + /** + * The internal {@link SmartIterator} that holds the elements to aggregate. + */ protected _elements: SmartIterator<[K, T]>; + /** + * Initializes a new instance of the {@link AggregatedIterator} class. + * + * ```ts + * const iterator = new AggregatedIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); + * ``` + * + * --- + * + * @param iterable The iterable to aggregate. + */ public constructor(iterable: Iterable<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedIterator} class. + * + * ```ts + * import { Random } from "@byloth/core"; + * + * const iterator = new AggregatedIterator({ + * _index: 0, + * next: () => + * { + * if (this._index >= 5) { return { done: true, value: undefined }; } + * this._index += 1; + * + * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * } + * }); + * ``` + * + * --- + * + * @param iterator The iterator to aggregate. + */ public constructor(iterator: Iterator<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedIterator} class. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator = new AggregatedIterator(function* () + * { + * for (const index of range(5)) + * { + * yield [Random.Choice(["A", "B", "C"]), (index + 1)]; + * } + * }); + * ``` + * + * --- + * + * @param generatorFn The generator function to aggregate. + */ public constructor(generatorFn: GeneratorFunction<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedIterator} class. + * + * ```ts + * const iterator = new AggregatedIterator(keyedValues); + * ``` + * + * --- + * + * @param argument The iterable, iterator or generator function to aggregate. + */ public constructor(argument: IteratorLike<[K, T]> | GeneratorFunction<[K, T]>); public constructor(argument: IteratorLike<[K, T]> | GeneratorFunction<[K, T]>) { this._elements = new SmartIterator(argument); } + /** + * Determines whether all elements of each group of the iterator satisfy a given condition. + * See also {@link AggregatedIterator.some}. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element of one group doesn't satisfy the condition, + * the result for the respective group will set to `false`. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the boolean results for each group. + * If the iterator is infinite, the function will never return. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator: SmartIterator = range(10).map(() => Random.Integer(10) + 1); + * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .count() + * .toObject(); + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns `true` if all elements satisfy the condition, `false` otherwise. + */ public every(predicate: KeyedIteratee): ReducedIterator { const values = new Map(); @@ -266,6 +398,25 @@ export default class AggregatedIterator } } + public rekey(iteratee: KeyedIteratee): AggregatedIterator + { + const elements = this._elements; + + return new AggregatedIterator(function* () + { + const indexes = new Map(); + + for (const [key, element] of elements) + { + const index = indexes.get(key) ?? 0; + + yield [iteratee(key, element, index), element]; + + indexes.set(key, index + 1); + } + }); + } + public keys(): SmartIterator { const elements = this._elements; diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index 84bb08d..4d0d597 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -167,6 +167,19 @@ export default class ReducedIterator } } + public rekey(iteratee: KeyedIteratee): AggregatedIterator + { + const elements = this._elements.enumerate(); + + return new AggregatedIterator(function* () + { + for (const [index, [key, element]] of elements) + { + yield [iteratee(key, element, index), element]; + } + }); + } + public keys(): SmartIterator { const elements = this._elements; diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index d9ed151..08b9a8f 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -44,7 +44,7 @@ export default class SwitchableCallback = Callbac * A flag indicating whether the callback is enabled or not. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use + * If you're looking for the public and readonly property, use * the {@link SwitchableCallback.isEnabled} getter instead. */ protected _isEnabled: boolean; @@ -61,7 +61,7 @@ export default class SwitchableCallback = Callbac * The key that is associated with the currently selected implementation. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link SwitchableCallback.key} getter instead. + * If you're looking for the public and readonly property, use the {@link SwitchableCallback.key} getter instead. */ protected _key: string; diff --git a/src/models/game-loop.ts b/src/models/game-loop.ts index b7e8bac..1c7143c 100644 --- a/src/models/game-loop.ts +++ b/src/models/game-loop.ts @@ -25,7 +25,7 @@ interface GameLoopEventMap * * Every time the callback is executed, it receives the * elapsed time since the start of the game loop. - * It's also possible to subscribe to the `start` and `stop` events to receive notifications when they occur. + * It's also possible to subscribe to the `start` & `stop` events to receive notifications when they occur. * * ```ts * const loop = new GameLoop((elapsedTime: number) => @@ -53,7 +53,7 @@ export default class GameLoop * of the start of the game loop, it's also used to calculate the elapsed time. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link GameLoop.startTime} getter instead. + * If you're looking for the public and readonly property, use the {@link GameLoop.startTime} getter instead. */ protected _startTime: number; @@ -71,7 +71,7 @@ export default class GameLoop * A flag indicating whether the game loop is currently running or not. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link GameLoop.isRunning} getter instead. + * If you're looking for the public and readonly property, use the {@link GameLoop.isRunning} getter instead. */ protected _isRunning: boolean; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 081c1e8..7315c7f 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -13,7 +13,7 @@ import type { } from "./types.js"; /** - * A wrapper class representing an enhanced & instantiable version + * A wrapper class representing an enhanced and instantiable version * of the native {@link AsyncIterable} & {@link AsyncIterator} interfaces. * * It provides a set of utility methods to better manipulate and transform @@ -21,6 +21,24 @@ import type { * It takes inspiration from the native {@link Array} methods like * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc... * + * The class is lazy, meaning that the transformations are applied + * only when the resulting iterator is materialized, not before. + * This allows to chain multiple transformations without + * the need to iterate over the elements multiple times. + * + * ```ts + * const result = new SmartAsyncIterator(["-5", "-4", "-3", "-2", "-1", "0", "1", "2", "3", "4", "5"]) + * .map((value) => Number(value)) + * .map((value) => value + Math.ceil(Math.abs(value / 2))) + * .filter((value) => value >= 0) + * .map((value) => value + 1) + * .reduce((acc, value) => acc + value); + * + * console.log(await result); // 31 + * ``` + * + * --- + * * @template T The type of elements in the iterator. * @template R The type of the final result of the iterator. Default is `void`. * @template N The type of the argument passed to the `next` method. Default is `undefined`. diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index afad72f..05e9b57 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -4,7 +4,7 @@ import { ValueException } from "../exceptions/index.js"; import type { GeneratorFunction, Iteratee, TypeGuardPredicate, Reducer, IteratorLike } from "./types.js"; /** - * A wrapper class representing an enhanced & instantiable version + * A wrapper class representing an enhanced and instantiable version * of the native {@link Iterable} & {@link Iterator} interfaces. * * It provides a set of utility methods to better manipulate and @@ -12,6 +12,24 @@ import type { GeneratorFunction, Iteratee, TypeGuardPredicate, Reducer, Iterator * It takes inspiration from the native {@link Array} methods like * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc... * + * The class is lazy, meaning that the transformations are applied + * only when the resulting iterator is materialized, not before. + * This allows to chain multiple transformations without + * the need to iterate over the elements multiple times. + * + * ```ts + * const result = new SmartIterator(["-5", "-4", "-3", "-2", "-1", "0", "1", "2", "3", "4", "5"]) + * .map(Number) + * .map((value) => value + Math.ceil(Math.abs(value / 2))) + * .filter((value) => value >= 0) + * .map((value) => value + 1) + * .reduce((acc, value) => acc + value); + * + * console.log(result); // 31 + * ``` + * + * --- + * * @template T The type of elements in the iterator. * @template R The type of the final result of the iterator. Default is `void`. * @template N The type of the argument required by the `next` method. Default is `undefined`. diff --git a/src/models/promises/deferred-promise.ts b/src/models/promises/deferred-promise.ts index 23b78cc..8ea0e94 100644 --- a/src/models/promises/deferred-promise.ts +++ b/src/models/promises/deferred-promise.ts @@ -35,7 +35,7 @@ export default class DeferredPromise extends SmartPr * The exposed function that allows to resolve the promise. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link DeferredPromise.resolve} getter instead. + * If you're looking for the public and readonly property, use the {@link DeferredPromise.resolve} getter instead. */ protected _resolve: PromiseResolver; @@ -48,7 +48,7 @@ export default class DeferredPromise extends SmartPr * The exposed function that allows to reject the promise. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link DeferredPromise.reject} getter instead. + * If you're looking for the public and readonly property, use the {@link DeferredPromise.reject} getter instead. */ protected _reject: PromiseRejecter; diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 37918bc..6596e5c 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -5,7 +5,7 @@ import type { FulfilledHandler, PromiseExecutor, RejectedHandler } from "./types * * It provides additional properties to check the state of the promise itself. * The state can be either `pending`, `fulfilled` or `rejected` and is accessible through - * the {@link SmartPromise.isPending}, {@link SmartPromise.isFulfilled} and {@link SmartPromise.isRejected} properties. + * the {@link SmartPromise.isPending}, {@link SmartPromise.isFulfilled} & {@link SmartPromise.isRejected} properties. * * ```ts * const promise = new SmartPromise((resolve, reject) => @@ -57,7 +57,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise is still pending or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link SmartPromise.isPending} getter instead. + * If you're looking for the public and readonly property, use the {@link SmartPromise.isPending} getter instead. */ protected _isPending: boolean; @@ -73,7 +73,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise has been fulfilled or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link SmartPromise.isFulfilled} getter instead. + * If you're looking for the public and readonly property, use the {@link SmartPromise.isFulfilled} getter instead. */ protected _isFulfilled: boolean; @@ -89,7 +89,7 @@ export default class SmartPromise implements Promise * A flag indicating whether the promise has been rejected or not. * * The protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link SmartPromise.isRejected} getter instead. + * If you're looking for the public and readonly property, use the {@link SmartPromise.isRejected} getter instead. */ protected _isRejected: boolean; diff --git a/src/models/timers/countdown.ts b/src/models/timers/countdown.ts index bdcf482..697b365 100644 --- a/src/models/timers/countdown.ts +++ b/src/models/timers/countdown.ts @@ -45,7 +45,7 @@ export default class Countdown extends GameLoop * The total duration of the countdown in milliseconds. * * This protected property is the only one that can be modified directly by the derived classes. - * If you're looking for the public & readonly property, use the {@link Countdown.duration} getter instead. + * If you're looking for the public and readonly property, use the {@link Countdown.duration} getter instead. */ protected _duration: number; From 30e904aa7c62f55477c5a19fb5b602179c3b1a85 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 11 Jan 2025 14:08:39 +0100 Subject: [PATCH 21/32] wip: Minor edits. --- .editorconfig | 2 +- src/models/aggregators/aggregated-iterator.ts | 75 ++++++++++++++++++- src/models/iterators/smart-async-iterator.ts | 34 ++++----- src/models/iterators/smart-iterator.ts | 44 ++++++----- src/utils/iterator.ts | 4 +- 5 files changed, 115 insertions(+), 44 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8591b8f..4bb7fad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,6 +19,6 @@ indent_size = 2 [*.{json,yml}] indent_size = 2 -[*.md] +[*.{js,md,ts,vue}] max_line_length = off trim_trailing_whitespace = false diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index a503ee9..edaae9f 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -18,7 +18,6 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ * operations and transformation on the elements of the iterator, * having also the knowledge and context of the groups to which * they belong, allowing to handle them in a grouped manner. - * * * This is particularly useful when you need to group elements and * then perform specific operations on the groups themselves. @@ -128,7 +127,7 @@ export default class AggregatedIterator * * The method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group doesn't satisfy the condition, - * the result for the respective group will set to `false`. + * the result for the respective group will set to `false`. * * Eventually, it will return a new {@link ReducedIterator} * object that will contain all the boolean results for each group. @@ -137,10 +136,13 @@ export default class AggregatedIterator * ```ts * import { range, Random } from "@byloth/core"; * - * const iterator: SmartIterator = range(10).map(() => Random.Integer(10) + 1); + * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .count() + * .every((value) => value >= 0) * .toObject(); + * + * if (even) { console.log("All even numbers are positive."); } + * if (odd) { console.log("All odd numbers are positive."); } * ``` * * --- @@ -167,6 +169,37 @@ export default class AggregatedIterator for (const [key, [_, result]] of values) { yield [key, result]; } }); } + + /** + * Determines whether any elements of each group of the iterator satisfy a given condition. + * See also {@link AggregatedIterator.every}. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * Once a single element of one group satisfies the condition, + * the result for the respective group will set to `true`. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the boolean results for each group. + * If the iterator is infinite, the function will never return. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); + * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .some((value) => value >= 0) + * .toObject(); + * + * if (even) { console.log("At least one even number is positive."); } + * if (odd) { console.log("At least one odd number is positive."); } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns `true` if any element satisfies the condition, `false` otherwise. + */ public some(predicate: KeyedIteratee): ReducedIterator { const values = new Map(); @@ -186,6 +219,40 @@ export default class AggregatedIterator }); } + /** + * Filters the elements of the iterator using a given condition. + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the result. + * + * Eventually, it will return a new {@link AggregatedIterator} + * object that will contain all the elements that satisfy the condition. + * If the iterator is infinite, the function will never return. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); + * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .filter((value) => value >= 0) + * .toObject(); + * + * console.log("Even numbers:", even); + * console.log("Odd numbers:", odd); + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new iterator with the elements that satisfy the condition. + */ public filter(predicate: KeyedIteratee): AggregatedIterator; public filter(predicate: KeyedTypeGuardPredicate): AggregatedIterator; public filter(predicate: KeyedIteratee): AggregatedIterator diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 7315c7f..b3742cd 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -239,7 +239,7 @@ export default class SmartAsyncIterator implements A * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartAsyncIterator.find} instead. - * + * * If the iterator is infinite and every element satisfies the condition, the function will never return. * * ```ts @@ -280,7 +280,7 @@ export default class SmartAsyncIterator implements A * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartAsyncIterator.find} instead. - * + * * If the iterator is infinite and no element satisfies the condition, the function will never return. * * ```ts @@ -318,7 +318,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); @@ -342,7 +342,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); @@ -356,7 +356,7 @@ export default class SmartAsyncIterator implements A * @template S * The type of the elements that satisfy the condition. * This allows the type-system to infer the correct type of the new iterator. - * + * * It must be a subtype of the original type of the iterator. * * @param predicate The condition to check for each element of the iterator. @@ -391,7 +391,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); @@ -433,7 +433,7 @@ export default class SmartAsyncIterator implements A * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each iteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * * The first accumulator value will be the first element of the iterator. * The last accumulator value will be the final result of the reduction. @@ -462,7 +462,7 @@ export default class SmartAsyncIterator implements A * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each iteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * * The first accumulator value will be the initial value provided. * The last accumulator value will be the final result of the reduction. @@ -517,7 +517,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); @@ -568,7 +568,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * Only the dropped elements will be consumed in the process. * The rest of the iterator will be consumed only once the new one is. @@ -621,7 +621,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * Only the taken elements will be consumed from the original iterator. * The rest of the original iterator will be available for further consumption. @@ -717,7 +717,7 @@ export default class SmartAsyncIterator implements A * @template S * The type of the element that satisfies the condition. * This allows the type-system to infer the correct type of the result. - * + * * It must be a subtype of the original type of the iterator. * * @param predicate The condition to check for each element of the iterator. @@ -749,7 +749,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator(["A", "M", "N", "Z"]); @@ -779,7 +779,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]); @@ -928,7 +928,7 @@ export default class SmartAsyncIterator implements A * for await (const value of iterator) * { * if (value > 5) { break; } // Closing the iterator... - * + * * console.log(value); // 1, 2, 3, 4, 5 * } * ``` @@ -1001,7 +1001,7 @@ export default class SmartAsyncIterator implements A * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * the new one is and that consuming one of them will consume also the other. + * the new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -1043,7 +1043,7 @@ export default class SmartAsyncIterator implements A * * console.log(result); // [0, 1, 2, 3, 4] * ``` - * + * * @returns A promise that will resolve to an array containing all elements of the iterator. */ public toArray(): Promise diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 05e9b57..e498c34 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -130,7 +130,7 @@ export default class SmartIterator implements Iterat * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartIterator.find} instead. - * + * * If the iterator is infinite and every element satisfies the condition, the function will never return. * * ```ts @@ -170,7 +170,7 @@ export default class SmartIterator implements Iterat * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartIterator.find} instead. - * + * * If the iterator is infinite and no element satisfies the condition, the function will never return. * * ```ts @@ -202,13 +202,17 @@ export default class SmartIterator implements Iterat } /** - * Filters the elements of the iterator using a given condition. + * Filters the elements of the iterator using a given condition. + * + * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the result. + * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); @@ -232,7 +236,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([-2, "-1", "0", 1, "2"]); @@ -246,7 +250,7 @@ export default class SmartIterator implements Iterat * @template S * The type of the elements that satisfy the condition. * This allows the type-system to infer the correct type of the new iterator. - * + * * It must be a subtype of the original type of the iterator. * * @param predicate The condition to check for each element of the iterator. @@ -281,7 +285,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); @@ -323,7 +327,7 @@ export default class SmartIterator implements Iterat * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each iteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * * The first accumulator value will be the first element of the iterator. * The last accumulator value will be the final result of the reduction. @@ -352,7 +356,7 @@ export default class SmartIterator implements Iterat * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each iteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * * The first accumulator value will be the initial value provided. * The last accumulator value will be the final result of the reduction. @@ -407,7 +411,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); @@ -457,7 +461,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * Only the dropped elements will be consumed in the process. * The rest of the iterator will be consumed only once the new one is. @@ -509,7 +513,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * Only the taken elements will be consumed from the original iterator. * The rest of the original iterator will be available for further consumption. @@ -550,7 +554,7 @@ export default class SmartIterator implements Iterat } /** - * Finds the first element of the iterator that satisfies a given condition. + * Finds the first element of the iterator that satisfies a given condition. * * The method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. @@ -579,7 +583,7 @@ export default class SmartIterator implements Iterat public find(predicate: Iteratee): T | undefined; /** - * Finds the first element of the iterator that satisfies a given condition. + * Finds the first element of the iterator that satisfies a given condition. * * The method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. @@ -604,7 +608,7 @@ export default class SmartIterator implements Iterat * @template S * The type of the element that satisfies the condition. * This allows the type-system to infer the correct type of the result. - * + * * It must be a subtype of the original type of the iterator. * * @param predicate The condition to check for each element of the iterator. @@ -636,7 +640,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator(["A", "M", "N", "Z"]); @@ -666,7 +670,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]); @@ -813,7 +817,7 @@ export default class SmartIterator implements Iterat * for (const value of iterator) * { * if (value > 5) { break; } // Closing the iterator... - * + * * console.log(value); // 1, 2, 3, 4, 5 * } * ``` @@ -885,7 +889,7 @@ export default class SmartIterator implements Iterat * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * the new one is and that consuming one of them will consume also the other. + * the new one is and that consuming one of them will consume the other as well. * * ```ts * const iterator = new SmartIterator([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -927,7 +931,7 @@ export default class SmartIterator implements Iterat * * console.log(result); // [0, 1, 2, 3, 4] * ``` - * + * * @returns The array containing all elements of the iterator. */ public toArray(): T[] diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index c8f16cd..19a3525 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -8,7 +8,7 @@ import { SmartIterator } from "../models/index.js"; * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * for (const value of chain([1, 2, 3], [4, 5, 6], [7, 8, 9])) @@ -74,7 +74,7 @@ export function count(elements: Iterable): number * * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the - * new one is and that consuming one of them will consume also the other. + * new one is and that consuming one of them will consume the other as well. * * ```ts * for (const [index, value] of enumerate(["A", "M", "N", "Z"])) From 8bf63d9a5b25ed3aba61de2599d2fe169e544be2 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 11 Jan 2025 14:09:54 +0100 Subject: [PATCH 22/32] =?UTF-8?q?upd:=20Updated=20dependencies.=20?= =?UTF-8?q?=F0=9F=94=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- pnpm-lock.yaml | 361 +++++++++++++++++++++++++------------------------ 2 files changed, 182 insertions(+), 181 deletions(-) diff --git a/package.json b/package.json index ec5b6e0..4e16c43 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@byloth/eslint-config-typescript": "^3.0.3", "@types/node": "^22.10.5", "husky": "^9.1.7", - "typescript": "^5.7.2", + "typescript": "^5.7.3", "vite": "^6.0.7" }, "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df69685..f9b1085 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: devDependencies: '@byloth/eslint-config-typescript': specifier: ^3.0.3 - version: 3.0.3(eslint@9.17.0)(typescript@5.7.2) + version: 3.0.3(eslint@9.18.0)(typescript@5.7.3) '@types/node': specifier: ^22.10.5 version: 22.10.5 @@ -18,8 +18,8 @@ importers: specifier: ^9.1.7 version: 9.1.7 typescript: - specifier: ^5.7.2 - version: 5.7.2 + specifier: ^5.7.3 + version: 5.7.3 vite: specifier: ^6.0.7 version: 6.0.7(@types/node@22.10.5) @@ -192,8 +192,8 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.2.4': - resolution: {integrity: sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==} + '@eslint/compat@1.2.5': + resolution: {integrity: sha512-5iuG/StT+7OfvhoBHPlmxkPA9om6aDUFgmD4+mWKAGsYt4vCe8rypneG03AuseyRHBmcCLXQtIH5S26tIoggLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.10.0 @@ -205,24 +205,24 @@ packages: resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.9.1': - resolution: {integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==} + '@eslint/core@0.10.0': + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.2.0': resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.17.0': - resolution: {integrity: sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==} + '@eslint/js@9.18.0': + resolution: {integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.5': resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.4': - resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==} + '@eslint/plugin-kit@0.2.5': + resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -257,98 +257,98 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@rollup/rollup-android-arm-eabi@4.29.1': - resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==} + '@rollup/rollup-android-arm-eabi@4.30.1': + resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.29.1': - resolution: {integrity: sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==} + '@rollup/rollup-android-arm64@4.30.1': + resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.29.1': - resolution: {integrity: sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==} + '@rollup/rollup-darwin-arm64@4.30.1': + resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.29.1': - resolution: {integrity: sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==} + '@rollup/rollup-darwin-x64@4.30.1': + resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.29.1': - resolution: {integrity: sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==} + '@rollup/rollup-freebsd-arm64@4.30.1': + resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.29.1': - resolution: {integrity: sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==} + '@rollup/rollup-freebsd-x64@4.30.1': + resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.29.1': - resolution: {integrity: sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==} + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.29.1': - resolution: {integrity: sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==} + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.29.1': - resolution: {integrity: sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==} + '@rollup/rollup-linux-arm64-gnu@4.30.1': + resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.29.1': - resolution: {integrity: sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==} + '@rollup/rollup-linux-arm64-musl@4.30.1': + resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.29.1': - resolution: {integrity: sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==} + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': - resolution: {integrity: sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==} + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.29.1': - resolution: {integrity: sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==} + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.29.1': - resolution: {integrity: sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==} + '@rollup/rollup-linux-s390x-gnu@4.30.1': + resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.29.1': - resolution: {integrity: sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==} + '@rollup/rollup-linux-x64-gnu@4.30.1': + resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.29.1': - resolution: {integrity: sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==} + '@rollup/rollup-linux-x64-musl@4.30.1': + resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.29.1': - resolution: {integrity: sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==} + '@rollup/rollup-win32-arm64-msvc@4.30.1': + resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.29.1': - resolution: {integrity: sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==} + '@rollup/rollup-win32-ia32-msvc@4.30.1': + resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.29.1': - resolution: {integrity: sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==} + '@rollup/rollup-win32-x64-msvc@4.30.1': + resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} cpu: [x64] os: [win32] @@ -361,51 +361,51 @@ packages: '@types/node@22.10.5': resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} - '@typescript-eslint/eslint-plugin@8.19.0': - resolution: {integrity: sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==} + '@typescript-eslint/eslint-plugin@8.19.1': + resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.19.0': - resolution: {integrity: sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==} + '@typescript-eslint/parser@8.19.1': + resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.19.0': - resolution: {integrity: sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==} + '@typescript-eslint/scope-manager@8.19.1': + resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.19.0': - resolution: {integrity: sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==} + '@typescript-eslint/type-utils@8.19.1': + resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.19.0': - resolution: {integrity: sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==} + '@typescript-eslint/types@8.19.1': + resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.19.0': - resolution: {integrity: sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==} + '@typescript-eslint/typescript-estree@8.19.1': + resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.19.0': - resolution: {integrity: sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==} + '@typescript-eslint/utils@8.19.1': + resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.19.0': - resolution: {integrity: sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==} + '@typescript-eslint/visitor-keys@8.19.1': + resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -496,8 +496,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.17.0: - resolution: {integrity: sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==} + eslint@9.18.0: + resolution: {integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -529,8 +529,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -728,8 +728,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.29.1: - resolution: {integrity: sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==} + rollup@4.30.1: + resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -765,18 +765,18 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} + ts-api-utils@2.0.0: + resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript@5.7.2: - resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true @@ -841,11 +841,11 @@ packages: snapshots: - '@byloth/eslint-config-typescript@3.0.3(eslint@9.17.0)(typescript@5.7.2)': + '@byloth/eslint-config-typescript@3.0.3(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@byloth/eslint-config': 3.0.3 - '@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) transitivePeerDependencies: - eslint - jiti @@ -854,9 +854,9 @@ snapshots: '@byloth/eslint-config@3.0.3': dependencies: - '@eslint/compat': 1.2.4(eslint@9.17.0) - '@eslint/js': 9.17.0 - eslint: 9.17.0 + '@eslint/compat': 1.2.5(eslint@9.18.0) + '@eslint/js': 9.18.0 + eslint: 9.18.0 globals: 15.14.0 transitivePeerDependencies: - jiti @@ -937,16 +937,16 @@ snapshots: '@esbuild/win32-x64@0.24.2': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.18.0)': dependencies: - eslint: 9.17.0 + eslint: 9.18.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.4(eslint@9.17.0)': + '@eslint/compat@1.2.5(eslint@9.18.0)': optionalDependencies: - eslint: 9.17.0 + eslint: 9.18.0 '@eslint/config-array@0.19.1': dependencies: @@ -956,7 +956,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/core@0.9.1': + '@eslint/core@0.10.0': dependencies: '@types/json-schema': 7.0.15 @@ -974,12 +974,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.17.0': {} + '@eslint/js@9.18.0': {} '@eslint/object-schema@2.1.5': {} - '@eslint/plugin-kit@0.2.4': + '@eslint/plugin-kit@0.2.5': dependencies: + '@eslint/core': 0.10.0 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -1007,61 +1008,61 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.18.0 - '@rollup/rollup-android-arm-eabi@4.29.1': + '@rollup/rollup-android-arm-eabi@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.29.1': + '@rollup/rollup-android-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.29.1': + '@rollup/rollup-darwin-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.29.1': + '@rollup/rollup-darwin-x64@4.30.1': optional: true - '@rollup/rollup-freebsd-arm64@4.29.1': + '@rollup/rollup-freebsd-arm64@4.30.1': optional: true - '@rollup/rollup-freebsd-x64@4.29.1': + '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.29.1': + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.29.1': + '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.29.1': + '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.29.1': + '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.29.1': + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.29.1': + '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.29.1': + '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.29.1': + '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.29.1': + '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.29.1': + '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.29.1': + '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.29.1': + '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true '@types/estree@1.0.6': {} @@ -1072,81 +1073,81 @@ snapshots: dependencies: undici-types: 6.20.0 - '@typescript-eslint/eslint-plugin@8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.19.0(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.19.0 - '@typescript-eslint/type-utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.19.0 - eslint: 9.17.0 + '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/type-utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.19.1 + eslint: 9.18.0 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.19.0(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.19.0 - '@typescript-eslint/types': 8.19.0 - '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.19.0 + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.19.1 debug: 4.4.0 - eslint: 9.17.0 - typescript: 5.7.2 + eslint: 9.18.0 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.19.0': + '@typescript-eslint/scope-manager@8.19.1': dependencies: - '@typescript-eslint/types': 8.19.0 - '@typescript-eslint/visitor-keys': 8.19.0 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/visitor-keys': 8.19.1 - '@typescript-eslint/type-utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/type-utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) - '@typescript-eslint/utils': 8.19.0(eslint@9.17.0)(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) debug: 4.4.0 - eslint: 9.17.0 - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + eslint: 9.18.0 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.19.0': {} + '@typescript-eslint/types@8.19.1': {} - '@typescript-eslint/typescript-estree@8.19.0(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.19.0 - '@typescript-eslint/visitor-keys': 8.19.0 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/visitor-keys': 8.19.1 debug: 4.4.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.7.2) - typescript: 5.7.2 + ts-api-utils: 2.0.0(typescript@5.7.3) + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.19.0(eslint@9.17.0)(typescript@5.7.2)': + '@typescript-eslint/utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0) - '@typescript-eslint/scope-manager': 8.19.0 - '@typescript-eslint/types': 8.19.0 - '@typescript-eslint/typescript-estree': 8.19.0(typescript@5.7.2) - eslint: 9.17.0 - typescript: 5.7.2 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) + '@typescript-eslint/scope-manager': 8.19.1 + '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + eslint: 9.18.0 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.19.0': + '@typescript-eslint/visitor-keys@8.19.1': dependencies: - '@typescript-eslint/types': 8.19.0 + '@typescript-eslint/types': 8.19.1 eslint-visitor-keys: 4.2.0 acorn-jsx@5.3.2(acorn@8.14.0): @@ -1249,15 +1250,15 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.17.0: + eslint@9.18.0: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.1 - '@eslint/core': 0.9.1 + '@eslint/core': 0.10.0 '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.17.0 - '@eslint/plugin-kit': 0.2.4 + '@eslint/js': 9.18.0 + '@eslint/plugin-kit': 0.2.5 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.1 @@ -1308,7 +1309,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -1475,29 +1476,29 @@ snapshots: reusify@1.0.4: {} - rollup@4.29.1: + rollup@4.30.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.29.1 - '@rollup/rollup-android-arm64': 4.29.1 - '@rollup/rollup-darwin-arm64': 4.29.1 - '@rollup/rollup-darwin-x64': 4.29.1 - '@rollup/rollup-freebsd-arm64': 4.29.1 - '@rollup/rollup-freebsd-x64': 4.29.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.29.1 - '@rollup/rollup-linux-arm-musleabihf': 4.29.1 - '@rollup/rollup-linux-arm64-gnu': 4.29.1 - '@rollup/rollup-linux-arm64-musl': 4.29.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.29.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.29.1 - '@rollup/rollup-linux-riscv64-gnu': 4.29.1 - '@rollup/rollup-linux-s390x-gnu': 4.29.1 - '@rollup/rollup-linux-x64-gnu': 4.29.1 - '@rollup/rollup-linux-x64-musl': 4.29.1 - '@rollup/rollup-win32-arm64-msvc': 4.29.1 - '@rollup/rollup-win32-ia32-msvc': 4.29.1 - '@rollup/rollup-win32-x64-msvc': 4.29.1 + '@rollup/rollup-android-arm-eabi': 4.30.1 + '@rollup/rollup-android-arm64': 4.30.1 + '@rollup/rollup-darwin-arm64': 4.30.1 + '@rollup/rollup-darwin-x64': 4.30.1 + '@rollup/rollup-freebsd-arm64': 4.30.1 + '@rollup/rollup-freebsd-x64': 4.30.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 + '@rollup/rollup-linux-arm-musleabihf': 4.30.1 + '@rollup/rollup-linux-arm64-gnu': 4.30.1 + '@rollup/rollup-linux-arm64-musl': 4.30.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 + '@rollup/rollup-linux-riscv64-gnu': 4.30.1 + '@rollup/rollup-linux-s390x-gnu': 4.30.1 + '@rollup/rollup-linux-x64-gnu': 4.30.1 + '@rollup/rollup-linux-x64-musl': 4.30.1 + '@rollup/rollup-win32-arm64-msvc': 4.30.1 + '@rollup/rollup-win32-ia32-msvc': 4.30.1 + '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -1524,15 +1525,15 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@1.4.3(typescript@5.7.2): + ts-api-utils@2.0.0(typescript@5.7.3): dependencies: - typescript: 5.7.2 + typescript: 5.7.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typescript@5.7.2: {} + typescript@5.7.3: {} undici-types@6.20.0: {} @@ -1544,7 +1545,7 @@ snapshots: dependencies: esbuild: 0.24.2 postcss: 8.4.49 - rollup: 4.29.1 + rollup: 4.30.1 optionalDependencies: '@types/node': 22.10.5 fsevents: 2.3.3 From 5e16ed05ac8296b8daa020c8b8b0f67999c059d2 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 11 Jan 2025 18:51:49 +0100 Subject: [PATCH 23/32] wip: Added some minor docs... --- src/models/aggregators/aggregated-iterator.ts | 95 +++++++++++-------- src/models/iterators/smart-async-iterator.ts | 18 +++- src/models/iterators/smart-iterator.ts | 16 +++- src/utils/iterator.ts | 4 +- 4 files changed, 84 insertions(+), 49 deletions(-) diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index edaae9f..040ca9b 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -23,15 +23,11 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ * then perform specific operations on the groups themselves. * * ```ts - * import { range, Random } from "@byloth/core"; + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .count(); * - * const iterator: SmartIterator = range(10).map(() => Random.Integer(10) + 1); - * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .count() - * .toObject(); - * - * if (odd > even) { console.log("There are more odd numbers."); } - * else { console.log("There are more even numbers."); } + * console.log(results.toObject()); // { odd: 4, even: 4 } * ``` * * --- @@ -134,15 +130,11 @@ export default class AggregatedIterator * If the iterator is infinite, the function will never return. * * ```ts - * import { range, Random } from "@byloth/core"; - * - * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); - * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .every((value) => value >= 0) - * .toObject(); + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .every((value) => value >= 0); * - * if (even) { console.log("All even numbers are positive."); } - * if (odd) { console.log("All odd numbers are positive."); } + * console.log(results.toObject()); // { odd: false, even: true } * ``` * * --- @@ -183,22 +175,18 @@ export default class AggregatedIterator * If the iterator is infinite, the function will never return. * * ```ts - * import { range, Random } from "@byloth/core"; - * - * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); - * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .some((value) => value >= 0) - * .toObject(); + * const results = new SmartIterator([-5, -4, -3, -2, -1, 0]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .some((value) => value >= 0); * - * if (even) { console.log("At least one even number is positive."); } - * if (odd) { console.log("At least one odd number is positive."); } + * console.log(results.toObject()); // { odd: false, even: true } * ``` * * --- * * @param predicate The condition to check for each element of the iterator. * - * @returns `true` if any element satisfies the condition, `false` otherwise. + * @returns A {@link ReducedIterator} object with the boolean results for each group. */ public some(predicate: KeyedIteratee): ReducedIterator { @@ -221,6 +209,10 @@ export default class AggregatedIterator /** * Filters the elements of the iterator using a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. + * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * @@ -228,32 +220,55 @@ export default class AggregatedIterator * This means that the original iterator won't be consumed until the * new one is and that consuming one of them will consume the other as well. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the result. + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .filter((value) => value >= 0); * - * Eventually, it will return a new {@link AggregatedIterator} - * object that will contain all the elements that satisfy the condition. - * If the iterator is infinite, the function will never return. + * console.log(results.toObject()); // { odd: [3, 5], even: [0, 2, 6, 8] } + * ``` * - * ```ts - * import { range, Random } from "@byloth/core"; + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link AggregatedIterator} with the elements that satisfy the condition. + */ + public filter(predicate: KeyedIteratee): AggregatedIterator; + + /** + * Filters the elements of the iterator using a given condition. * - * const iterator: SmartIterator = range(10).map(() => Random.Integer(-5, 5)); - * const { odd, even } = iterator.groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .filter((value) => value >= 0) - * .toObject(); + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. * - * console.log("Even numbers:", even); - * console.log("Odd numbers:", odd); + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, "-1", 0, "2", "3", 5, 6, "8"]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .filter((value) => typeof value === "number"); + * + * console.log(results.toObject()); // { odd: [-3, 5], even: [0, 6] } * ``` * * --- * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the iterator. + * * @param predicate The condition to check for each element of the iterator. * - * @returns A new iterator with the elements that satisfy the condition. + * @returns A new {@link AggregatedIterator} with the elements that satisfy the condition. */ - public filter(predicate: KeyedIteratee): AggregatedIterator; public filter(predicate: KeyedTypeGuardPredicate): AggregatedIterator; public filter(predicate: KeyedIteratee): AggregatedIterator { diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index b3742cd..72a9543 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -312,7 +312,11 @@ export default class SmartAsyncIterator implements A } /** - * Filters the elements of the iterator using a given condition. + * Filters the elements of the iterator using a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. + * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * @@ -336,7 +340,11 @@ export default class SmartAsyncIterator implements A public filter(predicate: MaybeAsyncIteratee): SmartAsyncIterator; /** - * Filters the elements of the iterator using a given condition. + * Filters the elements of the iterator using a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. + * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * @@ -385,7 +393,11 @@ export default class SmartAsyncIterator implements A } /** - * Maps the elements of the iterator using a given transformation function. + * Maps the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be included in the new iterator. + * * Since the iterator is lazy, the mapping process will * be executed once the resulting iterator is materialized. * diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index e498c34..4b1ff7b 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -204,8 +204,8 @@ export default class SmartIterator implements Iterat /** * Filters the elements of the iterator using a given condition. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the result. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. @@ -230,7 +230,11 @@ export default class SmartIterator implements Iterat public filter(predicate: Iteratee): SmartIterator; /** - * Filters the elements of the iterator using a given condition. + * Filters the elements of the iterator using a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is satisfied, the element will be included in the new iterator. + * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * @@ -279,7 +283,11 @@ export default class SmartIterator implements Iterat } /** - * Maps the elements of the iterator using a given transformation function. + * Maps the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be included in the new iterator. + * * Since the iterator is lazy, the mapping process will * be executed once the resulting iterator is materialized. * diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index 19a3525..0effb1a 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -23,7 +23,7 @@ import { SmartIterator } from "../models/index.js"; * * @param iterables The list of iterables to chain. * - * @returns A {@link SmartIterator} object that chains the iterables into a single one. + * @returns A new {@link SmartIterator} object that chains the iterables into a single one. */ export function chain(...iterables: Iterable[]): SmartIterator { @@ -89,7 +89,7 @@ export function count(elements: Iterable): number * * @param elements The iterable to enumerate. * - * @returns A {@link SmartIterator} object that enumerates the elements of the given iterable. + * @returns A new {@link SmartIterator} object that enumerates the elements of the given iterable. */ export function enumerate(elements: Iterable): SmartIterator<[number, T]> { From e0fbd6908e47ade908e8fd5e8e8eaba7c70a84c7 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Fri, 17 Jan 2025 07:39:05 +0100 Subject: [PATCH 24/32] wip: Added some other docs... --- .../aggregators/aggregated-async-iterator.ts | 11 +- src/models/aggregators/aggregated-iterator.ts | 114 ++++++++++++++++-- src/models/iterators/smart-async-iterator.ts | 8 +- src/models/iterators/smart-iterator.ts | 8 +- 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index 51f460b..e651f2f 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -103,10 +103,13 @@ export default class AggregatedAsyncIterator }); } public async reduce(reducer: MaybeAsyncKeyedReducer): Promise>; + public async reduce(reducer: MaybeAsyncKeyedReducer, initialValue: MaybePromise) + : Promise>; public async reduce(reducer: MaybeAsyncKeyedReducer, initialValue: (key: K) => MaybePromise) : Promise>; - public async reduce(reducer: MaybeAsyncKeyedReducer, initialValue?: (key: K) => MaybePromise) - : Promise> + public async reduce( + reducer: MaybeAsyncKeyedReducer, initialValue?: MaybePromise | ((key: K) => MaybePromise) + ): Promise> { const values = new Map(); @@ -119,7 +122,9 @@ export default class AggregatedAsyncIterator else if (initialValue !== undefined) { index = 0; - accumulator = await initialValue(key); + + if (initialValue instanceof Function) { accumulator = await initialValue(key); } + else { accumulator = await initialValue; } } else { diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 040ca9b..e825359 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -121,7 +121,7 @@ export default class AggregatedIterator * Determines whether all elements of each group of the iterator satisfy a given condition. * See also {@link AggregatedIterator.some}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group doesn't satisfy the condition, * the result for the respective group will set to `false`. * @@ -132,7 +132,7 @@ export default class AggregatedIterator * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .every((value) => value >= 0); + * .every((key, value) => value >= 0); * * console.log(results.toObject()); // { odd: false, even: true } * ``` @@ -166,7 +166,7 @@ export default class AggregatedIterator * Determines whether any elements of each group of the iterator satisfy a given condition. * See also {@link AggregatedIterator.every}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group satisfies the condition, * the result for the respective group will set to `true`. * @@ -177,7 +177,7 @@ export default class AggregatedIterator * ```ts * const results = new SmartIterator([-5, -4, -3, -2, -1, 0]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .some((value) => value >= 0); + * .some((key, value) => value >= 0); * * console.log(results.toObject()); // { odd: false, even: true } * ``` @@ -186,7 +186,7 @@ export default class AggregatedIterator * * @param predicate The condition to check for each element of the iterator. * - * @returns A {@link ReducedIterator} object with the boolean results for each group. + * @returns A {@link ReducedIterator} object containing the boolean results for each group. */ public some(predicate: KeyedIteratee): ReducedIterator { @@ -223,7 +223,7 @@ export default class AggregatedIterator * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .filter((value) => value >= 0); + * .filter((key, value) => value >= 0); * * console.log(results.toObject()); // { odd: [3, 5], even: [0, 2, 6, 8] } * ``` @@ -232,7 +232,7 @@ export default class AggregatedIterator * * @param predicate The condition to check for each element of the iterator. * - * @returns A new {@link AggregatedIterator} with the elements that satisfy the condition. + * @returns A new {@link AggregatedIterator} containing only the elements that satisfy the condition. */ public filter(predicate: KeyedIteratee): AggregatedIterator; @@ -252,7 +252,7 @@ export default class AggregatedIterator * ```ts * const results = new SmartIterator([-3, "-1", 0, "2", "3", 5, 6, "8"]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .filter((value) => typeof value === "number"); + * .filter((key, value) => typeof value === "number"); * * console.log(results.toObject()); // { odd: [-3, 5], even: [0, 6] } * ``` @@ -267,7 +267,7 @@ export default class AggregatedIterator * * @param predicate The condition to check for each element of the iterator. * - * @returns A new {@link AggregatedIterator} with the elements that satisfy the condition. + * @returns A new {@link AggregatedIterator} containing only the elements that satisfy the condition. */ public filter(predicate: KeyedTypeGuardPredicate): AggregatedIterator; public filter(predicate: KeyedIteratee): AggregatedIterator @@ -288,6 +288,36 @@ export default class AggregatedIterator } }); } + + /** + * Maps the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be included in the new iterator. + * + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map((key, value) => Math.abs(value)); + * + * console.log(results.toObject()); // { odd: [3, 1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link AggregatedIterator} containing the transformed elements. + */ public map(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements; @@ -306,9 +336,69 @@ export default class AggregatedIterator } }); } + + /** + * Reduces the elements of the iterator using a given reducer function. + * + * This method will iterate over all elements of the iterator applying the reducer function. + * The result of each riteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the first element of the iterator. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the function will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value); + * + * console.log(results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @param reducer The reducer function to apply to each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ public reduce(reducer: KeyedReducer): ReducedIterator; + + /** + * Reduces the elements of the iterator using a given reducer function. + * + * This method will iterate over all elements of the iterator applying the reducer function. + * The result of each riteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the initial value provided. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the function will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value, 0); + * + * console.log(results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the type of the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The initial value of the accumulator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ + public reduce(reducer: KeyedReducer, initialValue: A): ReducedIterator; public reduce(reducer: KeyedReducer, initialValue: (key: K) => A): ReducedIterator; - public reduce(reducer: KeyedReducer, initialValue?: (key: K) => A): ReducedIterator + public reduce(reducer: KeyedReducer, initialValue?: A | ((key: K) => A)): ReducedIterator { const values = new Map(); @@ -321,7 +411,9 @@ export default class AggregatedIterator else if (initialValue !== undefined) { index = 0; - accumulator = initialValue(key); + + if (initialValue instanceof Function) { accumulator = initialValue(key); } + else { accumulator = initialValue; } } else { diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 72a9543..e8e545e 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -233,7 +233,7 @@ export default class SmartAsyncIterator implements A * Determines whether all elements of the iterator satisfy a given condition. * See also {@link SmartAsyncIterator.some}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element doesn't satisfy the condition, the method will return `false` immediately. * * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. @@ -274,7 +274,7 @@ export default class SmartAsyncIterator implements A * Determines whether any element of the iterator satisfies a given condition. * See also {@link SmartAsyncIterator.every}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element satisfies the condition, the method will return `true` immediately. * * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. @@ -677,7 +677,7 @@ export default class SmartAsyncIterator implements A /** * Finds the first element of the iterator that satisfies a given condition. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first @@ -706,7 +706,7 @@ export default class SmartAsyncIterator implements A /** * Finds the first element of the iterator that satisfies a given condition. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 4b1ff7b..4f0856b 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -124,7 +124,7 @@ export default class SmartIterator implements Iterat /** * Determines whether all elements of the iterator satisfy a given condition. See also {@link SmartIterator.some}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element doesn't satisfy the condition, the method will return `false` immediately. * * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. @@ -164,7 +164,7 @@ export default class SmartIterator implements Iterat /** * Determines whether any element of the iterator satisfies a given condition. See also {@link SmartIterator.every}. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element satisfies the condition, the method will return `true` immediately. * * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. @@ -564,7 +564,7 @@ export default class SmartIterator implements Iterat /** * Finds the first element of the iterator that satisfies a given condition. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first @@ -593,7 +593,7 @@ export default class SmartIterator implements Iterat /** * Finds the first element of the iterator that satisfies a given condition. * - * The method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first From 3d0eaf56f97e6f5fe7b96bb59c2a085ff634d41e Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Mon, 20 Jan 2025 17:51:51 +0100 Subject: [PATCH 25/32] add: Minor comments. --- src/models/aggregators/aggregated-iterator.ts | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index e825359..4b7a14f 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -118,10 +118,11 @@ export default class AggregatedIterator } /** - * Determines whether all elements of each group of the iterator satisfy a given condition. - * See also {@link AggregatedIterator.some}. + * Determines whether all elements of each group of the iterator satisfy a given condition. + * See also {@link AggregatedIterator.some}. + * This method will consume the entire iterator in the process. * - * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * It will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group doesn't satisfy the condition, * the result for the respective group will set to `false`. * @@ -164,9 +165,10 @@ export default class AggregatedIterator /** * Determines whether any elements of each group of the iterator satisfy a given condition. - * See also {@link AggregatedIterator.every}. + * See also {@link AggregatedIterator.every}. + * This method will consume the entire iterator in the process. * - * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * It will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group satisfies the condition, * the result for the respective group will set to `true`. * @@ -338,9 +340,10 @@ export default class AggregatedIterator } /** - * Reduces the elements of the iterator using a given reducer function. + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. * - * This method will iterate over all elements of the iterator applying the reducer function. + * It will iterate over all elements of the iterator applying the reducer function. * The result of each riteration will be passed as the accumulator to the next one. * * The first accumulator value will be the first element of the iterator. @@ -367,9 +370,10 @@ export default class AggregatedIterator public reduce(reducer: KeyedReducer): ReducedIterator; /** - * Reduces the elements of the iterator using a given reducer function. + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. * - * This method will iterate over all elements of the iterator applying the reducer function. + * It will iterate over all elements of the iterator applying the reducer function. * The result of each riteration will be passed as the accumulator to the next one. * * The first accumulator value will be the initial value provided. @@ -397,6 +401,38 @@ export default class AggregatedIterator * @returns A new {@link ReducedIterator} containing the reduced results for each group. */ public reduce(reducer: KeyedReducer, initialValue: A): ReducedIterator; + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each riteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the initial value provided by the given function. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the function will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, { value }, currentValue) => ({ value: value + currentValue }), (key) => ({ value: 0 })); + * + * console.log(results.toObject()); // { odd: { value: 4 }, even: { value: 16 } } + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the type of the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The function that provides the initial value of the accumulator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ public reduce(reducer: KeyedReducer, initialValue: (key: K) => A): ReducedIterator; public reduce(reducer: KeyedReducer, initialValue?: A | ((key: K) => A)): ReducedIterator { From 6a5860f7d26d78cdad376fbe5292ed49c775ce0d Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 21 Jan 2025 01:09:42 +0100 Subject: [PATCH 26/32] add: Missing JSDoc for `models/aggregators/aggregated[-async]-iterator` file. + Various JSDoc improvements. --- .../aggregators/aggregated-async-iterator.ts | 779 +++++++++++++++++- src/models/aggregators/aggregated-iterator.ts | 441 +++++++++- src/models/aggregators/reduced-iterator.ts | 2 +- src/models/callbacks/switchable-callback.ts | 2 +- src/models/exceptions/core.ts | 2 +- src/models/game-loop.ts | 2 +- src/models/iterators/smart-async-iterator.ts | 55 +- src/models/iterators/smart-iterator.ts | 59 +- src/models/iterators/types.ts | 2 +- src/models/json/json-storage.ts | 4 +- src/models/promises/smart-promise.ts | 2 +- src/models/promises/timed-promise.ts | 2 +- src/models/timers/countdown.ts | 4 +- src/utils/math.ts | 2 +- src/utils/random.ts | 4 +- 15 files changed, 1266 insertions(+), 96 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index e651f2f..a6d1e22 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -11,22 +11,207 @@ import type { MaybePromise } from "../types.js"; import ReducedIterator from "./reduced-iterator.js"; import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js"; +/** + * A class representing an iterator that aggregates elements in a lazy and optimized way. + * + * It's part of the {@link SmartAsyncIterator} implementation, + * providing a way to group elements of an iterable by key. + * For this reason, it isn't recommended to instantiate this class directly + * (although it's still possible), but rather use the {@link SmartAsyncIterator.groupBy} method. + * + * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate. + * See the {@link AggregatedAsyncIterator.keys}, {@link AggregatedAsyncIterator.items} + * & {@link AggregatedAsyncIterator.values} methods. + * It does, however, provide the same set of methods to perform + * operations and transformations on the elements of the iterator, + * having also the knowledge and context of the groups to which + * they belong, allowing to handle them in a grouped manner. + * + * This is particularly useful when you need to group elements and + * then perform specific operations on the groups themselves. + * + * ```ts + * const elements = fetch([...]); // Promise<[-3, -1, 0, 2, 3, 5, 6, 8]>; + * const results = new SmartAsyncIterator(elements) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .count(); + * + * console.log(await results.toObject()); // { odd: 4, even: 4 } + * ``` + * + * --- + * + * @template K The type of the keys used to group the elements. + * @template T The type of the elements to aggregate. + */ export default class AggregatedAsyncIterator { + /** + * The internal {@link SmartAsyncIterator} object that holds the elements to aggregate. + */ protected _elements: SmartAsyncIterator<[K, T]>; + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * const iterator = new AggregatedAsyncIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); + * ``` + * + * --- + * + * @param iterable The iterable to aggregate. + */ public constructor(iterable: Iterable<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * const elements = fetch([...]); // Promise<[["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]> + * const iterator = new AggregatedAsyncIterator(elements); + * ``` + * + * --- + * + * @param iterable The iterable to aggregate. + */ public constructor(iterable: AsyncIterable<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * import { Random } from "@byloth/core"; + * + * const iterator = new AggregatedAsyncIterator({ + * _index: 0, + * next: () => + * { + * if (this._index >= 5) { return { done: true, value: undefined }; } + * this._index += 1; + * + * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * } + * }); + * ``` + * + * --- + * + * @param iterator The iterator to aggregate. + */ public constructor(iterator: Iterator<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * import { Random } from "@byloth/core"; + * + * const iterator = new AggregatedAsyncIterator({ + * _index: 0, + * next: async () => + * { + * if (this._index >= 5) { return { done: true, value: undefined }; } + * this._index += 1; + * + * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * } + * }); + * ``` + * + * --- + * + * @param iterator The iterator to aggregate. + */ public constructor(iterator: AsyncIterator<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator = new AggregatedAsyncIterator(function* () + * { + * for (const index of range(5)) + * { + * yield [Random.Choice(["A", "B", "C"]), (index + 1)]; + * } + * }); + * ``` + * + * --- + * + * @param generatorFn The generator function to aggregate. + */ public constructor(generatorFn: GeneratorFunction<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const iterator = new AggregatedAsyncIterator(async function* () + * { + * for await (const index of range(5)) + * { + * yield [Random.Choice(["A", "B", "C"]), (index + 1)]; + * } + * }); + * ``` + * + * --- + * + * @param generatorFn The generator function to aggregate. + */ public constructor(generatorFn: AsyncGeneratorFunction<[K, T]>); + + /** + * Initializes a new instance of the {@link AggregatedAsyncIterator} class. + * + * ```ts + * const iterator = new AggregatedAsyncIterator(asyncKeyedValues); + * ``` + * + * --- + * + * @param argument The iterable, iterator or generator function to aggregate. + */ public constructor(argument: MaybeAsyncIteratorLike<[K, T]> | MaybeAsyncGeneratorFunction<[K, T]>); public constructor(argument: MaybeAsyncIteratorLike<[K, T]> | MaybeAsyncGeneratorFunction<[K, T]>) { this._elements = new SmartAsyncIterator(argument); } + /** + * Determines whether all elements of each group of the iterator satisfy a given condition. + * See also {@link AggregatedAsyncIterator.some}. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checjing if they satisfy the condition. + * Once a single element of one group doesn't satisfy the condition, + * the result for the respective group will be `false`. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the boolean results for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .every(async (key, value) => value >= 0); + * + * console.log(await results.toObject()); // { odd: false, even: true } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the boolean results for each group. + */ public async every(predicate: MaybeAsyncKeyedIteratee): Promise> { const values = new Map(); @@ -45,6 +230,34 @@ export default class AggregatedAsyncIterator for (const [key, [_, result]] of values) { yield [key, result]; } }); } + + /** + * Determines whether any element of each group of the iterator satisfies a given condition. + * See also {@link AggregatedAsyncIterator.every}. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checjing if they satisfy the condition. + * Once a single element of one group satisfies the condition, + * the result for the respective group will be `true`. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the boolean results for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-5, -4, -3, -2, -1, 0]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .some(async (key, value) => value >= 0); + * + * console.log(await results.toObject()); // { odd: false, even: true } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the boolean results for each group. + */ public async some(predicate: MaybeAsyncKeyedIteratee): Promise> { const values = new Map(); @@ -64,7 +277,62 @@ export default class AggregatedAsyncIterator }); } + /** + * Filters the elements of the iterator based on a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is met, the element will be included in the new iterator. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .filter(async (key, value) => value >= 0); + * + * console.log(await results.toObject()); // { odd: [3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link AggregatedAsyncIterator} containing the elements that satisfy the condition. + */ public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator; + + /** + * Filters the elements of the iterator based on a given condition. + * + * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * If the condition is met, the element will be included in the new iterator. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, "-1", 0, "2", "3", 5, 6, "8"]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .filter(async (key, value) => typeof value === "number"); + * + * console.log(await results.toObject()); // { odd: [-3, 5], even: [0, 6] } + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the elements. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link AggregatedAsyncIterator} containing the elements that satisfy the condition. + */ public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator; public filter(predicate: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator { @@ -84,6 +352,36 @@ export default class AggregatedAsyncIterator } }); } + + /** + * Maps the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the condition. + * The result of each transformation will be included in the new iterator. + * + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .map(async (key, value) => Math.abs(value)); + * + * console.log(await results.toObject()); // { odd: [3, 1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link AggregatedAsyncIterator} containing the transformed elements. + */ public map(iteratee: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator { const elements = this._elements; @@ -102,9 +400,102 @@ export default class AggregatedAsyncIterator } }); } + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accoumulator value will be the first element of the iterator. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .reduce(async (key, accumulator, value) => accumulator + value); + * + * console.log(await results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @param reducer The reducer function to apply to each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ public async reduce(reducer: MaybeAsyncKeyedReducer): Promise>; + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accoumulator value will be the provided initial value. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .reduce(async (key, accumulator, value) => accumulator + value, 0); + * + * console.log(await results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The initial value for the accumulator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ public async reduce(reducer: MaybeAsyncKeyedReducer, initialValue: MaybePromise) : Promise>; + + /** + * Reduces the elements of the iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accoumulator value will be the provided initial value by the given function. + * The last accumulator value will be the final result of the reduction. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .reduce(async (key, { value }, currentValue) => ({ value: value + currentValue }), (key) => ({ value: 0 })); + * + * console.log(await results.toObject()); // { odd: { value: 4 }, even: { value: 16 } } + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the final result of the reduction. + * + * @param reducer The reducer function to apply to each element of the iterator. + * @param initialValue The function that provides the initial value for the accumulator. + * + * @returns A new {@link ReducedIterator} containing the reduced results for each group. + */ public async reduce(reducer: MaybeAsyncKeyedReducer, initialValue: (key: K) => MaybePromise) : Promise>; public async reduce( @@ -142,6 +533,35 @@ export default class AggregatedAsyncIterator }); } + /** + * Flattens the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be included in the new iterator. + * + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([[-3, -1], [0, 2], [3, 5], [6, 8]]) + * .groupBy(async ([value, _]) => value % 2 === 0 ? "even" : "odd") + * .flatMap(async (key, values) => values); + * + * console.log(await results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link AggregatedAsyncIterator} containing the transformed elements. + */ public flatMap(iteratee: MaybeAsyncKeyedIteratee>): AggregatedAsyncIterator { const elements = this._elements; @@ -162,6 +582,32 @@ export default class AggregatedAsyncIterator }); } + /** + * Drops a given number of elements from the beginning of each group of the iterator. + * The remaining elements will be included in the new iterator. + * See also {@link AggregatedAsyncIterator.take}. + * + * Since the iterator is lazy, the dropping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .drop(2); + * + * console.log(await results.toObject()); // { odd: [3, 5], even: [6, 8] } + * ``` + * + * --- + * + * @param count The number of elements to drop from the beginning of each group. + * + * @returns A new {@link AggregatedAsyncIterator} containing the remaining elements. + */ public drop(count: number): AggregatedAsyncIterator { const elements = this._elements; @@ -184,6 +630,33 @@ export default class AggregatedAsyncIterator } }); } + + /** + * Takes a given number of elements from the beginning of each group of the iterator. + * The elements will be included in the new iterator. + * See also {@link AggregatedAsyncIterator.drop}. + * + * Since the iterator is lazy, the taking process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .take(2); + * + * console.log(await results.toObject()); // { odd: [-3, -1], even: [0, 2] } + * ``` + * + * --- + * + * @param count The number of elements to take from the beginning of each group. + * + * @returns A new {@link AggregatedAsyncIterator} containing the taken elements. + */ public take(limit: number): AggregatedAsyncIterator { const elements = this._elements; @@ -204,7 +677,66 @@ export default class AggregatedAsyncIterator }); } + /** + * Finds the first element of each group of the iterator that satisfies a given condition. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checking if they satisfy the condition. + * Once the first element of one group satisfies the condition, + * the result for the respective group will be the element itself. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain the first element that satisfies the condition for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .find(async (key, value) => value > 0); + * + * console.log(await results.toObject()); // { odd: 3, even: 2 } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. + */ public async find(predicate: MaybeAsyncKeyedIteratee): Promise>; + + /** + * Finds the first element of each group of the iterator that satisfies a given condition. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checking if they satisfy the condition. + * Once the first element of one group satisfies the condition, + * the result for the respective group will be the element itself. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain the first element that satisfies the condition for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, "-1", 0, "2", "3", 5, 6, "8"]) + * .groupBy(async (value) => Number(value) % 2 === 0 ? "even" : "odd") + * .find(async (key, value) => typeof value === "number"); + * + * console.log(await results.toObject()); // { odd: -3, even: 0 } + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the elements. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. + */ public async find(predicate: MaybeAsyncKeyedIteratee) : Promise>; @@ -228,6 +760,57 @@ export default class AggregatedAsyncIterator }); } + /** + * Enumerates the elements of the iterator. + * Each element is paired with its index within the group in the new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, 0, 2, -1, 3]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .enumerate(); + * + * console.log(results.toObject()); // { odd: [[0, -3], [1, -1], [2, 3]], even: [[0, 0], [1, 2]] } + * ``` + * + * --- + * + * @returns A new {@link AggregatedAsyncIterator} containing the enumerated elements. + */ + public enumerate(): AggregatedAsyncIterator + { + return this.map((key, value, index) => [index, value]); + } + + /** + * Removes all duplicate elements from within each group of the iterator. + * The first occurrence of each element will be included in the new iterator. + * + * Since the iterator is lazy, the uniqueness process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 6, -3, -1, 0, 5, 6, 8, 0, 2]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .unique(); + * + * console.log(await results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns A new {@link AggregatedAsyncIterator} containing only the unique elements. + */ public unique(): AggregatedAsyncIterator { const elements = this._elements; @@ -250,6 +833,24 @@ export default class AggregatedAsyncIterator }); } + /** + * Counts the number of elements within each group of the iterator. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .count(); + * + * console.log(await results.toObject()); // { odd: 4, even: 4 } + * ``` + * + * --- + * + * @returns A new {@link ReducedIterator} containing the number of elements for each group. + */ public async count(): Promise> { const counters = new Map(); @@ -267,6 +868,27 @@ export default class AggregatedAsyncIterator }); } + /** + * Iterates over the elements of the iterator. + * The elements are passed to the given iteratee function along with their key and index within the group. + * + * This method will consume the entire iterator in the process. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartAsyncIterator([-3, 0, 2, -1, 3]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd"); + * + * await aggregator.forEach(async (key, value, index) => + * { + * console.log(`${index}: ${value}`); // "0: -3", "0: 0", "1: 2", "1: -1", "2: 3" + * }; + * ``` + * + * --- + * + * @param iteratee The function to execute for each element of the iterator. + */ public async forEach(iteratee: MaybeAsyncKeyedIteratee): Promise { const indexes = new Map(); @@ -275,13 +897,42 @@ export default class AggregatedAsyncIterator { const index = indexes.get(key) ?? 0; - iteratee(key, element, index); + await iteratee(key, element, index); indexes.set(key, index + 1); } } - public rekey(iteratee: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator + /** + * Changes the key of each element on which the iterator is aggregated. + * The new key is determined by the given iteratee function. + * + * Since the iterator is lazy, the reorganization process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .map(async (key, value, index) => index % 2 === 0 ? value : -value) + * .reorganizeBy(async (key, value) => value >= 0 ? "+" : "-"); + * + * console.log(await results.toObject()); // { "+": [1, 0, 3, 6], "-": [-3, -2, -5, -8] } + * ``` + * + * --- + * + * @template J The type of the new key. + * + * @param iteratee The function to determine the new key for each element of the iterator. + * + * @returns A new {@link AggregatedAsyncIterator} containing the elements reorganized by the new keys. + */ + public reorganizeBy(iteratee: MaybeAsyncKeyedIteratee) + : AggregatedAsyncIterator { const elements = this._elements; @@ -300,6 +951,29 @@ export default class AggregatedAsyncIterator }); } + /** + * An utility method that returns a new {@link SmartAsyncIterator} + * object containing all the keys of the iterator. + * + * Since the iterator is lazy, the keys will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const keys = new SmartAsyncIterator([-3, Symbol(), "A", { }, null, [1 , 2, 3], false]) + * .groupBy(async (value) => typeof value) + * .keys(); + * + * console.log(await keys.toArray()); // ["number", "symbol", "string", "object", "boolean"] + * ``` + * + * --- + * + * @returns A new {@link SmartAsyncIterator} containing all the keys of the iterator. + */ public keys(): SmartAsyncIterator { const elements = this._elements; @@ -317,10 +991,58 @@ export default class AggregatedAsyncIterator } }); } + + /** + * An utility method that returns a new {@link SmartAsyncIterator} + * object containing all the items of the iterator. + * + * Since the iterator is lazy, the items will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const items = new SmartAsyncIterator([-3, 0, 2, -1, 3]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .items(); + * + * console.log(await items.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] + * ``` + * + * --- + * + * @returns A new {@link SmartAsyncIterator} containing all the items of the iterator. + */ public items(): SmartAsyncIterator<[K, T]> { return this._elements; } + + /** + * An utility method that returns a new {@link SmartAsyncIterator} + * object containing all the values of the iterator. + * + * Since the iterator is lazy, the values will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const values = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") + * .values(); + * + * console.log(await values.toArray()); // [-3, -1, 0, 2, 3, 5, 6, 8] + * ``` + * + * --- + * + * @returns A new {@link SmartAsyncIterator} containing all the values of the iterator. + */ public values(): SmartAsyncIterator { const elements = this._elements; @@ -331,12 +1053,47 @@ export default class AggregatedAsyncIterator }); } + /** + * Materializes the iterator into an array of arrays. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(await aggregator.toArray()); // [[-3, -1, 3, 5], [0, 2, 6, 8]] + * ``` + * + * --- + * + * @returns An {@link Array} of arrays containing the elements of the iterator. + */ public async toArray(): Promise { const map = await this.toMap(); return Array.from(map.values()); } + + /** + * Materializes the iterator into a map. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(await aggregator.toMap()); // Map(2) { "odd" => [-3, -1, 3, 5], "even" => [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns A {@link Map} containing the elements of the iterator. + */ public async toMap(): Promise> { const groups = new Map(); @@ -351,6 +1108,24 @@ export default class AggregatedAsyncIterator return groups; } + + /** + * Materializes the iterator into an object. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(await aggregator.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns An {@link Object} containing the elements of the iterator. + */ public async toObject(): Promise> { const groups = { } as Record; diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 4b7a14f..821066f 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -14,7 +14,7 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate. * See the {@link AggregatedIterator.keys}, {@link AggregatedIterator.items} * & {@link AggregatedIterator.values} methods. - * It does, however, provides the same set of methods to perform + * It does, however, provide the same set of methods to perform * operations and transformation on the elements of the iterator, * having also the knowledge and context of the groups to which * they belong, allowing to handle them in a grouped manner. @@ -32,13 +32,13 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ * * --- * - * @template K The type of the keys of the elements. - * @template T The type of the elements. + * @template K The type of the keys used to group the elements. + * @template T The type of the elements to aggregate. */ export default class AggregatedIterator { /** - * The internal {@link SmartIterator} that holds the elements to aggregate. + * The internal {@link SmartIterator} object that holds the elements to aggregate. */ protected _elements: SmartIterator<[K, T]>; @@ -124,11 +124,11 @@ export default class AggregatedIterator * * It will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group doesn't satisfy the condition, - * the result for the respective group will set to `false`. + * the result for the respective group will be `false`. * * Eventually, it will return a new {@link ReducedIterator} * object that will contain all the boolean results for each group. - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) @@ -142,7 +142,7 @@ export default class AggregatedIterator * * @param predicate The condition to check for each element of the iterator. * - * @returns `true` if all elements satisfy the condition, `false` otherwise. + * @returns A new {@link ReducedIterator} containing the boolean results for each group. */ public every(predicate: KeyedIteratee): ReducedIterator { @@ -168,13 +168,13 @@ export default class AggregatedIterator * See also {@link AggregatedIterator.every}. * This method will consume the entire iterator in the process. * - * It will iterate over all elements of the iterator checking if they satisfy the condition. + * It will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element of one group satisfies the condition, - * the result for the respective group will set to `true`. + * the result for the respective group will be `true`. * * Eventually, it will return a new {@link ReducedIterator} - * object that will contain all the boolean results for each group. - * If the iterator is infinite, the function will never return. + * object that will contain all the boolean results for each group. + * If the iterator is infinite, the method will never return. * * ```ts * const results = new SmartIterator([-5, -4, -3, -2, -1, 0]) @@ -188,7 +188,7 @@ export default class AggregatedIterator * * @param predicate The condition to check for each element of the iterator. * - * @returns A {@link ReducedIterator} object containing the boolean results for each group. + * @returns A {@link ReducedIterator} containing the boolean results for each group. */ public some(predicate: KeyedIteratee): ReducedIterator { @@ -213,12 +213,12 @@ export default class AggregatedIterator * Filters the elements of the iterator using a given condition. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * - * A new iterator will be created, holding the reference to the original one. + * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the * new one is and that consuming one of them will consume the other as well. * @@ -242,12 +242,12 @@ export default class AggregatedIterator * Filters the elements of the iterator using a given condition. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. * - * A new iterator will be created, holding the reference to the original one. + * A new iterator will be created, holding the reference to the original one. * This means that the original iterator won't be consumed until the * new one is and that consuming one of them will consume the other as well. * @@ -265,7 +265,7 @@ export default class AggregatedIterator * The type of the elements that satisfy the condition. * This allows the type-system to infer the correct type of the new iterator. * - * It must be a subtype of the original type of the iterator. + * It must be a subtype of the original type of the elements. * * @param predicate The condition to check for each element of the iterator. * @@ -344,14 +344,14 @@ export default class AggregatedIterator * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each riteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * - * The first accumulator value will be the first element of the iterator. + * The first accumulator value will be the first element of the iterator. * The last accumulator value will be the final result of the reduction. * * Eventually, it will return a new {@link ReducedIterator} - * object that will contain all the reduced results for each group. - * If the iterator is infinite, the function will never return. + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. * * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) @@ -374,14 +374,14 @@ export default class AggregatedIterator * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each riteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * - * The first accumulator value will be the initial value provided. + * The first accumulator value will be the provided initial value. * The last accumulator value will be the final result of the reduction. * * Eventually, it will return a new {@link ReducedIterator} - * object that will contain all the reduced results for each group. - * If the iterator is infinite, the function will never return. + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. * * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) @@ -407,14 +407,14 @@ export default class AggregatedIterator * This method will consume the entire iterator in the process. * * It will iterate over all elements of the iterator applying the reducer function. - * The result of each riteration will be passed as the accumulator to the next one. + * The result of each iteration will be passed as the accumulator to the next one. * - * The first accumulator value will be the initial value provided by the given function. + * The first accumulator value will be the provided initial value by the given function. * The last accumulator value will be the final result of the reduction. * * Eventually, it will return a new {@link ReducedIterator} - * object that will contain all the reduced results for each group. - * If the iterator is infinite, the function will never return. + * object that will contain all the reduced results for each group. + * If the iterator is infinite, the method will never return. * * ```ts * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) @@ -429,7 +429,7 @@ export default class AggregatedIterator * @template A The type of the accumulator value which will also be the type of the final result of the reduction. * * @param reducer The reducer function to apply to each element of the iterator. - * @param initialValue The function that provides the initial value of the accumulator. + * @param initialValue The function that provides the initial value for the accumulator. * * @returns A new {@link ReducedIterator} containing the reduced results for each group. */ @@ -467,6 +467,35 @@ export default class AggregatedIterator }); } + /** + * Flattens the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be included in the new iterator. + * + * Since the iterator is lazy, the flattening process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([[-3, -1], [0, 2], [3, 5], [6, 8]]) + * .groupBy(([value, _]) => value % 2 === 0 ? "even" : "odd") + * .flatMap((key, values) => values); + * + * console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link AggregatedIterator} containing the transformed elements. + */ public flatMap(iteratee: KeyedIteratee>): AggregatedIterator { const elements = this._elements; @@ -487,6 +516,32 @@ export default class AggregatedIterator }); } + /** + * Drops a given number of elements from the beginning of each group of the iterator. + * The remaining elements will be included in the new iterator. + * See also {@link AggregatedIterator.take}. + * + * Since the iterator is lazy, the dropping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .drop(2); + * + * console.log(results.toObject()); // { odd: [3, 5], even: [6, 8] } + * ``` + * + * --- + * + * @param count The number of elements to drop from the beginning of each group. + * + * @returns A new {@link AggregatedIterator} containing the remaining elements. + */ public drop(count: number): AggregatedIterator { const elements = this._elements; @@ -509,6 +564,33 @@ export default class AggregatedIterator } }); } + + /** + * Takes a given number of elements from the beginning of each group of the iterator. + * The elements will be included in the new iterator. + * See also {@link AggregatedIterator.drop}. + * + * Since the iterator is lazy, the taking process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .take(2); + * + * console.log(results.toObject()); // { odd: [-3, -1], even: [0, 2] } + * ``` + * + * --- + * + * @param count The number of elements to take from the beginning of each group. + * + * @returns A new {@link AggregatedIterator} containing the taken elements. + */ public take(limit: number): AggregatedIterator { const elements = this._elements; @@ -529,7 +611,66 @@ export default class AggregatedIterator }); } + /** + * Finds the first element of each group of the iterator that satisfies a given condition. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checking if they satisfy the condition. + * Once the first element of one group satisfies the condition, + * the result for the respective group will be the element itself. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain the first element that satisfies the condition for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .find((key, value) => value > 0); + * + * console.log(results.toObject()); // { odd: 3, even: 2 } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. + */ public find(predicate: KeyedIteratee): ReducedIterator; + + /** + * Finds the first element of each group of the iterator that satisfies a given condition. + * This method will consume the entire iterator in the process. + * + * It will iterate over all elements of the iterator checking if they satisfy the condition. + * Once the first element of one group satisfies the condition, + * the result for the respective group will be the element itself. + * + * Eventually, it will return a new {@link ReducedIterator} + * object that will contain the first element that satisfies the condition for each group. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, "-1", 0, "2", "3", 5, 6, "8"]) + * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd") + * .find((key, value) => typeof value === "number"); + * + * console.log(results.toObject()); // { odd: -3, even: 0 } + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the new iterator. + * + * It must be a subtype of the original type of the elements. + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. + */ public find(predicate: KeyedTypeGuardPredicate): ReducedIterator; public find(predicate: KeyedIteratee): ReducedIterator { @@ -551,10 +692,57 @@ export default class AggregatedIterator }); } + /** + * Enumerates the elements of the iterator. + * Each element is paired with its index within the group in a new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, 0, 2, -1, 3]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .enumerate(); + * + * console.log(results.toObject()); // { odd: [[0, -3], [1, -1], [2, 3]], even: [[0, 0], [1, 2]] } + * ``` + * + * --- + * + * @returns A new {@link AggregatedIterator} containing the enumerated elements. + */ public enumerate(): AggregatedIterator { return this.map((_, value, index) => [index, value]); } + + /** + * Removes all duplicate elements from within each group of the iterator. + * The first occurrence of each element will be included in the new iterator. + * + * Since the iterator is lazy, the uniqueness process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 6, -3, -1, 0, 5, 6, 8, 0, 2]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .unique(); + * + * console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns A new {@link AggregatedIterator} containing only the unique elements. + */ public unique(): AggregatedIterator { const elements = this._elements; @@ -577,6 +765,24 @@ export default class AggregatedIterator }); } + /** + * Counts the number of elements within each group of the iterator. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .count(); + * + * console.log(results.toObject()); // { odd: 4, even: 4 } + * ``` + * + * --- + * + * @returns A new {@link ReducedIterator} containing the number of elements for each group. + */ public count(): ReducedIterator { const counters = new Map(); @@ -594,6 +800,27 @@ export default class AggregatedIterator }); } + /** + * Iterates over the elements of the iterator. + * The elements are passed to the given iteratee function along with their key and index within the group. + * + * This method will consume the entire iterator in the process. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartIterator([-3, 0, 2, -1, 3]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd"); + * + * aggregator.forEach((key, value, index) => + * { + * console.log(`${index}: ${value}`); // "0: -3", "0: 0", "1: 2", "1: -1", "2: 3" + * }; + * ``` + * + * --- + * + * @param iteratee The function to execute for each element of the iterator. + */ public forEach(iteratee: KeyedIteratee): void { const indexes = new Map(); @@ -608,7 +835,35 @@ export default class AggregatedIterator } } - public rekey(iteratee: KeyedIteratee): AggregatedIterator + /** + * Changes the key of each element on which the iterator is aggregated. + * The new key is determined by the given iteratee function. + * + * Since the iterator is lazy, the reorganization process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map((key, value, index) => index % 2 === 0 ? value : -value) + * .reorganizeBy((key, value) => value >= 0 ? "+" : "-"); + * + * console.log(results.toObject()); // { "+": [1, 0, 3, 6], "-": [-3, -2, -5, -8] } + * ``` + * + * --- + * + * @template J The type of the new key. + * + * @param iteratee The function to determine the new key for each element of the iterator. + * + * @returns A new {@link AggregatedIterator} containing the elements reorganized by the new keys. + */ + public reorganizeBy(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements; @@ -627,6 +882,29 @@ export default class AggregatedIterator }); } + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the keys of the iterator. + * + * Since the iterator is lazy, the keys will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const keys = new SmartIterator([-3, Symbol(), "A", { }, null, [1 , 2, 3], false]) + * .groupBy((value) => typeof value) + * .keys(); + * + * console.log(keys.toArray()); // ["number", "symbol", "string", "object", "boolean"] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the keys of the iterator. + */ public keys(): SmartIterator { const elements = this._elements; @@ -644,10 +922,58 @@ export default class AggregatedIterator } }); } + + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the items of the iterator. + * + * Since the iterator is lazy, the items will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const items = new SmartIterator([-3, 0, 2, -1, 3]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .items(); + * + * console.log(items.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the items of the iterator. + */ public items(): SmartIterator<[K, T]> { return this._elements; } + + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the values of the iterator. + * + * Since the iterator is lazy, the values will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const values = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .values(); + * + * console.log(values.toArray()); // [-3, -1, 0, 2, 3, 5, 6, 8] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the values of the iterator. + */ public values(): SmartIterator { const elements = this._elements; @@ -658,12 +984,47 @@ export default class AggregatedIterator }); } + /** + * Materializes the iterator into an array of arrays. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(aggregator.toArray()); // [[-3, -1, 3, 5], [0, 2, 6, 8]] + * ``` + * + * --- + * + * @returns An {@link Array} of arrays containing the elements of the iterator. + */ public toArray(): T[][] { const map = this.toMap(); return Array.from(map.values()); } + + /** + * Materializes the iterator into a map. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(aggregator.toMap()); // Map(2) { "odd" => [-3, -1, 3, 5], "even" => [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns A {@link Map} containing the elements of the iterator. + */ public toMap(): Map { const groups = new Map(); @@ -678,6 +1039,24 @@ export default class AggregatedIterator return groups; } + + /** + * Materializes the iterator into an object. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const aggregator = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd"); + * + * console.log(aggregator.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @returns An {@link Object} containing the elements of the iterator. + */ public toObject(): Record { const groups = { } as Record; diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index 4d0d597..6b78639 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -167,7 +167,7 @@ export default class ReducedIterator } } - public rekey(iteratee: KeyedIteratee): AggregatedIterator + public reorganizeBy(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements.enumerate(); diff --git a/src/models/callbacks/switchable-callback.ts b/src/models/callbacks/switchable-callback.ts index 08b9a8f..592ce6e 100644 --- a/src/models/callbacks/switchable-callback.ts +++ b/src/models/callbacks/switchable-callback.ts @@ -60,7 +60,7 @@ export default class SwitchableCallback = Callbac /** * The key that is associated with the currently selected implementation. * - * This protected property is the only one that can be modified directly by the derived classes. + * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public and readonly property, use the {@link SwitchableCallback.key} getter instead. */ protected _key: string; diff --git a/src/models/exceptions/core.ts b/src/models/exceptions/core.ts index eec1467..13319c3 100644 --- a/src/models/exceptions/core.ts +++ b/src/models/exceptions/core.ts @@ -13,7 +13,7 @@ * // Uncaught Exception: The game saves may be corrupted. Try to restart the game. * // at /src/game/index.ts:37:15 * // at /src/main.ts:23:17 - * // + * // * // Caused by SyntaxError: Unexpected end of JSON input * // at /src/models/saves.ts:47:17 * // at /src/game/index.ts:12:9 diff --git a/src/models/game-loop.ts b/src/models/game-loop.ts index 1c7143c..87038c3 100644 --- a/src/models/game-loop.ts +++ b/src/models/game-loop.ts @@ -70,7 +70,7 @@ export default class GameLoop /** * A flag indicating whether the game loop is currently running or not. * - * This protected property is the only one that can be modified directly by the derived classes. + * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public and readonly property, use the {@link GameLoop.isRunning} getter instead. */ protected _isRunning: boolean; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index e8e545e..4427319 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -240,7 +240,7 @@ export default class SmartAsyncIterator implements A * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartAsyncIterator.find} instead. * - * If the iterator is infinite and every element satisfies the condition, the function will never return. + * If the iterator is infinite and every element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); @@ -281,7 +281,7 @@ export default class SmartAsyncIterator implements A * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartAsyncIterator.find} instead. * - * If the iterator is infinite and no element satisfies the condition, the function will never return. + * If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); @@ -313,9 +313,9 @@ export default class SmartAsyncIterator implements A /** * Filters the elements of the iterator using a given condition. - * + * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. @@ -341,9 +341,9 @@ export default class SmartAsyncIterator implements A /** * Filters the elements of the iterator using a given condition. - * + * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. @@ -365,7 +365,7 @@ export default class SmartAsyncIterator implements A * The type of the elements that satisfy the condition. * This allows the type-system to infer the correct type of the new iterator. * - * It must be a subtype of the original type of the iterator. + * It must be a subtype of the original type of the elements. * * @param predicate The condition to check for each element of the iterator. * @@ -452,7 +452,7 @@ export default class SmartAsyncIterator implements A * * Also note that: * - If an empty iterator is provided, a {@link ValueException} will be thrown. - * - If the iterator is infinite, the function will never return. + * - If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); @@ -476,10 +476,10 @@ export default class SmartAsyncIterator implements A * It will iterate over all elements of the iterator applying the reducer function. * The result of each iteration will be passed as the accumulator to the next one. * - * The first accumulator value will be the initial value provided. + * The first accumulator value will be the provided initial value. * The last accumulator value will be the final result of the reduction. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); @@ -523,7 +523,11 @@ export default class SmartAsyncIterator implements A } /** - * Flattens the elements of the iterator using a given transformation function. + * Flattens the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be flattened and included in the new iterator. + * * Since the iterator is lazy, the flattening process will * be executed once the resulting iterator is materialized. * @@ -573,7 +577,8 @@ export default class SmartAsyncIterator implements A /** * Drops a given number of elements at the beginning of the iterator. - * The remaining elements will be returned in a new iterator. + * The remaining elements will be included in a new iterator. + * See also {@link SmartAsyncIterator.take}. * * Since the iterator is lazy, the dropping process will * be executed once the resulting iterator is materialized. @@ -626,7 +631,8 @@ export default class SmartAsyncIterator implements A /** * Takes a given number of elements at the beginning of the iterator. - * These elements will be returned in a new iterator. + * These elements will be included in a new iterator. + * See also {@link SmartAsyncIterator.drop}. * * Since the iterator is lazy, the taking process will * be executed once the resulting iterator is materialized. @@ -675,18 +681,18 @@ export default class SmartAsyncIterator implements A } /** - * Finds the first element of the iterator that satisfies a given condition. + * Finds the first element of the iterator that satisfies a given condition. * - * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first - * satisfying one will be consumed from the original iterator. + * satisfying one will be consumed from the original iterator. * The rest of the original iterator will be available for further consumption. * * Also note that: * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. - * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * - If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([-2, -1, 0, 1, 2]); @@ -704,7 +710,7 @@ export default class SmartAsyncIterator implements A public async find(predicate: MaybeAsyncIteratee): Promise; /** - * Finds the first element of the iterator that satisfies a given condition. + * Finds the first element of the iterator that satisfies a given condition. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. @@ -715,7 +721,7 @@ export default class SmartAsyncIterator implements A * * Also note that: * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. - * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * - If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([-2, "-1", "0", 1, "2"]); @@ -730,7 +736,7 @@ export default class SmartAsyncIterator implements A * The type of the element that satisfies the condition. * This allows the type-system to infer the correct type of the result. * - * It must be a subtype of the original type of the iterator. + * It must be a subtype of the original type of the elements. * * @param predicate The condition to check for each element of the iterator. * @@ -830,7 +836,7 @@ export default class SmartAsyncIterator implements A * Counts the number of elements in the iterator. * This method will consume the entire iterator in the process. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator([1, 2, 3, 4, 5]); @@ -858,9 +864,10 @@ export default class SmartAsyncIterator implements A /** * Iterates over all elements of the iterator applying a given function. - * This method will consume the entire iterator in the process. + * The elements are passed to the function along with their index. * - * If the iterator is infinite, the function will never return. + * This method will consume the entire iterator in the process. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator(["A", "M", "N", "Z"]); @@ -1044,7 +1051,7 @@ export default class SmartAsyncIterator implements A * Materializes the iterator into an array. * This method will consume the entire iterator in the process. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartAsyncIterator(async function* () diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 4f0856b..48109a1 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -122,7 +122,8 @@ export default class SmartIterator implements Iterat } /** - * Determines whether all elements of the iterator satisfy a given condition. See also {@link SmartIterator.some}. + * Determines whether all elements of the iterator satisfy a given condition. + * See also {@link SmartIterator.some}. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element doesn't satisfy the condition, the method will return `false` immediately. @@ -131,7 +132,7 @@ export default class SmartIterator implements Iterat * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartIterator.find} instead. * - * If the iterator is infinite and every element satisfies the condition, the function will never return. + * If the iterator is infinite and every element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); @@ -162,7 +163,8 @@ export default class SmartIterator implements Iterat } /** - * Determines whether any element of the iterator satisfies a given condition. See also {@link SmartIterator.every}. + * Determines whether any element of the iterator satisfies a given condition. + * See also {@link SmartIterator.every}. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. * Once a single element satisfies the condition, the method will return `true` immediately. @@ -171,7 +173,7 @@ export default class SmartIterator implements Iterat * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. * Consider using {@link SmartIterator.find} instead. * - * If the iterator is infinite and no element satisfies the condition, the function will never return. + * If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); @@ -205,7 +207,7 @@ export default class SmartIterator implements Iterat * Filters the elements of the iterator using a given condition. * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. @@ -231,9 +233,9 @@ export default class SmartIterator implements Iterat /** * Filters the elements of the iterator using a given condition. - * + * * This method will iterate over all elements of the iterator checking if they satisfy the condition. - * If the condition is satisfied, the element will be included in the new iterator. + * If the condition is met, the element will be included in the new iterator. * * Since the iterator is lazy, the filtering process will * be executed once the resulting iterator is materialized. @@ -255,7 +257,7 @@ export default class SmartIterator implements Iterat * The type of the elements that satisfy the condition. * This allows the type-system to infer the correct type of the new iterator. * - * It must be a subtype of the original type of the iterator. + * It must be a subtype of the original type of the elements. * * @param predicate The condition to check for each element of the iterator. * @@ -342,7 +344,7 @@ export default class SmartIterator implements Iterat * * Also note that: * - If an empty iterator is provided, a {@link ValueException} will be thrown. - * - If the iterator is infinite, the function will never return. + * - If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartIterator([1, 2, 3, 4, 5]); @@ -366,10 +368,10 @@ export default class SmartIterator implements Iterat * It will iterate over all elements of the iterator applying the reducer function. * The result of each iteration will be passed as the accumulator to the next one. * - * The first accumulator value will be the initial value provided. + * The first accumulator value will be the provided initial value. * The last accumulator value will be the final result of the reduction. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartIterator([1, 2, 3, 4, 5]); @@ -413,7 +415,11 @@ export default class SmartIterator implements Iterat } /** - * Flattens the elements of the iterator using a given transformation function. + * Flattens the elements of the iterator using a given transformation function. + * + * This method will iterate over all elements of the iterator applying the transformation function. + * The result of each transformation will be flattened into the new iterator. + * * Since the iterator is lazy, the flattening process will * be executed once the resulting iterator is materialized. * @@ -462,7 +468,8 @@ export default class SmartIterator implements Iterat /** * Drops a given number of elements at the beginning of the iterator. - * The remaining elements will be returned in a new iterator. + * The remaining elements will be included in a new iterator. + * See also {@link SmartIterator.take}. * * Since the iterator is lazy, the dropping process will * be executed once the resulting iterator is materialized. @@ -514,7 +521,8 @@ export default class SmartIterator implements Iterat /** * Takes a given number of elements at the beginning of the iterator. - * These elements will be returned in a new iterator. + * These elements will be included in a new iterator. + * See also {@link SmartIterator.drop}. * * Since the iterator is lazy, the taking process will * be executed once the resulting iterator is materialized. @@ -564,16 +572,16 @@ export default class SmartIterator implements Iterat /** * Finds the first element of the iterator that satisfies a given condition. * - * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first - * satisfying one will be consumed from the original iterator. + * satisfying one will be consumed from the original iterator. * The rest of the original iterator will be available for further consumption. * * Also note that: * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. - * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * - If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartIterator([-2, -1, 0, 1, 2]); @@ -593,16 +601,16 @@ export default class SmartIterator implements Iterat /** * Finds the first element of the iterator that satisfies a given condition. * - * This method will iterate over all elements of the iterator checking if they satisfy the condition. + * This method will iterate over all elements of the iterator checking if they satisfy the condition. * The first element that satisfies the condition will be returned immediately. * * Only the elements that are necessary to find the first - * satisfying one will be consumed from the original iterator. + * satisfying one will be consumed from the original iterator. * The rest of the original iterator will be available for further consumption. * * Also note that: * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed. - * - If the iterator is infinite and no element satisfies the condition, the function will never return. + * - If the iterator is infinite and no element satisfies the condition, the method will never return. * * ```ts * const iterator = new SmartIterator([-2, "-1", "0", 1, "2"]); @@ -617,7 +625,7 @@ export default class SmartIterator implements Iterat * The type of the element that satisfies the condition. * This allows the type-system to infer the correct type of the result. * - * It must be a subtype of the original type of the iterator. + * It must be a subtype of the original type of the elements. * * @param predicate The condition to check for each element of the iterator. * @@ -717,7 +725,7 @@ export default class SmartIterator implements Iterat * Counts the number of elements in the iterator. * This method will consume the entire iterator in the process. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartIterator([1, 2, 3, 4, 5]); @@ -745,9 +753,10 @@ export default class SmartIterator implements Iterat /** * Iterates over all elements of the iterator applying a given function. - * This method will consume the entire iterator in the process. + * The elements are passed to the function along with their index. * - * If the iterator is infinite, the function will never return. + * This method will consume the entire iterator in the process. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartIterator(["A", "M", "N", "Z"]); @@ -928,7 +937,7 @@ export default class SmartIterator implements Iterat * Materializes the iterator into an array. * This method will consume the entire iterator in the process. * - * If the iterator is infinite, the function will never return. + * If the iterator is infinite, the method will never return. * * ```ts * const iterator = new SmartIterator(function* () diff --git a/src/models/iterators/types.ts b/src/models/iterators/types.ts index dd71bfd..f618239 100644 --- a/src/models/iterators/types.ts +++ b/src/models/iterators/types.ts @@ -90,7 +90,7 @@ export type AsyncGeneratorFunction = () => AsyncGene /** * An utility type that represents a function that returns a - * generator object that can be either synchronous or asynchronous. + * generator object that can be either synchronous or asynchronous. * * ```ts * const generatorFn: MaybeAsyncGeneratorFunction = [async] function*() { ... }; diff --git a/src/models/json/json-storage.ts b/src/models/json/json-storage.ts index 38d000c..c4b3c6b 100644 --- a/src/models/json/json-storage.ts +++ b/src/models/json/json-storage.ts @@ -463,7 +463,7 @@ export default class JSONStorage } /** - * Sets the value with the specified key in the volatile {@link sessionStorage}. + * Sets the value with the specified key in the volatile {@link sessionStorage}. * If the value is `undefined` or omitted, the key is removed from the storage. * * ```ts @@ -485,7 +485,7 @@ export default class JSONStorage } /** - * Sets the value with the specified key in the persistent {@link localStorage}. + * Sets the value with the specified key in the persistent {@link localStorage}. * If the value is `undefined` or omitted, the key is removed from the storage. * * ```ts diff --git a/src/models/promises/smart-promise.ts b/src/models/promises/smart-promise.ts index 6596e5c..c81420e 100644 --- a/src/models/promises/smart-promise.ts +++ b/src/models/promises/smart-promise.ts @@ -292,7 +292,7 @@ export default class SmartPromise implements Promise * setTimeout(reject, Math.random() * 1_000); * }); * - * + * * promise * .then(() => console.log("OK!")) // Logs "OK!" if the promise is fulfilled. * .catch(() => console.log("KO!")) // Logs "KO!" if the promise is rejected. diff --git a/src/models/promises/timed-promise.ts b/src/models/promises/timed-promise.ts index 684eb2a..9a1a2fa 100644 --- a/src/models/promises/timed-promise.ts +++ b/src/models/promises/timed-promise.ts @@ -15,7 +15,7 @@ import type { MaybePromise, PromiseExecutor } from "./types.js"; * setTimeout(() => resolve("Hello, World!"), Math.random() * 10_000); * * }, 5_000); - * + * * promise * .then((result) => console.log(result)) // "Hello, World!" * .catch((error) => console.error(error)); // TimeoutException: The operation has timed out. diff --git a/src/models/timers/countdown.ts b/src/models/timers/countdown.ts index 697b365..2640faa 100644 --- a/src/models/timers/countdown.ts +++ b/src/models/timers/countdown.ts @@ -44,7 +44,7 @@ export default class Countdown extends GameLoop /** * The total duration of the countdown in milliseconds. * - * This protected property is the only one that can be modified directly by the derived classes. + * This protected property is the only one that can be modified directly by the derived classes. * If you're looking for the public and readonly property, use the {@link Countdown.duration} getter instead. */ protected _duration: number; @@ -149,7 +149,7 @@ export default class Countdown extends GameLoop * --- * * @param remainingTime - * The remaining time to set as default when the countdown starts. + * The remaining time to set as default when the countdown starts. * Default is the {@link Countdown.duration} itself. * * @returns A {@link SmartPromise} that will be resolved or rejected when the countdown expires or stops. diff --git a/src/utils/math.ts b/src/utils/math.ts index 277f482..4f808c2 100644 --- a/src/utils/math.ts +++ b/src/utils/math.ts @@ -109,7 +109,7 @@ export function hash(value: string): number * * --- * - * @template T The type of the values in the list. It must be or extend a `number` object. + * @template T The type of the values in the list. It must be or extend a `number` object. * * @param values The list of values to sum. * diff --git a/src/utils/random.ts b/src/utils/random.ts index 2da829b..6252b05 100644 --- a/src/utils/random.ts +++ b/src/utils/random.ts @@ -57,7 +57,7 @@ export default class Random * * --- * - * @param min The minimum value (included). + * @param min The minimum value (included). * @param max The maximum value (excluded). * * @returns A random integer value. @@ -107,7 +107,7 @@ export default class Random * * --- * - * @param min The minimum value (included). + * @param min The minimum value (included). * @param max The maximum value (excluded). * * @returns A random decimal value From e1b0ed50e68621a34ee34f08433e96c8514e47f2 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 21 Jan 2025 01:10:18 +0100 Subject: [PATCH 27/32] =?UTF-8?q?upd:=20Updated=20dependencies.=20?= =?UTF-8?q?=F0=9F=94=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- pnpm-lock.yaml | 280 ++++++++++++++++++++++++------------------------- 2 files changed, 142 insertions(+), 142 deletions(-) diff --git a/package.json b/package.json index 4e16c43..794ccb6 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,10 @@ }, "devDependencies": { "@byloth/eslint-config-typescript": "^3.0.3", - "@types/node": "^22.10.5", + "@types/node": "^22.10.7", "husky": "^9.1.7", "typescript": "^5.7.3", - "vite": "^6.0.7" + "vite": "^6.0.10" }, "packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9b1085..4eecd06 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^3.0.3 version: 3.0.3(eslint@9.18.0)(typescript@5.7.3) '@types/node': - specifier: ^22.10.5 - version: 22.10.5 + specifier: ^22.10.7 + version: 22.10.7 husky: specifier: ^9.1.7 version: 9.1.7 @@ -21,8 +21,8 @@ importers: specifier: ^5.7.3 version: 5.7.3 vite: - specifier: ^6.0.7 - version: 6.0.7(@types/node@22.10.5) + specifier: ^6.0.10 + version: 6.0.10(@types/node@22.10.7) packages: @@ -257,98 +257,98 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@rollup/rollup-android-arm-eabi@4.30.1': - resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} + '@rollup/rollup-android-arm-eabi@4.31.0': + resolution: {integrity: sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.30.1': - resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} + '@rollup/rollup-android-arm64@4.31.0': + resolution: {integrity: sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.30.1': - resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} + '@rollup/rollup-darwin-arm64@4.31.0': + resolution: {integrity: sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.30.1': - resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} + '@rollup/rollup-darwin-x64@4.31.0': + resolution: {integrity: sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.30.1': - resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + '@rollup/rollup-freebsd-arm64@4.31.0': + resolution: {integrity: sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.30.1': - resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + '@rollup/rollup-freebsd-x64@4.31.0': + resolution: {integrity: sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': - resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} + '@rollup/rollup-linux-arm-gnueabihf@4.31.0': + resolution: {integrity: sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.30.1': - resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} + '@rollup/rollup-linux-arm-musleabihf@4.31.0': + resolution: {integrity: sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.30.1': - resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} + '@rollup/rollup-linux-arm64-gnu@4.31.0': + resolution: {integrity: sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.30.1': - resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} + '@rollup/rollup-linux-arm64-musl@4.31.0': + resolution: {integrity: sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': - resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.31.0': + resolution: {integrity: sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': - resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': + resolution: {integrity: sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.30.1': - resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} + '@rollup/rollup-linux-riscv64-gnu@4.31.0': + resolution: {integrity: sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.30.1': - resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} + '@rollup/rollup-linux-s390x-gnu@4.31.0': + resolution: {integrity: sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.30.1': - resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} + '@rollup/rollup-linux-x64-gnu@4.31.0': + resolution: {integrity: sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.30.1': - resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} + '@rollup/rollup-linux-x64-musl@4.31.0': + resolution: {integrity: sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.30.1': - resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} + '@rollup/rollup-win32-arm64-msvc@4.31.0': + resolution: {integrity: sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.30.1': - resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} + '@rollup/rollup-win32-ia32-msvc@4.31.0': + resolution: {integrity: sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.30.1': - resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} + '@rollup/rollup-win32-x64-msvc@4.31.0': + resolution: {integrity: sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==} cpu: [x64] os: [win32] @@ -358,54 +358,54 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.10.5': - resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/node@22.10.7': + resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} - '@typescript-eslint/eslint-plugin@8.19.1': - resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} + '@typescript-eslint/eslint-plugin@8.21.0': + resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/parser@8.19.1': - resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} + '@typescript-eslint/parser@8.21.0': + resolution: {integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/scope-manager@8.19.1': - resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} + '@typescript-eslint/scope-manager@8.21.0': + resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.19.1': - resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} + '@typescript-eslint/type-utils@8.21.0': + resolution: {integrity: sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/types@8.19.1': - resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} + '@typescript-eslint/types@8.21.0': + resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.19.1': - resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} + '@typescript-eslint/typescript-estree@8.21.0': + resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.19.1': - resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} + '@typescript-eslint/utils@8.21.0': + resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/visitor-keys@8.19.1': - resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} + '@typescript-eslint/visitor-keys@8.21.0': + resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} acorn-jsx@5.3.2: @@ -705,8 +705,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -728,8 +728,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.30.1: - resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} + rollup@4.31.0: + resolution: {integrity: sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -786,8 +786,8 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - vite@6.0.7: - resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} + vite@6.0.10: + resolution: {integrity: sha512-MEszunEcMo6pFsfXN1GhCFQqnE25tWRH0MA4f0Q7uanACi4y1Us+ZGpTMnITwCTnYzB2b9cpmnelTlxgTBmaBA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -844,8 +844,8 @@ snapshots: '@byloth/eslint-config-typescript@3.0.3(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@byloth/eslint-config': 3.0.3 - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.7.3) transitivePeerDependencies: - eslint - jiti @@ -1008,79 +1008,79 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.18.0 - '@rollup/rollup-android-arm-eabi@4.30.1': + '@rollup/rollup-android-arm-eabi@4.31.0': optional: true - '@rollup/rollup-android-arm64@4.30.1': + '@rollup/rollup-android-arm64@4.31.0': optional: true - '@rollup/rollup-darwin-arm64@4.30.1': + '@rollup/rollup-darwin-arm64@4.31.0': optional: true - '@rollup/rollup-darwin-x64@4.30.1': + '@rollup/rollup-darwin-x64@4.31.0': optional: true - '@rollup/rollup-freebsd-arm64@4.30.1': + '@rollup/rollup-freebsd-arm64@4.31.0': optional: true - '@rollup/rollup-freebsd-x64@4.30.1': + '@rollup/rollup-freebsd-x64@4.31.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + '@rollup/rollup-linux-arm-gnueabihf@4.31.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.1': + '@rollup/rollup-linux-arm-musleabihf@4.31.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.1': + '@rollup/rollup-linux-arm64-gnu@4.31.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.30.1': + '@rollup/rollup-linux-arm64-musl@4.31.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + '@rollup/rollup-linux-loongarch64-gnu@4.31.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.30.1': + '@rollup/rollup-linux-riscv64-gnu@4.31.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.30.1': + '@rollup/rollup-linux-s390x-gnu@4.31.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.30.1': + '@rollup/rollup-linux-x64-gnu@4.31.0': optional: true - '@rollup/rollup-linux-x64-musl@4.30.1': + '@rollup/rollup-linux-x64-musl@4.31.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.30.1': + '@rollup/rollup-win32-arm64-msvc@4.31.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.30.1': + '@rollup/rollup-win32-ia32-msvc@4.31.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.30.1': + '@rollup/rollup-win32-x64-msvc@4.31.0': optional: true '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} - '@types/node@22.10.5': + '@types/node@22.10.7': dependencies: undici-types: 6.20.0 - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3))(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/type-utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.21.0 eslint: 9.18.0 graphemer: 1.4.0 ignore: 5.3.2 @@ -1090,27 +1090,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.21.0 debug: 4.4.0 eslint: 9.18.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.19.1': + '@typescript-eslint/scope-manager@8.21.0': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 - '@typescript-eslint/type-utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0)(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.7.3) debug: 4.4.0 eslint: 9.18.0 ts-api-utils: 2.0.0(typescript@5.7.3) @@ -1118,12 +1118,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.19.1': {} + '@typescript-eslint/types@8.21.0': {} - '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': + '@typescript-eslint/typescript-estree@8.21.0(typescript@5.7.3)': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/visitor-keys': 8.21.0 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -1134,20 +1134,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.19.1(eslint@9.18.0)(typescript@5.7.3)': + '@typescript-eslint/utils@8.21.0(eslint@9.18.0)(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.7.3) eslint: 9.18.0 typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.19.1': + '@typescript-eslint/visitor-keys@8.21.0': dependencies: - '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/types': 8.21.0 eslint-visitor-keys: 4.2.0 acorn-jsx@5.3.2(acorn@8.14.0): @@ -1460,7 +1460,7 @@ snapshots: picomatch@2.3.1: {} - postcss@8.4.49: + postcss@8.5.1: dependencies: nanoid: 3.3.8 picocolors: 1.1.1 @@ -1476,29 +1476,29 @@ snapshots: reusify@1.0.4: {} - rollup@4.30.1: + rollup@4.31.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.30.1 - '@rollup/rollup-android-arm64': 4.30.1 - '@rollup/rollup-darwin-arm64': 4.30.1 - '@rollup/rollup-darwin-x64': 4.30.1 - '@rollup/rollup-freebsd-arm64': 4.30.1 - '@rollup/rollup-freebsd-x64': 4.30.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 - '@rollup/rollup-linux-arm-musleabihf': 4.30.1 - '@rollup/rollup-linux-arm64-gnu': 4.30.1 - '@rollup/rollup-linux-arm64-musl': 4.30.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 - '@rollup/rollup-linux-riscv64-gnu': 4.30.1 - '@rollup/rollup-linux-s390x-gnu': 4.30.1 - '@rollup/rollup-linux-x64-gnu': 4.30.1 - '@rollup/rollup-linux-x64-musl': 4.30.1 - '@rollup/rollup-win32-arm64-msvc': 4.30.1 - '@rollup/rollup-win32-ia32-msvc': 4.30.1 - '@rollup/rollup-win32-x64-msvc': 4.30.1 + '@rollup/rollup-android-arm-eabi': 4.31.0 + '@rollup/rollup-android-arm64': 4.31.0 + '@rollup/rollup-darwin-arm64': 4.31.0 + '@rollup/rollup-darwin-x64': 4.31.0 + '@rollup/rollup-freebsd-arm64': 4.31.0 + '@rollup/rollup-freebsd-x64': 4.31.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.31.0 + '@rollup/rollup-linux-arm-musleabihf': 4.31.0 + '@rollup/rollup-linux-arm64-gnu': 4.31.0 + '@rollup/rollup-linux-arm64-musl': 4.31.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.31.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.31.0 + '@rollup/rollup-linux-riscv64-gnu': 4.31.0 + '@rollup/rollup-linux-s390x-gnu': 4.31.0 + '@rollup/rollup-linux-x64-gnu': 4.31.0 + '@rollup/rollup-linux-x64-musl': 4.31.0 + '@rollup/rollup-win32-arm64-msvc': 4.31.0 + '@rollup/rollup-win32-ia32-msvc': 4.31.0 + '@rollup/rollup-win32-x64-msvc': 4.31.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -1541,13 +1541,13 @@ snapshots: dependencies: punycode: 2.3.1 - vite@6.0.7(@types/node@22.10.5): + vite@6.0.10(@types/node@22.10.7): dependencies: esbuild: 0.24.2 - postcss: 8.4.49 - rollup: 4.30.1 + postcss: 8.5.1 + rollup: 4.31.0 optionalDependencies: - '@types/node': 22.10.5 + '@types/node': 22.10.7 fsevents: 2.3.3 which@2.0.2: From 97944a8a87cb264fe6179a4291af5e20a5c0606a Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 21 Jan 2025 16:24:19 +0100 Subject: [PATCH 28/32] fix: Renamed `items` into `entries`. --- .../aggregators/aggregated-async-iterator.ts | 16 ++++++++-------- src/models/aggregators/aggregated-iterator.ts | 16 ++++++++-------- src/models/aggregators/reduced-iterator.ts | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index a6d1e22..d5a016b 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -20,7 +20,7 @@ import type { MaybeAsyncKeyedIteratee, MaybeAsyncKeyedReducer } from "./types.js * (although it's still possible), but rather use the {@link SmartAsyncIterator.groupBy} method. * * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate. - * See the {@link AggregatedAsyncIterator.keys}, {@link AggregatedAsyncIterator.items} + * See the {@link AggregatedAsyncIterator.keys}, {@link AggregatedAsyncIterator.entries} * & {@link AggregatedAsyncIterator.values} methods. * It does, however, provide the same set of methods to perform * operations and transformations on the elements of the iterator, @@ -994,9 +994,9 @@ export default class AggregatedAsyncIterator /** * An utility method that returns a new {@link SmartAsyncIterator} - * object containing all the items of the iterator. + * object containing all the entries of the iterator. * - * Since the iterator is lazy, the items will be extracted + * Since the iterator is lazy, the entries will be extracted * be executed once the resulting iterator is materialized. * * A new iterator will be created, holding the reference to the original one. @@ -1004,18 +1004,18 @@ export default class AggregatedAsyncIterator * new one is and that consuming one of them will consume the other as well. * * ```ts - * const items = new SmartAsyncIterator([-3, 0, 2, -1, 3]) + * const entries = new SmartAsyncIterator([-3, 0, 2, -1, 3]) * .groupBy(async (value) => value % 2 === 0 ? "even" : "odd") - * .items(); + * .entries(); * - * console.log(await items.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] + * console.log(await entries.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] * ``` * * --- * - * @returns A new {@link SmartAsyncIterator} containing all the items of the iterator. + * @returns A new {@link SmartAsyncIterator} containing all the entries of the iterator. */ - public items(): SmartAsyncIterator<[K, T]> + public entries(): SmartAsyncIterator<[K, T]> { return this._elements; } diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 821066f..8cec5d5 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -12,7 +12,7 @@ import type { KeyedIteratee, KeyedTypeGuardPredicate, KeyedReducer } from "./typ * (although it's still possible), but rather use the {@link SmartIterator.groupBy} method. * * It isn't directly iterable like its parent class but rather needs to specify on what you want to iterate. - * See the {@link AggregatedIterator.keys}, {@link AggregatedIterator.items} + * See the {@link AggregatedIterator.keys}, {@link AggregatedIterator.entries} * & {@link AggregatedIterator.values} methods. * It does, however, provide the same set of methods to perform * operations and transformation on the elements of the iterator, @@ -925,9 +925,9 @@ export default class AggregatedIterator /** * An utility method that returns a new {@link SmartIterator} - * object containing all the items of the iterator. + * object containing all the entries of the iterator. * - * Since the iterator is lazy, the items will be extracted + * Since the iterator is lazy, the entries will be extracted * be executed once the resulting iterator is materialized. * * A new iterator will be created, holding the reference to the original one. @@ -935,18 +935,18 @@ export default class AggregatedIterator * new one is and that consuming one of them will consume the other as well. * * ```ts - * const items = new SmartIterator([-3, 0, 2, -1, 3]) + * const entries = new SmartIterator([-3, 0, 2, -1, 3]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") - * .items(); + * .entries(); * - * console.log(items.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] + * console.log(entries.toArray()); // [["odd", -3], ["even", 0], ["even", 2], ["odd", -1], ["odd", 3]] * ``` * * --- * - * @returns A new {@link SmartIterator} containing all the items of the iterator. + * @returns A new {@link SmartIterator} containing all the entries of the iterator. */ - public items(): SmartIterator<[K, T]> + public entries(): SmartIterator<[K, T]> { return this._elements; } diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index 6b78639..963b951 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -192,7 +192,7 @@ export default class ReducedIterator } }); } - public items(): SmartIterator<[K, T]> + public entries(): SmartIterator<[K, T]> { return this._elements; } @@ -215,11 +215,11 @@ export default class ReducedIterator } public toMap(): Map { - return new Map(this.items()); + return new Map(this.entries()); } public toObject(): Record { - return Object.fromEntries(this.items()) as Record; + return Object.fromEntries(this.entries()) as Record; } public readonly [Symbol.toStringTag]: string = "ReducedIterator"; From ce05a2aa471b0da79ca8b0beb8ec2a81aa57b328 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Thu, 23 Jan 2025 11:14:54 +0100 Subject: [PATCH 29/32] wip: Added some documentation... --- src/core/types.ts | 4 +- src/models/aggregators/types.ts | 73 +++++++++++++++++++++++++++++++++ src/models/callbacks/types.ts | 2 +- src/models/json/types.ts | 6 +-- 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/core/types.ts b/src/core/types.ts index bbbed79..6a7df2b 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -12,7 +12,7 @@ export type Constructor = new (...args: P) => T; /** - * A type representing the return value of `setInterval` function, + * A type that represents the return value of `setInterval` function, * indipendently from the platform it's currently running on. * * For instance, in a browser environment, it's a `number` value representing the interval ID. @@ -29,7 +29,7 @@ export type Constructor = new (.. export type Interval = ReturnType; /** - * A type representing the return value of `setTimeout` function, + * A type that represents the return value of `setTimeout` function, * indipendently from the platform it's currently running on. * * For instance, in a browser environment, it's a `number` value representing the timeout ID. diff --git a/src/models/aggregators/types.ts b/src/models/aggregators/types.ts index f82aad7..3ed7111 100644 --- a/src/models/aggregators/types.ts +++ b/src/models/aggregators/types.ts @@ -1,10 +1,83 @@ import type { MaybePromise } from "../promises/types.js"; +/** + * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like function + * with the addition of a `key` parameter, compared to the JavaScript's standard ones. + * It can be used to transform the elements of an aggregated iterable. + * + * ```ts + * import { SmartIterator } from "@byloth/core"; + * + * const iteratee: KeyedIteratee = (key: string, value: number) => `${value}`; + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map(iteratee); + * + * console.log(results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ export type KeyedIteratee = (key: K, value: T, index: number) => R; + +/** + * An utility type that represents an asynchronous {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like + * function with the addition of a `key` parameter. + * It can be used to transform the elements of an aggregated iterable asynchronously. + * + * ```ts + * import { SmartAsyncIterator } from "@byloth/core"; + * + * const iteratee: AsyncKeyedIteratee = async (key: string, value: number) => `${value}`; + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map(iteratee); + * + * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ export type AsyncKeyedIteratee = (key: K, value: T, index: number) => Promise; + +/** + * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like function + * with the addition of a `key` parameter, which can be either synchronous or asynchronous. + * It can be used to transform the elements of an aggregated iterable. + * + * ```ts + * import { SmartAsyncIterator } from "@byloth/core"; + * + * const iteratee: AsyncKeyedIteratee = [async] (key: string, value: number) => `${value}`; + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map(iteratee); + * + * console.log(await results.toObject()); // { odd: ["-3", "-1", "3", "5"], even: ["0", "2", "6", "8"] } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template R The type of the return value of the iteratee. Default is `void`. + */ export type MaybeAsyncKeyedIteratee = (key: K, value: T, index: number) => MaybePromise; +/** + * An utility type that represents a {@link https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)|predicate}-like + * function with the addition of a `key` parameter, compared to the JavaScript's standard ones, + * which act as a + * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}. export type KeyedTypeGuardPredicate = (key: K, value: T, index: number) => value is R; diff --git a/src/models/callbacks/types.ts b/src/models/callbacks/types.ts index 5e95db0..06ddc71 100644 --- a/src/models/callbacks/types.ts +++ b/src/models/callbacks/types.ts @@ -1,5 +1,5 @@ /** - * A type representing a generic function. + * A type that represents a generic function. * * It can be used to define the signature of a callback, a event handler or any other function. * It's simply a shorthand for the `(...args: A) => R` function signature. diff --git a/src/models/json/types.ts b/src/models/json/types.ts index a19a856..b4f87a8 100644 --- a/src/models/json/types.ts +++ b/src/models/json/types.ts @@ -1,14 +1,14 @@ /** - * A type representing a JSON array. + * A type that represents a JSON array. */ export type JSONArray = JSONValue[]; /** - * A type representing a JSON object. + * A type that represents a JSON object. */ export interface JSONObject { [key: string]: JSONValue } /** - * A type representing all the possible values of a JSON value. + * A type that represents all the possible values of a JSON value. */ export type JSONValue = boolean | number | string | null | JSONObject | JSONArray; From 04c30d7529a63532243be81cbfdfd9be989a729a Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Thu, 23 Jan 2025 18:04:07 +0100 Subject: [PATCH 30/32] fix: Minor JSDoc fixes. --- src/models/aggregators/types.ts | 26 +++++++++++++++++++++++++- src/models/iterators/smart-iterator.ts | 5 +---- src/models/iterators/types.ts | 7 +++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/models/aggregators/types.ts b/src/models/aggregators/types.ts index 3ed7111..c8d3eed 100644 --- a/src/models/aggregators/types.ts +++ b/src/models/aggregators/types.ts @@ -77,7 +77,31 @@ export type MaybeAsyncKeyedIteratee = * An utility type that represents a {@link https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)|predicate}-like * function with the addition of a `key` parameter, compared to the JavaScript's standard ones, * which act as a - * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}. + * {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates|type guard}. + * It can be used to filter the elements of an aggregated iterable + * while allowing the type-system to infer them correctly. + * + * ```ts + * import { SmartIterator } from "@byloth/core"; + * + * const predicate: KeyedTypeGuardPredicate = + * (key: string, value: number | string): value is string => typeof value === "string"; + * + * const results = new SmartIterator([-3, -1, "0", 2, 3, "5", 6, "8"]) + * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd") + * .filter(predicate); + * + * console.log(results.toObject()); // { odd: ["0", "5", "8"], even: [] } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template R + * The type of the return value of the predicate. + * It must be a subtype of `T`. Default is `T`. + */ export type KeyedTypeGuardPredicate = (key: K, value: T, index: number) => value is R; diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index 48109a1..cd9dde6 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -662,10 +662,7 @@ export default class SmartIterator implements Iterat * const iterator = new SmartIterator(["A", "M", "N", "Z"]); * const result = iterator.enumerate(); * - * for (const [index, value] of result) - * { - * console.log(`${index}: ${value}`); // "0: A", "1: M", "2: N", "3: Z" - * } + * console.log(result.toArray()); // [[0, "A"], [1, "M"], [2, "N"], [3, "Z"]] * ``` * * --- diff --git a/src/models/iterators/types.ts b/src/models/iterators/types.ts index f618239..f018351 100644 --- a/src/models/iterators/types.ts +++ b/src/models/iterators/types.ts @@ -53,6 +53,7 @@ export type MaybeAsyncGenerator = Generator * ```ts * const generatorFn: GeneratorFunction = function*() { ... }; * const generator: Generator = generatorFn(); + * * for (const value of generator) * { * console.log(value); @@ -117,10 +118,8 @@ export type MaybeAsyncGeneratorFunction = () => Mayb * ```ts * const iteratee: Iteratee = (value: number) => `${value}`; * const values: string[] = [1, 2, 3, 4, 5].map(iteratee); - * for (const value of values) - * { - * console.log(value); // "1", "2", "3", "4", "5" - * } + * + * console.log(values); // ["1", "2", "3", "4", "5"] * ``` * * --- From 22ac117fe05c3c696950e52101aa19608fb3a706 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Fri, 24 Jan 2025 17:59:58 +0100 Subject: [PATCH 31/32] wip: Some other JSDoc... --- .../aggregators/aggregated-async-iterator.ts | 20 +- src/models/aggregators/aggregated-iterator.ts | 12 +- src/models/aggregators/reduced-iterator.ts | 387 ++++++++++++++++++ src/models/aggregators/types.ts | 74 +++- src/models/iterators/smart-async-iterator.ts | 4 +- src/models/iterators/smart-iterator.ts | 4 +- 6 files changed, 472 insertions(+), 29 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index d5a016b..fe639d6 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -55,7 +55,7 @@ export default class AggregatedAsyncIterator * Initializes a new instance of the {@link AggregatedAsyncIterator} class. * * ```ts - * const iterator = new AggregatedAsyncIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); + * const iterator = new AggregatedAsyncIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); * ``` * * --- @@ -69,7 +69,7 @@ export default class AggregatedAsyncIterator * * ```ts * const elements = fetch([...]); // Promise<[["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]> - * const iterator = new AggregatedAsyncIterator(elements); + * const iterator = new AggregatedAsyncIterator(elements); * ``` * * --- @@ -84,14 +84,14 @@ export default class AggregatedAsyncIterator * ```ts * import { Random } from "@byloth/core"; * - * const iterator = new AggregatedAsyncIterator({ + * const iterator = new AggregatedAsyncIterator({ * _index: 0, * next: () => * { * if (this._index >= 5) { return { done: true, value: undefined }; } * this._index += 1; * - * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * return { done: false, value: [Random.Choice(["A", "B", "C"]), (this._index + 1)] }; * } * }); * ``` @@ -108,14 +108,14 @@ export default class AggregatedAsyncIterator * ```ts * import { Random } from "@byloth/core"; * - * const iterator = new AggregatedAsyncIterator({ + * const iterator = new AggregatedAsyncIterator({ * _index: 0, * next: async () => * { * if (this._index >= 5) { return { done: true, value: undefined }; } * this._index += 1; * - * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * return { done: false, value: [Random.Choice(["A", "B", "C"]), (this._index + 1)] }; * } * }); * ``` @@ -132,7 +132,7 @@ export default class AggregatedAsyncIterator * ```ts * import { range, Random } from "@byloth/core"; * - * const iterator = new AggregatedAsyncIterator(function* () + * const iterator = new AggregatedAsyncIterator(function* () * { * for (const index of range(5)) * { @@ -153,7 +153,7 @@ export default class AggregatedAsyncIterator * ```ts * import { range, Random } from "@byloth/core"; * - * const iterator = new AggregatedAsyncIterator(async function* () + * const iterator = new AggregatedAsyncIterator(async function* () * { * for await (const index of range(5)) * { @@ -329,7 +329,7 @@ export default class AggregatedAsyncIterator * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link AggregatedAsyncIterator} containing the elements that satisfy the condition. */ @@ -733,7 +733,7 @@ export default class AggregatedAsyncIterator * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. */ diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 8cec5d5..583e3c2 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -46,7 +46,7 @@ export default class AggregatedIterator * Initializes a new instance of the {@link AggregatedIterator} class. * * ```ts - * const iterator = new AggregatedIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); + * const iterator = new AggregatedIterator([["A", 1], ["B", 2], ["A", 3], ["C", 4], ["B", 5]]); * ``` * * --- @@ -61,14 +61,14 @@ export default class AggregatedIterator * ```ts * import { Random } from "@byloth/core"; * - * const iterator = new AggregatedIterator({ + * const iterator = new AggregatedIterator({ * _index: 0, * next: () => * { * if (this._index >= 5) { return { done: true, value: undefined }; } * this._index += 1; * - * return { done: false, value: [Random.Choice(["A", "B", "C"]), this._index] }; + * return { done: false, value: [Random.Choice(["A", "B", "C"]), (this._index + 1)] }; * } * }); * ``` @@ -85,7 +85,7 @@ export default class AggregatedIterator * ```ts * import { range, Random } from "@byloth/core"; * - * const iterator = new AggregatedIterator(function* () + * const iterator = new AggregatedIterator(function* () * { * for (const index of range(5)) * { @@ -267,7 +267,7 @@ export default class AggregatedIterator * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link AggregatedIterator} containing only the elements that satisfy the condition. */ @@ -667,7 +667,7 @@ export default class AggregatedIterator * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link ReducedIterator} containing the first element that satisfies the condition for each group. */ diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index 963b951..1ca830c 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -5,19 +5,146 @@ import type { GeneratorFunction } from "../iterators/types.js"; import AggregatedIterator from "./aggregated-iterator.js"; import type { KeyedIteratee, KeyedReducer, KeyedTypeGuardPredicate } from "./types.js"; +/** + * A class representing an aggregated iterator that has been reduced in a lazy and optimized way. + * + * It's part of the {@link AggregatedIterator} and {@link AggregatedAsyncIterator} implementations, + * providing a way to reduce them into a single value or another aggregated iterable. + * For this reason, it isn't recommended to instantiate this class directly + * (although it's still possible), but rather use the reducing methods provided by the aggregated iterators. + * + * It isn't directly iterable, just like its parent class, and needs to specify on what you want to iterate. + * See the {@link ReducedIterator.keys}, {@link ReducedIterator.entries} + * & {@link ReducedIterator.values} methods. + * It does, however, provide the same set of methods to perform + * operations and transformation on the elements of the iterator, + * having also the knowledge and context of the groups to which + * they belong, allowing to handle them in a grouped manner. + * + * This is particularly useful when you have group elements and + * need perform specific operations on the reduced elements. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .count(); + * + * console.log(results.toObject()); // { odd: 4, even: 4 } + * ``` + * + * --- + * + * @template K The type of the key used to group the elements. + * @template T The type of the elements in the iterator. + */ export default class ReducedIterator { + /** + * The internal {@link SmartIterator} object that holds the reduced elements. + */ protected _elements: SmartIterator<[K, T]>; + /** + * Initializes a new instance of the {@link ReducedIterator} class. + * + * ```ts + * const results = new ReducedIterator([["A", 1], ["B", 2], ["C", 4]]); + * ``` + * + * --- + * + * @param iterable A reduced iterable object. + */ public constructor(iterable: Iterable<[K, T]>); + + /** + * Initializes a new instance of the {@link ReducedIterator} class. + * + * ```ts + * const results = new ReducedIterator({ + * _index: 0, + * next: () => + * { + * if (this._index >= 3) { return { done: true, value: undefined }; } + * this._index += 1; + * + * return { done: false, value: [["A", "B", "C"][this._index], (this._index + 1)] }; + * } + * }); + * ``` + * + * --- + * + * @param iterator An reduced iterator object. + */ public constructor(iterator: Iterator<[K, T]>); + + /** + * Initializes a new instance of the {@link ReducedIterator} class. + * + * ```ts + * import { range, Random } from "@byloth/core"; + * + * const results = new ReducedIterator(function* () + * { + * for (const index of range(3)) + * { + * yield [["A", "B", "C"][index], (index + 1)]; + * } + * }); + * ``` + * + * --- + * + * @param generatorFn A generator function that produces the reduced elements. + */ public constructor(generatorFn: GeneratorFunction<[K, T]>); + + /** + * Initializes a new instance of the {@link ReducedIterator} class. + * + * ```ts + * const results = new ReducedIterator(reducedValues); + * ``` + * + * --- + * + * @param argument An iterable, iterator or generator function that produces the reduced elements. + */ public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>); public constructor(argument: Iterable<[K, T]> | Iterator<[K, T]> | GeneratorFunction<[K, T]>) { this._elements = new SmartIterator(argument); } + /** + * Determines whether all elements of the reduced iterator satisfy the given condition. + * See also {@link ReducedIterator.some}. + * + * This method will iterate over all the elements of the iterator checking if they satisfy the condition. + * Once a single element doesn't satisfy the condition, the method will return `false` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link ReducedIterator.find} instead. + * + * If the iterator is infinite and every element satisfies the condition, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .every((key, value) => value > 0); + * + * console.log(results); // true + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns `true` if all elements satisfy the condition, `false` otherwise. + */ public every(predicate: KeyedIteratee): boolean { for (const [index, [key, element]] of this._elements.enumerate()) @@ -27,6 +154,35 @@ export default class ReducedIterator return true; } + + /** + * Determines whether any element of the reduced iterator satisfies the given condition. + * See also {@link ReducedIterator.every}. + * + * This method will iterate over all the elements of the iterator checking if they satisfy the condition. + * Once a single element satisfies the condition, the method will return `true` immediately. + * + * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. + * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. + * Consider using {@link ReducedIterator.find} instead. + * + * If the iterator is infinite and no element satisfies the condition, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .some((key, value) => value > 0); + * + * console.log(results); // true + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns `true` if any element satisfies the condition, `false` otherwise. + */ public some(predicate: KeyedIteratee): boolean { for (const [index, [key, element]] of this._elements.enumerate()) @@ -37,7 +193,70 @@ export default class ReducedIterator return false; } + /** + * Filters the elements of the reduced iterator using a given condition. + * + * This method will iterate over all the elements of the iterator checking if they satisfy the condition. + * If the condition is met, the element will be included in the new iterator. + * + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .filter((key, value) => value > 0); + * + * console.log(results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @param predicate The condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: KeyedIteratee): ReducedIterator; + + /** + * Filters the elements of the reduced iterator using a given type guard predicate. + * + * This method will iterate over all the elements of the iterator checking if they satisfy the condition. + * If the condition is met, the element will be included in the new iterator. + * + * Since the iterator is lazy, the filtering process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, "0", "2", 3, 5, "6", "8"]) + * .groupBy((value) => Number(value) % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .filter((key, value) => typeof value === "number"); + * + * console.log(results.toObject()); // { odd: 4 } + * ``` + * + * --- + * + * @template S + * The type of the elements that satisfy the condition. + * This allows the type-system to infer the correct type of the iterator. + * + * It must be a subtype of the original type of the elements. + * + * @param predicate The type guard condition to check for each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing only the elements that satisfy the condition. + */ public filter(predicate: KeyedTypeGuardPredicate): ReducedIterator; public filter(predicate: KeyedIteratee): ReducedIterator { @@ -51,6 +270,37 @@ export default class ReducedIterator } }); } + + /** + * Maps the elements of the reduced iterator using a given transformation function. + * + * This method will iterate over all the elements of the iterator applying the transformation function. + * The result of the transformation will be included in the new iterator. + * + * Since the iterator is lazy, the mapping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .map((key, value) => value * 2); + * + * console.log(results.toObject()); // { odd: 8, even: 32 } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link ReducedIterator} containing the transformed elements. + */ public map(iteratee: KeyedIteratee): ReducedIterator { const elements = this._elements.enumerate(); @@ -63,7 +313,68 @@ export default class ReducedIterator } }); } + + /** + * Reduces the elements of the reduced iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all the elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the first element of the iterator. + * The last accumulator value will be the final result of the reduction. + * + * Also note that: + * - If an empty iterator is provided, a {@link ValueException} will be thrown. + * - If the iterator is infinite, the method will never return. + * + * ```ts + * const result = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .reduce((key, accumulator, value) => accumulator + value); + * + * console.log(result); // 20 + * ``` + * + * --- + * + * @param reducer The reducer function to apply to the elements of the iterator. + * + * @returns The final value after reducing all the elements of the iterator. + */ public reduce(reducer: KeyedReducer): T; + + /** + * Reduces the elements of the reduced iterator using a given reducer function. + * This method will consume the entire iterator in the process. + * + * It will iterate over all the elements of the iterator applying the reducer function. + * The result of each iteration will be passed as the accumulator to the next one. + * + * The first accumulator value will be the provided initial value. + * The last accumulator value will be the final result of the reduction. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const result = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .reduce((key, { value }, currentValue) => ({ value: value + currentValue }), { value: 0 }); + * + * console.log(result); // { value: 20 } + * ``` + * + * --- + * + * @template A The type of the accumulator value which will also be the type of the final result of the reduction. + * + * @param reducer The reducer function to apply to the elements of the iterator. + * @param initialValue The initial value of the accumulator. + * + * @returns The final result of the reduction. + */ public reduce(reducer: KeyedReducer, initialValue: A): A; public reduce(reducer: KeyedReducer, initialValue?: A): A { @@ -88,6 +399,36 @@ export default class ReducedIterator return accumulator; } + /** + * Flattens the elements of the reduced iterator using a given transformation function. + * + * This method will iterate over all the elements of the iterator applying the transformation function. + * The result of each transformation will be flattened into the new iterator. + * + * Since the iterator is lazy, the flattening process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator.concat([value]), () => []) + * .flatMap((key, value) => value); + * + * console.log(results.toObject()); // { odd: [-3, -1, 3, 5], even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @template V The type of the elements after the transformation. + * + * @param iteratee The transformation function to apply to each element of the iterator. + * + * @returns A new {@link AggregatedIterator} containing the flattened elements. + */ public flatMap(iteratee: KeyedIteratee>): AggregatedIterator { const elements = this._elements.enumerate(); @@ -101,6 +442,36 @@ export default class ReducedIterator }); } + /** + * Drops a given number of elements at the beginning of the reduced iterator. + * The remaining elements will be included in the new iterator. + * See also {@link ReducedIterator.take}. + * + * Since the iterator is lazy, the dropping process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * Only the dropped elements will be consumed in the process. + * The rest of the iterator will be consumed once the new iterator is. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator.concat(value), () => []) + * .drop(2); + * + * console.log(results.toObject()); // { odd: [3, 5], even: [6, 8] } + * ``` + * + * --- + * + * @param count The number of elements to drop. + * + * @returns A new {@link ReducedIterator} containing the remaining elements. + */ public drop(count: number): ReducedIterator { const elements = this._elements.enumerate(); @@ -113,6 +484,22 @@ export default class ReducedIterator } }); } + + /** + * Takes a given number of elements at the beginning of the reduced iterator. + * The elements will be included in the new iterator. + * See also {@link ReducedIterator.drop}. + * + * Since the iterator is lazy, the taking process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * Only the taken elements will be consumed in the process. + * The rest of the original iterator will ... + */ public take(count: number): ReducedIterator { const elements = this._elements.enumerate(); diff --git a/src/models/aggregators/types.ts b/src/models/aggregators/types.ts index c8d3eed..7169ca0 100644 --- a/src/models/aggregators/types.ts +++ b/src/models/aggregators/types.ts @@ -6,8 +6,6 @@ import type { MaybePromise } from "../promises/types.js"; * It can be used to transform the elements of an aggregated iterable. * * ```ts - * import { SmartIterator } from "@byloth/core"; - * * const iteratee: KeyedIteratee = (key: string, value: number) => `${value}`; * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") @@ -30,8 +28,6 @@ export type KeyedIteratee = (key: K, value: * It can be used to transform the elements of an aggregated iterable asynchronously. * * ```ts - * import { SmartAsyncIterator } from "@byloth/core"; - * * const iteratee: AsyncKeyedIteratee = async (key: string, value: number) => `${value}`; * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") @@ -50,12 +46,10 @@ export type AsyncKeyedIteratee = (key: K, va /** * An utility type that represents an {@link https://en.wikipedia.org/wiki/Iteratee|iteratee}-like function - * with the addition of a `key` parameter, which can be either synchronous or asynchronous. + * with the addition of a `key` parameter that can be either synchronous or asynchronous. * It can be used to transform the elements of an aggregated iterable. * * ```ts - * import { SmartAsyncIterator } from "@byloth/core"; - * * const iteratee: AsyncKeyedIteratee = [async] (key: string, value: number) => `${value}`; * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") @@ -82,8 +76,6 @@ export type MaybeAsyncKeyedIteratee = * while allowing the type-system to infer them correctly. * * ```ts - * import { SmartIterator } from "@byloth/core"; - * * const predicate: KeyedTypeGuardPredicate = * (key: string, value: number | string): value is string => typeof value === "string"; * @@ -112,9 +104,73 @@ export type KeyedTypeGuardPredicate = // export type MaybeAsyncKeyedTypeGuardPredicate = // (key: K, value: T, index: number) => value is MaybePromise; +/** + * An utility type that represents a reducer-like function. + * It can be used to reduce the elements of an aggregated iterable into a single value. + * + * ```ts + * const sum: KeyedReducer = + * (key: string, accumulator: number, value: number) => accumulator + value; + * + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce(sum); + * + * console.log(results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type KeyedReducer = (key: K, accumulator: A, value: T, index: number) => A; + +/** + * An utility type that represents an asynchronous reducer-like function. + * It can be used to reduce the elements of an aggregated iterable into a single value. + * + * ```ts + * const sum: AsyncKeyedReducer = + * async (key: string, accumulator: number, value: number) => accumulator + value; + * + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce(sum); + * + * console.log(await results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type AsyncKeyedReducer = (key: K, accumulator: A, value: T, index: number) => Promise; +/** + * An utility type that represents a reducer-like function that can be either synchronous or asynchronous. + * It can be used to reduce the elements of an aggregated iterable into a single value. + * + * ```ts + * const sum: MaybeAsyncKeyedReducer = + * [async] (key: string, accumulator: number, value: number) => accumulator + value; + * + * const results = new SmartAsyncIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce(sum); + * + * console.log(await results.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @template K The type of the key used to aggregate elements in the iterable. + * @template T The type of the elements in the iterable. + * @template A The type of the accumulator. + */ export type MaybeAsyncKeyedReducer = (key: K, accumulator: A, value: T, index: number) => MaybePromise; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 4427319..892816e 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -367,7 +367,7 @@ export default class SmartAsyncIterator implements A * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link SmartAsyncIterator} containing only the elements that satisfy the condition. */ @@ -738,7 +738,7 @@ export default class SmartAsyncIterator implements A * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A promise that will resolve to the first element that satisfies the condition, `undefined` otherwise. */ diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index cd9dde6..fe8b4b5 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -259,7 +259,7 @@ export default class SmartIterator implements Iterat * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition. */ @@ -627,7 +627,7 @@ export default class SmartIterator implements Iterat * * It must be a subtype of the original type of the elements. * - * @param predicate The condition to check for each element of the iterator. + * @param predicate The type guard condition to check for each element of the iterator. * * @returns The first element that satisfies the condition, `undefined` otherwise. */ From 0061acf89ae8c5b3a02a304acd7202012554ab77 Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Sat, 25 Jan 2025 16:47:54 +0100 Subject: [PATCH 32/32] =?UTF-8?q?imp:=20Completed=20the=20JSDoc!=20FINALLY?= =?UTF-8?q?!=20=F0=9F=A5=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aggregators/aggregated-async-iterator.ts | 15 +- src/models/aggregators/aggregated-iterator.ts | 15 +- src/models/aggregators/reduced-iterator.ts | 287 +++++++++++++++++- src/models/iterators/smart-async-iterator.ts | 13 +- src/models/iterators/smart-iterator.ts | 15 +- src/utils/iterator.ts | 6 +- 6 files changed, 318 insertions(+), 33 deletions(-) diff --git a/src/models/aggregators/aggregated-async-iterator.ts b/src/models/aggregators/aggregated-async-iterator.ts index fe639d6..95df719 100644 --- a/src/models/aggregators/aggregated-async-iterator.ts +++ b/src/models/aggregators/aggregated-async-iterator.ts @@ -547,7 +547,7 @@ export default class AggregatedAsyncIterator * new one is and that consuming one of them will consume the other as well. * * ```ts - * const results = new SmartAsyncIterator([[-3, -1], [0, 2], [3, 5], [6, 8]]) + * const results = new SmartAsyncIterator([[-3, -1], 0, 2, 3, 5, [6, 8]]) * .groupBy(async ([value, _]) => value % 2 === 0 ? "even" : "odd") * .flatMap(async (key, values) => values); * @@ -562,7 +562,7 @@ export default class AggregatedAsyncIterator * * @returns A new {@link AggregatedAsyncIterator} containing the transformed elements. */ - public flatMap(iteratee: MaybeAsyncKeyedIteratee>): AggregatedAsyncIterator + public flatMap(iteratee: MaybeAsyncKeyedIteratee): AggregatedAsyncIterator { const elements = this._elements; @@ -575,7 +575,11 @@ export default class AggregatedAsyncIterator const index = indexes.get(key) ?? 0; const values = await iteratee(key, element, index); - for await (const value of values) { yield [key, value]; } + if (values instanceof Array) + { + for (const value of values) { yield [key, value]; } + } + else { yield [key, values]; } indexes.set(key, index + 1); } @@ -792,7 +796,7 @@ export default class AggregatedAsyncIterator * Removes all duplicate elements from within each group of the iterator. * The first occurrence of each element will be included in the new iterator. * - * Since the iterator is lazy, the uniqueness process will + * Since the iterator is lazy, the deduplication process will * be executed once the resulting iterator is materialized. * * A new iterator will be created, holding the reference to the original one. @@ -994,7 +998,8 @@ export default class AggregatedAsyncIterator /** * An utility method that returns a new {@link SmartAsyncIterator} - * object containing all the entries of the iterator. + * object containing all the entries of the iterator. + * Each entry is a tuple containing the key and the element. * * Since the iterator is lazy, the entries will be extracted * be executed once the resulting iterator is materialized. diff --git a/src/models/aggregators/aggregated-iterator.ts b/src/models/aggregators/aggregated-iterator.ts index 583e3c2..9ba4b92 100644 --- a/src/models/aggregators/aggregated-iterator.ts +++ b/src/models/aggregators/aggregated-iterator.ts @@ -481,7 +481,7 @@ export default class AggregatedIterator * new one is and that consuming one of them will consume the other as well. * * ```ts - * const results = new SmartIterator([[-3, -1], [0, 2], [3, 5], [6, 8]]) + * const results = new SmartIterator([[-3, -1], 0, 2, 3, 5, [6, 8]]) * .groupBy(([value, _]) => value % 2 === 0 ? "even" : "odd") * .flatMap((key, values) => values); * @@ -496,7 +496,7 @@ export default class AggregatedIterator * * @returns A new {@link AggregatedIterator} containing the transformed elements. */ - public flatMap(iteratee: KeyedIteratee>): AggregatedIterator + public flatMap(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements; @@ -509,7 +509,11 @@ export default class AggregatedIterator const index = indexes.get(key) ?? 0; const values = iteratee(key, element, index); - for (const value of values) { yield [key, value]; } + if (values instanceof Array) + { + for (const value of values) { yield [key, value]; } + } + else { yield [key, values]; } indexes.set(key, index + 1); } @@ -724,7 +728,7 @@ export default class AggregatedIterator * Removes all duplicate elements from within each group of the iterator. * The first occurrence of each element will be included in the new iterator. * - * Since the iterator is lazy, the uniqueness process will + * Since the iterator is lazy, the deduplication process will * be executed once the resulting iterator is materialized. * * A new iterator will be created, holding the reference to the original one. @@ -925,7 +929,8 @@ export default class AggregatedIterator /** * An utility method that returns a new {@link SmartIterator} - * object containing all the entries of the iterator. + * object containing all the entries of the iterator. + * Each entry is a tuple containing the key and the element. * * Since the iterator is lazy, the entries will be extracted * be executed once the resulting iterator is materialized. diff --git a/src/models/aggregators/reduced-iterator.ts b/src/models/aggregators/reduced-iterator.ts index 1ca830c..1ffe238 100644 --- a/src/models/aggregators/reduced-iterator.ts +++ b/src/models/aggregators/reduced-iterator.ts @@ -429,7 +429,7 @@ export default class ReducedIterator * * @returns A new {@link AggregatedIterator} containing the flattened elements. */ - public flatMap(iteratee: KeyedIteratee>): AggregatedIterator + public flatMap(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements.enumerate(); @@ -437,7 +437,13 @@ export default class ReducedIterator { for (const [index, [key, element]] of elements) { - for (const value of iteratee(key, element, index)) { yield [key, value]; } + const values = iteratee(key, element, index); + + if (values instanceof Array) + { + for (const value of values) { yield [key, value]; } + } + else { yield [key, values]; } } }); } @@ -461,9 +467,9 @@ export default class ReducedIterator * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) * .groupBy((value) => value % 2 === 0 ? "even" : "odd") * .reduce((key, accumulator, value) => accumulator.concat(value), () => []) - * .drop(2); + * .drop(1); * - * console.log(results.toObject()); // { odd: [3, 5], even: [6, 8] } + * console.log(results.toObject()); // { even: [0, 2, 6, 8] } * ``` * * --- @@ -497,8 +503,25 @@ export default class ReducedIterator * This means that the original iterator won't be consumed until the * new one is and that consuming one of them will consume the other as well. * - * Only the taken elements will be consumed in the process. - * The rest of the original iterator will ... + * Only the taken elements will be consumed from the original reduced iterator. + * The rest of the original reduced iterator will be available for further consumption. + * + * ```ts + * const reduced = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator.concat(value), () => []); + * + * const results = iterator.take(1); + * + * console.log(results.toObject()); // { odd: [-3, -1, 3, 5] } + * console.log(reduced.toObject()); // { even: [0, 2, 6, 8] } + * ``` + * + * --- + * + * @param count The number of elements to take. + * + * @returns A new {@link ReducedIterator} containing the taken elements. */ public take(count: number): ReducedIterator { @@ -515,10 +538,62 @@ export default class ReducedIterator }); } + public find() + { + // TODO! + } + + /** + * Enumerates the elements of the reduced iterator. + * Each element is paired with its index in a new iterator. + * + * Since the iterator is lazy, the enumeration process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new ReducedIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .enumerate(); + * + * console.log(results.toObject()); // [[0, 4], [1, 16]] + * ``` + * + * --- + * + * @returns A new {@link ReducedIterator} object containing the enumerated elements. + */ public enumerate(): ReducedIterator { return this.map((_, element, index) => [index, element]); } + + /** + * Removes all duplicate elements from the reduced iterator. + * The first occurrence of each element will be kept. + * + * Since the iterator is lazy, the deduplication process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new ReducedIterator([-3, -1, 0, 2, 3, 6, -3, -1, 1, 5, 6, 8, 7, 2]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .map((key, value) => Math.abs(value)) + * .reduce((key, accumulator, value) => accumulator + value) + * .unique(); + * + * console.log(results.toObject()); // { odd: 24 } + * + * @returns A new {@link ReducedIterator} containing only the unique elements. + */ public unique(): ReducedIterator { const elements = this._elements; @@ -537,6 +612,25 @@ export default class ReducedIterator }); } + /** + * Counts the number of elements in the reduced iterator. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .count(); + * + * console.log(results); // 2 + * ``` + * + * --- + * + * @returns The number of elements in the iterator. + */ public count(): number { let index = 0; @@ -546,6 +640,28 @@ export default class ReducedIterator return index; } + /** + * Iterates over all elements of the reduced iterator. + * The elements are passed to the function along with their key and index. + * + * This method will consume the entire iterator in the process. + * If the iterator is infinite, the method will never return. + * + * ```ts + * const reduced = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value); + * + * reduced.forEach((key, value, index) => + * { + * console.log(`#${index} - ${key}: ${value}`); // "#0 - odd: 4", "#1 - even: 16" + * }); + * ``` + * + * --- + * + * @param iteratee The function to apply to each element of the reduced iterator. + */ public forEach(iteratee: KeyedIteratee): void { for (const [index, [key, element]] of this._elements.enumerate()) @@ -554,6 +670,34 @@ export default class ReducedIterator } } + /** + * Reaggregates the elements of the reduced iterator. + * The elements are grouped by a new key computed by the given iteratee function. + * + * Since the iterator is lazy, the reorganizing process will + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const results = new SmartIterator([-3, -1, 0, 2, 3, 5, -6, -8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .reorganizeBy((key, value) => value > 0 ? "positive" : "negative"); + * + * console.log(results.toObject()); // { positive: 4, negative: -12 } + * ``` + * + * --- + * + * @template J The type of the new keys used to group the elements. + * + * @param iteratee The function to determine the new key of each element of the iterator. + * + * @returns A new {@link AggregatedIterator} containing the elements reorganized by the new keys. + */ public reorganizeBy(iteratee: KeyedIteratee): AggregatedIterator { const elements = this._elements.enumerate(); @@ -567,6 +711,30 @@ export default class ReducedIterator }); } + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the keys of the iterator. + * + * Since the iterator is lazy, the keys will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const keys = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .keys(); + * + * console.log(keys.toArray()); // ["odd", "even"] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the keys of the iterator. + */ public keys(): SmartIterator { const elements = this._elements; @@ -579,10 +747,61 @@ export default class ReducedIterator } }); } + + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the entries of the iterator. + * Each entry is a tuple containing the key and the element. + * + * Since the iterator is lazy, the entries will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const entries = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .entries(); + * + * console.log(entries.toArray()); // [["odd", 4], ["even", 16]] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the entries of the iterator. + */ public entries(): SmartIterator<[K, T]> { return this._elements; } + + /** + * An utility method that returns a new {@link SmartIterator} + * object containing all the values of the iterator. + * + * Since the iterator is lazy, the values will be extracted + * be executed once the resulting iterator is materialized. + * + * A new iterator will be created, holding the reference to the original one. + * This means that the original iterator won't be consumed until the + * new one is and that consuming one of them will consume the other as well. + * + * ```ts + * const values = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value) + * .values(); + * + * console.log(values.toArray()); // [4, 16] + * ``` + * + * --- + * + * @returns A new {@link SmartIterator} containing all the values of the iterator. + */ public values(): SmartIterator { const elements = this._elements; @@ -596,14 +815,70 @@ export default class ReducedIterator }); } + /** + * Materializes the iterator into an array. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const reduced = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value); + * + * console.log(reduced.toArray()); // [4, 16] + * ``` + * + * --- + * + * @returns The {@link Array} containing all elements of the iterator. + */ public toArray(): T[] { return Array.from(this.values()); } + + /** + * Materializes the iterator into a map. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const reduced = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value); + * + * console.log(reduced.toMap()); // Map(2) { "odd" => 4, "even" => 16 } + * ``` + * + * --- + * + * @returns The {@link Map} containing all elements of the iterator. + */ public toMap(): Map { return new Map(this.entries()); } + + /** + * Materializes the iterator into an object. + * This method will consume the entire iterator in the process. + * + * If the iterator is infinite, the method will never return. + * + * ```ts + * const reduced = new SmartIterator([-3, -1, 0, 2, 3, 5, 6, 8]) + * .groupBy((value) => value % 2 === 0 ? "even" : "odd") + * .reduce((key, accumulator, value) => accumulator + value); + * + * console.log(reduced.toObject()); // { odd: 4, even: 16 } + * ``` + * + * --- + * + * @returns The {@link Object} containing all elements of the iterator. + */ public toObject(): Record { return Object.fromEntries(this.entries()) as Record; diff --git a/src/models/iterators/smart-async-iterator.ts b/src/models/iterators/smart-async-iterator.ts index 892816e..2b4629e 100644 --- a/src/models/iterators/smart-async-iterator.ts +++ b/src/models/iterators/smart-async-iterator.ts @@ -7,7 +7,6 @@ import type { MaybeAsyncGeneratorFunction, MaybeAsyncIteratee, MaybeAsyncReducer, - MaybeAsyncIterable, MaybeAsyncIteratorLike } from "./types.js"; @@ -536,7 +535,7 @@ export default class SmartAsyncIterator implements A * new one is and that consuming one of them will consume the other as well. * * ```ts - * const iterator = new SmartAsyncIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); + * const iterator = new SmartAsyncIterator([[-2, -1], 0, 1, 2, [3, 4, 5]]); * const result = iterator.flatMap(async (value) => value); * * console.log(await result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5] @@ -550,7 +549,7 @@ export default class SmartAsyncIterator implements A * * @returns A new {@link SmartAsyncIterator} containing the flattened elements. */ - public flatMap(iteratee: MaybeAsyncIteratee>): SmartAsyncIterator + public flatMap(iteratee: MaybeAsyncIteratee): SmartAsyncIterator { const iterator = this._iterator; @@ -564,11 +563,11 @@ export default class SmartAsyncIterator implements A if (result.done) { return result.value; } const elements = await iteratee(result.value, index); - - for await (const element of elements) + if (elements instanceof Array) { - yield element; + for (const value of elements) { yield value; } } + else { yield elements; } index += 1; } @@ -863,7 +862,7 @@ export default class SmartAsyncIterator implements A } /** - * Iterates over all elements of the iterator applying a given function. + * Iterates over all elements of the iterator. * The elements are passed to the function along with their index. * * This method will consume the entire iterator in the process. diff --git a/src/models/iterators/smart-iterator.ts b/src/models/iterators/smart-iterator.ts index fe8b4b5..4649bce 100644 --- a/src/models/iterators/smart-iterator.ts +++ b/src/models/iterators/smart-iterator.ts @@ -428,7 +428,7 @@ export default class SmartIterator implements Iterat * new one is and that consuming one of them will consume the other as well. * * ```ts - * const iterator = new SmartIterator([[-2, -1], [0], [1, 2], [3, 4, 5]]); + * const iterator = new SmartIterator([[-2, -1], 0, 1, 2, [3, 4, 5]]); * const result = iterator.flatMap((value) => value); * * console.log(result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5] @@ -442,7 +442,7 @@ export default class SmartIterator implements Iterat * * @returns A new {@link SmartIterator} containing the flattened elements. */ - public flatMap(iteratee: Iteratee>): SmartIterator + public flatMap(iteratee: Iteratee): SmartIterator { const iterator = this._iterator; @@ -455,11 +455,12 @@ export default class SmartIterator implements Iterat const result = iterator.next(); if (result.done) { return result.value; } - const iterable = iteratee(result.value, index); - for (const value of iterable) + const elements = iteratee(result.value, index); + if (elements instanceof Array) { - yield value; + for (const value of elements) { yield value; } } + else { yield elements; } index += 1; } @@ -749,7 +750,7 @@ export default class SmartIterator implements Iterat } /** - * Iterates over all elements of the iterator applying a given function. + * Iterates over all elements of the iterator. * The elements are passed to the function along with their index. * * This method will consume the entire iterator in the process. @@ -946,7 +947,7 @@ export default class SmartIterator implements Iterat * console.log(result); // [0, 1, 2, 3, 4] * ``` * - * @returns The array containing all elements of the iterator. + * @returns The {@link Array} containing all elements of the iterator. */ public toArray(): T[] { diff --git a/src/utils/iterator.ts b/src/utils/iterator.ts index 0effb1a..7e363a3 100644 --- a/src/utils/iterator.ts +++ b/src/utils/iterator.ts @@ -25,7 +25,7 @@ import { SmartIterator } from "../models/index.js"; * * @returns A new {@link SmartIterator} object that chains the iterables into a single one. */ -export function chain(...iterables: Iterable[]): SmartIterator +export function chain(...iterables: readonly Iterable[]): SmartIterator { return new SmartIterator(function* () { @@ -57,7 +57,7 @@ export function chain(...iterables: Iterable[]): SmartIterator */ export function count(elements: Iterable): number { - if (Array.isArray(elements)) { return elements.length; } + if (elements instanceof Array) { return elements.length; } let _count = 0; for (const _ of elements) { _count += 1; } @@ -89,7 +89,7 @@ export function count(elements: Iterable): number * * @param elements The iterable to enumerate. * - * @returns A new {@link SmartIterator} object that enumerates the elements of the given iterable. + * @returns A new {@link SmartIterator} object containing the enumerated elements. */ export function enumerate(elements: Iterable): SmartIterator<[number, T]> {