| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 | import { BSONValue } from './bson_value';import { BSONError } from './error';import { type InspectFn, defaultInspect } from './parser/utils';import { BSONDataView, ByteUtils } from './utils/byte_utils';// Regular expression that checks for hex valueconst checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');// Unique sequence for the current process (initialized on first use)let PROCESS_UNIQUE: Uint8Array | null = null;/** @public */export interface ObjectIdLike {  id: string | Uint8Array;  __id?: string;  toHexString(): string;}/** @public */export interface ObjectIdExtended {  $oid: string;}const kId = Symbol('id');/** * A class representation of the BSON ObjectId type. * @public * @category BSONType */export class ObjectId extends BSONValue {  get _bsontype(): 'ObjectId' {    return 'ObjectId';  }  /** @internal */  private static index = Math.floor(Math.random() * 0xffffff);  static cacheHexString: boolean;  /** ObjectId Bytes @internal */  private [kId]!: Uint8Array;  /** ObjectId hexString cache @internal */  private __id?: string;  /**   * Create an ObjectId type   *   * @param inputId - Can be a 24 character hex string, 12 byte binary Buffer, or a number.   */  constructor(inputId?: string | number | ObjectId | ObjectIdLike | Uint8Array) {    super();    // workingId is set based on type of input and whether valid id exists for the input    let workingId;    if (typeof inputId === 'object' && inputId && 'id' in inputId) {      if (typeof inputId.id !== 'string' && !ArrayBuffer.isView(inputId.id)) {        throw new BSONError('Argument passed in must have an id that is of type string or Buffer');      }      if ('toHexString' in inputId && typeof inputId.toHexString === 'function') {        workingId = ByteUtils.fromHex(inputId.toHexString());      } else {        workingId = inputId.id;      }    } else {      workingId = inputId;    }    // the following cases use workingId to construct an ObjectId    if (workingId == null || typeof workingId === 'number') {      // The most common use case (blank id, new objectId instance)      // Generate a new id      this[kId] = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined);    } else if (ArrayBuffer.isView(workingId) && workingId.byteLength === 12) {      // If intstanceof matches we can escape calling ensure buffer in Node.js environments      this[kId] = ByteUtils.toLocalBufferType(workingId);    } else if (typeof workingId === 'string') {      if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {        this[kId] = ByteUtils.fromHex(workingId);      } else {        throw new BSONError(          'input must be a 24 character hex string, 12 byte Uint8Array, or an integer'        );      }    } else {      throw new BSONError('Argument passed in does not match the accepted types');    }    // If we are caching the hex string    if (ObjectId.cacheHexString) {      this.__id = ByteUtils.toHex(this.id);    }  }  /**   * The ObjectId bytes   * @readonly   */  get id(): Uint8Array {    return this[kId];  }  set id(value: Uint8Array) {    this[kId] = value;    if (ObjectId.cacheHexString) {      this.__id = ByteUtils.toHex(value);    }  }  /** Returns the ObjectId id as a 24 lowercase character hex string representation */  toHexString(): string {    if (ObjectId.cacheHexString && this.__id) {      return this.__id;    }    const hexString = ByteUtils.toHex(this.id);    if (ObjectId.cacheHexString && !this.__id) {      this.__id = hexString;    }    return hexString;  }  /**   * Update the ObjectId index   * @internal   */  private static getInc(): number {    return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);  }  /**   * Generate a 12 byte id buffer used in ObjectId's   *   * @param time - pass in a second based timestamp.   */  static generate(time?: number): Uint8Array {    if ('number' !== typeof time) {      time = Math.floor(Date.now() / 1000);    }    const inc = ObjectId.getInc();    const buffer = ByteUtils.allocate(12);    // 4-byte timestamp    BSONDataView.fromUint8Array(buffer).setUint32(0, time, false);    // set PROCESS_UNIQUE if yet not initialized    if (PROCESS_UNIQUE === null) {      PROCESS_UNIQUE = ByteUtils.randomBytes(5);    }    // 5-byte process unique    buffer[4] = PROCESS_UNIQUE[0];    buffer[5] = PROCESS_UNIQUE[1];    buffer[6] = PROCESS_UNIQUE[2];    buffer[7] = PROCESS_UNIQUE[3];    buffer[8] = PROCESS_UNIQUE[4];    // 3-byte counter    buffer[11] = inc & 0xff;    buffer[10] = (inc >> 8) & 0xff;    buffer[9] = (inc >> 16) & 0xff;    return buffer;  }  /**   * Converts the id into a 24 character hex string for printing, unless encoding is provided.   * @param encoding - hex or base64   */  toString(encoding?: 'hex' | 'base64'): string {    // Is the id a buffer then use the buffer toString method to return the format    if (encoding === 'base64') return ByteUtils.toBase64(this.id);    if (encoding === 'hex') return this.toHexString();    return this.toHexString();  }  /** Converts to its JSON the 24 character hex string representation. */  toJSON(): string {    return this.toHexString();  }  /** @internal */  private static is(variable: unknown): variable is ObjectId {    return (      variable != null &&      typeof variable === 'object' &&      '_bsontype' in variable &&      variable._bsontype === 'ObjectId'    );  }  /**   * Compares the equality of this ObjectId with `otherID`.   *   * @param otherId - ObjectId instance to compare against.   */  equals(otherId: string | ObjectId | ObjectIdLike | undefined | null): boolean {    if (otherId === undefined || otherId === null) {      return false;    }    if (ObjectId.is(otherId)) {      return this[kId][11] === otherId[kId][11] && ByteUtils.equals(this[kId], otherId[kId]);    }    if (typeof otherId === 'string') {      return otherId.toLowerCase() === this.toHexString();    }    if (typeof otherId === 'object' && typeof otherId.toHexString === 'function') {      const otherIdString = otherId.toHexString();      const thisIdString = this.toHexString();      return typeof otherIdString === 'string' && otherIdString.toLowerCase() === thisIdString;    }    return false;  }  /** Returns the generation date (accurate up to the second) that this ID was generated. */  getTimestamp(): Date {    const timestamp = new Date();    const time = BSONDataView.fromUint8Array(this.id).getUint32(0, false);    timestamp.setTime(Math.floor(time) * 1000);    return timestamp;  }  /** @internal */  static createPk(): ObjectId {    return new ObjectId();  }  /**   * Creates an ObjectId from a second based number, with the rest of the ObjectId zeroed out. Used for comparisons or sorting the ObjectId.   *   * @param time - an integer number representing a number of seconds.   */  static createFromTime(time: number): ObjectId {    const buffer = ByteUtils.fromNumberArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);    // Encode time into first 4 bytes    BSONDataView.fromUint8Array(buffer).setUint32(0, time, false);    // Return the new objectId    return new ObjectId(buffer);  }  /**   * Creates an ObjectId from a hex string representation of an ObjectId.   *   * @param hexString - create a ObjectId from a passed in 24 character hexstring.   */  static createFromHexString(hexString: string): ObjectId {    if (hexString?.length !== 24) {      throw new BSONError('hex string must be 24 characters');    }    return new ObjectId(ByteUtils.fromHex(hexString));  }  /** Creates an ObjectId instance from a base64 string */  static createFromBase64(base64: string): ObjectId {    if (base64?.length !== 16) {      throw new BSONError('base64 string must be 16 characters');    }    return new ObjectId(ByteUtils.fromBase64(base64));  }  /**   * Checks if a value can be used to create a valid bson ObjectId   * @param id - any JS value   */  static isValid(id: string | number | ObjectId | ObjectIdLike | Uint8Array): boolean {    if (id == null) return false;    try {      new ObjectId(id);      return true;    } catch {      return false;    }  }  /** @internal */  toExtendedJSON(): ObjectIdExtended {    if (this.toHexString) return { $oid: this.toHexString() };    return { $oid: this.toString('hex') };  }  /** @internal */  static fromExtendedJSON(doc: ObjectIdExtended): ObjectId {    return new ObjectId(doc.$oid);  }  /**   * Converts to a string representation of this Id.   *   * @returns return the 24 character hex string representation.   */  inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {    inspect ??= defaultInspect;    return `new ObjectId(${inspect(this.toHexString(), options)})`;  }}
 |