UNPKG

@melleb/pgtx

Version:

Simple node-postgres wrapper that abstracts transactions and savepoints

95 lines (91 loc) 2.83 kB
// src/TransactionContext.ts import { randomUUID } from "crypto"; // src/create-proxy.ts function createProxy(client, transactionContext) { return new Proxy( { client, transactionContext }, { get(target, prop) { if (prop in target.client) { return target.client[prop].bind(target.client); } if (prop in target.transactionContext) { return target.transactionContext[prop].bind( target.transactionContext ); } return void 0; } } ); } // src/TransactionContext.ts var TransactionContext = class _TransactionContext { constructor(client, parentTransaction, activeSavePoint) { this.client = client; this.parentTransaction = parentTransaction; this.activeSavePoint = activeSavePoint; } status = 0 /* ACTIVE */; async transaction(childTransaction) { let activeSavePointName = void 0; if (this.parentTransaction) { activeSavePointName = "sp_" + randomUUID().replaceAll("-", "_"); await this.client.query(`SAVEPOINT ${activeSavePointName}`); } else { await this.client.query("BEGIN"); } const childCtx = new _TransactionContext( this.client, this, activeSavePointName ); const proxy = createProxy(this.client, childCtx); if (!childTransaction) { return proxy; } try { await childTransaction(proxy); } catch (e) { await childCtx.rollback(); throw e; } if (childCtx.status === 0 /* ACTIVE */) { await childCtx.commit(); } return createProxy(this.client, this); } async commit() { if (this.status !== 0 /* ACTIVE */) { throw new Error("Cannot commit a transaction that is not active"); } if (this.parentTransaction && this.activeSavePoint) { await this.client.query(`RELEASE SAVEPOINT ${this.activeSavePoint}`); return createProxy(this.client, this.parentTransaction); } await this.client.query("COMMIT"); this.status = 1 /* COMMITTED */; return createProxy(this.client, this.parentTransaction || this); } async rollback() { if (this.status !== 0 /* ACTIVE */) { throw new Error("Cannot roll back a transaction that is not active"); } if (this.activeSavePoint && this.parentTransaction) { await this.client.query(`ROLLBACK TO SAVEPOINT ${this.activeSavePoint}`); this.status = 2 /* CANCELLED */; return createProxy(this.client, this.parentTransaction); } await this.client.query("ROLLBACK"); this.status = 2 /* CANCELLED */; return createProxy(this.client, this.parentTransaction || this); } }; // src/index.ts function pgtx(client) { const transactionContext = new TransactionContext(client); return createProxy(client, transactionContext); } export { pgtx as default };