"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseError = exports.sql = exports.jsonArrayFrom = void 0;
exports.useKysely = useKysely;
exports.useAsyncPostgres = useAsyncPostgres;
exports.useAsyncKysely = useAsyncKysely;
exports.usePostgres = usePostgres;
exports.forceCloseAllPostgres = forceCloseAllPostgres;
exports.withStack = withStack;
const utils_1 = require("@finalytic/utils");
const kysely_1 = require("kysely");
const kysely_postgres_js_1 = require("kysely-postgres-js");
const postgres_1 = __importDefault(require("postgres"));
var postgres_2 = require("kysely/helpers/postgres");
Object.defineProperty(exports, "jsonArrayFrom", { enumerable: true, get: function () { return postgres_2.jsonArrayFrom; } });
__exportStar(require("./v1.generated"), exports);
const node_path_1 = require("node:path");
var kysely_2 = require("kysely");
Object.defineProperty(exports, "sql", { enumerable: true, get: function () { return kysely_2.sql; } });
/**
 * @deprecated This method is deprecated in favor of the useAsyncKysely method.
 */
function useKysely(postgres, options = {}) {
    console.debug('Creating Kysely instance');
    const kysely = new kysely_1.Kysely({
        dialect: new kysely_postgres_js_1.PostgresJSDialect({
            postgres,
        }),
        plugins: [new kysely_1.CamelCasePlugin()],
        log: options?.log ? ['query', 'error'] : undefined,
    });
    const destroy = kysely.destroy;
    kysely.destroy = async () => {
        console.debug('Closing Kysely connection');
        return destroy();
    };
    return kysely;
}
const _connections = [];
async function useAsyncPostgres(connectionString, opt) {
    const delayMs = 2500;
    const retries = opt?.retries === false ? 0 : opt?.retries || 3;
    const ssl = opt?.forceDisableSSL !== true;
    console.debug(`Creating Postgres connection with ${retries} retries and ssl=${ssl}`);
    if (_connections.length > 0) {
        console.warn(`We already have ${_connections.length} Postgres connections`);
    }
    for (let attempt = 1; attempt <= retries; attempt++) {
        try {
            const sql = (0, postgres_1.default)(connectionString, {
                ssl: ssl ? 'require' : false,
                // connect_timeout: 10
                // idle_timeout: 10
                // disable prepare statements since we use pgbouncer
                prepare: false,
                connect_timeout: 10,
                idle_timeout: 60,
            });
            await sql `select 1`;
            try {
                // connect 30s, idle 60s, max lifetime 45min, statement 60s, lock 10s, log_min_duration 20s, log_lock_waits off
                await sql `set statement_timeout to '30s';`;
                await sql `set lock_timeout to '10s';`;
            }
            catch (e) {
                console.warn('Failed to set statement timeout, continuing', e);
            }
            _connections.push(sql);
            return sql;
        }
        catch (err) {
            if (attempt === retries)
                throw err;
            console.warn(`Postgres connection failed (attempt ${attempt}), retrying...`);
            console.error('error', err);
            await new Promise((res) => setTimeout(res, delayMs));
        }
    }
    throw new Error('Unable to connect to Postgres');
}
async function useAsyncKysely(connectionString, opt) {
    const sql = await useAsyncPostgres(connectionString, opt);
    return useKysely(sql);
}
/**
 * @deprecated This method is deprecated in favor of the useAsyncKysely method.
 */
function usePostgres(connectionString, opt) {
    if (!connectionString)
        throw new Error('No connection string');
    const ssl = opt?.forceDisableSSL !== true;
    const pg = (0, postgres_1.default)(connectionString, {
        ssl: ssl ? 'require' : false,
    });
    _connections.push(pg);
    return pg;
}
async function forceCloseAllPostgres(timeoutMs = 5000) {
    await Promise.all(_connections.map((pg) => pg.end({ timeout: timeoutMs }).catch(() => undefined)));
    _connections.length = 0;
}
class DatabaseError extends Error {
    constructor(message, cause) {
        super(message);
        this.cause = cause;
    }
}
exports.DatabaseError = DatabaseError;
const dirname = typeof __dirname === 'string' ? (0, node_path_1.join)(__dirname, '../../..') : '';
async function withStack(a) {
    // skip if we can't get the stack, probably in a worker
    if (!dirname)
        return await a;
    const now = performance.now();
    const error = new DatabaseError('Error in execute', undefined);
    const stack = error.stack?.split('\n').slice(3) || undefined;
    const firstLine = stack?.length && stack[0]?.includes('(') && dirname
        ? (0, node_path_1.relative)(dirname, stack[0].split('(')[1].split(')')[0])
        : undefined;
    try {
        const result = await a;
        console.log(`[${firstLine}] Execution time: ${Math.round(performance.now() - now)}ms`);
        return result;
    }
    catch (e) {
        if (stack)
            error.stack = stack.join('\n').trim();
        else
            error.stack = e.stack;
        // error.cause = e;
        error.message = e.message;
        const serialized = (0, utils_1.serializeError)(e);
        delete serialized.stack;
        error.cause = serialized;
        console.error(`Error in execute: ${firstLine}`, (0, utils_1.serializeError)(error));
        console.log(`[${firstLine}] Execution time: ${Math.round(performance.now() - now)}ms`);
        throw error;
    }
}
//# sourceMappingURL=index.js.map