import type {
IAdapter,
LogbunLog,
LogbunQueryFilters,
LogbunQueryResult,
} from 'logbun';
class PostgresAuditAdapter implements IAdapter {
async init(): Promise<void> {
// Must be idempotent: ConnectionPool always calls init() after adapterFactory.
}
async bulkInsert(
tenantId: string | null,
logs: LogbunLog[],
): Promise<boolean> {
// Prefer idempotent insert on log.id.
// return true on success; false is a soft/retryable failure.
// Prefer throwing on unexpected failures so the engine can emit error text.
return true;
}
async query(
tenantId: string | null,
filters: LogbunQueryFilters,
pagination: { cursor?: string; limit: number },
): Promise<LogbunQueryResult> {
return { logs: [], nextCursor: null };
}
async prune(days: number): Promise<void> {
// delete older than `days` (finite integer >= 1)
}
async close(): Promise<void> {}
}Contract
- Idempotent on
idso WAL replay / retries do not duplicate rows. - Honor
tenantId:null= unscoped;''= tenant-less rows only; any other string = that tenant. - Cursor: newest-first pages (
ORDER BY id DESC) andnextCursor= last row’sidwhen more pages exist.pagination.limitis required onIAdapter.query. - Empty
bulkInsert: returntrue. - Closed adapter:
query/bulkInsert/prunemust throw when closed or not initialized (do not return empty results as if no rows existed). - Integrity fields: persist
prevHash/contentHashwhenintegrityChainis enabled. After restart the tip is restored from WAL, then the newest destination row (limit: 1). - Prune:
daysis a finite number>= 1. Prefer bounded batches. Built-in SQLite/Turso throwprune_incompletewhen the 10_000 × 1_000 safety cap is hit sorunMaintenance()can reject and the host can schedule a follow-up.
AuditLogger.query() throws AuditLogger is not initialized — query unavailable when the logger is shut down or bootstrap failed. Built-in destination adapters throw from query / bulkInsert / prune if they are closed or not initialized.