1export interface GeneratorOptions {2 length: number;3 includeUppercase: boolean;4 includeLowercase: boolean;5 includeNumbers: boolean;6 includeSymbols: boolean;7 8 forbiddenChars?: string;9}1011121314export function generatePassword(options: Partial<GeneratorOptions> = {}): string {15 const {16 length = 20,17 includeUppercase = true,18 includeLowercase = true,19 includeNumbers = true,20 includeSymbols = true,21 forbiddenChars = "",22 } = options;2324 const forbidden = new Set(forbiddenChars);25 const filter = (pool: string): string =>26 forbidden.size === 027 ? pool28 : Array.from(pool).filter((c) => !forbidden.has(c)).join("");2930 let chars = "";31 if (includeLowercase) chars += filter("abcdefghijklmnopqrstuvwxyz");32 if (includeUppercase) chars += filter("ABCDEFGHIJKLMNOPQRSTUVWXYZ");33 if (includeNumbers) chars += filter("0123456789");34 if (includeSymbols) chars += filter("!@#$%^&*");3536 if (chars.length === 0) chars = filter("abcdefghijklmnopqrstuvwxyz") || "abcdefghijklmnopqrstuvwxyz";3738 39 40 41 42 const limit = Math.floor(0x1_0000_0000 / chars.length) * chars.length;43 let password = "";44 while (password.length < length) {45 const batch = crypto.getRandomValues(new Uint32Array(length - password.length));46 for (let i = 0; i < batch.length && password.length < length; i++) {47 const v = batch[i]!;48 if (v >= limit) continue; 49 password += chars[v % chars.length];50 }51 }52 return password;53}