UNPKG

flakeid53

Version:

Simple module that aims to provide [snowflake](https://en.wikipedia.org/wiki/Snowflake_ID) alike ID generator that fits into [53bit number as specificed on IEEE_754.](https://en.wikipedia.org/wiki/IEEE_754)

88 lines (76 loc) 2.4 kB
'use strict'; /** * Create new Flake ID generator. * * @param {Object} options * @param {number} options.epoch - Epoch time to starts from. * The time span is limited to 28 years, so current date should fit within the epoch time. * Example usage: `epoch: +new Date('2021-03-03')` * @param {number=} options.workerId * Worker ID, range must be between 0..9 * * @throws Error */ function createFlakeID53({ epoch, workerId }) { let sequenceTime = 0; let sequenceTick = 0; if (!epoch) { throw Error("No epoch set"); } if (workerId && !Number.isInteger(workerId)) { throw Error("Cannot use workerId out of 0..9"); } workerId = Math.abs((workerId || 0) % 10); return { nextId, parse, }; /** * Generate next ID number * @returns {Promise<number>} next int ID, or fail with message */ function nextId() { return new Promise(nextIdPromise); } /** * @param {(num: number) => void} resolve * @param {(error: any) => void} reject */ function nextIdPromise(resolve, reject) { const current = Date.now(); if (current < epoch) { return reject("Epoch is out of range"); } else if (sequenceTime < current) { sequenceTick = 0; sequenceTime = current; } else if (sequenceTime > current) { return reject("Clock is shifted"); } else { sequenceTick++; } if (sequenceTick > 999) { setTimeout(nextIdPromise, 1, resolve, reject); } else { const t = (current - epoch) % 1000000000000; const oor = (current - epoch) / 1000000000000; if (oor >= 1 || t > 900719925473) { reject(`Timestamp ${current} is out of range. Rejecting ID generating, as it could exceed Number.MAX_SAFE_INTEGER`); } else { resolve((t * 10 + workerId) * 1000 + sequenceTick); } } } /** * Parse ID number into pieces * @param {number} id * @returns {{time: Date, workerId: number, sequence: number}} */ function parse(id) { return { time: new Date(Math.floor(id / 10000) + epoch), sequence: id % 1000, workerId: Math.floor((id % 10000) / 1000), }; } } module.exports = createFlakeID53;