@shyk/kadak 0.1.0 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["z"],"sources":["../src/schema.ts","../src/error.ts","../src/query.ts","../src/connect.ts"],"sourcesContent":["// src/schema.ts — The heart of KadakORM\n// Purely declarative — no SQL generation, no database calls.\n// Same metadata drives DDL, query building, Zod validation, AND TypeScript inference.\n\nimport { z } from 'zod'\n\n// ============================================================\n// Utilities\n// ============================================================\n\n/** Converts camelCase to snake_case for PostgreSQL column naming */\n// JS convention is camelCase, PG convention is snake_case.\n// Users write camelCase, KadakORM translates to snake_case in SQL automatically.\nexport function camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\n/** Flattens intersection types so IDE tooltips show clean objects, not A & B */\ntype Simplify<T> = { [K in keyof T]: T[K] } & {}\n\n// ============================================================\n// Column Configuration — runtime metadata for every column\n// ============================================================\n\n/** @internal Runtime metadata describing a column's PG type, constraints, behavior */\nexport interface ColumnConfig {\n pgType: string\n nullable: boolean\n hasDefault: boolean\n isPrimaryKey: boolean\n isUnique: boolean\n defaultValue?: unknown\n /** SQL expression for default — 'now()' or 'gen_random_uuid()' */\n defaultSql?: string\n /** DB auto-generates this value (IDENTITY columns) */\n isGenerated: boolean\n /** Zod validation hint — 'email', 'uuid' */\n zodCheck?: string\n /** Stored Zod schema — only for JSONB columns */\n zodSchema?: z.ZodType\n /** Auto-update on every UPDATE — for updatedAt patterns */\n autoUpdate: boolean\n /** Soft delete marker — findMany auto-filters, delete sets timestamp */\n softDelete: boolean\n min?: number\n max?: number\n}\n\n// ============================================================\n// Column Class — builder pattern with compile-time type tracking\n// ============================================================\n\n/**\n * A column definition carrying runtime metadata AND compile-time types.\n *\n * Three generics flow through the entire ORM:\n * - TType: JS type (string, number, boolean, Date)\n * - TNullable: whether NULL is allowed (adds `| null`)\n * - THasDefault: whether INSERT can omit this (adds `?`)\n *\n * Every modifier returns a NEW Column — immutable builder.\n */\nexport class Column<\n TType,\n TNullable extends boolean = false,\n THasDefault extends boolean = false,\n> {\n readonly _config: ColumnConfig\n\n // Brand fields — TypeScript needs these to distinguish Column<string> from Column<number>\n // at the structural type level. They're never assigned a value at runtime.\n declare readonly _type: TType\n declare readonly _nullable: TNullable\n declare readonly _hasDefault: THasDefault\n\n constructor(config: ColumnConfig) {\n // Freeze prevents accidental mutation — immutability is a core invariant\n this._config = Object.freeze({ ...config })\n }\n\n /** NOT NULL — already the default. Exists for explicit readability. */\n required(): Column<TType, false, THasDefault> {\n return new Column({ ...this._config, nullable: false })\n }\n\n /** Allow NULL — column type becomes TType | null in TypeScript */\n optional(): Column<TType, true, THasDefault> {\n return new Column({ ...this._config, nullable: true })\n }\n\n /** Add UNIQUE constraint */\n unique(): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, isUnique: true })\n }\n\n /** Mark as PRIMARY KEY */\n primaryKey(): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, isPrimaryKey: true })\n }\n\n /**\n * Set default value — makes field optional in INSERT.\n *\n * Named shortcuts are resolved per column type to avoid ambiguity:\n * - 'now' on TIMESTAMPTZ → SQL DEFAULT now()\n * - 'uuid' on UUID → SQL DEFAULT gen_random_uuid()\n * On other types, these strings are treated as literal values.\n */\n default(value: TType extends Date ? TType | 'now' : TType): Column<TType, TNullable, true> {\n // Shortcuts scoped by PG type — 'now' is only special on TIMESTAMPTZ, not TEXT\n const typeShortcuts: Record<string, Record<string, string>> = {\n TIMESTAMPTZ: { now: 'now()' },\n UUID: { uuid: 'gen_random_uuid()' },\n }\n const shortcuts = typeShortcuts[this._config.pgType] ?? {}\n const sqlExpr = typeof value === 'string' ? shortcuts[value as string] : undefined\n\n return new Column({\n ...this._config,\n hasDefault: true,\n defaultValue: sqlExpr ? undefined : value,\n defaultSql: sqlExpr ?? this._config.defaultSql,\n })\n }\n\n /** Min constraint — string length for text, numeric value for numbers */\n min(n: number): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, min: n })\n }\n\n /** Max constraint — string length for text, numeric value for numbers */\n max(n: number): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, max: n })\n }\n\n /** Auto-update on every UPDATE query — designed for updatedAt columns */\n autoUpdate(): Column<TType, TNullable, true> {\n // Implies hasDefault because the ORM injects the value automatically\n return new Column({ ...this._config, autoUpdate: true, hasDefault: true })\n }\n\n /**\n * Soft delete marker. When set on a column:\n * - findMany() auto-adds WHERE column IS NULL\n * - delete() sets this column to now() instead of DELETE\n * - findMany({ withDeleted: true }) bypasses the filter\n */\n softDelete(): Column<TType, true, true> {\n // Always nullable (null = not deleted) and has default (null)\n return new Column({ ...this._config, softDelete: true, nullable: true, hasDefault: true })\n }\n}\n\n// ============================================================\n// Column Factories — the `kadak` namespace\n// ============================================================\n\n/** Base config with required-by-default semantics */\nfunction baseConfig(pgType: string): ColumnConfig {\n return {\n pgType,\n nullable: false,\n hasDefault: false,\n isPrimaryKey: false,\n isUnique: false,\n isGenerated: false,\n autoUpdate: false,\n softDelete: false,\n }\n}\n\n/**\n * Column factory namespace. Every column definition starts here.\n *\n * ```ts\n * const users = table('users', {\n * id: kadak.id(),\n * name: kadak.text().required(),\n * email: kadak.email().unique(),\n * })\n * ```\n */\nexport const kadak = {\n /**\n * Modern auto-incrementing integer primary key.\n * Uses IDENTITY (not deprecated SERIAL).\n * DDL: INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY\n */\n id: (): Column<number, false, true> =>\n new Column({ ...baseConfig('INTEGER'), isPrimaryKey: true, isGenerated: true, hasDefault: true }),\n\n /**\n * UUID primary key with auto-generation via gen_random_uuid().\n * Built into PostgreSQL 13+ — no extensions needed.\n * Ideal for distributed systems and secure-by-default IDs.\n */\n uuidId: (): Column<string, false, true> =>\n new Column({ ...baseConfig('UUID'), isPrimaryKey: true, hasDefault: true, defaultSql: 'gen_random_uuid()' }),\n\n /**\n * Legacy SERIAL primary key — use kadak.id() for new projects.\n * Kept for compatibility with existing schemas.\n */\n serialId: (): Column<number, false, true> =>\n new Column({ ...baseConfig('SERIAL'), isPrimaryKey: true, isGenerated: true, hasDefault: true }),\n\n /** Unlimited-length text. Use .min()/.max() for Zod length validation. */\n text: (): Column<string, false, false> =>\n new Column(baseConfig('TEXT')),\n\n /** 4-byte integer. For whole numbers — counts, ages, quantities. */\n int: (): Column<number, false, false> =>\n new Column(baseConfig('INTEGER')),\n\n /**\n * Alias for kadak.int(). Maps to INTEGER.\n * ⚠️ This is NOT floating point — 9.99 becomes 9 in the database.\n * Use kadak.float() for decimals or kadak.decimal() for money.\n */\n number: (): Column<number, false, false> =>\n new Column(baseConfig('INTEGER')),\n\n /**\n * 8-byte floating point (DOUBLE PRECISION).\n * For scientific data, coordinates, measurements.\n * ⚠️ NEVER use for money — use kadak.decimal() instead.\n */\n float: (): Column<number, false, false> =>\n new Column(baseConfig('FLOAT8')),\n\n /**\n * Arbitrary precision decimal (NUMERIC).\n * Returns STRING in TypeScript — intentional to preserve precision.\n * JS floats lose precision: 0.1 + 0.2 !== 0.3\n * ✅ Use for: money, prices, financial math\n */\n decimal: (): Column<string, false, false> =>\n new Column(baseConfig('NUMERIC')),\n\n /** Boolean true/false */\n boolean: (): Column<boolean, false, false> =>\n new Column(baseConfig('BOOLEAN')),\n\n /**\n * Timestamp with timezone — always TIMESTAMPTZ.\n * Prevents the #1 PostgreSQL date/time footgun (timezone-naive timestamps).\n * Supports .default('now') for SQL DEFAULT now().\n */\n timestamp: (): Column<Date, false, false> =>\n new Column(baseConfig('TIMESTAMPTZ')),\n\n /**\n * TEXT with automatic Zod email validation.\n * PostgreSQL has no EMAIL type — this is TEXT underneath.\n * The email check is applied for free in validator().\n */\n email: (): Column<string, false, false> =>\n new Column({ ...baseConfig('TEXT'), zodCheck: 'email' }),\n\n /** UUID column (non-primary-key). For foreign keys, tokens, etc. */\n uuid: (): Column<string, false, false> =>\n new Column({ ...baseConfig('UUID'), zodCheck: 'uuid' }),\n\n /**\n * JSONB column with required Zod schema for runtime validation.\n * The schema provides BOTH the TS type (via z.infer) AND runtime validation.\n *\n * For unstructured JSON, pass z.unknown():\n * ```ts\n * metadata: kadak.json(z.unknown())\n * ```\n */\n json: <T extends z.ZodType>(schema: T): Column<z.infer<T>, false, false> =>\n new Column({ ...baseConfig('JSONB'), zodSchema: schema }),\n} as const\n\n// ============================================================\n// Table Options — table-level constraints\n// ============================================================\n\n/** Table-level constraints beyond what individual columns express */\nexport interface TableOptions {\n /**\n * Compound unique constraints — each array is a group of columns\n * that must be unique together.\n * ```ts\n * { unique: [['userId', 'teamId']] }\n * ```\n */\n unique?: string[][]\n}\n\n// ============================================================\n// Table Class\n// ============================================================\n\ntype ColumnsRecord = Record<string, Column<any, any, any>>\n\n/**\n * A table definition. Created via the `table()` function.\n * Holds column metadata and generates typed Zod validators.\n */\nexport class Table<TName extends string, TColumns extends ColumnsRecord> {\n readonly _name: TName\n readonly _columns: TColumns\n readonly _options?: TableOptions\n /** Maps camelCase TS keys → snake_case PG column names */\n readonly _columnMap: Record<string, string>\n\n constructor(name: TName, columns: TColumns, options?: TableOptions) {\n this._name = name\n this._columns = columns\n this._options = options\n\n // Pre-compute mapping once — reused by query builder and DDL generator\n this._columnMap = {}\n for (const key of Object.keys(columns)) {\n this._columnMap[key] = camelToSnake(key)\n }\n }\n\n /** Alias for insertValidator() — the most common use case (validating user input) */\n validator() {\n return this.insertValidator()\n }\n\n /** Validates data for INSERT — generated/defaulted fields are optional */\n insertValidator() {\n return z.object(this._buildShape('insert'))\n }\n\n /** Validates data for SELECT — all columns present, nullability applied */\n selectValidator() {\n return z.object(this._buildShape('select'))\n }\n\n /** Validates data for UPDATE — everything optional (partial update) */\n updateValidator() {\n return z.object(this._buildShape('update'))\n }\n\n /** @internal Builds Zod shape for a given operation mode */\n private _buildShape(mode: 'insert' | 'select' | 'update'): Record<string, z.ZodType> {\n const shape: Record<string, z.ZodType> = {}\n\n for (const [key, col] of Object.entries(this._columns)) {\n let zodType = this._baseZodType(col._config)\n\n // Apply nullable before optional — Zod order matters\n if (col._config.nullable) {\n zodType = zodType.nullable()\n }\n\n // Mode-specific optionality\n if (mode === 'insert' && (col._config.hasDefault || col._config.isGenerated)) {\n zodType = zodType.optional()\n } else if (mode === 'update') {\n // Everything is optional in an update — you only send what changed\n zodType = zodType.optional()\n }\n // mode === 'select' — nothing is optional, every column comes back from DB\n\n shape[key] = zodType\n }\n\n return shape\n }\n\n /** @internal Creates the base Zod type for a column, without nullable/optional */\n private _baseZodType(config: ColumnConfig): z.ZodType {\n // JSONB columns carry their own Zod schema — use it directly\n if (config.pgType === 'JSONB' && config.zodSchema) {\n return config.zodSchema\n }\n\n switch (config.pgType) {\n case 'TEXT': {\n let s = z.string()\n if (config.zodCheck === 'email') s = s.email()\n if (config.min !== undefined) s = s.min(config.min)\n if (config.max !== undefined) s = s.max(config.max)\n return s\n }\n case 'INTEGER': {\n let n = z.number().int()\n if (config.min !== undefined) n = n.min(config.min)\n if (config.max !== undefined) n = n.max(config.max)\n return n\n }\n case 'FLOAT8': {\n let n = z.number()\n if (config.min !== undefined) n = n.min(config.min)\n if (config.max !== undefined) n = n.max(config.max)\n return n\n }\n case 'NUMERIC':\n // Decimal returns string — validated as decimal format\n return z.string().regex(/^-?\\d+(\\.\\d+)?$/, 'Must be a valid decimal number')\n case 'BOOLEAN':\n return z.boolean()\n case 'TIMESTAMPTZ':\n // Coerce accepts both Date objects and ISO strings — handles API input gracefully\n return z.coerce.date()\n case 'UUID':\n return z.string().uuid()\n case 'SERIAL':\n return z.number().int()\n default:\n return z.unknown()\n }\n }\n}\n\n// ============================================================\n// table() Factory\n// ============================================================\n\n/**\n * Define a table. This is the primary API for declaring your database schema.\n *\n * ```ts\n * export const users = table('users', {\n * id: kadak.id(),\n * name: kadak.text().required(),\n * email: kadak.email().unique(),\n * age: kadak.int().optional(),\n * createdAt: kadak.timestamp().default('now'),\n * })\n * ```\n */\nexport function table<TName extends string, TColumns extends ColumnsRecord>(\n name: TName,\n columns: TColumns,\n options?: TableOptions,\n): Table<TName, TColumns> {\n return new Table(name, columns, options)\n}\n\n// ============================================================\n// Type Inference Utilities\n// ============================================================\n\n/** Extract the SELECT type for a column — what the database returns */\ntype ColumnSelectType<C> = C extends Column<infer T, infer N, any>\n ? N extends true ? T | null : T\n : never\n\n/** Keys of columns that are REQUIRED in INSERT (no default, not generated) */\ntype RequiredInsertKeys<C extends ColumnsRecord> = {\n [K in keyof C]: C[K] extends Column<any, any, true> ? never : K\n}[keyof C]\n\n/** Keys of columns that are OPTIONAL in INSERT (has default or is generated) */\ntype OptionalInsertKeys<C extends ColumnsRecord> = {\n [K in keyof C]: C[K] extends Column<any, any, true> ? K : never\n}[keyof C]\n\n/**\n * Infer the SELECT type — what you get back from queries.\n * Every column is present. Nullable columns have `| null`.\n *\n * ```ts\n * type User = Infer<typeof users>\n * // { id: number, name: string, email: string, age: number | null, createdAt: Date }\n * ```\n */\nexport type Infer<T extends Table<any, any>> = Simplify<{\n [K in keyof T['_columns']]: ColumnSelectType<T['_columns'][K]>\n}>\n\n/**\n * Infer the INSERT type — what you provide to create a row.\n * Generated/defaulted columns become optional. Nullable columns accept null.\n *\n * ```ts\n * type NewUser = InferInsert<typeof users>\n * // { name: string, email: string, age?: number | null, id?: number, createdAt?: Date }\n * ```\n */\nexport type InferInsert<T extends Table<any, any>> = Simplify<\n // Required fields — no default, must provide\n { [K in RequiredInsertKeys<T['_columns']>]: ColumnSelectType<T['_columns'][K]> } &\n // Optional fields — has default or generated, can omit\n { [K in OptionalInsertKeys<T['_columns']>]?: ColumnSelectType<T['_columns'][K]> }\n>\n\n/**\n * Infer the UPDATE type — what you provide for partial updates.\n * Everything is optional. Nullable columns accept null.\n *\n * ```ts\n * type UserUpdate = InferUpdate<typeof users>\n * // { id?: number, name?: string, email?: string, age?: number | null, createdAt?: Date }\n * ```\n */\nexport type InferUpdate<T extends Table<any, any>> = Simplify<\n Partial<Infer<T>>\n>\n","// src/error.ts — Human-readable error wrapping for PostgreSQL errors\n// Every raw pg error gets caught and wrapped before it reaches the user.\n\n/** All error codes KadakORM can produce */\nexport type KadakErrorCode =\n | 'NOT_NULL_VIOLATION'\n | 'UNIQUE_VIOLATION'\n | 'FOREIGN_KEY_VIOLATION'\n | 'TABLE_NOT_FOUND'\n | 'COLUMN_NOT_FOUND'\n | 'CONNECTION_ERROR'\n | 'AUTH_ERROR'\n | 'QUERY_TIMEOUT'\n | 'UNKNOWN'\n\n/** Maps PostgreSQL error codes to human-readable KadakORM codes + hints */\nconst PG_ERROR_MAP: Record<string, { code: KadakErrorCode; hint: string }> = {\n '23505': { code: 'UNIQUE_VIOLATION', hint: 'A row with this value already exists. Check your unique columns.' },\n '23502': { code: 'NOT_NULL_VIOLATION', hint: 'A required column is missing. Add .optional() if it should be nullable.' },\n '23503': { code: 'FOREIGN_KEY_VIOLATION', hint: 'The referenced row does not exist in the related table.' },\n '42P01': { code: 'TABLE_NOT_FOUND', hint: 'Did you run migrations? The table does not exist yet.' },\n '42703': { code: 'COLUMN_NOT_FOUND', hint: 'Check your schema — this column is not in the table.' },\n '08006': { code: 'CONNECTION_ERROR', hint: 'Cannot reach the database. Check your DATABASE_URL and network.' },\n '08001': { code: 'CONNECTION_ERROR', hint: 'Connection refused. Is PostgreSQL running?' },\n '28P01': { code: 'AUTH_ERROR', hint: 'Wrong username or password in your connection string.' },\n '3D000': { code: 'CONNECTION_ERROR', hint: 'This database does not exist. Check the database name in your URL.' },\n '57014': { code: 'QUERY_TIMEOUT', hint: 'Query took too long. Consider adding an index or simplifying the query.' },\n}\n\n/**\n * The one error class users ever see from KadakORM.\n * Always includes: what went wrong, which table/column, and how to fix it.\n */\nexport class KadakError extends Error {\n readonly code: KadakErrorCode\n readonly table?: string\n readonly column?: string\n readonly constraint?: string\n readonly hint: string\n /** Always preserved — the raw pg error for advanced debugging */\n readonly originalError: unknown\n\n constructor(opts: {\n code: KadakErrorCode\n message: string\n hint: string\n table?: string\n column?: string\n constraint?: string\n originalError: unknown\n }) {\n super(opts.message)\n this.name = 'KadakError'\n this.code = opts.code\n this.hint = opts.hint\n this.table = opts.table\n this.column = opts.column\n this.constraint = opts.constraint\n this.originalError = opts.originalError\n }\n}\n\n/** Wraps any postgres.js error into a KadakError with context */\nexport function wrapPgError(err: unknown, tableName?: string): KadakError {\n const pgErr = err as Record<string, any> | undefined\n const pgCode: string = pgErr?.code ?? ''\n const mapped = PG_ERROR_MAP[pgCode] ?? { code: 'UNKNOWN' as const, hint: 'An unexpected database error occurred.' }\n\n const table = pgErr?.table_name ?? tableName\n const column: string | undefined = pgErr?.column_name\n const constraint: string | undefined = pgErr?.constraint_name\n\n // Build a message that actually tells you what to do\n let message: string = pgErr?.message ?? 'Unknown database error'\n if (mapped.code === 'NOT_NULL_VIOLATION' && column) {\n message = `Column '${column}' in table '${table}' cannot be null. Did you forget to pass '${column}'?`\n } else if (mapped.code === 'UNIQUE_VIOLATION' && constraint) {\n message = `Duplicate value violates constraint '${constraint}' on table '${table}'.`\n } else if (mapped.code === 'TABLE_NOT_FOUND') {\n message = `Table '${table ?? 'unknown'}' does not exist. Have you run your migrations?`\n }\n\n return new KadakError({ code: mapped.code, message, hint: mapped.hint, table, column, constraint, originalError: err })\n}\n","// src/query.ts — Query builder for KadakORM\n// Thin layer over postgres.js tagged templates — no query string concatenation.\n// Handles camelCase↔snake_case, soft deletes, autoUpdate injection.\n\nimport type postgres from 'postgres'\nimport type { Table, Infer, InferInsert, InferUpdate, Column } from './schema'\nimport { wrapPgError } from './error'\n\n// ============================================================\n// Query Option Types\n// ============================================================\n\n/** Options for findMany/findFirst — without select (returns full row) */\nexport interface FindManyOptions<T extends Table<any, any>> {\n where?: Partial<Infer<T>>\n orderBy?: Partial<Record<keyof Infer<T>, 'asc' | 'desc'>>\n limit?: number\n offset?: number\n /** When true, includes soft-deleted rows */\n withDeleted?: boolean\n /** Reserved for relations — not yet implemented */\n include?: never\n}\n\n// ============================================================\n// TableClient — the query interface for a single table\n// ============================================================\n\n/**\n * Query client for a single table. Created by `connect()` — never instantiated directly.\n * Provides findMany, findFirst, insert, update, delete with full type safety.\n */\nexport class TableClient<T extends Table<any, any>> {\n /** @internal */ readonly _sql: postgres.Sql\n /** @internal */ readonly _table: T\n /** @internal */ readonly _reverseMap: Record<string, string>\n /** @internal */ _onError?: (err: Error) => void\n\n constructor(sql: postgres.Sql, table: T) {\n this._sql = sql\n this._table = table\n\n // Pre-compute reverse mapping: snake_case → camelCase\n // Used to transform query results back to JS convention\n this._reverseMap = {}\n for (const [camel, snake] of Object.entries(table._columnMap)) {\n this._reverseMap[snake] = camel\n }\n }\n\n // ----------------------------------------------------------\n // findMany — SELECT with filtering, ordering, pagination\n // ----------------------------------------------------------\n\n /** Find rows with optional select for precise return types */\n findMany<K extends keyof Infer<T>>(\n options: FindManyOptions<T> & { select: Record<K, true> }\n ): Promise<Pick<Infer<T>, K>[]>\n /** Find rows — returns full row type */\n findMany(options?: FindManyOptions<T>): Promise<Infer<T>[]>\n async findMany(options?: FindManyOptions<T> & { select?: Record<string, true> }): Promise<any[]> {\n try {\n const sql = this._sql\n const t = this._table\n\n // Build column list — either selected columns or all\n const cols = options?.select\n ? Object.keys(options.select).filter(k => options.select![k]).map(k => t._columnMap[k] ?? k)\n : Object.values(t._columnMap)\n\n // Assemble fragments\n const whereFragment = this._buildWhere(options?.where, options?.withDeleted)\n const orderFragment = this._buildOrderBy(options?.orderBy)\n const limitFragment = options?.limit !== undefined ? sql`LIMIT ${options.limit}` : sql``\n const offsetFragment = options?.offset !== undefined ? sql`OFFSET ${options.offset}` : sql``\n\n const rows = await sql`\n SELECT ${sql(cols)} FROM ${sql(t._name)}\n ${whereFragment} ${orderFragment} ${limitFragment} ${offsetFragment}\n `\n return rows.map(row => this._toJs(row))\n } catch (err) {\n throw wrapPgError(err, this._table._name)\n }\n }\n\n // ----------------------------------------------------------\n // findFirst — same as findMany but returns single row or null\n // ----------------------------------------------------------\n\n findFirst<K extends keyof Infer<T>>(\n options: FindManyOptions<T> & { select: Record<K, true> }\n ): Promise<Pick<Infer<T>, K> | null>\n findFirst(options?: FindManyOptions<T>): Promise<Infer<T> | null>\n async findFirst(options?: FindManyOptions<T> & { select?: Record<string, true> }): Promise<any> {\n const rows = await this.findMany({ ...options, limit: 1 } as any)\n return rows[0] ?? null\n }\n\n // ----------------------------------------------------------\n // insert — INSERT INTO with RETURNING *\n // ----------------------------------------------------------\n\n /** Insert a single row */\n async insert(data: InferInsert<T>): Promise<Infer<T>>\n /** Insert multiple rows */\n async insert(data: InferInsert<T>[]): Promise<Infer<T>[]>\n async insert(data: InferInsert<T> | InferInsert<T>[]): Promise<any> {\n try {\n const sql = this._sql\n const t = this._table\n const isBulk = Array.isArray(data)\n const items = isBulk ? data : [data]\n\n // Convert each item's keys from camelCase to snake_case\n const snakeItems = items.map(item => this._toDb(item as Record<string, unknown>))\n\n // postgres.js handles bulk insert when given an array of column names + values\n const cols = Object.keys(snakeItems[0])\n const rows = await sql`\n INSERT INTO ${sql(t._name)} ${sql(snakeItems, ...cols)} RETURNING *\n `\n\n const result = rows.map(row => this._toJs(row))\n return isBulk ? result : result[0]\n } catch (err) {\n throw wrapPgError(err, this._table._name)\n }\n }\n\n // ----------------------------------------------------------\n // update — UPDATE SET ... WHERE ... RETURNING *\n // ----------------------------------------------------------\n\n async update(options: { where: Partial<Infer<T>>; data: InferUpdate<T> }): Promise<Infer<T>[]> {\n try {\n const sql = this._sql\n const t = this._table\n\n // Convert update data to snake_case, stripping undefined values\n const snakeData: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(options.data as Record<string, unknown>)) {\n if (value !== undefined) {\n snakeData[t._columnMap[key] ?? key] = value\n }\n }\n\n // Auto-inject autoUpdate columns (e.g., updatedAt → new Date())\n for (const [key, col] of Object.entries(t._columns)) {\n if ((col as Column<any, any, any>)._config.autoUpdate) {\n snakeData[t._columnMap[key]] = new Date()\n }\n }\n\n const whereFragment = this._buildWhere(options.where, false)\n\n const rows = await sql`\n UPDATE ${sql(t._name)} SET ${sql(snakeData)} ${whereFragment} RETURNING *\n `\n return rows.map(row => this._toJs(row)) as Infer<T>[]\n } catch (err) {\n throw wrapPgError(err, this._table._name)\n }\n }\n\n // ----------------------------------------------------------\n // delete — soft delete if configured, otherwise hard delete\n // ----------------------------------------------------------\n\n async delete(options: { where: Partial<Infer<T>> }): Promise<Infer<T>[]> {\n // Find soft delete column if one exists\n const softDeleteEntry = Object.entries(this._table._columns)\n .find(([_, col]) => (col as Column<any, any, any>)._config.softDelete)\n\n if (softDeleteEntry) {\n // Soft delete — set the timestamp instead of deleting\n const [key] = softDeleteEntry\n const snakeCol = this._table._columnMap[key]\n return this.update({\n where: options.where,\n data: { [key]: new Date() } as any,\n })\n }\n\n return this.hardDelete(options)\n }\n\n /** Always performs a real DELETE — bypasses soft delete */\n async hardDelete(options: { where: Partial<Infer<T>> }): Promise<Infer<T>[]> {\n try {\n const sql = this._sql\n const whereFragment = this._buildWhere(options.where, true)\n\n const rows = await sql`\n DELETE FROM ${sql(this._table._name)} ${whereFragment} RETURNING *\n `\n return rows.map(row => this._toJs(row)) as Infer<T>[]\n } catch (err) {\n throw wrapPgError(err, this._table._name)\n }\n }\n\n // ============================================================\n // Internal helpers\n // ============================================================\n\n /** Builds a WHERE fragment from a JS object, including soft delete filter */\n private _buildWhere(\n where?: Partial<Infer<T>>,\n withDeleted?: boolean,\n ): postgres.PendingQuery<any> {\n const sql = this._sql\n const conditions: postgres.PendingQuery<any>[] = []\n\n // User-provided conditions\n if (where) {\n for (const [key, value] of Object.entries(where)) {\n const col = this._table._columnMap[key] ?? key\n if (value === undefined) continue\n if (value === null) {\n conditions.push(sql`${sql(col)} IS NULL`)\n } else {\n conditions.push(sql`${sql(col)} = ${value as postgres.SerializableParameter}`)\n }\n }\n }\n\n // Auto-add soft delete filter unless explicitly bypassed\n if (!withDeleted) {\n for (const [key, col] of Object.entries(this._table._columns)) {\n if ((col as Column<any, any, any>)._config.softDelete) {\n conditions.push(sql`${sql(this._table._columnMap[key])} IS NULL`)\n }\n }\n }\n\n if (conditions.length === 0) return sql``\n\n // Join conditions with AND using postgres.js fragment composition\n let combined = conditions[0]\n for (let i = 1; i < conditions.length; i++) {\n combined = sql`${combined} AND ${conditions[i]}`\n }\n return sql`WHERE ${combined}`\n }\n\n /** Builds an ORDER BY fragment */\n private _buildOrderBy(\n orderBy?: Partial<Record<string, 'asc' | 'desc'>>,\n ): postgres.PendingQuery<any> {\n const sql = this._sql\n if (!orderBy) return sql``\n\n const entries = Object.entries(orderBy)\n if (entries.length === 0) return sql``\n\n const parts = entries.map(([key, dir]) => {\n const col = this._table._columnMap[key] ?? key\n // postgres.js unsafe() needed for ASC/DESC since they're SQL keywords, not values\n return sql`${sql(col)} ${dir === 'desc' ? sql`DESC` : sql`ASC`}`\n })\n\n let combined = parts[0]\n for (let i = 1; i < parts.length; i++) {\n combined = sql`${combined}, ${parts[i]}`\n }\n return sql`ORDER BY ${combined}`\n }\n\n /** Converts a JS object (camelCase) to DB format (snake_case) */\n private _toDb(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[this._table._columnMap[key] ?? key] = value\n }\n }\n return result\n }\n\n /** Converts a DB row (snake_case) back to JS format (camelCase) */\n private _toJs(row: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [snake, value] of Object.entries(row as Record<string, unknown>)) {\n result[this._reverseMap[snake] ?? snake] = value\n }\n return result\n }\n}\n","// src/connect.ts — PostgreSQL connection with sensible defaults\n// SSL auto-detection, pooling, and typed database client creation.\n\nimport postgres from 'postgres'\nimport type { Table } from './schema'\nimport { TableClient } from './query'\n\n/** Connection config for production use */\nexport interface ConnectionConfig {\n /** PostgreSQL connection URL */\n url: string\n /** Max connections in pool — default 10 */\n max?: number\n /**\n * SSL mode — default: auto-detected.\n * Localhost → false. Everything else → 'require'.\n * This means Supabase, Neon, Railway, Vercel Postgres work out of the box.\n */\n ssl?: boolean | 'require' | 'prefer' | 'allow'\n /** Query timeout in seconds */\n timeout?: number\n /** Called on connection-level errors */\n onError?: (err: Error) => void\n}\n\n/** The database client — each key is a table name mapped to its query client */\nexport type Database<TTables extends Record<string, Table<any, any>>> = {\n [K in keyof TTables]: TTables[K] extends Table<any, any> ? TableClient<TTables[K]> : never\n} & {\n /** Close all connections in the pool */\n close(): Promise<void>\n /** Raw postgres.js client — escape hatch for custom queries */\n sql: postgres.Sql\n}\n\n/**\n * Connect to PostgreSQL and create a typed database client.\n *\n * ```ts\n * const db = connect('postgresql://user:pass@localhost/mydb', { users, posts })\n * const allUsers = await db.users.findMany()\n * await db.close()\n * ```\n */\nexport function connect<TTables extends Record<string, Table<any, any>>>(\n config: string | ConnectionConfig,\n tables: TTables,\n): Database<TTables> {\n const isString = typeof config === 'string'\n const url = isString ? config : config.url\n const opts = isString ? {} as ConnectionConfig : config\n\n // Auto-detect SSL: localhost = no SSL, everything else = require\n // This single default prevents 90% of \"connection refused\" issues\n // with managed PostgreSQL services\n const isLocal = url.includes('localhost') || url.includes('127.0.0.1')\n const ssl = opts.ssl ?? (isLocal ? false : 'require')\n\n const sql = postgres(url, {\n max: opts.max ?? 10,\n idle_timeout: 20,\n connect_timeout: 30,\n ssl,\n max_lifetime: 60 * 30,\n onnotice: () => {}, // suppress PostgreSQL NOTICE messages — they confuse beginners\n })\n\n // Create a TableClient for each registered table\n const db = {} as any\n for (const [key, table] of Object.entries(tables)) {\n const client = new TableClient(sql, table as Table<any, any>)\n if (opts.onError) client._onError = opts.onError\n db[key] = client\n }\n\n db.close = () => sql.end()\n db.sql = sql\n\n return db as Database<TTables>\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["z","ZodError","path","fs"],"sources":["../src/schema.ts","../src/error.ts","../src/query/analyzer.ts","../src/query/ast.ts","../src/query/normalize.ts","../src/query/sql.ts","../src/query/index.ts","../src/connect.ts","../src/migrate.ts","../src/observability.ts","../src/version.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport function camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)\n}\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {}\n\nexport interface ColumnConfig {\n pgType: string\n nullable: boolean\n hasDefault: boolean\n isPrimaryKey: boolean\n isUnique: boolean\n defaultValue?: unknown\n defaultSql?: string\n isGenerated: boolean\n zodCheck?: string\n zodSchema?: z.ZodType\n autoUpdate: boolean\n softDelete: boolean\n isOptional: boolean\n min?: number\n max?: number\n}\n\nexport class Column<\n TType,\n TNullable extends boolean = false,\n THasDefault extends boolean = false,\n> {\n readonly _config: ColumnConfig\n\n declare readonly _type: TType\n declare readonly _nullable: TNullable\n declare readonly _hasDefault: THasDefault\n\n constructor(config: ColumnConfig) {\n this._config = Object.freeze({ ...config })\n }\n\n optional(): Column<TType, true, THasDefault> {\n return new Column({ ...this._config, nullable: true, isOptional: true })\n }\n\n unique(): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, isUnique: true })\n }\n\n primaryKey(): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, isPrimaryKey: true })\n }\n\n default(value: TType extends Date ? TType | 'now' : TType): Column<TType, TNullable, true> {\n const typeShortcuts: Record<string, Record<string, string>> = {\n TIMESTAMPTZ: { now: 'now()' },\n UUID: { uuid: 'gen_random_uuid()' },\n }\n const shortcuts = typeShortcuts[this._config.pgType] ?? {}\n const sqlExpr = typeof value === 'string' ? shortcuts[value as string] : undefined\n\n return new Column({\n ...this._config,\n hasDefault: true,\n defaultValue: sqlExpr ? undefined : value,\n defaultSql: sqlExpr ?? this._config.defaultSql,\n })\n }\n\n min(n: number): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, min: n })\n }\n\n max(n: number): Column<TType, TNullable, THasDefault> {\n return new Column({ ...this._config, max: n })\n }\n\n autoUpdate(): Column<TType, TNullable, true> {\n return new Column({ ...this._config, autoUpdate: true, hasDefault: true })\n }\n\n softDelete(): Column<TType, true, true> {\n return new Column({ ...this._config, softDelete: true, nullable: true, hasDefault: true })\n }\n}\n\nfunction baseConfig(pgType: string): ColumnConfig {\n return {\n pgType,\n nullable: false,\n hasDefault: false,\n isPrimaryKey: false,\n isUnique: false,\n isGenerated: false,\n autoUpdate: false,\n softDelete: false,\n isOptional: false,\n }\n}\n\nexport const kadak = {\n id: (): Column<number, false, true> =>\n new Column({ ...baseConfig('INTEGER'), isPrimaryKey: true, isGenerated: true, hasDefault: true }),\n\n uuidId: (): Column<string, false, true> =>\n new Column({ ...baseConfig('UUID'), isPrimaryKey: true, hasDefault: true, defaultSql: 'gen_random_uuid()' }),\n\n serialId: (): Column<number, false, true> =>\n new Column({ ...baseConfig('SERIAL'), isPrimaryKey: true, isGenerated: true, hasDefault: true }),\n\n text: (): Column<string, false, false> => new Column(baseConfig('TEXT')),\n int: (): Column<number, false, false> => new Column(baseConfig('INTEGER')),\n number: (): Column<number, false, false> => new Column(baseConfig('INTEGER')),\n float: (): Column<number, false, false> => new Column(baseConfig('FLOAT8')),\n decimal: (): Column<string, false, false> => new Column(baseConfig('NUMERIC')),\n boolean: (): Column<boolean, false, false> => new Column(baseConfig('BOOLEAN')),\n timestamp: (): Column<Date, false, false> => new Column(baseConfig('TIMESTAMPTZ')),\n email: (): Column<string, false, false> => new Column({ ...baseConfig('TEXT'), zodCheck: 'email' }),\n uuid: (): Column<string, false, false> => new Column({ ...baseConfig('UUID'), zodCheck: 'uuid' }),\n json: <T extends z.ZodType>(schema: T): Column<z.infer<T>, false, false> =>\n new Column({ ...baseConfig('JSONB'), zodSchema: schema }),\n} as const\n\ntype ColumnsRecord = Record<string, Column<any, any, any>>\ntype ShorthandBase =\n | 'id'\n | 'uuidId'\n | 'serialId'\n | 'text'\n | 'int'\n | 'number'\n | 'float'\n | 'decimal'\n | 'boolean'\n | 'timestamp'\n | 'email'\n | 'uuid'\ntype ColumnShorthand = ShorthandBase | `${ShorthandBase}?`\ntype TableDefinition = Record<string, Column<any, any, any> | ColumnShorthand>\n\ntype MakeOptional<C> = C extends Column<infer T, any, infer D> ? Column<T, true, D> : never\n\ntype ColumnFromBase<S extends ShorthandBase> =\n S extends 'id' ? Column<number, false, true> :\n S extends 'uuidId' ? Column<string, false, true> :\n S extends 'serialId' ? Column<number, false, true> :\n S extends 'text' ? Column<string, false, false> :\n S extends 'int' ? Column<number, false, false> :\n S extends 'number' ? Column<number, false, false> :\n S extends 'float' ? Column<number, false, false> :\n S extends 'decimal' ? Column<string, false, false> :\n S extends 'boolean' ? Column<boolean, false, false> :\n S extends 'timestamp' ? Column<Date, false, false> :\n S extends 'email' ? Column<string, false, false> :\n S extends 'uuid' ? Column<string, false, false> :\n never\n\ntype ColumnFromDefinition<T> =\n T extends Column<any, any, any> ? T :\n T extends `${infer Base extends ShorthandBase}?` ? MakeOptional<ColumnFromBase<Base>> :\n T extends ShorthandBase ? ColumnFromBase<T> :\n never\n\ntype NormalizeColumns<TColumns extends TableDefinition> = {\n [K in keyof TColumns]: ColumnFromDefinition<TColumns[K]>\n}\n\ntype WithTimestamps<C extends ColumnsRecord> = C &\n ('createdAt' extends keyof C ? {} : { createdAt: Column<Date, false, true> }) &\n ('updatedAt' extends keyof C ? {} : { updatedAt: Column<Date, false, true> })\n\ntype WithSoftDelete<C extends ColumnsRecord> = C &\n ('deletedAt' extends keyof C ? {} : { deletedAt: Column<Date, true, true> })\n\ntype ApplyTableOptions<C extends ColumnsRecord, TOptions extends TableOptions | undefined> =\n TOptions extends { timestamps: true }\n ? TOptions extends { softDelete: true }\n ? WithSoftDelete<WithTimestamps<C>>\n : WithTimestamps<C>\n : TOptions extends { softDelete: true }\n ? WithSoftDelete<C>\n : C\n\nexport interface TableOptions {\n timestamps?: boolean\n softDelete?: boolean\n unique?: string[][]\n}\n\nexport class Table<TName extends string, TColumns extends ColumnsRecord> {\n readonly _name: TName\n readonly _columns: TColumns\n readonly _options?: TableOptions\n readonly _columnMap: Record<string, string>\n\n constructor(name: TName, columns: TColumns, options?: TableOptions) {\n this._name = name\n this._columns = columns\n this._options = options\n this._columnMap = {}\n\n for (const key of Object.keys(columns)) {\n this._columnMap[key] = camelToSnake(key)\n }\n }\n\n validator() {\n return this.insertValidator()\n }\n\n insertValidator() {\n return z.object(this._buildShape('insert'))\n }\n\n selectValidator() {\n return z.object(this._buildShape('select'))\n }\n\n updateValidator() {\n return z.object(this._buildShape('update'))\n }\n\n private _buildShape(mode: 'insert' | 'select' | 'update'): Record<string, z.ZodType> {\n const shape: Record<string, z.ZodType> = {}\n\n for (const [key, col] of Object.entries(this._columns)) {\n let zodType = this._baseZodType(col._config)\n\n if (col._config.nullable) {\n zodType = zodType.nullable()\n }\n\n if (mode === 'insert' && (col._config.hasDefault || col._config.isGenerated || col._config.isOptional)) {\n zodType = zodType.optional()\n } else if (mode === 'update') {\n zodType = zodType.optional()\n }\n\n shape[key] = zodType\n }\n\n return shape\n }\n\n private _baseZodType(config: ColumnConfig): z.ZodType {\n if (config.pgType === 'JSONB' && config.zodSchema) {\n return config.zodSchema\n }\n\n switch (config.pgType) {\n case 'TEXT': {\n let s = z.string()\n if (config.zodCheck === 'email') s = s.email()\n if (config.min !== undefined) s = s.min(config.min)\n if (config.max !== undefined) s = s.max(config.max)\n return s\n }\n case 'INTEGER': {\n let n = z.number().int()\n if (config.min !== undefined) n = n.min(config.min)\n if (config.max !== undefined) n = n.max(config.max)\n return n\n }\n case 'FLOAT8': {\n let n = z.number()\n if (config.min !== undefined) n = n.min(config.min)\n if (config.max !== undefined) n = n.max(config.max)\n return n\n }\n case 'NUMERIC':\n return z.string().regex(/^-?\\d+(\\.\\d+)?$/, 'Must be a valid decimal number')\n case 'BOOLEAN':\n return z.boolean()\n case 'TIMESTAMPTZ':\n return z.coerce.date()\n case 'UUID':\n return z.string().uuid()\n case 'SERIAL':\n return z.number().int()\n default:\n return z.unknown()\n }\n }\n}\n\nfunction fromShorthand(key: string, definition: ColumnShorthand): Column<any, any, any> {\n const optional = definition.endsWith('?')\n const base = (optional ? definition.slice(0, -1) : definition) as ShorthandBase\n\n let column: Column<any, any, any>\n switch (base) {\n case 'id':\n column = kadak.id()\n break\n case 'uuidId':\n column = kadak.uuidId()\n break\n case 'serialId':\n column = kadak.serialId()\n break\n case 'text':\n column = kadak.text()\n break\n case 'int':\n column = kadak.int()\n break\n case 'number':\n column = kadak.number()\n break\n case 'float':\n column = kadak.float()\n break\n case 'decimal':\n column = kadak.decimal()\n break\n case 'boolean':\n column = kadak.boolean()\n break\n case 'timestamp':\n column = kadak.timestamp()\n break\n case 'email':\n column = kadak.email().unique()\n break\n case 'uuid':\n column = kadak.uuid()\n break\n default:\n throw new Error(`Unsupported shorthand '${definition}' for column '${key}'`)\n }\n\n return optional ? column.optional() : column\n}\n\nfunction normalizeColumns<TColumns extends TableDefinition>(\n columns: TColumns,\n options?: TableOptions,\n): NormalizeColumns<TColumns> {\n const normalized = {} as NormalizeColumns<TColumns>\n\n for (const [key, value] of Object.entries(columns)) {\n normalized[key as keyof TColumns] = (\n typeof value === 'string' ? fromShorthand(key, value as ColumnShorthand) : value\n ) as NormalizeColumns<TColumns>[keyof TColumns]\n }\n\n if (options?.timestamps) {\n if (!('createdAt' in normalized)) {\n ;(normalized as Record<string, Column<any, any, any>>).createdAt = kadak.timestamp().default('now')\n }\n if (!('updatedAt' in normalized)) {\n ;(normalized as Record<string, Column<any, any, any>>).updatedAt = kadak.timestamp().default('now').autoUpdate()\n }\n }\n\n if (options?.softDelete && !('deletedAt' in normalized)) {\n ;(normalized as Record<string, Column<any, any, any>>).deletedAt = kadak.timestamp().softDelete()\n }\n\n return normalized\n}\n\nexport function table<TName extends string, TColumns extends TableDefinition, TOptions extends TableOptions | undefined>(\n name: TName,\n columns: TColumns,\n options?: TOptions,\n): Table<TName, ApplyTableOptions<NormalizeColumns<TColumns>, TOptions>> {\n return new Table(name, normalizeColumns(columns, options), options) as Table<\n TName,\n ApplyTableOptions<NormalizeColumns<TColumns>, TOptions>\n >\n}\n\ntype ColumnSelectType<C> = C extends Column<infer T, infer N, any>\n ? N extends true ? T | null : T\n : never\n\ntype RequiredInsertKeys<C extends ColumnsRecord> = {\n [K in keyof C]: C[K] extends Column<any, any, true> ? never : K\n}[keyof C]\n\ntype OptionalInsertKeys<C extends ColumnsRecord> = {\n [K in keyof C]: C[K] extends Column<any, any, true> ? K : never\n}[keyof C]\n\nexport type Infer<T extends Table<any, any>> = Simplify<{\n [K in keyof T['_columns']]: ColumnSelectType<T['_columns'][K]>\n}>\n\nexport type InferInsert<T extends Table<any, any>> = Simplify<\n { [K in RequiredInsertKeys<T['_columns']>]: ColumnSelectType<T['_columns'][K]> } &\n { [K in OptionalInsertKeys<T['_columns']>]?: ColumnSelectType<T['_columns'][K]> }\n>\n\nexport type InferUpdate<T extends Table<any, any>> = Simplify<Partial<Infer<T>>>\n","import { ZodError } from 'zod'\n\nexport type KadakErrorCode =\n | 'VALIDATION_ERROR'\n | 'QUERY_WARNING'\n | 'NOT_NULL_VIOLATION'\n | 'UNIQUE_VIOLATION'\n | 'FOREIGN_KEY_VIOLATION'\n | 'TABLE_NOT_FOUND'\n | 'COLUMN_NOT_FOUND'\n | 'CONNECTION_ERROR'\n | 'AUTH_ERROR'\n | 'QUERY_TIMEOUT'\n | 'UNKNOWN'\n\nconst PG_ERROR_MAP: Record<string, { code: KadakErrorCode; hint: string }> = {\n '23505': { code: 'UNIQUE_VIOLATION', hint: 'A row with this value already exists. Check your unique columns.' },\n '23502': { code: 'NOT_NULL_VIOLATION', hint: 'A required column is missing. Add ? in schema if it should be optional.' },\n '23503': { code: 'FOREIGN_KEY_VIOLATION', hint: 'The referenced row does not exist in the related table.' },\n '42P01': { code: 'TABLE_NOT_FOUND', hint: 'Did you run migrations? The table does not exist yet.' },\n '42703': { code: 'COLUMN_NOT_FOUND', hint: 'Check your schema — this column is not in the table.' },\n '08006': { code: 'CONNECTION_ERROR', hint: 'Cannot reach the database. Check your DATABASE_URL and network.' },\n '08001': { code: 'CONNECTION_ERROR', hint: 'Connection refused. Is PostgreSQL running?' },\n '28P01': { code: 'AUTH_ERROR', hint: 'Wrong username or password in your connection string.' },\n '3D000': { code: 'CONNECTION_ERROR', hint: 'This database does not exist. Check the database name in your URL.' },\n '57014': { code: 'QUERY_TIMEOUT', hint: 'Query took too long. Consider adding an index or simplifying the query.' },\n}\n\nexport class KadakError extends Error {\n readonly code: KadakErrorCode\n readonly table?: string\n readonly column?: string\n readonly constraint?: string\n readonly hint: string\n readonly originalError: unknown\n\n constructor(opts: {\n code: KadakErrorCode\n message: string\n hint: string\n table?: string\n column?: string\n constraint?: string\n originalError: unknown\n }) {\n super(opts.message)\n this.name = 'KadakError'\n this.code = opts.code\n this.hint = opts.hint\n this.table = opts.table\n this.column = opts.column\n this.constraint = opts.constraint\n this.originalError = opts.originalError\n }\n}\n\nexport function fromValidationError(error: unknown, tableName?: string): KadakError {\n if (error instanceof ZodError) {\n const issue = error.issues[0]\n const path = issue?.path.join('.') || 'payload'\n return new KadakError({\n code: 'VALIDATION_ERROR',\n message: `Missing or invalid field: ${path}`,\n hint: `Fix: ${issue?.message ?? 'Check the value against your schema.'}`,\n table: tableName,\n column: path,\n originalError: error,\n })\n }\n\n return new KadakError({\n code: 'VALIDATION_ERROR',\n message: 'Invalid input for this query.',\n hint: 'Check the payload against your schema validators.',\n table: tableName,\n originalError: error,\n })\n}\n\nexport function wrapPgError(err: unknown, tableName?: string): KadakError {\n const pgErr = err as Record<string, any> | undefined\n const pgCode: string = pgErr?.code ?? ''\n const mapped = PG_ERROR_MAP[pgCode] ?? { code: 'UNKNOWN' as const, hint: 'An unexpected database error occurred.' }\n\n const table = pgErr?.table_name ?? tableName\n const column: string | undefined = pgErr?.column_name\n const constraint: string | undefined = pgErr?.constraint_name\n\n let message: string = pgErr?.message ?? 'Unknown database error'\n if (mapped.code === 'NOT_NULL_VIOLATION' && column) {\n message = `Missing required field: ${column}`\n } else if (mapped.code === 'UNIQUE_VIOLATION') {\n message = constraint \n ? `A record already exists with this value (violates '${constraint}')`\n : 'A duplicate record already exists.'\n } else if (mapped.code === 'TABLE_NOT_FOUND') {\n message = `Table '${table ?? 'unknown'}' does not exist.`\n }\n\n return new KadakError({\n code: mapped.code,\n message,\n hint: mapped.hint,\n table,\n column: column || (constraint?.includes(table || '') ? constraint.replace(table + '_', '').replace('_key', '') : undefined),\n constraint,\n originalError: err,\n })\n}\n","import type { Table, Column } from '../schema'\nimport type { Condition, QueryAst } from './ast'\n\nexport interface AnalyzerWarning {\n code: 'full_scan' | 'missing_limit' | 'large_limit' | 'unsafe_mutation' | 'slow_query' | 'large_result' | 'index_hint' | 'repeated_query'\n message: string\n suggestion?: string\n}\n\nexport interface ExecutionMetrics {\n durationMs: number\n rows: number\n}\n\nexport interface AnalyzerConfig {\n slowQueryMs: number\n largeResultRows: number\n largeLimitThreshold: number\n}\n\nfunction collectFields(condition: Condition | undefined): string[] {\n if (!condition) return []\n if (condition.isInternal) return []\n\n switch (condition.type) {\n case 'eq':\n case 'isNull':\n case 'in':\n case 'gt':\n case 'gte':\n case 'lt':\n case 'lte':\n return [condition.field]\n case 'and':\n case 'or':\n return condition.conditions.flatMap((entry) => collectFields(entry))\n }\n}\n\nfunction isIndexedField(table: Table<any, any>, dbField: string): boolean {\n const jsField = Object.entries(table._columnMap).find(([, value]) => value === dbField)?.[0]\n if (!jsField) return false\n\n const column = table._columns[jsField] as Column<any, any, any> | undefined\n if (!column) return false\n\n return column._config.isPrimaryKey || column._config.isUnique\n}\n\nfunction hasUserFilter(condition: Condition | undefined): boolean {\n if (!condition) return false\n if (condition.isInternal) return false\n\n if (condition.type === 'and' || condition.type === 'or') {\n return condition.conditions.some((c) => hasUserFilter(c))\n }\n\n return true\n}\n\nexport function analyzeAst(ast: QueryAst, table: Table<any, any>, config: AnalyzerConfig): AnalyzerWarning[] {\n const warnings: AnalyzerWarning[] = []\n const where = ast.type === 'insert' ? undefined : ast.where\n const hasFilter = hasUserFilter(where)\n\n if (ast.type === 'select' && !hasFilter) {\n warnings.push({\n code: 'full_scan',\n message: `Query on \"${ast.table}\" has no filter. This may cause a full table scan.`,\n suggestion: 'Add a filter like { id: 1 } or another narrowing field.',\n })\n }\n\n if (ast.type === 'select' && ast.limit === undefined) {\n warnings.push({\n code: 'missing_limit',\n message: `Query on \"${ast.table}\" has no limit. This may load large datasets.`,\n suggestion: 'Add $limit: 100',\n })\n }\n\n if (ast.type === 'select' && ast.limit !== undefined && ast.limit > config.largeLimitThreshold) {\n warnings.push({\n code: 'large_limit',\n message: `Query on \"${ast.table}\" uses a large limit (${ast.limit}).`,\n suggestion: 'Reduce the limit or paginate in smaller batches.',\n })\n }\n\n if ((ast.type === 'update' || ast.type === 'delete') && !hasFilter) {\n warnings.push({\n code: 'unsafe_mutation',\n message: `${ast.type.toUpperCase()} on \"${ast.table}\" has no filter and will affect every row.`,\n suggestion: `Add a filter to ${ast.type} only the rows you intend to change.`,\n })\n }\n\n const filterFields = Array.from(new Set(collectFields(where)))\n const nonIndexedFields = filterFields.filter((field) => !isIndexedField(table, field))\n if (nonIndexedFields.length > 0 && (ast.type === 'select' || ast.type === 'update' || ast.type === 'delete')) {\n warnings.push({\n code: 'index_hint',\n message: `Query on \"${ast.table}\" filters on non-indexed fields: ${nonIndexedFields.join(', ')}.`,\n suggestion: 'Consider adding an index if this query is common.',\n })\n }\n\n return warnings\n}\n\nexport function analyzeExecution(ast: QueryAst, metrics: ExecutionMetrics): AnalyzerWarning[] {\n return analyzeExecutionWithConfig(ast, metrics, {\n slowQueryMs: 250,\n largeResultRows: 1000,\n })\n}\n\nexport function analyzeExecutionWithConfig(\n ast: QueryAst,\n metrics: ExecutionMetrics,\n config: Pick<AnalyzerConfig, 'slowQueryMs' | 'largeResultRows'>,\n): AnalyzerWarning[] {\n const warnings: AnalyzerWarning[] = []\n\n if (metrics.durationMs >= config.slowQueryMs) {\n warnings.push({\n code: 'slow_query',\n message: `Query on \"${ast.table}\" took ${metrics.durationMs}ms.`,\n suggestion: 'Check filters, indexes, or reduce the result size.',\n })\n }\n\n if (metrics.rows >= config.largeResultRows) {\n warnings.push({\n code: 'large_result',\n message: `Query on \"${ast.table}\" returned ${metrics.rows} rows.`,\n suggestion: 'Add a stricter filter or a smaller $limit.',\n })\n }\n\n return warnings\n}\n\nexport function repeatedQueryWarning(table: string, count: number): AnalyzerWarning {\n return {\n code: 'repeated_query',\n message: `Query pattern on \"${table}\" repeated ${count} times in this process.`,\n suggestion: 'Consider caching, batching, or moving this pattern behind a single query.',\n }\n}\n\nexport function formatWarning(warning: AnalyzerWarning): string {\n const lines = ['Kadak Warning:', warning.message]\n if (warning.suggestion) {\n lines.push('', 'Suggestion:', `-> ${warning.suggestion}`)\n }\n return lines.join('\\n')\n}\n","export type Condition =\n | { type: 'eq'; field: string; value: unknown; isInternal?: boolean }\n | { type: 'isNull'; field: string; isInternal?: boolean }\n | { type: 'in'; field: string; values: unknown[]; isInternal?: boolean }\n | { type: 'gt'; field: string; value: unknown; isInternal?: boolean }\n | { type: 'gte'; field: string; value: unknown; isInternal?: boolean }\n | { type: 'lt'; field: string; value: unknown; isInternal?: boolean }\n | { type: 'lte'; field: string; value: unknown; isInternal?: boolean }\n | { type: 'and'; conditions: Condition[]; isInternal?: boolean }\n | { type: 'or'; conditions: Condition[]; isInternal?: boolean }\n\nexport interface OrderNode {\n field: string\n direction: 'asc' | 'desc'\n}\n\nexport interface AssignmentNode {\n field: string\n value: unknown\n}\n\nexport type InsertCell =\n | { kind: 'value'; value: unknown }\n | { kind: 'default' }\n\nexport interface SelectAst {\n type: 'select'\n table: string\n where?: Condition\n order: OrderNode[]\n limit?: number\n}\n\nexport interface UpdateAst {\n type: 'update'\n table: string\n where?: Condition\n data: AssignmentNode[]\n}\n\nexport interface DeleteAst {\n type: 'delete'\n table: string\n where?: Condition\n}\n\nexport interface InsertAst {\n type: 'insert'\n table: string\n columns: string[]\n rows: InsertCell[][]\n}\n\nexport type QueryAst = SelectAst | UpdateAst | DeleteAst | InsertAst\n\nexport function andConditions(conditions: Array<Condition | undefined>): Condition | undefined {\n const compact = conditions.filter((condition): condition is Condition => Boolean(condition))\n if (compact.length === 0) return undefined\n if (compact.length === 1) return compact[0]\n return { type: 'and', conditions: compact }\n}\n\nexport function orConditions(conditions: Array<Condition | undefined>): Condition | undefined {\n const compact = conditions.filter((condition): condition is Condition => Boolean(condition))\n if (compact.length === 0) return undefined\n if (compact.length === 1) return compact[0]\n return { type: 'or', conditions: compact }\n}\n","import type { Infer, InferUpdate, Table, Column } from '../schema'\nimport { andConditions, orConditions, type AssignmentNode, type Condition, type InsertAst, type OrderNode, type SelectAst, type UpdateAst, type DeleteAst } from './ast'\n\ntype OperatorValue<T> = {\n $in?: T[]\n $gt?: T\n $gte?: T\n $lt?: T\n $lte?: T\n}\n\ntype FilterValue<T> = T | null | T[] | OperatorValue<T>\n\nexport type FindManyOptions<T extends Table<any, any>> = {\n [K in keyof Infer<T>]?: FilterValue<Infer<T>[K]>\n} & {\n $limit?: number\n $order?: Partial<Record<keyof Infer<T>, 'asc' | 'desc'>>\n $or?: Array<FindManyOptions<T>>\n}\n\nfunction isOperatorObject(value: unknown): value is OperatorValue<unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {\n return false\n }\n\n return Object.keys(value as Record<string, unknown>).some((key) => key.startsWith('$'))\n}\n\nfunction normalizeFieldValue(field: string, value: unknown): Condition | undefined {\n if (value === undefined) return undefined\n if (value === null) return { type: 'isNull', field }\n if (Array.isArray(value)) return { type: 'in', field, values: value }\n if (!isOperatorObject(value)) return { type: 'eq', field, value }\n\n const operatorValue = value as OperatorValue<unknown>\n return andConditions([\n operatorValue.$in ? { type: 'in', field, values: operatorValue.$in } : undefined,\n operatorValue.$gt !== undefined ? { type: 'gt', field, value: operatorValue.$gt } : undefined,\n operatorValue.$gte !== undefined ? { type: 'gte', field, value: operatorValue.$gte } : undefined,\n operatorValue.$lt !== undefined ? { type: 'lt', field, value: operatorValue.$lt } : undefined,\n operatorValue.$lte !== undefined ? { type: 'lte', field, value: operatorValue.$lte } : undefined,\n ])\n}\n\nfunction normalizeWhere<T extends Table<any, any>>(\n table: T,\n input?: Record<string, unknown>,\n): Condition | undefined {\n if (!input) return undefined\n\n const { $or, $limit, $order, ...plainFields } = input\n const conditions: Array<Condition | undefined> = []\n\n for (const [key, value] of Object.entries(plainFields)) {\n const field = table._columnMap[key] ?? key\n conditions.push(normalizeFieldValue(field, value))\n }\n\n if (Array.isArray($or)) {\n conditions.push(\n orConditions(\n $or.map((entry) => normalizeWhere(table, entry as Record<string, unknown>)),\n ),\n )\n }\n\n void $limit\n void $order\n\n return andConditions(conditions)\n}\n\nfunction normalizeOrder<T extends Table<any, any>>(\n table: T,\n order?: Partial<Record<keyof Infer<T>, 'asc' | 'desc'>>,\n): OrderNode[] {\n return Object.entries((order ?? {}) as Record<string, 'asc' | 'desc'>).map(([key, direction]) => ({\n field: table._columnMap[key] ?? key,\n direction: direction === 'desc' ? 'desc' : 'asc',\n }))\n}\n\nexport function normalizeSelect<T extends Table<any, any>>(\n table: T,\n query?: FindManyOptions<T>,\n options?: { includeSoftDeleted?: boolean; defaultLimit?: number },\n): SelectAst {\n const softDeleteCondition = options?.includeSoftDeleted ? undefined : normalizeSoftDelete(table)\n const explicitLimit = typeof query?.$limit === 'number' ? query.$limit : undefined\n\n return {\n type: 'select',\n table: table._name,\n where: andConditions([\n normalizeWhere(table, query as Record<string, unknown> | undefined),\n softDeleteCondition,\n ]),\n order: normalizeOrder(table, query?.$order),\n limit: explicitLimit ?? options?.defaultLimit,\n }\n}\n\nexport function normalizeUpdate<T extends Table<any, any>>(\n table: T,\n filter: Partial<Infer<T>> | undefined,\n data: InferUpdate<T>,\n): UpdateAst {\n const entries: AssignmentNode[] = Object.entries(data as Record<string, unknown>)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => ({\n field: table._columnMap[key] ?? key,\n value,\n }))\n\n return {\n type: 'update',\n table: table._name,\n where: normalizeWhere(table, filter as Record<string, unknown> | undefined),\n data: entries,\n }\n}\n\nexport function normalizeDelete<T extends Table<any, any>>(\n table: T,\n filter: Partial<Infer<T>> | undefined,\n): DeleteAst {\n return {\n type: 'delete',\n table: table._name,\n where: normalizeWhere(table, filter as Record<string, unknown> | undefined),\n }\n}\n\nexport function normalizeInsert<T extends Table<any, any>>(\n table: T,\n rows: Array<Record<string, unknown>>,\n): InsertAst {\n const jsColumns = Array.from(new Set(rows.flatMap((row) => Object.keys(row))))\n\n return {\n type: 'insert',\n table: table._name,\n columns: jsColumns.map((key) => table._columnMap[key] ?? key),\n rows: rows.map((row) =>\n jsColumns.map((columnKey) =>\n columnKey in row\n ? { kind: 'value', value: row[columnKey] }\n : { kind: 'default' },\n ),\n ),\n }\n}\n\nfunction normalizeSoftDelete(table: Table<any, any>): Condition | undefined {\n for (const [key, column] of Object.entries(table._columns)) {\n if ((column as Column<any, any, any>)._config.softDelete) {\n return { type: 'isNull', field: table._columnMap[key], isInternal: true }\n }\n }\n\n return undefined\n}\n","import type { DeleteAst, InsertAst, QueryAst, SelectAst, UpdateAst, Condition } from './ast'\n\nexport interface SqlStatement {\n text: string\n params: unknown[]\n}\n\nfunction quoteIdentifier(value: string): string {\n return `\"${value.replace(/\"/g, '\"\"')}\"`\n}\n\nfunction buildCondition(condition: Condition | undefined, params: unknown[]): string {\n if (!condition) return ''\n\n switch (condition.type) {\n case 'eq':\n params.push(condition.value)\n return `${quoteIdentifier(condition.field)} = $${params.length}`\n case 'isNull':\n return `${quoteIdentifier(condition.field)} IS NULL`\n case 'in':\n if (condition.values.length === 0) return 'FALSE'\n return `${quoteIdentifier(condition.field)} IN (${condition.values.map((value) => {\n params.push(value)\n return `$${params.length}`\n }).join(', ')})`\n case 'gt':\n params.push(condition.value)\n return `${quoteIdentifier(condition.field)} > $${params.length}`\n case 'gte':\n params.push(condition.value)\n return `${quoteIdentifier(condition.field)} >= $${params.length}`\n case 'lt':\n params.push(condition.value)\n return `${quoteIdentifier(condition.field)} < $${params.length}`\n case 'lte':\n params.push(condition.value)\n return `${quoteIdentifier(condition.field)} <= $${params.length}`\n case 'and':\n return `(${condition.conditions.map((entry) => buildCondition(entry, params)).join(' AND ')})`\n case 'or':\n return `(${condition.conditions.map((entry) => buildCondition(entry, params)).join(' OR ')})`\n }\n}\n\nfunction buildWhere(where: Condition | undefined, params: unknown[]): string {\n const sql = buildCondition(where, params)\n return sql ? ` WHERE ${sql}` : ''\n}\n\nfunction buildSelect(ast: SelectAst): SqlStatement {\n const params: unknown[] = []\n const where = buildWhere(ast.where, params)\n const order = ast.order.length > 0\n ? ` ORDER BY ${ast.order.map((entry) => `${quoteIdentifier(entry.field)} ${entry.direction.toUpperCase()}`).join(', ')}`\n : ''\n const limit = ast.limit !== undefined ? ` LIMIT ${ast.limit}` : ''\n\n return {\n text: `SELECT * FROM ${quoteIdentifier(ast.table)}${where}${order}${limit}`,\n params,\n }\n}\n\nfunction buildUpdate(ast: UpdateAst): SqlStatement {\n const params: unknown[] = []\n const setSql = ast.data.map((entry) => {\n params.push(entry.value)\n return `${quoteIdentifier(entry.field)} = $${params.length}`\n }).join(', ')\n\n const where = ast.where ? ` WHERE ${buildCondition(ast.where, params)}` : ''\n return {\n text: `UPDATE ${quoteIdentifier(ast.table)} SET ${setSql}${where} RETURNING *`,\n params,\n }\n}\n\nfunction buildDelete(ast: DeleteAst): SqlStatement {\n const params: unknown[] = []\n const where = buildWhere(ast.where, params)\n\n return {\n text: `DELETE FROM ${quoteIdentifier(ast.table)}${where} RETURNING *`,\n params,\n }\n}\n\nfunction buildInsert(ast: InsertAst): SqlStatement {\n if (ast.columns.length === 0) {\n return {\n text: `INSERT INTO ${quoteIdentifier(ast.table)} DEFAULT VALUES RETURNING *`,\n params: [],\n }\n }\n\n const params: unknown[] = []\n const values = ast.rows.map((row) =>\n `(${row.map((cell) => {\n if (cell.kind === 'default') return 'DEFAULT'\n params.push(cell.value)\n return `$${params.length}`\n }).join(', ')})`,\n ).join(', ')\n\n return {\n text: `INSERT INTO ${quoteIdentifier(ast.table)} (${ast.columns.map(quoteIdentifier).join(', ')}) VALUES ${values} RETURNING *`,\n params,\n }\n}\n\nexport function buildSql(ast: QueryAst): SqlStatement {\n switch (ast.type) {\n case 'select':\n return buildSelect(ast)\n case 'update':\n return buildUpdate(ast)\n case 'delete':\n return buildDelete(ast)\n case 'insert':\n return buildInsert(ast)\n }\n}\n","import type postgres from 'postgres'\nimport type { Table, Infer, InferInsert, InferUpdate, Column } from '../schema'\nimport { KadakError, fromValidationError, wrapPgError } from '../error'\nimport { analyzeAst, analyzeExecutionWithConfig, formatWarning, repeatedQueryWarning, type AnalyzerConfig, type AnalyzerWarning } from './analyzer'\nimport { normalizeDelete, normalizeInsert, normalizeSelect, normalizeUpdate, type FindManyOptions } from './normalize'\nimport { buildSql } from './sql'\nimport type { QueryAst } from './ast'\nimport type { KadakMetrics, QueryTelemetryEvent } from '../observability'\n\ntype RowShape = Record<string, unknown>\n\ninterface TableClientConfig {\n readSql?: postgres.Sql[]\n debug?: boolean\n warn?: (message: string) => void\n onQuery?: (event: QueryTelemetryEvent) => void\n warningMode?: 'relaxed' | 'strict' | 'silent'\n retryAttempts?: number\n retryDelayMs?: number\n retryOnCodes?: string[]\n retryWrites?: boolean\n defaultLimit?: number\n validateResults?: boolean\n slowQueryMs?: number\n largeResultRows?: number\n largeLimitThreshold?: number\n repeatedQueryThreshold?: number\n explainAnalyze?: boolean\n explainThresholdMs?: number\n metrics?: KadakMetrics\n}\n\nexport type { FindManyOptions } from './normalize'\n\nexport class TableClient<T extends Table<any, any>> {\n readonly _sql: postgres.Sql\n readonly _readSql: postgres.Sql[]\n readonly _table: T\n readonly _reverseMap: Record<string, string>\n readonly _debug: boolean\n readonly _warn: (message: string) => void\n readonly _onQuery?: (event: QueryTelemetryEvent) => void\n readonly _warningMode: 'relaxed' | 'strict' | 'silent'\n readonly _retryAttempts: number\n readonly _retryDelayMs: number\n readonly _retryOnCodes: Set<string>\n readonly _retryWrites: boolean\n readonly _defaultLimit?: number\n readonly _validateResults: boolean\n readonly _analyzerConfig: AnalyzerConfig\n readonly _repeatedQueryThreshold: number\n readonly _queryFingerprintCount: Map<string, number>\n readonly _selectValidator: { parse(data: unknown): any }\n readonly _explainAnalyze: boolean\n readonly _explainThresholdMs: number\n readonly _metrics?: KadakMetrics\n private _replicaIndex: number\n\n constructor(sql: postgres.Sql, table: T, config: TableClientConfig = {}) {\n this._sql = sql\n this._readSql = config.readSql ?? []\n this._table = table\n this._debug = config.debug ?? false\n this._warn = config.warn ?? ((message) => console.warn(message))\n this._onQuery = config.onQuery\n this._warningMode = config.warningMode ?? 'relaxed'\n this._retryAttempts = Math.max(0, config.retryAttempts ?? 0)\n this._retryDelayMs = Math.max(0, config.retryDelayMs ?? 200)\n this._retryOnCodes = new Set(config.retryOnCodes ?? ['08006', '08001', '57P01', '40001', '40P01'])\n this._retryWrites = config.retryWrites ?? false\n this._defaultLimit = config.defaultLimit\n this._validateResults = config.validateResults ?? false\n this._analyzerConfig = {\n slowQueryMs: config.slowQueryMs ?? 250,\n largeResultRows: config.largeResultRows ?? 1000,\n largeLimitThreshold: config.largeLimitThreshold ?? 1000,\n }\n this._repeatedQueryThreshold = config.repeatedQueryThreshold ?? 25\n this._queryFingerprintCount = new Map<string, number>()\n this._selectValidator = table.selectValidator()\n this._explainAnalyze = config.explainAnalyze ?? false\n this._explainThresholdMs = config.explainThresholdMs ?? 300\n this._metrics = config.metrics\n this._replicaIndex = 0\n this._reverseMap = {}\n\n for (const [camel, snake] of Object.entries(table._columnMap)) {\n this._reverseMap[snake] = camel\n }\n }\n\n async findMany(query?: FindManyOptions<T>): Promise<Infer<T>[]> {\n const ast = normalizeSelect(this._table, query, { defaultLimit: this._defaultLimit })\n return this._executeAst(ast)\n }\n\n async findFirst(query?: FindManyOptions<T>): Promise<Infer<T> | null> {\n const rows = await this.findMany({ ...(query ?? {}), $limit: 1 } as FindManyOptions<T>)\n return rows[0] ?? null\n }\n\n async insert(data: InferInsert<T>): Promise<Infer<T>>\n async insert(data: InferInsert<T>[]): Promise<Infer<T>[]>\n async insert(data: InferInsert<T> | InferInsert<T>[]): Promise<Infer<T> | Infer<T>[]> {\n const isBulk = Array.isArray(data)\n const validator = this._table.insertValidator()\n const rawRows = (isBulk ? data : [data]).map((row) => this._applyInsertAutomation(this._parseOrThrow(validator, row)))\n const ast = normalizeInsert(this._table, rawRows)\n const rows = await this._executeAst(ast)\n return isBulk ? rows : rows[0]\n }\n\n async update(filter: Partial<Infer<T>>, data: InferUpdate<T>): Promise<Infer<T>[]> {\n const parsed = this._applyUpdateAutomation(this._parseOrThrow(this._table.updateValidator(), data))\n const ast = normalizeUpdate(this._table, filter, parsed as InferUpdate<T>)\n\n if (ast.data.length === 0) {\n throw new KadakError({\n code: 'VALIDATION_ERROR',\n message: `Nothing to update in table '${this._table._name}'.`,\n hint: 'Pass at least one defined field in the update payload.',\n table: this._table._name,\n originalError: null,\n })\n }\n\n return this._executeAst(ast)\n }\n\n async delete(filter: Partial<Infer<T>>): Promise<Infer<T>[]> {\n const softDeleteKey = this._getSoftDeleteKey()\n if (softDeleteKey) {\n return this.update(filter, { [softDeleteKey]: new Date() } as InferUpdate<T>)\n }\n\n return this.hardDelete(filter)\n }\n\n async hardDelete(filter: Partial<Infer<T>>): Promise<Infer<T>[]> {\n const ast = normalizeDelete(this._table, filter)\n return this._executeAst(ast)\n }\n\n private async _executeAst(ast: QueryAst): Promise<Infer<T>[]> {\n const statement = buildSql(ast)\n this._registerQueryFingerprint(ast, statement.text)\n const astWarnings = analyzeAst(ast, this._table, this._analyzerConfig)\n this._handleWarnings(astWarnings, ast)\n\n const startedAt = Date.now()\n\n try {\n const rows = await this._executeWithRetry(\n ast,\n () => this._pickSqlClient(ast).unsafe(statement.text, statement.params as any[]),\n )\n const durationMs = Date.now() - startedAt\n\n const executionWarnings = analyzeExecutionWithConfig(ast, { durationMs, rows: rows.length }, this._analyzerConfig)\n this._handleWarnings(executionWarnings, ast)\n\n if (this._explainAnalyze && ast.type === 'select' && durationMs >= this._explainThresholdMs) {\n const explainWarnings = await this._explainWarnings(statement.text, statement.params as any[], ast.table)\n this._handleWarnings(explainWarnings, ast)\n }\n\n if (this._debug) {\n this._logDebug(ast.type, statement.text, durationMs, rows.length)\n }\n\n this._recordTelemetry({\n table: ast.table,\n operation: ast.type,\n sql: statement.text,\n durationMs,\n rows: rows.length,\n warned: astWarnings.length + executionWarnings.length > 0,\n warningCount: astWarnings.length + executionWarnings.length,\n })\n\n return rows.map((row) => this._parseResultOrThrow(this._toJs(row as Record<string, unknown>))) as Infer<T>[]\n } catch (error) {\n throw wrapPgError(error, this._table._name)\n }\n }\n\n private _pickSqlClient(ast: QueryAst): postgres.Sql {\n if (ast.type !== 'select') return this._sql\n if (this._readSql.length === 0) return this._sql\n\n const client = this._readSql[this._replicaIndex % this._readSql.length]\n this._replicaIndex += 1\n return client\n }\n\n private async _explainWarnings(sqlText: string, params: any[], table: string): Promise<AnalyzerWarning[]> {\n try {\n const rows = await this._sql.unsafe(`EXPLAIN (ANALYZE, FORMAT JSON) ${sqlText}`, params)\n const plan = rows?.[0]?.['QUERY PLAN']?.[0]?.Plan\n if (!plan) return []\n\n const warnings: AnalyzerWarning[] = []\n if (plan['Node Type'] === 'Seq Scan') {\n warnings.push({\n code: 'index_hint',\n message: `EXPLAIN on \"${table}\" shows a sequential scan.`,\n suggestion: 'Consider adding an index for frequent filter columns.',\n })\n }\n return warnings\n } catch {\n return []\n }\n }\n\n private async _executeWithRetry<T>(ast: QueryAst, fn: () => Promise<T>): Promise<T> {\n let attempt = 0\n\n while (true) {\n try {\n return await fn()\n } catch (error) {\n if (!this._shouldRetry(ast, error, attempt)) {\n throw error\n }\n\n attempt += 1\n await this._sleep(this._retryDelayMs * attempt)\n }\n }\n }\n\n private _shouldRetry(ast: QueryAst, error: unknown, attempt: number): boolean {\n if (attempt >= this._retryAttempts) return false\n if (ast.type !== 'select' && !this._retryWrites) return false\n\n const pgError = error as { code?: string } | undefined\n const code = pgError?.code\n return Boolean(code && this._retryOnCodes.has(code))\n }\n\n private _sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n }\n\n private _registerQueryFingerprint(ast: QueryAst, sqlText: string) {\n const key = `${ast.type}:${sqlText}`\n const count = (this._queryFingerprintCount.get(key) ?? 0) + 1\n this._queryFingerprintCount.set(key, count)\n\n if (count === this._repeatedQueryThreshold) {\n this._handleWarnings([repeatedQueryWarning(ast.table, count)], ast)\n }\n }\n\n private _handleWarnings(warnings: AnalyzerWarning[], ast: QueryAst) {\n if (this._warningMode === 'silent') return\n\n for (const warning of warnings) {\n const message = formatWarning(warning)\n\n if (this._warningMode === 'strict') {\n throw new KadakError({\n code: 'QUERY_WARNING',\n message,\n hint: `Fix the warning or switch warnings mode from 'strict'.`,\n table: ast.table,\n originalError: warning,\n })\n }\n\n this._warn(message)\n }\n }\n\n private _applyInsertAutomation(data: RowShape): RowShape {\n const next = { ...data }\n\n for (const [key, column] of Object.entries(this._table._columns)) {\n const config = (column as Column<any, any, any>)._config\n if (next[key] !== undefined) continue\n\n if (config.autoUpdate) {\n next[key] = new Date()\n } else if (config.defaultValue !== undefined && !config.defaultSql) {\n // Only inject JS-side defaultValue if there is no SQL-side defaultSql\n next[key] = config.defaultValue\n }\n }\n\n return next\n }\n\n private _applyUpdateAutomation(data: RowShape): RowShape {\n const next = { ...data }\n\n for (const [key, column] of Object.entries(this._table._columns)) {\n if ((column as Column<any, any, any>)._config.autoUpdate) {\n // updatedAt pattern\n next[key] = new Date()\n }\n }\n\n return next\n }\n\n private _parseOrThrow(validator: { parse(data: unknown): any }, data: unknown): any {\n try {\n return validator.parse(data)\n } catch (error) {\n throw fromValidationError(error, this._table._name)\n }\n }\n\n private _parseResultOrThrow(row: Record<string, unknown>) {\n if (!this._validateResults) return row\n\n try {\n return this._selectValidator.parse(row)\n } catch (error) {\n throw fromValidationError(error, this._table._name)\n }\n }\n\n private _recordTelemetry(event: QueryTelemetryEvent) {\n this._metrics?.record(event)\n this._onQuery?.(event)\n }\n\n private _getSoftDeleteKey(): string | undefined {\n for (const [key, column] of Object.entries(this._table._columns)) {\n if ((column as Column<any, any, any>)._config.softDelete) {\n return key\n }\n }\n\n return undefined\n }\n\n private _logDebug(operation: string, sqlText: string, durationMs: number, rows: number) {\n console.log(\n ['[Kadak]', `Query: ${this._table._name}.${operation}`, `SQL: ${sqlText}`, `Time: ${durationMs}ms`, `Rows: ${rows}`].join('\\n'),\n )\n }\n\n private _toJs(row: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [snake, value] of Object.entries(row)) {\n result[this._reverseMap[snake] ?? snake] = value\n }\n return result\n }\n}\n","import postgres from 'postgres'\nimport type { Table } from './schema'\nimport { TableClient } from './query'\nimport type { KadakMetrics, QueryTelemetryEvent } from './observability'\n\nexport type WarningMode = 'relaxed' | 'strict' | 'silent'\n\nexport interface ConnectionConfig {\n url: string\n readReplicas?: string[]\n max?: number\n ssl?: boolean | 'require' | 'prefer' | 'allow'\n timeout?: number\n retryAttempts?: number\n retryDelayMs?: number\n retryOnCodes?: string[]\n retryWrites?: boolean\n debug?: boolean\n warnings?: WarningMode\n onWarn?: (message: string) => void\n defaultLimit?: number\n validateResults?: boolean\n slowQueryMs?: number\n largeResultRows?: number\n largeLimitThreshold?: number\n repeatedQueryThreshold?: number\n explainAnalyze?: boolean\n explainThresholdMs?: number\n metrics?: KadakMetrics\n onQuery?: (event: QueryTelemetryEvent) => void\n}\n\nexport type Database<TTables extends Record<string, Table<any, any>>> = {\n [K in keyof TTables]: TTables[K] extends Table<any, any> ? TableClient<TTables[K]> : never\n} & {\n close(): Promise<void>\n sql: postgres.Sql\n readSql: postgres.Sql[]\n transaction<R>(fn: (tx: Database<TTables>) => Promise<R>): Promise<R>\n}\n\ntype RuntimeConfig = Pick<\n ConnectionConfig,\n | 'debug'\n | 'onWarn'\n | 'onQuery'\n | 'warnings'\n | 'retryAttempts'\n | 'retryDelayMs'\n | 'retryOnCodes'\n | 'retryWrites'\n | 'defaultLimit'\n | 'validateResults'\n | 'slowQueryMs'\n | 'largeResultRows'\n | 'largeLimitThreshold'\n | 'repeatedQueryThreshold'\n | 'explainAnalyze'\n | 'explainThresholdMs'\n | 'metrics'\n>\n\nfunction buildDatabase<TTables extends Record<string, Table<any, any>>>(\n sql: postgres.Sql,\n readSql: postgres.Sql[],\n tables: TTables,\n runtimeConfig: RuntimeConfig,\n canClose: boolean,\n): Database<TTables> {\n const db = {} as any\n for (const [key, table] of Object.entries(tables)) {\n db[key] = new TableClient(sql, table as Table<any, any>, {\n readSql,\n debug: runtimeConfig.debug,\n warn: runtimeConfig.onWarn,\n onQuery: runtimeConfig.onQuery,\n warningMode: runtimeConfig.warnings,\n retryAttempts: runtimeConfig.retryAttempts,\n retryDelayMs: runtimeConfig.retryDelayMs,\n retryOnCodes: runtimeConfig.retryOnCodes,\n retryWrites: runtimeConfig.retryWrites,\n defaultLimit: runtimeConfig.defaultLimit,\n validateResults: runtimeConfig.validateResults,\n slowQueryMs: runtimeConfig.slowQueryMs,\n largeResultRows: runtimeConfig.largeResultRows,\n largeLimitThreshold: runtimeConfig.largeLimitThreshold,\n repeatedQueryThreshold: runtimeConfig.repeatedQueryThreshold,\n explainAnalyze: runtimeConfig.explainAnalyze,\n explainThresholdMs: runtimeConfig.explainThresholdMs,\n metrics: runtimeConfig.metrics,\n })\n }\n\n db.close = async () => {\n if (!canClose) return\n await Promise.all([sql.end(), ...readSql.map((replica) => replica.end())])\n }\n db.sql = sql\n db.readSql = readSql\n db.transaction = async <R>(fn: (tx: Database<TTables>) => Promise<R>) => {\n return sql.begin(async (txSql) => {\n const txDb = buildDatabase(txSql as unknown as postgres.Sql, [], tables, runtimeConfig, false)\n return fn(txDb)\n })\n }\n\n return db as Database<TTables>\n}\n\nexport function connect<TTables extends Record<string, Table<any, any>>>(\n config: string | ConnectionConfig,\n tables: TTables,\n): Database<TTables> {\n const isString = typeof config === 'string'\n const url = isString ? config : config.url\n const opts = isString ? {} as ConnectionConfig : config\n\n const isLocal = url.includes('localhost') || url.includes('127.0.0.1')\n const ssl = opts.ssl ?? (isLocal ? false : 'require')\n\n const sql = postgres(url, {\n max: opts.max ?? 10,\n idle_timeout: 20,\n connect_timeout: opts.timeout ?? 30,\n ssl,\n max_lifetime: 60 * 30,\n onnotice: () => {},\n })\n\n const readSql = (opts.readReplicas ?? []).map((replicaUrl) =>\n postgres(replicaUrl, {\n max: opts.max ?? 10,\n idle_timeout: 20,\n connect_timeout: opts.timeout ?? 30,\n ssl,\n max_lifetime: 60 * 30,\n onnotice: () => {},\n }),\n )\n\n return buildDatabase(sql, readSql, tables, opts, true)\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport postgres from 'postgres'\n\nexport interface MigrationConfig {\n url: string\n dir?: string\n tableName?: string\n}\n\nexport interface MigrationResult {\n applied: string[]\n skipped: string[]\n}\n\nasync function ensureMigrationTable(sql: postgres.Sql, tableName: string) {\n await sql.unsafe(`\n CREATE TABLE IF NOT EXISTS \"${tableName}\" (\n id BIGSERIAL PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n )\n `)\n}\n\nexport async function runMigrations(config: MigrationConfig): Promise<MigrationResult> {\n const dir = config.dir ?? path.resolve(process.cwd(), 'migrations')\n const tableName = config.tableName ?? 'kadak_migrations'\n const sql = postgres(config.url)\n\n try {\n await ensureMigrationTable(sql, tableName)\n\n const entries = await fs.readdir(dir).catch(() => [])\n const files = entries.filter((entry) => entry.endsWith('.sql')).sort()\n const rows = await sql.unsafe(`SELECT name FROM \"${tableName}\"`)\n const appliedSet = new Set<string>(rows.map((row: any) => row.name))\n\n const applied: string[] = []\n const skipped: string[] = []\n\n for (const file of files) {\n if (appliedSet.has(file)) {\n skipped.push(file)\n continue\n }\n\n const sqlText = await fs.readFile(path.join(dir, file), 'utf8')\n await sql.begin(async (tx) => {\n await tx.unsafe(sqlText)\n await tx.unsafe(`INSERT INTO \"${tableName}\" (name) VALUES ($1)`, [file])\n })\n applied.push(file)\n }\n\n return { applied, skipped }\n } finally {\n await sql.end()\n }\n}\n\nexport async function createMigration(name: string, dir = path.resolve(process.cwd(), 'migrations')): Promise<string> {\n const safeName = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '')\n const timestamp = new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14)\n const fileName = `${timestamp}_${safeName || 'migration'}.sql`\n const filePath = path.join(dir, fileName)\n\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, '-- Write your SQL migration here\\n')\n return filePath\n}\n","export interface QueryTelemetryEvent {\n table: string\n operation: 'select' | 'insert' | 'update' | 'delete'\n sql: string\n durationMs: number\n rows: number\n warned: boolean\n warningCount: number\n}\n\nexport class KadakMetrics {\n private totalQueries = 0\n private totalDurationMs = 0\n private warningQueries = 0\n private byOperation: Record<string, { count: number; totalDurationMs: number; rows: number }> = {}\n\n record(event: QueryTelemetryEvent) {\n this.totalQueries += 1\n this.totalDurationMs += event.durationMs\n if (event.warned) this.warningQueries += 1\n\n const bucket = this.byOperation[event.operation] ?? { count: 0, totalDurationMs: 0, rows: 0 }\n bucket.count += 1\n bucket.totalDurationMs += event.durationMs\n bucket.rows += event.rows\n this.byOperation[event.operation] = bucket\n }\n\n snapshot() {\n return {\n totalQueries: this.totalQueries,\n totalDurationMs: this.totalDurationMs,\n avgDurationMs: this.totalQueries === 0 ? 0 : this.totalDurationMs / this.totalQueries,\n warningQueries: this.warningQueries,\n byOperation: { ...this.byOperation },\n }\n }\n\n exportPrometheus(prefix = 'kadak') {\n const lines: string[] = []\n lines.push(`# HELP ${prefix}_queries_total Total queries executed`)\n lines.push(`# TYPE ${prefix}_queries_total counter`)\n lines.push(`${prefix}_queries_total ${this.totalQueries}`)\n\n lines.push(`# HELP ${prefix}_query_duration_ms_total Total query duration in milliseconds`)\n lines.push(`# TYPE ${prefix}_query_duration_ms_total counter`)\n lines.push(`${prefix}_query_duration_ms_total ${this.totalDurationMs}`)\n\n lines.push(`# HELP ${prefix}_warning_queries_total Queries that emitted at least one warning`)\n lines.push(`# TYPE ${prefix}_warning_queries_total counter`)\n lines.push(`${prefix}_warning_queries_total ${this.warningQueries}`)\n\n for (const [operation, value] of Object.entries(this.byOperation)) {\n lines.push(`${prefix}_operation_queries_total{operation=\"${operation}\"} ${value.count}`)\n lines.push(`${prefix}_operation_duration_ms_total{operation=\"${operation}\"} ${value.totalDurationMs}`)\n lines.push(`${prefix}_operation_rows_total{operation=\"${operation}\"} ${value.rows}`)\n }\n\n return lines.join('\\n')\n }\n}\n","export const KADAK_API_VERSION = '1.0.0'\n"],"mappings":"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"}