/** * Drizzle ORM Driver for postgres.do * * This module provides the main entry point for using Drizzle ORM * with postgres.do. It creates a Drizzle database instance that * uses the postgres.do client for query execution. * * @example * ```typescript * import { drizzle } from 'postgres.do/drizzle' * import postgres from 'postgres.do' * import * as schema from './schema' * * const sql = postgres('postgres://db.postgres.do/mydb') * const db = drizzle(sql, { schema }) * * // Full type safety! * const users = await db.select().from(schema.users) * ``` */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { entityKind } from 'drizzle-orm/entity' import { DefaultLogger, type Logger } from 'drizzle-orm/logger' import { PgDatabase } from 'drizzle-orm/pg-core/db' import { PgDialect } from 'drizzle-orm/pg-core/dialect' import { createTableRelationsHelpers, extractTablesRelationalConfig, type RelationalSchemaConfig, type TablesRelationalConfig, } from 'drizzle-orm/relations' import type { DrizzleConfig } from 'drizzle-orm/utils' import type { Sql } from '../types.js' import { PostgresDoSession, type PostgresDoClient, type PostgresDoQueryResultHKT } from './session.js' /** * Driver options for postgres.do */ export interface PostgresDoDriverOptions { logger?: Logger } /** * Drizzle Database class for postgres.do * * This class extends PgDatabase with postgres.do-specific functionality. */ export class PostgresDoDatabase< TSchema extends Record = Record > extends PgDatabase { static override readonly [entityKind]: string = 'PostgresDoDatabase' } /** * Construct a Drizzle database instance from a postgres.do client */ function construct = Record>( client: PostgresDoClient, config: DrizzleConfig = {} ): PostgresDoDatabase & { $client: PostgresDoClient } { // Configure transparent parsers for date/time types // This ensures Drizzle handles the type conversions const transparentParser = (val: unknown) => val if (client.options) { const parsers = client.options.parsers ?? {} const serializers = client.options.serializers ?? {} // Date/time types that should be passed through const passThroughTypes = [ '1184', // timestamptz '1082', // date '1083', // time '1114', // timestamp '1182', // date[] '1185', // timestamptz[] '1115', // timestamp[] '1231', // numeric[] ] for (const type of passThroughTypes) { parsers[Number(type)] = transparentParser as (value: string) => unknown serializers[Number(type)] = transparentParser as (value: unknown) => string } // JSON types serializers[114] = transparentParser as (value: unknown) => string // json serializers[3802] = transparentParser as (value: unknown) => string // jsonb client.options.parsers = parsers client.options.serializers = serializers } // Create dialect without casing config (drizzle-orm 0.30+ doesn't take constructor args) const dialect = new PgDialect() let logger: Logger | undefined if (config.logger === true) { logger = new DefaultLogger() } else if (config.logger !== false && config.logger !== undefined) { logger = config.logger } let schema: RelationalSchemaConfig | undefined if (config.schema) { const tablesConfig = extractTablesRelationalConfig( config.schema, createTableRelationsHelpers ) schema = { fullSchema: config.schema, schema: tablesConfig.tables, tableNamesMap: tablesConfig.tableNamesMap, } } // Build session options carefully to avoid undefined values const sessionOptions = logger !== undefined ? { logger } : {} const session = new PostgresDoSession(client, dialect, schema, sessionOptions) const db = new PostgresDoDatabase(dialect, session, schema as any) as unknown as PostgresDoDatabase & { $client: PostgresDoClient } // Attach the client for direct access db.$client = client return db } /** * Create a Drizzle ORM instance for postgres.do * * This function creates a Drizzle database instance that uses the * postgres.do client for query execution. It supports all Drizzle * features including schemas, relations, and type-safe queries. * * @param client - The postgres.do SQL client * @param config - Optional Drizzle configuration * @returns A Drizzle database instance * * @example Basic usage * ```typescript * import { drizzle } from 'postgres.do/drizzle' * import postgres from 'postgres.do' * * const sql = postgres('postgres://db.postgres.do/mydb') * const db = drizzle(sql) * * const users = await db.select().from(users) * ``` * * @example With schema for type safety * ```typescript * import { drizzle } from 'postgres.do/drizzle' * import postgres from 'postgres.do' * import * as schema from './schema' * * const sql = postgres('postgres://db.postgres.do/mydb') * const db = drizzle(sql, { schema }) * * // Full type inference * const result = await db.query.users.findMany({ * with: { posts: true } * }) * ``` * * @example With logging * ```typescript * import { drizzle } from 'postgres.do/drizzle' * import postgres from 'postgres.do' * * const sql = postgres('postgres://db.postgres.do/mydb') * const db = drizzle(sql, { logger: true }) * ``` */ export function drizzle< TSchema extends Record = Record, TClient extends Sql = Sql >( client: TClient, config?: DrizzleConfig ): PostgresDoDatabase & { $client: TClient } export function drizzle< TSchema extends Record = Record, TClient extends Sql = Sql >( config: DrizzleConfig & { client: TClient } ): PostgresDoDatabase & { $client: TClient } export function drizzle< TSchema extends Record = Record, TClient extends Sql = Sql >( clientOrConfig: TClient | (DrizzleConfig & { client: TClient }), maybeConfig?: DrizzleConfig ): PostgresDoDatabase & { $client: TClient } { // Handle single config object with client if (typeof clientOrConfig === 'object' && clientOrConfig !== null && 'client' in clientOrConfig) { const { client, ...drizzleConfig } = clientOrConfig return construct(client as PostgresDoClient, drizzleConfig) as unknown as PostgresDoDatabase & { $client: TClient } } // Handle client with optional config return construct(clientOrConfig as unknown as PostgresDoClient, maybeConfig) as unknown as PostgresDoDatabase & { $client: TClient } } // Add mock function for testing drizzle.mock = function mock = Record>( config?: DrizzleConfig ): PostgresDoDatabase & { $client: '$client is not available on drizzle.mock()' } { return construct( { unsafe: async () => [], options: { parsers: {}, serializers: {}, }, }, config ) as unknown as PostgresDoDatabase & { $client: '$client is not available on drizzle.mock()' } }