"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = exports.TestingHelpers = exports.TestingFacade = void 0;
exports.createTestingFacade = createTestingFacade;
exports.createTestingFacadeWithDatabase = createTestingFacadeWithDatabase;
exports.createTestingFacadeBuilder = createTestingFacadeBuilder;
exports.makeMockRequest = makeMockRequest;
exports.installStandardHooks = installStandardHooks;
exports.resetSrcModuleCache = resetSrcModuleCache;
exports.setDefaultDatabaseConfig = setDefaultDatabaseConfig;
exports.cleanupTestDatabases = cleanupTestDatabases;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
const node_test_1 = require("node:test");
const node_path_1 = require("node:path");
const app_1 = require("../app");
const database_1 = require("../database");
const env_1 = require("../env");
const http_1 = require("../http");
const sql_1 = require("../database/sql");
const logger_1 = require("../services/logger");
const entity_1 = require("../services/worker/entity");
const database_readiness_1 = require("./database-readiness");
const database_name_1 = require("./database-name");
const fixtures_1 = require("./fixtures");
const sql_2 = require("./sql");
const __tsf_metadata_type = _a.createCompactMetadataRegistryV1("[1,[{\"kind\":18,\"types\":[{\"kind\":20,\"name\":\"name\",\"type\":{\"kind\":6},\"optional\":false}]},{\"kind\":6},{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"mysql\"},{\"kind\":10,\"literal\":\"postgres\"}],\"typeName\":\"DatabaseDialect\",\"typeName\":\"TestDbAdapter\"},{\"kind\":2},{\"kind\":22,\"type\":{\"kind\":3}},{\"kind\":2,\"typeName\":\"TestingFacadeSeedData\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":22,\"type\":{\"kind\":8}},{\"kind\":2,\"typeName\":\"Parameters\\u003cApp\\u003cC\\u003e['get']\\u003e[0]\"},{\"kind\":2,\"typeName\":\"T\"},{\"kind\":18,\"typeName\":\"Partial\",\"utilityType\":\"Partial\",\"typeArguments\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"(typeof DATABASE_ENV_KEYS)[number]\"},{\"kind\":6}],\"index\":{\"kind\":6},\"types\":[]}],\"utilityKeys\":[],\"types\":[],\"typeName\":\"DatabaseEnvSnapshot\"}]]", []);
tslib_1.__exportStar(require("./expect"), exports);
tslib_1.__exportStar(require("./fixtures"), exports);
tslib_1.__exportStar(require("./sql"), exports);
let _TestingFacadeMetadata_1;
class TestingFacade {
    app;
    options;
    databaseName;
    dbAdapter;
    migrationExecutions = [];
    sql = new sql_2.SqlTestingHelper();
    savepointIsolationActive = false;
    originalDatabaseEnv;
    originalAppDatabaseConfig;
    savepointConnection;
    savepointCleanup;
    savepointIsolationLocksReady = false;
    sharedDatabaseState;
    sharedMySQLSessionKey;
    sharedMySQLSessionLeaseId;
    databaseCreated = false;
    logger = (0, logger_1.createLogger)('TestingFacade');
    constructor(app, options = {}, originalDatabaseEnv) {
        this.app = app;
        this.options = options;
        this.originalDatabaseEnv = originalDatabaseEnv ?? (options.enableDatabase ? snapshotDatabaseEnv() : undefined);
        this.dbAdapter = options.enableDatabase ? resolveTestDbAdapter(app.config, options.dbAdapter) : options.dbAdapter;
        this.originalAppDatabaseConfig = snapshotAppDatabaseConfig(app.config);
    }
    async start() {
        try {
            this.logger.info('Starting test facade', {
                enableDatabase: this.options.enableDatabase === true,
                dbAdapter: this.dbAdapter,
                migrations: this.shouldRunMigrations(),
                useSavepoints: this.shouldUseSavepoints
            });
            (0, app_1.setCurrentApp)(this.app);
            if (this.options.enableDatabase)
                await this.createDatabase();
            if (this.shouldPrepareDatabaseSchema())
                await this.prepareDatabaseSchema();
            if (this.options.enableDatabase && this.shouldUseSavepoints)
                await this.initSavepointIsolation();
            await this.options.onBeforeStart?.(this);
            (0, app_1.setCurrentApp)(this.app);
            await this.app.start();
            await this.options.onStart?.(this);
            if (this.options.autoSeedData && !this.shouldUseSavepoints)
                await this.seed();
        }
        catch (error) {
            await this.app.stop().catch(() => { });
            await this.cleanupDatabase();
            throw error;
        }
    }
    async stop() {
        (0, app_1.setCurrentApp)(this.app);
        let errorToThrow;
        try {
            await this.options.onBeforeStop?.(this);
        }
        catch (error) {
            errorToThrow ??= error;
        }
        try {
            await this.teardownSavepointIsolation();
        }
        catch (error) {
            errorToThrow ??= error;
        }
        try {
            await this.app.stop();
        }
        catch (error) {
            errorToThrow ??= error;
        }
        try {
            await this.cleanupDatabase();
        }
        catch (error) {
            errorToThrow ??= error;
        }
        try {
            await this.options.onStop?.(this);
        }
        catch (error) {
            errorToThrow ??= error;
        }
        if (errorToThrow)
            throw errorToThrow;
    }
    async createDatabase() {
        if (!this.options.enableDatabase)
            return;
        if (this.databaseCreated)
            return;
        const adapter = this.requireDbAdapter();
        await (0, database_readiness_1.waitForTestDatabaseReady)(adapter, this.app.config);
        const sharedKey = this.getSharedDatabaseKey(adapter);
        if (adapter === 'mysql' && sharedKey && (0, database_1.getSharedMySQLSessionManagerConfig)()) {
            await this.createManagedSharedMySQLDatabase(sharedKey);
            return;
        }
        if (sharedKey) {
            await this.createSharedDatabase(adapter, sharedKey);
            return;
        }
        this.databaseName = createTestDatabaseName(this.options.databasePrefix);
        const adminDb = this.createAdminDatabase(adapter);
        try {
            await adminDb.rawExecute((0, sql_1.sql) `CREATE DATABASE ${sql_1.sql.identifier(this.databaseName)}`);
        }
        finally {
            await adminDb.driver.close();
        }
        this.databaseCreated = true;
        this.applyDatabaseConfig(adapter, this.databaseName);
    }
    async destroyDatabase() {
        if (this.sharedMySQLSessionKey) {
            await (0, database_1.releaseSharedMySQLSessionDatabase)(this.sharedMySQLSessionKey, this.sharedMySQLSessionLeaseId).catch(() => { });
            this.sharedMySQLSessionKey = undefined;
            this.sharedMySQLSessionLeaseId = undefined;
            this.databaseCreated = false;
            return;
        }
        if (this.sharedDatabaseState) {
            this.releaseSharedDatabase();
            return;
        }
        if (!this.databaseCreated || !this.databaseName || this.options.keepDatabase || env_1.Env.TEST_KEEP_DB)
            return;
        await this.dropDatabase(this.requireDbAdapter(), this.databaseName);
        this.databaseCreated = false;
    }
    async truncateTables() {
        if (!this.options.enableDatabase || !this.databaseCreated)
            return;
        const db = this.get(database_1.BaseDatabase);
        if (db.driver.dialect === 'postgres') {
            const rows = await db.rawFind((0, sql_1.sql) `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename <> '_migrations'`);
            if (!rows.length)
                return;
            await db.rawExecute((0, sql_1.sql) `TRUNCATE TABLE ${sql_1.sql.join(rows.map(row => sql_1.sql.identifier(row.tablename)))} RESTART IDENTITY CASCADE`);
            return;
        }
        const rows = await db.rawFind((0, sql_1.sql) `SHOW TABLES`);
        const tables = rows
            .map(row => String(Object.values(row)[0]))
            .filter(table => table && table !== '_migrations')
            .sort();
        if (!tables.length)
            return;
        await db.transaction(async (session) => {
            await db.rawExecute((0, sql_1.sql) `SET FOREIGN_KEY_CHECKS = 0`, session);
            try {
                for (const table of tables) {
                    await db.rawExecute((0, sql_1.sql) `TRUNCATE TABLE ${sql_1.sql.identifier(table)}`, session);
                }
            }
            finally {
                await db.rawExecute((0, sql_1.sql) `SET FOREIGN_KEY_CHECKS = 1`, session);
            }
        });
    }
    async request(request) {
        return (await this.app.request(request, new http_1.MemoryHttpResponse()));
    }
    async seed() {
        await this.options.seedData?.(this);
    }
    async runMigrations() {
        if (!this.app.options.db)
            throw new Error('TestingFacade migrations require an app database provider');
        const db = this.get(database_1.BaseDatabase);
        const migrations = await this.loadTestingMigrations();
        const executions = await new database_1.MigrationRunner(db).run(migrations, {
            beforeRun: () => this.prepareInternalMigrationTables(db)
        });
        this.migrationExecutions.push(...executions);
        return executions;
    }
    async resetToSeed() {
        if (this.options.enableDatabase && this.shouldUseSavepoints) {
            if (!this.savepointIsolationActive) {
                await this.initSavepointIsolation();
            }
            else {
                if (!this.savepointConnection)
                    throw new Error('Savepoint isolation is active without a database connection');
                await this.savepointConnection.rollbackToSavepoint('after_seed');
            }
            if (this.savepointIsolationActive)
                return;
        }
        if (this.options.enableDatabase)
            await this.truncateTables();
        await this.seed();
    }
    async createSeedSavepoint(name, seedData) {
        if (!this.options.enableDatabase || !this.shouldUseSavepoints || !this.getDatabaseProvider())
            return false;
        if (!this.savepointIsolationActive)
            await this.initSavepointIsolation();
        if (!this.savepointConnection)
            throw new Error('Savepoint isolation is active without a database connection');
        await seedData?.(this);
        await this.savepointConnection.savepoint(name);
        return true;
    }
    async resetToSeedSavepoint(name) {
        if (!this.options.enableDatabase || !this.shouldUseSavepoints || !this.getDatabaseProvider())
            return false;
        if (!this.savepointIsolationActive)
            await this.initSavepointIsolation();
        if (!this.savepointConnection)
            throw new Error('Savepoint isolation is active without a database connection');
        await this.savepointConnection.rollbackToSavepoint(name);
        return true;
    }
    get(token) {
        return this.app.get(token);
    }
    async cleanupDatabase() {
        try {
            await this.teardownSavepointIsolation();
            await this.closeDatabaseProvider();
            await this.destroyDatabase();
        }
        finally {
            this.restoreDatabaseConfig();
        }
    }
    async prepareDatabaseSchema() {
        if (this.sharedMySQLSessionKey) {
            await this.prepareManagedSharedMySQLSchema(this.sharedMySQLSessionKey, this.sharedMySQLSessionLeaseId);
            return;
        }
        const sharedState = this.sharedDatabaseState;
        if (!sharedState) {
            await this.createSchemaFromEntities();
            if (this.shouldRunMigrations())
                await this.runMigrations();
            if (this.shouldTruncateAfterMigrations())
                await this.truncateTables();
            return;
        }
        if (!sharedState.schemaReady) {
            sharedState.schemaReady = (async () => {
                try {
                    await this.createSchemaFromEntities();
                    if (this.shouldRunMigrations())
                        await this.runMigrations();
                    if (this.shouldTruncateAfterMigrations())
                        await this.truncateTables();
                }
                catch (error) {
                    sharedTestDatabases.delete(sharedState.key);
                    if (!sharedState.keepDatabase && !env_1.Env.TEST_KEEP_DB)
                        await sharedState.destroy().catch(() => { });
                    throw error;
                }
            })();
        }
        await sharedState.schemaReady;
    }
    async prepareManagedSharedMySQLSchema(key, leaseId) {
        const preparation = await (0, database_1.prepareSharedMySQLSessionSchema)(key, leaseId);
        if (!preparation.run)
            return;
        try {
            await this.createSchemaFromEntities();
            if (this.shouldRunMigrations())
                await this.runMigrations();
            if (this.shouldTruncateAfterMigrations())
                await this.truncateTables();
        }
        catch (error) {
            await (0, database_1.completeSharedMySQLSessionSchema)(key, leaseId, preparation.preparationId, error).catch(() => { });
            throw error;
        }
        await (0, database_1.completeSharedMySQLSessionSchema)(key, leaseId, preparation.preparationId);
    }
    async closeDatabaseProvider() {
        if (!this.app.options.db)
            return;
        try {
            const db = this.app.get(this.app.options.db);
            await db.driver.close();
        }
        catch {
            // The DB may never have been constructed, or start() may have failed before it was usable.
        }
    }
    createAdminDatabase(adapter) {
        return adapter === 'postgres'
            ? new database_1.BaseDatabase(new database_1.PostgresDriver({
                host: this.app.config.PG_HOST,
                port: this.app.config.PG_PORT,
                user: this.app.config.PG_USER,
                password: this.app.config.PG_PASSWORD_SECRET,
                database: 'postgres',
                ssl: this.app.config.PG_SSL ? { rejectUnauthorized: this.app.config.PG_SSL_REJECT_UNAUTHORIZED ?? true } : undefined,
                max: 1
            }))
            : new database_1.BaseDatabase(new database_1.MySQLDriver({
                host: this.app.config.MYSQL_HOST,
                port: this.app.config.MYSQL_PORT,
                user: this.app.config.MYSQL_USER,
                password: this.app.config.MYSQL_PASSWORD_SECRET,
                database: 'mysql',
                connectionLimit: 1
            }));
    }
    async dropDatabase(adapter, databaseName) {
        const adminDb = this.createAdminDatabase(adapter);
        try {
            if (adapter === 'postgres') {
                await adminDb.rawExecute((0, sql_1.sql) `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${databaseName} AND pid <> pg_backend_pid()`);
            }
            await adminDb.rawExecute((0, sql_1.sql) `DROP DATABASE IF EXISTS ${sql_1.sql.identifier(databaseName)}`);
        }
        finally {
            await adminDb.driver.close();
        }
    }
    async createSharedDatabase(adapter, key) {
        let state = sharedTestDatabases.get(key);
        if (!state) {
            const databaseName = createTestDatabaseName(this.options.databasePrefix);
            state = {
                key,
                adapter,
                databaseName,
                references: 0,
                keepDatabase: !!this.options.keepDatabase,
                destroy: () => this.dropDatabase(adapter, databaseName)
            };
            sharedTestDatabases.set(key, state);
            installSharedDatabaseCleanup();
            state.databaseReady = this.initializeSharedDatabase(state);
        }
        else {
            state.keepDatabase = state.keepDatabase || !!this.options.keepDatabase;
        }
        state.references++;
        this.sharedDatabaseState = state;
        try {
            await state.databaseReady;
        }
        catch (error) {
            this.releaseSharedDatabase();
            sharedTestDatabases.delete(key);
            throw error;
        }
        this.databaseName = state.databaseName;
        this.databaseCreated = true;
        this.applyDatabaseConfig(adapter, state.databaseName);
    }
    async createManagedSharedMySQLDatabase(key) {
        const state = await (0, database_1.ensureSharedMySQLSessionDatabase)({
            key,
            prefix: this.options.databasePrefix ?? 'test',
            keepDatabase: !!this.options.keepDatabase
        });
        this.sharedMySQLSessionKey = key;
        this.sharedMySQLSessionLeaseId = state.leaseId;
        this.databaseName = state.databaseName;
        this.databaseCreated = true;
        this.applyDatabaseConfig('mysql', state.databaseName);
        env_1.Env.TSF_TEST_MYSQL_SESSION_KEY = key;
        env_1.Env.TSF_TEST_MYSQL_SESSION_LEASE_ID = state.leaseId;
        env_1.Env.TSF_TEST_MYSQL_SESSION_DATABASE = state.databaseName;
    }
    async initializeSharedDatabase(state) {
        const adminDb = this.createAdminDatabase(state.adapter);
        try {
            await adminDb.rawExecute((0, sql_1.sql) `CREATE DATABASE ${sql_1.sql.identifier(state.databaseName)}`);
        }
        finally {
            await adminDb.driver.close();
        }
    }
    releaseSharedDatabase() {
        const state = this.sharedDatabaseState;
        if (!state)
            return;
        state.references = Math.max(0, state.references - 1);
        this.sharedDatabaseState = undefined;
        this.databaseCreated = false;
    }
    applyDatabaseConfig(adapter, databaseName) {
        this.app.config.DB_ADAPTER = adapter;
        env_1.Env.DB_ADAPTER = adapter;
        env_1.Env.TSF_TEST_DATABASE_NAME = databaseName;
        if (adapter === 'postgres') {
            this.app.config.PG_DATABASE = databaseName;
            env_1.Env.PG_DATABASE = databaseName;
        }
        else {
            this.app.config.MYSQL_DATABASE = databaseName;
            env_1.Env.MYSQL_DATABASE = databaseName;
        }
    }
    restoreDatabaseConfig() {
        Object.assign(this.app.config, this.originalAppDatabaseConfig);
        if (this.originalDatabaseEnv)
            restoreDatabaseEnv(this.originalDatabaseEnv);
    }
    requireDbAdapter() {
        if (!this.dbAdapter)
            throw new Error("TestingFacade enableDatabase requires dbAdapter or DB_ADAPTER to be 'mysql' or 'postgres'");
        return this.dbAdapter;
    }
    shouldRunMigrations() {
        if (this.options.enableMigrations !== undefined)
            return this.options.enableMigrations;
        if (this.options.enableDatabase)
            return !!this.app.options.db;
        return !!this.options.migrations?.length || this.options.migrationsDir !== undefined;
    }
    shouldCreateSchemaFromEntities() {
        return !!this.options.schemaFromEntities;
    }
    shouldPrepareDatabaseSchema() {
        return !!this.options.enableDatabase && (this.shouldCreateSchemaFromEntities() || this.shouldRunMigrations());
    }
    async createSchemaFromEntities() {
        const option = this.options.schemaFromEntities;
        if (!option)
            return;
        const db = this.getDatabaseProvider();
        if (!db)
            throw new Error('TestingFacade schemaFromEntities requires an app database provider');
        const plan = await (0, database_1.createMigrationPlan)(db, option === true ? {} : option);
        for (const statement of plan.statements) {
            if (statement.startsWith('\0table:'))
                continue;
            await db.rawExecuteUnsafe(statement);
        }
    }
    shouldTruncateAfterMigrations() {
        if (!this.options.enableDatabase)
            return false;
        return this.options.truncateAfterMigrations ?? true;
    }
    shouldPrepareWorkerJobsTable() {
        return !!this.options.enableDatabase && !!this.app.options.enableWorker && !!this.app.options.db;
    }
    async prepareInternalMigrationTables(db) {
        await (0, database_1.ensureMySQLLocksTable)(db);
        if (db.driver.dialect === 'mysql' && db.options.enableLocksTable)
            this.savepointIsolationLocksReady = true;
        if (this.shouldPrepareWorkerJobsTable())
            await this.prepareWorkerJobsTableForDatabase(db);
    }
    async prepareWorkerJobsTableForDatabase(db) {
        if (!db.entityRegistry.includes(entity_1.JobEntity))
            db.entityRegistry.push(entity_1.JobEntity);
        entity_1.JobEntity.registerDatabase(db);
        const plan = await (0, database_1.createMigrationPlan)(db, { tableNames: ['_jobs'] });
        for (const statement of plan.statements) {
            if (statement.startsWith('\0table:'))
                continue;
            await db.rawExecuteUnsafe(statement);
        }
    }
    async loadTestingMigrations() {
        const migrations = [...(this.options.migrations ?? [])];
        const dirs = this.getMigrationDirectories();
        for (const dir of dirs) {
            migrations.push(...(await (0, database_1.loadMigrationsFromDirectory)(dir)));
        }
        return migrations;
    }
    getMigrationDirectories() {
        if (this.options.migrationsDir !== undefined) {
            return typeof this.options.migrationsDir === 'string' ? [this.options.migrationsDir] : this.options.migrationsDir;
        }
        if (this.options.migrations !== undefined)
            return [];
        return ['src/migrations'];
    }
    get shouldUseSavepoints() {
        if (!readEnvBoolean(env_1.Env.TSF_TEST_ALLOW_SAVEPOINTS, true))
            return false;
        if (this.options.useSavepoints !== undefined)
            return this.options.useSavepoints;
        return !!this.options.enableDatabase;
    }
    getSharedDatabaseKey(adapter) {
        if (!this.options.enableDatabase || !this.shouldUseSavepoints)
            return undefined;
        const migrations = this.options.migrations ?? [];
        const inlineMigrations = migrations.map(migration => `${migration.name}:${getInlineMigrationIdentity(migration)}`).sort();
        const migrationDirs = this.getMigrationDirectories()
            .map(dir => (0, node_path_1.resolve)(dir))
            .sort();
        const schemaFromEntities = this.options.schemaFromEntities === true
            ? true
            : this.options.schemaFromEntities
                ? {
                    pgSchema: this.options.schemaFromEntities.pgSchema,
                    tableNames: this.options.schemaFromEntities.tableNames ? [...this.options.schemaFromEntities.tableNames].sort() : undefined
                }
                : false;
        return JSON.stringify({
            adapter,
            prefix: this.options.databasePrefix ?? 'test',
            schemaFromEntities,
            migrations: this.shouldRunMigrations() ? { inlineMigrations, migrationDirs } : false,
            truncateAfterMigrations: this.shouldTruncateAfterMigrations(),
            workerJobsTable: this.shouldPrepareWorkerJobsTable()
        });
    }
    getDatabaseProvider() {
        if (!this.app.options.db)
            return undefined;
        try {
            return this.app.get(this.app.options.db);
        }
        catch {
            try {
                return this.get(database_1.BaseDatabase);
            }
            catch {
                return undefined;
            }
        }
    }
    async initSavepointIsolation() {
        const db = this.getDatabaseProvider();
        if (!db)
            return;
        if (!this.savepointIsolationLocksReady)
            await this.ensureSavepointIsolationLocksReady(db);
        const originalAcquire = db.driver.acquire.bind(db.driver);
        const connection = await originalAcquire();
        const originalBegin = connection.begin.bind(connection);
        const originalCommit = connection.commit.bind(connection);
        const originalRollback = connection.rollback.bind(connection);
        const originalSavepoint = connection.savepoint.bind(connection);
        const originalRollbackToSavepoint = connection.rollbackToSavepoint.bind(connection);
        const originalRelease = connection.release.bind(connection);
        const transactionStack = [];
        let savepointId = 0;
        const cleanup = () => {
            db.driver.acquire = originalAcquire;
            connection.begin = originalBegin;
            connection.commit = originalCommit;
            connection.rollback = originalRollback;
            connection.release = originalRelease;
        };
        try {
            await originalBegin();
            db.driver.acquire = async () => connection;
            connection.begin = async () => {
                const name = `tsf_test_tx_${++savepointId}`;
                transactionStack.push(name);
                await originalSavepoint(name);
            };
            connection.commit = async () => {
                transactionStack.pop();
            };
            connection.rollback = async () => {
                const name = transactionStack.pop();
                if (name)
                    await originalRollbackToSavepoint(name);
            };
            connection.release = async () => { };
            this.savepointConnection = connection;
            this.savepointCleanup = cleanup;
            await this.seed();
            await originalSavepoint('after_seed');
            this.savepointIsolationActive = true;
        }
        catch (error) {
            cleanup();
            this.savepointConnection = undefined;
            this.savepointCleanup = undefined;
            try {
                await originalRollback();
            }
            finally {
                await originalRelease();
            }
            throw error;
        }
    }
    async teardownSavepointIsolation() {
        if (!this.savepointConnection)
            return;
        const connection = this.savepointConnection;
        this.savepointCleanup?.();
        this.savepointConnection = undefined;
        this.savepointCleanup = undefined;
        this.savepointIsolationActive = false;
        try {
            await connection.rollback();
        }
        finally {
            await connection.release();
        }
    }
    async ensureSavepointIsolationLocksReady(db = this.getDatabaseProvider()) {
        if (!db)
            return;
        await (0, database_1.ensureMySQLLocksTable)(db);
        this.savepointIsolationLocksReady = true;
    }
    static __tsfType = _TestingFacadeMetadata_1 = _a.decodeCompactMetadataV1("[1,{\"kind\":16,\"name\":\"TestingFacade\",\"typeName\":\"TestingFacade\",\"classType\":{\"$tsf\":0},\"properties\":[{\"name\":\"databaseName\",\"type\":{\"$tsfType\":1},\"optional\":true},{\"name\":\"dbAdapter\",\"type\":{\"$tsfType\":2},\"optional\":true},{\"name\":\"migrationExecutions\",\"type\":{\"kind\":14,\"type\":{\"kind\":18,\"typeName\":\"MigrationExecution\",\"types\":[{\"kind\":20,\"name\":\"name\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"executedAt\",\"type\":{\"kind\":16,\"typeName\":\"Date\",\"classType\":{\"$tsf\":1}},\"optional\":false},{\"kind\":20,\"name\":\"durationMs\",\"type\":{\"kind\":7},\"optional\":false}]}},\"optional\":false},{\"name\":\"sql\",\"type\":{\"$tsfType\":3},\"optional\":false},{\"name\":\"savepointIsolationActive\",\"type\":{\"$tsfType\":3},\"optional\":false}],\"methods\":[{\"name\":\"start\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"stop\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"createDatabase\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"destroyDatabase\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"truncateTables\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"request\",\"parameters\":[{\"name\":\"request\",\"type\":{\"kind\":16,\"typeName\":\"HttpRequest\",\"classType\":{\"$tsfImport\":[2,\"../http/index\",\"HttpRequest\"]}},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":22,\"type\":{\"kind\":16,\"typeName\":\"MemoryHttpResponse\",\"classType\":{\"$tsfImport\":[2,\"../http/index\",\"MemoryHttpResponse\"]}}}},{\"name\":\"seed\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"runMigrations\",\"parameters\":[],\"returnType\":{\"kind\":22,\"type\":{\"kind\":14,\"type\":{\"kind\":18,\"typeName\":\"MigrationExecution\",\"types\":[{\"kind\":20,\"name\":\"name\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"executedAt\",\"type\":{\"kind\":16,\"typeName\":\"Date\",\"classType\":{\"$tsf\":1}},\"optional\":false},{\"kind\":20,\"name\":\"durationMs\",\"type\":{\"kind\":7},\"optional\":false}]}}}},{\"name\":\"resetToSeed\",\"parameters\":[],\"returnType\":{\"$tsfType\":4}},{\"name\":\"createSeedSavepoint\",\"parameters\":[{\"name\":\"name\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false},{\"name\":\"seedData\",\"type\":{\"$tsfType\":5},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":6}},{\"name\":\"resetToSeedSavepoint\",\"parameters\":[{\"name\":\"name\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":6}},{\"name\":\"get\",\"parameters\":[{\"name\":\"token\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":8}}],\"hasConstructor\":true,\"constructorParameters\":[{\"name\":\"app\",\"type\":{\"kind\":16,\"typeName\":\"App\",\"classType\":{\"$tsfImport\":[2,\"../app/index\",\"App\"]},\"typeArguments\":[{\"kind\":1}]},\"optional\":false,\"default\":false},{\"name\":\"options\",\"type\":{\"kind\":16,\"typeName\":\"TestingFacadeOptions\",\"classType\":{\"$tsf\":3},\"typeArguments\":[{\"kind\":1}]},\"optional\":true,\"default\":true},{\"name\":\"originalDatabaseEnv\",\"type\":{\"$tsfType\":9},\"optional\":true,\"default\":false}]}]", [() => TestingFacade, () => (typeof Date !== "undefined" ? Date : (typeof exports !== "undefined" ? exports.Date : undefined)), require, () => (typeof TestingFacadeOptions !== "undefined" ? TestingFacadeOptions : (typeof exports !== "undefined" ? exports.TestingFacadeOptions : undefined))], __tsf_metadata_type);
}
exports.TestingFacade = TestingFacade;
_TestingFacadeMetadata_1.classType = TestingFacade;
TestingFacade.__tsfType = _TestingFacadeMetadata_1;
function createTestingFacade(appOptions, options = {}) {
    const originalDatabaseEnv = options.enableDatabase ? snapshotDatabaseEnv() : undefined;
    const app = (0, app_1.createApp)({
        config: app_1.BaseAppConfig,
        ...appOptions,
        frameworkConfig: {
            ...appOptions.frameworkConfig,
            port: 0
        }
    });
    if (originalDatabaseEnv)
        restoreDatabaseEnv(originalDatabaseEnv);
    return new TestingFacade(app, options, originalDatabaseEnv);
}
function createTestingFacadeWithDatabase(appOptions, options = {}) {
    return createTestingFacade(appOptions, { ...options, enableDatabase: true });
}
function createTestingFacadeBuilder(defaultAppOptions, defaultOptions = {}) {
    return (appOptions = {}, options = {}) => {
        const resolvedAppOptions = typeof defaultAppOptions === 'function'
            ? defaultAppOptions(normalizeTestingAppOptions(appOptions))
            : mergeTestingAppOptions(defaultAppOptions, appOptions);
        const resolvedOptions = typeof defaultOptions === 'function' ? defaultOptions(options) : mergeTestingFacadeOptions(defaultOptions, options);
        return createTestingFacade(resolvedAppOptions, resolvedOptions);
    };
}
function mergeTestingAppOptions(defaults, overrides) {
    const merged = {
        ...defaults,
        ...overrides,
        imports: mergeTestingArrays(defaults.imports, overrides.imports),
        exports: mergeTestingArrays(defaults.exports, overrides.exports),
        providers: mergeTestingArrays(defaults.providers, overrides.providers),
        controllers: mergeTestingArrays(defaults.controllers, overrides.controllers),
        listeners: mergeTestingArrays(defaults.listeners, overrides.listeners),
        commands: mergeTestingArrays(defaults.commands, overrides.commands)
    };
    const defaultConfig = mergeTestingObjects(defaults.defaultConfig, overrides.defaultConfig);
    if (defaultConfig)
        merged.defaultConfig = defaultConfig;
    const frameworkConfig = mergeTestingObjects(defaults.frameworkConfig, overrides.frameworkConfig);
    if (frameworkConfig)
        merged.frameworkConfig = frameworkConfig;
    const serverConfig = mergeTestingObjects(defaults.serverConfig, overrides.serverConfig);
    if (serverConfig)
        merged.serverConfig = serverConfig;
    return merged;
}
function normalizeTestingAppOptions(options) {
    return {
        ...options,
        imports: [...(options.imports ?? [])],
        exports: [...(options.exports ?? [])],
        providers: [...(options.providers ?? [])],
        controllers: [...(options.controllers ?? [])],
        listeners: [...(options.listeners ?? [])],
        commands: [...(options.commands ?? [])]
    };
}
function mergeTestingFacadeOptions(defaults, overrides) {
    const merged = {
        ...defaults,
        ...overrides,
        defaultTestHeaders: mergeTestingObjects(defaults.defaultTestHeaders, overrides.defaultTestHeaders),
        migrations: mergeTestingArrays(defaults.migrations, overrides.migrations),
        migrationsDir: mergeTestingMigrationDirs(defaults.migrationsDir, overrides.migrationsDir),
        seedData: composeTestingFacadeCallbacks(defaults.seedData, overrides.seedData),
        onBeforeStart: composeTestingFacadeCallbacks(defaults.onBeforeStart, overrides.onBeforeStart),
        onStart: composeTestingFacadeCallbacks(defaults.onStart, overrides.onStart),
        onBeforeStop: composeTestingFacadeCallbacks(defaults.onBeforeStop, overrides.onBeforeStop),
        onStop: composeTestingFacadeCallbacks(defaults.onStop, overrides.onStop)
    };
    return merged;
}
function mergeTestingArrays(defaults, overrides) {
    if (!defaults?.length && !overrides?.length)
        return undefined;
    return [...(defaults ?? []), ...(overrides ?? [])];
}
function mergeTestingObjects(defaults, overrides) {
    if (!defaults && !overrides)
        return undefined;
    return { ...(defaults ?? {}), ...(overrides ?? {}) };
}
function mergeTestingMigrationDirs(defaults, overrides) {
    if (defaults === undefined)
        return overrides;
    if (overrides === undefined)
        return defaults;
    return [...toTestingStringArray(defaults), ...toTestingStringArray(overrides)];
}
function toTestingStringArray(value) {
    return typeof value === 'string' ? [value] : value;
}
function composeTestingFacadeCallbacks(first, second) {
    if (!first)
        return second;
    if (!second)
        return first;
    return async (facade) => {
        await first(facade);
        await second(facade);
    };
}
async function makeMockRequest(tf, method, url, headersOrBody = {}, body) {
    const bodyless = method === 'GET';
    const hasSeparateBody = arguments.length >= 5;
    const hasBody = hasSeparateBody || !bodyless;
    const headers = normalizeMockHeaders({
        'content-type': 'application/json',
        ...tf.options.defaultTestHeaders,
        ...(hasSeparateBody || (bodyless && !hasSeparateBody) ? headersOrBody : {})
    });
    const payload = hasSeparateBody ? body : headersOrBody;
    if (!hasBody)
        return tf.request(new http_1.HttpRequest(method, url, headers));
    return tf.request(new http_1.HttpRequest(method, url, headers, payload));
}
function normalizeMockHeaders(headers) {
    return Object.fromEntries(Object.entries(headers)
        .filter((entry) => entry[1] !== undefined)
        .map(([key, value]) => [key, Array.isArray(value) ? value.map(String).join(', ') : String(value)]));
}
function installStandardHooks(tf, options = {}) {
    const suiteSeedSavepoint = 'after_suite_seed';
    let suiteSeedUsesSavepoint = false;
    (0, node_test_1.before)(async () => {
        await tf.start();
        if (options.suiteSeedData)
            suiteSeedUsesSavepoint = await tf.createSeedSavepoint(suiteSeedSavepoint, options.suiteSeedData);
    });
    (0, node_test_1.after)(() => tf.stop());
    (0, node_test_1.beforeEach)(async () => {
        if (!options.suiteSeedData) {
            await tf.resetToSeed();
            return;
        }
        if (suiteSeedUsesSavepoint && (await tf.resetToSeedSavepoint(suiteSeedSavepoint)))
            return;
        await tf.resetToSeed();
        await options.suiteSeedData(tf);
    });
    (0, node_test_1.afterEach)(() => {
        tf.sql.clearMocks();
        node_test_1.mock.timers.reset();
        node_test_1.mock.restoreAll();
    });
}
function resetSrcModuleCache() {
    for (const key of Object.keys(require.cache)) {
        if (key.includes('/dist/') || key.includes('/src/'))
            delete require.cache[key];
    }
}
function setDefaultDatabaseConfig(config) {
    for (const [key, value] of Object.entries(config)) {
        if (value === undefined || env_1.Env[key] !== undefined)
            continue;
        env_1.Env[key] = String(value);
    }
}
async function cleanupTestDatabases(prefix = 'test', adapter) {
    const databasePrefix = (0, database_name_1.createTestDatabasePrefix)(prefix);
    clearSharedTestDatabases(databasePrefix, adapter);
    const adapters = adapter ? [adapter] : ['mysql', 'postgres'].filter(item => hasDatabaseConfig(item));
    for (const dbAdapter of adapters) {
        const adminDb = createCleanupAdminDatabase(dbAdapter);
        try {
            if (dbAdapter === 'postgres') {
                const rows = await adminDb.rawFindUnsafe('SELECT datname AS name FROM pg_database WHERE datname LIKE ?', [
                    `${databasePrefix}_%`
                ], undefined, (__tsf_metadata_type(0)));
                for (const row of rows) {
                    await adminDb.rawExecute((0, sql_1.sql) `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${row.name} AND pid <> pg_backend_pid()`);
                    await adminDb.rawExecute((0, sql_1.sql) `DROP DATABASE IF EXISTS ${sql_1.sql.identifier(row.name)}`);
                }
            }
            else {
                const rows = await adminDb.rawFindUnsafe('SELECT SCHEMA_NAME AS name FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME LIKE ?', [`${databasePrefix}_%`], undefined, (__tsf_metadata_type(0)));
                for (const row of rows) {
                    await adminDb.rawExecute((0, sql_1.sql) `DROP DATABASE IF EXISTS ${sql_1.sql.identifier(row.name)}`);
                }
            }
        }
        finally {
            await adminDb.driver.close();
        }
    }
}
function hasDatabaseConfig(adapter) {
    return adapter === 'postgres' ? !!env_1.Env.PG_HOST : !!env_1.Env.MYSQL_HOST;
}
function createCleanupAdminDatabase(adapter) {
    if (adapter === 'postgres') {
        return new database_1.BaseDatabase(new database_1.PostgresDriver({
            host: env_1.Env.PG_HOST,
            port: env_1.Env.PG_PORT ? Number(env_1.Env.PG_PORT) : 5432,
            user: env_1.Env.PG_USER,
            password: env_1.Env.PG_PASSWORD_SECRET,
            database: 'postgres',
            ssl: readEnvBoolean(env_1.Env.PG_SSL) ? { rejectUnauthorized: readEnvBoolean(env_1.Env.PG_SSL_REJECT_UNAUTHORIZED, true) } : undefined,
            max: 1
        }));
    }
    return new database_1.BaseDatabase(new database_1.MySQLDriver({
        host: env_1.Env.MYSQL_HOST,
        port: env_1.Env.MYSQL_PORT ? Number(env_1.Env.MYSQL_PORT) : 3306,
        user: env_1.Env.MYSQL_USER,
        password: env_1.Env.MYSQL_PASSWORD_SECRET,
        database: 'mysql',
        connectionLimit: 1
    }));
}
function readEnvBoolean(value, defaultValue = false) {
    if (value === undefined)
        return defaultValue;
    if (typeof value === 'boolean')
        return value;
    return value === '1' || value === 'true';
}
exports.TestingHelpers = {
    cleanupTestDatabases,
    createTestingFacade,
    createTestingFacadeBuilder,
    createTestingFacadeWithDatabase,
    defineEntityFixtures: fixtures_1.defineEntityFixtures,
    installStandardHooks,
    loadEntityFixtures: fixtures_1.loadEntityFixtures,
    makeMockRequest,
    prepareEntityFixtures: fixtures_1.prepareEntityFixtures,
    resetSrcModuleCache,
    SqlTestingHelper: sql_2.SqlTestingHelper,
    setDefaultDatabaseConfig
};
const DATABASE_ENV_KEYS = [
    'DB_ADAPTER',
    'MYSQL_HOST',
    'MYSQL_PORT',
    'MYSQL_USER',
    'MYSQL_PASSWORD_SECRET',
    'MYSQL_DATABASE',
    'MYSQL_CONNECTION_LIMIT',
    'MYSQL_MIN_IDLE_CONNECTIONS',
    'MYSQL_IDLE_TIMEOUT_SECONDS',
    'PG_HOST',
    'PG_PORT',
    'PG_USER',
    'PG_PASSWORD_SECRET',
    'PG_DATABASE',
    'PG_SCHEMA',
    'PG_SSL',
    'PG_SSL_REJECT_UNAUTHORIZED',
    'PG_CONNECTION_LIMIT',
    'PG_IDLE_TIMEOUT_SECONDS',
    'TSF_TEST_DATABASE_NAME',
    'TSF_TEST_ALLOW_SAVEPOINTS',
    'TSF_TEST_MYSQL_SESSION_KEY',
    'TSF_TEST_MYSQL_SESSION_LEASE_ID',
    'TSF_TEST_MYSQL_SESSION_DATABASE'
];
let nextDatabaseId = 1;
let nextInlineMigrationId = 1;
let sharedDatabaseCleanupInstalled = false;
let sharedDatabaseCleanupStarted = false;
const inlineMigrationIds = new WeakMap();
const sharedTestDatabases = new Map();
function resolveTestDbAdapter(config, explicit) {
    if (explicit)
        return explicit;
    if (config.DB_ADAPTER === 'mysql' || config.DB_ADAPTER === 'postgres')
        return config.DB_ADAPTER;
    if (env_1.Env.DB_ADAPTER === 'mysql' || env_1.Env.DB_ADAPTER === 'postgres')
        return env_1.Env.DB_ADAPTER;
    const hasPostgres = !!(config.PG_HOST ?? env_1.Env.PG_HOST);
    const hasMySQL = !!(config.MYSQL_HOST ?? env_1.Env.MYSQL_HOST);
    if (hasPostgres && !hasMySQL)
        return 'postgres';
    if (hasMySQL && !hasPostgres)
        return 'mysql';
    throw new Error("TestingFacade enableDatabase requires dbAdapter or DB_ADAPTER to be 'mysql' or 'postgres'");
}
function createTestDatabaseName(prefix = 'test') {
    const ts = env_1.Env.TEST_RUN_TS ?? String(Math.floor(Date.now() / 1000));
    return (0, database_name_1.formatTestDatabaseName)(prefix, [ts, process.pid, nextDatabaseId++]);
}
function getInlineMigrationIdentity(migration) {
    let id = inlineMigrationIds.get(migration);
    if (!id) {
        id = nextInlineMigrationId++;
        inlineMigrationIds.set(migration, id);
    }
    return id;
}
function installSharedDatabaseCleanup() {
    if (sharedDatabaseCleanupInstalled)
        return;
    sharedDatabaseCleanupInstalled = true;
    process.once('beforeExit', () => {
        void cleanupSharedTestDatabases();
    });
}
async function cleanupSharedTestDatabases() {
    if (sharedDatabaseCleanupStarted)
        return;
    sharedDatabaseCleanupStarted = true;
    const states = [...sharedTestDatabases.values()];
    sharedTestDatabases.clear();
    for (const state of states) {
        if (state.keepDatabase || env_1.Env.TEST_KEEP_DB)
            continue;
        try {
            await state.destroy();
        }
        catch {
            // Best-effort process cleanup should not mask the original test result.
        }
    }
}
function clearSharedTestDatabases(prefix, adapter) {
    for (const [key, state] of sharedTestDatabases) {
        if (adapter && state.adapter !== adapter)
            continue;
        if (!state.databaseName.startsWith(prefix))
            continue;
        sharedTestDatabases.delete(key);
    }
}
function snapshotDatabaseEnv() {
    const snapshot = {};
    for (const key of DATABASE_ENV_KEYS) {
        if (env_1.Env[key] !== undefined)
            snapshot[key] = env_1.Env[key];
    }
    return snapshot;
}
function restoreDatabaseEnv(snapshot) {
    for (const key of DATABASE_ENV_KEYS) {
        if (snapshot[key] === undefined)
            delete env_1.Env[key];
        else
            env_1.Env[key] = snapshot[key];
    }
}
function snapshotAppDatabaseConfig(config) {
    return {
        DB_ADAPTER: config.DB_ADAPTER,
        MYSQL_DATABASE: config.MYSQL_DATABASE,
        PG_DATABASE: config.PG_DATABASE
    };
}
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"AsymmetricMatcher\":{\"kind\":18,\"typeName\":\"AsymmetricMatcher\",\"types\":[],\"index\":{\"kind\":10,\"literal\":true}},\"MockBody\":{\"kind\":2,\"typeName\":\"MockBody\"},\"MockHeaders\":{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":2}],\"index\":{\"kind\":2},\"types\":[],\"typeName\":\"MockHeaders\"},\"MockMethod\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"GET\"},{\"kind\":10,\"literal\":\"PUT\"},{\"kind\":10,\"literal\":\"POST\"},{\"kind\":10,\"literal\":\"PATCH\"},{\"kind\":10,\"literal\":\"DELETE\"}],\"typeName\":\"MockMethod\"},\"StandardHookOptions\":{\"kind\":18,\"typeName\":\"StandardHookOptions\",\"types\":[{\"kind\":20,\"name\":\"suiteSeedData\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"TestingFacadeSeedData\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true}]},\"TestDbAdapter\":{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"mysql\"},{\"kind\":10,\"literal\":\"postgres\"}],\"typeName\":\"DatabaseDialect\",\"typeName\":\"TestDbAdapter\"},\"TestingFacadeOptions\":{\"kind\":18,\"typeName\":\"TestingFacadeOptions\",\"types\":[{\"kind\":20,\"name\":\"defaultTestHeaders\",\"type\":{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"Record\",\"utilityType\":\"Record\",\"typeArguments\":[{\"kind\":6},{\"kind\":6}],\"index\":{\"kind\":6},\"types\":[]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"seedData\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"TestingFacadeSeedData\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"C\"}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"autoSeedData\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"onBeforeStart\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(facade: TestingFacade\\u003cC\\u003e) =\\u003e Promise\\u003cvoid\\u003e | void\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"onStart\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(facade: TestingFacade\\u003cC\\u003e) =\\u003e Promise\\u003cvoid\\u003e | void\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"onBeforeStop\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(facade: TestingFacade\\u003cC\\u003e) =\\u003e Promise\\u003cvoid\\u003e | void\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"onStop\",\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"(facade: TestingFacade\\u003cC\\u003e) =\\u003e Promise\\u003cvoid\\u003e | void\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableDatabase\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"dbAdapter\",\"type\":{\"kind\":12,\"types\":[{\"kind\":12,\"types\":[{\"kind\":10,\"literal\":\"mysql\"},{\"kind\":10,\"literal\":\"postgres\"}],\"typeName\":\"DatabaseDialect\",\"typeName\":\"TestDbAdapter\"},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"useSavepoints\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"databasePrefix\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"keepDatabase\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"enableMigrations\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"schemaFromEntities\",\"type\":{\"kind\":12,\"types\":[{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":18,\"typeName\":\"CreateMigrationPlanOptions\",\"types\":[{\"kind\":20,\"name\":\"pgSchema\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"tableNames\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":6}},{\"kind\":4}]},\"optional\":true}]}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"migrations\",\"type\":{\"kind\":12,\"types\":[{\"kind\":14,\"type\":{\"kind\":18,\"typeName\":\"Migration\",\"types\":[{\"kind\":20,\"name\":\"name\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"up\",\"type\":{\"kind\":2,\"typeName\":\"MigrationFunction\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"T\"}]},\"optional\":false}]}},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"migrationsDir\",\"type\":{\"kind\":12,\"types\":[{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":14,\"type\":{\"kind\":6}}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"truncateAfterMigrations\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]}}]", [], __tsf_metadata_type);
//# sourceMappingURL=index.js.map