import { isModuleDatabaseError, type ModuleDatabaseError, type ModuleDatabaseParameter, type ModuleDatabaseQuery, type ModuleSchemaMetadataDefinition, parseModuleSchemaMetadataDefinition, } from "@ekklesia/contracts"; /** * `@ekklesia/module-database`: everything a module needs for the Module Database ABI v1 (ADR 0050), * with no ORM. Drizzle support lives in the `@ekklesia/module-database/drizzle` subpath, the only * place that needs Drizzle (ADR 0053). */ export { isModuleDatabaseError, MODULE_DATABASE_ABI_VERSION, MODULE_DATABASE_ERROR_CODES, type ModuleDatabaseAbiVersion, type ModuleDatabaseArrayRow, ModuleDatabaseError, type ModuleDatabaseErrorCode, type ModuleDatabaseExecutor, type ModuleDatabaseField, type ModuleDatabaseIsolationLevel, type ModuleDatabaseObjectRow, type ModuleDatabaseParameter, type ModuleDatabaseQuery, type ModuleDatabaseQueryable, type ModuleDatabaseQueryResult, type ModuleDatabaseRowMode, type ModuleDatabaseScalar, type ModuleDatabaseTransaction, type ModuleDatabaseTransactionOptions, type ModuleDatabaseValue, type ModuleSchemaForeignKeyMetadata, type ModuleSchemaMetadataDefinition, type ModuleSchemaTableMetadata, moduleTableName, moduleTablePrefix, } from "@ekklesia/contracts"; /** * A module's plain schema metadata (ADR 0050): the tables it owns and their foreign keys, with no * ORM object. Validated and frozen; a Drizzle module derives the same data with * `moduleDrizzleSchemaMetadata` from `@ekklesia/module-database/drizzle`. */ export function defineModuleSchemaMetadata( definition: Definition, ): Readonly { return parseModuleSchemaMetadataDefinition(definition) as Readonly; } /** SQL text and values ready to pass directly to a Module Database ABI v1 executor. */ export type ModuleSqlQuery = Readonly>>; /** * Builds one PostgreSQL statement without ever interpolating a value into its text. * * Every interpolation becomes the next positional parameter. Dynamic identifiers and trusted SQL * fragments are deliberately unsupported in v1: keep identifiers in the static template text. */ export function sql( strings: TemplateStringsArray, ...values: readonly ModuleDatabaseParameter[] ): ModuleSqlQuery { let text = strings[0] ?? ""; for (let index = 0; index < values.length; index += 1) { text += `$${index + 1}${strings[index + 1] ?? ""}`; } return Object.freeze({ text, values: Object.freeze([...values]) }); } export type EkklesiaDatabaseError = Pick< ModuleDatabaseError, "code" | "message" | "name" | "sqlState" >; /** * Finds a stable Ekklesia database error through wrappers such as DrizzleQueryError. * * Callers inspect the returned `code` and `sqlState`; they never need a PostgreSQL driver error, * `.cause.code`, or message parsing. */ export function findEkklesiaDatabaseError(value: unknown): EkklesiaDatabaseError | undefined { const visited = new Set(); let current = value; while (current instanceof Error && !visited.has(current)) { if (isModuleDatabaseError(current)) { return current; } visited.add(current); current = current.cause; } return undefined; }