import { getRandomIndex } from "./base.ts" // Step 1: 统一处理长度参数。 // // 把这一步单独放出来之后,主流程里就不需要反复出现相同的边界判断。 const internalAssertRandomStringLength = (length: number): void => { if (Number.isInteger(length) === false || length < 0) { throw new RangeError(`Expected length to be a non-negative integer, got: ${length}`) } } // Step 2: 规范化字符集。 // // 这里返回字符数组而不是原始字符串,后面就可以直接按索引取字符。 // 同时也顺手把空字符集的情况拦截掉。 const internalPrepareRandomStringAlphabet = (chars: string): string[] => { const alphabet = Array.from(chars) if (alphabet.length === 0) { throw new RangeError("Expected chars to contain at least one character") } return alphabet } // Step 3: 逐个位置生成随机字符并拼接结果。 // // 这里的流程很直接:循环 length 次,每次取一个随机索引,再取出对应字符。 const internalRandomStringFromAlphabet = (length: number, alphabet: string[]): string => { let result = "" for (let index = 0; index < length; index = index + 1) { const alphabetIndex = getRandomIndex(alphabet.length) result = result + alphabet[alphabetIndex]! } return result } /** * @description 生成多环境可用的随机字符串,并可选限制字符集。 * * 当运行时提供 `crypto.getRandomValues` 时,会使用统一的随机源逻辑生成字符索引;否则回退到 `Math.random`,以保持在常见浏览器、Node.js 与 Bun * 环境中的可用性。 * * @example * ``` * // Expect: 12 * const example1 = randomString(12).length * // Expect: true * const example2 = randomString(6, "ABC").split("").every(char => char === "A" || char === "B" || char === "C") * ``` */ export const randomString = (length: number, chars?: string | undefined): string => { internalAssertRandomStringLength(length) if (length === 0) { return "" } // 默认字符集包含数字、小写字母与大写字母。 const defaultRandomStringChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const alphabet = internalPrepareRandomStringAlphabet(chars ?? defaultRandomStringChars) const string = internalRandomStringFromAlphabet(length, alphabet) return string } /** * @description 生成多环境可用的随机字母字符串,并可选限制字符集。 * * 当运行时提供 `crypto.getRandomValues` 时,会使用统一的随机源逻辑生成字符索引;否则回退到 `Math.random`,以保持在常见浏览器、Node.js 与 Bun * 环境中的可用性。 * * @example * ``` * // Expect: 12 * const example1 = randomAlpha(12).length * // Expect: true * const example2 = randomAlpha(6, "ABCabc").split("").every(char => char === "A" || char === "B" || char === "C" || char === "a" || char === "b" || char === "c") * ``` */ export const randomAlpha = (length: number, chars?: string | undefined): string => { const defaultAlphaChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const string = randomString(length, chars ?? defaultAlphaChars) return string } /** * @description 生成多环境可用的随机数字字符串,并可选限制字符集。 * * 当运行时提供 `crypto.getRandomValues` 时,会使用统一的随机源逻辑生成字符索引;否则回退到 `Math.random`,以保持在常见浏览器、Node.js 与 Bun * 环境中的可用性。 * * @example * ``` * // Expect: 12 * const example1 = randomNumeric(12).length * // Expect: true * const example2 = randomNumeric(6, "123").split("").every(char => char === "1" || char === "2" || char === "3") * ``` */ export const randomNumeric = (length: number, chars?: string | undefined): string => { const defaultNumericChars = "0123456789" const string = randomString(length, chars ?? defaultNumericChars) return string }