@microsoft/applicationinsights-core-js
Version:
Microsoft Application Insights Core Javascript SDK
540 lines (537 loc) • 28.6 kB
JavaScript
/*
* Application Insights JavaScript SDK - Core, 3.3.6
* Copyright (c) Microsoft and contributors. All rights reserved.
*/
import dynamicProto from "@microsoft/dynamicproto-js";
import { createPromise, doAwaitResponse } from "@nevware21/ts-async";
import { arrForEach, dumpObj, getNavigator, getWindow, isFunction, objKeys } from "@nevware21/ts-utils";
import { _DYN_DATA, _DYN_HEADERS, _DYN_INITIALIZE, _DYN_LENGTH, _DYN_MESSAGE, _DYN_REPLACE, _DYN_STATUS, _DYN_TIMEOUT, _DYN_TO_LOWER_CASE, _DYN_URL_STRING, _DYN_VALUE, _DYN__DO_TEARDOWN } from "../__DynamicConstants";
import { DisabledPropertyName } from "./Constants";
import { _throwInternal, _warnToConsole } from "./DiagnosticLogger";
import { getLocation, isBeaconsSupported, isFetchSupported, isXhrSupported, useXDomainRequest } from "./EnvUtils";
import { _getAllResponseHeaders, formatErrorMessageXdr, formatErrorMessageXhr, getResponseText, openXhr } from "./HelperFuncs";
var STR_EMPTY = "";
var STR_NO_RESPONSE_BODY = "NoResponseBody";
var _noResponseQs = "&" + STR_NO_RESPONSE_BODY + "=true";
var STR_POST_METHOD = "POST";
/**
* This Internal component
* Manager SendPost functions
* SendPostManger
* @internal for internal use only
*/
var SenderPostManager = /** @class */ (function () {
function SenderPostManager() {
var _syncFetchPayload = 0; // Keep track of the outstanding sync fetch payload total (as sync fetch has limits)
var _enableSendPromise;
var _isInitialized;
var _diagLog;
var _isOneDs;
var _onCompleteFuncs;
var _disableCredentials;
var _fetchCredentials;
var _fallbackInst;
var _disableXhr;
var _disableBeacon;
var _disableBeaconSync;
var _disableFetchKeepAlive;
var _addNoResponse;
var _timeoutWrapper;
dynamicProto(SenderPostManager, this, function (_self, _base) {
var _sendCredentials = true; // for 1ds
_initDefaults();
_self[_DYN_INITIALIZE /* @min:%2einitialize */] = function (config, diagLog) {
_diagLog = diagLog;
if (_isInitialized) {
_throwInternal(_diagLog, 1 /* eLoggingSeverity.CRITICAL */, 28 /* _eInternalMessageId.SenderNotInitialized */, "Sender is already initialized");
}
_self.SetConfig(config);
_isInitialized = true;
};
_self["_getDbgPlgTargets"] = function () {
return [_isInitialized, _isOneDs, _disableCredentials, _enableSendPromise];
};
// This componet might get its config from sender, offline sender, 1ds post
// so set this function to mock dynamic changes
_self.SetConfig = function (config) {
try {
_onCompleteFuncs = config.senderOnCompleteCallBack || {};
_disableCredentials = !!config.disableCredentials;
_fetchCredentials = config.fetchCredentials;
_isOneDs = !!config.isOneDs;
_enableSendPromise = !!config.enableSendPromise;
_disableXhr = !!config.disableXhr;
_disableBeacon = !!config.disableBeacon;
_disableBeaconSync = !!config.disableBeaconSync;
_timeoutWrapper = config.timeWrapper;
_addNoResponse = !!config.addNoResponse;
_disableFetchKeepAlive = !!config.disableFetchKeepAlive;
_fallbackInst = { sendPOST: _xhrSender };
if (!_isOneDs) {
_sendCredentials = false; // for appInsights, set it to false always
}
if (_disableCredentials) {
var location_1 = getLocation();
if (location_1 && location_1.protocol && location_1.protocol[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */]() === "file:") {
// Special case where a local html file fails with a CORS error on Chromium browsers
_sendCredentials = false;
}
}
return true;
}
catch (e) {
// eslint-disable-next-line no-empty
}
return false;
};
_self.getSyncFetchPayload = function () {
return _syncFetchPayload;
};
_self.getSenderInst = function (transports, sync) {
if (transports && transports[_DYN_LENGTH /* @min:%2elength */]) {
return _getSenderInterface(transports, sync);
}
return null;
};
_self.getFallbackInst = function () {
return _fallbackInst;
};
_self[_DYN__DO_TEARDOWN /* @min:%2e_doTeardown */] = function (unloadCtx, unloadState) {
_initDefaults();
};
/**
* success handler
*/
function _onSuccess(res, onComplete) {
_doOnComplete(onComplete, 200, {}, res);
}
/**
* error handler
*/
function _onError(message, onComplete) {
_throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 26 /* _eInternalMessageId.OnError */, "Failed to send telemetry.", { message: message });
_doOnComplete(onComplete, 400, {});
}
function _onNoPayloadUrl(onComplete) {
_onError("No endpoint url is provided for the batch", onComplete);
}
function _getSenderInterface(transports, syncSupport) {
var transportType = 0 /* TransportType.NotSet */;
var sendPostFunc = null;
var lp = 0;
while (sendPostFunc == null && lp < transports[_DYN_LENGTH /* @min:%2elength */]) {
transportType = transports[lp];
if (!_disableXhr && transportType === 1 /* TransportType.Xhr */) {
if (useXDomainRequest()) {
// IE 8 and 9
sendPostFunc = _xdrSender;
}
else if (isXhrSupported()) {
sendPostFunc = _xhrSender;
}
}
else if (transportType === 2 /* TransportType.Fetch */ && isFetchSupported(syncSupport) && (!syncSupport || !_disableFetchKeepAlive)) {
sendPostFunc = _doFetchSender;
}
else if (transportType === 3 /* TransportType.Beacon */ && isBeaconsSupported() && (syncSupport ? !_disableBeaconSync : !_disableBeacon)) {
sendPostFunc = _beaconSender;
}
lp++;
}
if (sendPostFunc) {
return {
_transport: transportType,
_isSync: syncSupport,
sendPOST: sendPostFunc
};
}
return null;
}
function _doOnComplete(oncomplete, status, headers, response) {
try {
oncomplete && oncomplete(status, headers, response);
}
catch (e) {
// eslint-disable-next-line no-empty
}
}
function _doBeaconSend(payload, oncomplete) {
var nav = getNavigator();
var url = payload[_DYN_URL_STRING /* @min:%2eurlString */];
if (!url) {
_onNoPayloadUrl(oncomplete);
// return true here, because we don't want to retry it with fallback sender
return true;
}
url = payload[_DYN_URL_STRING /* @min:%2eurlString */] + (_addNoResponse ? _noResponseQs : STR_EMPTY);
var data = payload[_DYN_DATA /* @min:%2edata */];
// Chrome only allows CORS-safelisted values for the sendBeacon data argument
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=720283
// Chrome only allows CORS-safelisted values for the sendBeacon data argument
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=720283
var plainTextBatch = _isOneDs ? data : new Blob([data], { type: "text/plain;charset=UTF-8" });
// The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
var queued = nav.sendBeacon(url, plainTextBatch);
return queued;
}
/**
* Send Beacon API request
* @param payload - The data payload to be sent.
* @param sync - not used
* Note: Beacon API does not support custom headers and we are not able to get
* appId from the backend for the correct correlation.
*/
function _beaconSender(payload, oncomplete, sync) {
var data = payload[_DYN_DATA /* @min:%2edata */];
try {
if (data) {
// The sendBeacon method returns true if the user agent is able to successfully queue the data for transfer. Otherwise it returns false.
if (!_doBeaconSend(payload, oncomplete)) {
var onRetry = _onCompleteFuncs && _onCompleteFuncs.beaconOnRetry;
if (onRetry && isFunction(onRetry)) {
onRetry(payload, oncomplete, _doBeaconSend);
}
else {
_fallbackInst && _fallbackInst.sendPOST(payload, oncomplete, true);
_throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 40 /* _eInternalMessageId.TransmissionFailed */, ". " + "Failed to send telemetry with Beacon API, retried with normal sender.");
}
}
else {
// if can send
_onSuccess(STR_EMPTY, oncomplete);
}
}
}
catch (e) {
_isOneDs && _warnToConsole(_diagLog, "Failed to send telemetry using sendBeacon API. Ex:" + dumpObj(e));
_doOnComplete(oncomplete, _isOneDs ? 0 : 400, {}, STR_EMPTY);
}
return;
}
/**
* Send XMLHttpRequest
* @param payload - The data payload to be sent.
* @param sync - Indicates if the request should be sent synchronously
*/
function _xhrSender(payload, oncomplete, sync) {
//let internalPayload = payload as IInternalPayloadData;
var thePromise;
var resolveFunc;
var rejectFunc;
var headers = payload[_DYN_HEADERS /* @min:%2eheaders */] || {};
if (!sync && _enableSendPromise) {
thePromise = createPromise(function (resolve, reject) {
resolveFunc = resolve;
rejectFunc = reject;
});
}
if (_isOneDs && sync && payload.disableXhrSync) {
sync = false;
}
//const xhr = new XMLHttpRequest();
var endPointUrl = payload[_DYN_URL_STRING /* @min:%2eurlString */];
if (!endPointUrl) {
_onNoPayloadUrl(oncomplete);
resolveFunc && resolveFunc(false);
return;
}
var xhr = openXhr(STR_POST_METHOD, endPointUrl, _sendCredentials, true, sync, payload[_DYN_TIMEOUT /* @min:%2etimeout */]);
if (!_isOneDs) {
// application/json should NOT add to 1ds post by default
xhr.setRequestHeader("Content-type", "application/json");
}
arrForEach(objKeys(headers), function (headerName) {
xhr.setRequestHeader(headerName, headers[headerName]);
});
xhr.onreadystatechange = function () {
if (!_isOneDs) {
_doOnReadyFunc(xhr);
if (xhr.readyState === 4) {
resolveFunc && resolveFunc(true);
}
}
};
xhr.onload = function () {
if (_isOneDs) {
_doOnReadyFunc(xhr);
}
};
function _doOnReadyFunc(xhr) {
var onReadyFunc = _onCompleteFuncs && _onCompleteFuncs.xhrOnComplete;
var onReadyFuncExist = onReadyFunc && isFunction(onReadyFunc);
if (onReadyFuncExist) {
onReadyFunc(xhr, oncomplete, payload);
}
else {
var response = getResponseText(xhr);
_doOnComplete(oncomplete, xhr[_DYN_STATUS /* @min:%2estatus */], _getAllResponseHeaders(xhr, _isOneDs), response);
}
}
xhr.onerror = function (event) {
_doOnComplete(oncomplete, _isOneDs ? xhr[_DYN_STATUS /* @min:%2estatus */] : 400, _getAllResponseHeaders(xhr, _isOneDs), _isOneDs ? STR_EMPTY : formatErrorMessageXhr(xhr));
rejectFunc && rejectFunc(event);
};
xhr.ontimeout = function () {
_doOnComplete(oncomplete, _isOneDs ? xhr[_DYN_STATUS /* @min:%2estatus */] : 500, _getAllResponseHeaders(xhr, _isOneDs), _isOneDs ? STR_EMPTY : formatErrorMessageXhr(xhr));
resolveFunc && resolveFunc(false);
};
xhr.send(payload[_DYN_DATA /* @min:%2edata */]);
return thePromise;
}
/**
* Send fetch API request
* @param payload - The data payload to be sent.
* @param sync - For fetch this identifies whether we are "unloading" (false) or a normal request
*/
function _doFetchSender(payload, oncomplete, sync) {
var _a;
var endPointUrl = payload[_DYN_URL_STRING /* @min:%2eurlString */];
var batch = payload[_DYN_DATA /* @min:%2edata */];
var plainTextBatch = _isOneDs ? batch : new Blob([batch], { type: "application/json" });
var thePromise;
var resolveFunc;
var rejectFunc;
var requestHeaders = new Headers();
var batchLength = batch[_DYN_LENGTH /* @min:%2elength */];
var ignoreResponse = false;
var responseHandled = false;
var headers = payload[_DYN_HEADERS /* @min:%2eheaders */] || {};
//TODO: handle time out for 1ds
var init = (_a = {
method: STR_POST_METHOD,
body: plainTextBatch
},
_a[DisabledPropertyName] = true // Mark so we don't attempt to track this request
,
_a);
// Only add headers if there are headers to add, due to issue with some polyfills
if (payload.headers && objKeys(payload.headers)[_DYN_LENGTH /* @min:%2elength */] > 0) {
arrForEach(objKeys(headers), function (headerName) {
requestHeaders.append(headerName, headers[headerName]);
});
init[_DYN_HEADERS /* @min:%2eheaders */] = requestHeaders;
}
if (_fetchCredentials) { // if user passed in this value via post channel (1ds), then use it
init.credentials = _fetchCredentials;
}
else if (_sendCredentials && _isOneDs) {
// for 1ds, Don't send credentials when URL is file://
init.credentials = "include";
}
if (sync) {
init.keepalive = true;
_syncFetchPayload += batchLength;
if (_isOneDs) {
if (payload["_sendReason"] === 2 /* SendRequestReason.Unload */) {
// As a sync request (during unload), it is unlikely that we will get a chance to process the response so
// just like beacon send assume that the events have been accepted and processed
ignoreResponse = true;
if (_addNoResponse) {
endPointUrl += _noResponseQs;
}
}
}
else {
// for appinsights, set to true for all sync request
ignoreResponse = true;
}
}
var request = new Request(endPointUrl, init);
try {
// Also try and tag the request (just in case the value in init is not copied over)
request[DisabledPropertyName] = true;
}
catch (e) {
// 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
}
if (!sync && _enableSendPromise) {
thePromise = createPromise(function (resolve, reject) {
resolveFunc = resolve;
rejectFunc = reject;
});
}
if (!endPointUrl) {
_onNoPayloadUrl(oncomplete);
resolveFunc && resolveFunc(false);
return;
}
function _handleError(res) {
// In case there is an error in the request. Set the status to 0 for 1ds and 400 for appInsights
// so that the events can be retried later.
_doOnComplete(oncomplete, _isOneDs ? 0 : 400, {}, _isOneDs ? STR_EMPTY : res);
}
function _onFetchComplete(response, payload, value) {
var status = response[_DYN_STATUS /* @min:%2estatus */];
var onCompleteFunc = _onCompleteFuncs.fetchOnComplete;
if (onCompleteFunc && isFunction(onCompleteFunc)) {
onCompleteFunc(response, oncomplete, value || STR_EMPTY, payload);
}
else {
_doOnComplete(oncomplete, status, {}, value || STR_EMPTY);
}
}
try {
doAwaitResponse(fetch(_isOneDs ? endPointUrl : request, _isOneDs ? init : null), function (result) {
if (sync) {
_syncFetchPayload -= batchLength;
batchLength = 0;
}
if (!responseHandled) {
responseHandled = true;
if (!result.rejected) {
var response_1 = result[_DYN_VALUE /* @min:%2evalue */];
try {
/**
* The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500.
* Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure
* or if anything prevented the request from completing.
*/
if (!_isOneDs && !response_1.ok) {
// this is for appInsights only
_handleError(response_1.statusText);
resolveFunc && resolveFunc(false);
}
else {
if (_isOneDs && !response_1.body) {
_onFetchComplete(response_1, null, STR_EMPTY);
resolveFunc && resolveFunc(true);
}
else {
doAwaitResponse(response_1.text(), function (resp) {
_onFetchComplete(response_1, payload, resp[_DYN_VALUE /* @min:%2evalue */]);
resolveFunc && resolveFunc(true);
});
}
}
}
catch (e) {
_handleError(dumpObj(e));
rejectFunc && rejectFunc(e);
}
}
else {
_handleError(result.reason && result.reason[_DYN_MESSAGE /* @min:%2emessage */]);
rejectFunc && rejectFunc(result.reason);
}
}
});
}
catch (e) {
if (!responseHandled) {
_handleError(dumpObj(e));
rejectFunc && rejectFunc(e);
}
}
if (ignoreResponse && !responseHandled) {
// Assume success during unload processing as we most likely won't get the response
responseHandled = true;
_doOnComplete(oncomplete, 200, {});
resolveFunc && resolveFunc(true);
}
if (_isOneDs && !responseHandled && payload[_DYN_TIMEOUT /* @min:%2etimeout */] > 0) {
// Simulate timeout
_timeoutWrapper && _timeoutWrapper.set(function () {
if (!responseHandled) {
// Assume a 500 response (which will cause a retry)
responseHandled = true;
_doOnComplete(oncomplete, 500, {});
resolveFunc && resolveFunc(true);
}
}, payload[_DYN_TIMEOUT /* @min:%2etimeout */]);
}
return thePromise;
}
/**
* Send XDomainRequest
* @param payload - The data payload to be sent.
* @param sync - Indicates if the request should be sent synchronously
*
* Note: XDomainRequest does not support sync requests. This 'isAsync' parameter is added
* to maintain consistency with the xhrSender's contract
* Note: XDomainRequest does not support custom headers and we are not able to get
* appId from the backend for the correct correlation.
*/
function _xdrSender(payload, oncomplete, sync) {
// It doesn't support custom headers, so no action is taken with current requestHeaders
var _window = getWindow();
var xdr = new XDomainRequest();
var data = payload[_DYN_DATA /* @min:%2edata */];
xdr.onload = function () {
var response = getResponseText(xdr);
var onloadFunc = _onCompleteFuncs && _onCompleteFuncs.xdrOnComplete;
if (onloadFunc && isFunction(onloadFunc)) {
onloadFunc(xdr, oncomplete, payload);
}
else {
_doOnComplete(oncomplete, 200, {}, response);
}
};
xdr.onerror = function () {
_doOnComplete(oncomplete, 400, {}, _isOneDs ? STR_EMPTY : formatErrorMessageXdr(xdr));
};
xdr.ontimeout = function () {
_doOnComplete(oncomplete, 500, {});
};
xdr.onprogress = function () { };
// XDomainRequest requires the same protocol as the hosting page.
// If the protocol doesn't match, we can't send the telemetry :(.
var hostingProtocol = _window && _window.location && _window.location.protocol || "";
var endpoint = payload[_DYN_URL_STRING /* @min:%2eurlString */];
if (!endpoint) {
_onNoPayloadUrl(oncomplete);
return;
}
if (!_isOneDs && endpoint.lastIndexOf(hostingProtocol, 0) !== 0) {
var msg = "Cannot send XDomain request. The endpoint URL protocol doesn't match the hosting page protocol.";
_throwInternal(_diagLog, 2 /* eLoggingSeverity.WARNING */, 40 /* _eInternalMessageId.TransmissionFailed */, ". " + msg);
_onError(msg, oncomplete);
return;
}
var endpointUrl = _isOneDs ? endpoint : endpoint[_DYN_REPLACE /* @min:%2ereplace */](/^(https?:)/, "");
xdr.open(STR_POST_METHOD, endpointUrl);
if (payload[_DYN_TIMEOUT /* @min:%2etimeout */]) {
xdr[_DYN_TIMEOUT /* @min:%2etimeout */] = payload[_DYN_TIMEOUT /* @min:%2etimeout */];
}
xdr.send(data);
if (_isOneDs && sync) {
_timeoutWrapper && _timeoutWrapper.set(function () {
xdr.send(data);
}, 0);
}
else {
xdr.send(data);
}
}
function _initDefaults() {
_syncFetchPayload = 0;
_isInitialized = false;
_enableSendPromise = false;
_diagLog = null;
_isOneDs = null;
_onCompleteFuncs = null;
_disableCredentials = null;
_fetchCredentials = null;
_fallbackInst = null;
_disableXhr = false;
_disableBeacon = false;
_disableBeaconSync = false;
_disableFetchKeepAlive = false;
_addNoResponse = false;
_timeoutWrapper = null;
}
});
}
// Removed Stub for SenderPostManager.prototype.initialize.
// Removed Stub for SenderPostManager.prototype.getSyncFetchPayload.
// Removed Stub for SenderPostManager.prototype.SetConfig.
// Removed Stub for SenderPostManager.prototype.getSenderInst.
// Removed Stub for SenderPostManager.prototype.getFallbackInst.
// Removed Stub for SenderPostManager.prototype._doTeardown.
// This is a workaround for an IE bug when using dynamicProto() with classes that don't have any
// non-dynamic functions or static properties/functions when using uglify-js to minify the resulting code.
SenderPostManager.__ieDyn=1;
return SenderPostManager;
}());
export { SenderPostManager };
//# sourceMappingURL=SenderPostManager.js.map