UNPKG

ulid

Version:

A universally unique lexicographically sortable identifier generator

162 lines (161 loc) 5.91 kB
(function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports"], factory); } })(function (require, exports) { "use strict"; var factory = function (prng) { // These values should NEVER change. If // they do, we're no longer making ulids! var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // Crockford's Base32 var ENCODING_LEN = ENCODING.length; var TIME_MAX = Math.pow(2, 48) - 1; var TIME_LEN = 10; var RANDOM_LEN = 16; function replaceCharAt(str, index, char) { if (index > str.length - 1) { return str; } return str.substr(0, index) + char + str.substr(index + 1); } function incrementBase32(str) { var done = undefined; var index = str.length; var char; var charIndex; var maxCharIndex = ENCODING_LEN - 1; while (!done && index-- >= 0) { char = str[index]; charIndex = ENCODING.indexOf(char); if (charIndex === -1) { throw new Error("incorrectly encoded string"); } if (charIndex === maxCharIndex) { str = replaceCharAt(str, index, ENCODING[0]); continue; } done = replaceCharAt(str, index, ENCODING[charIndex + 1]); } if (typeof done === "string") { return done; } throw new Error("cannot increment this string"); } function randomChar() { var rand = Math.floor(prng() * ENCODING_LEN); if (rand === ENCODING_LEN) { rand = ENCODING_LEN - 1; } return ENCODING.charAt(rand); } function encodeTime(now, len) { if (isNaN(now)) { throw new Error(now + " must be a number"); } if (now > TIME_MAX) { throw new Error("cannot encode time greater than " + TIME_MAX); } if (now < 0) { throw new Error("time must be positive"); } if (Number.isInteger(now) === false) { throw new Error("time must be an integer"); } var mod; var str = ""; for (var x = len; x > 0; x--) { mod = now % ENCODING_LEN; str = ENCODING.charAt(mod) + str; now = (now - mod) / ENCODING_LEN; } return str; } function encodeRandom(len) { var str = ""; for (; len > 0; len--) { str = randomChar() + str; } return str; } function decodeTime(id) { if (id.length !== TIME_LEN + RANDOM_LEN) { throw new Error("malformed ulid"); } var time = id .substr(0, TIME_LEN) .split('') .reverse() .reduce(function (carry, char, index) { var encodingIndex = ENCODING.indexOf(char); if (encodingIndex === -1) { throw new Error("invalid character found: " + char); } return carry += encodingIndex * Math.pow(ENCODING_LEN, index); }, 0); if (time > TIME_MAX) { throw new Error("malformed ulid, timestamp too large"); } return time; } function createMonotonic() { var lastTime = 0; var lastRandom; return function ulid(seedTime) { if (isNaN(seedTime)) { seedTime = Date.now(); } if (seedTime <= lastTime) { var incrementedRandom = lastRandom = incrementBase32(lastRandom); return encodeTime(lastTime, TIME_LEN) + incrementedRandom; } lastTime = seedTime; var newRandom = lastRandom = encodeRandom(RANDOM_LEN); return encodeTime(seedTime, TIME_LEN) + newRandom; }; } var ulid = function ulid(seedTime) { if (isNaN(seedTime)) { seedTime = Date.now(); } return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN); }; ulid.prng = prng; ulid.createMonotonic = createMonotonic; ulid.incrementBase32 = incrementBase32; ulid.randomChar = randomChar; ulid.encodeTime = encodeTime; ulid.encodeRandom = encodeRandom; ulid.decodeTime = decodeTime; ulid.factory = factory; return ulid; }; /* istanbul ignore next */ function _prng(root) { var browserCrypto = root && (root.crypto || root.msCrypto); if (browserCrypto) { try { return function () { return browserCrypto.getRandomValues(new Uint8Array(1))[0] / 0xFF; }; } catch (e) { } } else { try { var nodeCrypto_1 = require("crypto"); return function () { return nodeCrypto_1.randomBytes(1).readUInt8() / 0xFF; }; } catch (e) { } } try { console.error("[ulid] secure crypto unusable, falling back to insecure Math.random()!"); } catch (e) { } return function () { return Math.random(); }; } var root = typeof window !== "undefined" ? window : null; var prng = _prng(root); return factory(prng); });