"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = exports.App = exports.setCurrentApp = exports.getCurrentApp = void 0;
exports.createApp = createApp;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
const di_1 = require("../di");
const di_2 = require("../di");
const database_1 = require("../database");
const events_1 = require("../events");
const health_1 = require("../health");
const devconsole_1 = require("../devconsole");
const http_1 = require("../http");
const helpers_1 = require("../helpers");
const services_1 = require("../services");
const mail_1 = require("../services/mail");
const sql_1 = require("../database/sql");
const openapi_1 = require("../openapi");
const metrics_controller_1 = require("../telemetry/otel/metrics.controller");
const helpers_2 = require("../telemetry/otel/helpers");
const metrics_1 = require("../telemetry/otel/metrics");
const sentry_1 = require("../telemetry/sentry");
const types_1 = require("../types");
const config_1 = require("./config");
const config_loader_1 = require("./config-loader");
const lifecycle_1 = require("./lifecycle");
const migrations_entrypoint_1 = require("./migrations-entrypoint");
const commands_1 = require("./commands");
const current_1 = require("./current");
const __tsf_metadata_type = _a.createCompactMetadataRegistryV1("[1,[{\"kind\":2},{\"kind\":2,\"typeName\":\"C\"},{\"kind\":2,\"typeName\":\"Token\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"T\"}]},{\"kind\":2,\"typeName\":\"T\"},{\"kind\":2,\"typeName\":\"(event: TEvent) =\\u003e void | Promise\\u003cvoid\\u003e\"},{\"kind\":2,\"typeName\":\"() =\\u003e void\"},{\"kind\":22,\"type\":{\"kind\":3}},{\"kind\":7},{\"kind\":6}]]", []);
var current_2 = require("./current");
Object.defineProperty(exports, "getCurrentApp", { enumerable: true, get: function () { return current_2.getCurrentApp; } });
Object.defineProperty(exports, "setCurrentApp", { enumerable: true, get: function () { return current_2.setCurrentApp; } });
let _AppMetadata_1;
class App {
    options;
    container;
    events = new events_1.EventBus();
    config;
    router;
    http;
    listenerClasses;
    commandClasses;
    commandModuleIds;
    started = false;
    starting;
    signalHandlers;
    forceWorkerRunner = false;
    devConsole;
    openApiDumpTimer;
    constructor(options) {
        this.options = options;
        const configClass = options.config ?? config_1.BaseAppConfig;
        this.config = new config_loader_1.ConfigLoader(configClass, options.defaultConfig).load();
        if (this.config.SENTRY_DSN) {
            (0, sentry_1.installSentry)({ dsn: this.config.SENTRY_DSN });
        }
        const configProviders = [{ provide: configClass, useValue: this.config }];
        if (configClass !== config_1.BaseAppConfig) {
            configProviders.push({ provide: config_1.BaseAppConfig, useExisting: configClass });
        }
        const defaultControllers = [];
        if (options.enableHealthcheck !== false)
            defaultControllers.push(health_1.HealthcheckController);
        if ((0, devconsole_1.shouldEnableDevConsole)(this.config))
            defaultControllers.push(devconsole_1.DevConsoleController);
        if ((0, openapi_1.shouldExposeOpenApi)(this.config))
            defaultControllers.push(openapi_1.OpenApiController);
        if ((0, metrics_1.isOtelMetricsEndpointEnabled)())
            defaultControllers.push(metrics_controller_1.MetricsController);
        const rootControllers = uniqueClasses([...(options.controllers ?? []), ...defaultControllers]);
        assertHttpControllerClasses(rootControllers);
        const rootCommands = uniqueClasses([...(options.commands ?? [])]);
        const imports = addFrameworkProvidersToImports(options.imports ?? []);
        const controllers = uniqueClasses([...rootControllers, ...collectImportedControllers(imports)]);
        this.listenerClasses = uniqueClasses([...(options.listeners ?? []), ...collectImportedListeners(imports)]);
        this.commandClasses = uniqueClasses([...rootCommands, ...collectImportedCommands(imports)]);
        assertCommandClasses(this.commandClasses);
        const appProviders = uniqueProviders([
            { provide: App, useValue: this },
            ...configProviders,
            ...(0, services_1.createLoggerProviders)(),
            mail_1.MailService,
            ...(options.enableHealthcheck === false ? [] : [health_1.HealthcheckService]),
            ...(options.db ? [options.db, { provide: database_1.BaseDatabase, useExisting: options.db }] : []),
            ...(options.enableWorker
                ? [
                    services_1.WorkerQueueRegistry,
                    services_1.WorkerRecorderService,
                    {
                        provide: services_1.WorkerRunnerService,
                        useFactory: (app, queueRegistry, recorder, logger) => new services_1.WorkerRunnerService(app, queueRegistry, recorder, logger),
                        deps: [App, services_1.WorkerQueueRegistry, services_1.WorkerRecorderService, services_1.ScopedLogger]
                    },
                    services_1.WorkerService
                ]
                : []),
            ...(options.providers ?? [])
        ]);
        const controllerProviders = rootControllers.map(controller => createControllerProvider(controller, this));
        const commandProviders = rootCommands.map(command => createCommandProvider(command));
        this.container = new di_1.Container({
            ...options,
            imports,
            providers: [...appProviders, ...controllerProviders, ...commandProviders]
        });
        const rootLogger = this.container.get(services_1.ScopedLogger);
        this.router = new http_1.HttpRouter(this.container, this.events, options.httpResolvers);
        this.http = new http_1.HttpServerRuntime({
            config: this.config,
            router: this.router,
            logger: rootLogger,
            frameworkConfig: options.frameworkConfig,
            serverConfig: options.serverConfig,
            cors: options.cors,
            staticFiles: options.staticFiles,
            requestLogging: options.requestLogging,
            packageName: (0, helpers_1.getPackageName)() ?? 'app',
            devConsoleEnabled: (0, devconsole_1.shouldEnableDevConsole)(this.config),
            listenHooks: () => {
                const startedBeforeListen = this.started;
                return {
                    start: () => this.start(),
                    afterListen: async (server) => {
                        this.devConsole?.start(server);
                        this.installSignalHandlers();
                        await this.events.dispatch(lifecycle_1.onServerBootstrap, undefined);
                        await this.events.dispatch(lifecycle_1.onServerMainBootstrapDone, undefined);
                    },
                    onListenError: async () => {
                        if (!startedBeforeListen && this.started)
                            await this.stop();
                    }
                };
            }
        });
        if ((0, devconsole_1.shouldEnableDevConsole)(this.config))
            this.devConsole = new devconsole_1.DevConsoleRuntime(this, rootLogger);
        if (options.db && options.enableHealthcheck !== false) {
            this.registerDatabaseHealthcheck(options.db);
        }
        const controllerModuleIds = this.getControllerModuleIds(controllers);
        this.commandModuleIds = this.getCommandModuleIds(this.commandClasses);
        for (const controller of controllers)
            this.router.registerController(controller, controllerModuleIds.get(controller));
        this.registerDecoratedListeners();
    }
    get(token) {
        return this.container.get(token);
    }
    getInjectorContext() {
        return this.container;
    }
    on(token, handler, order = 0) {
        return this.events.listen(token, handler, order);
    }
    async start() {
        if (this.started)
            return;
        if (this.starting) {
            await this.starting;
            return;
        }
        this.starting = this.startInternal();
        try {
            await this.starting;
        }
        finally {
            this.starting = undefined;
        }
    }
    async startInternal() {
        await this.events.dispatch(lifecycle_1.onAppBootstrap, undefined);
        if (this.options.db)
            this.container.get(this.options.db);
        this.createAutoConstructInstances();
        let workerStartAttempted = false;
        try {
            if (this.shouldStartWorkerRunner()) {
                workerStartAttempted = true;
                await this.container.get(services_1.WorkerRunnerService).start();
            }
            if ((0, openapi_1.shouldDumpOpenApiSchema)(this.config))
                this.scheduleOpenApiSchemaDump();
            this.started = true;
        }
        catch (error) {
            const cleanupErrors = [];
            const runCleanup = async (cleanup) => {
                try {
                    await cleanup();
                }
                catch (cleanupError) {
                    cleanupErrors.push(cleanupError);
                }
            };
            if (workerStartAttempted)
                await runCleanup(() => this.container.get(services_1.WorkerRunnerService).shutdown());
            if (this.options.enableWorker)
                await runCleanup(() => this.container.get(services_1.WorkerQueueRegistry).shutdown());
            if (cleanupErrors.length) {
                const rollbackError = cleanupErrors.length === 1 ? cleanupErrors[0] : new AggregateError(cleanupErrors, 'Application startup rollback failed');
                if (error instanceof Error && error.cause === undefined)
                    error.cause = rollbackError;
                else
                    throw new AggregateError([error, rollbackError], 'Application startup and rollback failed');
            }
            throw error;
        }
    }
    async stop() {
        if (!this.started) {
            this.clearOpenApiDumpTimer();
            this.devConsole?.close();
            return;
        }
        const errors = [];
        const runStep = async (step) => {
            try {
                await step();
            }
            catch (error) {
                errors.push(error);
            }
        };
        await runStep(() => this.events.dispatch(lifecycle_1.onServerShutdownRequested, undefined));
        this.clearOpenApiDumpTimer();
        if (this.options.enableWorker) {
            await runStep(() => this.container.get(services_1.WorkerRunnerService).shutdown());
            await runStep(() => this.container.get(services_1.WorkerQueueRegistry).shutdown());
        }
        await runStep(() => this.removeSignalHandlers());
        await runStep(() => this.devConsole?.close());
        await runStep(() => this.http.close());
        await runStep(() => this.events.dispatch(lifecycle_1.onServerShutdown, undefined));
        await runStep(() => this.shutdownTelemetryIfInstalled());
        this.started = false;
        if (errors.length === 1)
            throw errors[0];
        if (errors.length > 1)
            throw new AggregateError(errors, 'Application shutdown failed');
    }
    async run(port, host) {
        if (await this.runEntrypointCommand(process.argv.slice(2)))
            return;
        await this.http.listen(port, host);
    }
    async runEntrypointCommand(args) {
        const [command, ...rest] = args;
        if (command) {
            const customCommand = this.findCommand(command);
            if (customCommand) {
                const instance = this.container.resolve(customCommand.classType, customCommand.moduleId);
                await this.invokeCommand(instance, rest);
                return true;
            }
        }
        switch (command) {
            case 'server:start':
                return false;
            case 'worker:start':
                await this.runWorkerEntrypoint();
                return true;
            case 'migrate':
            case 'migrate:run':
                await this.runMigrationsFromEntrypoint(rest);
                return true;
            case 'openapi:generate':
                await this.runOpenApiGenerationFromEntrypoint(rest);
                return true;
            case undefined:
                this.printEntrypointUsage();
                return true;
            default:
                this.printEntrypointUsage(`Unknown entrypoint command: ${command}`);
                return true;
        }
    }
    printEntrypointUsage(message) {
        if (message)
            console.error(message);
        const entrypoint = process.argv[1] || '<entrypoint>';
        const customCommands = this.commandClasses
            .map(commandClass => (0, commands_1.getCommandMetadata)(commandClass))
            .filter((metadata) => !!metadata);
        const customCommandLines = customCommands.map(metadata => `  ${metadata.name.padEnd(20)} ${metadata.description ?? ''}`.trimEnd()).join('\n');
        console.error(`Usage: node ${entrypoint} <command> [options]

Commands:
  server:start          Start the HTTP server
  worker:start          Start the worker runner and HTTP health checks
  migrate, migrate:run  Run compiled database migrations
  openapi:generate      Write openapi.yaml from registered routes
${customCommandLines ? `${customCommandLines}\n` : ''}

Examples:
  node ${entrypoint} server:start
  node ${entrypoint} worker:start
  node ${entrypoint} migrate:run
  node ${entrypoint} openapi:generate`);
        process.exitCode = 1;
    }
    findCommand(command) {
        const classType = this.commandClasses.find(commandClass => (0, commands_1.getCommandMetadata)(commandClass)?.name === command);
        if (!classType)
            return undefined;
        return {
            classType,
            moduleId: this.commandModuleIds.get(classType)
        };
    }
    async invokeCommand(instance, args) {
        if (!instance || typeof instance.execute !== 'function') {
            throw new Error(`Command ${instance?.constructor?.name ?? '<unknown>'} does not define execute()`);
        }
        await instance.execute(args);
    }
    async runWorkerEntrypoint() {
        if (!this.options.enableWorker)
            throw new Error('Cannot start worker without enableWorker: true');
        this.forceWorkerRunner = true;
        await this.http.listen();
        await new Promise(() => { });
    }
    async runMigrationsFromEntrypoint(args) {
        if (!this.options.db)
            throw new Error('Cannot run migrations without a configured database provider');
        const db = this.get(database_1.BaseDatabase);
        try {
            const migrationsDir = (0, migrations_entrypoint_1.parseEntrypointMigrationsDir)(args);
            const migrations = await (0, database_1.loadMigrationsFromDirectory)(migrationsDir);
            const executions = await new database_1.MigrationRunner(db).run(migrations);
            console.log(`Ran ${executions.length} migration(s).`);
        }
        finally {
            await db.driver.close();
        }
    }
    async runOpenApiGenerationFromEntrypoint(args) {
        if (args.length)
            throw new Error(`openapi:generate does not accept arguments: ${args.join(' ')}`);
        const path = await (0, openapi_1.dumpOpenApiSchema)(this);
        console.log(`Wrote OpenAPI schema to ${path}`);
    }
    async request(request, response = new http_1.MemoryHttpResponse()) {
        return this.http.request(request, response);
    }
    shouldStartWorkerRunner() {
        if (!this.options.enableWorker)
            return false;
        if (this.forceWorkerRunner)
            return true;
        if (this.config.ENABLE_JOB_RUNNER !== undefined)
            return this.config.ENABLE_JOB_RUNNER === true;
        return this.config.APP_ENV !== 'production';
    }
    createAutoConstructInstances() {
        for (const registered of this.container.listRegisteredProviders()) {
            const classType = getProviderClass(registered.provider);
            if (classType && (0, lifecycle_1.isAutoConstructProvider)(classType)) {
                this.container.resolve(registered.token, registered.moduleId);
            }
        }
    }
    scheduleOpenApiSchemaDump() {
        this.clearOpenApiDumpTimer();
        const logger = this.container.get(services_1.ScopedLogger).scoped('app');
        this.openApiDumpTimer = setTimeout(() => {
            this.openApiDumpTimer = undefined;
            void (0, openapi_1.dumpOpenApiSchema)(this)
                .then(path => logger.info(`OpenAPI schema written to: ${path}`))
                .catch(error => logger.error('Failed to write OpenAPI schema', error));
        }, 250);
        this.openApiDumpTimer.unref?.();
    }
    clearOpenApiDumpTimer() {
        if (!this.openApiDumpTimer)
            return;
        clearTimeout(this.openApiDumpTimer);
        this.openApiDumpTimer = undefined;
    }
    registerDatabaseHealthcheck(dbClass) {
        const healthcheck = this.container.get(health_1.HealthcheckService);
        healthcheck.register('database', async () => {
            const db = this.container.get(dbClass);
            if (!db?.driver || typeof db.driver.connect !== 'function')
                throw new Error('Configured db provider is not a BaseDatabase instance');
            await db.driver.connect();
            await db.rawFind((0, sql_1.sql) `SELECT 1`);
        });
    }
    getControllerModuleIds(controllers) {
        const controllerSet = new Set(controllers);
        const moduleIds = new Map();
        for (const registered of this.container.listRegisteredProviders()) {
            if (typeof registered.token === 'function' && controllerSet.has(registered.token)) {
                moduleIds.set(registered.token, registered.moduleId);
            }
        }
        return moduleIds;
    }
    getCommandModuleIds(commands) {
        const commandSet = new Set(commands);
        const moduleIds = new Map();
        for (const registered of this.container.listRegisteredProviders()) {
            if (typeof registered.token === 'function' &&
                commandSet.has(registered.token) &&
                !moduleIds.has(registered.token)) {
                moduleIds.set(registered.token, registered.moduleId);
            }
        }
        return moduleIds;
    }
    registerDecoratedListeners() {
        const registeredProviders = this.container.listRegisteredProviders();
        for (const listenerClass of this.listenerClasses) {
            const registered = registeredProviders.find(item => item.token === listenerClass);
            const moduleId = registered?.moduleId;
            for (const metadata of (0, events_1.getListenerMethodMetadata)(listenerClass.prototype)) {
                this.events.listen(metadata.token, async (event) => {
                    const listener = moduleId === undefined ? this.container.get(listenerClass) : this.container.resolve(listenerClass, moduleId);
                    await listener[metadata.methodName](event);
                }, metadata.order);
            }
        }
    }
    async shutdownTelemetryIfInstalled() {
        if (!helpers_2.OtelState.tracerProvider && !helpers_2.OtelState.meterProvider)
            return;
        const { shutdownTelemetry } = await Promise.resolve().then(() => tslib_1.__importStar(require('../telemetry/otel/index')));
        await shutdownTelemetry();
    }
    installSignalHandlers() {
        if (this.signalHandlers)
            return;
        const signals = ['SIGTERM', 'SIGINT'];
        this.signalHandlers = {};
        for (const signal of signals) {
            const handler = () => {
                this.stop()
                    .catch(error => {
                    console.error(error);
                    process.exit(1);
                })
                    .then(() => process.exit(0));
            };
            this.signalHandlers[signal] = handler;
            process.once(signal, handler);
        }
    }
    removeSignalHandlers() {
        if (!this.signalHandlers)
            return;
        for (const [signal, handler] of Object.entries(this.signalHandlers)) {
            if (handler)
                process.off(signal, handler);
        }
        this.signalHandlers = undefined;
    }
    static __tsfType = _AppMetadata_1 = _a.decodeCompactMetadataV1("[1,{\"kind\":16,\"name\":\"App\",\"typeName\":\"App\",\"classType\":{\"$tsf\":0},\"properties\":[{\"name\":\"container\",\"type\":{\"kind\":16,\"typeName\":\"Container\",\"classType\":{\"$tsfImport\":[1,\"../di/index\",\"Container\"]}},\"optional\":false},{\"name\":\"events\",\"type\":{\"$tsfType\":0},\"optional\":false},{\"name\":\"config\",\"type\":{\"$tsfType\":1},\"optional\":false},{\"name\":\"router\",\"type\":{\"kind\":16,\"typeName\":\"HttpRouter\",\"classType\":{\"$tsfImport\":[1,\"../http/index\",\"HttpRouter\"]}},\"optional\":false},{\"name\":\"http\",\"type\":{\"kind\":16,\"typeName\":\"HttpServerRuntime\",\"classType\":{\"$tsfImport\":[1,\"../http/index\",\"HttpServerRuntime\"]},\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},\"optional\":false}],\"methods\":[{\"name\":\"get\",\"parameters\":[{\"name\":\"token\",\"type\":{\"$tsfType\":2},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":3}},{\"name\":\"getInjectorContext\",\"parameters\":[],\"returnType\":{\"kind\":16,\"typeName\":\"Container\",\"classType\":{\"$tsfImport\":[1,\"../di/index\",\"Container\"]}}},{\"name\":\"on\",\"parameters\":[{\"name\":\"token\",\"type\":{\"kind\":16,\"typeName\":\"EventToken\",\"classType\":{\"$tsfImport\":[1,\"../events/index\",\"EventToken\"]},\"typeArguments\":[{\"kind\":16,\"typeName\":\"TEvent\",\"classType\":{\"$tsf\":2}}]},\"optional\":false,\"default\":false},{\"name\":\"handler\",\"type\":{\"$tsfType\":4},\"optional\":false,\"default\":false},{\"name\":\"order\",\"type\":{\"$tsfType\":0},\"optional\":true,\"default\":true}],\"returnType\":{\"$tsfType\":5}},{\"name\":\"start\",\"parameters\":[],\"returnType\":{\"$tsfType\":6}},{\"name\":\"stop\",\"parameters\":[],\"returnType\":{\"$tsfType\":6}},{\"name\":\"run\",\"parameters\":[{\"name\":\"port\",\"type\":{\"$tsfType\":7},\"optional\":true,\"default\":false},{\"name\":\"host\",\"type\":{\"$tsfType\":8},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":6}},{\"name\":\"request\",\"parameters\":[{\"name\":\"request\",\"type\":{\"kind\":16,\"typeName\":\"HttpRequest\",\"classType\":{\"$tsfImport\":[1,\"../http/index\",\"HttpRequest\"]}},\"optional\":false,\"default\":false},{\"name\":\"response\",\"type\":{\"kind\":16,\"typeName\":\"HttpResponse\",\"classType\":{\"$tsfImport\":[1,\"../http/index\",\"HttpResponse\"]}},\"optional\":true,\"default\":true}],\"returnType\":{\"kind\":22,\"type\":{\"kind\":16,\"typeName\":\"HttpResponse\",\"classType\":{\"$tsfImport\":[1,\"../http/index\",\"HttpResponse\"]}}}}],\"hasConstructor\":true,\"constructorParameters\":[{\"name\":\"options\",\"type\":{\"kind\":16,\"typeName\":\"CreateAppOptions\",\"classType\":{\"$tsf\":3},\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},\"optional\":false,\"default\":false}]}]", [() => App, require, () => (typeof TEvent !== "undefined" ? TEvent : (typeof exports !== "undefined" ? exports.TEvent : undefined)), () => (typeof CreateAppOptions !== "undefined" ? CreateAppOptions : (typeof exports !== "undefined" ? exports.CreateAppOptions : undefined))], __tsf_metadata_type);
}
exports.App = App;
_AppMetadata_1.classType = App;
App.__tsfType = _AppMetadata_1;
function createApp(options) {
    const app = new App(options);
    (0, current_1.setCurrentApp)(app);
    return app;
}
function getProviderClass(provider) {
    if (!(0, di_1.isStructuredProvider)(provider))
        return (0, types_1.isClass)(provider) ? provider : undefined;
    if ('useClass' in provider)
        return provider.useClass;
    return undefined;
}
function createControllerProvider(controller, app) {
    if (controller === devconsole_1.DevConsoleController) {
        if (!app)
            throw new Error('DevConsoleController requires an app instance');
        return {
            provide: devconsole_1.DevConsoleController,
            useFactory: () => new devconsole_1.DevConsoleController(app),
            scope: 'request'
        };
    }
    if (controller === openapi_1.OpenApiController) {
        if (!app)
            throw new Error('OpenApiController requires an app instance');
        return {
            provide: openapi_1.OpenApiController,
            useFactory: () => new openapi_1.OpenApiController(app),
            scope: 'request'
        };
    }
    return {
        provide: controller,
        useClass: controller,
        scope: 'request'
    };
}
function createCommandProvider(command) {
    return {
        provide: command,
        useClass: command
    };
}
function uniqueClasses(classes) {
    return [...new Set(classes)];
}
function addFrameworkProvidersToImports(imports) {
    return imports.map(imported => addFrameworkProvidersToModule((0, di_2.normalizeModule)(imported)));
}
function addFrameworkProvidersToModule(definition) {
    const controllers = definition.controllers ?? [];
    assertHttpControllerClasses(controllers);
    const commands = definition.commands ?? [];
    assertCommandClasses(commands);
    const controllerProviders = controllers.map(controller => createControllerProvider(controller));
    const commandProviders = commands.map(command => createCommandProvider(command));
    return {
        ...definition,
        imports: addFrameworkProvidersToImports(definition.imports ?? []),
        providers: uniqueProviders([...(definition.providers ?? []), ...controllerProviders, ...commandProviders])
    };
}
function collectImportedControllers(imports) {
    const result = [];
    for (const imported of imports) {
        const definition = (0, di_2.normalizeModule)(imported);
        result.push(...(definition.controllers ?? []), ...collectImportedControllers(definition.imports ?? []));
    }
    return result;
}
function collectImportedListeners(imports) {
    const result = [];
    for (const imported of imports) {
        const definition = (0, di_2.normalizeModule)(imported);
        result.push(...(definition.listeners ?? []), ...collectImportedListeners(definition.imports ?? []));
    }
    return result;
}
function collectImportedCommands(imports) {
    const result = [];
    for (const imported of imports) {
        const definition = (0, di_2.normalizeModule)(imported);
        result.push(...(definition.commands ?? []), ...collectImportedCommands(definition.imports ?? []));
    }
    return result;
}
function uniqueProviders(providers) {
    const order = [];
    const byToken = new Map();
    for (const provider of providers) {
        const token = (0, di_1.getProviderToken)(provider);
        if (!byToken.has(token))
            order.push(token);
        byToken.set(token, provider);
    }
    return order.map(token => byToken.get(token));
}
function assertHttpControllerClasses(controllers) {
    for (const controller of controllers) {
        if (typeof controller === 'function' && (0, http_1.getControllerMetadata)(controller))
            continue;
        throw new Error(`Controller ${getClassName(controller)} passed to controllers must be decorated with @http.controller()`);
    }
}
function assertCommandClasses(commands) {
    for (const command of commands) {
        if (typeof command === 'function' && (0, commands_1.getCommandMetadata)(command))
            continue;
        throw new Error(`Command ${getClassName(command)} passed to commands must be decorated with @cli.command() or @cli.controller()`);
    }
}
function getClassName(classType) {
    return typeof classType === 'function' && classType.name ? classType.name : '<anonymous>';
}
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"CreateAppOptions\":{\"kind\":18,\"typeName\":\"CreateAppOptions\",\"types\":[{\"kind\":20,\"name\":\"config\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ClassType\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"defaultConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Partial\",\"utilityType\":\"Partial\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}],\"utilityKeys\":[],\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"imports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"ModuleDefinition\",\"types\":[{\"kind\":20,\"name\":\"config\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ClassType\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"defaultConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Partial\",\"utilityType\":\"Partial\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}],\"utilityKeys\":[],\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"imports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ModuleDefinition\"},{\"kind\":2,\"typeName\":\"ClassType\"},{\"kind\":18,\"typeName\":\"ModuleInstance\",\"types\":[{\"kind\":20,\"name\":\"definition\",\"type\":{\"kind\":2,\"typeName\":\"ModuleDefinition\"},\"optional\":false}]}],\"typeName\":\"ModuleLike\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"exports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Token\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"providers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Provider\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"controllers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"listeners\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"commands\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true}]},{\"kind\":2,\"typeName\":\"ClassType\"},{\"kind\":18,\"typeName\":\"ModuleInstance\",\"types\":[{\"kind\":20,\"name\":\"definition\",\"type\":{\"kind\":18,\"typeName\":\"ModuleDefinition\",\"types\":[{\"kind\":20,\"name\":\"config\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ClassType\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"defaultConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Partial\",\"utilityType\":\"Partial\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}],\"utilityKeys\":[],\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"imports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ModuleDefinition\"},{\"kind\":2,\"typeName\":\"ClassType\"},{\"kind\":2,\"typeName\":\"ModuleInstance\"}],\"typeName\":\"ModuleLike\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"exports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Token\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"providers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Provider\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"controllers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"listeners\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"commands\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true}]},\"optional\":false}]}],\"typeName\":\"ModuleLike\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"exports\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Token\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"providers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"Provider\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"controllers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"listeners\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"commands\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"ClassType\"}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"config\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ClassType\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"defaultConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Partial\",\"utilityType\":\"Partial\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}],\"utilityKeys\":[],\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"db\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"ClassType\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"frameworkConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":2}],\"index\":{\"kind\":2},\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"serverConfig\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":2}],\"index\":{\"kind\":2},\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"cors\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"HttpCorsConfig\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"staticFiles\",\"type\":{\"kind\":12,\"types\":[{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":18,\"typeName\":\"StaticFilesOptions\",\"types\":[{\"kind\":20,\"name\":\"directory\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"index\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"spaFallback\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true}]}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"requestLogging\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"HttpRequestLoggingOptions\",\"types\":[{\"kind\":20,\"name\":\"excludePaths\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":16,\"typeName\":\"RegExp\",\"classType\":{\"$tsf\":0}}]}},{\"kind\":4}]},\"optional\":true}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"httpResolvers\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":2,\"typeName\":\"RouteParameterResolverInput\"}],\"index\":{\"kind\":2,\"typeName\":\"RouteParameterResolverInput\"},\"types\":[],\"typeName\":\"RouteParameterResolverRegistry\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableHealthcheck\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableWorker\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableDkRpc\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]}}]", [() => (typeof RegExp !== "undefined" ? RegExp : (typeof exports !== "undefined" ? exports.RegExp : undefined))], __tsf_metadata_type);
//# sourceMappingURL=base.js.map