import type { ConnectionPoolClient } from "./factories/createConnectionPool.js"; import type { IntegrityValidation } from "./routines/executeQuery.js"; import type { DriverFactory, DriverNotice, DriverStream, DriverTypeParser } from "@slonik/driver"; import type { DataIntegrityError, SlonikError } from "@slonik/errors"; import type { PrimitiveValueExpression, QuerySqlToken, SqlToken } from "@slonik/sql-tag"; import type { StandardSchemaV1 } from "@standard-schema/spec"; import type EventEmitter from "node:events"; import type { ConnectionOptions as TlsConnectionOptions } from "node:tls"; import type { Logger } from "roarr"; import type { StrictEventEmitter } from "strict-event-emitter-types"; export type ClientConfiguration = { /** * Dictates whether to capture stack trace before executing query. Middlewares access stack trace through query execution context. (Default: true) */ readonly captureStackTrace: boolean; /** * Number of times to retry establishing a new connection. (Default: 3) */ readonly connectionRetryLimit: number; /** * Timeout (in milliseconds) after which an error is raised if connection cannot cannot be established. (Default: 5000) */ readonly connectionTimeout: "DISABLE_TIMEOUT" | number; /** * Connection URI, e.g. `postgres://user:password@localhost/database`. */ readonly connectionUri: string; /** * Allow using connections that are not associated with the transaction. (Default: false) */ readonly dangerouslyAllowForeignConnections: boolean; /** * Overrides the default DriverFactory. (Default: "pg" driver factory) */ readonly driverFactory?: DriverFactory; /** * Timeout (in milliseconds) that kicks in after a connection with an active query is requested to end. This is the amount of time that is allowed for query to complete before terminating it. (Default: 5000) */ readonly gracefulTerminationTimeout: number; /** * Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000) */ readonly idleInTransactionSessionTimeout: "DISABLE_TIMEOUT" | number; /** * Timeout (in milliseconds) after which idle clients are closed. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 5000) */ readonly idleTimeout: "DISABLE_TIMEOUT" | number; /** * An array of [Slonik interceptors](https://github.com/gajus/slonik#slonik-interceptors). */ readonly interceptors: readonly Interceptor[]; /** * The maximum age of a connection allowed in the pool. * After this age, the connection will be destroyed. * @default 30 minutes */ readonly maximumConnectionAge?: "DISABLE_TIMEOUT" | number; /** * Do not allow more than this many connections. (Default: 10) */ readonly maximumPoolSize?: number; /** * Ensure that at least this many connections are available in the pool. (Default: 0) */ readonly minimumPoolSize?: number; /** * Number of times a query failing with Transaction Rollback class error, that doesn't belong to a transaction, is retried. (Default: 5) */ readonly queryRetryLimit: number; /** * Routine that's invoked to reset the connection. * The default routine invokes `DISCARD ALL`. */ readonly resetConnection?: (basicConnection: BasicConnection) => Promise; /** * tls.connect options * */ readonly ssl?: TlsConnectionOptions; /** * Timeout (in milliseconds) after which database is instructed to abort the query. Use 'DISABLE_TIMEOUT' constant to disable the timeout. (Default: 60000) */ readonly statementTimeout: "DISABLE_TIMEOUT" | number; /** * Number of times a transaction failing with Transaction Rollback class error is retried. (Default: 5) */ readonly transactionRetryLimit: number; /** * An array of [Slonik type parsers](https://github.com/gajus/slonik#slonik-type-parsers). */ readonly typeParsers: readonly DriverTypeParser[]; }; export type ClientConfigurationInput = Partial; export type CommonQueryMethods = { readonly any: QueryAnyFunction; readonly anyFirst: QueryAnyFirstFunction; readonly exists: QueryExistsFunction; readonly many: QueryManyFunction; readonly manyFirst: QueryManyFirstFunction; readonly maybeOne: QueryMaybeOneFunction; readonly maybeOneFirst: QueryMaybeOneFirstFunction; readonly one: QueryOneFunction; readonly oneFirst: QueryOneFirstFunction; readonly query: QueryFunction; readonly stream: StreamFunction; readonly transaction: ( handler: TransactionFunction, transactionRetryLimit?: number, ) => Promise; }; export type Connection = "EXPLICIT" | "IMPLICIT_QUERY" | "IMPLICIT_TRANSACTION"; /** * @see https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS */ export type ConnectionOptions = { applicationName?: string; databaseName?: string; host?: string; options?: string; password?: string; port?: number; sslMode?: "disable" | "no-verify" | "require"; username?: string; }; export type ConnectionRoutine = (connection: DatabasePoolConnection) => Promise; export type DatabaseConnection = DatabasePool | DatabasePoolConnection; export type DatabasePool = CommonQueryMethods & DatabasePoolEventEmitter & { readonly configuration: ClientConfiguration; readonly connect: (connectionRoutine: ConnectionRoutine) => Promise; readonly end: () => Promise; readonly state: () => PoolState; }; export type DatabasePoolConnection = CommonQueryMethods; export type DatabasePoolEventEmitter = StrictEventEmitter< EventEmitter, { error: (error: SlonikError) => void; } >; export type DatabaseTransactionConnection = CommonQueryMethods & DatabaseTransactionEventEmitter & { readonly transactionDepth: number; readonly transactionId: string; }; export type DatabaseTransactionEventEmitter = StrictEventEmitter< EventEmitter, { commit: (event: { transactionDepth: number; transactionId: string }) => void; rollback: (event: { error: Error; transactionDepth: number; transactionId: string }) => void; rollbackToSavepoint: (event: { error: Error; transactionDepth: number; transactionId: string; }) => void; savepoint: (event: { transactionDepth: number; transactionId: string }) => void; } >; export type Field = { readonly dataTypeId: number; readonly name: string; }; export type IdentifierNormalizer = (identifierName: string) => string; export type Interceptor = { readonly afterPoolConnection?: ( connectionContext: ConnectionContext, connection: DatabasePoolConnection, ) => MaybePromise; readonly afterQueryExecution?: ( queryContext: QueryContext, query: Query, result: QueryResult, ) => MaybePromise; readonly beforePoolConnection?: ( connectionContext: PoolContext, ) => MaybePromise; readonly beforePoolConnectionRelease?: ( connectionContext: ConnectionContext, connection: DatabasePoolConnection, ) => MaybePromise; readonly beforeQueryExecution?: ( queryContext: QueryContext, query: Query, ) => MaybePromise>; readonly beforeQueryResult?: ( queryContext: QueryContext, query: Query, result: QueryResult, ) => MaybePromise; readonly beforeTransformQuery?: (queryContext: QueryContext, query: Query) => MaybePromise; readonly dataIntegrityError?: ( queryContext: QueryContext, query: Query, error: DataIntegrityError, result: QueryResult, ) => MaybePromise; readonly name: string; readonly queryExecutionError?: ( queryContext: QueryContext, query: Query, error: SlonikError, notices: readonly DriverNotice[], ) => MaybePromise; readonly transformQuery?: (queryContext: QueryContext, query: Query) => Query; readonly transformRow?: ( queryContext: QueryContext, query: Query, row: QueryResultRow, fields: readonly Field[], ) => QueryResultRow; readonly transformRowAsync?: ( queryContext: QueryContext, query: Query, row: QueryResultRow, fields: readonly Field[], ) => Promise; }; export type InternalNestedTransactionFunction = ( log: Logger, connection: ConnectionPoolClient, clientConfiguration: ClientConfiguration, handler: TransactionFunction, transactionDepth: number, transactionRetryLimit?: number, eventEmitter?: DatabaseTransactionEventEmitter, transactionId?: string, ) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type InternalQueryMethod = ( log: Logger, connection: ConnectionPoolClient, clientConfiguration: ClientConfiguration, slonikSql: QuerySqlToken, uid?: QueryId, integrityValidation?: IntegrityValidation, ) => R; export type InternalStreamFunction = ( log: Logger, connection: ConnectionPoolClient, clientConfiguration: ClientConfiguration, slonikSql: QuerySqlToken, streamHandler: StreamHandler, uid?: QueryId, ) => Promise; export type InternalTransactionFunction = ( log: Logger, connection: ConnectionPoolClient, clientConfiguration: ClientConfiguration, handler: TransactionFunction, transactionRetryLimit?: number, eventEmitter?: DatabaseTransactionEventEmitter, transactionId?: string, ) => Promise; export type MaybePromise = Promise | T; /** * @property log Instance of Roarr logger with bound connection context parameters. * @property poolId Unique connection pool ID. * @property query The query that is initiating the connection. */ export type PoolContext = { readonly log: Logger; readonly poolId: string; readonly query: null | QuerySqlToken; }; export type Query = { /** * Optional name for the prepared statement. When provided, PostgreSQL will * create a named prepared statement that can be reused across multiple executions. */ readonly name?: string; readonly sql: string; readonly values: readonly PrimitiveValueExpression[]; }; /** * @property connectionId Unique connection ID. * @property log Instance of Roarr logger with bound query context parameters. * @property originalQuery A copy of the query before `transformQuery` middleware. * @property poolId Unique connection pool ID. * @property queryId Unique query ID. * @property queryInputTime `process.hrtime.bigint()` for when query was received. * @property resultParser A Zod function that parses the query result. * @property sandbox Object used by interceptors to assign interceptor-specific, query-specific context. * @property transactionId Unique transaction ID. */ export type QueryContext = { readonly connectionId: string; readonly log: Logger; readonly originalQuery: Query; readonly poolId: string; readonly queryId: QueryId; readonly queryInputTime: bigint | number; readonly resultParser?: StandardSchemaV1; readonly sandbox: Record; readonly stackTrace: null | readonly CallSite[]; readonly transactionId: null | string; }; export type QueryFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise>>; export type QueryId = string; export type QueryResult = { readonly command: "COPY" | "DELETE" | "INSERT" | "SELECT" | "UPDATE"; readonly fields: readonly Field[]; readonly notices: readonly DriverNotice[]; readonly rowCount: number; readonly rows: readonly T[]; readonly type: "QueryResult"; }; export type QueryResultRow = Record; export type QueryResultRowColumn = PrimitiveValueExpression; export type StreamHandler = (stream: DriverStream) => void; export type StreamResult = { readonly notices: readonly DriverNotice[]; readonly type: "StreamResult"; }; /** * "string" type covers all type name identifiers – the literal values are added only to assist developer * experience with auto suggestions for commonly used type name identifiers. */ export type TypeNameIdentifier = | "bool" | "bytea" | "float4" | "float8" | "int2" | "int4" | "int8" | "json" | "text" | "timestamptz" | "uuid" | string; export type ValueExpression = PrimitiveValueExpression | SqlToken; type BasicConnection = { readonly query: (query: string) => Promise; }; type CallSite = { readonly columnNumber: null | number; readonly fileName: null | string; readonly functionName: null | string; readonly lineNumber: null | number; }; /** * @property connectionId Unique connection ID. * @property log Instance of Roarr logger with bound connection context parameters. * @property poolId Unique connection pool ID. */ type ConnectionContext = { readonly connectionId: string; readonly connectionType: Connection; readonly log: Logger; readonly poolId: string; }; type PoolState = { readonly acquiredConnections: number; readonly idleConnections: number; readonly pendingConnections: number; readonly pendingDestroyConnections: number; readonly pendingReleaseConnections: number; readonly state: PoolStateName; readonly waitingClients: number; }; type PoolStateName = "ACTIVE" | "ENDED" | "ENDING"; type QueryAnyFirstFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise[keyof StandardSchemaV1.InferOutput]>>; type QueryAnyFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise>>; type QueryExistsFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise; type QueryManyFirstFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise[keyof StandardSchemaV1.InferOutput]>>; type QueryManyFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise>>; type QueryMaybeOneFirstFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise[keyof StandardSchemaV1.InferOutput]>; type QueryMaybeOneFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise>; type QueryOneFirstFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise[keyof StandardSchemaV1.InferOutput]>; type QueryOneFunction = ( sql: QuerySqlToken, values?: PrimitiveValueExpression[], ) => Promise>; type StreamFunction = ( sql: QuerySqlToken, streamHandler: StreamHandler>, ) => Promise; type TransactionFunction = (connection: DatabaseTransactionConnection) => Promise; export type { Logger } from "roarr";