UNPKG

cuid-range

Version:

Generate and validate CUID between two bounds. Useful for order-preserving pagination keys, gap insertion, or sequence IDs.

72 lines (54 loc) 2.01 kB
const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz' const MIN = 0 const MAX = ALPHABET.length - 1 function assertAllInAlphabet(s: string) { for (const ch of s) { if (ALPHABET.indexOf(ch) === -1) throw new Error('Invalid CUID') } } function validateRange(start: string, end: string) { if (start.length === 0) throw new Error('Invalid CUID range') if (start.length > end.length) throw new Error('Invalid CUID range') assertAllInAlphabet(start) assertAllInAlphabet(end) if (start > end) throw new Error('Invalid CUID range') } export function generateCuidBetween(start: string, end: string): string { if (start === end) { assertAllInAlphabet(start) return start } validateRange(start, end) const length = Math.max(start.length, end.length) let res = '' let prefixStart = true let prefixEnd = true for (let i = 0; i < length; i++) { const sCh = prefixStart ? (start[i] ?? ALPHABET[MIN]) : ALPHABET[MIN] const eCh = prefixEnd ? (end[i] ?? ALPHABET[MAX]) : ALPHABET[MAX] const sIdx = ALPHABET.indexOf(sCh) const eIdx = ALPHABET.indexOf(eCh) if (sIdx === -1 || eIdx === -1 || sIdx > eIdx) { throw new Error('Invalid CUID range') } const range = ALPHABET.substring(sIdx, eIdx + 1) const pick = range[Math.floor(Math.random() * range.length)] res += pick if (prefixStart) { prefixStart = (pick === (start[i] ?? ALPHABET[MIN])) } if (prefixEnd) { prefixEnd = (pick === (end[i] ?? ALPHABET[MAX])) } } return res } export function isCuidBetween(cuid: string, start: string, end: string): boolean { assertAllInAlphabet(cuid) if (start === end) { assertAllInAlphabet(start) return cuid === start } validateRange(start, end) return start <= cuid && cuid <= end }