UNPKG

@w3ux/utils

Version:

A collection of reusable utilities for manipulating data

1 lines 33 kB
{"version":3,"sources":["../src/index.ts","../src/base.ts","../src/convert.ts","../src/jsx.ts","../src/unit.ts"],"sourcesContent":["/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nexport * from './base'\nexport * from './convert'\nexport * from './jsx'\nexport * from './unit'\n","/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\n/**\n * Ensures a number has at least the specified number of decimal places, retaining commas in the output if they are present in the input.\n *\n * @function minDecimalPlaces\n * @param {string | number | BigInt} val - The input number, which can be a `string` with or without commas, a `number`, or a `BigInt`.\n * @param {number} minDecimals - The minimum number of decimal places to enforce.\n * @returns {string} The formatted number as a string, padded with zeros if needed to meet `minDecimals`, retaining commas if originally provided.\n * If `val` is invalid, returns \"0\".\n * @example\n * // Pads \"1,234.5\" to have at least 3 decimal places, with commas\n * minDecimalPlaces(\"1,234.5\", 3); // returns \"1,234.500\"\n *\n * // Returns \"1234.56\" unchanged\n * minDecimalPlaces(1234.56, 2); // returns \"1234.56\"\n *\n * // Pads BigInt 1234 with 2 decimals\n * minDecimalPlaces(BigInt(1234), 2); // returns \"1234.00\"\n */\nexport const minDecimalPlaces = (\n\tval: string | number | bigint,\n\tminDecimals: number,\n): string => {\n\ttry {\n\t\t// Determine if we should retain commas based on original input type\n\t\tconst retainCommas = typeof val === 'string' && val.includes(',')\n\n\t\t// Convert `val` to a plain string for processing\n\t\tconst strVal =\n\t\t\ttypeof val === 'string' ? val.replace(/,/g, '') : val.toString()\n\n\t\t// Separate integer and decimal parts\n\t\tconst [integerPart, fractionalPart = ''] = strVal.split('.')\n\n\t\t// Parse the integer part as a BigInt\n\t\tconst whole = BigInt(integerPart || '0')\n\n\t\t// Calculate missing decimal places\n\t\tconst missingDecimals = minDecimals - fractionalPart.length\n\n\t\t// Format the integer part back with commas only if the input had commas\n\t\tconst formattedWhole = retainCommas\n\t\t\t? Intl.NumberFormat('en-US').format(whole)\n\t\t\t: whole.toString()\n\n\t\t// If missing decimals are needed, pad with zeros; otherwise, return the original value\n\t\treturn missingDecimals > 0\n\t\t\t? `${formattedWhole}.${fractionalPart}${'0'.repeat(missingDecimals)}`\n\t\t\t: `${formattedWhole}.${fractionalPart}`\n\t} catch {\n\t\t// The provided value is not a valid number, return \"0\".\n\t\treturn '0'\n\t}\n}\n\n/**\n * @name camelize\n * @summary Converts a string of text to camelCase.\n */\nexport const camelize = (str: string) => {\n\tconst convertToString = (string: string) => {\n\t\tif (string) {\n\t\t\tif (typeof string === 'string') {\n\t\t\t\treturn string\n\t\t\t}\n\t\t\treturn String(string)\n\t\t}\n\t\treturn ''\n\t}\n\n\tconst toWords = (inp: string) =>\n\t\tconvertToString(inp).match(\n\t\t\t/[A-Z\\xC0-\\xD6\\xD8-\\xDE]?[a-z\\xDF-\\xF6\\xF8-\\xFF]+|[A-Z\\xC0-\\xD6\\xD8-\\xDE]+(?![a-z\\xDF-\\xF6\\xF8-\\xFF])|\\d+/g,\n\t\t)\n\n\tconst simpleCamelCase = (inp: string[]) => {\n\t\tlet result = ''\n\t\tfor (let i = 0; i < inp?.length; i++) {\n\t\t\tconst currString = inp[i]\n\t\t\tlet tmpStr = currString.toLowerCase()\n\t\t\tif (i !== 0) {\n\t\t\t\ttmpStr =\n\t\t\t\t\ttmpStr.slice(0, 1).toUpperCase() + tmpStr.slice(1, tmpStr.length)\n\t\t\t}\n\t\t\tresult += tmpStr\n\t\t}\n\t\treturn result\n\t}\n\n\tconst w = toWords(str)?.map((a) => a.toLowerCase())\n\treturn simpleCamelCase(w || [])\n}\n\n/**\n * @name ellipsisFn\n * @summary Receives an address and creates ellipsis on the given string, based on parameters.\n * @param str - The string to apply the ellipsis on\n * @param amount - The amount of characters that the ellipsis will be\n * @param position - where the ellipsis will apply; if center the amount of character is the\n * same for beginning and end; if \"start\" or \"end\" then its only once the amount; defaults to \"start\"\n */\nexport const ellipsisFn = (\n\tstr: string,\n\tamount = 6,\n\tposition: 'start' | 'end' | 'center' = 'center',\n) => {\n\tconst half = str.length / 2\n\n\t// having an amount less than 4 is a bit extreme so we default there\n\tif (amount <= 4) {\n\t\tif (position === 'center') {\n\t\t\treturn str.slice(0, 4) + '...' + str.slice(-4)\n\t\t}\n\t\tif (position === 'end') {\n\t\t\treturn str.slice(0, 4) + '...'\n\t\t}\n\t\treturn '...' + str.slice(-4)\n\t}\n\t// if the amount requested is in a \"logical\" amount - meaning that it can display the address\n\t// without repeating the same information twice - then go for it;\n\tif (position === 'center') {\n\t\treturn amount >= (str.length - 2) / 2\n\t\t\t? str.slice(0, half - 3) + '...' + str.slice(-(half - 3))\n\t\t\t: str.slice(0, amount) + '...' + str.slice(-amount)\n\t}\n\t// else, the user has been mistaskenly extreme, so just show the maximum possible amount\n\tif (amount >= str.length) {\n\t\tif (position === 'end') {\n\t\t\treturn str.slice(0, str.length - 3) + '...'\n\t\t}\n\t\treturn '...' + str.slice(-(str.length - 3))\n\t} else {\n\t\tif (position === 'end') {\n\t\t\treturn str.slice(0, amount) + '...'\n\t\t}\n\t\treturn '...' + str.slice(amount)\n\t}\n}\n\n/**\n * @name pageFromUri\n * @summary Use url variables to load the default components upon the first page visit.\n */\nexport const pageFromUri = (pathname: string, fallback: string) => {\n\tconst lastUriItem = pathname.substring(pathname.lastIndexOf('/') + 1)\n\tconst page = lastUriItem.trim() === '' ? fallback : lastUriItem\n\treturn page.trim()\n}\n\n/**\n * @name rmCommas\n * @summary Removes the commas from a string.\n */\nexport const rmCommas = (val: string): string => val.replace(/,/g, '')\n\n/**\n * @name rmDecimals\n * @summary Removes the decimal point and decimals from a string.\n */\nexport const rmDecimals = (str: string) => str.split('.')[0]\n\n/**\n * @name shuffle\n * @summary Shuffle a set of objects.\n */\nexport const shuffle = <T>(array: T[]) => {\n\tlet currentIndex = array.length\n\tlet randomIndex\n\twhile (currentIndex !== 0) {\n\t\trandomIndex = Math.floor(Math.random() * currentIndex)\n\t\tcurrentIndex--\n\t\t;[array[currentIndex], array[randomIndex]] = [\n\t\t\tarray[randomIndex],\n\t\t\tarray[currentIndex],\n\t\t]\n\t}\n\treturn array\n}\n\n/**\n * @name withTimeout\n * @summary Timeout a promise after a specified number of milliseconds.\n */\nexport const withTimeout = (\n\tms: number,\n\tpromise: Promise<unknown>,\n\toptions?: {\n\t\tonTimeout?: () => void\n\t},\n) => {\n\tconst timeout = new Promise((resolve) =>\n\t\tsetTimeout(async () => {\n\t\t\tif (typeof options?.onTimeout === 'function') {\n\t\t\t\toptions.onTimeout()\n\t\t\t}\n\t\t\tresolve(undefined)\n\t\t}, ms),\n\t)\n\treturn Promise.race([promise, timeout])\n}\n\n/**\n * @name withTimeoutThrow\n * @summary Timeout a promise after a specified number of milliseconds by throwing an error\n */\nexport const withTimeoutThrow = <T>(\n\tms: number,\n\tpromise: Promise<T>,\n\toptions?: {\n\t\tonTimeout?: () => void\n\t},\n) => {\n\tconst timeout = new Promise((reject) =>\n\t\tsetTimeout(async () => {\n\t\t\tif (typeof options?.onTimeout === 'function') {\n\t\t\t\toptions.onTimeout()\n\t\t\t}\n\t\t\treject('Function timeout')\n\t\t}, ms),\n\t)\n\treturn Promise.race([promise, timeout])\n}\n\n/**\n * @name appendOrEmpty\n * @summary Returns ` value` if a condition is truthy, or an empty string otherwise.\n */\nexport const appendOrEmpty = (\n\tcondition: boolean | string | undefined,\n\tvalue: string,\n) => (condition ? ` ${value}` : '')\n\n/**\n * @name appendOr\n * @summary Returns ` value` if condition is truthy, or ` fallback` otherwise.\n */\nexport const appendOr = (\n\tcondition: boolean | string | undefined,\n\tvalue: string,\n\tfallback: string,\n) => (condition ? ` ${value}` : ` ${fallback}`)\n\n/**\n * @name removeHexPrefix\n * @summary Takes a string str as input and returns a new string with the \"0x\" prefix removed if it\n * exists at the beginning of the input string.\n */\nexport const removeHexPrefix = (str: string): string => str.replace(/^0x/, '')\n\n// Check if 2 sets contain the same elements.\n// biome-ignore lint/suspicious/noExplicitAny: <>\nexport const eqSet = (xs: Set<any>, ys: Set<any>) =>\n\txs.size === ys.size && [...xs].every((x) => ys.has(x))\n\n// Check if one set contains all the elements of another set.\n// biome-ignore lint/suspicious/noExplicitAny: <>\nexport const isSuperset = (set: Set<any>, subset: Set<any>) => {\n\tfor (const elem of subset) {\n\t\tif (!set.has(elem)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n/**\n * Finds the maximum value among a list of BigInt values.\n *\n * @function maxBigInt\n * @param {...bigint} values - A list of BigInt values to compare.\n * @returns {bigint} The largest BigInt value in the provided list.\n * @example\n * // Returns the maximum BigInt value\n * maxBigInt(10n, 50n, 30n, 100n, 20n); // 100n\n */\nexport const maxBigInt = (...values: bigint[]): bigint =>\n\tvalues.reduce((max, current) => (current > max ? current : max))\n\n/**\n * Finds the minimum value among a list of BigInt values.\n *\n * @function minBigInt\n * @param {...bigint} values - A list of BigInt values to compare.\n * @returns {bigint} The smallest BigInt value in the provided list.\n * @example\n * // Returns the minimum BigInt value\n * minBigInt(10n, 50n, 30n, 100n, 20n); // 10n\n */\nexport const minBigInt = (...values: bigint[]): bigint =>\n\tvalues.reduce((min, current) => (current < min ? current : min))\n","// /* @license Copyright 2024 w3ux authors & contributors\n// SPDX-License-Identifier: GPL-3.0-only */\n\n/**\n * Concatenates multiple Uint8Array instances into a single Uint8Array.\n *\n * @param {Uint8Array[]} u8as - An array of Uint8Array instances to concatenate.\n * @returns {Uint8Array} A new Uint8Array containing all the input arrays concatenated.\n */\nexport const u8aConcat = (...u8as: Uint8Array[]): Uint8Array => {\n\t// Calculate the total length of the resulting Uint8Array\n\tconst totalLength = u8as.reduce((sum, u8a) => sum + u8a.length, 0)\n\n\t// Create a new Uint8Array with the total length\n\tconst result = new Uint8Array(totalLength)\n\n\tlet offset = 0 // Initialize the offset for placing elements\n\tfor (const u8a of u8as) {\n\t\tresult.set(u8a, offset) // Set the current Uint8Array at the current offset\n\t\toffset += u8a.length // Update the offset for the next Uint8Array\n\t}\n\n\treturn result\n}\n","/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { ForwardedRef, RefObject } from 'react'\n\n// Merge an external ref (callback or RefObject) with an internal ref so both stay in sync\nexport const mergeRefs = <T extends HTMLElement>(\n\tel: T | null,\n\tinternalRef: RefObject<HTMLElement | null>,\n\texternalRef: ForwardedRef<T>,\n): void => {\n\tinternalRef.current = el\n\tif (externalRef) {\n\t\tif (typeof externalRef === 'function') {\n\t\t\texternalRef(el)\n\t\t} else {\n\t\t\texternalRef.current = el\n\t\t}\n\t}\n}\n","/* @license Copyright 2024 w3ux authors & contributors\nSPDX-License-Identifier: GPL-3.0-only */\n\nimport type { RefObject } from 'react'\nimport { rmCommas, rmDecimals } from './base'\nimport type { AnyObject } from './types'\n\n/**\n * Converts an on-chain balance value from planck to a decimal value in token units.\n *\n * @function planckToUnit\n * @param {number | BigInt | string} val - The balance value in planck. Accepts a `number`, `BigInt`, or `string`.\n * @param {number} units - The number of decimal places in the token unit (10^units planck per 1 token).\n * @returns {string} The equivalent token unit value as a decimal string.\n * @example\n * // Convert 1500000000000 planck to tokens with 12 decimal places\n * planckToUnit(\"1500000000000\", 12); // returns \"1.5\"\n */\nexport const planckToUnit = (\n\tval: number | bigint | string,\n\tunits: number,\n): string => {\n\ttry {\n\t\t// Ensure `units` is a positive integer.\n\t\tunits = Math.max(Math.round(units), 0)\n\n\t\t// Convert `val` to BigInt based on its type\n\t\tconst bigIntVal =\n\t\t\ttypeof val === 'bigint'\n\t\t\t\t? val\n\t\t\t\t: BigInt(\n\t\t\t\t\t\ttypeof val === 'number'\n\t\t\t\t\t\t\t? Math.floor(val).toString()\n\t\t\t\t\t\t\t: rmDecimals(rmCommas(val)),\n\t\t\t\t\t)\n\n\t\tconst divisor = units === 0 ? 1n : BigInt(10) ** BigInt(units)\n\n\t\t// Integer division and remainder for the fractional part\n\t\tconst integerPart = bigIntVal / divisor\n\t\tconst fractionalPart = bigIntVal % divisor\n\n\t\t// Format fractional part with leading zeros to maintain `units` decimal places\n\t\tconst fractionalStr =\n\t\t\tunits > 0 ? `.${fractionalPart.toString().padStart(units, '0')}` : ``\n\n\t\t// Combine integer and fractional parts as a decimal string\n\t\treturn `${integerPart}${fractionalStr}`\n\t} catch {\n\t\treturn '0'\n\t}\n}\n\n/**\n * Converts a token unit value to an integer value in planck.\n *\n * @function unitToPlanck\n * @param {string | number | BigInt} val - The token unit value to convert. Accepts a string, number, or BigInt.\n * @param {number} units - The number of decimal places for conversion (10^units planck per 1 token).\n * @returns {BigInt} The equivalent value in planck as a BigInt.\n * @example\n * // Convert \"1.5\" tokens to planck with 12 decimal places\n * unitToPlanck(\"1.5\", 12); // returns BigInt(\"1500000000000\")\n */\nexport const unitToPlanck = (\n\tval: string | number | bigint,\n\tunits: number,\n): bigint => {\n\ttry {\n\t\t// Ensure `units` is a positive integer.\n\t\tunits = Math.max(Math.round(units), 0)\n\n\t\t// Convert `val` to a string; if empty or invalid, default to \"0\"\n\t\tconst strVal =\n\t\t\t(typeof val === 'string' ? rmCommas(val) : val.toString()) || '0'\n\n\t\t// Split into integer and fractional parts\n\t\tconst [integerPart, fractionalPart = ''] = strVal.split('.')\n\n\t\t// Process the integer part by converting to BigInt and scaling it to the given units\n\t\tlet bigIntValue = BigInt(integerPart) * BigInt(10) ** BigInt(units)\n\n\t\t// Process the fractional part if it exists\n\t\tif (fractionalPart) {\n\t\t\tlet fractionalValue: bigint\n\n\t\t\tif (fractionalPart.length > units) {\n\t\t\t\t// If fractional part exceeds units, truncate it\n\t\t\t\tfractionalValue = BigInt(fractionalPart.slice(0, units))\n\t\t\t} else {\n\t\t\t\t// Otherwise, pad the fractional part to match units\n\t\t\t\tfractionalValue = BigInt(fractionalPart.padEnd(units, '0'))\n\t\t\t}\n\n\t\t\tbigIntValue += fractionalValue\n\t\t}\n\n\t\treturn bigIntValue\n\t} catch {\n\t\treturn BigInt(0)\n\t}\n}\n\n/**\n * @name remToUnit\n * @summary Converts a rem string to a number.\n */\nexport const remToUnit = (rem: string) =>\n\tNumber(rem.slice(0, rem.length - 3)) *\n\tparseFloat(getComputedStyle(document.documentElement).fontSize)\n\n/**\n * @name capitalizeFirstLetter\n * @summary Capitalize the first letter of a string.\n */\nexport const capitalizeFirstLetter = (string: string) =>\n\tstring.charAt(0).toUpperCase() + string.slice(1)\n\n/**\n * @name snakeToCamel\n * @summary converts a string from snake / kebab-case to camel-case.\n */\nexport const snakeToCamel = (str: string) =>\n\tstr\n\t\t.toLowerCase()\n\t\t.replace(/([-_][a-z])/g, (group) =>\n\t\t\tgroup.toUpperCase().replace('-', '').replace('_', ''),\n\t\t)\n\n/**\n * @name setStateWithRef\n * @summary Synchronize React state and its reference with the provided value.\n */\nexport const setStateWithRef = <T>(\n\tvalue: T,\n\tsetState: (_state: T) => void,\n\tref: RefObject<T>,\n): void => {\n\tsetState(value)\n\tref.current = value\n}\n\n/**\n * @name localStorageOrDefault\n * @summary Retrieve the local stroage value with the key, return defult value if it is not\n * found.\n */\nexport const localStorageOrDefault = <T>(\n\tkey: string,\n\t_default: T,\n\tparse = false,\n): T | string => {\n\tconst val: string | null = localStorage.getItem(key)\n\n\tif (val === null) {\n\t\treturn _default\n\t}\n\n\tif (parse) {\n\t\treturn JSON.parse(val) as T\n\t}\n\treturn val\n}\n\n/**\n * @name extractUrlValue\n * @summary Extracts a URL value from a URL string.\n */\nexport const extractUrlValue = (key: string, url?: string) => {\n\tif (typeof url === 'undefined') {\n\t\turl = window.location.href\n\t}\n\tconst match = url.match(`[?&]${key}=([^&]+)`)\n\treturn match ? match[1] : null\n}\n\n/**\n * @name varToUrlHash\n * @summary Puts a variable into the URL hash as a param.\n * @description\n * Since url variables are added to the hash and are not treated as URL params, the params are split\n * and parsed into a `URLSearchParams`.\n */\nexport const varToUrlHash = (\n\tkey: string,\n\tval: string,\n\taddIfMissing: boolean,\n) => {\n\tconst hash = window.location.hash\n\tconst [page, params] = hash.split('?')\n\tconst searchParams = new URLSearchParams(params)\n\n\tif (searchParams.get(key) === null && !addIfMissing) {\n\t\treturn\n\t}\n\tsearchParams.set(key, val)\n\twindow.location.hash = `${page}?${searchParams.toString()}`\n}\n\n/**\n * @name removeVarFromUrlHash\n * @summary\n * Removes a variable `key` from the URL hash if it exists. Removes dangling `?` if no URL variables\n * exist.\n */\nexport const removeVarFromUrlHash = (key: string) => {\n\tconst hash = window.location.hash\n\tconst [page, params] = hash.split('?')\n\tconst searchParams = new URLSearchParams(params)\n\tif (searchParams.get(key) === null) {\n\t\treturn\n\t}\n\tsearchParams.delete(key)\n\tconst paramsAsStr = searchParams.toString()\n\twindow.location.hash = `${page}${paramsAsStr ? `?${paramsAsStr}` : ``}`\n}\n\n/**\n * @name sortWithNull\n * @summary Sorts an array with nulls last.\n */\nexport const sortWithNull =\n\t(ascending: boolean) => (a: unknown, b: unknown) => {\n\t\t// if both items are undefined, treat them as equal\n\t\tif (typeof a === 'undefined' && typeof b === 'undefined') {\n\t\t\treturn 0\n\t\t}\n\t\t// if either item is undefined, sort it last\n\t\tif (typeof a === 'undefined' || typeof b === 'undefined') {\n\t\t\treturn typeof a === 'undefined' ? 1 : -1\n\t\t}\n\t\t// equal items sort equally\n\t\tif (a === b) {\n\t\t\treturn 0\n\t\t}\n\t\t// nulls sort after anything else\n\t\tif (a === null) {\n\t\t\treturn 1\n\t\t}\n\t\tif (b === null) {\n\t\t\treturn -1\n\t\t}\n\t\t// otherwise, if we're ascending, lowest sorts first\n\t\tif (ascending) {\n\t\t\treturn a < b ? -1 : 1\n\t\t}\n\t\t// if descending, highest sorts first\n\t\treturn a < b ? 1 : -1\n\t}\n\n/**\n * @name applyWidthAsPadding\n * @summary Applies width of subject to paddingRight of container.\n */\nexport const applyWidthAsPadding = (\n\tsubjectRef: RefObject<HTMLDivElement | null>,\n\tcontainerRef: RefObject<HTMLDivElement | null>,\n) => {\n\tif (containerRef.current && subjectRef.current) {\n\t\tcontainerRef.current.style.paddingRight = `${\n\t\t\tsubjectRef.current.offsetWidth + remToUnit('1rem')\n\t\t}px`\n\t}\n}\n\n/**\n * @name unescape\n * @summary Replaces \\” with “\n */\nexport const unescape = (val: string) => val.replace(/\\\\\"/g, '\"')\n\n/**\n * @name inChrome\n * @summary Whether the application is rendering in Chrome.\n */\nexport const inChrome = () => {\n\tconst isChromium = (window as Window & { chrome?: boolean })?.chrome || null\n\tconst winNav = (window as Window)?.navigator || null\n\tconst isOpera =\n\t\ttypeof (window as Window & { opr?: boolean })?.opr !== 'undefined'\n\tconst isIEedge = winNav?.userAgent.indexOf('Edg') > -1 || false\n\tconst isIOSChrome = winNav?.userAgent.match('CriOS') || false\n\n\tif (isIOSChrome) {\n\t\treturn true\n\t}\n\tif (\n\t\tisChromium !== null &&\n\t\ttypeof isChromium !== 'undefined' &&\n\t\tisOpera === false &&\n\t\tisIEedge === false\n\t) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n/**\n * @name addedTo\n * @summary Given 2 objects and some keys, return items in the fresh object that do not exist in the\n * stale object by matching the given common key values of both objects.\n */\nexport const addedTo = (\n\tfresh: AnyObject[],\n\tstale: AnyObject[],\n\tkeys: string[],\n): AnyObject[] =>\n\ttypeof fresh !== 'object' || typeof stale !== 'object' || !keys.length\n\t\t? []\n\t\t: fresh.filter(\n\t\t\t\t(freshItem) =>\n\t\t\t\t\t!stale.find((staleItem) =>\n\t\t\t\t\t\tkeys.every((key) =>\n\t\t\t\t\t\t\t!(key in staleItem) || !(key in freshItem)\n\t\t\t\t\t\t\t\t? false\n\t\t\t\t\t\t\t\t: staleItem[key] === freshItem[key],\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t)\n\n/**\n * @name removedFrom\n * @summary Given 2 objects and some keys, return items in the stale object that do not exist in the\n * fresh object by matching the given common key values of both objects.\n */\nexport const removedFrom = (\n\tfresh: AnyObject[],\n\tstale: AnyObject[],\n\tkeys: string[],\n): AnyObject[] =>\n\ttypeof fresh !== 'object' || typeof stale !== 'object' || !keys.length\n\t\t? []\n\t\t: stale.filter(\n\t\t\t\t(staleItem) =>\n\t\t\t\t\t!fresh.find((freshItem) =>\n\t\t\t\t\t\tkeys.every((key) =>\n\t\t\t\t\t\t\t!(key in staleItem) || !(key in freshItem)\n\t\t\t\t\t\t\t\t? false\n\t\t\t\t\t\t\t\t: freshItem[key] === staleItem[key],\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t)\n\n/**\n * @name matchedProperties\n * @summary Given 2 objects and some keys, return items in object 1 that also exist in object 2 by\n * matching the given common key values of both objects.\n */\nexport const matchedProperties = (\n\tobjX: AnyObject[],\n\tobjY: AnyObject[],\n\tkeys: string[],\n): AnyObject[] =>\n\ttypeof objX !== 'object' || typeof objY !== 'object' || !keys.length\n\t\t? []\n\t\t: objY.filter((x) =>\n\t\t\t\tobjX.find((y) =>\n\t\t\t\t\tkeys.every((key) =>\n\t\t\t\t\t\t!(key in x) || !(key in y) ? false : y[key] === x[key],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\n/**\n * @name isValidHttpUrl\n * @summary Give a string, return whether it is a valid http URL.\n * @param string - The string to check.\n */\nexport const isValidHttpUrl = (string: string) => {\n\tlet url: URL\n\ttry {\n\t\turl = new URL(string)\n\t} catch (_) {\n\t\treturn false\n\t}\n\treturn url.protocol === 'http:' || url.protocol === 'https:'\n}\n\n/**\n * @name makeCancelable\n * @summary Makes a promise cancellable.\n * @param promise - The promise to make cancellable.\n */\nexport const makeCancelable = (promise: Promise<AnyObject>) => {\n\tlet hasCanceled = false\n\n\tconst wrappedPromise = new Promise((resolve, reject) => {\n\t\tpromise.then((val) =>\n\t\t\thasCanceled ? reject(Error('Cancelled')) : resolve(val),\n\t\t)\n\t\tpromise.catch((error) =>\n\t\t\thasCanceled ? reject(Error('Cancelled')) : reject(error),\n\t\t)\n\t})\n\n\treturn {\n\t\tpromise: wrappedPromise,\n\t\tcancel: () => {\n\t\t\thasCanceled = true\n\t\t},\n\t}\n}\n\n/**\n * @name unimplemented\n * @summary A placeholder function to signal a deliberate unimplementation.\n * Consumes an arbitrary number of props.\n */\nexport const unimplemented = (_props?: unknown) => {\n\t/* unimplemented */\n}\n\n/**\n * Deep merge two objects.\n * @param target\n * @param ...sources\n */\n\nexport const mergeDeep = (\n\ttarget: AnyObject,\n\t...sources: AnyObject[]\n): AnyObject => {\n\tif (!sources.length) {\n\t\treturn target\n\t}\n\n\tconst isObject = (item: AnyObject) =>\n\t\titem && typeof item === 'object' && !Array.isArray(item)\n\tconst source = sources.shift()\n\n\tif (isObject(target) && isObject(source)) {\n\t\tfor (const key in source) {\n\t\t\tif (isObject(source[key])) {\n\t\t\t\tif (!target[key]) {\n\t\t\t\t\tObject.assign(target, { [key]: {} })\n\t\t\t\t}\n\t\t\t\tmergeDeep(target[key], source[key])\n\t\t\t} else {\n\t\t\t\tObject.assign(target, { [key]: source[key] })\n\t\t\t}\n\t\t}\n\t}\n\treturn mergeDeep(target, ...sources)\n}\n"],"mappings":"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","names":[]}