@microsoft/applicationinsights-core-js
Version:
Microsoft Application Insights Core Javascript SDK
461 lines (459 loc) • 20.4 kB
JavaScript
/*
* Application Insights JavaScript SDK - Core, 3.3.6
* Copyright (c) Microsoft and contributors. All rights reserved.
*/
import { ObjAssign, ObjClass } from "@microsoft/applicationinsights-shims";
import { arrForEach, asString as asString21, isArray, isBoolean, isError, isFunction, isNullOrUndefined, isNumber, isObject, isPlainObject, isString, isUndefined, objDeepFreeze, objDefine, objForEachKey, objHasOwn, strIndexOf, strTrim } from "@nevware21/ts-utils";
import { _DYN_APPLY, _DYN_GET_ALL_RESPONSE_HEA5, _DYN_LENGTH, _DYN_NAME, _DYN_REPLACE, _DYN_SPLIT, _DYN_STATUS, _DYN_TO_LOWER_CASE } from "../__DynamicConstants";
import { STR_EMPTY } from "./InternalConstants";
// RESTRICT and AVOID circular dependencies you should not import other contained modules or export the contents of this file directly
// Added to help with minification
var strGetPrototypeOf = "getPrototypeOf";
var rCamelCase = /-([a-z])/g;
var rNormalizeInvalid = /([^\w\d_$])/g;
var rLeadingNumeric = /^(\d+[\w\d_$])/;
export var _getObjProto = Object[strGetPrototypeOf];
export function isNotUndefined(value) {
return !isUndefined(value);
}
export function isNotNullOrUndefined(value) {
return !isNullOrUndefined(value);
}
/**
* Validates that the string name conforms to the JS IdentifierName specification and if not
* normalizes the name so that it would. This method does not identify or change any keywords
* meaning that if you pass in a known keyword the same value will be returned.
* This is a simplified version
* @param name - The name to validate
*/
export function normalizeJsName(name) {
var value = name;
if (value && isString(value)) {
// CamelCase everything after the "-" and remove the dash
value = value[_DYN_REPLACE /* @min:%2ereplace */](rCamelCase, function (_all, letter) {
return letter.toUpperCase();
});
value = value[_DYN_REPLACE /* @min:%2ereplace */](rNormalizeInvalid, "_");
value = value[_DYN_REPLACE /* @min:%2ereplace */](rLeadingNumeric, function (_all, match) {
return "_" + match;
});
}
return value;
}
/**
* A simple wrapper (for minification support) to check if the value contains the search string.
* @param value - The string value to check for the existence of the search value
* @param search - The value search within the value
*/
export function strContains(value, search) {
if (value && search) {
return strIndexOf(value, search) !== -1;
}
return false;
}
/**
* Convert a date to I.S.O. format in IE8
*/
export function toISOString(date) {
return date && date.toISOString() || "";
}
export var deepFreeze = objDeepFreeze;
/**
* Returns the name of object if it's an Error. Otherwise, returns empty string.
*/
export function getExceptionName(object) {
if (isError(object)) {
return object[_DYN_NAME /* @min:%2ename */];
}
return STR_EMPTY;
}
/**
* Sets the provided value on the target instance using the field name when the provided chk function returns true, the chk
* function will only be called if the new value is no equal to the original value.
* @param target - The target object
* @param field - The key of the target
* @param value - The value to set
* @param valChk - [Optional] Callback to check the value that if supplied will be called check if the new value can be set
* @param srcChk - [Optional] Callback to check to original value that if supplied will be called if the new value should be set (if allowed)
* @returns The existing or new value, depending what was set
*/
export function setValue(target, field, value, valChk, srcChk) {
var theValue = value;
if (target) {
theValue = target[field];
if (theValue !== value && (!srcChk || srcChk(theValue)) && (!valChk || valChk(value))) {
theValue = value;
target[field] = theValue;
}
}
return theValue;
}
/**
* Returns the current value from the target object if not null or undefined otherwise sets the new value and returns it
* @param target - The target object to return or set the default value
* @param field - The key for the field to set on the target
* @param defValue - [Optional] The value to set if not already present, when not provided a empty object will be added
*/
export function getSetValue(target, field, defValue) {
var theValue;
if (target) {
theValue = target[field];
if (!theValue && isNullOrUndefined(theValue)) {
// Supports having the default as null
theValue = !isUndefined(defValue) ? defValue : {};
target[field] = theValue;
}
}
else {
// Expanded for performance so we only check defValue if required
theValue = !isUndefined(defValue) ? defValue : {};
}
return theValue;
}
function _createProxyFunction(source, funcName) {
var srcFunc = null;
var src = null;
if (isFunction(source)) {
srcFunc = source;
}
else {
src = source;
}
return function () {
// Capture the original arguments passed to the method
var originalArguments = arguments;
if (srcFunc) {
src = srcFunc();
}
if (src) {
return src[funcName][_DYN_APPLY /* @min:%2eapply */](src, originalArguments);
}
};
}
/**
* Effectively assigns all enumerable properties (not just own properties) and functions (including inherited prototype) from
* the source object to the target, it attempts to use proxy getters / setters (if possible) and proxy functions to avoid potential
* implementation issues by assigning prototype functions as instance ones
*
* This method is the primary method used to "update" the snippet proxy with the ultimate implementations.
*
* Special ES3 Notes:
* Updates (setting) of direct property values on the target or indirectly on the source object WILL NOT WORK PROPERLY, updates to the
* properties of "referenced" object will work (target.context.newValue = 10 =\> will be reflected in the source.context as it's the
* same object). ES3 Failures: assigning target.myProp = 3 -\> Won't change source.myProp = 3, likewise the reverse would also fail.
* @param target - The target object to be assigned with the source properties and functions
* @param source - The source object which will be assigned / called by setting / calling the targets proxies
* @param chkSet - An optional callback to determine whether a specific property/function should be proxied
*/
export function proxyAssign(target, source, chkSet) {
if (target && source && isObject(target) && isObject(source)) {
var _loop_1 = function (field) {
if (isString(field)) {
var value = source[field];
if (isFunction(value)) {
if (!chkSet || chkSet(field, true, source, target)) {
// Create a proxy function rather than just copying the (possible) prototype to the new object as an instance function
target[field] = _createProxyFunction(source, field);
}
}
else if (!chkSet || chkSet(field, false, source, target)) {
if (objHasOwn(target, field)) {
// Remove any previous instance property
delete target[field];
}
objDefine(target, field, {
g: function () {
return source[field];
},
s: function (theValue) {
source[field] = theValue;
}
});
}
}
};
// effectively apply/proxy full source to the target instance
for (var field in source) {
_loop_1(field);
}
}
return target;
}
/**
* Creates a proxy function on the target which internally will call the source version with all arguments passed to the target method.
*
* @param target - The target object to be assigned with the source properties and functions
* @param name - The function name that will be added on the target
* @param source - The source object which will be assigned / called by setting / calling the targets proxies
* @param theFunc - The function name on the source that will be proxied on the target
* @param overwriteTarget - If `false` this will not replace any pre-existing name otherwise (the default) it will overwrite any existing name
*/
export function proxyFunctionAs(target, name, source, theFunc, overwriteTarget) {
if (target && name && source) {
if (overwriteTarget !== false || isUndefined(target[name])) {
target[name] = _createProxyFunction(source, theFunc);
}
}
}
/**
* Creates proxy functions on the target which internally will call the source version with all arguments passed to the target method.
*
* @param target - The target object to be assigned with the source properties and functions
* @param source - The source object which will be assigned / called by setting / calling the targets proxies
* @param functionsToProxy - An array of function names that will be proxied on the target
* @param overwriteTarget - If false this will not replace any pre-existing name otherwise (the default) it will overwrite any existing name
*/
export function proxyFunctions(target, source, functionsToProxy, overwriteTarget) {
if (target && source && isObject(target) && isArray(functionsToProxy)) {
arrForEach(functionsToProxy, function (theFuncName) {
if (isString(theFuncName)) {
proxyFunctionAs(target, theFuncName, source, theFuncName, overwriteTarget);
}
});
}
return target;
}
/**
* Simpler helper to create a dynamic class that implements the interface and populates the values with the defaults.
* Only instance properties (hasOwnProperty) values are copied from the defaults to the new instance
* @param defaults - Simple helper
*/
export function createClassFromInterface(defaults) {
return /** @class */ (function () {
function class_1() {
var _this = this;
if (defaults) {
objForEachKey(defaults, function (field, value) {
_this[field] = value;
});
}
}
return class_1;
}());
}
/**
* A helper function to assist with JIT performance for objects that have properties added / removed dynamically
* this is primarily for chromium based browsers and has limited effects on Firefox and none of IE. Only call this
* function after you have finished "updating" the object, calling this within loops reduces or defeats the benefits.
* This helps when iterating using for..in, objKeys() and objForEach()
* @param theObject - The object to be optimized if possible
*/
export function optimizeObject(theObject) {
// V8 Optimization to cause the JIT compiler to create a new optimized object for looking up the own properties
// primarily for object with <= 19 properties for >= 20 the effect is reduced or non-existent
if (theObject && ObjAssign) {
theObject = ObjClass(ObjAssign({}, theObject));
}
return theObject;
}
export function objExtend(obj1, obj2, obj3, obj4, obj5, obj6) {
// Variables
var theArgs = arguments;
var extended = theArgs[0] || {};
var argLen = theArgs[_DYN_LENGTH /* @min:%2elength */];
var deep = false;
var idx = 1;
// Check for "Deep" flag
if (argLen > 0 && isBoolean(extended)) {
deep = extended;
extended = theArgs[idx] || {};
idx++;
}
// Handle case when target is a string or something (possible in deep copy)
if (!isObject(extended)) {
extended = {};
}
// Loop through each remaining object and conduct a merge
for (; idx < argLen; idx++) {
var arg = theArgs[idx];
var isArgArray = isArray(arg);
var isArgObj = isObject(arg);
for (var prop in arg) {
var propOk = (isArgArray && (prop in arg)) || (isArgObj && objHasOwn(arg, prop));
if (!propOk) {
continue;
}
var newValue = arg[prop];
var isNewArray = void 0;
// If deep merge and property is an object, merge properties
if (deep && newValue && ((isNewArray = isArray(newValue)) || isPlainObject(newValue))) {
// Grab the current value of the extended object
var clone = extended[prop];
if (isNewArray) {
if (!isArray(clone)) {
// We can't "merge" an array with a non-array so overwrite the original
clone = [];
}
}
else if (!isPlainObject(clone)) {
// We can't "merge" an object with a non-object
clone = {};
}
// Never move the original objects always clone them
newValue = objExtend(deep, clone, newValue);
}
// Assign the new (or previous) value (unless undefined)
if (newValue !== undefined) {
extended[prop] = newValue;
}
}
}
return extended;
}
export var asString = asString21;
export function isFeatureEnabled(feature, cfg) {
var rlt = false;
var ft = cfg && cfg.featureOptIn && cfg.featureOptIn[feature];
if (feature && ft) {
var mode = ft.mode;
// NOTE: None will be considered as true
rlt = (mode == 3 /* FeatureOptInMode.enable */) || (mode == 1 /* FeatureOptInMode.none */);
}
return rlt;
}
export function getResponseText(xhr) {
try {
return xhr.responseText;
}
catch (e) {
// Best effort, as XHR may throw while XDR wont so just ignore
}
return null;
}
export function formatErrorMessageXdr(xdr, message) {
if (xdr) {
return "XDomainRequest,Response:" + getResponseText(xdr) || "";
}
return message;
}
export function formatErrorMessageXhr(xhr, message) {
if (xhr) {
return "XMLHttpRequest,Status:" + xhr[_DYN_STATUS /* @min:%2estatus */] + ",Response:" + getResponseText(xhr) || xhr.response || "";
}
return message;
}
export function prependTransports(theTransports, newTransports) {
if (newTransports) {
if (isNumber(newTransports)) {
theTransports = [newTransports].concat(theTransports);
}
else if (isArray(newTransports)) {
theTransports = newTransports.concat(theTransports);
}
}
return theTransports;
}
var strDisabledPropertyName = "Microsoft_ApplicationInsights_BypassAjaxInstrumentation";
var strWithCredentials = "withCredentials";
var strTimeout = "timeout";
/**
* Create and open an XMLHttpRequest object
* @param method - The request method
* @param urlString - The url
* @param withCredentials - Option flag indicating that credentials should be sent
* @param disabled - Optional flag indicating that the XHR object should be marked as disabled and not tracked (default is false)
* @param isSync - Optional flag indicating if the instance should be a synchronous request (defaults to false)
* @param timeout - Optional value identifying the timeout value that should be assigned to the XHR request
* @returns A new opened XHR request
*/
export function openXhr(method, urlString, withCredentials, disabled, isSync, timeout) {
if (disabled === void 0) { disabled = false; }
if (isSync === void 0) { isSync = false; }
function _wrapSetXhrProp(xhr, prop, value) {
try {
xhr[prop] = value;
}
catch (e) {
// - Wrapping as depending on the environment setting the property may fail (non-terminally)
}
}
var xhr = new XMLHttpRequest();
if (disabled) {
// Tag the instance so it's not tracked (trackDependency)
// If the environment has locked down the XMLHttpRequest (preventExtensions and/or freeze), this would
// cause the request to fail and we no telemetry would be sent
_wrapSetXhrProp(xhr, strDisabledPropertyName, disabled);
}
if (withCredentials) {
// Some libraries require that the withCredentials flag is set "before" open and
// - Wrapping as IE 10 has started throwing when setting before open
_wrapSetXhrProp(xhr, strWithCredentials, withCredentials);
}
xhr.open(method, urlString, !isSync);
if (withCredentials) {
// withCredentials should be set AFTER open (https://xhr.spec.whatwg.org/#the-withcredentials-attribute)
// And older firefox instances from 11+ will throw for sync events (current versions don't) which happens during unload processing
_wrapSetXhrProp(xhr, strWithCredentials, withCredentials);
}
// Only set the timeout for asynchronous requests as
// "Timeout shouldn't be used for synchronous XMLHttpRequests requests used in a document environment or it will throw an InvalidAccessError exception.""
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout
if (!isSync && timeout) {
_wrapSetXhrProp(xhr, strTimeout, timeout);
}
return xhr;
}
/**
* Converts the XHR getAllResponseHeaders to a map containing the header key and value.
* @internal
*/
// tslint:disable-next-line: align
export function convertAllHeadersToMap(headersString) {
var headers = {};
if (isString(headersString)) {
var headersArray = strTrim(headersString)[_DYN_SPLIT /* @min:%2esplit */](/[\r\n]+/);
arrForEach(headersArray, function (headerEntry) {
if (headerEntry) {
var idx = headerEntry.indexOf(": ");
if (idx !== -1) {
// The new spec has the headers returning all as lowercase -- but not all browsers do this yet
var header = strTrim(headerEntry.substring(0, idx))[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]();
var value = strTrim(headerEntry.substring(idx + 1));
headers[header] = value;
}
else {
headers[strTrim(headerEntry)] = 1;
}
}
});
}
return headers;
}
/**
* append the XHR headers.
* @internal
*/
export function _appendHeader(theHeaders, xhr, name) {
if (!theHeaders[name] && xhr && xhr.getResponseHeader) {
var value = xhr.getResponseHeader(name);
if (value) {
theHeaders[name] = strTrim(value);
}
}
return theHeaders;
}
var STR_KILL_DURATION_HEADER = "kill-duration";
var STR_KILL_DURATION_SECONDS_HEADER = "kill-duration-seconds";
var STR_TIME_DELTA_HEADER = "time-delta-millis";
/**
* get the XHR getAllResponseHeaders.
* @internal
*/
export function _getAllResponseHeaders(xhr, isOneDs) {
var theHeaders = {};
if (!xhr[_DYN_GET_ALL_RESPONSE_HEA5 /* @min:%2egetAllResponseHeaders */]) {
// Firefox 2-63 doesn't have getAllResponseHeaders function but it does have getResponseHeader
// Only call these if getAllResponseHeaders doesn't exist, otherwise we can get invalid response errors
// as collector is not currently returning the correct header to allow JS to access these headers
if (!!isOneDs) {
theHeaders = _appendHeader(theHeaders, xhr, STR_TIME_DELTA_HEADER);
theHeaders = _appendHeader(theHeaders, xhr, STR_KILL_DURATION_HEADER);
theHeaders = _appendHeader(theHeaders, xhr, STR_KILL_DURATION_SECONDS_HEADER);
}
}
else {
theHeaders = convertAllHeadersToMap(xhr[_DYN_GET_ALL_RESPONSE_HEA5 /* @min:%2egetAllResponseHeaders */]());
}
return theHeaders;
}
//# sourceMappingURL=HelperFuncs.js.map