import crypto from 'node:crypto' /** Letras y dígitos: el alfabeto por defecto del `get_random_string` de Django. */ export const ALPHANUMERIC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' /** Solo letras, para el token de seguridad (formato `uRlPBR`). */ export const LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' /** * Código aleatorio de `length` caracteres tomados de `alphabet`. * * Estos códigos se portaron del Django original como `randomBytes(n).toString('hex')`, * que cambiaba el formato sin querer: hex solo usa `0-9a-f`, mientras que el * `get_random_string` original daba alfanumérico con mayúsculas y minúsculas. * * Muestreo por rechazo: 256 no suele ser múltiplo del tamaño del alfabeto, así que * `byte % alphabet.length` haría más probables sus primeros caracteres. Descartando * los bytes desde el múltiplo más alto que cabe en uno, el reparto queda uniforme. */ export function randomToken(length: number, alphabet: string = ALPHANUMERIC): string { const limit = 256 - (256 % alphabet.length) let token = '' while (token.length < length) { for (const byte of crypto.randomBytes(length)) { if (byte >= limit) continue token += alphabet[byte % alphabet.length] if (token.length === length) break } } return token }