UNPKG

onenex-snowflake

Version:

Library to help you create a Snowflake Id or parse the same. This solves the problem of generating unique identifiers at scale by Onenex.

134 lines (133 loc) 4.58 kB
"use strict"; /** * Interface of a Snowflake after `Generator.deconstruct()`. * @property {bigint} snowflake - Snowflake deconstructed from * @property {bigint} timestamp - The timestamp the snowflake was generated * @property {bigint} shard_id - The shard_id used when generating * @property {bigint} increment - The increment of this snowflake * @property {string} binary - The 64Bit snowflake binary string * @interface DeconstructedSnowflake */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Snowflake = void 0; class Snowflake { constructor() { /* c8 ignore start */ /** * The generators epoch timestamp in milliseconds. * * Defaults to "1st of January, 2000, 00:00". * * @type {number} */ /* c8 ignore end */ this.EPOCH = Date.UTC(1970, 0, 1).valueOf(); /* c8 ignore start */ /** * The id of the shard running this generator. * * Defaults to "1". * * @type {number} */ /* c8 ignore end */ this.SHARD_ID = 1; /* c8 ignore start */ /** * The sequence of the current running generator. * * Defaults to "1". * * @type {number} */ /* c8 ignore end */ this._SEQUENCE = 1; /* c8 ignore start */ /** * Generates a single snowflake. * @param {Date|number} [timestamp = Date.now] - Timestamp to generate from * @returns {bigint} */ /* c8 ignore end */ /** * Generates a single snowflake binary string. */ this.cached64BitZeros = '0000000000000000000000000000000000000000000000000000000000000000'; } get getSequence() { return this._SEQUENCE; } set setSequence(value) { if (value < 0 || value >= 4096) { throw new Error('SEQUENCE must be between 0 and 4095.'); } this._SEQUENCE = value; } generate({ timestamp = Date.now(), shard_id = this.SHARD_ID, } = {}) { if (timestamp instanceof Date) timestamp = timestamp.valueOf(); else timestamp = new Date(timestamp).valueOf(); let result = (BigInt(timestamp) - BigInt(this.EPOCH)) << BigInt(22); result = result | (BigInt(shard_id % 1024) << BigInt(12)); result = result | BigInt(this._SEQUENCE++ % 4096); return result.toString(); } generateShortId({ timestamp = Date.now(), shard_id = this.SHARD_ID, } = {}) { const fullSnowflake = this.generate({ timestamp, shard_id }); // Use the last 8 characters of the full Snowflake for the short ID return fullSnowflake.slice(-8); } /** * Deconstruct a snowflake to its values using the `Generator.epoch`. * @param {string|string[]} snowflake - Snowflake(s) to deconstruct * @returns {DeconstructedSnowflake|DeconstructedSnowflake[]} */ parse(snowflake) { const binary = this.binary(snowflake); return { timestamp: this.extractBits(snowflake, 1, 41), shard_id: this.extractBits(snowflake, 42, 10), sequence: this.extractBits(snowflake, 52), binary, }; } isValid(snowflake) { if (snowflake.length !== 19 || !/^\d+$/.test(snowflake)) { return false; } try { this.parse(snowflake); return true; } catch (error) { console.error(error); return false; } } /** * Extract bits and their values from a snowflake. * @param {string} snowflake - Snowflake to extract from * @param {number|bigint} shift - Number of bits to shift before extracting * @param {number|bigint} length - Number of bits to extract before stopping * @returns {bigint} */ extractBits(snowflake, start, length) { return parseInt(length ? this.binary(snowflake).substring(start, start + length) : this.binary(snowflake).substring(start), 2); } /** * Transform a snowflake into its 64Bit binary string. * @param {string} snowflake - Snowflake to transform * @returns {string} * @private */ binary(snowflake) { const binValue = BigInt(snowflake).toString(2); return binValue.length < 64 ? this.cached64BitZeros.substring(0, 64 - binValue.length) + binValue : binValue; } } exports.Snowflake = Snowflake;