import { __read, __spread } from "tslib";
import { logger } from '@sentry/utils';
/**
 * Express integration
 *
 * Provides an request and error handler for Express framework as well as tracing capabilities
 */
var Express = /** @class */ (function () {
    /**
     * @inheritDoc
     */
    function Express(options) {
        if (options === void 0) { options = {}; }
        /**
         * @inheritDoc
         */
        this.name = Express.id;
        this._router = options.router || options.app;
        this._methods = (Array.isArray(options.methods) ? options.methods : []).concat('use');
    }
    /**
     * @inheritDoc
     */
    Express.prototype.setupOnce = function () {
        if (!this._router) {
            logger.error('ExpressIntegration is missing an Express instance');
            return;
        }
        instrumentMiddlewares(this._router, this._methods);
    };
    /**
     * @inheritDoc
     */
    Express.id = 'Express';
    return Express;
}());
export { Express };
/**
 * Wraps original middleware function in a tracing call, which stores the info about the call as a span,
 * and finishes it once the middleware is done invoking.
 *
 * Express middlewares have 3 various forms, thus we have to take care of all of them:
 * // sync
 * app.use(function (req, res) { ... })
 * // async
 * app.use(function (req, res, next) { ... })
 * // error handler
 * app.use(function (err, req, res, next) { ... })
 *
 * They all internally delegate to the `router[method]` of the given application instance.
 */
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any
function wrap(fn, method) {
    var arity = fn.length;
    switch (arity) {
        case 2: {
            return function (req, res) {
                var transaction = res.__sentry_transaction;
                if (transaction) {
                    var span_1 = transaction.startChild({
                        description: fn.name,
                        op: "middleware." + method,
                    });
                    res.once('finish', function () {
                        span_1.finish();
                    });
                }
                return fn.call(this, req, res);
            };
        }
        case 3: {
            return function (req, res, next) {
                var _a;
                var transaction = res.__sentry_transaction;
                var span = (_a = transaction) === null || _a === void 0 ? void 0 : _a.startChild({
                    description: fn.name,
                    op: "middleware." + method,
                });
                fn.call(this, req, res, function () {
                    var args = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        args[_i] = arguments[_i];
                    }
                    var _a;
                    (_a = span) === null || _a === void 0 ? void 0 : _a.finish();
                    next.call.apply(next, __spread([this], args));
                });
            };
        }
        case 4: {
            return function (err, req, res, next) {
                var _a;
                var transaction = res.__sentry_transaction;
                var span = (_a = transaction) === null || _a === void 0 ? void 0 : _a.startChild({
                    description: fn.name,
                    op: "middleware." + method,
                });
                fn.call(this, err, req, res, function () {
                    var args = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        args[_i] = arguments[_i];
                    }
                    var _a;
                    (_a = span) === null || _a === void 0 ? void 0 : _a.finish();
                    next.call.apply(next, __spread([this], args));
                });
            };
        }
        default: {
            throw new Error("Express middleware takes 2-4 arguments. Got: " + arity);
        }
    }
}
/**
 * Takes all the function arguments passed to the original `app` or `router` method, eg. `app.use` or `router.use`
 * and wraps every function, as well as array of functions with a call to our `wrap` method.
 * We have to take care of the arrays as well as iterate over all of the arguments,
 * as `app.use` can accept middlewares in few various forms.
 *
 * app.use([<path>], <fn>)
 * app.use([<path>], <fn>, ...<fn>)
 * app.use([<path>], ...<fn>[])
 */
function wrapMiddlewareArgs(args, method) {
    return args.map(function (arg) {
        if (typeof arg === 'function') {
            return wrap(arg, method);
        }
        if (Array.isArray(arg)) {
            return arg.map(function (a) {
                if (typeof a === 'function') {
                    return wrap(a, method);
                }
                return a;
            });
        }
        return arg;
    });
}
/**
 * Patches original router to utilize our tracing functionality
 */
function patchMiddleware(router, method) {
    var originalCallback = router[method];
    router[method] = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        return originalCallback.call.apply(originalCallback, __spread([this], wrapMiddlewareArgs(args, method)));
    };
    return router;
}
/**
 * Patches original router methods
 */
function instrumentMiddlewares(router, methods) {
    if (methods === void 0) { methods = []; }
    methods.forEach(function (method) { return patchMiddleware(router, method); });
}
//# sourceMappingURL=express.js.map