lib/uuid.js

/*
 *  Author: Vlad Seryakov vseryakov@gmail.com
 *  backendjs 2018
 */

const crypto = require('crypto');
const lib = require(__dirname + '/../lib');

/**
 * Unique Id (UUID v4) without any special characters and in lower case
 * @param {string} [prefix] - prepend with prefix
 * @return {string}
 * @memberof module:lib
 * @method uuid
 */
lib.uuid = function(prefix)
{
    return (prefix || "") + crypto.randomUUID().replace(/[-]/g, '').toLowerCase();
}

/**
 * Short unique id within a microsecond or local epoch
 * @param {string} [prefix] - prepend with prefix
 * @param {object} [options] - same options as for {@link module:lib.getHashid}
 * @param {int} [options.epoch] - local epoch type via {@link module:lib.localEpoch}, default is milliseconds, `m` for microseconds, `s` for seconds
 * @return {string} generated hash
 * @memberof module:lib
 * @method suuid
 */
lib.suuid = function(prefix, options)
{
    var hashid = this.getHashid(options);
    var c = options?.epoch ? lib.localEpoch(options?.epoch) : lib.clock();
    var s = hashid.encode(c, hashid._counter);
    return prefix ? prefix + s : s;
}

/**
 * Generate a SnowFlake unique id as 64-bit number
 * Format: time - 41 bit, node - 10 bit, counter - 12 bit
 * @param {object} [options]
 * @param {int} [options.now] - time, if not given local epoch clock is used in microseconds
 * @param {int} [options.epoch] - local epoch type via {@link module:lib.localEpoch}, default is milliseconds, `m` for microseconds, `s` for seconds
 * @param {int} [options.node] - node id, limited to max 1024
 * @param {int} [options.radix] - default is 10, use any value between 2 - 36 for other numeric encoding
 * @memberof module:lib
 * @method sfuuid
 */
lib.sfuuid = function(options)
{
    var node = options?.node || lib.sfuuidNode;
    if (node === undefined) {
        var intf = lib.networkInterfaces()[0];
        if (intf) lib.sfuuidNode = node = lib.murmurHash3(intf.mac);
    }
    var now = options?.now || lib.localEpoch(options?.epoch);
    var n = BigInt(now) << 22n | (BigInt(node % 1024) << 12n) | BigInt(lib.sfuuidCounter++ % 4096);
    return n.toString(options?.radix || 10);
}

lib.sfuuidCounter = lib.randomShort();

/**
 * Parse an SFUUID numeric id into its original components.
 *
 * Splits the id into bit fields:
 * - `now`:     64 bits starting at bit 22
 * - `node`:    10 bits starting at bit 12
 * - `counter`: 12 bits starting at bit 0
 *
 * @function lib.sfuuidParse
 * @param {string|number|bigint} id
 *   SFUUID value to parse. Can be a BigInt, a decimal string, or a number
 *   (numbers may lose precision for large ids).
 *
 * @returns {Object} rc
 * @returns {bigint} rc.id Parsed id as a BigInt (only set if parsing succeeded).
 * @returns {number} rc.now Extracted `now` component (only set if parsing succeeded).
 * @returns {number} rc.node Extracted `node` component (only set if parsing succeeded).
 * @returns {number} rc.counter Extracted `counter` component (only set if parsing succeeded).
 */
lib.sfuuidParse = function(id)
{
    const _map = { now: [22n, 64n], node: [12n, 10n], counter: [0n, 12n] };
    const rc = {};
    try {
        id = rc.id = BigInt(id);
        for (const p in _map) {
            rc[p] = Number((id & (((1n << _map[p][1]) - 1n) << _map[p][0])) >> _map[p][0]);
        }
    } catch (e) {}
    return rc;
}