misc.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /**
  2. * @module lib/misc
  3. * This module contains miscellaneous functions that don't fit in another category - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#misc)
  4. */
  5. import type { Prettify, Stringifiable } from "./types.js";
  6. /**
  7. * Automatically appends an `s` to the passed {@linkcode word}, if {@linkcode num} is not equal to 1
  8. * @param word A word in singular form, to auto-convert to plural
  9. * @param num If this is an array or NodeList, the amount of items is used
  10. */
  11. export function autoPlural(word: Stringifiable, num: number | unknown[] | NodeList): string {
  12. if(Array.isArray(num) || num instanceof NodeList)
  13. num = num.length;
  14. return `${word}${num === 1 ? "" : "s"}`;
  15. }
  16. /**
  17. * Inserts the passed values into a string at the respective placeholders.
  18. * The placeholder format is `%n`, where `n` is the 1-indexed argument number.
  19. * @param input The string to insert the values into
  20. * @param values The values to insert, in order, starting at `%1`
  21. */
  22. export function insertValues(input: string, ...values: Stringifiable[]): string {
  23. return input.replace(/%\d/gm, (match) => {
  24. const argIndex = Number(match.substring(1)) - 1;
  25. return (values[argIndex] ?? match)?.toString();
  26. });
  27. }
  28. /** Pauses async execution for the specified time in ms */
  29. export function pauseFor(time: number): Promise<void> {
  30. return new Promise<void>((res) => {
  31. setTimeout(() => res(), time);
  32. });
  33. }
  34. /** Options for the `fetchAdvanced()` function */
  35. export type FetchAdvancedOpts = Prettify<
  36. Partial<{
  37. /** Timeout in milliseconds after which the fetch call will be canceled with an AbortController signal */
  38. timeout: number;
  39. }> & RequestInit
  40. >;
  41. /** Calls the fetch API with special options like a timeout */
  42. export async function fetchAdvanced(input: string | RequestInfo | URL, options: FetchAdvancedOpts = {}): Promise<Response> {
  43. const { timeout = 10000 } = options;
  44. const { signal, abort } = new AbortController();
  45. options.signal?.addEventListener("abort", abort);
  46. let signalOpts: Partial<RequestInit> = {},
  47. id: ReturnType<typeof setTimeout> | undefined = undefined;
  48. if(timeout >= 0) {
  49. id = setTimeout(() => abort(), timeout);
  50. signalOpts = { signal };
  51. }
  52. try {
  53. const res = await fetch(input, {
  54. ...options,
  55. ...signalOpts,
  56. });
  57. id && clearTimeout(id);
  58. return res;
  59. }
  60. catch(err) {
  61. id && clearTimeout(id);
  62. throw err;
  63. }
  64. }
  65. /**
  66. * A ValueGen value is either its type, a promise that resolves to its type, or a function that returns its type, either synchronous or asynchronous.
  67. * ValueGen allows for the utmost flexibility when applied to any type, as long as {@linkcode consumeGen()} is used to get the final value.
  68. * @template TValueType The type of the value that the ValueGen should yield
  69. */
  70. export type ValueGen<TValueType> = TValueType | Promise<TValueType> | (() => TValueType | Promise<TValueType>);
  71. /**
  72. * Turns a {@linkcode ValueGen} into its final value.
  73. * @template TValueType The type of the value that the ValueGen should yield
  74. */
  75. export async function consumeGen<TValueType>(valGen: ValueGen<TValueType>): Promise<TValueType> {
  76. return await (typeof valGen === "function"
  77. ? (valGen as (() => Promise<TValueType> | TValueType))()
  78. : valGen
  79. )as TValueType;
  80. }
  81. /**
  82. * A StringGen value is either a string, anything that can be converted to a string, or a function that returns one of the previous two, either synchronous or asynchronous, or a promise that returns a string.
  83. * StringGen allows for the utmost flexibility when dealing with strings, as long as {@linkcode consumeStringGen()} is used to get the final string.
  84. */
  85. export type StringGen = ValueGen<Stringifiable>;
  86. /**
  87. * Turns a {@linkcode StringGen} into its final string value.
  88. * @template TStrUnion The union of strings that the StringGen should yield - this allows for finer type control compared to {@linkcode consumeGen()}
  89. */
  90. export async function consumeStringGen<TStrUnion extends string>(strGen: StringGen): Promise<TStrUnion> {
  91. return (
  92. typeof strGen === "string"
  93. ? strGen
  94. : String(
  95. typeof strGen === "function"
  96. ? await strGen()
  97. : strGen
  98. )
  99. ) as TStrUnion;
  100. }