UNPKG

snowflake-uuid

Version:

Twitter's Snowflake generator implementation for NodeJS

94 lines (93 loc) 3.35 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Worker = void 0; class Worker { #epoch; #workerId; #workerIdBits; #maxWorkerId; #datacenterId; #datacenterIdBits; #maxDatacenterId; #sequence; #sequenceBits; #workerIdShift; #datacenterIdShift; #timestampLeftShift; #sequenceMask; #lastTimestamp = -1n; constructor(workerId = 0n, datacenterId = 0n, options) { this.#epoch = BigInt(options?.epoch ?? 1609459200000); this.#workerId = BigInt(workerId); this.#workerIdBits = BigInt(options?.workerIdBits ?? 5); this.#maxWorkerId = -1n ^ (-1n << this.#workerIdBits); if (this.#workerId < 0 || this.#workerId > this.#maxWorkerId) { throw new Error('With ' + this.#workerIdBits.toString() + " bits, worker id can't be greater than " + this.#maxWorkerId.toString() + ' or less than 0'); } this.#datacenterId = BigInt(datacenterId); this.#datacenterIdBits = BigInt(options?.datacenterIdBits ?? 5); this.#maxDatacenterId = -1n ^ (-1n << this.#datacenterIdBits); if (this.#datacenterId > this.#maxDatacenterId || this.#datacenterId < 0) { throw new Error('With ' + this.#datacenterIdBits.toString() + " bits, datacenter id can't be greater than " + this.#maxDatacenterId.toString() + ' or less than 0'); } this.#sequence = BigInt(options?.sequence ?? 0); this.#sequenceBits = BigInt(options?.sequenceBits ?? 12); this.#sequenceMask = -1n ^ (-1n << this.#sequenceBits); this.#workerIdShift = this.#sequenceBits; this.#datacenterIdShift = this.#sequenceBits + this.#workerIdBits; this.#timestampLeftShift = this.#sequenceBits + this.#workerIdBits + this.#datacenterIdBits; } get workerId() { return this.#workerId; } get datacenterId() { return this.#datacenterId; } get currentSequence() { return this.#sequence; } get lastTimestamp() { return this.#lastTimestamp; } nextId() { let timestamp = Worker.now(); if (timestamp < this.#lastTimestamp) { throw new Error("Clock moved backwards. Can't generate new ID for " + (this.#lastTimestamp - timestamp).toString() + 'milliseconds.'); } if (timestamp === this.#lastTimestamp) { this.#sequence = (this.#sequence + 1n) & this.#sequenceMask; if (this.#sequence === 0n) { timestamp = this.tilNextMillis(this.#lastTimestamp); } } else { this.#sequence = 0n; } this.#lastTimestamp = timestamp; return (((timestamp - this.#epoch) << this.#timestampLeftShift) | (this.#datacenterId << this.#datacenterIdShift) | (this.#workerId << this.#workerIdShift) | this.#sequence); } tilNextMillis(lastTimestamp) { let timestamp; do { timestamp = Worker.now(); } while (timestamp <= lastTimestamp); return timestamp; } static now() { return BigInt(Date.now()); } } exports.Worker = Worker;