UNPKG

universal-common

Version:

Library that provides useful missing base class library functionality.

175 lines (151 loc) 5.51 kB
import ArgumentError from "./ArgumentError.js"; /** * A utility class for generating and manipulating Globally Unique Identifiers (GUIDs). * This implementation creates RFC4122 version 4 compliant UUIDs. * The class stores GUIDs as byte arrays internally for efficient operations. */ export default class Guid { /** * The internal byte array representation of the GUID. * @type {Uint8Array} * @private */ #bytes; /** * Creates a new Guid instance. * @param {Uint8Array|string|null} value - Optional byte array or string to initialize the GUID. */ constructor(value = null) { if (value) { if (value instanceof Uint8Array) { if (value.length !== 16) { throw new Error("GUID must be initialized with a 16-byte Uint8Array"); } this.#bytes = new Uint8Array(value); } else if (typeof value === "string") { const hexString = value.replace(/[^0-9a-f]/gi, ""); if (hexString.length !== 32) { throw new ArgumentError("Invalid GUID format."); } this.#bytes = new Uint8Array(16); for (let i = 0; i < 16; i++) { this.#bytes[i] = parseInt(hexString.substring(i * 2, i * 2 + 2), 16); } } } else { this.#bytes = new Uint8Array(16); } } /** * Creates a Guid from a byte array. * @param {Uint8Array} bytes - A 16-byte array. * @returns {Guid} A new Guid instance. */ static fromBytes(bytes) { return new Guid(bytes); } /** * Creates a new random GUID instance. * @returns {Guid} A new Guid instance with random values. */ static newGuid() { const bytes = new Uint8Array(16); // Fill with random values using Math.random() for consistent behavior // Wait until crypto is standard in both Node and browsers without a environment check. for (let i = 0; i < 16; i++) { bytes[i] = Math.floor(Math.random() * 256); } // Set version bits (Version 4 - random UUID) bytes[6] = (bytes[6] & 0x0f) | 0x40; // Set variant bits (Variant 1 - RFC4122) bytes[8] = (bytes[8] & 0x3f) | 0x80; return new Guid(bytes); } /** * Creates a Guid from a string representation. * @param {string} guidString - The string representation of a GUID. * @returns {Guid} A new Guid instance. * @throws {ArgumentError} If the string is not a valid GUID format. */ static parse(guidString) { const hexString = guidString.replace(/[^0-9a-f]/gi, ""); if (hexString.length !== 32) { throw new ArgumentError("Invalid GUID format."); } const bytes = new Uint8Array(16); for (let i = 0; i < 16; i++) { bytes[i] = parseInt(hexString.substring(i * 2, i * 2 + 2), 16); } return new Guid(bytes); } /** * Tries to parse a GUID string without throwing an exception. * @param {string} guidString - The string to parse. * @param {Object} result - An object with a "value" property that will be set to the parsed GUID if successful. * @returns {Guid?} True if parsing was successful, false otherwise. */ static tryParse(guidString) { try { return Guid.parse(guidString); } catch { return null; } } /** * Checks if this GUID is equal to another GUID. * @param {Guid} other - The GUID to compare with. * @returns {boolean} True if the GUIDs are equal, false otherwise. */ equals(other) { if (!(other instanceof Guid)) { return false; } const a = this.#bytes; const b = other.#bytes; for (let i = 0; i < 16; i++) { if (a[i] !== b[i]) { return false; } } return true; } /** * Checks if this GUID is the empty GUID. * @returns {boolean} True if this is the empty GUID, false otherwise. */ isEmpty() { for (let i = 0; i < 16; i++) { if (this.#bytes[i] !== 0) { return false; } } return true; } /** * Returns the byte array representation of the GUID. * @returns {Uint8Array} The byte array. */ toBytes() { return new Uint8Array(this.#bytes); } /** * Returns the GUID as a hyphenated string. * @returns {string} The string representation. */ toString() { const byteToHex = byte => byte.toString(16).padStart(2, "0"); const formatSection = (start, length) => { let result = ""; for (let i = 0; i < length; i++) { result += byteToHex(this.#bytes[start + i]); } return result; }; return `${formatSection(0, 4)}-${ formatSection(4, 2)}-${ formatSection(6, 2)}-${ formatSection(8, 2)}-${ formatSection(10, 6)}`; } }