UNPKG

snowyflake

Version:

A modern implementation Snowflake on TypeScript

125 lines (121 loc) 3.57 kB
const Epoch = { Unix: 0n, Twitter: 1288834974657n, Discord: 1420070400000n, }; const DEFAULT_VALUE = 0n; const DEFAULT_SEQUENCE = DEFAULT_VALUE; const USIGNED_INCREASE = 1n; const SIGNED_INCREASE = -1n; const WORKER_ID_BITS = 5n; const PROCESS_ID_BITS = 5n; const SEQUENCE_BITS = 12n; const WORKER_ID_DECONSTRUCT_MASK = 0x3e0000n; const PROCESS_ID_DECONSTRUCT_MASK = 0x1f000n; const WORKER_ID_MASK = SIGNED_INCREASE ^ (SIGNED_INCREASE << WORKER_ID_BITS); const PROCESS_ID_MASK = SIGNED_INCREASE ^ (SIGNED_INCREASE << PROCESS_ID_BITS); const SEQUNCE_MASK = SIGNED_INCREASE ^ (SIGNED_INCREASE << SEQUENCE_BITS); const TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + PROCESS_ID_BITS; const WORKER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; const PROCESS_ID_SHIFT = SEQUENCE_BITS; const getNowBigInt = () => BigInt(Date.now()); class Snowyflake { /** * Snowflake start epoch */ epoch; /** * Internal worker ID */ workerId; /** * Internal process ID */ processId; /** * Sequence increment for process */ sequence = DEFAULT_SEQUENCE; /** * Latest timestamp */ latestTimestamp = getNowBigInt(); /** * Constructor */ constructor({ epoch = Epoch.Unix, workerId = DEFAULT_VALUE, processId = DEFAULT_VALUE, } = {}) { this.epoch = epoch; this.workerId = workerId & WORKER_ID_MASK; this.processId = processId & PROCESS_ID_MASK; } /** * Generate a Snowflake */ nextId() { const timestamp = getNowBigInt(); if (this.latestTimestamp === timestamp) { this.sequence = (this.sequence + USIGNED_INCREASE) & SEQUNCE_MASK; } else { this.sequence = DEFAULT_SEQUENCE; this.latestTimestamp = timestamp; } return this.generateCustomId({ timestamp, sequence: this.sequence, }); } /** * Deconstruct the Snowflake with local epoch */ deconstruct(snowflake) { return Snowyflake.deconstruct(snowflake, { epoch: this.epoch, }); } /** * Generate a custom Snowflake */ generateCustomId({ timestamp, sequence }) { return (((timestamp - this.epoch) << TIMESTAMP_LEFT_SHIFT) | (this.workerId << WORKER_ID_SHIFT) | (this.processId << PROCESS_ID_SHIFT) | sequence); } /** * Deconstruct the Snowflake timestamp */ static deconstructTimestamp(snowflake, epoch) { return (snowflake >> TIMESTAMP_LEFT_SHIFT) + epoch; } /** * Deconstruct the Snowflake workerId */ static deconstructWorkerId(snowflake) { return (snowflake & WORKER_ID_DECONSTRUCT_MASK) >> WORKER_ID_SHIFT; } /** * Deconstruct the Snowflake processId */ static deconstructProcessId(snowflake) { return (snowflake & PROCESS_ID_DECONSTRUCT_MASK) >> PROCESS_ID_SHIFT; } /** * Deconstruct the Snowflake sequence */ static deconstructSequence(snowflake) { return snowflake & SEQUNCE_MASK; } /** * Deconstruct the Snowflake */ static deconstruct(snowflake, { epoch }) { return { timestamp: Snowyflake.deconstructTimestamp(snowflake, epoch), workerId: Snowyflake.deconstructWorkerId(snowflake), processId: Snowyflake.deconstructProcessId(snowflake), sequence: Snowyflake.deconstructSequence(snowflake), }; } } export { Epoch, Snowyflake };