index.js 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import crypto from 'crypto'
  2. import { urlAlphabet } from './url-alphabet/index.js'
  3. const POOL_SIZE_MULTIPLIER = 128
  4. let pool, poolOffset
  5. let fillPool = bytes => {
  6. if (bytes < 0) throw new RangeError('Wrong ID size')
  7. try {
  8. if (!pool || pool.length < bytes) {
  9. pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER)
  10. crypto.randomFillSync(pool)
  11. poolOffset = 0
  12. } else if (poolOffset + bytes > pool.length) {
  13. crypto.randomFillSync(pool)
  14. poolOffset = 0
  15. }
  16. } catch (e) {
  17. pool = undefined
  18. throw e
  19. }
  20. poolOffset += bytes
  21. }
  22. let random = bytes => {
  23. fillPool((bytes |= 0))
  24. return pool.subarray(poolOffset - bytes, poolOffset)
  25. }
  26. let customRandom = (alphabet, defaultSize, getRandom) => {
  27. let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1
  28. let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length)
  29. return (size = defaultSize) => {
  30. if (size <= 0) return ''
  31. let id = ''
  32. while (true) {
  33. let bytes = getRandom(step)
  34. let i = step
  35. while (i--) {
  36. id += alphabet[bytes[i] & mask] || ''
  37. if (id.length === size) return id
  38. }
  39. }
  40. }
  41. }
  42. let customAlphabet = (alphabet, size = 21) =>
  43. customRandom(alphabet, size, random)
  44. let nanoid = (size = 21) => {
  45. fillPool((size |= 0))
  46. let id = ''
  47. for (let i = poolOffset - size; i < poolOffset; i++) {
  48. id += urlAlphabet[pool[i] & 63]
  49. }
  50. return id
  51. }
  52. export { nanoid, customAlphabet, customRandom, urlAlphabet, random }