//#region src/transaction.ts
var TransactionCapabilityError = class extends Error {
	reason;
	code = "transaction-capability";
	constructor(reason) {
		super(reason === "inactive" ? "Expected an active transaction minted by a @platform-modules/db transaction callback" : "Transaction identity does not match the active transaction");
		this.reason = reason;
		this.name = "TransactionCapabilityError";
	}
};
function isTransactionCapabilityError(error) {
	if (typeof error !== "object" || error === null) return false;
	const candidate = error;
	return candidate.code === "transaction-capability" && (candidate.reason === "inactive" || candidate.reason === "identity-mismatch");
}
const transactionAuthenticatorKey = Symbol.for("@platform-modules/db.transaction-authenticator");
const transactionIdentityValidatorKey = Symbol.for("@platform-modules/db.transaction-identity-validator");
function getActiveTransactionIdentity(tx) {
	const identity = Reflect.get(tx, transactionAuthenticatorKey)?.(tx);
	const validate = identity === void 0 ? void 0 : Reflect.get(identity, transactionIdentityValidatorKey);
	if (identity === void 0 || validate?.(tx) !== true) throw new TransactionCapabilityError("inactive");
	return identity;
}
function getTransactionIdentity(tx) {
	return getActiveTransactionIdentity(tx);
}
function assertTransactionIdentity(tx, expectedIdentity) {
	if (getTransactionIdentity(tx) !== expectedIdentity) throw new TransactionCapabilityError("identity-mismatch");
}
function hasTransactionProperty(handle, property) {
	if (property === "execute") return handle.execute !== void 0 || handle.all !== void 0;
	if (property === "transaction") return handle.transaction !== void 0;
	return Reflect.has(handle, property);
}
const transactionProperties = /* @__PURE__ */ new Set([
	"query",
	"$with",
	"$count",
	"with",
	"select",
	"selectDistinct",
	"selectDistinctOn",
	"update",
	"insert",
	"delete",
	"refreshMaterializedView",
	"run",
	"all",
	"get",
	"values",
	"execute",
	"transaction"
]);
const terminalOperationProperties = /* @__PURE__ */ new Set([
	"execute",
	"run",
	"all",
	"get",
	"findMany",
	"findFirst"
]);
function isPromiseLike(value) {
	return (typeof value === "object" && value !== null || typeof value === "function") && typeof Reflect.get(value, "then") === "function";
}
function normalizeRows(result) {
	if (typeof result === "object" && result !== null && "rows" in result && Array.isArray(result.rows)) return result.rows;
	return result;
}
function guardValue(value, assertActive, trackOperation) {
	if ((typeof value !== "object" || value === null) && typeof value !== "function") return value;
	const source = value;
	return new Proxy({}, {
		get(_target, property) {
			assertActive();
			const member = Reflect.get(source, property, source);
			if (typeof member === "function") return (...args) => {
				const result = Reflect.apply(member, source, args);
				if (terminalOperationProperties.has(property) && isPromiseLike(result)) return guardValue(trackOperation(result), assertActive, trackOperation);
				if (property === "then") return trackOperation(Promise.resolve(result));
				return guardValue(result, assertActive, trackOperation);
			};
			return guardValue(member, assertActive, trackOperation);
		},
		getOwnPropertyDescriptor(_target, property) {
			assertActive();
			if (!Reflect.has(source, property)) return void 0;
			return {
				configurable: true,
				enumerable: Reflect.getOwnPropertyDescriptor(source, property)?.enumerable ?? false,
				value: guardValue(Reflect.get(source, property, source), assertActive, trackOperation),
				writable: false
			};
		},
		getPrototypeOf() {
			assertActive();
			return null;
		},
		has(_target, property) {
			assertActive();
			return Reflect.has(source, property);
		},
		ownKeys() {
			assertActive();
			return Reflect.ownKeys(source);
		},
		set() {
			return false;
		},
		defineProperty() {
			return false;
		},
		deleteProperty() {
			return false;
		},
		setPrototypeOf() {
			return false;
		},
		preventExtensions() {
			return false;
		},
		isExtensible() {
			assertActive();
			return true;
		}
	});
}
async function runTransaction(owner, transaction, callback, config) {
	return await transaction.call(owner, async (raw) => {
		let active = true;
		let facade;
		let identity;
		const operations = [];
		const trackOperation = (operation) => {
			const promise = Promise.resolve(operation);
			operations.push(promise);
			promise.catch(() => void 0);
			return promise;
		};
		const settleOperations = async () => {
			let index = 0;
			let rejected = false;
			let rejection;
			while (index < operations.length) {
				const end = operations.length;
				const results = await Promise.allSettled(operations.slice(index, end));
				for (const result of results) if (result.status === "rejected" && !rejected) {
					rejected = true;
					rejection = result.reason;
				}
				index = end;
			}
			if (rejected) throw rejection;
		};
		const validateIdentity = (candidate) => active && candidate === facade;
		const authenticate = (candidate) => validateIdentity(candidate) ? identity : void 0;
		const assertActive = () => {
			getActiveTransactionIdentity(facade);
		};
		facade = new Proxy({}, {
			get(_target, property) {
				if (property === transactionAuthenticatorKey) return authenticate;
				if (typeof property === "number" || !transactionProperties.has(property)) return void 0;
				const handle = raw;
				if (property === "execute") return (...args) => {
					try {
						assertActive();
						const execute = handle.execute ?? handle.all;
						if (execute === void 0) throw new TransactionCapabilityError("inactive");
						return trackOperation(Promise.resolve(execute.apply(raw, args)).then(normalizeRows));
					} catch (error) {
						const rejection = Promise.reject(error);
						rejection.catch(() => void 0);
						return rejection;
					}
				};
				if (property === "transaction" && handle.transaction !== void 0) return async (nestedCallback, nestedConfig) => {
					assertActive();
					return trackOperation(runTransaction(raw, handle.transaction, nestedCallback, nestedConfig));
				};
				const member = Reflect.get(raw, property, raw);
				if (typeof member === "function") return (...args) => {
					assertActive();
					const result = Reflect.apply(member, raw, args);
					if ((terminalOperationProperties.has(property) || property === "values") && isPromiseLike(result)) return guardValue(trackOperation(result), assertActive, trackOperation);
					return guardValue(result, assertActive, trackOperation);
				};
				assertActive();
				return guardValue(member, assertActive, trackOperation);
			},
			getOwnPropertyDescriptor(_target, property) {
				if (typeof property === "number" || !transactionProperties.has(property) || !hasTransactionProperty(raw, property)) return void 0;
				assertActive();
				return {
					configurable: true,
					enumerable: false,
					value: Reflect.get(facade, property),
					writable: false
				};
			},
			getPrototypeOf() {
				assertActive();
				return null;
			},
			has(_target, property) {
				assertActive();
				return typeof property !== "number" && transactionProperties.has(property) && hasTransactionProperty(raw, property);
			},
			ownKeys() {
				assertActive();
				return [...transactionProperties].filter((property) => hasTransactionProperty(raw, property));
			},
			set() {
				return false;
			},
			defineProperty() {
				return false;
			},
			deleteProperty() {
				return false;
			},
			setPrototypeOf() {
				return false;
			},
			preventExtensions() {
				return false;
			},
			isExtensible() {
				assertActive();
				return true;
			}
		});
		identity = Object.freeze(Object.defineProperty({ $platformTransactionIdentity: "v1" }, transactionIdentityValidatorKey, { value: validateIdentity }));
		try {
			const result = await callback(facade);
			await settleOperations();
			return result;
		} catch (error) {
			await settleOperations().catch(() => void 0);
			throw error;
		} finally {
			active = false;
		}
	}, config);
}
/**
* Brands a driver-built handle so its transactions carry platform transaction identity. First-party
* adapters apply it for you. It is public because a consumer that constructs its own driver client — a
* real-Postgres test harness, or any driver with no first-party adapter — otherwise cannot produce a
* value satisfying `TransactionalDatabase`, and reaching into `db/src` for it breaks the moment the
* package is consumed as published `dist`.
*/
function withTransactionIdentity(database) {
	const transaction = database.transaction;
	if (transaction === void 0) return database;
	Object.defineProperty(database, "transaction", {
		configurable: true,
		value: (callback, config) => runTransaction(database, transaction, callback, config),
		writable: true
	});
	return database;
}
//#endregion
export { withTransactionIdentity as a, isTransactionCapabilityError as i, assertTransactionIdentity as n, getTransactionIdentity as r, TransactionCapabilityError as t };
