@microsoft/applicationinsights-core-js
Version:
Microsoft Application Insights Core Javascript SDK
213 lines • 9.43 kB
JavaScript
/*
* Application Insights JavaScript SDK - Core, 3.3.6
* Copyright (c) Microsoft and contributors. All rights reserved.
*/
import { arrForEach, isArray, isString, strLeft, strTrim } from "@nevware21/ts-utils";
import { _DYN_GET_ATTRIBUTE, _DYN_LENGTH, _DYN_PUSH, _DYN_SPLIT, _DYN_TO_LOWER_CASE, _DYN_TRACE_FLAGS, _DYN_VERSION } from "../__DynamicConstants";
import { generateW3CId } from "./CoreUtils";
import { findMetaTag, findNamedServerTiming } from "./EnvUtils";
import { STR_EMPTY } from "./InternalConstants";
// using {0,16} for leading and trailing whitespace just to constrain the possible runtime of a random string
var TRACE_PARENT_REGEX = /^([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})(-[^\s]{1,64})?$/i;
var DEFAULT_VERSION = "00";
var INVALID_VERSION = "ff";
var INVALID_TRACE_ID = "00000000000000000000000000000000";
var INVALID_SPAN_ID = "0000000000000000";
var SAMPLED_FLAG = 0x01;
function _isValid(value, len, invalidValue) {
if (value && value[_DYN_LENGTH /* @min:%2elength */] === len && value !== invalidValue) {
return !!value.match(/^[\da-f]*$/i);
}
return false;
}
function _formatValue(value, len, defValue) {
if (_isValid(value, len)) {
return value;
}
return defValue;
}
function _formatFlags(value) {
if (isNaN(value) || value < 0 || value > 255) {
value = 0x01;
}
var result = value.toString(16);
while (result[_DYN_LENGTH /* @min:%2elength */] < 2) {
result = "0" + result;
}
return result;
}
/**
* Create a new ITraceParent instance using the provided values.
* @param traceId - The traceId to use, when invalid a new random W3C id will be generated.
* @param spanId - The parent/span id to use, a new random value will be generated if it is invalid.
* @param flags - The traceFlags to use, defaults to zero (0) if not supplied or invalid
* @param version - The version to used, defaults to version "01" if not supplied or invalid.
* @returns
*/
export function createTraceParent(traceId, spanId, flags, version) {
return {
version: _isValid(version, 2, INVALID_VERSION) ? version : DEFAULT_VERSION,
traceId: isValidTraceId(traceId) ? traceId : generateW3CId(),
spanId: isValidSpanId(spanId) ? spanId : strLeft(generateW3CId(), 16),
traceFlags: flags >= 0 && flags <= 0xFF ? flags : 1
};
}
/**
* Attempt to parse the provided string as a W3C TraceParent header value (https://www.w3.org/TR/trace-context/#traceparent-header)
*
* @param value - The value to be parsed
* @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first
* @returns
*/
export function parseTraceParent(value, selectIdx) {
if (!value) {
// Don't pass a null/undefined or empty string
return null;
}
if (isArray(value)) {
// The value may have been encoded on the page into an array so handle this automatically
value = value[0] || "";
}
if (!value || !isString(value) || value[_DYN_LENGTH /* @min:%2elength */] > 8192) {
// limit potential processing based on total length
return null;
}
if (value.indexOf(",") !== -1) {
var values = value[_DYN_SPLIT /* @min:%2esplit */](",");
value = values[selectIdx > 0 && values[_DYN_LENGTH /* @min:%2elength */] > selectIdx ? selectIdx : 0];
}
// See https://www.w3.org/TR/trace-context/#versioning-of-traceparent
var match = TRACE_PARENT_REGEX.exec(strTrim(value));
if (!match || // No match
match[1] === INVALID_VERSION || // version ff is forbidden
match[2] === INVALID_TRACE_ID || // All zeros is considered to be invalid
match[3] === INVALID_SPAN_ID) { // All zeros is considered to be invalid
return null;
}
return {
version: (match[1] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](),
traceId: (match[2] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](),
spanId: (match[3] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](),
traceFlags: parseInt(match[4], 16)
};
}
/**
* Is the provided W3c Trace Id a valid string representation, it must be a 32-character string
* of lowercase hexadecimal characters for example, 4bf92f3577b34da6a3ce929d0e0e4736.
* If all characters as zero (00000000000000000000000000000000) it will be considered an invalid value.
* @param value - The W3c trace Id to be validated
* @returns true if valid otherwise false
*/
export function isValidTraceId(value) {
return _isValid(value, 32, INVALID_TRACE_ID);
}
/**
* Is the provided W3c span id (aka. parent id) a valid string representation, it must be a 16-character
* string of lowercase hexadecimal characters, for example, 00f067aa0ba902b7.
* If all characters are zero (0000000000000000) this is considered an invalid value.
* @param value - The W3c span id to be validated
* @returns true if valid otherwise false
*/
export function isValidSpanId(value) {
return _isValid(value, 16, INVALID_SPAN_ID);
}
/**
* Validates that the provided ITraceParent instance conforms to the currently supported specifications
* @param value - The parsed traceParent value
* @returns
*/
export function isValidTraceParent(value) {
if (!value ||
!_isValid(value[_DYN_VERSION /* @min:%2eversion */], 2, INVALID_VERSION) ||
!_isValid(value.traceId, 32, INVALID_TRACE_ID) ||
!_isValid(value.spanId, 16, INVALID_SPAN_ID) ||
!_isValid(_formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]), 2)) {
// Each known field must contain a valid value
return false;
}
return true;
}
/**
* Is the parsed traceParent indicating that the trace is currently sampled.
* @param value - The parsed traceParent value
* @returns
*/
export function isSampledFlag(value) {
if (isValidTraceParent(value)) {
return (value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */] & SAMPLED_FLAG) === SAMPLED_FLAG;
}
return false;
}
/**
* Format the ITraceParent value as a string using the supported and know version formats.
* So even if the passed traceParent is a later version the string value returned from this
* function will convert it to only the known version formats.
* This currently only supports version "00" and invalid "ff"
* @param value - The parsed traceParent value
* @returns
*/
export function formatTraceParent(value) {
if (value) {
// Special Note: This only supports formatting as version 00, future versions should encode any known supported version
// So parsing a future version will populate the correct version value but reformatting will reduce it to version 00.
var flags = _formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]);
if (!_isValid(flags, 2)) {
flags = "01";
}
var version = value[_DYN_VERSION /* @min:%2eversion */] || DEFAULT_VERSION;
if (version !== "00" && version !== "ff") {
// Reduce version to "00"
version = DEFAULT_VERSION;
}
// Format as version 00
return "".concat(version.toLowerCase(), "-").concat(_formatValue(value.traceId, 32, INVALID_TRACE_ID).toLowerCase(), "-").concat(_formatValue(value.spanId, 16, INVALID_SPAN_ID).toLowerCase(), "-").concat(flags.toLowerCase());
}
return "";
}
/**
* Helper function to fetch the passed traceparent from the page, looking for it as a meta-tag or a Server-Timing header.
* @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first
* @returns
*/
export function findW3cTraceParent(selectIdx) {
var name = "traceparent";
var traceParent = parseTraceParent(findMetaTag(name), selectIdx);
if (!traceParent) {
traceParent = parseTraceParent(findNamedServerTiming(name), selectIdx);
}
return traceParent;
}
/**
* Find all script tags in the provided document and return the information about them.
* @param doc - The document to search for script tags
* @returns
*/
export function findAllScripts(doc) {
var scripts = doc.getElementsByTagName("script");
var result = [];
arrForEach(scripts, function (script) {
var src = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("src");
if (src) {
var crossOrigin = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("crossorigin");
var async = script.hasAttribute("async") === true;
var defer = script.hasAttribute("defer") === true;
var referrerPolicy = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("referrerpolicy");
var info = { url: src };
if (crossOrigin) {
info.crossOrigin = crossOrigin;
}
if (async) {
info.async = async;
}
if (defer) {
info.defer = defer;
}
if (referrerPolicy) {
info.referrerPolicy = referrerPolicy;
}
result[_DYN_PUSH /* @min:%2epush */](info);
}
});
return result;
}
//# sourceMappingURL=W3cTraceParent.js.map