"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpCorsListener = exports.HttpCorsDescriptor = exports.HttpCors = exports.HttpCorsOptions = exports.HttpCorsOptionsMulti = exports.CorsHeaders = void 0;
const tslib_1 = require("tslib");
const __ΩRecord = ['K', 'T', 'Record', 'l\'e#"Rb!b"Pde"!N#!w#y'];
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
const event_1 = require("@deepkit/event");
const http_1 = require("@deepkit/http");
exports.CorsHeaders = Symbol('CorsHeaders');
class HttpCorsOptionsMulti {
    constructor(options) {
        this.options = options;
    }
}
exports.HttpCorsOptionsMulti = HttpCorsOptionsMulti;
HttpCorsOptionsMulti.__type = [() => HttpCorsOptions, 'options', 'constructor', 'HttpCorsOptionsMulti', 'PP7!F2":9"0#5w$'];
class HttpCorsOptions {
}
exports.HttpCorsOptions = HttpCorsOptions;
HttpCorsOptions.__type = ['hosts', 'paths', 'methods', 'credentials', 'allowHeaders', 'exposeHeaders', 'HttpCorsOptions', 'P&AJF3!P&AJF3"8&F3#8)3$8&F3%8&F3&85w\''];
class HttpCors {
    static getResponseHeaders(response) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return response[exports.CorsHeaders];
    }
}
exports.HttpCors = HttpCors;
HttpCors.__type = [() => http_1.HttpResponse, 'response', () => __ΩRecord, 'getResponseHeaders', 'HttpCors', 'PP7!2"P&&o##-J0$s5w%'];
class HttpCorsDescriptor {
    constructor(options) {
        this.options = options;
        this.cachedPreflightHeaders = {};
        this.cachedResponseCorsHeaders = {};
        this.cachedPreflightHeaders['Access-Control-Allow-Methods'] = options.methods?.join(',') ?? 'GET,HEAD,PUT,PATCH,POST,DELETE';
        if (options.allowHeaders) {
            this.cachedPreflightHeaders['Access-Control-Allow-Headers'] = options.allowHeaders.join(', ');
        }
        if (options.credentials) {
            this.cachedPreflightHeaders['Access-Control-Allow-Credentials'] = 'true';
            this.cachedResponseCorsHeaders['Access-Control-Allow-Credentials'] = 'true';
        }
        if (options.exposeHeaders) {
            this.cachedResponseCorsHeaders['Access-Control-Expose-Headers'] = options.exposeHeaders.join(', ');
        }
    }
}
exports.HttpCorsDescriptor = HttpCorsDescriptor;
HttpCorsDescriptor.__type = [() => __ΩRecord, 'cachedPreflightHeaders', function () { return {}; }, () => __ΩRecord, 'cachedResponseCorsHeaders', function () { return {}; }, () => HttpCorsOptions, 'options', 'constructor', 'HttpCorsDescriptor', '&&o!#3">#&&o$#3%>&PP7\'2(:9"0)5w*'];
class HttpCorsListener {
    constructor(router, corsOptionsMulti) {
        this.router = router;
        this.corsOptionsMulti = corsOptionsMulti;
        this.descriptors = corsOptionsMulti.options.map(__assignType(options => new HttpCorsDescriptor(options), ['options', '', 'P"2!"/"']));
    }
    // because DK's middleware implemtnation only aborts executing middleware when it encounters a finshed response,
    // we perform writeHead+end in our HttpMiddleware wrapper. this means that the response is already finished
    // by the time it would reach an onResponse handler, and would prevent us from injecting CORS headers into
    // a response such as an HTTP 401. to work around this, we precompute the CORS response headers so they can
    // be used in the normal response flow, or in our HttpMiddleware wrapper
    // note we already have a listener at priority 99, so we use 98 here since it needs to run before that one
    async onRoute(event) {
        if (event.sent)
            return;
        if (event.hasNext())
            return;
        const descriptor = this.findMatchingDescriptor(event.request);
        if (descriptor) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            event.response[exports.CorsHeaders] = this.getCorsResponseHeaders(event.request, descriptor);
        }
    }
    async routeNotFound(event) {
        if (event.sent)
            return;
        if (event.hasNext())
            return;
        if (event.request.method === 'OPTIONS') {
            const descriptor = this.findMatchingDescriptor(event.request);
            if (descriptor) {
                // DK's response requires a content-type, which doesn't make sense here, so we go around it
                // by using Node's built-in server response
                event.response.writeHead(204, this.getCorsPreflightHeaders(event.request, descriptor));
                event.response.end();
                event.send(event.response);
            }
        }
    }
    async onResponse(event) {
        if (event.response.headersSent)
            return;
        if (event.hasNext())
            return;
        if (exports.CorsHeaders in event.response) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            for (const key in event.response[exports.CorsHeaders]) {
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                event.response.setHeader(key, event.response[exports.CorsHeaders][key]);
            }
        }
    }
    findMatchingDescriptor(request) {
        const originHeader = request.headers.origin;
        if (!originHeader)
            return null;
        if (!request.url)
            return null;
        return this.descriptors.find(__assignType(descriptor => {
            const hostMatches = descriptor.options.hosts.some(__assignType(host => {
                if (host === '*') {
                    return true;
                }
                if (host instanceof RegExp) {
                    return host.test(originHeader);
                }
                return host === request.headers.origin;
            }, ['host', '', 'P"2!"/"']));
            const patchMatches = !descriptor.options.paths ||
                descriptor.options.paths.some(__assignType(path => {
                    if (path instanceof RegExp) {
                        return path.test(request.url);
                    }
                    return request.url.startsWith(path);
                }, ['path', '', 'P"2!"/"']));
            return hostMatches && patchMatches;
        }, ['descriptor', '', 'P"2!"/"']));
    }
    getCorsPreflightHeaders(request, descriptor) {
        const headers = {
            ...descriptor.cachedPreflightHeaders,
            'Access-Control-Allow-Origin': request.headers.origin ?? '*',
            'Access-Control-Allow-Credentials': 'true',
            'Content-Length': '0'
        };
        if (!('Access-Control-Allow-Headers' in headers)) {
            if (request.headers['access-control-request-headers']) {
                headers['Access-Control-Allow-Headers'] = request.headers['access-control-request-headers'];
            }
        }
        return headers;
    }
    getCorsResponseHeaders(request, descriptor) {
        return {
            ...descriptor.cachedResponseCorsHeaders,
            'Access-Control-Allow-Origin': request.headers.origin ?? '*'
        };
    }
}
exports.HttpCorsListener = HttpCorsListener;
HttpCorsListener.__type = [() => HttpCorsDescriptor, 'descriptors', () => http_1.HttpRouter, 'router', () => HttpCorsOptionsMulti, 'corsOptionsMulti', 'constructor', () => http_1.httpWorkflow.onRoute.event, 'event', 'onRoute', () => http_1.httpWorkflow.onRouteNotFound.event, 'routeNotFound', () => http_1.httpWorkflow.onResponse.event, 'onResponse', () => http_1.HttpRequest, 'request', 'findMatchingDescriptor', () => http_1.HttpRequest, () => HttpCorsDescriptor, 'descriptor', () => __ΩRecord, 'getCorsPreflightHeaders', () => http_1.HttpRequest, () => HttpCorsDescriptor, () => __ΩRecord, 'getCorsResponseHeaders', 'HttpCorsListener', 'P7!F3"PP7#2$<P7%2&<"0\'Pi(2)$`0*Pi+2)$`0,Pi-2)$`0.PP7/20"01;PP7220P7324&&o5#06;PP7720P7824&&o9#0:;5w;'];
tslib_1.__decorate([
    event_1.eventDispatcher.listen(http_1.httpWorkflow.onRoute, 98),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [Object]),
    tslib_1.__metadata("design:returntype", Promise)
], HttpCorsListener.prototype, "onRoute", null);
tslib_1.__decorate([
    event_1.eventDispatcher.listen(http_1.httpWorkflow.onRouteNotFound, 99),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [Object]),
    tslib_1.__metadata("design:returntype", Promise)
], HttpCorsListener.prototype, "routeNotFound", null);
tslib_1.__decorate([
    event_1.eventDispatcher.listen(http_1.httpWorkflow.onResponse, -101),
    tslib_1.__metadata("design:type", Function),
    tslib_1.__metadata("design:paramtypes", [Object]),
    tslib_1.__metadata("design:returntype", Promise)
], HttpCorsListener.prototype, "onResponse", null);
//# sourceMappingURL=cors.js.map