All files / src/utils/common generate-random-string.ts

82.35% Statements 14/17
77.77% Branches 7/9
66.66% Functions 2/3
81.25% Lines 13/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63                                                                  35x 179x 6x     173x 173x 173x 173x 1688x                 35x   35x 35x         35x   35x  
import type { Writable } from 'type-fest';
 
type GenerateRandomString = ((length?: number) => string) & {
  /**
   * The crypto global, if available.
   */
  // eslint-disable-next-line no-undef
  readonly crypto?: typeof crypto;
};
 
/**
 * Generates a random string of the specified length using alphanumeric characters (i.e., "A-Z", "a-z" and "0-9").
 *
 * @example
 *
 * ```ts
 * import { generateRandomString } from 'emitnlog/utils';
 *
 * // Generate a random string with default length (8)
 * const id = generateRandomString();
 *
 * // Generate a random string with custom length
 * const longerId = generateRandomString(64);
 * ```
 *
 * @param length - Defaults to 8. Must be a number between 8 and 128.
 * @returns A random string of the specified length.
 * @throws An error if length is not in the range between 8 and 128 (inclusive).
 * @note NOT SUITABLE FOR CRYPTOGRAPHIC OR SECURITY-CRITICAL PURPOSES.
 * For security-sensitive applications, use a cryptographically secure random generator instead -
 * `generateRandomString.crypto` is defined if your environment supports the `crypto` global. Even when `crypto` is
 * available, the output is not uniformly random due to character mapping; do not use for security.
 */
export const generateRandomString: GenerateRandomString = (length = 8) => {
  if (length < 8 || length > 128) {
    throw new Error('IllegalArgument: length must be a number between 8 and 128');
  }
 
  const localCrypto = generateRandomString.crypto;
  Eif (localCrypto) {
    const bytes = new Uint8Array(length);
    localCrypto.getRandomValues(bytes);
    return Array.from(bytes, (byte) => UNIQUE_CHARACTERS[byte % UNIQUE_CHARACTERS.length]).join('');
  }
 
  return Array.from({ length }, () => {
    const randomIndex = Math.floor(Math.random() * UNIQUE_CHARACTERS.length);
    return UNIQUE_CHARACTERS.charAt(randomIndex);
  }).join('');
};
 
try {
  // eslint-disable-next-line no-undef
  const resolvedCrypto = typeof crypto === 'undefined' ? undefined : crypto;
  (generateRandomString as unknown as Writable<GenerateRandomString>).crypto = resolvedCrypto;
} catch {
  // ignore
}
 
Object.freeze(generateRandomString);
 
const UNIQUE_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';