UNPKG

memorable-ids

Version:

A flexible library for generating human-readable, memorable identifiers

142 lines (139 loc) 4.25 kB
import { dictionaryStats, adjectives, nouns, verbs, adverbs, prepositions } from './dictionary.mjs'; export { dictionary } from './dictionary.mjs'; /** * Memorable ID Generator * * A flexible library for generating human-readable, memorable identifiers. * Uses combinations of adjectives, nouns, verbs, adverbs, and prepositions * with optional numeric/custom suffixes. * * @author Aris Ripandi * @license MIT */ function generate(options = {}) { const { components = 2, suffix = null, separator = "-" } = options; if (components < 1 || components > 5) { throw new Error("Components must be between 1 and 5"); } function random(max) { return Math.floor(Math.random() * max); } function randomItem(array) { return array[random(array.length)]; } const parts = []; const componentGenerators = [ () => randomItem(adjectives), // 0: adjective () => randomItem(nouns), // 1: noun () => randomItem(verbs), // 2: verb () => randomItem(adverbs), // 3: adverb () => randomItem(prepositions) // 4: preposition ]; for (let i = 0; i < components; i++) { parts.push(componentGenerators[i]()); } if (suffix && typeof suffix === "function") { const suffixValue = suffix(); if (suffixValue !== null && suffixValue !== void 0) { parts.push(suffixValue); } } return parts.join(separator); } function defaultSuffix() { return Math.floor(Math.random() * 1e3).toString().padStart(3, "0"); } function parse(id, separator = "-") { const parts = id.split(separator); const result = { components: [], suffix: null }; const lastPart = parts[parts.length - 1]; if (/^\d+$/.test(lastPart)) { result.suffix = lastPart; result.components = parts.slice(0, -1); } else { result.components = parts; } return result; } function calculateCombinations(components = 2, suffixRange = 1) { const componentSizes = [ dictionaryStats.adjectives, // 78 adjectives dictionaryStats.nouns, // 68 nouns dictionaryStats.verbs, // 40 verbs dictionaryStats.adverbs, // 27 adverbs dictionaryStats.prepositions // 26 prepositions ]; let total = 1; for (let i = 0; i < components; i++) { total *= componentSizes[i]; } return total * suffixRange; } function calculateCollisionProbability(totalCombinations, generatedIds) { if (generatedIds >= totalCombinations) return 1; if (generatedIds <= 1) return 0; const exponent = -(generatedIds * generatedIds) / (2 * totalCombinations); return 1 - Math.exp(exponent); } function getCollisionAnalysis(components = 2, suffixRange = 1) { const total = calculateCombinations(components, suffixRange); const testSizes = [50, 100, 200, 500, 1e3, 2e3, 5e3, 1e4, 2e4, 5e4]; return { totalCombinations: total, scenarios: testSizes.filter((size) => size < total * 0.8).map((size) => ({ ids: size, probability: calculateCollisionProbability(total, size), percentage: `${(calculateCollisionProbability(total, size) * 100).toFixed(2)}%` })) }; } const suffixGenerators = { /** * Random 3-digit number (000-999) * Adds 1,000x multiplier to total combinations */ number: defaultSuffix, /** * Random 4-digit number (0000-9999) * Adds 10,000x multiplier to total combinations */ number4: () => Math.floor(Math.random() * 1e4).toString().padStart(4, "0"), /** * Random 2-digit hex (00-ff) * Adds 256x multiplier to total combinations */ hex: () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0"), /** * Last 4 digits of current timestamp * Adds ~10,000x multiplier (time-based, not truly random) */ timestamp: () => Date.now().toString().slice(-4), /** * Random lowercase letter (a-z) * Adds 26x multiplier to total combinations */ letter: () => String.fromCharCode(97 + Math.floor(Math.random() * 26)) }; const memorableId = { generate, parse, calculateCombinations, calculateCollisionProbability, getCollisionAnalysis, suffixGenerators, defaultSuffix }; export { calculateCollisionProbability, calculateCombinations, memorableId as default, defaultSuffix, dictionaryStats, generate, getCollisionAnalysis, parse, suffixGenerators };