packageDocumentation module public export type Id64String = string ; public export type GuidString = string ; public export type Id64Set = Set < Id64String > ; public export type Id64Array = Id64String [ ] ; public export type Id64Arg = Id64String | Id64Set | Id64Array ; function toHex ( str ) { const v = parseInt ( str , 16 ) ; return Number . isNaN ( v ) ? 0 : v ; } function isLowerCaseNonZeroHexDigit ( str , index ) { return isLowerCaseHexDigit ( str , index , false ) ; } function isLowerCaseHexDigit ( str , index , allowZero = true ) { const charCode = str . charCodeAt ( index ) ; const minDecimalDigit = allowZero ? 0x30 : 0x31 ; return ( charCode >= minDecimalDigit && charCode <= 0x39 ) || ( charCode >= 0x61 && charCode <= 0x66 ) ; } function isValidHexString ( id , startIndex , len ) { if ( len === 0 ) return false ; if ( ! isLowerCaseNonZeroHexDigit ( id , startIndex ) ) return false ; for ( let i = 1 ; i < len ; i ++ ) if ( ! isLowerCaseHexDigit ( id , startIndex + i ) ) return false ; return true ; } public export namespace Id64 { /** Extract the "local" Id portion of an Id64String, contained in the lower 40 bits of the 64-bit value. */ export function getLocalId ( id ) { if ( isInvalid ( id ) ) return 0 ; const len = id . length ; const start = ( len > 12 ) ? ( len - 10 ) : 2 ; return toHex ( id . slice ( start ) ) ; } /** Extract the briefcase Id portion of an Id64String, contained in the upper 24 bits of the 64-bit value. */ export function getBriefcaseId ( id ) { if ( isInvalid ( id ) ) return 0 ; const len = id . length ; return ( len <= 12 ) ? 0 : toHex ( id . slice ( 2 , len - 10 ) ) ; } param prop returns note note export function fromJSON ( prop ? ) { return typeof prop === "string" ? Id64 . fromString ( prop ) : Id64 . invalid ; } /** Given a string value, attempt to normalize it into a well-formed Id string.
     * If the input is already a well-formed Id string, it is returned unmodified.
     * Otherwise, the input is trimmed of leading and trailing whitespace, converted to lowercase, and an attempt is made to parse it as a 64-bit hexadecimal integer.
     * If parsing succeeds the normalized result is returned; otherwise the result is "0", indicating an invalid Id.
     *
     * For a description of "well-formed", see [Working with Ids]($docs/learning/common/Id64.md).
     */ export function fromString ( val ) { if ( typeof val !== "string" ) return invalid ; if ( Id64 . isId64 ( val ) ) return val ; val = val . toLowerCase ( ) . trim ( ) ; const len = val . length ; if ( len < 2 || val [ 0 ] !== "0" || val [ 1 ] !== "x" ) return invalid ; let low = 0 ; let high = 0 ; let start = 2 ; if ( len > 12 ) { start = ( len - 10 ) ; high = toHex ( val . slice ( 2 , start ) ) ; } low = toHex ( val . slice ( start ) ) ; return fromLocalAndBriefcaseIds ( low , high ) ; } const _localIdPrefixByLocalIdLength = [ "0000000000" , "000000000" , "00000000" , "0000000" , "000000" , "00000" , "0000" , "000" , "00" , "0" , "" , ] ; param localId param briefcaseId returns export function fromLocalAndBriefcaseIds ( localId , briefcaseId ) { if ( typeof localId !== "number" || typeof briefcaseId !== "number" ) return invalid ; localId = Math . floor ( localId ) ; if ( 0 === localId ) return invalid ; briefcaseId = Math . floor ( briefcaseId ) ; const lowStr = localId . toString ( 16 ) ; return `0x${ ( briefcaseId === 0 ) ? lowStr : ( briefcaseId . toString ( 16 ) + ( _localIdPrefixByLocalIdLength [ lowStr . length ] + lowStr ) ) }` ; } const scratchCharCodes = [ 0x30 , 0x78 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , 0x30 , ] ; function uint4ToCharCode ( uint4 ) { return uint4 + ( uint4 < 10 ? 0x30 : 0x57 ) ; } function charCodeToUint4 ( char ) { return char - ( char >= 0x57 ? 0x57 : 0x30 ) ; } function substringToUint32 ( id , start , end ) { let uint32 = 0 ; for ( let i = start ; i < end ; i ++ ) { const uint4 = charCodeToUint4 ( id . charCodeAt ( i ) ) ; const shift = ( end - i - 1 ) << 2 ; const mask = uint4 << shift ; uint32 = ( uint32 | mask ) >>> 0 ; } return uint32 ; } param lowBytes param highBytes returns see export function fromUint32Pair ( lowBytes , highBytes ) { const localIdLow = lowBytes >>> 0 ; const localIdHigh = ( highBytes & 0x000000ff ) * ( 0xffffffff + 1 ) ; const localId = localIdLow + localIdHigh ; if ( 0 === localId ) return invalid ; const buffer = scratchCharCodes ; let index = 2 ; for ( let i = 7 ; i >= 0 ; i -- ) { const shift = i << 2 ; const mask = 0xf << shift ; const uint4 = ( highBytes & mask ) >>> shift ; if ( index > 2 || 0 !== uint4 ) buffer [ index ++ ] = uint4ToCharCode ( uint4 ) ; } for ( let i = 7 ; i >= 0 ; i -- ) { const shift = i << 2 ; const mask = 0xf << shift ; const uint4 = ( lowBytes & mask ) >>> shift ; if ( index > 2 || 0 !== uint4 ) buffer [ index ++ ] = uint4ToCharCode ( uint4 ) ; } if ( buffer . length !== index ) buffer . length = index ; return String . fromCharCode ( ... scratchCharCodes ) ; } see export function fromUint32PairObject ( pair ) { return fromUint32Pair ( pair . lower , pair . upper ) ; } see export function isValidUint32Pair ( lowBytes , highBytes ) { return 0 !== lowBytes || 0 !== ( highBytes & 0x000000ff ) ; } see see export interface Uint32Pair { /** The lower 4 bytes of the 64-bit integer. */ lower : number ; /** The upper 4 bytes of the 64-bit integer. */ upper : number ; } param id param out returns export function getUint32Pair ( id , out ? ) { if ( ! out ) out = { lower : 0 , upper : 0 } ; out . lower = getLowerUint32 ( id ) ; out . upper = getUpperUint32 ( id ) ; return out ; } /** Extract an unsigned 32-bit integer from the lower 4 bytes of an Id64String. */ export function getLowerUint32 ( id ) { if ( isInvalid ( id ) ) return 0 ; const end = id . length ; const start = end > 10 ? end - 8 : 2 ; return substringToUint32 ( id , start , end ) ; } /** Extract an unsigned 32-bit integer from the upper 4 bytes of an Id64String. */ export function getUpperUint32 ( id ) { const len = id . length ; if ( len <= 10 || isInvalid ( id ) ) return 0 ; return substringToUint32 ( id , 2 , len - 8 ) ; } param arg param makeCopy returns export function toIdSet ( arg , makeCopy = false ) { if ( arg instanceof Set ) return makeCopy ? new Set < string > ( arg ) : arg ; const ids = new Set < Id64String > ( ) ; if ( typeof arg === "string" ) ids . add ( arg ) ; else if ( Array . isArray ( arg ) ) { arg . forEach ( ( id ) => { if ( typeof id === "string" ) ids . add ( id ) ; } ) ; } return ids ; } see export function * iterator ( ids ) { if ( typeof ids === "string" ) { yield ids ; } else { for ( const id of ids ) yield id ; } } /** Obtain an iterable over the specified Ids. Example usage:
     * ```ts
     *  const ids = ["0x123", "0xfed"];
     *  for (const id of Id64.iterable(ids))
     *    console.log(id);
     * ```
     */ export function iterable ( ids ) { return { [ Symbol . iterator ] : ( ) => iterator ( ids ) } ; } /** Return the first [[Id64String]] of an [[Id64Arg]]. */ export function getFirst ( arg ) { return typeof arg === "string" ? arg : ( Array . isArray ( arg ) ? arg [ 0 ] : arg . values ( ) . next ( ) . value ) ; } /** Return the number of [[Id64String]]s represented by an [[Id64Arg]]. */ export function sizeOf ( arg ) { return typeof arg === "string" ? 1 : ( Array . isArray ( arg ) ? arg . length : arg . size ) ; } /** Returns true if the [[Id64Arg]] contains the specified Id. */ export function has ( arg , id ) { if ( typeof arg === "string" ) return arg === id ; if ( Array . isArray ( arg ) ) return - 1 !== arg . indexOf ( id ) ; return arg . has ( id ) ; } /** The string representation of an invalid Id. */ export const invalid = "0" ; param id returns note see see export function isTransient ( id ) { return 18 === id . length && id . startsWith ( "0xffffff" ) ; } see see see export function isTransientId64 ( id ) { return isValidId64 ( id ) && isTransient ( id ) ; } see export function isId64 ( id ) { const len = id . length ; if ( 0 === len || 18 < len ) return false ; if ( "0" !== id [ 0 ] ) return false ; if ( 1 === len ) return true ; if ( 2 === len || "x" !== id [ 1 ] ) return false ; let localIdStart = 2 ; if ( len > 12 ) { localIdStart = len - 10 ; if ( ! isValidHexString ( id , 2 , localIdStart - 2 ) ) return false ; for ( let i = localIdStart ; i < len ; i ++ ) { if ( 0x30 !== id . charCodeAt ( i ) ) break ; else localIdStart ++ ; } if ( localIdStart >= len ) return false ; } return isValidHexString ( id , localIdStart , len - localIdStart ) ; } note see see export function isValid ( id ) { return Id64 . invalid !== id ; } see see export function isValidId64 ( id ) { return Id64 . invalid !== id && Id64 . isId64 ( id ) ; } see export function isInvalid ( id ) { return Id64 . invalid === id ; } see public export class Uint32Set { protected readonly _map = new Map < number , Set < number > > ( ) ; param ids public constructor ( ids ? ) { if ( undefined !== ids ) this . addIds ( ids ) ; } /** Remove all contents of this set. */ public clear ( ) { this . _map . clear ( ) ; } /** Add an Id to the set. */ public addId ( id ) { this . add ( Id64 . getLowerUint32 ( id ) , Id64 . getUpperUint32 ( id ) ) ; } /** Add any number of Ids to the set. */ public addIds ( ids ) { for ( const id of Id64 . iterable ( ids ) ) this . addId ( id ) ; } /** Returns true if the set contains the specified Id. */ public hasId ( id ) { return this . has ( Id64 . getLowerUint32 ( id ) , Id64 . getUpperUint32 ( id ) ) ; } /** Add an Id to the set. */ public add ( low , high ) { let set = this . _map . get ( high ) ; if ( undefined === set ) { set = new Set < number > ( ) ; this . _map . set ( high , set ) ; } set . add ( low ) ; } /** Remove an Id from the set. */ public deleteId ( id ) { this . delete ( Id64 . getLowerUint32 ( id ) , Id64 . getUpperUint32 ( id ) ) ; } /** Remove any number of Ids from the set. */ public deleteIds ( ids ) { for ( const id of Id64 . iterable ( ids ) ) this . deleteId ( id ) ; } /** Remove an Id from the set. */ public delete ( low , high ) { const set = this . _map . get ( high ) ; if ( undefined !== set ) set . delete ( low ) ; } /** Returns true if the set contains the specified Id. */ public has ( low , high ) { const set = this . _map . get ( high ) ; return undefined !== set && set . has ( low ) ; } /** Returns true if the set contains no Ids. */ public get isEmpty ( ) : boolean { return 0 === this . _map . size ; } /** Returns the number of Ids contained in the set. */ public get size ( ) : number { let size = 0 ; for ( const entry of this . _map ) size += entry [ 1 ] . size ; return size ; } /** Populates and returns an array of all Ids contained in the set. */ public toId64Array ( ) { const ids = [ ] ; for ( const entry of this . _map ) for ( const low of entry [ 1 ] ) ids . push ( Id64 . fromUint32Pair ( low , entry [ 0 ] ) ) ; return ids ; } /** Populates and returns a set of all Ids contained in the set. */ public toId64Set ( ) { const ids = new Set < string > ( ) ; for ( const entry of this . _map ) for ( const low of entry [ 1 ] ) ids . add ( Id64 . fromUint32Pair ( low , entry [ 0 ] ) ) ; return ids ; } /** Execute a function against each Id in this set. */ public forEach ( func ) { for ( const entry of this . _map ) for ( const lo of entry [ 1 ] ) func ( lo , entry [ 0 ] ) ; } } see public export class Uint32Map < T > { protected readonly _map = new Map < number , Map < number , T > > ( ) ; /** Remove all entries from the map. */ public clear ( ) { this . _map . clear ( ) ; } /** Find an entry in the map by Id. */ public getById ( id ) { return this . get ( Id64 . getLowerUint32 ( id ) , Id64 . getUpperUint32 ( id ) ) ; } /** Set an entry in the map by Id. */ public setById ( id , value ) { this . set ( Id64 . getLowerUint32 ( id ) , Id64 . getUpperUint32 ( id ) , value ) ; } /** Set an entry in the map by Id components. */ public set ( low , high , value ) { let map = this . _map . get ( high ) ; if ( undefined === map ) { map = new Map < number , T > ( ) ; this . _map . set ( high , map ) ; } map . set ( low , value ) ; } /** Get an entry from the map by Id components. */ public get ( low , high ) { const map = this . _map . get ( high ) ; return undefined !== map ? map . get ( low ) : undefined ; } /** Returns true if the map contains no entries. */ public get isEmpty ( ) : boolean { return 0 === this . _map . size ; } /** Returns the number of entries in the map. */ public get size ( ) : number { let size = 0 ; for ( const entry of this . _map ) size += entry [ 1 ] . size ; return size ; } /** Execute a function against each entry in this map. */ public forEach ( func ) { for ( const outerEntry of this . _map ) for ( const innerEntry of outerEntry [ 1 ] ) func ( innerEntry [ 0 ] , outerEntry [ 0 ] , innerEntry [ 1 ] ) ; } } } public export class TransientIdSequence { private _localId : number = 0 ; /** Generate and return the next transient Id64String in the sequence. */ public get next ( ) : Id64String { return Id64 . fromLocalAndBriefcaseIds ( ++ this . _localId , 0xffffff ) ; } } public export namespace Guid { const uuidPattern = new RegExp ( "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" ) ; /** Represents the empty Guid 00000000-0000-0000-0000-000000000000 */ export const empty = "00000000-0000-0000-0000-000000000000" ; /** Determine whether the input string is "guid-like". That is, it follows the 8-4-4-4-12 pattern. This does not enforce
     *  that the string is actually in valid UUID format.
     */ export function isGuid ( value ) { return uuidPattern . test ( value ) ; } /** Determine whether the input string is a valid V4 Guid string */ export function isV4Guid ( value ) { return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/ . test ( value ) ; } /** Create a new V4 Guid value */ export function createValue ( ) { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" . replace ( /[xy]/g , ( c ) => { const r = Math . random ( ) * 16 | 0 ; const v = c === "x" ? r : ( r & 0x3 | 0x8 ) ; return v . toString ( 16 ) ; } ) ; } param value returns export function normalize ( value ) { const lowerValue = value . toLowerCase ( ) . trim ( ) ; if ( isGuid ( lowerValue ) ) return lowerValue ; const noDashValue = lowerValue . replace ( /-/g , "" ) ; const noDashPattern = /^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/ ; if ( noDashPattern . test ( noDashValue ) ) { return noDashValue . replace ( noDashPattern , ( _match , p1 , p2 , p3 , p4 , p5 ) => `${ p1 }-${ p2 }-${ p3 }-${ p4 }-${ p5 }` ) ; } return value ; } }