UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

373 lines (342 loc) 11 kB
'use strict'; var buffer = require('buffer'); const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; /** * An object containing type validation functions and their evaluation order. * * Each item in `typeValidator.items` is a function that receives any value * and returns a boolean indicating whether the value matches the corresponding type. * * The `order` array defines the priority in which types should be checked, * which can be useful for functions that infer types in a consistent manner. * */ const typeValidator = { items: {}, /** * Evaluation order of the type checkers. * @type {string[]} * */ order: [], }; /** @typedef {Object.<string, (val: any) => *>} ExtendObjType */ /** @typedef {Array<[string, (val: any) => *]>} ExtendObjTypeArray */ /** * Adds new type checkers to the typeValidator without overwriting existing ones. * * Accepts either an object with named functions or an array of [key, fn] arrays. * If no index is provided, the type is inserted just before 'object' (if it exists), or at the end. * * @param {ExtendObjType|ExtendObjTypeArray} newItems * - New type validators to be added. * @param {number} [index] - Optional. Position at which to insert each new type. Ignored if the type already exists. * @returns {string[]} - A list of successfully added type names. * * @example * extendObjType({ * htmlElement2: val => typeof HTMLElement !== 'undefined' && val instanceof HTMLElement * }); * * @example * extendObjType([ * ['alpha', val => typeof val === 'string'], * ['beta', val => Array.isArray(val)] * ]); */ function extendObjType(newItems, index) { const added = []; const entries = Array.isArray(newItems) ? newItems : Object.entries(newItems); for (const [key, fn] of entries) { if (!typeValidator.items.hasOwnProperty(key)) { // @ts-ignore typeValidator.items[key] = fn; let insertAt = typeof index === 'number' ? index : -1; // Default to -1 if index isn't provided // Default to before 'object', or to the end if (insertAt === -1) { const objectIndex = typeValidator.order.indexOf('object'); insertAt = objectIndex > -1 ? objectIndex : typeValidator.order.length; } // Ensure insertAt is a valid number and not out of bounds insertAt = Math.min(Math.max(0, insertAt), typeValidator.order.length); typeValidator.order.splice(insertAt, 0, key); added.push(key); } } return added; } /** * Reorders the typeValidator.order array according to a custom new order. * All values in the new order must already exist in the current order. * The function does not mutate the original array structure directly. * * @param {string[]} newOrder - The new order of type names. * @returns {boolean} - Returns true if the reorder was successful, false if invalid keys were found. * * @example * reorderObjTypeOrder([ * 'string', 'number', 'array', 'object' * ]); */ function reorderObjTypeOrder(newOrder) { const currentOrder = [...typeValidator.order]; // shallow clone // All keys in newOrder must exist in currentOrder const isValid = newOrder.every((type) => currentOrder.includes(type)); if (!isValid) return false; // Reassign only if valid typeValidator.order = newOrder.slice(); // assign shallow copy return true; } /** * Returns a cloned version of the `typeValidator.order` array. * The cloned array will not be affected by future changes to the original `order`. * * @returns {string[]} - A new array with the same values as `typeValidator.order`. */ function cloneObjTypeOrder() { return [...typeValidator.order]; // Creates a shallow copy of the array } /** * Returns the detected type name of a given value based on predefined type validators. * * This function uses `getType` with a predefined `typeValidator` to determine or compare types safely. * in the specified `typeValidator.order`. The first matching type is returned. * * If `val` is `null`, it immediately returns `'null'`. * If no match is found, it returns `'unknown'`. * * @param {any} val - The value whose type should be determined. * @returns {string} - The type name of the value (e.g., "array", "date", "map"), or "unknown" if no match is found. * * @example * getType([]); // "array" * getType(null); // "null" * getType(new Set()); // "set" * getType(() => {}); // "unknown" */ const getType = (val) => { if (val === null) return 'null'; // @ts-ignore for (const name of typeValidator.order) { // @ts-ignore if (typeof typeValidator.items[name] !== 'function' || typeValidator.items[name](val)) return name; } return 'unknown'; }; /** * Checks the type of a given object or returns its type as a string. * * @param {*} obj - The object to check or identify. * @param {string} [type] - Optional. If provided, checks whether the object matches this type (e.g., "object", "array", "string"). * @returns {boolean|string|null} - Returns `true` if the type matches, `false` if not, * the type string if no type is provided, or `null` if the object is `undefined`. * * @example * objType([], 'array'); // true * objType({}, 'object'); // true * objType('hello'); // "string" * objType(undefined); // null */ function objType(obj, type) { if (typeof obj === 'undefined') return null; const result = getType(obj); if (typeof type === 'string') return result === type.toLowerCase(); return result; } /** * Checks the type of a given object and returns the validation value if a known type is detected. * * @param {*} obj - The object to check or identify. * @returns {{ valid:*; type: string | null }} - Returns the type result. */ function checkObj(obj) { /** @type {{ valid:*; type: string | null }} */ const data = { valid: null, type: null }; for (const name of typeValidator.order) { // @ts-ignore if (typeof typeValidator.items[name] === 'function') { // @ts-ignore const result = typeValidator.items[name](obj); if (result) { data.valid = result; data.type = name; break; } } } return data; } /** * Creates a clone of the functions from the `typeValidator` object. * It returns a new object where the keys are the same and the values are the cloned functions. */ function getCheckObj() { return Object.fromEntries(Object.entries(typeValidator.items).map(([key, fn]) => [key, fn])); } /** * Counts the number of elements in an array or the number of properties in an object. * * @param {Array<*>|Record<string|number, any>} obj - The array or object to count. * @returns {number} - The count of items (array elements or object keys), or `0` if the input is neither an array nor an object. * * @example * countObj([1, 2, 3]); // 3 * countObj({ a: 1, b: 2 }); // 2 * countObj('not an object'); // 0 */ function countObj(obj) { // Is Array if (Array.isArray(obj)) return obj.length; // Object if (objType(obj, 'object')) return Object.keys(obj).length; // Nothing return 0; } /** * Determines whether a given value is a pure JSON object (plain object). * * A pure object satisfies the following: * - It is not null. * - Its type is "object". * - Its internal [[Class]] is "[object Object]". * - It is not an instance of built-in types like Array, Date, Map, Set, etc. * * This function is useful for strict data validation when you want to ensure * a value is a clean JSON-compatible object, free of class instances or special types. * * @param {unknown} value - The value to test. * @returns {value is Record<string | number | symbol, unknown>} Returns true if the value is a pure object. */ function isJsonObject(value) { if (value === null || typeof value !== 'object') return false; if (Array.isArray(value)) return false; if (Object.prototype.toString.call(value) !== '[object Object]') return false; return true; } // Insert obj types extendObjType([ [ 'undefined', /** @param {*} val @returns {val is undefined} */ (val) => typeof val === 'undefined', ], [ 'null', /** @param {*} val @returns {val is null} */ (val) => val === null, ], [ 'boolean', /** @param {*} val @returns {val is boolean} */ (val) => typeof val === 'boolean', ], [ 'number', /** @param {*} val @returns {val is number} */ (val) => typeof val === 'number' && !Number.isNaN(val), ], [ 'bigint', /** @param {*} val @returns {val is bigint} */ (val) => typeof val === 'bigint', ], [ 'string', /** @param {*} val @returns {val is string} */ (val) => typeof val === 'string', ], [ 'symbol', /** @param {*} val @returns {val is symbol} */ (val) => typeof val === 'symbol', ], [ 'function', /** @param {*} val @returns {val is Function} */ (val) => typeof val === 'function', ], [ 'array', /** @param {*} val @returns {val is any[]} */ (val) => Array.isArray(val), ], ]); if (!isBrowser) { extendObjType([ [ 'buffer', /** @param {*} val @returns {val is Buffer} */ (val) => typeof buffer.Buffer !== 'undefined' && buffer.Buffer.isBuffer(val), ], ]); } if (isBrowser) { extendObjType([ [ 'file', /** @param {*} val @returns {val is File} */ (val) => typeof File !== 'undefined' && val instanceof File, ], ]); } extendObjType([ [ 'date', /** @param {*} val @returns {val is Date} */ (val) => val instanceof Date, ], [ 'regexp', /** @param {*} val @returns {val is RegExp} */ (val) => val instanceof RegExp, ], [ 'map', /** @param {*} val @returns {val is Map<unknown, unknown>} */ (val) => val instanceof Map, ], [ 'set', /** @param {*} val @returns {val is Set<unknown>} */ (val) => val instanceof Set, ], [ 'weakmap', /** @param {*} val @returns {val is WeakMap<unknown, unknown>} */ (val) => val instanceof WeakMap, ], [ 'weakset', /** @param {*} val @returns {val is WeakSet<unknown>} */ (val) => val instanceof WeakSet, ], [ 'promise', /** @param {*} val @returns {val is Promise<unknown>} */ (val) => val instanceof Promise, ], ]); if (isBrowser) { extendObjType([ [ 'htmlelement', /** @param {*} val @returns {val is HTMLElement} */ (val) => typeof HTMLElement !== 'undefined' && val instanceof HTMLElement, ], ]); } extendObjType([ [ 'object', /** @param {*} val @returns {val is Record<string | number | symbol, unknown>} */ (val) => isJsonObject(val), ], ]); exports.checkObj = checkObj; exports.cloneObjTypeOrder = cloneObjTypeOrder; exports.countObj = countObj; exports.extendObjType = extendObjType; exports.getCheckObj = getCheckObj; exports.isJsonObject = isJsonObject; exports.objType = objType; exports.reorderObjTypeOrder = reorderObjTypeOrder;