import type { AsyncClient, Client, SqlQuery, SyncClient } from './types.js'; import { createOtelHook } from './otel.js'; export type QueryOperation = 'all' | 'run'; export interface QueryExecutionContext { query: SqlQuery; operation: QueryOperation; system: string; } export interface SyncQueryExecutionHookArgs { context: QueryExecutionContext; execute: () => TResult; } export interface AsyncQueryExecutionHookArgs { context: QueryExecutionContext; execute: () => Promise; } export type QueryExecutionHookArgs = SyncQueryExecutionHookArgs | AsyncQueryExecutionHookArgs; export type SyncQueryExecutionHook = (args: SyncQueryExecutionHookArgs) => TResult; export type AsyncQueryExecutionHook = (args: AsyncQueryExecutionHookArgs) => Promise; export interface QueryExecutionHook { sync: SyncQueryExecutionHook; async: AsyncQueryExecutionHook; } export type SyncQueryExecutionHookInput = SyncQueryExecutionHook | QueryExecutionHook; export type AsyncQueryExecutionHookInput = AsyncQueryExecutionHook | QueryExecutionHook; /** * Wrap a Client so every `all`/`run` flows through `hook`. `raw`, `iterate`, * and `transaction` are passed through unchanged. Queries issued inside a * transaction still fire the hook because the tx client is re-instrumented. */ export declare function instrumentClient(client: SyncClient, hook: SyncQueryExecutionHookInput): SyncClient; export declare function instrumentClient(client: AsyncClient, hook: AsyncQueryExecutionHookInput): AsyncClient; export declare function instrumentClient(client: TClient, hook: QueryExecutionHook): TClient; /** * Run `hooks` left-to-right, each wrapping the next. The first hook is the * outermost — it sees the call before any others and gets the final result * or error last. */ export declare function composeSyncHooks(...hooks: SyncQueryExecutionHookInput[]): SyncQueryExecutionHook; /** * Async variant of `composeSyncHooks`. The first hook is still outermost. */ export declare function composeAsyncHooks(...hooks: AsyncQueryExecutionHookInput[]): AsyncQueryExecutionHook; export declare function composeHooks(...hooks: QueryExecutionHook[]): QueryExecutionHook; export interface QueryErrorReport { context: QueryExecutionContext; error: unknown; } /** * Reference error-reporter hook. Invokes `report` whenever a query throws * (or its promise rejects), then always rethrows so higher hooks (and the * caller) still see the error. `report`'s return value is discarded; any * promise it returns is not awaited. * * Like `createOtelHook`, this is a deliberately small reference impl — if * you want extra context (breadcrumbs, rate limiting, redaction), copy * this body and edit it. * * Useful for Sentry-style capture without pulling Sentry into the library: * `createErrorReporterHook(({ context, error }) => Sentry.captureException(error, { tags: { 'db.query.summary': context.query.name || 'sql' } }))`. */ export declare function createErrorReporterHook(report: (params: QueryErrorReport) => unknown): QueryExecutionHook; interface InstrumentFn { (client: SyncClient, ...hooks: SyncQueryExecutionHookInput[]): SyncClient; (client: AsyncClient, ...hooks: AsyncQueryExecutionHookInput[]): AsyncClient; (client: TClient, ...hooks: QueryExecutionHook[]): TClient; otel: typeof createOtelHook; onError: typeof createErrorReporterHook; } /** * Wrap a Client with one or more query-execution hooks. Hooks run * left-to-right — the first one is the outermost, seeing the call first * and the result/error last. * * ```ts * const client = instrument(baseClient, * instrument.otel({tracer}), * instrument.onError(({context, error}) => Sentry.captureException(error)), * ) * ``` * * `instrument.otel` and `instrument.onError` are small reference * implementations. If your team has different conventions (different * attribute names, extra resource attributes, rate limiting, redaction) * copy their bodies and edit them. The stable contract is * `QueryExecutionHook`; these helpers are just one way to satisfy it. */ export declare const instrument: InstrumentFn; export {};