@byloth/core
Version:
An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧
1 lines • 405 kB
Source Map (JSON)
{"version":3,"file":"core.umd.cjs","sources":["../src/helpers.ts","../src/models/exceptions/core.ts","../src/models/exceptions/index.ts","../src/models/iterators/smart-iterator.ts","../src/models/aggregators/reduced-iterator.ts","../src/models/aggregators/aggregated-async-iterator.ts","../src/models/iterators/smart-async-iterator.ts","../src/models/aggregators/aggregated-iterator.ts","../src/models/callbacks/callable-object.ts","../src/models/callbacks/publisher.ts","../src/models/callbacks/switchable-callback.ts","../src/models/collections/map-view.ts","../src/models/collections/set-view.ts","../src/models/json/json-storage.ts","../src/models/promises/smart-promise.ts","../src/models/promises/deferred-promise.ts","../src/models/promises/timed-promise.ts","../src/models/promises/promise-queue.ts","../src/utils/date.ts","../src/models/timers/game-loop.ts","../src/models/timers/clock.ts","../src/models/timers/countdown.ts","../src/utils/curve.ts","../src/utils/random.ts","../src/utils/async.ts","../src/utils/dom.ts","../src/utils/iterator.ts","../src/utils/math.ts","../src/utils/string.ts","../src/index.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-ts-comment */\n\n/**\n * An utility constant that indicates whether the current environment is a browser.\n */\n// @ts-ignore\nexport const isBrowser = ((typeof window !== \"undefined\") && (typeof window.document !== \"undefined\"));\n\n/**\n * An utility constant that indicates whether the current environment is a Node.js runtime.\n */\n// @ts-ignore\nexport const isNode = ((typeof process !== \"undefined\") && !!(process.versions?.node));\n\n/**\n * An utility constant that indicates whether the current environment is a Web Worker.\n */\n// @ts-ignore\nexport const isWorker = ((typeof self === \"object\") && (self.constructor?.name === \"DedicatedWorkerGlobalScope\"));\n","/**\n * A class representing an exception, subclass of the native {@link Error} class. \n * It's the base class for any other further exception.\n *\n * It allows to chain exceptions together, tracking the initial cause of an error and\n * storing its stack trace while providing a clear and friendly message to the user.\n *\n * ---\n *\n * @example\n * ```ts\n * try { loadGameSaves(); }\n * catch (error)\n * {\n * throw new Exception(\"The game saves may be corrupted. Try to restart the game.\", error);\n * // Uncaught Exception: The game saves may be corrupted. Try to restart the game.\n * // at /src/game/index.ts:37:15\n * // at /src/main.ts:23:17\n * //\n * // Caused by SyntaxError: Unexpected end of JSON input\n * // at /src/models/saves.ts:47:17\n * // at /src/game/index.ts:12:9\n * // at /src/main.ts:23:17\n * }\n * ```\n */\nexport default class Exception extends Error\n{\n /**\n * A static method to convert a generic caught error, ensuring it's an instance of the {@link Exception} class.\n *\n * ---\n *\n * @example\n * ```ts\n * try { [...] }\n * catch (error)\n * {\n * const exc = Exception.FromUnknown(error);\n *\n * [...]\n * }\n * ```\n *\n * ---\n *\n * @param error The caught error to convert.\n *\n * @returns An instance of the {@link Exception} class.\n */\n public static FromUnknown(error: unknown): Exception\n {\n if (error instanceof Exception)\n {\n return error;\n }\n if (error instanceof Error)\n {\n const exc = new Exception(error.message);\n\n exc.stack = error.stack;\n exc.name = error.name;\n\n return exc;\n }\n\n return new Exception(`${error}`);\n }\n\n /**\n * Initializes a new instance of the {@link Exception} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new Exception(\"An error occurred while processing the request.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"Exception\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"Exception\")\n {\n super(message);\n\n this.cause = cause;\n this.name = name;\n\n if (cause)\n {\n if (cause instanceof Error)\n {\n this.stack += `\\n\\nCaused by ${cause.stack}`;\n }\n else\n {\n this.stack += `\\n\\nCaused by ${cause}`;\n }\n }\n }\n\n public readonly [Symbol.toStringTag]: string = \"Exception\";\n}\n\n/**\n * An utility class representing that kind of situation where the program should never reach. \n * Also commonly used to satisfy the type-system, but not part of a real feasible scenario.\n *\n * It provides a clear and friendly message by default.\n *\n * ---\n *\n * @example\n * ```ts\n * function checkCase(value: \"A\" | \"B\" | \"C\"): 1 | 2 | 3\n * {\n * switch (value)\n * {\n * case \"A\": return 1;\n * case \"B\": return 2;\n * case \"C\": return 3;\n * default: throw new FatalErrorException();\n * }\n * }\n * ```\n */\nexport class FatalErrorException extends Exception\n{\n /**\n * Initializes a new instance of the {@link FatalErrorException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new FatalErrorException(\"This error should never happen. Please, contact the support team.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"FatalErrorException\"`.\n */\n public constructor(message?: string, cause?: unknown, name = \"FatalErrorException\")\n {\n if (message === undefined)\n {\n message = \"The program has encountered an unrecoverable error and cannot continue as expected. \" +\n \"Please, try again later. If the problem persists, contact the support team.\";\n }\n\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"FatalErrorException\";\n}\n\n/**\n * An utility class representing a situation where a feature isn't implemented yet. \n * It's commonly used as a placeholder for future implementations.\n *\n * It provides a clear and friendly message by default.\n *\n * ---\n *\n * @example\n * ```ts\n * class Database\n * {\n * public async connect(): Promise<void>\n * {\n * throw new NotImplementedException();\n * }\n * }\n * ```\n */\nexport class NotImplementedException extends FatalErrorException\n{\n /**\n * Initializes a new instance of the {@link NotImplementedException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new NotImplementedException(\"This method hasn't been implemented yet. Check back later.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"NotImplementedException\"`.\n */\n public constructor(message?: string, cause?: unknown, name = \"NotImplementedException\")\n {\n if (message === undefined)\n {\n message = \"This feature isn't implemented yet. Please, try again later.\";\n }\n\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"NotImplementedException\";\n}\n","import Exception from \"./core.js\";\n\n/**\n * A class representing a generic exception that can be thrown when a file\n * operation fails, such as reading, writing, copying, moving, deleting, etc...\n *\n * It can also be used to catch all file-related exceptions at once.\n *\n * ---\n *\n * @example\n * ```ts\n * try { [...] }\n * catch (error)\n * {\n * if (error instanceof FileException)\n * {\n * // A file-related exception occurred. Handle it...\n * }\n * }\n * ```\n */\nexport class FileException extends Exception\n{\n /**\n * Initializes a new instance of the {@link FileException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new FileException(\"An error occurred while trying to read the file.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"FileException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"FileException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"FileException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a file already exists.\n *\n * ---\n *\n * @example\n * ```ts\n * import { existsSync } from \"node:fs\";\n *\n * if (existsSync(\"file.txt\"))\n * {\n * throw new FileExistsException(\"The file named 'file.txt' already exists.\");\n * }\n * ```\n */\nexport class FileExistsException extends FileException\n{\n /**\n * Initializes a new instance of the {@link FileExistsException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new FileExistsException(\"The file named 'data.json' already exists on the server.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"FileExistsException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"FileExistsException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"FileExistsException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a file isn't found.\n *\n * ---\n *\n * @example\n * ```ts\n * import { existsSync } from \"node:fs\";\n *\n * if (!existsSync(\"file.txt\"))\n * {\n * throw new FileNotFoundException(\"The file named 'file.txt' wasn't found.\");\n * }\n * ```\n */\nexport class FileNotFoundException extends FileException\n{\n /**\n * Initializes a new instance of the {@link FileNotFoundException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new FileNotFoundException(\"The file named 'data.json' wasn't found on the server.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"FileNotFoundException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"FileNotFoundException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"FileNotFoundException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a key is invalid or not found. \n * It's commonly used when working with dictionaries, maps, objects, sets, etc...\n *\n * ---\n *\n * @example\n * ```ts\n * const map = new Map<string, number>();\n * if (!map.has(\"hash\"))\n * {\n * throw new KeyException(\"The key 'hash' wasn't found in the collection.\");\n * }\n * ```\n */\nexport class KeyException extends Exception\n{\n /**\n * Initializes a new instance of the {@link KeyException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new KeyException(\"The 'id' key wasn't found in the dictionary.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"KeyException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"KeyException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"KeyException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a network operation fails. \n * It's commonly used when it's unable to connect to a server or when a request times out.\n *\n * ---\n *\n * @example\n * ```ts\n * import axios, { isAxiosError } from \"axios\";\n *\n * try { await axios.get(\"https://api.example.com/data\"); }\n * catch (error)\n * {\n * if (isAxiosError(error) && !error.response)\n * {\n * throw new NetworkException(\n * \"Unable to establish a connection to the server. \" +\n * \"Please, check your internet connection and try again.\"\n * );\n * }\n * }\n * ```\n */\nexport class NetworkException extends Exception\n{\n /**\n * Initializes a new instance of the {@link NetworkException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new NetworkException(\"Couldn't connect to the server. Please, try again later.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"NetworkException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"NetworkException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"NetworkException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a permission is denied. \n * It's commonly used when an user tries to access a restricted resource or perform a forbidden action.\n *\n * ---\n *\n * @example\n * ```ts\n * const $user = useUserStore();\n * if (!$user.isAdmin)\n * {\n * throw new PermissionException(\"You don't have permission to perform this action.\");\n * }\n * ```\n */\nexport class PermissionException extends Exception\n{\n /**\n * Initializes a new instance of the {@link PermissionException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new PermissionException(\"You don't have permission to access this resource.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"PermissionException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"PermissionException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"PermissionException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a reference is invalid or not found. \n * It's commonly used when a variable is `null`, `undefined` or when an object doesn't exist.\n *\n * ---\n *\n * @example\n * ```ts\n * const $el = document.getElementById(\"app\");\n * if ($el === null)\n * {\n * throw new ReferenceException(\"The element with the ID 'app' wasn't found in the document.\");\n * }\n * ```\n */\nexport class ReferenceException extends Exception\n{\n /**\n * Initializes a new instance of the {@link ReferenceException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new ReferenceException(\"The 'canvas' element wasn't found in the document.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"ReferenceException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"ReferenceException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"ReferenceException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a runtime error occurs. \n * It's commonly used when an unexpected condition is encountered during the execution of a program.\n *\n * ---\n *\n * @example\n * ```ts\n * let status: \"enabled\" | \"disabled\" = \"enabled\";\n *\n * function enable(): void\n * {\n * if (status === \"enabled\") { throw new RuntimeException(\"The feature is already enabled.\"); }\n * status = \"enabled\";\n * }\n * ```\n */\nexport class RuntimeException extends Exception\n{\n /**\n * Initializes a new instance of the {@link RuntimeException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new RuntimeException(\"The received input seems to be malformed or corrupted.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"RuntimeException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"RuntimeException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"RuntimeException\";\n}\n\n/**\n * A class representing an exception that can be thrown when an environment\n * isn't properly configured or when a required variable isn't set. \n * It can also be used when the environment on which the program is running is unsupported.\n *\n * ---\n *\n * @example\n * ```ts\n * if (!navigator.geolocation)\n * {\n * throw new EnvironmentException(\"The Geolocation API isn't supported in this environment.\");\n * }\n * ```\n */\nexport class EnvironmentException extends RuntimeException\n{\n /**\n * Initializes a new instance of the {@link EnvironmentException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new EnvironmentException(\"The required environment variable 'API_KEY' isn't set.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"EnvironmentException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"EnvironmentException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"EnvironmentException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a timeout occurs. \n * It's commonly used when a task takes too long to complete or when a request times out.\n *\n * ---\n *\n * @example\n * ```ts\n * const timeoutId = setTimeout(() => { throw new TimeoutException(\"The request timed out.\"); }, 5_000);\n * const response = await fetch(\"https://api.example.com/data\");\n *\n * clearTimeout(timeoutId);\n * ```\n */\nexport class TimeoutException extends Exception\n{\n /**\n * Initializes a new instance of the {@link TimeoutException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new TimeoutException(\"The task took too long to complete.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"TimeoutException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"TimeoutException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"TimeoutException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a type is invalid or not supported. \n * It's commonly used when a function receives an unexpected type of argument.\n *\n * ---\n *\n * @example\n * ```ts\n * function greet(name: string): void\n * {\n * if (typeof name !== \"string\")\n * {\n * throw new TypeException(\"The 'name' argument must be a valid string.\");\n * }\n * }\n * ```\n */\nexport class TypeException extends Exception\n{\n /**\n * Initializes a new instance of the {@link TypeException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new TypeException(\"The 'username' argument must be a valid string.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"TypeException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"TypeException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"TypeException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a value is invalid. \n * It's commonly used when a function receives an unexpected value as an argument.\n *\n * ---\n *\n * @example\n * ```ts\n * function setVolume(value: number): void\n * {\n * if (value < 0)\n * {\n * throw new ValueException(\"The 'value' argument must be greater than or equal to 0.\");\n * }\n * }\n * ```\n */\nexport class ValueException extends Exception\n{\n /**\n * Initializes a new instance of the {@link ValueException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new ValueException(\"The 'grade' argument cannot be negative.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"ValueException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"ValueException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"ValueException\";\n}\n\n/**\n * A class representing an exception that can be thrown when a value is out of range. \n * It's commonly used when a function receives an unexpected value as an argument.\n *\n * ---\n *\n * @example\n * ```ts\n * function setVolume(value: number): void\n * {\n * if ((value < 0) || (value > 100))\n * {\n * throw new RangeException(\"The 'value' argument must be between 0 and 100.\");\n * }\n * }\n * ```\n */\nexport class RangeException extends ValueException\n{\n /**\n * Initializes a new instance of the {@link RangeException} class.\n *\n * ---\n *\n * @example\n * ```ts\n * throw new RangeException(\"The 'percentage' argument must be between 0 and 100.\");\n * ```\n *\n * ---\n *\n * @param message The message that describes the error.\n * @param cause The previous caught error that caused this one, if any.\n * @param name The name of the exception. Default is `\"RangeException\"`.\n */\n public constructor(message: string, cause?: unknown, name = \"RangeException\")\n {\n super(message, cause, name);\n }\n\n public override readonly [Symbol.toStringTag]: string = \"RangeException\";\n}\n\nexport { Exception };\nexport { FatalErrorException, NotImplementedException } from \"./core.js\";\n","import AggregatedIterator from \"../aggregators/aggregated-iterator.js\";\nimport { ValueException } from \"../exceptions/index.js\";\n\nimport type { GeneratorFunction, Iteratee, TypeGuardPredicate, Reducer, IteratorLike } from \"./types.js\";\n\n/**\n * A wrapper class representing an enhanced and instantiable version\n * of the native {@link Iterable} & {@link Iterator} interfaces.\n *\n * It provides a set of utility methods to better manipulate and\n * transform iterators in a functional and highly performant way. \n * It takes inspiration from the native {@link Array} methods like\n * {@link Array.map}, {@link Array.filter}, {@link Array.reduce}, etc...\n *\n * The class is lazy, meaning that the transformations are applied\n * only when the resulting iterator is materialized, not before. \n * This allows to chain multiple transformations without\n * the need to iterate over the elements multiple times.\n *\n * ---\n *\n * @example\n * ```ts\n * const result = new SmartIterator<number>([\"-5\", \"-4\", \"-3\", \"-2\", \"-1\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\"])\n * .map(Number)\n * .map((value) => value + Math.ceil(Math.abs(value / 2)))\n * .filter((value) => value >= 0)\n * .map((value) => value + 1)\n * .reduce((acc, value) => acc + value);\n *\n * console.log(result); // 31\n * ```\n *\n * ---\n *\n * @template T The type of elements in the iterator.\n * @template R The type of the final result of the iterator. Default is `void`.\n * @template N The type of the argument required by the `next` method. Default is `undefined`.\n */\nexport default class SmartIterator<T, R = void, N = undefined> implements Iterator<T, R, N>\n{\n /**\n * The native {@link Iterator} object that is being wrapped by this instance.\n */\n protected readonly _iterator: Iterator<T, R, N>;\n\n /**\n * Initializes a new instance of the {@link SmartIterator} class.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<string>([\"A\", \"B\", \"C\"]);\n * ```\n *\n * ---\n *\n * @param iterable The iterable object to wrap.\n */\n public constructor(iterable: Iterable<T, R, N>);\n\n /**\n * Initializes a new instance of the {@link SmartIterator} class.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number, void, number>({\n * _sum: 0, _count: 0,\n *\n * next: function(value: number)\n * {\n * this._sum += value;\n * this._count += 1;\n *\n * return { done: false, value: this._sum / this._count };\n * }\n * })\n * ```\n *\n * ---\n *\n * @param iterator The iterator object to wrap.\n */\n public constructor(iterator: Iterator<T, R, N>);\n\n /**\n * Initializes a new instance of the {@link SmartIterator} class.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>(function* ()\n * {\n * for (let i = 2; i < 65_536; i *= 2) { yield (i - 1); }\n * });\n * ```\n *\n * ---\n *\n * @param generatorFn The generator function to wrap.\n */\n public constructor(generatorFn: GeneratorFunction<T, R, N>);\n\n /**\n * Initializes a new instance of the {@link SmartIterator} class.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator(values);\n * ```\n *\n * ---\n *\n * @param argument The iterable, iterator or generator function to wrap.\n */\n public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>);\n public constructor(argument: IteratorLike<T, R, N> | GeneratorFunction<T, R, N>)\n {\n if (argument instanceof Function)\n {\n this._iterator = argument();\n }\n else if (Symbol.iterator in argument)\n {\n this._iterator = argument[Symbol.iterator]() as Iterator<T, R, N>;\n }\n else\n {\n this._iterator = argument;\n }\n }\n\n /**\n * Determines whether all elements of the iterator satisfy a given condition.\n * See also {@link SmartIterator.some}.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * Once a single element doesn't satisfy the condition, the method will return `false` immediately.\n *\n * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. \n * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. \n * Consider using {@link SmartIterator.find} instead.\n *\n * If the iterator is infinite and every element satisfies the condition, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.every((value) => value < 0);\n *\n * console.log(result); // false\n * ```\n *\n * ---\n *\n * @param predicate The condition to check for each element of the iterator.\n *\n * @returns `true` if all elements satisfy the condition, `false` otherwise.\n */\n public every(predicate: Iteratee<T, boolean>): boolean\n {\n let index = 0;\n\n while (true)\n {\n const result = this._iterator.next();\n\n if (result.done) { return true; }\n if (!(predicate(result.value, index))) { return false; }\n\n index += 1;\n }\n }\n\n /**\n * Determines whether any element of the iterator satisfies a given condition.\n * See also {@link SmartIterator.every}.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * Once a single element satisfies the condition, the method will return `true` immediately.\n *\n * This may lead to an unknown final state of the iterator, which may be entirely or partially consumed. \n * For this reason, it's recommended to consider it as consumed in any case and to not use it anymore. \n * Consider using {@link SmartIterator.find} instead.\n *\n * If the iterator is infinite and no element satisfies the condition, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.some((value) => value < 0);\n *\n * console.log(result); // true\n * ```\n *\n * ---\n *\n * @param predicate The condition to check for each element of the iterator.\n *\n * @returns `true` if any element satisfies the condition, `false` otherwise.\n */\n public some(predicate: Iteratee<T, boolean>): boolean\n {\n let index = 0;\n\n while (true)\n {\n const result = this._iterator.next();\n\n if (result.done) { return false; }\n if (predicate(result.value, index)) { return true; }\n\n index += 1;\n }\n }\n\n /**\n * Filters the elements of the iterator using a given condition.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * If the condition is met, the element will be included in the new iterator.\n *\n * Since the iterator is lazy, the filtering process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.filter((value) => value < 0);\n *\n * console.log(result.toArray()); // [-2, -1]\n * ```\n *\n * ---\n *\n * @param predicate The condition to check for each element of the iterator.\n *\n * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition.\n */\n public filter(predicate: Iteratee<T, boolean>): SmartIterator<T, R>;\n\n /**\n * Filters the elements of the iterator using a given condition.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * If the condition is met, the element will be included in the new iterator.\n *\n * Since the iterator is lazy, the filtering process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number | string>([-2, \"-1\", \"0\", 1, \"2\"]);\n * const result = iterator.filter<number>((value) => typeof value === \"number\");\n *\n * console.log(result.toArray()); // [-2, 1]\n * ```\n *\n * ---\n *\n * @template S\n * The type of the elements that satisfy the condition. \n * This allows the type-system to infer the correct type of the new iterator.\n *\n * It must be a subtype of the original type of the elements.\n *\n * @param predicate The type guard condition to check for each element of the iterator.\n *\n * @returns A new {@link SmartIterator} containing only the elements that satisfy the condition.\n */\n public filter<S extends T>(predicate: TypeGuardPredicate<T, S>): SmartIterator<S, R>;\n public filter(predicate: Iteratee<T, boolean>): SmartIterator<T, R>\n {\n const iterator = this._iterator;\n\n return new SmartIterator<T, R>(function* ()\n {\n let index = 0;\n while (true)\n {\n const result = iterator.next();\n if (result.done) { return result.value; }\n if (predicate(result.value, index)) { yield result.value; }\n\n index += 1;\n }\n });\n }\n\n /**\n * Maps the elements of the iterator using a given transformation function.\n *\n * This method will iterate over all elements of the iterator applying the transformation function. \n * The result of each transformation will be included in the new iterator.\n *\n * Since the iterator is lazy, the mapping process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.map((value) => Math.abs(value));\n *\n * console.log(result.toArray()); // [2, 1, 0, 1, 2]\n * ```\n *\n * ---\n *\n * @template V The type of the elements after the transformation.\n *\n * @param iteratee The transformation function to apply to each element of the iterator.\n *\n * @returns A new {@link SmartIterator} containing the transformed elements.\n */\n public map<V>(iteratee: Iteratee<T, V>): SmartIterator<V, R>\n {\n const iterator = this._iterator;\n\n return new SmartIterator<V, R>(function* ()\n {\n let index = 0;\n while (true)\n {\n const result = iterator.next();\n if (result.done) { return result.value; }\n\n yield iteratee(result.value, index);\n\n index += 1;\n }\n });\n }\n\n /**\n * Reduces the elements of the iterator using a given reducer function. \n * This method will consume the entire iterator in the process.\n *\n * It will iterate over all elements of the iterator applying the reducer function. \n * The result of each iteration will be passed as the accumulator to the next one.\n *\n * The first accumulator value will be the first element of the iterator. \n * The last accumulator value will be the final result of the reduction.\n *\n * Also note that:\n * - If an empty iterator is provided, a {@link ValueException} will be thrown.\n * - If the iterator is infinite, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);\n * const result = iterator.reduce((acc, value) => acc + value);\n *\n * console.log(result); // 15\n * ```\n *\n * ---\n *\n * @param reducer The reducer function to apply to each element of the iterator.\n *\n * @returns The final result of the reduction.\n */\n public reduce(reducer: Reducer<T, T>): T;\n\n /**\n * Reduces the elements of the iterator using a given reducer function. \n * This method will consume the entire iterator in the process.\n *\n * It will iterate over all elements of the iterator applying the reducer function. \n * The result of each iteration will be passed as the accumulator to the next one.\n *\n * The first accumulator value will be the provided initial value. \n * The last accumulator value will be the final result of the reduction.\n *\n * If the iterator is infinite, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([1, 2, 3, 4, 5]);\n * const result = iterator.reduce((acc, value) => acc + value, 10);\n *\n * console.log(result); // 25\n * ```\n *\n * ---\n *\n * @template A The type of the accumulator value which will also be the type of the final result of the reduction.\n *\n * @param reducer The reducer function to apply to each element of the iterator.\n * @param initialValue The initial value of the accumulator.\n *\n * @returns The final result of the reduction.\n */\n public reduce<A>(reducer: Reducer<T, A>, initialValue: A): A;\n public reduce<A>(reducer: Reducer<T, A>, initialValue?: A): A\n {\n let index = 0;\n let accumulator = initialValue;\n if (accumulator === undefined)\n {\n const result = this._iterator.next();\n if (result.done) { throw new ValueException(\"Cannot reduce an empty iterator without an initial value.\"); }\n\n accumulator = (result.value as unknown) as A;\n index += 1;\n }\n\n while (true)\n {\n const result = this._iterator.next();\n if (result.done) { return accumulator; }\n\n accumulator = reducer(accumulator, result.value, index);\n\n index += 1;\n }\n }\n\n /**\n * Flattens the elements of the iterator using a given transformation function.\n *\n * This method will iterate over all elements of the iterator applying the transformation function. \n * The result of each transformation will be flattened into the new iterator.\n *\n * Since the iterator is lazy, the flattening process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number[]>([[-2, -1], 0, 1, 2, [3, 4, 5]]);\n * const result = iterator.flatMap((value) => value);\n *\n * console.log(result.toArray()); // [-2, -1, 0, 1, 2, 3, 4, 5]\n * ```\n *\n * ---\n *\n * @template V The type of the elements after the transformation.\n *\n * @param iteratee The transformation function to apply to each element of the iterator.\n *\n * @returns A new {@link SmartIterator} containing the flattened elements.\n */\n public flatMap<V>(iteratee: Iteratee<T, V | readonly V[]>): SmartIterator<V, R>\n {\n const iterator = this._iterator;\n\n return new SmartIterator<V, R>(function* ()\n {\n let index = 0;\n while (true)\n {\n const result = iterator.next();\n if (result.done) { return result.value; }\n\n const elements = iteratee(result.value, index);\n if (elements instanceof Array)\n {\n for (const value of elements) { yield value; }\n }\n else { yield elements; }\n\n index += 1;\n }\n });\n }\n\n /**\n * Drops a given number of elements at the beginning of the iterator. \n * The remaining elements will be included in a new iterator.\n * See also {@link SmartIterator.take}.\n *\n * Since the iterator is lazy, the dropping process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * Only the dropped elements will be consumed in the process. \n * The rest of the iterator will be consumed only once the new one is.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.drop(3);\n *\n * console.log(result.toArray()); // [1, 2]\n * ```\n *\n * ---\n *\n * @param count The number of elements to drop.\n *\n * @returns A new {@link SmartIterator} containing the remaining elements.\n */\n public drop(count: number): SmartIterator<T, R | undefined>\n {\n const iterator = this._iterator;\n\n return new SmartIterator<T, R | undefined>(function* ()\n {\n let index = 0;\n while (index < count)\n {\n const result = iterator.next();\n if (result.done) { return; }\n\n index += 1;\n }\n\n while (true)\n {\n const result = iterator.next();\n if (result.done) { return result.value; }\n\n yield result.value;\n }\n });\n }\n\n /**\n * Takes a given number of elements at the beginning of the iterator. \n * These elements will be included in a new iterator.\n * See also {@link SmartIterator.drop}.\n *\n * Since the iterator is lazy, the taking process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * Only the taken elements will be consumed from the original iterator. \n * The rest of the original iterator will be available for further consumption.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.take(3);\n *\n * console.log(result.toArray()); // [-2, -1, 0]\n * console.log(iterator.toArray()); // [1, 2]\n * ```\n *\n * ---\n *\n * @param limit The number of elements to take.\n *\n * @returns A new {@link SmartIterator} containing the taken elements.\n */\n public take(limit: number): SmartIterator<T, R | undefined>\n {\n const iterator = this._iterator;\n\n return new SmartIterator<T, R | undefined>(function* ()\n {\n let index = 0;\n while (index < limit)\n {\n const result = iterator.next();\n if (result.done) { return result.value; }\n\n yield result.value;\n\n index += 1;\n }\n\n return;\n });\n }\n\n /**\n * Finds the first element of the iterator that satisfies a given condition.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * The first element that satisfies the condition will be returned immediately.\n *\n * Only the elements that are necessary to find the first\n * satisfying one will be consumed from the original iterator. \n * The rest of the original iterator will be available for further consumption.\n *\n * Also note that:\n * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.\n * - If the iterator is infinite and no element satisfies the condition, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([-2, -1, 0, 1, 2]);\n * const result = iterator.find((value) => value > 0);\n *\n * console.log(result); // 1\n * ```\n *\n * ---\n *\n * @param predicate The condition to check for each element of the iterator.\n *\n * @returns The first element that satisfies the condition, `undefined` otherwise.\n */\n public find(predicate: Iteratee<T, boolean>): T | undefined;\n\n /**\n * Finds the first element of the iterator that satisfies a given condition.\n *\n * This method will iterate over all elements of the iterator checking if they satisfy the condition. \n * The first element that satisfies the condition will be returned immediately.\n *\n * Only the elements that are necessary to find the first\n * satisfying one will be consumed from the original iterator. \n * The rest of the original iterator will be available for further consumption.\n *\n * Also note that:\n * - If no element satisfies the condition, `undefined` will be returned once the entire iterator is consumed.\n * - If the iterator is infinite and no element satisfies the condition, the method will never return.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number | string>([-2, \"-1\", \"0\", 1, \"2\"]);\n * const result = iterator.find<number>((value) => typeof value === \"number\");\n *\n * console.log(result); // -2\n * ```\n *\n * ---\n *\n * @template S\n * The type of the element that satisfies the condition. \n * This allows the type-system to infer the correct type of the result.\n *\n * It must be a subtype of the original type of the elements.\n *\n * @param predicate The type guard condition to check for each element of the iterator.\n *\n * @returns The first element that satisfies the condition, `undefined` otherwise.\n */\n public find<S extends T>(predicate: TypeGuardPredicate<T, S>): S | undefined;\n public find(predicate: Iteratee<T, boolean>): T | undefined\n {\n let index = 0;\n\n while (true)\n {\n const result = this._iterator.next();\n\n if (result.done) { return; }\n if (predicate(result.value, index)) { return result.value; }\n\n index += 1;\n }\n }\n\n /**\n * Enumerates the elements of the iterator. \n * Each element is be paired with its index in a new iterator.\n *\n * Since the iterator is lazy, the enumeration process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<string>([\"A\", \"M\", \"N\", \"Z\"]);\n * const result = iterator.enumerate();\n *\n * console.log(result.toArray()); // [[0, \"A\"], [1, \"M\"], [2, \"N\"], [3, \"Z\"]]\n * ```\n *\n * ---\n *\n * @returns A new {@link SmartIterator} containing the enumerated elements.\n */\n public enumerate(): SmartIterator<[number, T], R>\n {\n return this.map((value, index) => [index, value]);\n }\n\n /**\n * Removes all duplicate elements from the iterator. \n * The first occurrence of each element will be kept.\n *\n * Since the iterator is lazy, the deduplication process will\n * be executed once the resulting iterator is materialized.\n *\n * A new iterator will be created, holding the reference to the original one. \n * This means that the original iterator won't be consumed until the\n * new one is and that consuming one of them will consume the other as well.\n *\n * ---\n *\n * @example\n * ```ts\n * const iterator = new SmartIterator<number>([1, 1, 2, 3, 2, 3, 4, 5, 5, 4]);\n * const result = iterator.unique();\n *\n * console.log(result.toArray()); // [1, 2, 3, 4, 5]\n * ```\n *\n * ---\n *\n