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>; 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; /** 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 { 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. */ export type TypeNameIdentifier = string | 'bool' | 'bytea' | 'float4' | 'float8' | 'int2' | 'int4' | 'int8' | 'json' | 'text' | 'timestamptz' | 'uuid'; 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']; 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']; }