UNPKG

@itwin/core-bentley

Version:

Bentley JavaScript core components

729 lines • 30.8 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Ids */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Guid = exports.TransientIdSequence = exports.Id64 = void 0; 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; // '0' or '1'... return (charCode >= minDecimalDigit && charCode <= 0x39) || (charCode >= 0x61 && charCode <= 0x66); // '0'-'9, 'a' -'f' } function isValidHexString(id, startIndex, len) { if (len === 0) return false; // No leading zeroes... if (!isLowerCaseNonZeroHexDigit(id, startIndex)) return false; // ...followed by len-1 lowercase hexadecimal digits. for (let i = 1; i < len; i++) if (!isLowerCaseHexDigit(id, startIndex + i)) return false; return true; } /** * The Id64 namespace provides facilities for working with 64-bit identifiers. These Ids are stored as 64-bit integers inside an [[IModelDb]], but must be represented * as strings in JavaScript because JavaScript does not intrinsically support 64-bit integers. * * The [[Id64String]] type alias is used to indicate function arguments, return types, and variables which are known to contain a well-formed representation of a 64-bit Id. * * See [Working with Ids]($docs/learning/common/Id64.md) for a detailed description and code examples. * @public */ var Id64; (function (Id64) { /** Extract the "local" Id portion of an Id64String, contained in the lower 40 bits of the 64-bit value. */ function getLocalId(id) { if (isInvalid(id)) return 0; const len = id.length; const start = (len > 12) ? (len - 10) : 2; return toHex(id.slice(start)); } Id64.getLocalId = getLocalId; /** Extract the briefcase Id portion of an Id64String, contained in the upper 24 bits of the 64-bit value. */ function getBriefcaseId(id) { if (isInvalid(id)) return 0; const len = id.length; return (len <= 12) ? 0 : toHex(id.slice(2, len - 10)); } Id64.getBriefcaseId = getBriefcaseId; /** Create an Id64String from its JSON representation. * @param prop The JSON representation of an Id. * @returns A well-formed Id string. * @note if the input is undefined, the result is "0", indicating an invalid Id. * @note if the input is not undefined, the result is the same as that of [[Id64.fromString]]. */ function fromJSON(prop) { return typeof prop === "string" ? Id64.fromString(prop) : Id64.invalid; } Id64.fromJSON = fromJSON; /** 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). */ function fromString(val) { // NB: in case this is called from JavaScript, we must check the run-time type... if (typeof val !== "string") return Id64.invalid; // Skip the common case in which the input is already a well-formed Id string if (Id64.isId64(val)) return val; // Attempt to normalize the input into a well-formed Id string val = val.toLowerCase().trim(); const len = val.length; if (len < 2 || val[0] !== "0" || val[1] !== "x") return Id64.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); } Id64.fromString = fromString; // Used when constructing local ID portion of Id64String. Performance optimization. const _localIdPrefixByLocalIdLength = [ "0000000000", "000000000", "00000000", "0000000", "000000", "00000", "0000", "000", "00", "0", "", ]; /** Produce an Id string from a local and briefcase Id. * @param localId The non-zero local Id as an unsigned 40-bit integer. * @param briefcaseId The briefcase Id as an unsigned 24-bit integer. * @returns an Id64String containing the hexadecimal string representation of the unsigned 64-bit integer which would result from the * operation `localId | (briefcaseId << 40)`, or an invalid Id "0" if the inputs are invalid. */ function fromLocalAndBriefcaseIds(localId, briefcaseId) { // NB: Yes, we must check the run-time type... if (typeof localId !== "number" || typeof briefcaseId !== "number") return Id64.invalid; localId = Math.floor(localId); if (0 === localId) return Id64.invalid; briefcaseId = Math.floor(briefcaseId); const lowStr = localId.toString(16); return `0x${(briefcaseId === 0) ? lowStr : (briefcaseId.toString(16) + (_localIdPrefixByLocalIdLength[lowStr.length] + lowStr))}`; } Id64.fromLocalAndBriefcaseIds = fromLocalAndBriefcaseIds; // Used as a buffer when converting a pair of 32-bit integers to an Id64String. Significant performance optimization. const scratchCharCodes = [ 0x30, // "0" 0x78, // "x" 0x30, // "0" 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, ]; // Convert 4-bit unsigned integer to char code representing lower-case hexadecimal digit. function uint4ToCharCode(uint4) { return uint4 + (uint4 < 10 ? 0x30 : 0x57); } // Convert char code representing lower-case hexadecimal digit to 4-bit unsigned integer. function charCodeToUint4(char) { return char - (char >= 0x57 ? 0x57 : 0x30); } // Convert a substring to a uint32. This is twice as fast as using Number.parseInt(). 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; // >>> 0 to force unsigned because javascript } return uint32; } /** Create an Id64String from a pair of unsigned 32-bit integers. * @param lowBytes The lower 4 bytes of the Id * @param highBytes The upper 4 bytes of the Id * @returns an Id64String containing the hexadecimal string representation of the unsigned 64-bit integer which would result from the * operation `lowBytes | (highBytes << 32)`. * @see [[Id64.fromUint32PairObject]] if you have a [[Id64.Uint32Pair]] object. */ function fromUint32Pair(lowBytes, highBytes) { const localIdLow = lowBytes >>> 0; const localIdHigh = (highBytes & 0x000000ff) * (0xffffffff + 1); // aka (highBytes & 0xff) << 32 const localId = localIdLow + localIdHigh; // aka localIdLow | localIdHigh if (0 === localId) return Id64.invalid; // Need to omit or preserve leading zeroes... 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); } Id64.fromUint32Pair = fromUint32Pair; /** Create an Id64String from a [[Id64.Uint32Pair]]. * @see [[Id64.fromUint32Pair]]. */ function fromUint32PairObject(pair) { return fromUint32Pair(pair.lower, pair.upper); } Id64.fromUint32PairObject = fromUint32PairObject; /** Returns true if the inputs represent two halves of a valid 64-bit Id. * @see [[Id64.Uint32Pair]]. */ function isValidUint32Pair(lowBytes, highBytes) { // Detect local ID of zero return 0 !== lowBytes || 0 !== (highBytes & 0x000000ff); } Id64.isValidUint32Pair = isValidUint32Pair; /** Convert an Id64String to a 64-bit unsigned integer represented as a pair of unsigned 32-bit integers. * @param id The well-formed string representation of a 64-bit Id. * @param out Used as the return value if supplied; otherwise a new object is returned. * @returns An object containing the parsed lower and upper 32-bit integers comprising the 64-bit Id. */ function getUint32Pair(id, out) { if (!out) out = { lower: 0, upper: 0 }; out.lower = getLowerUint32(id); out.upper = getUpperUint32(id); return out; } Id64.getUint32Pair = getUint32Pair; /** Extract an unsigned 32-bit integer from the lower 4 bytes of an Id64String. */ function getLowerUint32(id) { if (isInvalid(id)) return 0; const end = id.length; const start = end > 10 ? end - 8 : 2; return substringToUint32(id, start, end); } Id64.getLowerUint32 = getLowerUint32; /** Extract an unsigned 32-bit integer from the upper 4 bytes of an Id64String. */ function getUpperUint32(id) { const len = id.length; if (len <= 10 || isInvalid(id)) return 0; return substringToUint32(id, 2, len - 8); } Id64.getUpperUint32 = getUpperUint32; /** Convert an [[Id64Arg]] into an [[Id64Set]]. * * This method can be used by functions that accept an Id64Arg to conveniently process the value(s). For example: * ```ts * public addCategories(arg: Id64Arg) { Id64.toIdSet(arg).forEach((id) => this.categories.add(id)); } * ``` * * Alternatively, to avoid allocating a new Id64Set, use [[Id64.iterable]]. * * @param arg The Ids to convert to an Id64Set. * @param makeCopy If true, and the input is already an Id64Set, returns a deep copy of the input. * @returns An Id64Set containing the set of [[Id64String]]s represented by the Id64Arg. */ function toIdSet(arg, makeCopy = false) { if (arg instanceof Set) return makeCopy ? new Set(arg) : arg; const ids = new Set(); if (typeof arg === "string") ids.add(arg); else if (Array.isArray(arg)) { arg.forEach((id) => { if (typeof id === "string") ids.add(id); }); } return ids; } Id64.toIdSet = toIdSet; /** Obtain iterator over the specified Ids. * @see [[Id64.iterable]]. */ function* iterator(ids) { if (typeof ids === "string") { yield ids; } else { for (const id of ids) yield id; } } Id64.iterator = iterator; /** Obtain an iterable over the specified Ids. Example usage: * ```ts * const ids = ["0x123", "0xfed"]; * for (const id of Id64.iterable(ids)) * console.log(id); * ``` */ function iterable(ids) { return { [Symbol.iterator]: () => iterator(ids), }; } Id64.iterable = iterable; /** Return the first [[Id64String]] of an [[Id64Arg]]. */ function getFirst(arg) { return typeof arg === "string" ? arg : (Array.isArray(arg) ? arg[0] : arg.values().next().value) ?? Id64.invalid; } Id64.getFirst = getFirst; /** Return the number of [[Id64String]]s represented by an [[Id64Arg]]. */ function sizeOf(arg) { return typeof arg === "string" ? 1 : (Array.isArray(arg) ? arg.length : arg.size); } Id64.sizeOf = sizeOf; /** Returns true if the [[Id64Arg]] contains the specified Id. */ function has(arg, id) { if (typeof arg === "string") return arg === id; if (Array.isArray(arg)) return -1 !== arg.indexOf(id); return arg.has(id); } Id64.has = has; /** The string representation of an invalid Id. */ Id64.invalid = "0"; /** Determine if the supplied id string represents a transient Id. * @param id A well-formed Id string. * @returns true if the Id represents a transient Id. * @note This method assumes the input is a well-formed Id string. * @see [[Id64.isTransientId64]] * @see [[TransientIdSequence]] */ function isTransient(id) { // A transient Id is of the format "0xffffffxxxxxxxxxx" where the leading 6 digits indicate an invalid briefcase Id. return 18 === id.length && id.startsWith("0xffffff"); } Id64.isTransient = isTransient; /** Determine if the input is a well-formed [[Id64String]] and represents a transient Id. * @see [[Id64.isTransient]] * @see [[Id64.isId64]] * @see [[TransientIdSequence]] */ function isTransientId64(id) { return isValidId64(id) && isTransient(id); } Id64.isTransientId64 = isTransientId64; /** Determine if the input is a well-formed [[Id64String]]. * * For a description of "well-formed", see [Working with Ids]($docs/learning/common/Id64.md). * @see [[Id64.isValidId64]] */ function isId64(id) { const len = id.length; if (0 === len || 18 < len) return false; if ("0" !== id[0]) return false; // Well-formed invalid Id: "0" if (1 === len) return true; // Valid Ids begin with "0x" followed by at least one lower-case hexadecimal digit. if (2 === len || "x" !== id[1]) return false; // If briefcase Id is present, it occupies at least one digit, followed by 10 digits for local Id let localIdStart = 2; if (len > 12) { localIdStart = len - 10; // Verify briefcase Id if (!isValidHexString(id, 2, localIdStart - 2)) return false; // Skip leading zeroes in local Id for (let i = localIdStart; i < len; i++) { if (0x30 !== id.charCodeAt(i)) // '0' break; else localIdStart++; } if (localIdStart >= len) return false; } return isValidHexString(id, localIdStart, len - localIdStart); } Id64.isId64 = isId64; /** Returns true if the input is not equal to the representation of an invalid Id. * @note This method assumes the input is a well-formed Id string. * @see [[Id64.isInvalid]] * @see [[Id64.isValidId64]] */ function isValid(id) { return Id64.invalid !== id; } Id64.isValid = isValid; /** Returns true if the input is a well-formed [[Id64String]] representing a valid Id. * @see [[Id64.isValid]] * @see [[Id64.isId64]] */ function isValidId64(id) { return Id64.invalid !== id && Id64.isId64(id); } Id64.isValidId64 = isValidId64; /** Returns true if the input is a well-formed [[Id64String]] representing an invalid Id. * @see [[Id64.isValid]] */ function isInvalid(id) { return Id64.invalid === id; } Id64.isInvalid = isInvalid; /** A specialized replacement for Set<Id64String> optimized for performance-critical code which represents large sets of [[Id64]]s as pairs of * 32-bit integers. * The internal representation is a Map<number, Set<number>> where the Map key is the upper 4 bytes of the IDs and the Set elements are the lower 4 bytes of the IDs. * Because the upper 4 bytes store the 24-bit briefcase ID plus the upper 8 bits of the local ID, there will be a very small distribution of unique Map keys. * To further optimize this data type, the following assumptions are made regarding the { lower, upper } inputs, and no validation is performed to confirm them: * - The inputs are unsigned 32-bit integers; * - The inputs represent a valid Id64String (e.g., local ID is not zero). * @see [[Id64.Uint32Map]] for a similarly-optimized replacement for Map<Id64String, T> * @public */ class Uint32Set { _map = new Map(); /** Construct a new Uint32Set. * @param ids If supplied, all of the specified Ids will be added to the new set. */ constructor(ids) { if (undefined !== ids) this.addIds(ids); } /** Return true if `this` and `other` contain the same set of Ids. */ equals(other) { if (this === other) { return true; } if (this.size !== other.size) { return false; } for (const [key, thisValue] of this._map) { const otherValue = other._map.get(key); if (!otherValue || thisValue.size !== otherValue.size) { return false; } for (const value of thisValue) { if (!otherValue.has(value)) { return false; } } } return true; } /** Remove all contents of this set. */ clear() { this._map.clear(); } /** Add an Id to the set. */ addId(id) { this.add(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Add any number of Ids to the set. */ addIds(ids) { for (const id of Id64.iterable(ids)) this.addId(id); } /** Returns true if the set contains the specified Id. */ hasId(id) { return this.has(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Add an Id to the set. */ add(low, high) { let set = this._map.get(high); if (undefined === set) { set = new Set(); this._map.set(high, set); } set.add(low); } /** Remove an Id from the set. */ deleteId(id) { this.delete(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Remove any number of Ids from the set. */ deleteIds(ids) { for (const id of Id64.iterable(ids)) this.deleteId(id); } /** Remove an Id from the set. */ delete(low, high) { const set = this._map.get(high); if (undefined !== set) { set.delete(low); if (set.size === 0) this._map.delete(high); } } /** Returns true if the set contains the specified Id. */ has(low, high) { const set = this._map.get(high); return undefined !== set && set.has(low); } /** Returns true if the set contains the Id specified by `pair`. */ hasPair(pair) { return this.has(pair.lower, pair.upper); } /** Returns true if the set contains no Ids. */ get isEmpty() { return 0 === this._map.size; } /** Returns the number of Ids contained in the set. */ get size() { 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. */ 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. */ toId64Set() { const ids = new Set(); 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. */ forEach(func) { for (const entry of this._map) for (const lo of entry[1]) func(lo, entry[0]); } } Id64.Uint32Set = Uint32Set; /** A specialized replacement for Map<Id64String, T> optimized for performance-critical code. * @see [[Id64.Uint32Set]] for implementation details. * @public */ class Uint32Map { _map = new Map(); /** Remove all entries from the map. */ clear() { this._map.clear(); } /** Find an entry in the map by Id. */ getById(id) { return this.get(Id64.getLowerUint32(id), Id64.getUpperUint32(id)); } /** Set an entry in the map by Id. */ setById(id, value) { this.set(Id64.getLowerUint32(id), Id64.getUpperUint32(id), value); } /** Set an entry in the map by Id components. */ set(low, high, value) { let map = this._map.get(high); if (undefined === map) { map = new Map(); this._map.set(high, map); } map.set(low, value); } /** Get an entry from the map by Id components. */ get(low, high) { const map = this._map.get(high); return undefined !== map ? map.get(low) : undefined; } /** Returns true if the map contains no entries. */ get isEmpty() { return 0 === this._map.size; } /** Returns the number of entries in the map. */ get size() { let size = 0; for (const entry of this._map) size += entry[1].size; return size; } /** Execute a function against each entry in this map. */ forEach(func) { for (const outerEntry of this._map) for (const innerEntry of outerEntry[1]) func(innerEntry[0], outerEntry[0], innerEntry[1]); } } Id64.Uint32Map = Uint32Map; })(Id64 || (exports.Id64 = Id64 = {})); function validateLocalId(num) { if (num < 0 || Math.round(num) !== num) { throw new Error("Local Id must be a non-negative integer"); } } /** * Generates unique [[Id64String]] values in sequence, which are guaranteed not to conflict with Ids associated with persistent elements or models. * This is useful for associating stable, non-persistent identifiers with things like [Decorator]($frontend)s. * A TransientIdSequence can generate a maximum of (2^40)-2 unique Ids. * @public */ class TransientIdSequence { /** The starting local Id provided to the constructor. The sequence begins at `initialLocalId + 1`. */ initialLocalId; _localId; /** Constructor. * @param initialLocalId The starting local Id. The local Id of the first [[Id64String]] generated by [[getNext]] will be `initialLocalId + 1`. */ constructor(initialLocalId = 0) { validateLocalId(initialLocalId); this.initialLocalId = initialLocalId; this._localId = initialLocalId; } /** The maximum local Id generated by the sequence thus far. It is never less than [[initialLocalId]]. If it is equal to [[initialLocalId]], then the sequence has * not yet generated any Ids. * Each call to [[getNext]] increments this by 1 and uses it as the local Id of the generated [[Id64String]]. */ get currentLocalId() { return this._localId; } /** Generate and return the next transient Id64String in the sequence. */ getNext() { return Id64.fromLocalAndBriefcaseIds(++this._localId, 0xffffff); } /** Preview the transient Id64String that will be returned by the next call to [[getNext]]. * This is primarily useful for tests. */ peekNext() { return Id64.fromLocalAndBriefcaseIds(this._localId + 1, 0xffffff); } /** Convert this sequence to its JSON representation. */ toJSON() { return { initialLocalId: this.initialLocalId, currentLocalId: this.currentLocalId, }; } /** Create a sequence from its JSON representation. */ static fromJSON(props) { validateLocalId(props.currentLocalId); const sequence = new TransientIdSequence(props.initialLocalId); sequence._localId = props.currentLocalId; return sequence; } /** Obtain the JSON representation of a new sequence that diverges from this sequence, with its [[initialLocalId]] set to this sequence's [[currentLocalId]]. * The two sequences can generate Ids independently. Later, you can [[merge]] the sequences, resolving conflicts where the two sequences generated identical Ids. * This is chiefly useful when generating transient Ids on a [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker). */ fork() { return { initialLocalId: this.currentLocalId, currentLocalId: this.currentLocalId, }; } /** Integrate the Ids generated by a [[fork]] of this sequence. All of the Ids generated by `source` will be remapped to Ids at the end of this sequence. * This is chiefly useful when generating transient Ids on a [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker). * @param source The JSON representation of the [[fork]]ed sequence to be merged with this one. * @returns a function that permits you to remap the local Ids generated by `source` into the corresponding local Ids assigned by this sequence. * @throws Error if `source` is not a fork of this sequence or is malformed (e.g., contains negative and/or non-integer local Ids). */ merge(source) { const { initialLocalId, currentLocalId } = source; validateLocalId(initialLocalId); validateLocalId(currentLocalId); if (initialLocalId > this.currentLocalId) { throw new Error("Transient Id sequences do not intersect"); } if (initialLocalId > currentLocalId) { throw new Error("Current local Id cannot be less than initial local Id"); } const delta = this.currentLocalId - initialLocalId; this._localId += currentLocalId - initialLocalId; return (sourceLocalId) => { if (sourceLocalId > initialLocalId && sourceLocalId <= currentLocalId) { return sourceLocalId + delta; } return sourceLocalId; }; } } exports.TransientIdSequence = TransientIdSequence; /** * The Guid namespace provides facilities for working with GUID strings using the "8-4-4-4-12" pattern. * * The [[GuidString]] type alias is used to indicate function arguments, return types, and variables which are known to * be in the GUID format. * @public */ var Guid; (function (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 */ Guid.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. */ function isGuid(value) { return uuidPattern.test(value); } Guid.isGuid = isGuid; /** Determine whether the input string is a valid V4 Guid string */ 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); } Guid.isV4Guid = isV4Guid; /** Create a new V4 Guid value */ function createValue() { // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript 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); }); } Guid.createValue = createValue; /** * Normalize a Guid string if possible. Normalization consists of: * - Convert all characters to lower case * - Trim any leading or trailing whitespace * - Convert to the standard Guid format "8-4-4-4-12", repositioning the '-' characters as necessary, presuming there are exactly 32 hexadecimal digits. * @param value Input value that represents a Guid * @returns Normalized representation of the Guid string. If the normalization fails, return the *original* value unmodified (Note: it is *not* a valid Guid) */ function normalize(value) { const lowerValue = value.toLowerCase().trim(); // Return if it's already formatted to be a Guid if (isGuid(lowerValue)) return lowerValue; // Remove any existing "-" characters and position them properly, if there remains exactly 32 hexadecimal digits 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 unmodified string - (note: it is *not* a valid Guid) return value; } Guid.normalize = normalize; })(Guid || (exports.Guid = Guid = {})); //# sourceMappingURL=Id.js.map