"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = exports.DatabaseSession = void 0;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
const entity_1 = require("./entity");
const __tsf_metadata_type = _a.createCompactMetadataRegistryV1("[1,[{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"DriverConnection\",\"types\":[]},{\"kind\":4}]},{\"kind\":14,\"type\":{\"kind\":17}},{\"kind\":3},{\"kind\":2,\"typeName\":\"() =\\u003e Promise\\u003cT\\u003e\"},{\"kind\":22,\"type\":{\"kind\":2,\"typeName\":\"T\"}},{\"kind\":22,\"type\":{\"kind\":3}},{\"kind\":2,\"typeName\":\"EntityClass\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"T\"}]},{\"kind\":12,\"types\":[{\"kind\":18,\"typeName\":\"SqlQuery\",\"types\":[{\"kind\":20,\"name\":\"kind\",\"type\":{\"kind\":10,\"literal\":\"sql\"},\"optional\":false},{\"kind\":20,\"name\":\"chunks\",\"type\":{\"kind\":14,\"type\":{\"kind\":6}},\"optional\":false},{\"kind\":20,\"name\":\"values\",\"type\":{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"SqlValue\"}},\"optional\":false}]},{\"kind\":6}],\"typeName\":\"SqlInput\"},{\"kind\":22,\"type\":{\"kind\":14,\"type\":{\"kind\":2,\"typeName\":\"T\"}}},{\"kind\":22,\"type\":{\"kind\":12,\"types\":[{\"kind\":2,\"typeName\":\"T\"},{\"kind\":4}]}},{\"kind\":22,\"type\":{\"kind\":18,\"typeName\":\"ExecuteResult\",\"types\":[{\"kind\":20,\"name\":\"affectedRows\",\"type\":{\"kind\":7},\"optional\":false},{\"kind\":20,\"name\":\"insertId\",\"type\":{\"kind\":12,\"types\":[{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":6}]},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"warningStatus\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"rowCount\",\"type\":{\"kind\":12,\"types\":[{\"kind\":7},{\"kind\":4}]},\"optional\":true}]}},{\"kind\":18,\"typeName\":\"DatabaseSessionRawQuery\",\"types\":[]},{\"kind\":6},{\"kind\":14,\"type\":{\"kind\":2}},{\"kind\":2,\"typeName\":\"ReceiveType\",\"typeArguments\":[{\"kind\":2,\"typeName\":\"T\"}]},{\"kind\":2},{\"kind\":12,\"types\":[{\"kind\":1,\"typeName\":\"MutexKey\"},{\"kind\":14,\"type\":{\"kind\":1,\"typeName\":\"MutexKey\"}}]},{\"kind\":2,\"typeName\":\"() =\\u003e Promise\\u003cvoid\\u003e\"},{\"kind\":18,\"typeName\":\"DriverConnection\",\"types\":[]},{\"kind\":18,\"typeName\":\"DatabaseSessionOptions\",\"types\":[{\"kind\":20,\"name\":\"transactional\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]}]]", []);
let _DatabaseSessionMetadata_1;
class DatabaseSession {
    db;
    connection;
    options;
    queued = new Set();
    managed = new Set();
    removed = new Set();
    pendingOperations = new Set();
    pendingOperationErrors = [];
    preCommitHooks = [];
    postCommitHooks = [];
    flushing = false;
    autoFlushSuppressionDepth = 0;
    constructor(db, connection, options = {}) {
        this.db = db;
        this.connection = connection;
        this.options = options;
    }
    getConnection() {
        return this.connection;
    }
    get isFlushing() {
        return this.flushing;
    }
    get shouldAutoFlush() {
        return !this.flushing && this.autoFlushSuppressionDepth === 0;
    }
    get isTransactional() {
        return this.options.transactional ?? false;
    }
    add(...entities) {
        for (const entity of entities) {
            this.removed.delete(entity);
            this.queued.add(entity);
        }
    }
    manage(...entities) {
        for (const entity of entities)
            this.managed.add(entity);
    }
    removeQueued(...entities) {
        for (const entity of entities)
            this.queued.delete(entity);
    }
    unmanage(...entities) {
        for (const entity of entities) {
            this.queued.delete(entity);
            this.managed.delete(entity);
            this.removed.delete(entity);
        }
    }
    remove(...entities) {
        for (const entity of entities) {
            const wasQueued = this.queued.delete(entity);
            if (!wasQueued || this.managed.has(entity) || (0, entity_1.hasEntitySnapshot)(entity)) {
                this.removed.add(entity);
            }
            this.managed.delete(entity);
        }
    }
    trackOperation(worker) {
        let promise;
        try {
            promise = worker();
        }
        catch (error) {
            promise = Promise.reject(error);
        }
        const tracked = promise.then(() => {
            this.pendingOperations.delete(tracked);
        }, error => {
            this.pendingOperationErrors.push(error);
            this.pendingOperations.delete(tracked);
        });
        this.pendingOperations.add(tracked);
        promise.catch(() => { });
        tracked.catch(() => { });
        return promise;
    }
    async waitForPendingOperations() {
        while (this.pendingOperations.size) {
            await Promise.allSettled(this.pendingOperations);
        }
        if (this.pendingOperationErrors.length) {
            const [error] = this.pendingOperationErrors;
            this.pendingOperationErrors = [];
            throw error;
        }
    }
    async withoutAutoFlush(worker) {
        this.autoFlushSuppressionDepth++;
        try {
            return await worker();
        }
        finally {
            this.autoFlushSuppressionDepth--;
        }
    }
    query(Entity) {
        return this.db.query(Entity, this);
    }
    async rawQuery(input) {
        await this.flush();
        return this.db.rawQuery(input, this);
    }
    async rawFind(input) {
        return this.rawQuery(input);
    }
    async rawFindOne(input) {
        return (await this.rawFind(input))[0];
    }
    async rawExecute(input) {
        await this.flush();
        return this.db.rawExecute(input, this);
    }
    raw(input) {
        return {
            execute: () => this.rawExecute(input),
            find: () => this.rawFind(input),
            findOne: () => this.rawFindOne(input)
        };
    }
    async rawFindUnsafe(text, bindings = [], type) {
        await this.flush();
        return this.db.rawFindUnsafe(text, bindings, this, type);
    }
    async rawFindOneUnsafe(text, bindings = [], type) {
        await this.flush();
        return this.db.rawFindOneUnsafe(text, bindings, this, type);
    }
    async rawExecuteUnsafe(text, bindings = [], type) {
        await this.flush();
        return this.db.rawExecuteUnsafe(text, bindings, this, type);
    }
    async acquireSessionLock(key) {
        await this.db.acquireSessionLock(key, this);
    }
    async savepoint(name) {
        this.assertTransactional('savepoints');
        await this.flush();
        await this.connection.savepoint(name);
    }
    async rollbackToSavepoint(name) {
        this.assertTransactional('savepoints');
        await this.connection.rollbackToSavepoint(name);
    }
    async withSavepoint(name, worker) {
        await this.savepoint(name);
        const queuedBefore = new Set(this.queued);
        const managedBefore = new Set(this.managed);
        const removedBefore = new Set(this.removed);
        const preCommitHookCount = this.preCommitHooks.length;
        const postCommitHookCount = this.postCommitHooks.length;
        try {
            return await worker();
        }
        catch (error) {
            await this.rollbackToSavepoint(name);
            this.queued = queuedBefore;
            this.managed = managedBefore;
            this.removed = removedBefore;
            this.preCommitHooks.length = preCommitHookCount;
            this.postCommitHooks.length = postCommitHookCount;
            throw error;
        }
    }
    async flush() {
        if (this.flushing)
            return;
        this.flushing = true;
        try {
            for (const entity of [...this.queued]) {
                if (this.removed.has(entity)) {
                    this.queued.delete(entity);
                    continue;
                }
                await this.db.saveEntity(entity, this);
                this.queued.delete(entity);
                this.managed.add(entity);
            }
            for (const entity of [...this.managed]) {
                if (this.queued.has(entity))
                    continue;
                if (this.removed.has(entity))
                    continue;
                await this.db.saveEntity(entity, this);
            }
            for (const entity of [...this.removed]) {
                await this.db.deleteEntity(entity, this);
                this.unmanage(entity);
            }
        }
        finally {
            this.flushing = false;
        }
    }
    addPreCommitHook(hook) {
        this.preCommitHooks.push(hook);
    }
    addPostCommitHook(hook) {
        this.postCommitHooks.push(hook);
    }
    async runPreCommitHooks() {
        for (const hook of this.preCommitHooks)
            await hook();
    }
    async runPostCommitHooks() {
        for (const hook of this.postCommitHooks)
            await hook();
    }
    assertTransactional(feature) {
        if (!this.isTransactional || !this.connection)
            throw new Error(`Database ${feature} require an active transaction`);
    }
    static __tsfType = _DatabaseSessionMetadata_1 = _a.decodeCompactMetadataV1("[1,{\"kind\":16,\"name\":\"DatabaseSession\",\"typeName\":\"DatabaseSession\",\"classType\":{\"$tsf\":0},\"properties\":[],\"methods\":[{\"name\":\"getConnection\",\"parameters\":[],\"returnType\":{\"$tsfType\":0}},{\"name\":\"add\",\"parameters\":[{\"name\":\"entities\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"manage\",\"parameters\":[{\"name\":\"entities\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"removeQueued\",\"parameters\":[{\"name\":\"entities\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"unmanage\",\"parameters\":[{\"name\":\"entities\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"remove\",\"parameters\":[{\"name\":\"entities\",\"type\":{\"$tsfType\":1},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"trackOperation\",\"parameters\":[{\"name\":\"worker\",\"type\":{\"$tsfType\":3},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":4}},{\"name\":\"waitForPendingOperations\",\"parameters\":[],\"returnType\":{\"$tsfType\":5}},{\"name\":\"withoutAutoFlush\",\"parameters\":[{\"name\":\"worker\",\"type\":{\"$tsfType\":3},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":4}},{\"name\":\"query\",\"parameters\":[{\"name\":\"Entity\",\"type\":{\"$tsfType\":6},\"optional\":false,\"default\":false}],\"returnType\":{\"kind\":16,\"typeName\":\"QueryBuilder\",\"classType\":{\"$tsfImport\":[1,\"./query\",\"QueryBuilder\"]},\"typeArguments\":[{\"kind\":2,\"typeName\":\"T\"}]}},{\"name\":\"rawQuery\",\"parameters\":[{\"name\":\"input\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":8}},{\"name\":\"rawFind\",\"parameters\":[{\"name\":\"input\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":8}},{\"name\":\"rawFindOne\",\"parameters\":[{\"name\":\"input\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":9}},{\"name\":\"rawExecute\",\"parameters\":[{\"name\":\"input\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":10}},{\"name\":\"raw\",\"parameters\":[{\"name\":\"input\",\"type\":{\"$tsfType\":7},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":11}},{\"name\":\"rawFindUnsafe\",\"parameters\":[{\"name\":\"text\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false},{\"name\":\"bindings\",\"type\":{\"$tsfType\":13},\"optional\":true,\"default\":true},{\"name\":\"type\",\"type\":{\"$tsfType\":14},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":8}},{\"name\":\"rawFindOneUnsafe\",\"parameters\":[{\"name\":\"text\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false},{\"name\":\"bindings\",\"type\":{\"$tsfType\":13},\"optional\":true,\"default\":true},{\"name\":\"type\",\"type\":{\"$tsfType\":14},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":9}},{\"name\":\"rawExecuteUnsafe\",\"parameters\":[{\"name\":\"text\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false},{\"name\":\"bindings\",\"type\":{\"$tsfType\":13},\"optional\":true,\"default\":true},{\"name\":\"type\",\"type\":{\"$tsfType\":15},\"optional\":true,\"default\":false}],\"returnType\":{\"$tsfType\":10}},{\"name\":\"acquireSessionLock\",\"parameters\":[{\"name\":\"key\",\"type\":{\"$tsfType\":16},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":5}},{\"name\":\"savepoint\",\"parameters\":[{\"name\":\"name\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":5}},{\"name\":\"rollbackToSavepoint\",\"parameters\":[{\"name\":\"name\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":5}},{\"name\":\"withSavepoint\",\"parameters\":[{\"name\":\"name\",\"type\":{\"$tsfType\":12},\"optional\":false,\"default\":false},{\"name\":\"worker\",\"type\":{\"$tsfType\":3},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":4}},{\"name\":\"flush\",\"parameters\":[],\"returnType\":{\"$tsfType\":5}},{\"name\":\"addPreCommitHook\",\"parameters\":[{\"name\":\"hook\",\"type\":{\"$tsfType\":17},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"addPostCommitHook\",\"parameters\":[{\"name\":\"hook\",\"type\":{\"$tsfType\":17},\"optional\":false,\"default\":false}],\"returnType\":{\"$tsfType\":2}},{\"name\":\"runPreCommitHooks\",\"parameters\":[],\"returnType\":{\"$tsfType\":5}},{\"name\":\"runPostCommitHooks\",\"parameters\":[],\"returnType\":{\"$tsfType\":5}}],\"hasConstructor\":true,\"constructorParameters\":[{\"name\":\"db\",\"type\":{\"kind\":16,\"typeName\":\"BaseDatabase\",\"classType\":{\"$tsfImport\":[1,\"./database\",\"BaseDatabase\"]}},\"optional\":false,\"default\":false},{\"name\":\"connection\",\"type\":{\"$tsfType\":18},\"optional\":true,\"default\":false},{\"name\":\"options\",\"type\":{\"$tsfType\":19},\"optional\":true,\"default\":true}]}]", [() => DatabaseSession, require], __tsf_metadata_type);
}
exports.DatabaseSession = DatabaseSession;
_DatabaseSessionMetadata_1.classType = DatabaseSession;
DatabaseSession.__tsfType = _DatabaseSessionMetadata_1;
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"DatabaseSessionOptions\":{\"kind\":18,\"typeName\":\"DatabaseSessionOptions\",\"types\":[{\"kind\":20,\"name\":\"transactional\",\"type\":{\"kind\":12,\"types\":[{\"kind\":8},{\"kind\":4}]},\"optional\":true}]},\"DatabaseSessionRawQuery\":{\"kind\":18,\"typeName\":\"DatabaseSessionRawQuery\",\"types\":[]},\"DbSession\":{\"kind\":16,\"typeName\":\"DatabaseSession\",\"classType\":{\"$tsf\":0},\"typeName\":\"DbSession\"}}]", [() => (typeof DatabaseSession !== "undefined" ? DatabaseSession : (typeof exports !== "undefined" ? exports.DatabaseSession : undefined))], __tsf_metadata_type);
//# sourceMappingURL=session.js.map