// @ts-check
///
///
///
///
///
///
"use strict";
/**
* @name Celestra
* @version 6.6.0 node
* @author Ferenc Czigler
* @see https://github.com/Serrin/Celestra/
* @license MIT https://opensource.org/licenses/MIT
*/
const VERSION = "Celestra v6.6.0 node";
/** TS types */
/**
* @description Map-like object with string or number or symbol keys.
*
* @private
* */
type MapLike = Record;
/**
* @description Number-like object.
*
* @private
* */
type NumberLike = number | bigint;
/**
* @description Any iterable or iterator.
*
* @private
*/
type IterableLike = Iterable | Iterator | IterableIterator;
/**
* @description Any iterable, iterator, or array-like structure.
*
* @private
*/
type IterableLikeAndArrayLike =
| Iterable
| Iterator
| IterableIterator
| ArrayLike;
/**
* @description Iterable and Iterator and Generator types.
*
* @private
*/
type IteratorReturn =
| Iterable
| Iterator
| Generator;
/**
* @description Type for undefined and null values.
*
* @private
*/
type Nullish = undefined | null;
/*
built-in type:
type NonNullable = number | boolean | string | symbol | object | Function;
*/
/**
* @description Not null or undefined or object or function.
*
* @private
*/
type NonNullablePrimitive = number | bigint | boolean | string | symbol;
/**
* @description Not object or function.
*
* @private
*/
type Primitive = null | undefined | number | bigint | boolean | string | symbol;
/**
* Generic comparable types.
*
* @private
*/
type Comparable = number | bigint | string | boolean | Date;
/**
* @description Object key type.
*
* @private
*/
type PropertyKey = string | symbol;
/**
* @description Type AsyncFunction.
*
* @private
*/
type AsyncFunction = (...args: ReadonlyArray) => Promise;
/**
* @description Type ArrowFunction.
*
* @private
*/
type ArrowFunction =
(this: void, ...args: Args) => R;
/**
* @description TypedArray types.
*
* @private
*/
type TypedArray = Exclude;
/** polyfills **/
/* globalThis; */
(function (global) {
if (!global.globalThis) {
if (Object.defineProperty) {
Object.defineProperty(global, "globalThis", {
configurable: true, enumerable: false, value: global, writable: true
});
} else {
global.globalThis = global;
}
}
})(typeof this === "object" ? this : Function("return this")());
/* Math.sumPrecise(); */
if (!("sumPrecise" in Math)) {
// @ts-ignore
Math.sumPrecise = function sumPrecise ([...array]): number {
/* empty iterator */
if (array.length === 0) { return -0; }
/* iterator with items */
if (array.every((value: unknown): boolean => typeof value === "number")) {
/* return NaN + Infinity + -Infinity */
let inf = array.indexOf(Infinity) >- 1;
let negInf = array.indexOf(-Infinity) > -1;
if (array.some((value: unknown): boolean => value !== value)
|| (inf && negInf)) { return NaN; }
if (inf) { return Infinity; }
if (negInf) { return -Infinity; }
/* sum hi */
let hi = array.filter((value: unknown): boolean =>
(value === 1e20 || value === -1e20))
.reduce((acc, value): number => acc + value, 0);
/* sum lo - Kahan sum */
let lo: number = 0.0;
let c: number = 0.0;
for (let item of array.filter((value: unknown): boolean =>
(value !== 1e20 && value !== -1e20))) {
let y = item - c; let t = lo + y; c = (t - lo) - y; lo = t;
}
/* return sum values */
/*
if (lo === 0 && hi !== 0) { return hi; }
if (lo > 0 && hi > 0) { return hi; }
if (lo < 0 && hi < 0) { return hi; }
if (lo > 0 && hi < 0) { return lo + hi; }
if (lo < 0 && hi > 0) { return lo + hi; }
if (lo === 0 && hi === 0) { return lo; }
if (lo !== 0 && hi === 0) { return lo; }
*/
if ((lo === 0 && hi !== 0) || (lo > 0 && hi > 0) || (lo < 0 && hi < 0)) {
return hi;
}
if ((lo > 0 && hi < 0) || (lo < 0 && hi > 0)) { return lo + hi; }
return lo;
}
/* not number items -> TypeError */
throw new TypeError("values passed to Math.sumPrecise must be numbers");
};
}
/* Error.isError(); */
if (!("isError" in Error)) {
// @ts-ignore
Error.isError = function isError (value: unknown) {
let className =
Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
return (className === "error" || className === "domexception");
};
}
/* crypto.randomUUID(); */
if (("crypto" in globalThis) && !("randomUUID" in globalThis.crypto)) {
// @ts-ignore
globalThis.crypto.randomUUID = function randomUUID () {
// @ts-ignore
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,
(c: any): any =>
(c^crypto.getRandomValues(new Uint8Array(1))[0]&15 >> c/4).toString(16)
);
};
}
/* globalThis.GeneratorFunction; */
// @ts-ignore
if (!globalThis.GeneratorFunction) {
// @ts-ignore
globalThis.GeneratorFunction =
Object.getPrototypeOf(function*(){}).constructor;
}
/* globalThis.AsyncFunction; */
// @ts-ignore
if (!globalThis.AsyncFunction) {
// @ts-ignore
globalThis.AsyncFunction =
Object.getPrototypeOf(async function(){}).constructor;
}
/* globalThis.AsyncGeneratorFunction; */
// @ts-ignore
if (!globalThis.AsyncGeneratorFunction) {
// @ts-ignore
globalThis.AsyncGeneratorFunction =
Object.getPrototypeOf(async function* () {}).constructor;
}
/** Core API **/
/* Alphabet constans */
const BASE16 = "0123456789ABCDEF";
const BASE32 = "234567ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE36 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const WORDSAFEALPHABET = "23456789CFGHJMPQRVWXcfghjmpqvwx"; /* 31 characters */
/**
* @description Ensures that `condition` is truthy. Throws an `Error` if falsy.
*
* @param {unknown} condition The value to check.
* @param {unknown} [message] - Optional message or Error to throw.
* @throws {Error} If assertion is failed.
*/
function assert (condition: unknown, message?: unknown): asserts condition {
if (!condition) {
// @ts-ignore
if (Error.isError(message)) { throw message; }
let errorMessage =
`[assert] Assertion failed: ${condition} should be truly${message ? ` - ${message}` : ""}`;
throw new Error(errorMessage, {cause: errorMessage});
}
}
/**
* @description SameValueZero equality (like `Object.is`, but +0 === -0).
*
* @param {unknown} value1
* @param {unknown} value2
* @returns {boolean}
*/
const eq = (value1: unknown, value2: unknown): boolean =>
value1 === value2 || (value1 !== value1 && value2 !== value2);
/**
* @description Greater than.
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function gt (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2) && value1 > value2;
}
/**
* @description Greater than or equal (SameValueZero).
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function gte (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2)
&& (value1 > value2
|| value1 === value2
|| (value1 !== value1 && value2 !== value2));
}
/**
* @description Less than.
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function lt (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2) && value1 < value2;
}
/**
* @description Less than or equal (SameValueZero).
*
* @param {any} value1
* @param {any} value2
* @returns {boolean}
*/
function lte (value1: Comparable, value2: Comparable): boolean {
const _typeOf = (value: unknown): string =>
value === null ? "null" : typeof value;
return _typeOf(value1) === _typeOf(value2)
&& (value1 < value2
|| value1 === value2
|| (value1 !== value1 && value2 !== value2));
}
/**
* @description Calls `callback` with the given `value` and then returns `value`.
*
* @param {Function} callback
* @returns {Function}
*/
function tap (callback: Function): any {
return function (value: unknown): any { callback(value); return value; };
}
/**
* @description Creates a function that is restricted to invoking `callback` once.
*
* @param {Function} callback
* @returns {Function}
*/
function once (callback: Function): Function {
let called: boolean = false;
let result: any;
return function (...args: any[]): any {
if (!called) {
called = true;
result = callback(...args);
}
return result;
};
}
/**
* @description Transforms a function of N arguments into N functions of one argument.
*
* @param {Function} callback
* @returns {Function}
*/
function curry (callback: Function): Function {
const curried = (...args: any[]): any =>
args.length >= callback.length
? callback(...args)
: (...rest: any[]): any => curried(...args, ...rest);
return curried;
}
/**
* @description Creates a function that is the composition of the provided functions.
*
* @param {Function} functions
* @returns {Function}
*/
const pipe = (...functions: Function[]): Function =>
(first: any): any =>
functions.reduce((value: unknown, callback: Function): any =>
callback(value), first);
/**
* @description Creates a function that is the composition of the provided functions.
*
* @param {Function} functions
* @returns {Function}
*/
const compose = (...functions: Function[]): Function =>
(first: any): any => functions.reduceRight((value, callback): any =>
callback(value), first);
/**
* @description Creates a new object composed of the picked `object` properties.
*
* @param {object} obj
* @param {string[]} keys
*/
const pick = (obj: MapLike, keys: string[]): MapLike =>
keys.reduce(function (acc: MapLike, key: string) {
if (key in obj) { acc[key] = obj[key]; }
return acc;
}, {});
/**
* @description Creates a new object composed of the `object` properties except for those omitted.
*
* @param {object} obj
* @param {string[]} keys
* @returns {object}
*/
const omit = (obj: MapLike, keys: string[]): MapLike =>
Object.keys(obj).reduce(function (acc: MapLike, key: string) {
// @ts-ignore
if (!keys.includes(key)) { acc[key] = obj[key]; }
return acc;
}, {});
/**
* @description Returns a new object with the specified key-value pair added or updated.
*
* @param {object} obj
* @param {string} key
* @param {object} value
*/
const assoc = (obj: MapLike, key: string, value: unknown): MapLike =>
({...obj, [key]: value});
/**
* @description An asynchronous no-operation function that returns a resolved Promise.
*
* @returns {Promise}
*/
// @ts-ignore
function asyncNoop (): Promise {
return new Promise(function (resolve: Function) { resolve(); });
}
/**
* @description Asynchronous function that returns a resolved Promise with `true`.
*
* @returns {Promise}
*/
async function asyncT (): Promise { return true; }
/**
* @description Asynchronous function that returns a resolved Promise with `false`.
*
* @returns {Promise}
*/
async function asyncF (): Promise { return false; }
/**
* @description Creates an asynchronous function that returns a resolved Promise with the specified value.
*
* @param {unknown} value
* @returns {Function}
*/
function asyncConstant (value: unknown): Function {
return async function() { return value; };
}
/**
* @description Asynchronous identity function that returns a resolved Promise with the given value.
*
* @param {unknown} value
* @returns {Promise}
*/
async function asyncIdentity (value: unknown): Promise { return value; }
/**
* @description Creates a polyfill method on an object if it does not already exist.
*
* @param {Object} obj - The object on which to create the method.
* @param {string} property - The name of the method to create.
* @param {Function} value - The function to assign as the method.
* @returns {boolean} - Returns true if the method was created or already exists with the same value.
*/
function createPolyfillMethod (
obj: Object,
property: string,
value: Function): boolean {
if (!(Object.hasOwn(obj, property))) {
Object.defineProperty(obj, property, {
writable: true, enumerable: false, configurable: true, value: value
});
}
// @ts-ignore
return (obj[property] === value);
}
/**
* @description Creates a polyfill property on an object if it does not already exist.
*
* @param {Object} obj - The object on which to create the property.
* @param {string} property - The name of the property to create.
* @param {unknown} value - The value to assign to the property.
* @returns {boolean} - Returns true if the property was created or already exists with the same value.
*/
function createPolyfillProperty (
obj: object,
property: string,
value: unknown): boolean {
if (!(Object.hasOwn(obj, property))) {
Object.defineProperty(obj, property, {
writable: true, enumerable: true, configurable: true, value: value
});
}
// @ts-ignore
return (obj[property] === value);
}
/**
* @description Generates a random UUID version 7 or UUID version 7 with version 4 ID.
*
* @param {boolean} [v4=false] - If true, generates a UUID version 4; otherwise, generates version 7.
* @returns {string} A randomly generated UUID string.
*/
function randomUUIDv7 (v4: boolean = false): string {
let ts = Date.now().toString(16).padStart(12,"0") + (v4 ? "4" : "7");
// @ts-ignore
let uuid = Array.from(([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,
(c: number): any =>
(c^crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
));
let index: number = 0;
let pos: number = 0;
while (index < 13) {
if (pos === 8 || pos === 13) { pos++; }
uuid[pos] = ts[index];
pos++;
index++;
}
return uuid.join("");
}
/**
* @description Returns a Promise that resolves after a specified delay in milliseconds.
*
* @param {number} milisec - The delay duration in milliseconds.
* @returns {Promise} A Promise that resolves after the specified delay.
*/
const delay = (milisec: number): Promise =>
new Promise(resolve => setTimeout(resolve, milisec));
/**
* @description Generates a random boolean value.
*
* @returns {boolean} A randomly generated boolean value.
*/
const randomBoolean = (): boolean => !Math.round(Math.random());
/**
* @description Parses URL query parameters into an object.
*
* @param {string} [str=location.search] - The URL query string to parse. Defaults to the current location's search string.
* @returns {Object} An object containing the parsed query parameters as key-value pairs.
*/
const getUrlVars = (str: string = location.search): Object =>
[...new URLSearchParams(str).entries()]
// @ts-ignore
.reduce(function (obj, item) { obj[item[0]] = item[1]; return obj; }, {});
/**
* @description Create a parsable string from an object.
*
* @param {object} obj
* @returns {string}
*/
const obj2string = (obj: any): string => Object.keys(obj).reduce(
(str, key: string): string =>
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]) + "&",
""
).slice(0, -1);
/**
* @description Deep assign of an object (Object, Array, etc.)
*
* @returns any
*/
function extend (target: T, source: U): T & U;
function extend (target: T, s1: U, s2: V): T & U & V;
function extend (deep: true, target: T, ...sources: any[]): T;
function extend (deep: false, target: T, ...sources: any[]): T;
function extend (target: object, ...sources: any[]): object;
function extend (...args: any[]): any {
/* Arguments checking */
let deep: boolean = false;
let target: any;
let i = 0;
if (args[0] === true) {
deep = true;
target = args[1] || {};
i = 2;
} else {
target = args[0] || {};
i = 1;
}
/* Helper functions */
const _isPlainObject = (obj: any): obj is Record =>
obj != null
&& typeof obj === "object"
&& (obj.constructor === Object || obj.constructor == null);
function merge(target: any, source: any): any {
/* Identical or non-object -> direct assign */
if (Object.is(source, target) || source == null || typeof source !== "object") {
return source;
}
/* Date -> clone */
if (source instanceof Date) { return new Date(source.getTime()); }
/* RegExp -> clone */
if (source instanceof RegExp) { return new RegExp(source); }
/* Map -> deep merge entries */
if (source instanceof Map) {
if (target instanceof Map) { target = new Map(); }
for (let [key, value] of source) {
const tv = target.get(key);
target.set(key, deep ? merge(tv, value) : value);
}
return target;
}
/* Set -> deep union */
if (source instanceof Set) {
if (!(target instanceof Set)) { target = new Set(); }
for (let item of source) {
if (deep) {
if (target.has(item)) { continue; }
}
target.add(item);
}
return target;
}
/* Array -> deep merge by index */
if (Array.isArray(source)) {
if (!Array.isArray(target)) { target = []; }
const srcLength = source.length;
for (let i = 0; i < srcLength; i++) {
let sv = source[i];
let tv = target[i];
target[i] = deep ? merge(tv, sv) : sv;
}
return target;
}
/* Plain object -> deep merge keys */
if (_isPlainObject(source)) {
if (!_isPlainObject(target)) { target = {}; }
for (let key in source) {
let sv = source[key];
let tv = target[key];
target[key] = deep ? merge(tv, sv) : sv;
}
return target;
}
/* Fallback: copy by reference */
return source;
}
/* Clone all sources */
const length = args.length;
for (; i < length; i++) { merge(target, args[i]); }
return target;
}
/**
* @description Returns the number of own properties (including symbols) of an object.
*
* @param {object} obj
* @returns {number}
*/
const sizeIn = (obj: object): number =>
Object.getOwnPropertyNames(obj).length
+ Object.getOwnPropertySymbols(obj).length;
/**
* @description Creates a function that invokes `callback` with its `this` binding removed.
*
* @param {Function} callback
* @returns {Function}
*/
const unBind = (callback: Function): Function =>
Function.prototype.call.bind(callback);
/**
* @description Creates a function that invokes `callback` with its `this` binding set to the provided context.
*
* @param {Function} callback
* @param {Function} context
*/
const bind = Function.prototype.call.bind(Function.prototype.bind);
/**
* @description Returns a function that always returns the same value.
*
* @param {unknown} value
* @returns {unknown}
*/
const constant = (value: T): (() => T) => () => value;
/**
* @description Returns value unchanged.
*
* @param {unknown} value
* @returns {unknown}
*/
const identity = (value: T): T => value;
/**
* @description A function that does nothing.
*
* @returns {void}
*/
function noop (): void {}
/**
* @description Always returns true.
*
* @returns {true}
*/
const T = (): boolean => true;
/**
* @description Always returns false.
*
* @returns {false}
*/
const F = (): boolean => false;
/**
* @description Generates a random string ID of specified size using the provided alphabet.
*
* @param {number} [size=21] - The length of the generated ID.
* @param {string} [alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"] - The set of characters to use for generating the ID.
*/
function nanoid (
size: number = 21,
alphabet: string =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
): string {
let result: string = "";
let dl: number = alphabet.length;
let pos: number;
let index: number = size;
while (index--) {
do { pos = crypto.getRandomValues(new Uint8Array(1))[0]; } while
(pos >= dl);
result += alphabet[pos];
}
return result;
}
/**
* @description Generates a timestamp-based string ID of specified size using the provided alphabet.
*
* @param {number} [size=21] - The total length of the generated ID, including the timestamp.
* @param {string} [alphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"] - The set of characters to use for generating the ID.
* @returns {string} The generated timestamp-based ID.
*/
function timestampID (
size: number = 21,
alphabet: string =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
): string {
let result: string = Date.now().toString(36).padStart(10, "0") + "-";
let dl: number = alphabet.length;
let pos: number;
let index: number = ((size > 11) ? size : 12) - 11;
while (index--) {
do { pos = crypto.getRandomValues(new Uint8Array(1))[0]; } while
(pos >= dl);
result += alphabet[pos];
}
return result;
}
/** String API **/
/**
* @description Encodes a string to Base64 format.
*
* @param {any} str - The string to encode.
* @returns {string} The Base64 encoded string.
*/
function b64Encode (str: any): string {
return btoa(encodeURIComponent(String(str)).replace(/%([0-9A-F]{2})/g,
function toSolidBytes (_match, p1): string {
// @ts-ignore
return String.fromCharCode("0x" + p1);
}
));
}
/**
* @description Decodes a Base64 encoded string.
*
* @param {string} str - The Base64 encoded string to decode.
* @returns {string} The decoded string.
*/
function b64Decode (str: any): string {
return decodeURIComponent(atob(String(str)).split("").map(function (c) {
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
}).join(""));
}
/**
* @description Counts the occurrences of a substring in a string.
*
* @param {unknown} str - The string to search in.
* @param {unknown} substr - The substring to search for.
* @returns {number} The number of occurrences of the substring.
*/
function strCount (str: unknown, substr: unknown): number {
let count = (String(str)?.split(String(substr)) ?? []).length - 1;
return count < 0 ? 0 : count;
}
/**
* @description Truncates a string to a specified length, optionally adding an omission string.
*
* @param {string} str - The string to truncate.
* @param {number} newLength - The maximum length of the truncated string.
* @param {string} [omission=""] - The string to append to the truncated string.
* @returns {string} The truncated string.
*/
function strTruncate (
str: any,
newLength: number,
omission: string = ""): string {
str = String(str);
omission = String(omission);
let strUC = Array.from(str);
if (newLength >= strUC.length) { return str; }
return strUC.slice(0, newLength-Array.from(omission).length).join("")
+ omission;
}
/**
* @description Converts the first character of each word in a string to uppercase and the rest to lowercase.
*
* @param {any} str - The string to convert.
* @returns {string} The converted string.
*/
const strPropercase = (str: any): string =>
String(str).trim().split(" ").map(function (value: string) {
let chars = Array.from(value).map( (c: string): string => c.toLowerCase() );
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}).join(" ");
/**
* @description Converts the first character of each word in a string to uppercase and the rest to lowercase.
*
* @param {any} str - The string to convert.
* @returns {string} The converted string.
*/
const strTitlecase = (str: any): string =>
String(str).trim().split(" ").map(function (value: string) {
let chars = Array.from(value).map( (c: string): string => c.toLowerCase() );
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}).join(" ");
/**
* @description Capitalizes the first character of a string and converts the rest to lowercase.
*
* @param {any} str - The string to capitalize.
* @returns {string} The capitalized string.
*/
function strCapitalize (str: any): string {
let chars = [...String(str).trim().toLowerCase()];
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}
/**
* @description Converts the first character of a string to uppercase.
*
* @param {any} str - The string to modify.
* @returns {string} The modified string.
*/
function strUpFirst (str: any): string {
let chars = [...String(str).trim()];
if (chars.length) { chars[0] = chars[0].toUpperCase(); }
return chars.join("");
}
/**
* @description Converts the first character of a string to lowercase.
*
* @param {any} str - The string to modify.
* @returns {string} The modified string.
*/
function strDownFirst (str: any): string {
let chars = [...String(str).trim()];
if (chars.length) { chars[0] = chars[0].toLowerCase(); }
return chars.join("");
}
/**
* @description Reverses the characters in a string.
*
* @param {any} str - The string to reverse.
* @returns {string} The reversed string.
*/
const strReverse = (str: any): string =>
Array.from(String(str)).reverse().join("");
/**
* @description Returns an array of Unicode code points for each character in a string.
*
* @param {any} str - The string to process.
* @returns {number[]} An array of Unicode code points.
*/
const strCodePoints = (str: any): any[] =>
Array.from(String(str), (value: string): number | undefined =>
value.codePointAt(0));
/**
* @description Creates a string from an array or iterable of Unicode code points.
*
* @param {Iterable} iterator - An array or iterable of Unicode code points.
* @returns {string} The constructed string.
*/
const strFromCodePoints = ([...array]): string =>
String.fromCodePoint(...array);
/**
* @description Gets or sets a unicode character at a specified index in a string.
*
* @param {string} str - The string to modify.
* @param {number} index - The index of the character to get or set.
*/
function strAt (str: string, index: number, newChar?: string): string {
let chars: string[] = Array.from(String(str));
if (newChar == null) { return chars.at(index) || ""; }
index = index < 0 ? chars.length + index : index;
if (index > chars.length) { return chars.join(""); }
chars[index] = newChar;
return chars.join("");
}
/**
* @description Splices a string by removing a specified number of characters at a given index and optionally adding new characters.
*
* @param {string} str - The string to splice.
* @param {number} index - The index at which to start splicing.
* @param {number} count - The number of characters to remove.
* @param {string} [add] - The string to add at the splice index.
* @returns {string} The spliced string.
*/
const strSplice = (str: string, index: number,count: number, ...add: any[]): string =>
Array.from(str).toSpliced(index, count, add.join("")).join("");
/**
* @description Removes HTML tags from a string.
*
* @param {any} str - The string from which to remove HTML tags.
* @returns {string} The string without HTML tags.
*/
const strHTMLRemoveTags = (str: any): string =>
String(str).trim().replace(/<[^>]*>/g, " ").replace(/\s{2,}/g, " ").trim();
/**
* @description Escapes special HTML characters in a string.
*
* @param {any} str - The string to escape.
* @returns {string} The escaped string.
*/
const strHTMLEscape = (str: any): string =>
String(str).trim()
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
/**
* @description Unescapes special HTML characters in a string.
*
* @param {any} str - The string to unescape.
* @returns {string} The unescaped string.
*/
const strHTMLUnEscape = (str: string): string =>
String(str).trim()
.replace(/&/g, "&").replace(/&/g, "&")
.replace(/</g, "<").replace(/</g, "<")
.replace(/>/g, ">").replace(/>/g, ">")
.replace(/"/g, '"').replace(/"/g, '"')
.replace(/'/g, "'").replace(/'/g, "'");
/** Type API **/
/**
* @description Checks if the given value is NonNullable (not null or undefined).
*
* @param {unknown} value - The value to check.
* @returns True if the value is a NonNullable, false otherwise.
*/
const isNonNullable = (value: unknown): value is NonNullable =>
value != null;
/**
* @description Checks if the given value is NonNullablePrimitive.
*
* @param {unknown} value - The value to check.
* @returns True if the value is a NonNullable, false otherwise.
*/
const isNonNullablePrimitive =
(value: unknown): value is NonNullablePrimitive =>
value != null && typeof value !== "object" && typeof value !== "function";
/**
* @description Checks if a value is an arrow function.
* @note There is no built-in type for ArrowFunction.
*
* @param {unknown} value
* @returns {boolean} true if the value is an arrow function, false otherwise.
*/
function isArrowFunction (value: unknown): value is ArrowFunction {
if (typeof value !== "function"
|| ("prototype" in value && value.prototype !== undefined)
|| !(value.toString().includes("=>"))
) { return false; }
/* Arrow functions cannot be used as constructors, so this will throw an error if it's an arrow function */
try {
// @ts-expect-error
new value();
return false;
} catch (error) {
return true;
}
}
/**
* @description Checks if a value is an async iterator.
*
* @param {unknown} value
* @returns {boolean} true if the value is an async iterator, false otherwise.
*/
const isAsyncIterator = (value: unknown): value is AsyncIterator =>
value != null
&& typeof (value as any).next === "function"
&& typeof (value as any)[Symbol.asyncIterator] === "function";
/**
* @description Checks if all items in an iterable or iterator match the expected type(s) or constructor(s).
*
* @param {IterableLike} iter - The iterable or iterator to check.
* @param {string | Function | Array} expectedType - The expected type(s) or constructor(s) for the items.
* @param {boolean} [Throw=false] - If true, throws a TypeError on mismatch; otherwise returns false.
* @returns {boolean} True if all items match the expected type(s) or constructor(s), false otherwise.
* @throws {TypeError} If `iter` is not iterable or iterator, or if `expectedType` is invalid, or if a mismatch occurs and `Throw` is true.
*/
function isTypedCollection (
iter: IterableLike,
expectedType: string | Function | Array,
Throw: boolean = false): boolean {
/* helper functions */
const _typeOf = (value: any): string =>
value === null ? "null" : typeof value;
const _isIterator = (value: any): boolean =>
value != null && typeof value === "object"
&& typeof value.next === "function";
const _isIterable = (value: any): boolean =>
value != null && typeof value[Symbol.iterator] === "function";
/* Validate `iter` */
if (!_isIterator(iter) && !_isIterable(iter)) {
throw new TypeError(
`[isTypedCollection] TypeError: iter must be iterable or iterator. Got ${_typeOf(iter)}`
);
}
/* Validate `expected` */
if (!(["string", "function"].includes(typeof expectedType))
&& !Array.isArray(expectedType)) {
throw new TypeError(
`[isTypedCollection] TypeError: expectedType must be string, function, array. Got ${_typeOf(expectedType)}`
);
}
/* Validate `Throw` */
if (typeof Throw !== "boolean") {
throw new TypeError(
`[isTypedCollection] TypeError: Throw has to be a boolean. Got ${typeof Throw}`
);
}
/* Normalize expected to an array */
let expectedArray: any[] =
Array.isArray(expectedType) ? expectedType : [expectedType];
/* Check values of iter against expected types or constructors */
let matched: boolean = true;
for (let value of iter as Iterable) {
const valueType: string = _typeOf(value);
matched = expectedArray.some(
function (item: string | Function): boolean {
if (typeof item === "string") { return valueType === item; }
if (typeof item === "function") {
return value != null && value instanceof item;
}
/* validate expected array elements */
throw new TypeError(
`[isTypedCollection] TypeError: expectedType array elements have to be a string or function. Got ${typeof item}`
);
}
);
if (!matched) { break; }
}
/* Throw error if mismatch and `Throw` is true */
if (Throw && !matched) {
let eNames: string = expectedArray.map((item: any): string =>
(typeof item === "string" ? item.toString() : item.name ?? "anonymous")
).join(", ");
throw new TypeError(
`[isTypedCollection] TypeError: one or more items are not ${eNames}`
);
}
return matched;
}
/**
* @description Checks if a value matches the expected type(s) or constructor(s).
*
* @param {any} value - The value to check.
* @param {string | Function | Array | undefined} expectedType - The expected type(s) or constructor(s).
* @param {boolan} Throw
* @returns {string | Function | boolean}
*/
function is (
value: any,
expectedType?: string | Function | Array | undefined,
Throw: boolean = false): string | Function | boolean {
/* Validate `expected` */
if (!(["string", "function", "undefined"].includes(typeof expectedType))
&& !Array.isArray(expectedType)) {
throw new TypeError(
`[is] TypeError: expectedType must be string, function, array or undefined. Got ${typeof expectedType}`
);
}
/* Validate `Throw` */
if (typeof Throw !== "boolean") {
throw new TypeError(
`[is] TypeError: Throw has to be a boolean. Got ${typeof Throw}`
);
}
/* Determine the type of `value` */
const vType: string = (value === null ? "null" : typeof value);
/* If no expected type provided, return type or constructor */
if (expectedType == null) {
return vType === "object"
? Object.getPrototypeOf(value)?.constructor ?? "object"
: vType;
}
/* Normalize expected to an array */
let expectedArray: Array =
Array.isArray(expectedType) ? expectedType : [expectedType];
/* Check against expected types or constructors */
let matched: boolean = expectedArray.some(
function (item: string | Function) {
if (typeof item === "string") { return vType === item; }
if (typeof item === "function") {
return value != null && value instanceof item;
}
/* validate expected array elements */
throw new TypeError(
`[is] TypeError: expectedType array elements have to be a string or function. Got ${typeof item}`
);
}
);
/* Throw error if mismatch and `Throw` is true */
if (Throw && !matched) {
let vName: string =
value.toString ? value.toString() : Object.prototype.toString.call(value);
let eNames: string = expectedArray.map((item: any): string =>
(typeof item === "string" ? item.toString() : item.name ?? "anonymous")
).join(", ");
throw new TypeError(`[is] TypeError: ${vName} is not a ${eNames}`);
}
return matched;
}
/**
* @description Converts a given value to an object, symbol, or function.
*
* @param {unknown} value - The value to convert.
* @returns {Object | symbol | Function} The converted object, symbol, or function.
* @throws {TypeError} If the value is null or undefined.
*/
function toObject (value: unknown): Object | symbol | Function {
if (value == null) {
throw new TypeError(`[toObject] error: ${value}`);
}
return (["object", "function"].includes(typeof value))
? value
: Object(value);
}
/* toPrimitive(value: unknown): primitive | object | symbol | Function */
/**
* @description Converts wrapper objects to their corresponding primitive values.
*
* @param {unknown} value - The value to convert.
* @returns {any} The primitive value or the original object if not a wrapper.
*/
function toPrimitive (value: unknown): any {
if (value == null || typeof value !== "object") { return value; }
const vType = Object.prototype.toString.call(value).slice(8, -1);
if (["Boolean", "BigInt", "Number", "String", "Symbol"].includes(vType)) {
return value.valueOf();
}
return value;
}
/**
* @description This function is a general purpose, type safe, predictable stringifier. Converts a value into a human-readable string for error messages Handles symbols, functions, nullish, circular references, etc.
*
* @param {unknown} value The value to inspect.
* @returns {string}
*/
function toSafeString (value: unknown): string {
const seen = new WeakSet