1import type { PasswordPolicy } from "./types";23export interface GeneratorOptions {4 length: number;5 includeUppercase: boolean;6 includeLowercase: boolean;7 includeNumbers: boolean;8 includeSymbols: boolean;9 10 forbiddenChars?: string;11}1213141516export 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 41 42 43 44 const limit = Math.floor(0x1_0000_0000 / chars.length) * chars.length;45 let password = "";46 while (password.length < length) {47 const batch = crypto.getRandomValues(new Uint32Array(length - password.length));48 for (let i = 0; i < batch.length && password.length < length; i++) {49 const v = batch[i]!;50 if (v >= limit) continue; 51 password += chars[v % chars.length];52 }53 }54 return password;55}5657585960616263export function generateSmartPassword(policy: PasswordPolicy = {}): string {64 const defaultLength = 20;65 let length = defaultLength;66 if (policy.minLength) length = Math.max(length, policy.minLength);67 if (policy.maxLength) length = Math.min(length, policy.maxLength);68 if (length < 8) length = 8;6970 return generatePassword({71 length,72 includeSymbols: !policy.forbidsSymbols,73 forbiddenChars: policy.forbiddenChars,74 });75}