@itwin/core-bentley
Version:
Bentley JavaScript core components
729 lines • 30.8 kB
JavaScript
"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