import { Buffer } from "buffer" ; import { BSONTypeError } from "./error" ; import { isUint8Array , randomBytes } from "./utils" ; const checkForHexRegExp = new RegExp ( "^[0-9a-fA-F]{24}$" ) ; let PROCESS_UNIQUE = null ; public export interface ObjectIdLike { id : string | Buffer ; __id ? : string ; toHexString ( ) : string ; } public export interface ObjectIdExtended { $oid : string ; } const kId = Symbol ( "id" ) ; public export class ObjectId { _bsontype ! : "ObjectId" ; internal static index = Math . floor ( Math . random ( ) * 0xffffff ) ; static cacheHexString : boolean ; /** ObjectId Bytes @internal */ private [ kId ] ? : Buffer ; /** ObjectId hexString cache @internal */ private __id ? : string ; param inputId constructor ( inputId ? ) { if ( ! ( this instanceof ObjectId ) ) return new ObjectId ( inputId ) ; if ( inputId instanceof ObjectId ) { inputId = inputId . toString ( ) ; } let workingId ; if ( typeof inputId === "object" && inputId && "id" in inputId ) { if ( typeof inputId . id !== "string" && ! ArrayBuffer . isView ( inputId . id ) ) { throw new BSONTypeError ( "Argument passed in must have an id that is of type string or Buffer" ) ; } if ( "toHexString" in inputId && typeof inputId . toHexString === "function" ) { workingId = Buffer . from ( inputId . toHexString ( ) , "hex" ) ; } else { workingId = inputId . id ; } } else { workingId = inputId ; } if ( workingId == null || typeof workingId === "number" ) { this [ kId ] = ObjectId . generate ( typeof workingId === "number" ? workingId : undefined ) ; } else if ( ArrayBuffer . isView ( workingId ) && workingId . byteLength === 12 ) { throw new Error ( "This use-case is not implemented" ) ; } else if ( typeof workingId === "string" ) { if ( workingId . length === 12 ) { const bytes = Buffer . from ( workingId ) ; if ( bytes . byteLength === 12 ) { this [ kId ] = bytes ; } else { throw new BSONTypeError ( "Argument passed in must be a string of 12 bytes" ) ; } } else if ( workingId . length === 24 && checkForHexRegExp . test ( workingId ) ) { this [ kId ] = Buffer . from ( workingId , "hex" ) ; } else { throw new BSONTypeError ( "Argument passed in must be a string of 12 bytes or a string of 24 hex characters" ) ; } } else { throw new BSONTypeError ( "Argument passed in does not match the accepted types" ) ; } if ( ObjectId . cacheHexString ) { this . __id = this . id . toString ( "hex" ) ; } } readonly get id ( ) : Buffer { return this [ kId ] ; } set id ( value ) { this [ kId ] = value ; if ( ObjectId . cacheHexString ) { this . __id = value . toString ( "hex" ) ; } } deprecated get generationTime ( ) : number { return this . id . readInt32BE ( 0 ) ; } set generationTime ( value ) { this . id . writeUInt32BE ( value , 0 ) ; } /** Returns the ObjectId id as a 24 character hex string representation */ toHexString ( ) { if ( ObjectId . cacheHexString && this . __id ) { return this . __id ; } const hexString = this . id . toString ( "hex" ) ; if ( ObjectId . cacheHexString && ! this . __id ) { this . __id = hexString ; } return hexString ; } privateRemarks internal static getInc ( ) { return ( ObjectId . index = ( ObjectId . index + 1 ) % 0xffffff ) ; } param time static generate ( time ? ) { if ( "number" !== typeof time ) { time = Math . floor ( Date . now ( ) / 1000 ) ; } const inc = ObjectId . getInc ( ) ; const buffer = Buffer . alloc ( 12 ) ; buffer . writeUInt32BE ( time , 0 ) ; if ( PROCESS_UNIQUE === null ) { PROCESS_UNIQUE = randomBytes ( 5 ) ; } 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 ] ; buffer [ 11 ] = inc & 0xff ; buffer [ 10 ] = ( inc >> 8 ) & 0xff ; buffer [ 9 ] = ( inc >> 16 ) & 0xff ; return buffer ; } param format toString ( format ? ) { if ( format ) return this . id . toString ( format as BufferEncoding ) ; return this . toHexString ( ) ; } /** Converts to its JSON the 24 character hex string representation. */ toJSON ( ) { return this . toHexString ( ) ; } param otherId equals ( otherId ) { if ( otherId === undefined || otherId === null ) { return false ; } if ( otherId instanceof ObjectId ) { return this . toString ( ) === otherId . toString ( ) ; } if ( typeof otherId === "string" && ObjectId . isValid ( otherId ) && otherId . length === 12 && isUint8Array ( this . id ) ) { return otherId === Buffer . prototype . toString . call ( this . id , "latin1" ) ; } if ( typeof otherId === "string" && ObjectId . isValid ( otherId ) && otherId . length === 24 ) { return otherId . toLowerCase ( ) === this . toHexString ( ) ; } if ( typeof otherId === "string" && ObjectId . isValid ( otherId ) && otherId . length === 12 ) { return Buffer . from ( otherId ) . equals ( this . id ) ; } if ( typeof otherId === "object" && "toHexString" in otherId && typeof otherId . toHexString === "function" ) { return otherId . toHexString ( ) === this . toHexString ( ) ; } return false ; } /** Returns the generation date (accurate up to the second) that this ID was generated. */ getTimestamp ( ) { const timestamp = new Date ( ) ; const time = this . id . readUInt32BE ( 0 ) ; timestamp . setTime ( Math . floor ( time ) * 1000 ) ; return timestamp ; } internal static createPk ( ) { return new ObjectId ( ) ; } param time static createFromTime ( time ) { const buffer = Buffer . from ( [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ; buffer . writeUInt32BE ( time , 0 ) ; return new ObjectId ( buffer ) ; } param hexString static createFromHexString ( hexString ) { if ( typeof hexString === "undefined" || ( hexString != null && hexString . length !== 24 ) ) { throw new BSONTypeError ( "Argument passed in must be a single String of 12 bytes or a string of 24 hex characters" ) ; } return new ObjectId ( Buffer . from ( hexString , "hex" ) ) ; } param id static isValid ( id ) { if ( id == null ) return false ; try { new ObjectId ( id ) ; return true ; } catch { return false ; } } returns internal [ Symbol . for ( "nodejs.util.inspect.custom" ) ] ( ) { return this . inspect ( ) ; } inspect ( ) { return `new ObjectId("${ this . toHexString ( ) }")` ; } } Object . defineProperty ( ObjectId . prototype , "_bsontype" , { value : "ObjectID" } ) ;