generate.ts

2.1 KB · raw

1import type { PasswordPolicy } from "./types";23export interface GeneratorOptions {4  length: number;5  includeUppercase: boolean;6  includeLowercase: boolean;7  includeNumbers: boolean;8  includeSymbols: boolean;9  /** Characters to exclude from every pool — honors site forbidden-char lists. */10  forbiddenChars?: string;11}1213/**14 * Generate a cryptographically random password.15 */16export function generatePassword(options: Partial<GeneratorOptions> = {}): string {17  const {18    length = 20,19    includeUppercase = true,20    includeLowercase = true,21    includeNumbers = true,22    includeSymbols = true,23    forbiddenChars = "",24  } = options;2526  const forbidden = new Set(forbiddenChars);27  const filter = (pool: string): string =>28    forbidden.size === 029      ? pool30      : Array.from(pool).filter((c) => !forbidden.has(c)).join("");3132  let chars = "";33  if (includeLowercase) chars += filter("abcdefghijklmnopqrstuvwxyz");34  if (includeUppercase) chars += filter("ABCDEFGHIJKLMNOPQRSTUVWXYZ");35  if (includeNumbers) chars += filter("0123456789");36  if (includeSymbols) chars += filter("!@#$%^&*");3738  if (chars.length === 0) chars = filter("abcdefghijklmnopqrstuvwxyz") || "abcdefghijklmnopqrstuvwxyz";3940  const array = crypto.getRandomValues(new Uint32Array(length));41  let password = "";42  for (let i = 0; i < length; i++) {43    password += chars[array[i]! % chars.length];44  }45  return password;46}4748/**49 * Generate a password that satisfies a detected site policy — clamps length50 * to maxLength, raises to minLength, honors forbidsSymbols, and excludes51 * forbiddenChars. Used by the inline popup when the user clicks "regen" on a52 * signup form with parseable requirements.53 */54export function generateSmartPassword(policy: PasswordPolicy = {}): string {55  const defaultLength = 20;56  let length = defaultLength;57  if (policy.minLength) length = Math.max(length, policy.minLength);58  if (policy.maxLength) length = Math.min(length, policy.maxLength);59  if (length < 8) length = 8;6061  return generatePassword({62    length,63    includeSymbols: !policy.forbidsSymbols,64    forbiddenChars: policy.forbiddenChars,65  });66}