CoolFace
Apppublic

opusdev/vector-similarity-api

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes
binary.ts749 linesDownload Raw Back to src
1import { type InspectFn, defaultInspect, isAnyArrayBuffer, isUint8Array } from './parser/utils';2import type { EJSONOptions } from './extended_json';3import { BSONError } from './error';4import { BSON_BINARY_SUBTYPE_UUID_NEW } from './constants';5import { ByteUtils } from './utils/byte_utils';6import { BSONValue } from './bson_value';7import { NumberUtils } from './utils/number_utils';8 9/** @public */10export type BinarySequence = Uint8Array | number[];11 12/** @public */13export interface BinaryExtendedLegacy {14  $type: string;15  $binary: string;16}17 18/** @public */19export interface BinaryExtended {20  $binary: {21    subType: string;22    base64: string;23  };24}25 26/**27 * A class representation of the BSON Binary type.28 * @public29 * @category BSONType30 */31export class Binary extends BSONValue {32  get _bsontype(): 'Binary' {33    return 'Binary';34  }35 36  /**37   * Binary default subtype38   * @internal39   */40  private static readonly BSON_BINARY_SUBTYPE_DEFAULT = 0;41 42  /** Initial buffer default size */43  static readonly BUFFER_SIZE = 256;44  /** Default BSON type */45  static readonly SUBTYPE_DEFAULT = 0;46  /** Function BSON type */47  static readonly SUBTYPE_FUNCTION = 1;48  /** Byte Array BSON type */49  static readonly SUBTYPE_BYTE_ARRAY = 2;50  /** Deprecated UUID BSON type @deprecated Please use SUBTYPE_UUID */51  static readonly SUBTYPE_UUID_OLD = 3;52  /** UUID BSON type */53  static readonly SUBTYPE_UUID = 4;54  /** MD5 BSON type */55  static readonly SUBTYPE_MD5 = 5;56  /** Encrypted BSON type */57  static readonly SUBTYPE_ENCRYPTED = 6;58  /** Column BSON type */59  static readonly SUBTYPE_COLUMN = 7;60  /** Sensitive BSON type */61  static readonly SUBTYPE_SENSITIVE = 8;62  /** Vector BSON type */63  static readonly SUBTYPE_VECTOR = 9;64  /** User BSON type */65  static readonly SUBTYPE_USER_DEFINED = 128;66 67  /** datatype of a Binary Vector (subtype: 9) */68  static readonly VECTOR_TYPE = Object.freeze({69    Int8: 0x03,70    Float32: 0x27,71    PackedBit: 0x1072  } as const);73 74  /**75   * The bytes of the Binary value.76   *77   * The format of a Binary value in BSON is defined as:78   * ```txt79   * binary	::= int32 subtype (byte*)80   * ```81   *82   * This `buffer` is the "(byte*)" segment.83   *84   * Unless the value is subtype 2, then deserialize will read the first 4 bytes as an int32 and set this to the remaining bytes.85   *86   * ```txt87   * binary	::= int32 unsigned_byte(2) int32 (byte*)88   * ```89   *90   * @see https://bsonspec.org/spec.html91   */92  public buffer: Uint8Array;93  /**94   * The binary subtype.95   *96   * Current defined values are:97   *98   * - `unsigned_byte(0)` Generic binary subtype99   * - `unsigned_byte(1)` Function100   * - `unsigned_byte(2)` Binary (Deprecated)101   * - `unsigned_byte(3)` UUID (Deprecated)102   * - `unsigned_byte(4)` UUID103   * - `unsigned_byte(5)` MD5104   * - `unsigned_byte(6)` Encrypted BSON value105   * - `unsigned_byte(7)` Compressed BSON column106   * - `unsigned_byte(8)` Sensitive107   * - `unsigned_byte(9)` Vector108   * - `unsigned_byte(128)` - `unsigned_byte(255)` User defined109   */110  public sub_type: number;111  /**112   * The Binary's `buffer` can be larger than the Binary's content.113   * This property is used to determine where the content ends in the buffer.114   */115  public position: number;116 117  /**118   * Create a new Binary instance.119   * @param buffer - a buffer object containing the binary data.120   * @param subType - the option binary type.121   */122  constructor(buffer?: BinarySequence, subType?: number) {123    super();124    if (125      !(buffer == null) &&126      typeof buffer === 'string' &&127      !ArrayBuffer.isView(buffer) &&128      !isAnyArrayBuffer(buffer) &&129      !Array.isArray(buffer)130    ) {131      throw new BSONError('Binary can only be constructed from Uint8Array or number[]');132    }133 134    this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;135 136    if (buffer == null) {137      // create an empty binary buffer138      this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);139      this.position = 0;140    } else {141      this.buffer = Array.isArray(buffer)142        ? ByteUtils.fromNumberArray(buffer)143        : ByteUtils.toLocalBufferType(buffer);144      this.position = this.buffer.byteLength;145    }146  }147 148  /**149   * Updates this binary with byte_value.150   *151   * @param byteValue - a single byte we wish to write.152   */153  put(byteValue: string | number | Uint8Array | number[]): void {154    // If it's a string and a has more than one character throw an error155    if (typeof byteValue === 'string' && byteValue.length !== 1) {156      throw new BSONError('only accepts single character String');157    } else if (typeof byteValue !== 'number' && byteValue.length !== 1)158      throw new BSONError('only accepts single character Uint8Array or Array');159 160    // Decode the byte value once161    let decodedByte: number;162    if (typeof byteValue === 'string') {163      decodedByte = byteValue.charCodeAt(0);164    } else if (typeof byteValue === 'number') {165      decodedByte = byteValue;166    } else {167      decodedByte = byteValue[0];168    }169 170    if (decodedByte < 0 || decodedByte > 255) {171      throw new BSONError('only accepts number in a valid unsigned byte range 0-255');172    }173 174    if (this.buffer.byteLength > this.position) {175      this.buffer[this.position++] = decodedByte;176    } else {177      const newSpace = ByteUtils.allocate(Binary.BUFFER_SIZE + this.buffer.length);178      newSpace.set(this.buffer, 0);179      this.buffer = newSpace;180      this.buffer[this.position++] = decodedByte;181    }182  }183 184  /**185   * Writes a buffer to the binary.186   *187   * @param sequence - a string or buffer to be written to the Binary BSON object.188   * @param offset - specify the binary of where to write the content.189   */190  write(sequence: BinarySequence, offset: number): void {191    offset = typeof offset === 'number' ? offset : this.position;192 193    // If the buffer is to small let's extend the buffer194    if (this.buffer.byteLength < offset + sequence.length) {195      const newSpace = ByteUtils.allocate(this.buffer.byteLength + sequence.length);196      newSpace.set(this.buffer, 0);197 198      // Assign the new buffer199      this.buffer = newSpace;200    }201 202    if (ArrayBuffer.isView(sequence)) {203      this.buffer.set(ByteUtils.toLocalBufferType(sequence), offset);204      this.position =205        offset + sequence.byteLength > this.position ? offset + sequence.length : this.position;206    } else if (typeof sequence === 'string') {207      throw new BSONError('input cannot be string');208    }209  }210 211  /**212   * Returns a view of **length** bytes starting at **position**.213   *214   * @param position - read from the given position in the Binary.215   * @param length - the number of bytes to read.216   */217  read(position: number, length: number): Uint8Array {218    length = length && length > 0 ? length : this.position;219    const end = position + length;220    return this.buffer.subarray(position, end > this.position ? this.position : end);221  }222 223  /** returns a view of the binary value as a Uint8Array */224  value(): Uint8Array {225    // Optimize to serialize for the situation where the data == size of buffer226    return this.buffer.length === this.position227      ? this.buffer228      : this.buffer.subarray(0, this.position);229  }230 231  /** the length of the binary sequence */232  length(): number {233    return this.position;234  }235 236  toJSON(): string {237    return ByteUtils.toBase64(this.buffer.subarray(0, this.position));238  }239 240  toString(encoding?: 'hex' | 'base64' | 'utf8' | 'utf-8'): string {241    if (encoding === 'hex') return ByteUtils.toHex(this.buffer.subarray(0, this.position));242    if (encoding === 'base64') return ByteUtils.toBase64(this.buffer.subarray(0, this.position));243    if (encoding === 'utf8' || encoding === 'utf-8')244      return ByteUtils.toUTF8(this.buffer, 0, this.position, false);245    return ByteUtils.toUTF8(this.buffer, 0, this.position, false);246  }247 248  /** @internal */249  toExtendedJSON(options?: EJSONOptions): BinaryExtendedLegacy | BinaryExtended {250    options = options || {};251 252    if (this.sub_type === Binary.SUBTYPE_VECTOR) {253      validateBinaryVector(this);254    }255 256    const base64String = ByteUtils.toBase64(this.buffer);257 258    const subType = Number(this.sub_type).toString(16);259    if (options.legacy) {260      return {261        $binary: base64String,262        $type: subType.length === 1 ? '0' + subType : subType263      };264    }265    return {266      $binary: {267        base64: base64String,268        subType: subType.length === 1 ? '0' + subType : subType269      }270    };271  }272 273  toUUID(): UUID {274    if (this.sub_type === Binary.SUBTYPE_UUID) {275      return new UUID(this.buffer.subarray(0, this.position));276    }277 278    throw new BSONError(279      `Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only "${Binary.SUBTYPE_UUID}" is currently supported.`280    );281  }282 283  /** Creates an Binary instance from a hex digit string */284  static createFromHexString(hex: string, subType?: number): Binary {285    return new Binary(ByteUtils.fromHex(hex), subType);286  }287 288  /** Creates an Binary instance from a base64 string */289  static createFromBase64(base64: string, subType?: number): Binary {290    return new Binary(ByteUtils.fromBase64(base64), subType);291  }292 293  /** @internal */294  static fromExtendedJSON(295    doc: BinaryExtendedLegacy | BinaryExtended | UUIDExtended,296    options?: EJSONOptions297  ): Binary {298    options = options || {};299    let data: Uint8Array | undefined;300    let type;301    if ('$binary' in doc) {302      if (options.legacy && typeof doc.$binary === 'string' && '$type' in doc) {303        type = doc.$type ? parseInt(doc.$type, 16) : 0;304        data = ByteUtils.fromBase64(doc.$binary);305      } else {306        if (typeof doc.$binary !== 'string') {307          type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;308          data = ByteUtils.fromBase64(doc.$binary.base64);309        }310      }311    } else if ('$uuid' in doc) {312      type = 4;313      data = UUID.bytesFromString(doc.$uuid);314    }315    if (!data) {316      throw new BSONError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);317    }318    return type === BSON_BINARY_SUBTYPE_UUID_NEW ? new UUID(data) : new Binary(data, type);319  }320 321  inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {322    inspect ??= defaultInspect;323    const base64 = ByteUtils.toBase64(this.buffer.subarray(0, this.position));324    const base64Arg = inspect(base64, options);325    const subTypeArg = inspect(this.sub_type, options);326    return `Binary.createFromBase64(${base64Arg}, ${subTypeArg})`;327  }328 329  /**330   * If this Binary represents a Int8 Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.Int8`),331   * returns a copy of the bytes in a new Int8Array.332   *333   * If the Binary is not a Vector, or the datatype is not Int8, an error is thrown.334   */335  public toInt8Array(): Int8Array {336    if (this.sub_type !== Binary.SUBTYPE_VECTOR) {337      throw new BSONError('Binary sub_type is not Vector');338    }339 340    if (this.buffer[0] !== Binary.VECTOR_TYPE.Int8) {341      throw new BSONError('Binary datatype field is not Int8');342    }343 344    validateBinaryVector(this);345 346    return new Int8Array(347      this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)348    );349  }350 351  /**352   * If this Binary represents a Float32 Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.Float32`),353   * returns a copy of the bytes in a new Float32Array.354   *355   * If the Binary is not a Vector, or the datatype is not Float32, an error is thrown.356   */357  public toFloat32Array(): Float32Array {358    if (this.sub_type !== Binary.SUBTYPE_VECTOR) {359      throw new BSONError('Binary sub_type is not Vector');360    }361 362    if (this.buffer[0] !== Binary.VECTOR_TYPE.Float32) {363      throw new BSONError('Binary datatype field is not Float32');364    }365 366    validateBinaryVector(this);367 368    const floatBytes = new Uint8Array(369      this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)370    );371 372    if (NumberUtils.isBigEndian) ByteUtils.swap32(floatBytes);373 374    return new Float32Array(floatBytes.buffer);375  }376 377  /**378   * If this Binary represents packed bit Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.PackedBit`),379   * returns a copy of the bytes that are packed bits.380   *381   * Use `toBits` to get the unpacked bits.382   *383   * If the Binary is not a Vector, or the datatype is not PackedBit, an error is thrown.384   */385  public toPackedBits(): Uint8Array {386    if (this.sub_type !== Binary.SUBTYPE_VECTOR) {387      throw new BSONError('Binary sub_type is not Vector');388    }389 390    if (this.buffer[0] !== Binary.VECTOR_TYPE.PackedBit) {391      throw new BSONError('Binary datatype field is not packed bit');392    }393 394    validateBinaryVector(this);395 396    return new Uint8Array(397      this.buffer.buffer.slice(this.buffer.byteOffset + 2, this.buffer.byteOffset + this.position)398    );399  }400 401  /**402   * If this Binary represents a Packed bit Vector (`binary.buffer[0] === Binary.VECTOR_TYPE.PackedBit`),403   * returns a copy of the bit unpacked into a new Int8Array.404   *405   * Use `toPackedBits` to get the bits still in packed form.406   *407   * If the Binary is not a Vector, or the datatype is not PackedBit, an error is thrown.408   */409  public toBits(): Int8Array {410    if (this.sub_type !== Binary.SUBTYPE_VECTOR) {411      throw new BSONError('Binary sub_type is not Vector');412    }413 414    if (this.buffer[0] !== Binary.VECTOR_TYPE.PackedBit) {415      throw new BSONError('Binary datatype field is not packed bit');416    }417 418    validateBinaryVector(this);419 420    const byteCount = this.length() - 2;421    const bitCount = byteCount * 8 - this.buffer[1];422    const bits = new Int8Array(bitCount);423 424    for (let bitOffset = 0; bitOffset < bits.length; bitOffset++) {425      const byteOffset = (bitOffset / 8) | 0;426      const byte = this.buffer[byteOffset + 2];427      const shift = 7 - (bitOffset % 8);428      const bit = (byte >> shift) & 1;429      bits[bitOffset] = bit;430    }431 432    return bits;433  }434 435  /**436   * Constructs a Binary representing an Int8 Vector.437   * @param array - The array to store as a view on the Binary class438   */439  public static fromInt8Array(array: Int8Array): Binary {440    const buffer = ByteUtils.allocate(array.byteLength + 2);441    buffer[0] = Binary.VECTOR_TYPE.Int8;442    buffer[1] = 0;443    const intBytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);444    buffer.set(intBytes, 2);445    const bin = new this(buffer, this.SUBTYPE_VECTOR);446    validateBinaryVector(bin);447    return bin;448  }449 450  /** Constructs a Binary representing an Float32 Vector. */451  public static fromFloat32Array(array: Float32Array): Binary {452    const binaryBytes = ByteUtils.allocate(array.byteLength + 2);453    binaryBytes[0] = Binary.VECTOR_TYPE.Float32;454    binaryBytes[1] = 0;455 456    const floatBytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);457    binaryBytes.set(floatBytes, 2);458 459    if (NumberUtils.isBigEndian) ByteUtils.swap32(new Uint8Array(binaryBytes.buffer, 2));460 461    const bin = new this(binaryBytes, this.SUBTYPE_VECTOR);462    validateBinaryVector(bin);463    return bin;464  }465 466  /**467   * Constructs a Binary representing a packed bit Vector.468   *469   * Use `fromBits` to pack an array of 1s and 0s.470   */471  public static fromPackedBits(array: Uint8Array, padding = 0): Binary {472    const buffer = ByteUtils.allocate(array.byteLength + 2);473    buffer[0] = Binary.VECTOR_TYPE.PackedBit;474    buffer[1] = padding;475    buffer.set(array, 2);476    const bin = new this(buffer, this.SUBTYPE_VECTOR);477    validateBinaryVector(bin);478    return bin;479  }480 481  /**482   * Constructs a Binary representing an Packed Bit Vector.483   * @param array - The array of 1s and 0s to pack into the Binary instance484   */485  public static fromBits(bits: ArrayLike<number>): Binary {486    const byteLength = (bits.length + 7) >>> 3; // ceil(bits.length / 8)487    const bytes = new Uint8Array(byteLength + 2);488    bytes[0] = Binary.VECTOR_TYPE.PackedBit;489 490    const remainder = bits.length % 8;491    bytes[1] = remainder === 0 ? 0 : 8 - remainder;492 493    for (let bitOffset = 0; bitOffset < bits.length; bitOffset++) {494      const byteOffset = bitOffset >>> 3; // floor(bitOffset / 8)495      const bit = bits[bitOffset];496 497      if (bit !== 0 && bit !== 1) {498        throw new BSONError(499          `Invalid bit value at ${bitOffset}: must be 0 or 1, found ${bits[bitOffset]}`500        );501      }502 503      if (bit === 0) continue;504 505      const shift = 7 - (bitOffset % 8);506      bytes[byteOffset + 2] |= bit << shift;507    }508 509    return new this(bytes, Binary.SUBTYPE_VECTOR);510  }511}512 513export function validateBinaryVector(vector: Binary): void {514  if (vector.sub_type !== Binary.SUBTYPE_VECTOR) return;515 516  const size = vector.position;517 518  // NOTE: Validation is only applied to **KNOWN** vector types519  // If a new datatype is introduced, a future version of the library will need to add validation520  const datatype = vector.buffer[0];521 522  // NOTE: We do not enable noUncheckedIndexedAccess so TS believes this is always number523  // a Binary vector may be empty, in which case the padding is undefined524  // this possible value is tolerable for our validation checks525  const padding: number | undefined = vector.buffer[1];526 527  if (528    (datatype === Binary.VECTOR_TYPE.Float32 || datatype === Binary.VECTOR_TYPE.Int8) &&529    padding !== 0530  ) {531    throw new BSONError('Invalid Vector: padding must be zero for int8 and float32 vectors');532  }533 534  if (datatype === Binary.VECTOR_TYPE.Float32) {535    if (size !== 0 && size - 2 !== 0 && (size - 2) % 4 !== 0) {536      throw new BSONError('Invalid Vector: Float32 vector must contain a multiple of 4 bytes');537    }538  }539 540  if (datatype === Binary.VECTOR_TYPE.PackedBit && padding !== 0 && size === 2) {541    throw new BSONError(542      'Invalid Vector: padding must be zero for packed bit vectors that are empty'543    );544  }545 546  if (datatype === Binary.VECTOR_TYPE.PackedBit && padding > 7) {547    throw new BSONError(548      `Invalid Vector: padding must be a value between 0 and 7. found: ${padding}`549    );550  }551}552 553/** @public */554export type UUIDExtended = {555  $uuid: string;556};557 558const UUID_BYTE_LENGTH = 16;559const UUID_WITHOUT_DASHES = /^[0-9A-F]{32}$/i;560const UUID_WITH_DASHES = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;561 562/**563 * A class representation of the BSON UUID type.564 * @public565 */566export class UUID extends Binary {567  /**568   * Create a UUID type569   *570   * When the argument to the constructor is omitted a random v4 UUID will be generated.571   *572   * @param input - Can be a 32 or 36 character hex string (dashes excluded/included) or a 16 byte binary Buffer.573   */574  constructor(input?: string | Uint8Array | UUID) {575    let bytes: Uint8Array;576    if (input == null) {577      bytes = UUID.generate();578    } else if (input instanceof UUID) {579      bytes = ByteUtils.toLocalBufferType(new Uint8Array(input.buffer));580    } else if (ArrayBuffer.isView(input) && input.byteLength === UUID_BYTE_LENGTH) {581      bytes = ByteUtils.toLocalBufferType(input);582    } else if (typeof input === 'string') {583      bytes = UUID.bytesFromString(input);584    } else {585      throw new BSONError(586        'Argument passed in UUID constructor must be a UUID, a 16 byte Buffer or a 32/36 character hex string (dashes excluded/included, format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).'587      );588    }589    super(bytes, BSON_BINARY_SUBTYPE_UUID_NEW);590  }591 592  /**593   * The UUID bytes594   * @readonly595   */596  get id(): Uint8Array {597    return this.buffer;598  }599 600  set id(value: Uint8Array) {601    this.buffer = value;602  }603 604  /**605   * Returns the UUID id as a 32 or 36 character hex string representation, excluding/including dashes (defaults to 36 character dash separated)606   * @param includeDashes - should the string exclude dash-separators.607   */608  toHexString(includeDashes = true): string {609    if (includeDashes) {610      return [611        ByteUtils.toHex(this.buffer.subarray(0, 4)),612        ByteUtils.toHex(this.buffer.subarray(4, 6)),613        ByteUtils.toHex(this.buffer.subarray(6, 8)),614        ByteUtils.toHex(this.buffer.subarray(8, 10)),615        ByteUtils.toHex(this.buffer.subarray(10, 16))616      ].join('-');617    }618    return ByteUtils.toHex(this.buffer);619  }620 621  /**622   * Converts the id into a 36 character (dashes included) hex string, unless a encoding is specified.623   */624  toString(encoding?: 'hex' | 'base64'): string {625    if (encoding === 'hex') return ByteUtils.toHex(this.id);626    if (encoding === 'base64') return ByteUtils.toBase64(this.id);627    return this.toHexString();628  }629 630  /**631   * Converts the id into its JSON string representation.632   * A 36 character (dashes included) hex string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx633   */634  toJSON(): string {635    return this.toHexString();636  }637 638  /**639   * Compares the equality of this UUID with `otherID`.640   *641   * @param otherId - UUID instance to compare against.642   */643  equals(otherId: string | Uint8Array | UUID): boolean {644    if (!otherId) {645      return false;646    }647 648    if (otherId instanceof UUID) {649      return ByteUtils.equals(otherId.id, this.id);650    }651 652    try {653      return ByteUtils.equals(new UUID(otherId).id, this.id);654    } catch {655      return false;656    }657  }658 659  /**660   * Creates a Binary instance from the current UUID.661   */662  toBinary(): Binary {663    return new Binary(this.id, Binary.SUBTYPE_UUID);664  }665 666  /**667   * Generates a populated buffer containing a v4 uuid668   */669  static generate(): Uint8Array {670    const bytes = ByteUtils.randomBytes(UUID_BYTE_LENGTH);671 672    // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`673    // Kindly borrowed from https://github.com/uuidjs/uuid/blob/master/src/v4.js674    bytes[6] = (bytes[6] & 0x0f) | 0x40;675    bytes[8] = (bytes[8] & 0x3f) | 0x80;676 677    return bytes;678  }679 680  /**681   * Checks if a value is a valid bson UUID682   * @param input - UUID, string or Buffer to validate.683   */684  static isValid(input: string | Uint8Array | UUID | Binary): boolean {685    if (!input) {686      return false;687    }688 689    if (typeof input === 'string') {690      return UUID.isValidUUIDString(input);691    }692 693    if (isUint8Array(input)) {694      return input.byteLength === UUID_BYTE_LENGTH;695    }696 697    return (698      input._bsontype === 'Binary' &&699      input.sub_type === this.SUBTYPE_UUID &&700      input.buffer.byteLength === 16701    );702  }703 704  /**705   * Creates an UUID from a hex string representation of an UUID.706   * @param hexString - 32 or 36 character hex string (dashes excluded/included).707   */708  static override createFromHexString(hexString: string): UUID {709    const buffer = UUID.bytesFromString(hexString);710    return new UUID(buffer);711  }712 713  /** Creates an UUID from a base64 string representation of an UUID. */714  static override createFromBase64(base64: string): UUID {715    return new UUID(ByteUtils.fromBase64(base64));716  }717 718  /** @internal */719  static bytesFromString(representation: string) {720    if (!UUID.isValidUUIDString(representation)) {721      throw new BSONError(722        'UUID string representation must be 32 hex digits or canonical hyphenated representation'723      );724    }725    return ByteUtils.fromHex(representation.replace(/-/g, ''));726  }727 728  /**729   * @internal730   *731   * Validates a string to be a hex digit sequence with or without dashes.732   * The canonical hyphenated representation of a uuid is hex in 8-4-4-4-12 groups.733   */734  static isValidUUIDString(representation: string) {735    return UUID_WITHOUT_DASHES.test(representation) || UUID_WITH_DASHES.test(representation);736  }737 738  /**739   * Converts to a string representation of this Id.740   *741   * @returns return the 36 character hex string representation.742   *743   */744  inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {745    inspect ??= defaultInspect;746    return `new UUID(${inspect(this.toHexString(), options)})`;747  }748}749