@visulima/humanizer 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -19,3 +19,42 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
23
+ **This project file (src/duration.ts and all files under the src/language) are licensed under the Unlicense license:**
24
+
25
+ ---
26
+ The Unlicense
27
+
28
+ Copyright (c) 2013-2024 Evan Hahn (@EvanHahn)
29
+
30
+ Other portions copyright their respective authors, see
31
+ https://github.com/EvanHahn/HumanizeDuration.js/graphs/contributors
32
+
33
+ This is free and unencumbered software released into the public domain.
34
+
35
+ Anyone is free to copy, modify, publish, use, compile, sell, or
36
+ distribute this software, either in source code form or as a compiled
37
+ binary, for any purpose, commercial or non-commercial, and by any
38
+ means.
39
+
40
+ In jurisdictions that recognize copyright laws, the author or authors
41
+ of this software dedicate any and all copyright interest in the
42
+ software to the public domain. We make this dedication for the benefit
43
+ of the public at large and to the detriment of our heirs and
44
+ successors. We intend this dedication to be an overt act of
45
+ relinquishment in perpetuity of all present and future rights to this
46
+ software under copyright law.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
49
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
50
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
51
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
52
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
53
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
54
+ OTHER DEALINGS IN THE SOFTWARE.
55
+
56
+ For more information, please refer to <https://unlicense.org/>
57
+
58
+ ---
59
+
60
+ SPDX-License-Identifier: MIT or UNLICENSE
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bytes.ts","../src/language/util/validate-duration-language.ts","../src/duration.ts"],"names":["BYTE_SIZES","parseLocalizedNumber","stringNumber","locale","thousandSeparator","decimalSeparator","fromBase","base","parseBytes","value","options","config","groups","localizedNumber","type","level","unit","formatBytes","bytes","givenBase","decimals","long","requestedUnit","l10nOptions","absoluteBytes","space","requestedUnitIndex","fractionDigits","validateDurationLanguage","language","requiredProperties","property","validate_duration_language_default","toFixed","number_","fixed","matches","renderPiece","unitCount","unitName","maxDecimalPoints","spacer","decimal","digitReplacements","formattedCount","normalizedUnitCount","countString","char","languageWord","word","getPieces","ms","units","unitMeasures","largest","unitCounts","index","msRemaining","unitMs","unitsRemainingBeforeRound","index_","smallerUnitName","smallerUnitCount","rounded","previousUnitName","previousUnitMs","amountOfPreviousUnit","result","remainder","remainderUnitName","formatPieces","pieces","smallestUnitName","conjunction","serialComma","delimiter","adverb","renderedPieces","piece_","piece","duration","milliseconds","durationLanguage","absTime","duration_default"],"mappings":"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","sourcesContent":["import type { FormateByteOptions, ParseByteOptions } from \"./types\";\n\nconst BYTE_SIZES = [\n {\n long: \"Bytes\",\n short: \"Bytes\",\n },\n {\n long: \"Kilobytes\",\n short: \"KB\",\n },\n {\n long: \"Megabytes\",\n short: \"MB\",\n },\n {\n long: \"Gigabytes\",\n short: \"GB\",\n },\n {\n long: \"Terabytes\",\n short: \"TB\",\n },\n {\n long: \"Petabytes\",\n short: \"PB\",\n },\n {\n long: \"Exabytes\",\n short: \"EB\",\n },\n {\n long: \"Zettabytes\",\n short: \"ZB\",\n },\n {\n long: \"Yottabytes\",\n short: \"YB\",\n },\n] as const;\n\ntype ByteSize = (typeof BYTE_SIZES)[number][\"short\"];\ntype LongByteSize = (typeof BYTE_SIZES)[number][\"long\"];\n\ntype Unit = ByteSize | LongByteSize;\n\n/**\n * Parse a localized number to a float.\n * @param stringNumber - the localized number\n * @param locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.\n */\nconst parseLocalizedNumber = (stringNumber: string, locale: string): number => {\n const thousandSeparator = new Intl.NumberFormat(locale).format(11_111).replaceAll(/\\p{Number}/gu, \"\");\n const decimalSeparator = new Intl.NumberFormat(locale).format(1.1).replaceAll(/\\p{Number}/gu, \"\");\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n return Number.parseFloat(stringNumber.replaceAll(new RegExp(`\\\\${thousandSeparator}`, \"g\"), \"\").replace(new RegExp(`\\\\${decimalSeparator}`), \".\"));\n};\n\nconst fromBase = (base: 2 | 10) => {\n if (base === 2) {\n return 1024;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (base === 10) {\n return 1000;\n }\n\n throw new TypeError(`Unsupported base.`);\n};\n\n/**\n * Parse the given bytesize string and return bytes.\n *\n * @param value - The string to parse\n * @param options - Options for the conversion from string to bytes\n * @throws Error if `value` is not a non-empty string or a number\n */\nexport const parseBytes = (value: string, options?: ParseByteOptions): number => {\n const config = {\n base: 2,\n locale: \"en-US\",\n ...options,\n } as Required<ParseByteOptions>;\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(\"Value is not a string or is empty.\");\n }\n\n if (value.length > 100) {\n throw new TypeError(\"Value exceeds the maximum length of 100 characters.\");\n }\n\n const match =\n // eslint-disable-next-line regexp/no-super-linear-backtracking,regexp/no-unused-capturing-group,regexp/no-misleading-capturing-group,security/detect-unsafe-regex\n /^(?<value>-?(?:\\d+(([.,])\\d+)*)?[.,]?\\d+) *(?<type>bytes?|b|kb|kib|mb|mib|gb|gib|tb|tib|pb|pib|eb|eib|zb|zib|yb|yib|(kilo|kibi|mega|mebi|giga|gibi|tera|tebi|peta|pebi|exa|exbi|zetta|zebi|yotta|yobi)?bytes)?$/i.exec(\n value,\n );\n // Named capture groups need to be manually typed today.\n // https://github.com/microsoft/TypeScript/issues/32098\n const groups = match?.groups as { type?: string; value: string } | undefined;\n\n if (!groups) {\n return Number.NaN;\n }\n\n const localizedNumber = parseLocalizedNumber(groups.value, config.locale);\n const type = (groups.type ?? \"Bytes\")\n .toUpperCase()\n .replace(/^KIBI/, \"KILO\")\n .replace(/^MIBI/, \"MEGA\")\n .replace(/^GIBI/, \"GIGA\")\n .replace(/^TEBI/, \"TERA\")\n .replace(/^PEBI/, \"PETA\")\n .replace(/^EXBI/, \"EXA\")\n .replace(/^ZEBI/, \"ZETTA\")\n .replace(/^YIBI/, \"YOTTA\")\n .replace(/^(.)IB$/, \"$1B\") as Uppercase<Unit> | \"B\";\n const level = BYTE_SIZES.findIndex((unit) => (unit.short[0] as string).toUpperCase() === type[0]);\n const base = fromBase(config.base);\n\n return localizedNumber * base ** level;\n};\n\n/**\n * Formats the given bytes into a human-readable string.\n * Per default, it will use the closest unit to the given value.\n *\n * @param bytes - The bytes to format\n * @param options - Options for the conversion from bytes to string\n */\nexport const formatBytes = (bytes: number, options?: FormateByteOptions<ByteSize>): string => {\n if (typeof bytes !== \"number\" || !Number.isFinite(bytes)) {\n throw new TypeError(\"Bytesize is not a number.\");\n }\n\n const {\n base: givenBase,\n decimals,\n locale,\n long,\n unit: requestedUnit,\n ...l10nOptions\n } = {\n base: 2,\n decimals: 0,\n locale: \"en-US\",\n long: false,\n ...options,\n } as FormateByteOptions<ByteSize>;\n const base = fromBase(givenBase as 2 | 10);\n\n const absoluteBytes = Math.abs(bytes);\n const space = options?.space ?? true ? \" \" : \"\";\n\n const requestedUnitIndex = BYTE_SIZES.findIndex((unit) => unit.short === requestedUnit);\n\n if (bytes === 0) {\n const level = Math.min(0, Math.max(requestedUnitIndex, BYTE_SIZES.length - 1));\n\n // eslint-disable-next-line security/detect-object-injection,prefer-template\n return \"0\" + space + (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n }\n\n const level = requestedUnitIndex >= 0 ? requestedUnitIndex : Math.min(Math.floor(Math.log(absoluteBytes) / Math.log(base)), BYTE_SIZES.length - 1);\n // eslint-disable-next-line security/detect-object-injection\n const unit = (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n\n const value = bytes / base ** level;\n const fractionDigits = (decimals as number) < 0 ? undefined : decimals;\n const formattedValue = new Intl.NumberFormat(locale, {\n maximumFractionDigits: fractionDigits,\n minimumFractionDigits: fractionDigits,\n ...l10nOptions,\n }).format(value);\n\n return formattedValue + space + unit;\n};\n","import type { DurationLanguage } from \"../../types\";\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst validateDurationLanguage = (language: DurationLanguage): void => {\n const requiredProperties = [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\", \"future\", \"past\"];\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of requiredProperties) {\n if (!Object.prototype.hasOwnProperty.call(language, property)) {\n throw new TypeError(`Missing required property: ${property}`);\n }\n }\n\n if (typeof language.future !== \"string\" || typeof language.past !== \"string\") {\n throw new TypeError(\"Properties future and past must be of type string\");\n }\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\"]) {\n if (typeof language[property as keyof typeof language] !== \"string\" && typeof language[property as keyof typeof language] !== \"function\") {\n throw new TypeError(`Property ${property} must be of type string or function`);\n }\n }\n\n if (language.decimal && typeof language.decimal !== \"string\") {\n throw new TypeError(\"Property decimal must be of type string\");\n }\n\n if (language.delimiter && typeof language.delimiter !== \"string\") {\n throw new TypeError(\"Property delimiter must be of type string\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._digitReplacements && !Array.isArray(language._digitReplacements)) {\n throw new TypeError(\"Property _digitReplacements must be an array\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst && typeof language._numberFirst !== \"boolean\") {\n throw new TypeError(\"Property _numberFirst must be of type boolean\");\n }\n};\n\nexport default validateDurationLanguage;\n","import { durationLanguage } from \"./language/en\";\nimport validateDurationLanguage from \"./language/util/validate-duration-language\";\nimport type { DurationDigitReplacements, DurationLanguage, DurationOptions, DurationPiece, DurationUnitMeasures, DurationUnitName } from \"./types\";\n\ninterface InternalOptions {\n conjunction: string;\n decimal?: string;\n delimiter?: string;\n digitReplacements?: DurationDigitReplacements;\n fallbacks?: string[];\n language: DurationLanguage;\n largest?: number;\n maxDecimalPoints?: number;\n round: boolean;\n serialComma: boolean;\n spacer: string;\n timeAdverb: boolean;\n unitMeasures: DurationUnitMeasures;\n units: DurationUnitName[];\n}\n\nconst toFixed = (number_: number, fixed: number): number => {\n // eslint-disable-next-line no-param-reassign\n fixed = fixed || -1;\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n const matches = new RegExp(`^-?\\\\d+(?:.\\\\d{0,${fixed}})?`).exec(number_.toString());\n\n if (matches === null) {\n return number_; // can be undefined when num is Number.POSITIVE_INFINITY\n }\n\n return Number.parseFloat(matches[0]);\n};\n\nconst renderPiece = ({ unitCount, unitName }: DurationPiece, language: DurationLanguage, options: InternalOptions): string => {\n const { maxDecimalPoints, spacer } = options;\n\n let decimal = \".\";\n\n if (options.decimal !== undefined) {\n decimal = options.decimal;\n } else if (language.decimal !== undefined) {\n decimal = language.decimal;\n }\n\n let digitReplacements: DurationDigitReplacements | undefined;\n\n if (\"digitReplacements\" in options) {\n digitReplacements = options.digitReplacements;\n } else if (\"_digitReplacements\" in language) {\n // eslint-disable-next-line no-underscore-dangle\n digitReplacements = language._digitReplacements;\n }\n\n let formattedCount: string;\n\n let normalizedUnitCount = unitCount;\n\n if (maxDecimalPoints !== undefined) {\n // normalizedUnitCount = Math.floor(unitCount * 10 ** maxDecimalPoints) / 10 ** maxDecimalPoints;\n normalizedUnitCount = toFixed(unitCount, maxDecimalPoints);\n }\n\n const countString = normalizedUnitCount.toString();\n\n if (digitReplacements) {\n formattedCount = \"\";\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const char of countString) {\n // @ts-expect-error because `char` should always be 0-9 at this point.\n // eslint-disable-next-line security/detect-object-injection\n formattedCount += char === \".\" ? decimal : digitReplacements[char];\n }\n } else {\n formattedCount = countString.replace(\".\", decimal);\n }\n\n // eslint-disable-next-line security/detect-object-injection\n const languageWord = language[unitName];\n let word = languageWord as string;\n\n if (typeof languageWord === \"function\") {\n word = languageWord(unitCount) as string;\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst) {\n return (word as string) + spacer + formattedCount;\n }\n\n return formattedCount + spacer + (word as string);\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst getPieces = (ms: number, options: InternalOptions): DurationPiece[] => {\n const { units } = options;\n\n if (units.length === 0) {\n return [];\n }\n\n const { unitMeasures } = options;\n const largest = options.largest ?? Number.POSITIVE_INFINITY;\n\n // Get the counts for each unit. Doesn't round or truncate anything.\n // For example, might create an object like `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`.\n const unitCounts: Partial<Record<DurationUnitName, number>> = {};\n\n let unitName: DurationUnitName;\n let index: number;\n let unitCount: number;\n let msRemaining: number = ms;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n const unitMs = unitMeasures[unitName];\n const isLast = index === units.length - 1;\n\n unitCount = isLast ? msRemaining / unitMs : Math.floor(msRemaining / unitMs);\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = unitCount;\n\n msRemaining -= unitCount * unitMs;\n }\n\n if (options.round) {\n // Update counts based on the `largest` option.\n // For example, if `largest === 2` and `unitCount` is `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`,\n // updates to something like `{ y: 7, m: 6.2 }`.\n let unitsRemainingBeforeRound = largest;\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // eslint-disable-next-line no-plusplus\n unitsRemainingBeforeRound--;\n\n // \"Take\" the rest of the units into this one.\n if (unitsRemainingBeforeRound === 0) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-loops/no-loops,no-underscore-dangle,no-plusplus\n for (let index_ = index + 1; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const smallerUnitName = units[index_] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const smallerUnitCount = unitCounts[smallerUnitName] as number;\n\n // @ts-expect-error unitCounts[unitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] += (smallerUnitCount * unitMeasures[smallerUnitName]) / unitMeasures[unitName];\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[smallerUnitName] = 0;\n }\n\n break;\n }\n }\n\n // Round the last piece (which should be the only non-integer).\n //\n // This can be a little tricky if the last piece \"bubbles up\" to a larger\n // unit. For example, \"3 days, 23.99 hours\" should be rounded to \"4 days\".\n // It can also require multiple passes. For example, \"6 days, 23.99 hours\"\n // should become \"1 week\".\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = units.length - 1; index >= 0; index--) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const rounded = Math.round(unitCount);\n\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = rounded;\n\n if (index === 0) {\n break;\n }\n\n const previousUnitName: DurationUnitName = units[index - 1] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const previousUnitMs = unitMeasures[previousUnitName];\n // eslint-disable-next-line security/detect-object-injection\n const amountOfPreviousUnit = Math.floor((rounded * unitMeasures[unitName]) / previousUnitMs);\n\n if (amountOfPreviousUnit) {\n // @ts-expect-error unitCounts[previousUnitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[previousUnitName] += amountOfPreviousUnit;\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = 0;\n } else {\n break;\n }\n }\n }\n\n const result: DurationPiece[] = [];\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length && result.length < largest; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n // If the result is not rounded, and `largest` option has been set, aggregate the rest and apply the\n // `maxDecimalPoints` to truncate the decimals\n if (unitCount && !options.round && result.length === largest - 1) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle\n let index_;\n let remainder = 0;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index_ = index + 1, units.length; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const remainderUnitName = units[index_] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n remainder += (unitCounts[remainderUnitName] as number) * (options.unitMeasures[remainderUnitName] / options.unitMeasures[unitName]);\n }\n\n unitCount += remainder;\n\n if (options.maxDecimalPoints !== undefined) {\n unitCount = toFixed(unitCount, options.maxDecimalPoints);\n }\n }\n\n if (unitCount) {\n result.push({ unitCount, unitName });\n }\n }\n\n return result;\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst formatPieces = (pieces: DurationPiece[], options: InternalOptions, ms: number): string => {\n const { language, units } = options;\n\n if (pieces.length === 0) {\n const smallestUnitName = units.at(-1) as DurationUnitName;\n\n return renderPiece({ unitCount: 0, unitName: smallestUnitName }, language, options);\n }\n\n const { conjunction, serialComma } = options;\n\n let delimiter = \", \";\n\n if (options.delimiter !== undefined) {\n delimiter = options.delimiter;\n } else if (language.delimiter !== undefined) {\n delimiter = language.delimiter;\n }\n\n // timeAdverb part\n let adverb = \"\";\n\n if (options.timeAdverb && ms !== 0) {\n adverb = language.future ?? \"\";\n\n if (ms < 0) {\n adverb = language.past ?? \"\";\n }\n }\n\n const renderedPieces: string[] = [];\n\n // eslint-disable-next-line no-loops/no-loops,@typescript-eslint/naming-convention,no-restricted-syntax,no-underscore-dangle\n for (const piece_ of pieces) {\n const piece = piece_ as DurationPiece;\n\n renderedPieces.push(renderPiece(piece, language, options));\n }\n\n let result: string;\n\n if (!conjunction || pieces.length === 1) {\n result = renderedPieces.join(delimiter);\n } else if (pieces.length === 2) {\n result = renderedPieces.join(conjunction);\n } else {\n result = renderedPieces.slice(0, -1).join(delimiter) + (serialComma ? \",\" : \"\") + conjunction + renderedPieces.at(-1);\n }\n\n if (adverb) {\n result = adverb.replace(\"%s\", result);\n }\n\n return result;\n};\n\nconst duration = (milliseconds: number, options?: DurationOptions): string => {\n if (Number.isNaN(milliseconds)) {\n throw new TypeError(\"Expected a valid number\");\n }\n\n if (typeof milliseconds !== \"number\") {\n throw new TypeError(\"Expected a number\");\n }\n\n const config = {\n conjunction: \"\",\n language: durationLanguage,\n round: false,\n serialComma: true,\n spacer: \" \",\n timeAdverb: false,\n unitMeasures: {\n d: 86_400_000,\n h: 3_600_000,\n m: 60_000,\n mo: 2_629_746_000, // 365.2425 / 12 = 30.436875 days\n ms: 1,\n s: 1000,\n w: 604_800_000,\n y: 31_556_952_000, // 365 + 1/4 - 1/100 + 1/400 (actual leap day rules) = 365.2425 days\n },\n units: [\"w\", \"d\", \"h\", \"m\", \"s\"],\n ...options,\n } as InternalOptions;\n\n validateDurationLanguage(config.language);\n\n // Has the nice side-effect of converting things to numbers. For example,\n // converts `\"123\"` and `Number(123)` to `123`.\n const absTime = Math.abs(milliseconds as number);\n\n const pieces = getPieces(absTime, config);\n\n return formatPieces(pieces, config, milliseconds as number);\n};\n\nexport default duration;\n"]}
1
+ {"version":3,"sources":["../src/bytes.ts","../src/language/util/validate-duration-language.ts","../src/duration.ts"],"names":["BYTE_SIZES","parseLocalizedNumber","stringNumber","locale","thousandSeparator","decimalSeparator","fromBase","base","parseBytes","value","options","config","groups","localizedNumber","type","level","unit","formatBytes","bytes","givenBase","decimals","long","requestedUnit","l10nOptions","absoluteBytes","space","requestedUnitIndex","fractionDigits","validateDurationLanguage","language","requiredProperties","property","validate_duration_language_default","toFixed","number_","fixed","matches","renderPiece","unitCount","unitName","maxDecimalPoints","spacer","decimal","digitReplacements","formattedCount","normalizedUnitCount","countString","char","languageWord","word","getPieces","ms","units","unitMeasures","largest","unitCounts","index","msRemaining","unitMs","unitsRemainingBeforeRound","index_","smallerUnitName","smallerUnitCount","rounded","previousUnitName","previousUnitMs","amountOfPreviousUnit","result","remainder","remainderUnitName","formatPieces","pieces","smallestUnitName","conjunction","serialComma","delimiter","adverb","renderedPieces","piece_","piece","duration","milliseconds","durationLanguage","absTime","duration_default"],"mappings":"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","sourcesContent":["import type { FormateByteOptions, ParseByteOptions } from \"./types\";\n\nconst BYTE_SIZES = [\n {\n long: \"Bytes\",\n short: \"Bytes\",\n },\n {\n long: \"Kilobytes\",\n short: \"KB\",\n },\n {\n long: \"Megabytes\",\n short: \"MB\",\n },\n {\n long: \"Gigabytes\",\n short: \"GB\",\n },\n {\n long: \"Terabytes\",\n short: \"TB\",\n },\n {\n long: \"Petabytes\",\n short: \"PB\",\n },\n {\n long: \"Exabytes\",\n short: \"EB\",\n },\n {\n long: \"Zettabytes\",\n short: \"ZB\",\n },\n {\n long: \"Yottabytes\",\n short: \"YB\",\n },\n] as const;\n\ntype ByteSize = (typeof BYTE_SIZES)[number][\"short\"];\ntype LongByteSize = (typeof BYTE_SIZES)[number][\"long\"];\n\ntype Unit = ByteSize | LongByteSize;\n\n/**\n * Parse a localized number to a float.\n * @param stringNumber - the localized number\n * @param locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.\n */\nconst parseLocalizedNumber = (stringNumber: string, locale: string): number => {\n const thousandSeparator = new Intl.NumberFormat(locale).format(11_111).replaceAll(/\\p{Number}/gu, \"\");\n const decimalSeparator = new Intl.NumberFormat(locale).format(1.1).replaceAll(/\\p{Number}/gu, \"\");\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n return Number.parseFloat(stringNumber.replaceAll(new RegExp(`\\\\${thousandSeparator}`, \"g\"), \"\").replace(new RegExp(`\\\\${decimalSeparator}`), \".\"));\n};\n\nconst fromBase = (base: 2 | 10) => {\n if (base === 2) {\n return 1024;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (base === 10) {\n return 1000;\n }\n\n throw new TypeError(`Unsupported base.`);\n};\n\n/**\n * Parse the given bytesize string and return bytes.\n *\n * @param value - The string to parse\n * @param options - Options for the conversion from string to bytes\n * @throws Error if `value` is not a non-empty string or a number\n */\nexport const parseBytes = (value: string, options?: ParseByteOptions): number => {\n const config = {\n base: 2,\n locale: \"en-US\",\n ...options,\n } as Required<ParseByteOptions>;\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(\"Value is not a string or is empty.\");\n }\n\n if (value.length > 100) {\n throw new TypeError(\"Value exceeds the maximum length of 100 characters.\");\n }\n\n const match =\n // eslint-disable-next-line regexp/no-super-linear-backtracking,regexp/no-unused-capturing-group,regexp/no-misleading-capturing-group,security/detect-unsafe-regex\n /^(?<value>-?(?:\\d+(([.,])\\d+)*)?[.,]?\\d+) *(?<type>bytes?|b|kb|kib|mb|mib|gb|gib|tb|tib|pb|pib|eb|eib|zb|zib|yb|yib|(kilo|kibi|mega|mebi|giga|gibi|tera|tebi|peta|pebi|exa|exbi|zetta|zebi|yotta|yobi)?bytes)?$/i.exec(\n value,\n );\n // Named capture groups need to be manually typed today.\n // https://github.com/microsoft/TypeScript/issues/32098\n const groups = match?.groups as { type?: string; value: string } | undefined;\n\n if (!groups) {\n return Number.NaN;\n }\n\n const localizedNumber = parseLocalizedNumber(groups.value, config.locale);\n const type = (groups.type ?? \"Bytes\")\n .toUpperCase()\n .replace(/^KIBI/, \"KILO\")\n .replace(/^MIBI/, \"MEGA\")\n .replace(/^GIBI/, \"GIGA\")\n .replace(/^TEBI/, \"TERA\")\n .replace(/^PEBI/, \"PETA\")\n .replace(/^EXBI/, \"EXA\")\n .replace(/^ZEBI/, \"ZETTA\")\n .replace(/^YIBI/, \"YOTTA\")\n .replace(/^(.)IB$/, \"$1B\") as Uppercase<Unit> | \"B\";\n const level = BYTE_SIZES.findIndex((unit) => (unit.short[0] as string).toUpperCase() === type[0]);\n const base = fromBase(config.base);\n\n return localizedNumber * base ** level;\n};\n\n/**\n * Formats the given bytes into a human-readable string.\n * Per default, it will use the closest unit to the given value.\n *\n * @param bytes - The bytes to format\n * @param options - Options for the conversion from bytes to string\n */\nexport const formatBytes = (bytes: number, options?: FormateByteOptions<ByteSize>): string => {\n if (typeof bytes !== \"number\" || !Number.isFinite(bytes)) {\n throw new TypeError(\"Bytesize is not a number.\");\n }\n\n const {\n base: givenBase,\n decimals,\n locale,\n long,\n unit: requestedUnit,\n ...l10nOptions\n } = {\n base: 2,\n decimals: 0,\n locale: \"en-US\",\n long: false,\n ...options,\n } as FormateByteOptions<ByteSize>;\n const base = fromBase(givenBase as 2 | 10);\n\n const absoluteBytes = Math.abs(bytes);\n const space = options?.space ?? true ? \" \" : \"\";\n\n const requestedUnitIndex = BYTE_SIZES.findIndex((unit) => unit.short === requestedUnit);\n\n if (bytes === 0) {\n const level = Math.min(0, Math.max(requestedUnitIndex, BYTE_SIZES.length - 1));\n\n // eslint-disable-next-line security/detect-object-injection,prefer-template\n return \"0\" + space + (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n }\n\n const level = requestedUnitIndex >= 0 ? requestedUnitIndex : Math.min(Math.floor(Math.log(absoluteBytes) / Math.log(base)), BYTE_SIZES.length - 1);\n // eslint-disable-next-line security/detect-object-injection\n const unit = (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n\n const value = bytes / base ** level;\n const fractionDigits = (decimals as number) < 0 ? undefined : decimals;\n const formattedValue = new Intl.NumberFormat(locale, {\n maximumFractionDigits: fractionDigits,\n minimumFractionDigits: fractionDigits,\n ...l10nOptions,\n }).format(value);\n\n return formattedValue + space + unit;\n};\n","import type { DurationLanguage } from \"../../types\";\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst validateDurationLanguage = (language: DurationLanguage): void => {\n const requiredProperties = [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\", \"future\", \"past\"];\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of requiredProperties) {\n if (!Object.prototype.hasOwnProperty.call(language, property)) {\n throw new TypeError(`Missing required property: ${property}`);\n }\n }\n\n if (typeof language.future !== \"string\" || typeof language.past !== \"string\") {\n throw new TypeError(\"Properties future and past must be of type string\");\n }\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\"]) {\n if (typeof language[property as keyof typeof language] !== \"string\" && typeof language[property as keyof typeof language] !== \"function\") {\n throw new TypeError(`Property ${property} must be of type string or function`);\n }\n }\n\n if (language.decimal && typeof language.decimal !== \"string\") {\n throw new TypeError(\"Property decimal must be of type string\");\n }\n\n if (language.delimiter && typeof language.delimiter !== \"string\") {\n throw new TypeError(\"Property delimiter must be of type string\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._digitReplacements && !Array.isArray(language._digitReplacements)) {\n throw new TypeError(\"Property _digitReplacements must be an array\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst && typeof language._numberFirst !== \"boolean\") {\n throw new TypeError(\"Property _numberFirst must be of type boolean\");\n }\n};\n\nexport default validateDurationLanguage;\n","import { durationLanguage } from \"./language/en\";\nimport validateDurationLanguage from \"./language/util/validate-duration-language\";\nimport type { DurationDigitReplacements, DurationLanguage, DurationOptions, DurationPiece, DurationUnitMeasures, DurationUnitName } from \"./types\";\n\ninterface InternalOptions {\n conjunction: string;\n decimal?: string;\n delimiter?: string;\n digitReplacements?: DurationDigitReplacements;\n fallbacks?: string[];\n language: DurationLanguage;\n largest?: number;\n maxDecimalPoints?: number;\n round: boolean;\n serialComma: boolean;\n spacer: string;\n timeAdverb: boolean;\n unitMeasures: DurationUnitMeasures;\n units: DurationUnitName[];\n}\n\nconst toFixed = (number_: number, fixed: number): number => {\n // eslint-disable-next-line no-param-reassign\n fixed = fixed || -1;\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n const matches = new RegExp(`^-?\\\\d+(?:.\\\\d{0,${fixed}})?`).exec(number_.toString());\n\n if (matches === null) {\n return number_; // can be undefined when num is Number.POSITIVE_INFINITY\n }\n\n return Number.parseFloat(matches[0]);\n};\n\nconst renderPiece = ({ unitCount, unitName }: DurationPiece, language: DurationLanguage, options: InternalOptions): string => {\n const { maxDecimalPoints, spacer } = options;\n\n let decimal = \".\";\n\n if (options.decimal !== undefined) {\n decimal = options.decimal;\n } else if (language.decimal !== undefined) {\n decimal = language.decimal;\n }\n\n let digitReplacements: DurationDigitReplacements | undefined;\n\n if (\"digitReplacements\" in options) {\n digitReplacements = options.digitReplacements;\n } else if (\"_digitReplacements\" in language) {\n // eslint-disable-next-line no-underscore-dangle\n digitReplacements = language._digitReplacements;\n }\n\n let formattedCount: string;\n\n let normalizedUnitCount = unitCount;\n\n if (maxDecimalPoints !== undefined) {\n // normalizedUnitCount = Math.floor(unitCount * 10 ** maxDecimalPoints) / 10 ** maxDecimalPoints;\n normalizedUnitCount = toFixed(unitCount, maxDecimalPoints);\n }\n\n const countString = normalizedUnitCount.toString();\n\n if (digitReplacements) {\n formattedCount = \"\";\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const char of countString) {\n // @ts-expect-error because `char` should always be 0-9 at this point.\n // eslint-disable-next-line security/detect-object-injection\n formattedCount += char === \".\" ? decimal : digitReplacements[char];\n }\n } else {\n formattedCount = countString.replace(\".\", decimal);\n }\n\n // eslint-disable-next-line security/detect-object-injection\n const languageWord = language[unitName];\n let word = languageWord as string;\n\n if (typeof languageWord === \"function\") {\n word = languageWord(unitCount) as string;\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst) {\n return (word as string) + spacer + formattedCount;\n }\n\n return formattedCount + spacer + (word as string);\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst getPieces = (ms: number, options: InternalOptions): DurationPiece[] => {\n const { units } = options;\n\n if (units.length === 0) {\n return [];\n }\n\n const { unitMeasures } = options;\n const largest = options.largest ?? Number.POSITIVE_INFINITY;\n\n // Get the counts for each unit. Doesn't round or truncate anything.\n // For example, might create an object like `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`.\n const unitCounts: Partial<Record<DurationUnitName, number>> = {};\n\n let unitName: DurationUnitName;\n let index: number;\n let unitCount: number;\n let msRemaining: number = ms;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n const unitMs = unitMeasures[unitName];\n const isLast = index === units.length - 1;\n\n unitCount = isLast ? msRemaining / unitMs : Math.floor(msRemaining / unitMs);\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = unitCount;\n\n msRemaining -= unitCount * unitMs;\n }\n\n if (options.round) {\n // Update counts based on the `largest` option.\n // For example, if `largest === 2` and `unitCount` is `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`,\n // updates to something like `{ y: 7, m: 6.2 }`.\n let unitsRemainingBeforeRound = largest;\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // eslint-disable-next-line no-plusplus\n unitsRemainingBeforeRound--;\n\n // \"Take\" the rest of the units into this one.\n if (unitsRemainingBeforeRound === 0) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-loops/no-loops,no-underscore-dangle,no-plusplus\n for (let index_ = index + 1; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const smallerUnitName = units[index_] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const smallerUnitCount = unitCounts[smallerUnitName] as number;\n\n // @ts-expect-error unitCounts[unitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] += (smallerUnitCount * unitMeasures[smallerUnitName]) / unitMeasures[unitName];\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[smallerUnitName] = 0;\n }\n\n break;\n }\n }\n\n // Round the last piece (which should be the only non-integer).\n //\n // This can be a little tricky if the last piece \"bubbles up\" to a larger\n // unit. For example, \"3 days, 23.99 hours\" should be rounded to \"4 days\".\n // It can also require multiple passes. For example, \"6 days, 23.99 hours\"\n // should become \"1 week\".\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = units.length - 1; index >= 0; index--) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const rounded = Math.round(unitCount);\n\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = rounded;\n\n if (index === 0) {\n break;\n }\n\n const previousUnitName: DurationUnitName = units[index - 1] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const previousUnitMs = unitMeasures[previousUnitName];\n // eslint-disable-next-line security/detect-object-injection\n const amountOfPreviousUnit = Math.floor((rounded * unitMeasures[unitName]) / previousUnitMs);\n\n if (amountOfPreviousUnit) {\n // @ts-expect-error unitCounts[previousUnitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[previousUnitName] += amountOfPreviousUnit;\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = 0;\n } else {\n break;\n }\n }\n }\n\n const result: DurationPiece[] = [];\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length && result.length < largest; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n // If the result is not rounded, and `largest` option has been set, aggregate the rest and apply the\n // `maxDecimalPoints` to truncate the decimals\n if (unitCount && !options.round && result.length === largest - 1) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle\n let index_;\n let remainder = 0;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index_ = index + 1, units.length; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const remainderUnitName = units[index_] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n remainder += (unitCounts[remainderUnitName] as number) * (options.unitMeasures[remainderUnitName] / options.unitMeasures[unitName]);\n }\n\n unitCount += remainder;\n\n if (options.maxDecimalPoints !== undefined) {\n unitCount = toFixed(unitCount, options.maxDecimalPoints);\n }\n }\n\n if (unitCount) {\n result.push({ unitCount, unitName });\n }\n }\n\n return result;\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst formatPieces = (pieces: DurationPiece[], options: InternalOptions, ms: number): string => {\n const { language, units } = options;\n\n if (pieces.length === 0) {\n const smallestUnitName = units.at(-1) as DurationUnitName;\n\n return renderPiece({ unitCount: 0, unitName: smallestUnitName }, language, options);\n }\n\n const { conjunction, serialComma } = options;\n\n let delimiter = \", \";\n\n if (options.delimiter !== undefined) {\n delimiter = options.delimiter;\n } else if (language.delimiter !== undefined) {\n delimiter = language.delimiter;\n }\n\n // timeAdverb part\n let adverb = \"\";\n\n if (options.timeAdverb && ms !== 0) {\n adverb = language.future ?? \"\";\n\n if (ms < 0) {\n adverb = language.past ?? \"\";\n }\n }\n\n const renderedPieces: string[] = [];\n\n // eslint-disable-next-line no-loops/no-loops,@typescript-eslint/naming-convention,no-restricted-syntax,no-underscore-dangle\n for (const piece_ of pieces) {\n const piece = piece_ as DurationPiece;\n\n renderedPieces.push(renderPiece(piece, language, options));\n }\n\n let result: string;\n\n if (!conjunction || pieces.length === 1) {\n result = renderedPieces.join(delimiter);\n } else if (pieces.length === 2) {\n result = renderedPieces.join(conjunction);\n } else {\n result = renderedPieces.slice(0, -1).join(delimiter) + (serialComma ? \",\" : \"\") + conjunction + renderedPieces.at(-1);\n }\n\n if (adverb) {\n result = adverb.replace(\"%s\", result);\n }\n\n return result;\n};\n\nconst duration = (milliseconds: number, options?: DurationOptions): string => {\n if (Number.isNaN(milliseconds)) {\n throw new TypeError(\"Expected a valid number\");\n }\n\n if (typeof milliseconds !== \"number\") {\n throw new TypeError(\"Expected a number\");\n }\n\n const config = {\n conjunction: \"\",\n language: durationLanguage,\n round: false,\n serialComma: true,\n spacer: \" \",\n timeAdverb: false,\n unitMeasures: {\n d: 86_400_000,\n h: 3_600_000,\n m: 60_000,\n mo: 2_629_746_000, // 365.2425 / 12 = 30.436875 days\n ms: 1,\n s: 1000,\n w: 604_800_000,\n y: 31_556_952_000, // 365 + 1/4 - 1/100 + 1/400 (actual leap day rules) = 365.2425 days\n },\n units: [\"w\", \"d\", \"h\", \"m\", \"s\"],\n ...options,\n } as InternalOptions;\n\n validateDurationLanguage(config.language);\n\n // Has the nice side effect of converting things to numbers. For example,\n // converts `\"123\"` and `Number(123)` to `123`.\n const absTime = Math.abs(milliseconds as number);\n\n const pieces = getPieces(absTime, config);\n\n return formatPieces(pieces, config, milliseconds as number);\n};\n\nexport default duration;\n"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bytes.ts","../src/language/util/validate-duration-language.ts","../src/duration.ts"],"names":["BYTE_SIZES","parseLocalizedNumber","stringNumber","locale","thousandSeparator","decimalSeparator","fromBase","base","parseBytes","value","options","config","groups","localizedNumber","type","level","unit","formatBytes","bytes","givenBase","decimals","long","requestedUnit","l10nOptions","absoluteBytes","space","requestedUnitIndex","fractionDigits","validateDurationLanguage","language","requiredProperties","property","validate_duration_language_default","toFixed","number_","fixed","matches","renderPiece","unitCount","unitName","maxDecimalPoints","spacer","decimal","digitReplacements","formattedCount","normalizedUnitCount","countString","char","languageWord","word","getPieces","ms","units","unitMeasures","largest","unitCounts","index","msRemaining","unitMs","unitsRemainingBeforeRound","index_","smallerUnitName","smallerUnitCount","rounded","previousUnitName","previousUnitMs","amountOfPreviousUnit","result","remainder","remainderUnitName","formatPieces","pieces","smallestUnitName","conjunction","serialComma","delimiter","adverb","renderedPieces","piece_","piece","duration","milliseconds","durationLanguage","absTime","duration_default"],"mappings":"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","sourcesContent":["import type { FormateByteOptions, ParseByteOptions } from \"./types\";\n\nconst BYTE_SIZES = [\n {\n long: \"Bytes\",\n short: \"Bytes\",\n },\n {\n long: \"Kilobytes\",\n short: \"KB\",\n },\n {\n long: \"Megabytes\",\n short: \"MB\",\n },\n {\n long: \"Gigabytes\",\n short: \"GB\",\n },\n {\n long: \"Terabytes\",\n short: \"TB\",\n },\n {\n long: \"Petabytes\",\n short: \"PB\",\n },\n {\n long: \"Exabytes\",\n short: \"EB\",\n },\n {\n long: \"Zettabytes\",\n short: \"ZB\",\n },\n {\n long: \"Yottabytes\",\n short: \"YB\",\n },\n] as const;\n\ntype ByteSize = (typeof BYTE_SIZES)[number][\"short\"];\ntype LongByteSize = (typeof BYTE_SIZES)[number][\"long\"];\n\ntype Unit = ByteSize | LongByteSize;\n\n/**\n * Parse a localized number to a float.\n * @param stringNumber - the localized number\n * @param locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.\n */\nconst parseLocalizedNumber = (stringNumber: string, locale: string): number => {\n const thousandSeparator = new Intl.NumberFormat(locale).format(11_111).replaceAll(/\\p{Number}/gu, \"\");\n const decimalSeparator = new Intl.NumberFormat(locale).format(1.1).replaceAll(/\\p{Number}/gu, \"\");\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n return Number.parseFloat(stringNumber.replaceAll(new RegExp(`\\\\${thousandSeparator}`, \"g\"), \"\").replace(new RegExp(`\\\\${decimalSeparator}`), \".\"));\n};\n\nconst fromBase = (base: 2 | 10) => {\n if (base === 2) {\n return 1024;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (base === 10) {\n return 1000;\n }\n\n throw new TypeError(`Unsupported base.`);\n};\n\n/**\n * Parse the given bytesize string and return bytes.\n *\n * @param value - The string to parse\n * @param options - Options for the conversion from string to bytes\n * @throws Error if `value` is not a non-empty string or a number\n */\nexport const parseBytes = (value: string, options?: ParseByteOptions): number => {\n const config = {\n base: 2,\n locale: \"en-US\",\n ...options,\n } as Required<ParseByteOptions>;\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(\"Value is not a string or is empty.\");\n }\n\n if (value.length > 100) {\n throw new TypeError(\"Value exceeds the maximum length of 100 characters.\");\n }\n\n const match =\n // eslint-disable-next-line regexp/no-super-linear-backtracking,regexp/no-unused-capturing-group,regexp/no-misleading-capturing-group,security/detect-unsafe-regex\n /^(?<value>-?(?:\\d+(([.,])\\d+)*)?[.,]?\\d+) *(?<type>bytes?|b|kb|kib|mb|mib|gb|gib|tb|tib|pb|pib|eb|eib|zb|zib|yb|yib|(kilo|kibi|mega|mebi|giga|gibi|tera|tebi|peta|pebi|exa|exbi|zetta|zebi|yotta|yobi)?bytes)?$/i.exec(\n value,\n );\n // Named capture groups need to be manually typed today.\n // https://github.com/microsoft/TypeScript/issues/32098\n const groups = match?.groups as { type?: string; value: string } | undefined;\n\n if (!groups) {\n return Number.NaN;\n }\n\n const localizedNumber = parseLocalizedNumber(groups.value, config.locale);\n const type = (groups.type ?? \"Bytes\")\n .toUpperCase()\n .replace(/^KIBI/, \"KILO\")\n .replace(/^MIBI/, \"MEGA\")\n .replace(/^GIBI/, \"GIGA\")\n .replace(/^TEBI/, \"TERA\")\n .replace(/^PEBI/, \"PETA\")\n .replace(/^EXBI/, \"EXA\")\n .replace(/^ZEBI/, \"ZETTA\")\n .replace(/^YIBI/, \"YOTTA\")\n .replace(/^(.)IB$/, \"$1B\") as Uppercase<Unit> | \"B\";\n const level = BYTE_SIZES.findIndex((unit) => (unit.short[0] as string).toUpperCase() === type[0]);\n const base = fromBase(config.base);\n\n return localizedNumber * base ** level;\n};\n\n/**\n * Formats the given bytes into a human-readable string.\n * Per default, it will use the closest unit to the given value.\n *\n * @param bytes - The bytes to format\n * @param options - Options for the conversion from bytes to string\n */\nexport const formatBytes = (bytes: number, options?: FormateByteOptions<ByteSize>): string => {\n if (typeof bytes !== \"number\" || !Number.isFinite(bytes)) {\n throw new TypeError(\"Bytesize is not a number.\");\n }\n\n const {\n base: givenBase,\n decimals,\n locale,\n long,\n unit: requestedUnit,\n ...l10nOptions\n } = {\n base: 2,\n decimals: 0,\n locale: \"en-US\",\n long: false,\n ...options,\n } as FormateByteOptions<ByteSize>;\n const base = fromBase(givenBase as 2 | 10);\n\n const absoluteBytes = Math.abs(bytes);\n const space = options?.space ?? true ? \" \" : \"\";\n\n const requestedUnitIndex = BYTE_SIZES.findIndex((unit) => unit.short === requestedUnit);\n\n if (bytes === 0) {\n const level = Math.min(0, Math.max(requestedUnitIndex, BYTE_SIZES.length - 1));\n\n // eslint-disable-next-line security/detect-object-injection,prefer-template\n return \"0\" + space + (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n }\n\n const level = requestedUnitIndex >= 0 ? requestedUnitIndex : Math.min(Math.floor(Math.log(absoluteBytes) / Math.log(base)), BYTE_SIZES.length - 1);\n // eslint-disable-next-line security/detect-object-injection\n const unit = (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n\n const value = bytes / base ** level;\n const fractionDigits = (decimals as number) < 0 ? undefined : decimals;\n const formattedValue = new Intl.NumberFormat(locale, {\n maximumFractionDigits: fractionDigits,\n minimumFractionDigits: fractionDigits,\n ...l10nOptions,\n }).format(value);\n\n return formattedValue + space + unit;\n};\n","import type { DurationLanguage } from \"../../types\";\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst validateDurationLanguage = (language: DurationLanguage): void => {\n const requiredProperties = [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\", \"future\", \"past\"];\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of requiredProperties) {\n if (!Object.prototype.hasOwnProperty.call(language, property)) {\n throw new TypeError(`Missing required property: ${property}`);\n }\n }\n\n if (typeof language.future !== \"string\" || typeof language.past !== \"string\") {\n throw new TypeError(\"Properties future and past must be of type string\");\n }\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\"]) {\n if (typeof language[property as keyof typeof language] !== \"string\" && typeof language[property as keyof typeof language] !== \"function\") {\n throw new TypeError(`Property ${property} must be of type string or function`);\n }\n }\n\n if (language.decimal && typeof language.decimal !== \"string\") {\n throw new TypeError(\"Property decimal must be of type string\");\n }\n\n if (language.delimiter && typeof language.delimiter !== \"string\") {\n throw new TypeError(\"Property delimiter must be of type string\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._digitReplacements && !Array.isArray(language._digitReplacements)) {\n throw new TypeError(\"Property _digitReplacements must be an array\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst && typeof language._numberFirst !== \"boolean\") {\n throw new TypeError(\"Property _numberFirst must be of type boolean\");\n }\n};\n\nexport default validateDurationLanguage;\n","import { durationLanguage } from \"./language/en\";\nimport validateDurationLanguage from \"./language/util/validate-duration-language\";\nimport type { DurationDigitReplacements, DurationLanguage, DurationOptions, DurationPiece, DurationUnitMeasures, DurationUnitName } from \"./types\";\n\ninterface InternalOptions {\n conjunction: string;\n decimal?: string;\n delimiter?: string;\n digitReplacements?: DurationDigitReplacements;\n fallbacks?: string[];\n language: DurationLanguage;\n largest?: number;\n maxDecimalPoints?: number;\n round: boolean;\n serialComma: boolean;\n spacer: string;\n timeAdverb: boolean;\n unitMeasures: DurationUnitMeasures;\n units: DurationUnitName[];\n}\n\nconst toFixed = (number_: number, fixed: number): number => {\n // eslint-disable-next-line no-param-reassign\n fixed = fixed || -1;\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n const matches = new RegExp(`^-?\\\\d+(?:.\\\\d{0,${fixed}})?`).exec(number_.toString());\n\n if (matches === null) {\n return number_; // can be undefined when num is Number.POSITIVE_INFINITY\n }\n\n return Number.parseFloat(matches[0]);\n};\n\nconst renderPiece = ({ unitCount, unitName }: DurationPiece, language: DurationLanguage, options: InternalOptions): string => {\n const { maxDecimalPoints, spacer } = options;\n\n let decimal = \".\";\n\n if (options.decimal !== undefined) {\n decimal = options.decimal;\n } else if (language.decimal !== undefined) {\n decimal = language.decimal;\n }\n\n let digitReplacements: DurationDigitReplacements | undefined;\n\n if (\"digitReplacements\" in options) {\n digitReplacements = options.digitReplacements;\n } else if (\"_digitReplacements\" in language) {\n // eslint-disable-next-line no-underscore-dangle\n digitReplacements = language._digitReplacements;\n }\n\n let formattedCount: string;\n\n let normalizedUnitCount = unitCount;\n\n if (maxDecimalPoints !== undefined) {\n // normalizedUnitCount = Math.floor(unitCount * 10 ** maxDecimalPoints) / 10 ** maxDecimalPoints;\n normalizedUnitCount = toFixed(unitCount, maxDecimalPoints);\n }\n\n const countString = normalizedUnitCount.toString();\n\n if (digitReplacements) {\n formattedCount = \"\";\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const char of countString) {\n // @ts-expect-error because `char` should always be 0-9 at this point.\n // eslint-disable-next-line security/detect-object-injection\n formattedCount += char === \".\" ? decimal : digitReplacements[char];\n }\n } else {\n formattedCount = countString.replace(\".\", decimal);\n }\n\n // eslint-disable-next-line security/detect-object-injection\n const languageWord = language[unitName];\n let word = languageWord as string;\n\n if (typeof languageWord === \"function\") {\n word = languageWord(unitCount) as string;\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst) {\n return (word as string) + spacer + formattedCount;\n }\n\n return formattedCount + spacer + (word as string);\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst getPieces = (ms: number, options: InternalOptions): DurationPiece[] => {\n const { units } = options;\n\n if (units.length === 0) {\n return [];\n }\n\n const { unitMeasures } = options;\n const largest = options.largest ?? Number.POSITIVE_INFINITY;\n\n // Get the counts for each unit. Doesn't round or truncate anything.\n // For example, might create an object like `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`.\n const unitCounts: Partial<Record<DurationUnitName, number>> = {};\n\n let unitName: DurationUnitName;\n let index: number;\n let unitCount: number;\n let msRemaining: number = ms;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n const unitMs = unitMeasures[unitName];\n const isLast = index === units.length - 1;\n\n unitCount = isLast ? msRemaining / unitMs : Math.floor(msRemaining / unitMs);\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = unitCount;\n\n msRemaining -= unitCount * unitMs;\n }\n\n if (options.round) {\n // Update counts based on the `largest` option.\n // For example, if `largest === 2` and `unitCount` is `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`,\n // updates to something like `{ y: 7, m: 6.2 }`.\n let unitsRemainingBeforeRound = largest;\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // eslint-disable-next-line no-plusplus\n unitsRemainingBeforeRound--;\n\n // \"Take\" the rest of the units into this one.\n if (unitsRemainingBeforeRound === 0) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-loops/no-loops,no-underscore-dangle,no-plusplus\n for (let index_ = index + 1; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const smallerUnitName = units[index_] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const smallerUnitCount = unitCounts[smallerUnitName] as number;\n\n // @ts-expect-error unitCounts[unitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] += (smallerUnitCount * unitMeasures[smallerUnitName]) / unitMeasures[unitName];\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[smallerUnitName] = 0;\n }\n\n break;\n }\n }\n\n // Round the last piece (which should be the only non-integer).\n //\n // This can be a little tricky if the last piece \"bubbles up\" to a larger\n // unit. For example, \"3 days, 23.99 hours\" should be rounded to \"4 days\".\n // It can also require multiple passes. For example, \"6 days, 23.99 hours\"\n // should become \"1 week\".\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = units.length - 1; index >= 0; index--) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const rounded = Math.round(unitCount);\n\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = rounded;\n\n if (index === 0) {\n break;\n }\n\n const previousUnitName: DurationUnitName = units[index - 1] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const previousUnitMs = unitMeasures[previousUnitName];\n // eslint-disable-next-line security/detect-object-injection\n const amountOfPreviousUnit = Math.floor((rounded * unitMeasures[unitName]) / previousUnitMs);\n\n if (amountOfPreviousUnit) {\n // @ts-expect-error unitCounts[previousUnitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[previousUnitName] += amountOfPreviousUnit;\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = 0;\n } else {\n break;\n }\n }\n }\n\n const result: DurationPiece[] = [];\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length && result.length < largest; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n // If the result is not rounded, and `largest` option has been set, aggregate the rest and apply the\n // `maxDecimalPoints` to truncate the decimals\n if (unitCount && !options.round && result.length === largest - 1) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle\n let index_;\n let remainder = 0;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index_ = index + 1, units.length; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const remainderUnitName = units[index_] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n remainder += (unitCounts[remainderUnitName] as number) * (options.unitMeasures[remainderUnitName] / options.unitMeasures[unitName]);\n }\n\n unitCount += remainder;\n\n if (options.maxDecimalPoints !== undefined) {\n unitCount = toFixed(unitCount, options.maxDecimalPoints);\n }\n }\n\n if (unitCount) {\n result.push({ unitCount, unitName });\n }\n }\n\n return result;\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst formatPieces = (pieces: DurationPiece[], options: InternalOptions, ms: number): string => {\n const { language, units } = options;\n\n if (pieces.length === 0) {\n const smallestUnitName = units.at(-1) as DurationUnitName;\n\n return renderPiece({ unitCount: 0, unitName: smallestUnitName }, language, options);\n }\n\n const { conjunction, serialComma } = options;\n\n let delimiter = \", \";\n\n if (options.delimiter !== undefined) {\n delimiter = options.delimiter;\n } else if (language.delimiter !== undefined) {\n delimiter = language.delimiter;\n }\n\n // timeAdverb part\n let adverb = \"\";\n\n if (options.timeAdverb && ms !== 0) {\n adverb = language.future ?? \"\";\n\n if (ms < 0) {\n adverb = language.past ?? \"\";\n }\n }\n\n const renderedPieces: string[] = [];\n\n // eslint-disable-next-line no-loops/no-loops,@typescript-eslint/naming-convention,no-restricted-syntax,no-underscore-dangle\n for (const piece_ of pieces) {\n const piece = piece_ as DurationPiece;\n\n renderedPieces.push(renderPiece(piece, language, options));\n }\n\n let result: string;\n\n if (!conjunction || pieces.length === 1) {\n result = renderedPieces.join(delimiter);\n } else if (pieces.length === 2) {\n result = renderedPieces.join(conjunction);\n } else {\n result = renderedPieces.slice(0, -1).join(delimiter) + (serialComma ? \",\" : \"\") + conjunction + renderedPieces.at(-1);\n }\n\n if (adverb) {\n result = adverb.replace(\"%s\", result);\n }\n\n return result;\n};\n\nconst duration = (milliseconds: number, options?: DurationOptions): string => {\n if (Number.isNaN(milliseconds)) {\n throw new TypeError(\"Expected a valid number\");\n }\n\n if (typeof milliseconds !== \"number\") {\n throw new TypeError(\"Expected a number\");\n }\n\n const config = {\n conjunction: \"\",\n language: durationLanguage,\n round: false,\n serialComma: true,\n spacer: \" \",\n timeAdverb: false,\n unitMeasures: {\n d: 86_400_000,\n h: 3_600_000,\n m: 60_000,\n mo: 2_629_746_000, // 365.2425 / 12 = 30.436875 days\n ms: 1,\n s: 1000,\n w: 604_800_000,\n y: 31_556_952_000, // 365 + 1/4 - 1/100 + 1/400 (actual leap day rules) = 365.2425 days\n },\n units: [\"w\", \"d\", \"h\", \"m\", \"s\"],\n ...options,\n } as InternalOptions;\n\n validateDurationLanguage(config.language);\n\n // Has the nice side-effect of converting things to numbers. For example,\n // converts `\"123\"` and `Number(123)` to `123`.\n const absTime = Math.abs(milliseconds as number);\n\n const pieces = getPieces(absTime, config);\n\n return formatPieces(pieces, config, milliseconds as number);\n};\n\nexport default duration;\n"]}
1
+ {"version":3,"sources":["../src/bytes.ts","../src/language/util/validate-duration-language.ts","../src/duration.ts"],"names":["BYTE_SIZES","parseLocalizedNumber","stringNumber","locale","thousandSeparator","decimalSeparator","fromBase","base","parseBytes","value","options","config","groups","localizedNumber","type","level","unit","formatBytes","bytes","givenBase","decimals","long","requestedUnit","l10nOptions","absoluteBytes","space","requestedUnitIndex","fractionDigits","validateDurationLanguage","language","requiredProperties","property","validate_duration_language_default","toFixed","number_","fixed","matches","renderPiece","unitCount","unitName","maxDecimalPoints","spacer","decimal","digitReplacements","formattedCount","normalizedUnitCount","countString","char","languageWord","word","getPieces","ms","units","unitMeasures","largest","unitCounts","index","msRemaining","unitMs","unitsRemainingBeforeRound","index_","smallerUnitName","smallerUnitCount","rounded","previousUnitName","previousUnitMs","amountOfPreviousUnit","result","remainder","remainderUnitName","formatPieces","pieces","smallestUnitName","conjunction","serialComma","delimiter","adverb","renderedPieces","piece_","piece","duration","milliseconds","durationLanguage","absTime","duration_default"],"mappings":"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","sourcesContent":["import type { FormateByteOptions, ParseByteOptions } from \"./types\";\n\nconst BYTE_SIZES = [\n {\n long: \"Bytes\",\n short: \"Bytes\",\n },\n {\n long: \"Kilobytes\",\n short: \"KB\",\n },\n {\n long: \"Megabytes\",\n short: \"MB\",\n },\n {\n long: \"Gigabytes\",\n short: \"GB\",\n },\n {\n long: \"Terabytes\",\n short: \"TB\",\n },\n {\n long: \"Petabytes\",\n short: \"PB\",\n },\n {\n long: \"Exabytes\",\n short: \"EB\",\n },\n {\n long: \"Zettabytes\",\n short: \"ZB\",\n },\n {\n long: \"Yottabytes\",\n short: \"YB\",\n },\n] as const;\n\ntype ByteSize = (typeof BYTE_SIZES)[number][\"short\"];\ntype LongByteSize = (typeof BYTE_SIZES)[number][\"long\"];\n\ntype Unit = ByteSize | LongByteSize;\n\n/**\n * Parse a localized number to a float.\n * @param stringNumber - the localized number\n * @param locale - [optional] the locale that the number is represented in. Omit this parameter to use the current locale.\n */\nconst parseLocalizedNumber = (stringNumber: string, locale: string): number => {\n const thousandSeparator = new Intl.NumberFormat(locale).format(11_111).replaceAll(/\\p{Number}/gu, \"\");\n const decimalSeparator = new Intl.NumberFormat(locale).format(1.1).replaceAll(/\\p{Number}/gu, \"\");\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n return Number.parseFloat(stringNumber.replaceAll(new RegExp(`\\\\${thousandSeparator}`, \"g\"), \"\").replace(new RegExp(`\\\\${decimalSeparator}`), \".\"));\n};\n\nconst fromBase = (base: 2 | 10) => {\n if (base === 2) {\n return 1024;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (base === 10) {\n return 1000;\n }\n\n throw new TypeError(`Unsupported base.`);\n};\n\n/**\n * Parse the given bytesize string and return bytes.\n *\n * @param value - The string to parse\n * @param options - Options for the conversion from string to bytes\n * @throws Error if `value` is not a non-empty string or a number\n */\nexport const parseBytes = (value: string, options?: ParseByteOptions): number => {\n const config = {\n base: 2,\n locale: \"en-US\",\n ...options,\n } as Required<ParseByteOptions>;\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(\"Value is not a string or is empty.\");\n }\n\n if (value.length > 100) {\n throw new TypeError(\"Value exceeds the maximum length of 100 characters.\");\n }\n\n const match =\n // eslint-disable-next-line regexp/no-super-linear-backtracking,regexp/no-unused-capturing-group,regexp/no-misleading-capturing-group,security/detect-unsafe-regex\n /^(?<value>-?(?:\\d+(([.,])\\d+)*)?[.,]?\\d+) *(?<type>bytes?|b|kb|kib|mb|mib|gb|gib|tb|tib|pb|pib|eb|eib|zb|zib|yb|yib|(kilo|kibi|mega|mebi|giga|gibi|tera|tebi|peta|pebi|exa|exbi|zetta|zebi|yotta|yobi)?bytes)?$/i.exec(\n value,\n );\n // Named capture groups need to be manually typed today.\n // https://github.com/microsoft/TypeScript/issues/32098\n const groups = match?.groups as { type?: string; value: string } | undefined;\n\n if (!groups) {\n return Number.NaN;\n }\n\n const localizedNumber = parseLocalizedNumber(groups.value, config.locale);\n const type = (groups.type ?? \"Bytes\")\n .toUpperCase()\n .replace(/^KIBI/, \"KILO\")\n .replace(/^MIBI/, \"MEGA\")\n .replace(/^GIBI/, \"GIGA\")\n .replace(/^TEBI/, \"TERA\")\n .replace(/^PEBI/, \"PETA\")\n .replace(/^EXBI/, \"EXA\")\n .replace(/^ZEBI/, \"ZETTA\")\n .replace(/^YIBI/, \"YOTTA\")\n .replace(/^(.)IB$/, \"$1B\") as Uppercase<Unit> | \"B\";\n const level = BYTE_SIZES.findIndex((unit) => (unit.short[0] as string).toUpperCase() === type[0]);\n const base = fromBase(config.base);\n\n return localizedNumber * base ** level;\n};\n\n/**\n * Formats the given bytes into a human-readable string.\n * Per default, it will use the closest unit to the given value.\n *\n * @param bytes - The bytes to format\n * @param options - Options for the conversion from bytes to string\n */\nexport const formatBytes = (bytes: number, options?: FormateByteOptions<ByteSize>): string => {\n if (typeof bytes !== \"number\" || !Number.isFinite(bytes)) {\n throw new TypeError(\"Bytesize is not a number.\");\n }\n\n const {\n base: givenBase,\n decimals,\n locale,\n long,\n unit: requestedUnit,\n ...l10nOptions\n } = {\n base: 2,\n decimals: 0,\n locale: \"en-US\",\n long: false,\n ...options,\n } as FormateByteOptions<ByteSize>;\n const base = fromBase(givenBase as 2 | 10);\n\n const absoluteBytes = Math.abs(bytes);\n const space = options?.space ?? true ? \" \" : \"\";\n\n const requestedUnitIndex = BYTE_SIZES.findIndex((unit) => unit.short === requestedUnit);\n\n if (bytes === 0) {\n const level = Math.min(0, Math.max(requestedUnitIndex, BYTE_SIZES.length - 1));\n\n // eslint-disable-next-line security/detect-object-injection,prefer-template\n return \"0\" + space + (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n }\n\n const level = requestedUnitIndex >= 0 ? requestedUnitIndex : Math.min(Math.floor(Math.log(absoluteBytes) / Math.log(base)), BYTE_SIZES.length - 1);\n // eslint-disable-next-line security/detect-object-injection\n const unit = (BYTE_SIZES[level] as { long: string; short: string })[long ? \"long\" : \"short\"];\n\n const value = bytes / base ** level;\n const fractionDigits = (decimals as number) < 0 ? undefined : decimals;\n const formattedValue = new Intl.NumberFormat(locale, {\n maximumFractionDigits: fractionDigits,\n minimumFractionDigits: fractionDigits,\n ...l10nOptions,\n }).format(value);\n\n return formattedValue + space + unit;\n};\n","import type { DurationLanguage } from \"../../types\";\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst validateDurationLanguage = (language: DurationLanguage): void => {\n const requiredProperties = [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\", \"future\", \"past\"];\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of requiredProperties) {\n if (!Object.prototype.hasOwnProperty.call(language, property)) {\n throw new TypeError(`Missing required property: ${property}`);\n }\n }\n\n if (typeof language.future !== \"string\" || typeof language.past !== \"string\") {\n throw new TypeError(\"Properties future and past must be of type string\");\n }\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const property of [\"y\", \"mo\", \"w\", \"d\", \"h\", \"m\", \"s\", \"ms\"]) {\n if (typeof language[property as keyof typeof language] !== \"string\" && typeof language[property as keyof typeof language] !== \"function\") {\n throw new TypeError(`Property ${property} must be of type string or function`);\n }\n }\n\n if (language.decimal && typeof language.decimal !== \"string\") {\n throw new TypeError(\"Property decimal must be of type string\");\n }\n\n if (language.delimiter && typeof language.delimiter !== \"string\") {\n throw new TypeError(\"Property delimiter must be of type string\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._digitReplacements && !Array.isArray(language._digitReplacements)) {\n throw new TypeError(\"Property _digitReplacements must be an array\");\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst && typeof language._numberFirst !== \"boolean\") {\n throw new TypeError(\"Property _numberFirst must be of type boolean\");\n }\n};\n\nexport default validateDurationLanguage;\n","import { durationLanguage } from \"./language/en\";\nimport validateDurationLanguage from \"./language/util/validate-duration-language\";\nimport type { DurationDigitReplacements, DurationLanguage, DurationOptions, DurationPiece, DurationUnitMeasures, DurationUnitName } from \"./types\";\n\ninterface InternalOptions {\n conjunction: string;\n decimal?: string;\n delimiter?: string;\n digitReplacements?: DurationDigitReplacements;\n fallbacks?: string[];\n language: DurationLanguage;\n largest?: number;\n maxDecimalPoints?: number;\n round: boolean;\n serialComma: boolean;\n spacer: string;\n timeAdverb: boolean;\n unitMeasures: DurationUnitMeasures;\n units: DurationUnitName[];\n}\n\nconst toFixed = (number_: number, fixed: number): number => {\n // eslint-disable-next-line no-param-reassign\n fixed = fixed || -1;\n\n // eslint-disable-next-line @rushstack/security/no-unsafe-regexp,security/detect-non-literal-regexp\n const matches = new RegExp(`^-?\\\\d+(?:.\\\\d{0,${fixed}})?`).exec(number_.toString());\n\n if (matches === null) {\n return number_; // can be undefined when num is Number.POSITIVE_INFINITY\n }\n\n return Number.parseFloat(matches[0]);\n};\n\nconst renderPiece = ({ unitCount, unitName }: DurationPiece, language: DurationLanguage, options: InternalOptions): string => {\n const { maxDecimalPoints, spacer } = options;\n\n let decimal = \".\";\n\n if (options.decimal !== undefined) {\n decimal = options.decimal;\n } else if (language.decimal !== undefined) {\n decimal = language.decimal;\n }\n\n let digitReplacements: DurationDigitReplacements | undefined;\n\n if (\"digitReplacements\" in options) {\n digitReplacements = options.digitReplacements;\n } else if (\"_digitReplacements\" in language) {\n // eslint-disable-next-line no-underscore-dangle\n digitReplacements = language._digitReplacements;\n }\n\n let formattedCount: string;\n\n let normalizedUnitCount = unitCount;\n\n if (maxDecimalPoints !== undefined) {\n // normalizedUnitCount = Math.floor(unitCount * 10 ** maxDecimalPoints) / 10 ** maxDecimalPoints;\n normalizedUnitCount = toFixed(unitCount, maxDecimalPoints);\n }\n\n const countString = normalizedUnitCount.toString();\n\n if (digitReplacements) {\n formattedCount = \"\";\n\n // eslint-disable-next-line no-loops/no-loops,no-restricted-syntax\n for (const char of countString) {\n // @ts-expect-error because `char` should always be 0-9 at this point.\n // eslint-disable-next-line security/detect-object-injection\n formattedCount += char === \".\" ? decimal : digitReplacements[char];\n }\n } else {\n formattedCount = countString.replace(\".\", decimal);\n }\n\n // eslint-disable-next-line security/detect-object-injection\n const languageWord = language[unitName];\n let word = languageWord as string;\n\n if (typeof languageWord === \"function\") {\n word = languageWord(unitCount) as string;\n }\n\n // eslint-disable-next-line no-underscore-dangle\n if (language._numberFirst) {\n return (word as string) + spacer + formattedCount;\n }\n\n return formattedCount + spacer + (word as string);\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst getPieces = (ms: number, options: InternalOptions): DurationPiece[] => {\n const { units } = options;\n\n if (units.length === 0) {\n return [];\n }\n\n const { unitMeasures } = options;\n const largest = options.largest ?? Number.POSITIVE_INFINITY;\n\n // Get the counts for each unit. Doesn't round or truncate anything.\n // For example, might create an object like `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`.\n const unitCounts: Partial<Record<DurationUnitName, number>> = {};\n\n let unitName: DurationUnitName;\n let index: number;\n let unitCount: number;\n let msRemaining: number = ms;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n const unitMs = unitMeasures[unitName];\n const isLast = index === units.length - 1;\n\n unitCount = isLast ? msRemaining / unitMs : Math.floor(msRemaining / unitMs);\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = unitCount;\n\n msRemaining -= unitCount * unitMs;\n }\n\n if (options.round) {\n // Update counts based on the `largest` option.\n // For example, if `largest === 2` and `unitCount` is `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`,\n // updates to something like `{ y: 7, m: 6.2 }`.\n let unitsRemainingBeforeRound = largest;\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // eslint-disable-next-line no-plusplus\n unitsRemainingBeforeRound--;\n\n // \"Take\" the rest of the units into this one.\n if (unitsRemainingBeforeRound === 0) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-loops/no-loops,no-underscore-dangle,no-plusplus\n for (let index_ = index + 1; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const smallerUnitName = units[index_] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const smallerUnitCount = unitCounts[smallerUnitName] as number;\n\n // @ts-expect-error unitCounts[unitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] += (smallerUnitCount * unitMeasures[smallerUnitName]) / unitMeasures[unitName];\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[smallerUnitName] = 0;\n }\n\n break;\n }\n }\n\n // Round the last piece (which should be the only non-integer).\n //\n // This can be a little tricky if the last piece \"bubbles up\" to a larger\n // unit. For example, \"3 days, 23.99 hours\" should be rounded to \"4 days\".\n // It can also require multiple passes. For example, \"6 days, 23.99 hours\"\n // should become \"1 week\".\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = units.length - 1; index >= 0; index--) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n if (unitCount === 0) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const rounded = Math.round(unitCount);\n\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = rounded;\n\n if (index === 0) {\n break;\n }\n\n const previousUnitName: DurationUnitName = units[index - 1] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n const previousUnitMs = unitMeasures[previousUnitName];\n // eslint-disable-next-line security/detect-object-injection\n const amountOfPreviousUnit = Math.floor((rounded * unitMeasures[unitName]) / previousUnitMs);\n\n if (amountOfPreviousUnit) {\n // @ts-expect-error unitCounts[previousUnitName] is defined\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[previousUnitName] += amountOfPreviousUnit;\n // eslint-disable-next-line security/detect-object-injection\n unitCounts[unitName] = 0;\n } else {\n break;\n }\n }\n }\n\n const result: DurationPiece[] = [];\n\n // eslint-disable-next-line no-plusplus,no-loops/no-loops\n for (index = 0; index < units.length && result.length < largest; index++) {\n // eslint-disable-next-line security/detect-object-injection\n unitName = units[index] as DurationUnitName;\n // eslint-disable-next-line security/detect-object-injection\n unitCount = unitCounts[unitName] as number;\n\n // If the result is not rounded, and `largest` option has been set, aggregate the rest and apply the\n // `maxDecimalPoints` to truncate the decimals\n if (unitCount && !options.round && result.length === largest - 1) {\n // eslint-disable-next-line @typescript-eslint/naming-convention,no-underscore-dangle\n let index_;\n let remainder = 0;\n\n // eslint-disable-next-line no-loops/no-loops,no-plusplus\n for (index_ = index + 1, units.length; index_ < units.length; index_++) {\n // eslint-disable-next-line no-underscore-dangle,security/detect-object-injection\n const remainderUnitName = units[index_] as DurationUnitName;\n\n // eslint-disable-next-line security/detect-object-injection\n remainder += (unitCounts[remainderUnitName] as number) * (options.unitMeasures[remainderUnitName] / options.unitMeasures[unitName]);\n }\n\n unitCount += remainder;\n\n if (options.maxDecimalPoints !== undefined) {\n unitCount = toFixed(unitCount, options.maxDecimalPoints);\n }\n }\n\n if (unitCount) {\n result.push({ unitCount, unitName });\n }\n }\n\n return result;\n};\n\n// eslint-disable-next-line sonarjs/cognitive-complexity\nconst formatPieces = (pieces: DurationPiece[], options: InternalOptions, ms: number): string => {\n const { language, units } = options;\n\n if (pieces.length === 0) {\n const smallestUnitName = units.at(-1) as DurationUnitName;\n\n return renderPiece({ unitCount: 0, unitName: smallestUnitName }, language, options);\n }\n\n const { conjunction, serialComma } = options;\n\n let delimiter = \", \";\n\n if (options.delimiter !== undefined) {\n delimiter = options.delimiter;\n } else if (language.delimiter !== undefined) {\n delimiter = language.delimiter;\n }\n\n // timeAdverb part\n let adverb = \"\";\n\n if (options.timeAdverb && ms !== 0) {\n adverb = language.future ?? \"\";\n\n if (ms < 0) {\n adverb = language.past ?? \"\";\n }\n }\n\n const renderedPieces: string[] = [];\n\n // eslint-disable-next-line no-loops/no-loops,@typescript-eslint/naming-convention,no-restricted-syntax,no-underscore-dangle\n for (const piece_ of pieces) {\n const piece = piece_ as DurationPiece;\n\n renderedPieces.push(renderPiece(piece, language, options));\n }\n\n let result: string;\n\n if (!conjunction || pieces.length === 1) {\n result = renderedPieces.join(delimiter);\n } else if (pieces.length === 2) {\n result = renderedPieces.join(conjunction);\n } else {\n result = renderedPieces.slice(0, -1).join(delimiter) + (serialComma ? \",\" : \"\") + conjunction + renderedPieces.at(-1);\n }\n\n if (adverb) {\n result = adverb.replace(\"%s\", result);\n }\n\n return result;\n};\n\nconst duration = (milliseconds: number, options?: DurationOptions): string => {\n if (Number.isNaN(milliseconds)) {\n throw new TypeError(\"Expected a valid number\");\n }\n\n if (typeof milliseconds !== \"number\") {\n throw new TypeError(\"Expected a number\");\n }\n\n const config = {\n conjunction: \"\",\n language: durationLanguage,\n round: false,\n serialComma: true,\n spacer: \" \",\n timeAdverb: false,\n unitMeasures: {\n d: 86_400_000,\n h: 3_600_000,\n m: 60_000,\n mo: 2_629_746_000, // 365.2425 / 12 = 30.436875 days\n ms: 1,\n s: 1000,\n w: 604_800_000,\n y: 31_556_952_000, // 365 + 1/4 - 1/100 + 1/400 (actual leap day rules) = 365.2425 days\n },\n units: [\"w\", \"d\", \"h\", \"m\", \"s\"],\n ...options,\n } as InternalOptions;\n\n validateDurationLanguage(config.language);\n\n // Has the nice side effect of converting things to numbers. For example,\n // converts `\"123\"` and `Number(123)` to `123`.\n const absTime = Math.abs(milliseconds as number);\n\n const pieces = getPieces(absTime, config);\n\n return formatPieces(pieces, config, milliseconds as number);\n};\n\nexport default duration;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visulima/humanizer",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Humanizer is a library for humanizing data in a human-readable form.",
5
5
  "keywords": [
6
6
  "visulima",