Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var types_1 = require("@sentry/types");
var utils_1 = require("@sentry/utils");
var global = utils_1.getGlobalObject();
var defaultTracingOrigins = ['localhost', /^\//];
/**
 * Tracing Integration
 */
var Tracing = /** @class */ (function () {
    /**
     * Constructor for Tracing
     *
     * @param _options TracingOptions
     */
    function Tracing(_options) {
        this._options = _options;
        /**
         * @inheritDoc
         */
        this.name = Tracing.id;
        this._emitOptionsWarning = false;
        var defaults = {
            idleTimeout: 500,
            maxTransactionDuration: 600,
            shouldCreateSpanForRequest: function (url) {
                var origins = (_options && _options.tracingOrigins) || defaultTracingOrigins;
                return (origins.some(function (origin) { return utils_1.isMatchingPattern(url, origin); }) &&
                    !utils_1.isMatchingPattern(url, 'sentry_key'));
            },
            startTransactionOnLocationChange: true,
            traceFetch: true,
            traceXHR: true,
            tracesSampleRate: 1,
            tracingOrigins: defaultTracingOrigins,
        };
        // NOTE: Logger doesn't work in contructors, as it's initialized after integrations instances
        if (!_options || !Array.isArray(_options.tracingOrigins) || _options.tracingOrigins.length === 0) {
            this._emitOptionsWarning = true;
        }
        Tracing.options = this._options = tslib_1.__assign({}, defaults, _options);
    }
    /**
     * @inheritDoc
     */
    Tracing.prototype.setupOnce = function (addGlobalEventProcessor, getCurrentHub) {
        Tracing._getCurrentHub = getCurrentHub;
        if (this._emitOptionsWarning) {
            utils_1.logger.warn('[Tracing] You need to define `tracingOrigins` in the options. Set an array of urls or patterns to trace.');
            utils_1.logger.warn("[Tracing] We added a reasonable default for you: " + defaultTracingOrigins);
        }
        if (!Tracing._isEnabled()) {
            return;
        }
        // tslint:disable-next-line: no-non-null-assertion
        if (this._options.traceXHR !== false) {
            utils_1.addInstrumentationHandler({
                callback: xhrCallback,
                type: 'xhr',
            });
        }
        // tslint:disable-next-line: no-non-null-assertion
        if (this._options.traceFetch !== false && utils_1.supportsNativeFetch()) {
            utils_1.addInstrumentationHandler({
                callback: fetchCallback,
                type: 'fetch',
            });
        }
        // tslint:disable-next-line: no-non-null-assertion
        if (this._options.startTransactionOnLocationChange) {
            utils_1.addInstrumentationHandler({
                callback: historyCallback,
                type: 'history',
            });
        }
        if (global.location && global.location.href) {
            // `${global.location.href}` will be used a temp transaction name
            Tracing.startIdleTransaction(global.location.href, {
                op: 'pageload',
                sampled: true,
            });
        }
        // This EventProcessor makes sure that the transaction is not longer than maxTransactionDuration
        addGlobalEventProcessor(function (event) {
            var self = getCurrentHub().getIntegration(Tracing);
            if (!self) {
                return event;
            }
            if (Tracing._isEnabled()) {
                var isOutdatedTransaction = event.timestamp &&
                    event.start_timestamp &&
                    (event.timestamp - event.start_timestamp > Tracing.options.maxTransactionDuration ||
                        event.timestamp - event.start_timestamp < 0);
                if (Tracing.options.maxTransactionDuration !== 0 && event.type === 'transaction' && isOutdatedTransaction) {
                    return null;
                }
            }
            return event;
        });
    };
    /**
     * Is tracing enabled
     */
    Tracing._isEnabled = function () {
        if (Tracing._enabled !== undefined) {
            return Tracing._enabled;
        }
        // This happens only in test cases where the integration isn't initalized properly
        // tslint:disable-next-line: strict-type-predicates
        if (!Tracing.options || typeof Tracing.options.tracesSampleRate !== 'number') {
            return false;
        }
        Tracing._enabled = Math.random() > Tracing.options.tracesSampleRate ? false : true;
        return Tracing._enabled;
    };
    /**
     * Starts a Transaction waiting for activity idle to finish
     */
    Tracing.startIdleTransaction = function (name, spanContext) {
        if (!Tracing._isEnabled()) {
            // Tracing is not enabled
            return undefined;
        }
        // If we already have an active transaction it means one of two things
        // a) The user did rapid navigation changes and didn't wait until the transaction was finished
        // b) A activity wasn't popped correctly and therefore the transaction is stalling
        Tracing.finishIdleTransaction();
        utils_1.logger.log('[Tracing] startIdleTransaction, name:', name);
        var _getCurrentHub = Tracing._getCurrentHub;
        if (!_getCurrentHub) {
            return undefined;
        }
        var hub = _getCurrentHub();
        if (!hub) {
            return undefined;
        }
        var span = hub.startSpan(tslib_1.__assign({}, spanContext, { transaction: name }), true);
        Tracing._activeTransaction = span;
        // We need to do this workaround here and not use configureScope
        // Reason being at the time we start the inital transaction we do not have a client bound on the hub yet
        // therefore configureScope wouldn't be executed and we would miss setting the transaction
        // tslint:disable-next-line: no-unsafe-any
        hub.getScope().setSpan(span);
        // The reason we do this here is because of cached responses
        // If we start and transaction without an activity it would never finish since there is no activity
        var id = Tracing.pushActivity('idleTransactionStarted');
        setTimeout(function () {
            Tracing.popActivity(id);
        }, (Tracing.options && Tracing.options.idleTimeout) || 100);
        return span;
    };
    /**
     * Update transaction
     * @deprecated
     */
    Tracing.updateTransactionName = function (name) {
        utils_1.logger.log('[Tracing] DEPRECATED, use Sentry.configureScope => scope.setTransaction instead', name);
        var _getCurrentHub = Tracing._getCurrentHub;
        if (_getCurrentHub) {
            var hub = _getCurrentHub();
            if (hub) {
                hub.configureScope(function (scope) {
                    scope.setTransaction(name);
                });
            }
        }
    };
    /**
     * Finshes the current active transaction
     */
    Tracing.finishIdleTransaction = function () {
        var active = Tracing._activeTransaction;
        if (active) {
            utils_1.logger.log('[Tracing] finishIdleTransaction', active.transaction);
            // true = use timestamp of last span
            active.finish(true);
        }
    };
    /**
     * Sets the status of the current active transaction (if there is one)
     */
    Tracing.setTransactionStatus = function (status) {
        var active = Tracing._activeTransaction;
        if (active) {
            utils_1.logger.log('[Tracing] setTransactionStatus', status);
            active.setStatus(status);
        }
    };
    /**
     * Starts tracking for a specifc activity
     *
     * @param name Name of the activity, can be any string (Only used internally to identify the activity)
     * @param spanContext If provided a Span with the SpanContext will be created.
     * @param options _autoPopAfter_ | Time in ms, if provided the activity will be popped automatically after this timeout. This can be helpful in cases where you cannot gurantee your application knows the state and calls `popActivity` for sure.
     */
    Tracing.pushActivity = function (name, spanContext, options) {
        if (!Tracing._isEnabled()) {
            // Tracing is not enabled
            return 0;
        }
        // We want to clear the timeout also here since we push a new activity
        clearTimeout(Tracing._debounce);
        var _getCurrentHub = Tracing._getCurrentHub;
        if (spanContext && _getCurrentHub) {
            var hub = _getCurrentHub();
            if (hub) {
                Tracing._activities[Tracing._currentIndex] = {
                    name: name,
                    span: hub.startSpan(spanContext),
                };
            }
        }
        else {
            Tracing._activities[Tracing._currentIndex] = {
                name: name,
            };
        }
        utils_1.logger.log("[Tracing] pushActivity: " + name + "#" + Tracing._currentIndex);
        utils_1.logger.log('[Tracing] activies count', Object.keys(Tracing._activities).length);
        if (options && typeof options.autoPopAfter === 'number') {
            utils_1.logger.log("[Tracing] auto pop of: " + name + "#" + Tracing._currentIndex + " in " + options.autoPopAfter + "ms");
            var index_1 = Tracing._currentIndex;
            setTimeout(function () {
                Tracing.popActivity(index_1, {
                    autoPop: true,
                    status: types_1.SpanStatus.DeadlineExceeded,
                });
            }, options.autoPopAfter);
        }
        return Tracing._currentIndex++;
    };
    /**
     * Removes activity and finishes the span in case there is one
     */
    Tracing.popActivity = function (id, spanData) {
        if (!Tracing._isEnabled()) {
            // Tracing is not enabled
            return;
        }
        var activity = Tracing._activities[id];
        if (activity) {
            utils_1.logger.log("[Tracing] popActivity " + activity.name + "#" + id);
            var span_1 = activity.span;
            if (span_1) {
                if (spanData) {
                    Object.keys(spanData).forEach(function (key) {
                        span_1.setData(key, spanData[key]);
                        if (key === 'status_code') {
                            span_1.setHttpStatus(spanData[key]);
                        }
                        if (key === 'status') {
                            span_1.setStatus(spanData[key]);
                        }
                    });
                }
                span_1.finish();
            }
            // tslint:disable-next-line: no-dynamic-delete
            delete Tracing._activities[id];
        }
        var count = Object.keys(Tracing._activities).length;
        clearTimeout(Tracing._debounce);
        utils_1.logger.log('[Tracing] activies count', count);
        if (count === 0) {
            var timeout = Tracing.options && Tracing.options.idleTimeout;
            utils_1.logger.log("[Tracing] Flushing Transaction in " + timeout + "ms");
            Tracing._debounce = setTimeout(function () {
                Tracing.finishIdleTransaction();
            }, timeout);
        }
    };
    /**
     * @inheritDoc
     */
    Tracing.id = 'Tracing';
    Tracing._currentIndex = 0;
    Tracing._activities = {};
    Tracing._debounce = 0;
    return Tracing;
}());
exports.Tracing = Tracing;
/**
 * Creates breadcrumbs from XHR API calls
 */
function xhrCallback(handlerData) {
    if (!Tracing.options.traceXHR) {
        return;
    }
    // tslint:disable-next-line: no-unsafe-any
    if (!handlerData || !handlerData.xhr || !handlerData.xhr.__sentry_xhr__) {
        return;
    }
    // tslint:disable: no-unsafe-any
    var xhr = handlerData.xhr.__sentry_xhr__;
    if (!Tracing.options.shouldCreateSpanForRequest(xhr.url)) {
        return;
    }
    // We only capture complete, non-sentry requests
    if (handlerData.xhr.__sentry_own_request__) {
        return;
    }
    if (handlerData.endTimestamp && handlerData.xhr.__sentry_xhr_activity_id__) {
        Tracing.popActivity(handlerData.xhr.__sentry_xhr_activity_id__, handlerData.xhr.__sentry_xhr__);
        return;
    }
    handlerData.xhr.__sentry_xhr_activity_id__ = Tracing.pushActivity('xhr', {
        data: tslib_1.__assign({}, xhr.data, { type: 'xhr' }),
        description: xhr.method + " " + xhr.url,
        op: 'http',
    });
    // Adding the trace header to the span
    var activity = Tracing._activities[handlerData.xhr.__sentry_xhr_activity_id__];
    if (activity) {
        var span = activity.span;
        if (span && handlerData.xhr.setRequestHeader) {
            handlerData.xhr.setRequestHeader('sentry-trace', span.toTraceparent());
        }
    }
    // tslint:enable: no-unsafe-any
}
/**
 * Creates breadcrumbs from fetch API calls
 */
function fetchCallback(handlerData) {
    // tslint:disable: no-unsafe-any
    if (!Tracing.options.traceFetch) {
        return;
    }
    if (!Tracing.options.shouldCreateSpanForRequest(handlerData.fetchData.url)) {
        return;
    }
    if (handlerData.endTimestamp && handlerData.fetchData.__activity) {
        Tracing.popActivity(handlerData.fetchData.__activity, handlerData.fetchData);
    }
    else {
        handlerData.fetchData.__activity = Tracing.pushActivity('fetch', {
            data: tslib_1.__assign({}, handlerData.fetchData, { type: 'fetch' }),
            description: handlerData.fetchData.method + " " + handlerData.fetchData.url,
            op: 'http',
        });
        var activity = Tracing._activities[handlerData.fetchData.__activity];
        if (activity) {
            var span = activity.span;
            if (span) {
                var options = (handlerData.args[1] = handlerData.args[1] || {});
                if (options.headers) {
                    if (Array.isArray(options.headers)) {
                        options.headers = tslib_1.__spread(options.headers, [{ 'sentry-trace': span.toTraceparent() }]);
                    }
                    else {
                        options.headers = tslib_1.__assign({}, options.headers, { 'sentry-trace': span.toTraceparent() });
                    }
                }
                else {
                    options.headers = { 'sentry-trace': span.toTraceparent() };
                }
            }
        }
    }
    // tslint:enable: no-unsafe-any
}
/**
 * Creates transaction from navigation changes
 */
function historyCallback(_) {
    if (Tracing.options.startTransactionOnLocationChange && global && global.location) {
        Tracing.startIdleTransaction(global.location.href, {
            op: 'navigation',
            sampled: true,
        });
    }
}
//# sourceMappingURL=tracing.js.map