UNPKG

snowflakify

Version:

The most complete Snowflake ID generator in TypeScript

117 lines 4.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const process_1 = require("process"); const FragmentBase_js_1 = __importDefault(require("../FragmentBase.js")); const DEFAULT_EPOCH = 1420070400000; /** * TimestampFragment class for timestamp IDs. * @public */ class TimestampFragment extends FragmentBase_js_1.default { /** * @param bits - The number of bits for the fragment. * @param epoch - A custom epoch timestamp. * * Defaults to `1420070400000` (2015-01-01 00:00:00) if omitted. * * @throws `[TIMESTAMP_BITS_INVALID_RANGE]` If bits is less than 38 * @throws `[EPOCH_INVALID_TYPE]` If epoch is not a number * @throws `[EPOCH_INVALID_RANGE]` If epoch is not within 0 and Date.now() */ constructor(bits, epoch = DEFAULT_EPOCH) { super(bits); if (bits < 38) throw new RangeError('[TIMESTAMP_BITS_INVALID_RANGE]: TimestampFragment bits must be greater than or equal to 38'); if (typeof epoch !== 'number') throw new TypeError('[EPOCH_INVALID_TYPE]: TimestampFragment epoch must be a number.'); if (epoch < 0 || epoch > Date.now()) throw new RangeError('[EPOCH_INVALID_RANGE]: TimestampFragment epoch must be within 0 and Date.now() at instantiation time.'); if (bits >= 58) { // nanosecond time unit this.timeUnit = BigInt(1); this.epoch = BigInt(epoch * 10 ** 6); } else if (bits >= 48) { // microsecond time unit this.timeUnit = BigInt(10 ** 3); this.epoch = BigInt(epoch * 10 ** 3); } else { // millisecond time unit this.timeUnit = BigInt(10 ** 6); this.epoch = BigInt(epoch); } const firstHrTime = process_1.hrtime.bigint(); const unixMilliseconds = Date.now(); const secondHrTime = process_1.hrtime.bigint(); this.nanoTimeAnchor = BigInt(unixMilliseconds) * BigInt(10 ** 6) - (firstHrTime + secondHrTime) / BigInt(2); this.lastTimestamp = BigInt(0); } /** * @internal */ set sequenceFragmentReference(sequenceFragment) { this.sequenceFragmentRef = sequenceFragment; } getValue() { this.value = this.unixNow(); // hrtime is not subject to clock drift // this.checkForClockDrift(); if (this.sequenceFragmentRef) this.checkForSequenceCollision(); this.lastTimestamp = this.value; return this.value - this.epoch; } destructure(snowflake) { const bits = BigInt(snowflake) & this.bitMask; return { identifier: this.identifier, value: (bits >> this.bitShift) + this.epoch, }; } /** * Returns a Unix timestamp. * * @remarks * The number of the fragment's bits defines the time unit. * This is done to avoid overflow when left shifting. * * @returns A Unix timestamp in the fragment's time unit. * @internal */ unixNow() { return (this.nanoTimeAnchor + process_1.hrtime.bigint()) / this.timeUnit; } /** * Wait for the next timestamp. * @internal */ waitForNextTimestamp() { // :) while (this.value === this.lastTimestamp) { this.value = this.unixNow(); } } /** * Check for sequence collision. * * @remarks * If a sequence completes its cycle, and the timestamp * is still the same, an already generated snowflake will * be re-generated. * @internal */ checkForSequenceCollision() { if (this.value !== this.lastTimestamp) return this.sequenceFragmentRef.resetSequence(); if (this.sequenceFragmentRef.willReset()) return this.waitForNextTimestamp(); } } exports.default = TimestampFragment; //# sourceMappingURL=TimestampFragment.js.map