/* eslint-disable @typescript-eslint/no-explicit-any */ import pgPromise from 'pg-promise' import {StandardSchemaV1} from './standard-schema/contract' export interface SQLQuery, Values extends unknown[] = unknown[]> { token: 'sql' name: string sql: string values: Values parse: (input: unknown) => Row | Promise /** @internal "segments" is the array of strings that make up the SQL query, including any parameter placeholders like `$1`, `$2`., and literals like dynamic table names, etc. It is joined together to form the `sql` property. */ segments: () => string[] /** @internal */ templateArgs: () => [strings: readonly string[], ...inputParameters: readonly unknown[]] } export interface AwaitableSQLQuery, Values extends unknown[] = unknown[]> extends SQLQuery { then: (callback: (rows: Promise>) => U) => Promise } export type TimeUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' export type IntervalInput = Partial> // eslint-disable-next-line @typescript-eslint/no-explicit-any export type SQLQueryRowType> = ReturnType export type SQLQueryParameter = {token: string} export type First = T[keyof T] /** See https://node-postgres.com/apis/result - faithfully copied here to avoid a type dependency */ export interface FieldInfo { name: string dataTypeID: number } /** See https://node-postgres.com/apis/result - faithfully copied here to avoid a type dependency */ export interface Result { rows: Row[] fields: FieldInfo[] command: string rowCount: number | null } export type DriverQueryable = { result: (query: string, values?: unknown[]) => Promise> } export interface Queryable { query(query: SQLQuery): Promise> one(query: SQLQuery): Promise maybeOne(query: SQLQuery): Promise oneFirst(query: SQLQuery): Promise> maybeOneFirst(query: SQLQuery): Promise | null> any(query: SQLQuery): Promise anyFirst(query: SQLQuery): Promise>> many(query: SQLQuery): Promise manyFirst(query: SQLQuery): Promise>> /** Returns a new Queryable which wraps the `query` method to throw an error if any row contains null values */ noNulls: NonNullQueryable } export interface SQLQueryable extends Queryable { sql: SQLTag } export interface NonNullQueryable { query(query: SQLQuery): Promise>> one(query: SQLQuery): Promise> maybeOne(query: SQLQuery): Promise | null> oneFirst(query: SQLQuery): Promise>> maybeOneFirst(query: SQLQuery): Promise> | null> any(query: SQLQuery): Promise[]> anyFirst(query: SQLQuery): Promise>>> many(query: SQLQuery): Promise[]> manyFirst(query: SQLQuery): Promise>>> } /** * An augmented array type that includes helper getters which perform validation and type coercion on the rows. */ export type AwaitSqlResultArray = Row[] & { /** returns the single row in the array, @throws if the array length is not 1 */ one: Row /** returns the single row in the array, or `null` if the array length is 0 */ maybeOne: Row | null /** returns the first column of the single row in the array, @throws if the array length is not 1 */ oneFirst: First /** returns the first column of the single row in the array, or `null` if the array length is 0 */ maybeOneFirst: First | null // no `any` because this is already an array of the rows /** returns the first column of each row in the array */ anyFirst: Array> /** returns all rows in the array and @throws if the array is empty */ many: Row[] /** returns the first column of each row in the array and @throws if the array is empty */ manyFirst: Array> /** returns a new array of results after checking that every record contains no null values */ noNulls: AwaitSqlResultArray> } export type NonNullRow = { [K in keyof Row]: NonNullable } export interface Transactable extends SQLQueryable { transaction(callback: (connection: Transaction) => Promise): Promise } export interface Connection extends Transactable { // todo: consolidate this with `transactionInfo`, and include a nullable `parent` property connectionInfo: {pgp: pgPromise.ITask | pgPromise.IDatabase} } export interface Transaction extends Connection { transactionInfo: {pgp: pgPromise.ITask} } export interface Client extends SQLQueryable { options: ClientOptions pgp: ReturnType> pgpOptions: PGPOptions connectionString(): string end(): Promise connect(callback: (connection: Connection) => Promise): Promise task(callback: (connection: Connection) => Promise): Promise transaction(callback: (connection: Transaction) => Promise): Promise } export type PrimitiveValueExpression = Primitive export type ValueExpression = Primitive | SqlFragment export type MemberType = | 'text' | 'bool' | 'bytea' | 'char' | 'name' | 'int2' | 'int4' | 'int8' | 'int2vector' | 'float4' | 'float8' | 'numeric' | 'regproc' | 'regclass' | 'regtype' | 'regoper' | 'regoperator' | 'regconfig' | 'regdictionary' | 'uuid' | 'json' | 'jsonb' | 'xml' | 'point' | 'lseg' | 'path' | 'box' | 'polygon' | 'line' | 'cidr' | 'inet' | 'macaddr' | 'macaddr8' | 'bit' | 'varbit' | 'timestamp' | 'timestamptz' | 'date' | 'time' | 'timetz' | 'interval' | 'money' | 'oid' | 'tid' | 'xid' | 'cid' | 'vector' | 'bpchar' | 'varchar' | 'array' | 'abstime' | 'reltime' | 'tinterval' | 'circle' | 'tsquery' | 'tsvector' export type SqlFragment = { token: 'sql' sql: string values: unknown[] /** @internal */ templateArgs: () => [strings: readonly string[], ...inputParameters: readonly unknown[]] } /** * "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. */ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ export type TypeNameIdentifier = | string | 'bool' | 'bytea' | 'float4' | 'float8' | 'int2' | 'int4' | 'int8' | 'json' | 'text' | 'timestamptz' | 'uuid' /* eslint-enable @typescript-eslint/no-redundant-type-constituents */ export type SQLTagHelperParameters = { array: [values: readonly PrimitiveValueExpression[], memberType: MemberType] binary: [data: Buffer] date: [date: Date] fragment: [ parts: TemplateStringsArray, ...values: readonly (ValueExpression | {token: 'sql' | keyof SQLTagHelperParameters})[], ] identifier: [names: readonly string[]] interval: [interval: IntervalInput] join: [members: readonly ValueExpression[], glue: SqlFragment] json: [value: unknown] jsonb: [value: unknown] literalValue: [value: string] timestamp: [date: Date] unnest: [tuples: ReadonlyArray, columnTypes: TypeNameIdentifier[]] } export type Primitive = string | number | boolean | null export type SQLParameterNonPrimitive = ReturnType | SqlFragment export type SQLParameter = SQLParameterNonPrimitive | Primitive export type SQLParameterToken = SQLParameterNonPrimitive['token'] /** the type definition for *just* the function part of the `sql` tag */ export type SQLTagFunction = >( strings: TemplateStringsArray, ...parameters: Row extends {'~parameters': SQLParameter[]} ? Row['~parameters'] : SQLParameter[] ) => AwaitableSQLQuery : Row> /** the type definition for the helpers of the `sql` tag - these are helpers for creating parameters to go in a sql`...` template */ export type SQLTagHelpers = { [K in keyof SQLTagHelperParameters]: (...args: SQLTagHelperParameters[K]) => { token: K args: SQLTagHelperParameters[K] } } /** the type definition for the methods of the `sql` tag */ export type SQLMethodHelpers = { raw: (query: string, values?: unknown[]) => SQLQuery type: ( schema: StandardSchemaV1, ) => ( strings: TemplateStringsArray, ...parameters: Parameters ) => AwaitableSQLQuery } /** the full type for the `sql` tag - callable function, helpers and all */ export type SQLTag = SQLTagFunction & SQLTagHelpers & SQLMethodHelpers /** Called `pgp` in pg-promise docs */ export type PGPromiseInitializer = typeof pgPromise /** Called `IMain` in pg-promise */ export type PGPromiseDBConnector = ReturnType /** Looks like `[cn: string | pg.IConnectionParameters, dc?: any]` in pg-promise */ export type PGPromiseDBConnectorParameters = Parameters /** Looks like `pg.IConnectionParameters` in pg-promise */ export type PGPromiseDBConnectionOptions = Exclude export type PGTypes = ReturnType['pg']['types'] // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type export type ParseFn = Extract[number], Function> export type PGTypesBuiltins = PGTypes['builtins'] export type PGTypesBuiltinOid = PGTypesBuiltins[keyof PGTypesBuiltins] export type ApplyTypeParsers = (params: { setTypeParser: (oid: PGTypesBuiltinOid, parseFn: ParseFn) => void builtins: PGTypes['builtins'] }) => void export type PGPOptions = { initialize?: pgPromise.IInitOptions connect?: PGPromiseDBConnectionOptions } export interface ClientOptions { pgpOptions?: PGPOptions applyTypeParsers?: ApplyTypeParsers wrapQueryFn?: (queryFn: SQLQueryable['query']) => SQLQueryable['query'] }