method/nanoid.ts

/** @module Function */

import { emptyObj } from '@ext/noOps'
import { uuid } from './uuid'
import { doIt } from './doIt'
import { isObj } from '@object/isObj'
import { isStr } from '@string/isStr'

export type TNanoidOpts = {
  joiner?: string
  parts?: number
  prefix?: string
}

const sudoRandomStr = (str: string) => {
  const times = Math.floor(str.length / 2)
  return doIt(times, {}, () =>
    str.charAt(Math.floor(Math.random() * str.length))
  ).join(``)
}

const sudoRandomNum = () => {
  return Math.floor(Math.floor(Math.random() * Date.now()))
}

const sudoRandom = (radix = 36) => {
  return sudoRandomNum().toString(radix)
}

/**
 * Generates am ID using the UUID method, but makes it shorter
 * Also allows passing some custom options like a prefix for the generated ID
 */
export const nanoid = (
  base?: string | TNanoidOpts,
  opts?: TNanoidOpts
): string => {
  if (!isObj(opts)) {
    if (isObj(base)) {
      opts = base
      base = undefined
    } else opts = emptyObj
  }

  const { parts = 2, prefix = ``, joiner = `.` } = opts

  const seed = (isStr(base) && base) || sudoRandom(16)
  const salt = uuid().split(`-`)

  let count = 0
  const gen = () => {
    count++
    const even = count % 2 === 0
    return sudoRandomStr((even && salt.pop()) || seed)
  }

  return `${prefix ? `${prefix}${joiner}` : ``}${doIt(parts, {}, gen).join(
    joiner
  )}`
}