@warlock.js/cascade 5.22.1 → 5.23.1

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +29 -12
  2. package/cjs/index.cjs +119 -40
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/drivers/postgres/index.d.mts +2 -2
  5. package/esm/drivers/postgres/index.mjs +1 -1
  6. package/esm/drivers/postgres/naming.mjs +18 -0
  7. package/esm/drivers/postgres/naming.mjs.map +1 -0
  8. package/esm/drivers/postgres/postgres-dialect.d.mts +9 -1
  9. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  10. package/esm/drivers/postgres/postgres-dialect.mjs +10 -0
  11. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  12. package/esm/drivers/postgres/postgres-driver.d.mts +7 -1
  13. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  14. package/esm/drivers/postgres/postgres-driver.mjs +46 -16
  15. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  16. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  17. package/esm/drivers/postgres/postgres-query-builder.mjs +22 -15
  18. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  19. package/esm/drivers/postgres/postgres-query-parser.d.mts +1 -0
  20. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  21. package/esm/drivers/postgres/postgres-query-parser.mjs +16 -10
  22. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  23. package/esm/drivers/postgres/types.d.mts +2 -1
  24. package/esm/drivers/postgres/types.d.mts.map +1 -1
  25. package/esm/index.d.mts +3 -3
  26. package/esm/index.mjs +2 -2
  27. package/esm/model/model.d.mts +5 -1
  28. package/esm/model/model.d.mts.map +1 -1
  29. package/esm/model/model.mjs +6 -0
  30. package/esm/model/model.mjs.map +1 -1
  31. package/esm/utils/connect-to-database.d.mts.map +1 -1
  32. package/esm/utils/connect-to-database.mjs +6 -0
  33. package/esm/utils/connect-to-database.mjs.map +1 -1
  34. package/esm/validation/plugins/database-rules-plugin.d.mts +12 -4
  35. package/esm/validation/plugins/database-rules-plugin.d.mts.map +1 -1
  36. package/esm/validation/plugins/database-rules-plugin.mjs.map +1 -1
  37. package/package.json +5 -5
@@ -1,6 +1,6 @@
1
- import { PostgresDialect, escapeSqlLiteral } from "./postgres-dialect.mjs";
1
+ import { PostgresDialect, PostgresDialectOptions, PostgresNaming, escapeSqlLiteral } from "./postgres-dialect.mjs";
2
2
  import { PostgresConnectionConfig, PostgresCopyOptions, PostgresIsolationLevel, PostgresNotification, PostgresOperation, PostgresPoolConfig, PostgresQueryResult, PostgresTransactionOptions, PostgresWhereClause } from "./types.mjs";
3
- import { PostgresDriver, buildPostgresPoolConfig } from "./postgres-driver.mjs";
3
+ import { PostgresDriver, buildPostgresPoolConfig, parsePostgresInt8 } from "./postgres-driver.mjs";
4
4
  import { PostgresBlueprint } from "./postgres-blueprint.mjs";
5
5
  import { PostgresMigrationDriver } from "./postgres-migration-driver.mjs";
6
6
  import { PostgresQueryBuilder } from "./postgres-query-builder.mjs";
@@ -3,6 +3,6 @@ import { PostgresDialect, escapeSqlLiteral } from "./postgres-dialect.mjs";
3
3
  import { PostgresMigrationDriver } from "./postgres-migration-driver.mjs";
4
4
  import { PostgresQueryParser } from "./postgres-query-parser.mjs";
5
5
  import { PostgresQueryBuilder } from "./postgres-query-builder.mjs";
6
- import { PostgresDriver, buildPostgresPoolConfig } from "./postgres-driver.mjs";
6
+ import { PostgresDriver, buildPostgresPoolConfig, parsePostgresInt8 } from "./postgres-driver.mjs";
7
7
 
8
8
  export { };
@@ -0,0 +1,18 @@
1
+ //#region ../cascade/src/drivers/postgres/naming.ts
2
+ /** Convert a JavaScript camelCase key to a PostgreSQL snake_case column name. */
3
+ function camelToSnake(value) {
4
+ if (value === "*" || value.includes("_")) return value;
5
+ return (value.match(/[A-Z]+(?=[A-Z][a-z]|\d|$)|[A-Z]?[a-z]+|\d+/g) ?? [value]).map((part) => part.toLowerCase()).join("_");
6
+ }
7
+ /** Convert a PostgreSQL snake_case column name to a JavaScript camelCase key. */
8
+ function snakeToCamel(value) {
9
+ return value.replace(/_([a-z0-9])/g, (_match, character) => character.toUpperCase());
10
+ }
11
+ /** Convert top-level database row keys without rewriting nested JSON values. */
12
+ function snakeKeysToCamel(row) {
13
+ return Object.fromEntries(Object.entries(row).map(([key, value]) => [snakeToCamel(key), value]));
14
+ }
15
+
16
+ //#endregion
17
+ export { camelToSnake, snakeKeysToCamel };
18
+ //# sourceMappingURL=naming.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/naming.ts"],"sourcesContent":["/** Convert a JavaScript camelCase key to a PostgreSQL snake_case column name. */\nexport function camelToSnake(value: string): string {\n if (value === \"*\" || value.includes(\"_\")) return value;\n\n return (value.match(/[A-Z]+(?=[A-Z][a-z]|\\d|$)|[A-Z]?[a-z]+|\\d+/g) ?? [value])\n .map((part) => part.toLowerCase())\n .join(\"_\");\n}\n\n/** Convert a PostgreSQL snake_case column name to a JavaScript camelCase key. */\nexport function snakeToCamel(value: string): string {\n return value.replace(/_([a-z0-9])/g, (_match, character: string) => character.toUpperCase());\n}\n\n/** Convert top-level database row keys without rewriting nested JSON values. */\nexport function snakeKeysToCamel<T extends Record<string, unknown>>(row: T): Record<string, unknown> {\n return Object.fromEntries(Object.entries(row).map(([key, value]) => [snakeToCamel(key), value]));\n}\n"],"mappings":";;AACA,SAAgB,aAAa,OAAuB;CAClD,IAAI,UAAU,OAAO,MAAM,SAAS,GAAG,GAAG,OAAO;CAEjD,QAAQ,MAAM,MAAM,6CAA6C,KAAK,CAAC,KAAK,EAAC,CAC1E,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC,CACjC,KAAK,GAAG;AACb;;AAGA,SAAgB,aAAa,OAAuB;CAClD,OAAO,MAAM,QAAQ,iBAAiB,QAAQ,cAAsB,UAAU,YAAY,CAAC;AAC7F;;AAGA,SAAgB,iBAAoD,KAAiC;CACnG,OAAO,OAAO,YAAY,OAAO,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,CAAC,aAAa,GAAG,GAAG,KAAK,CAAC,CAAC;AACjG"}
@@ -3,6 +3,10 @@ import { AggregateExpression } from "../../expressions/aggregate-expressions.mjs
3
3
  import { SqlDialectContract } from "../sql/sql-dialect.contract.mjs";
4
4
 
5
5
  //#region ../cascade/src/drivers/postgres/postgres-dialect.d.ts
6
+ type PostgresNaming = "preserve" | "snake_case";
7
+ type PostgresDialectOptions = {
8
+ readonly naming?: PostgresNaming;
9
+ };
6
10
  /**
7
11
  * PostgreSQL-specific SQL dialect implementation.
8
12
  *
@@ -23,6 +27,8 @@ import { SqlDialectContract } from "../sql/sql-dialect.contract.mjs";
23
27
  * ```
24
28
  */
25
29
  declare class PostgresDialect implements SqlDialectContract {
30
+ private readonly naming;
31
+ constructor(options?: PostgresDialectOptions);
26
32
  /**
27
33
  * Dialect name identifier.
28
34
  */
@@ -54,6 +60,8 @@ declare class PostgresDialect implements SqlDialectContract {
54
60
  * @returns The quoted identifier (e.g., '"user"')
55
61
  */
56
62
  quoteIdentifier(identifier: string): string;
63
+ /** Quote a table identifier without applying the column naming policy. */
64
+ quoteTableIdentifier(identifier: string): string;
57
65
  /**
58
66
  * Convert a boolean to PostgreSQL literal.
59
67
  *
@@ -172,5 +180,5 @@ declare class PostgresDialect implements SqlDialectContract {
172
180
  */
173
181
  declare function escapeSqlLiteral(value: string): string;
174
182
  //#endregion
175
- export { PostgresDialect, escapeSqlLiteral };
183
+ export { PostgresDialect, PostgresDialectOptions, PostgresNaming, escapeSqlLiteral };
176
184
  //# sourceMappingURL=postgres-dialect.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-dialect.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-dialect.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAgCa,eAAA,YAA2B,kBAAA;EAgHF;;;EAAA,SA5GpB,IAAA;EAqId;;;EAAA,SAhIc,iBAAA;EAoJK;;;EAAA,SA/IL,aAAA;EA4JF;;;;;;;;EAlJP,WAAA,CAAY,KAAA;EAkTsB;;;;;;AAoCkC;AAY7E;;EArVS,eAAA,CAAgB,UAAA;EAqVqB;AAAA;;;;;EAzUrC,cAAA,CAAe,KAAA;;;;;;;;EAWf,WAAA,CAAY,KAAA,WAAgB,MAAA;;;;;;;;;;;EAwB5B,WAAA,CAAY,MAAA,UAAgB,IAAA;;;;;;;;;;;EA4B5B,YAAA,CAAa,MAAA,UAAgB,KAAA,WAAgB,IAAA;;;;;;;;;;EAuB7C,WAAA,CACL,OAAA,UACA,eAAA;IACG,QAAA;IAAkB,OAAA;EAAA;;;;;;;;;;EAmBhB,aAAA,CAAc,MAAA,UAAgB,UAAA;;;;;;;;EAW9B,UAAA,CACL,IAAA,UACA,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,KAAA;IAAgB,UAAA;EAAA;;;;;;;;;;;;;EA8G5D,cAAA,CAAe,UAAA,EAAY,mBAAA;;;;;;;;;;;;EAkD3B,qBAAA,CAAsB,UAAA,EAAY,gBAAA;;;;;;;;;;;;;;EAoClC,YAAA,CAAa,MAAA,UAAgB,IAAA;AAAA;;;;iBAYtB,gBAAA,CAAiB,KAAa"}
1
+ {"version":3,"file":"postgres-dialect.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-dialect.ts"],"mappings":";;;;;KAcY,cAAA;AAAA,KAEA,sBAAA;EAAA,SAAoC,MAAA,GAAS,cAAc;AAAA;AAqBvE;;;;;;;;;;;;;;;;;;;AAAA,cAAa,eAAA,YAA2B,kBAAA;EAAA,iBACrB,MAAA;cAEE,OAAA,GAAS,sBAAA;EAgDA;;;EAAA,SA1CZ,IAAA;EA+DG;;;EAAA,SA1DH,iBAAA;EAkFmB;;;EAAA,SA7EnB,aAAA;EAyGoC;;;;;;;;EA/F7C,WAAA,CAAY,KAAA;EAuJZ;;;;;;;;;EA1IA,eAAA,CAAgB,UAAA;EA4ShB;EAnSA,oBAAA,CAAqB,UAAA;EAmSC;;;;;AAoC8C;EA7TpE,cAAA,CAAe,KAAA;EAyUQ;;;AAAc;;;;EA9TrC,WAAA,CAAY,KAAA,WAAgB,MAAA;;;;;;;;;;;EAwB5B,WAAA,CAAY,MAAA,UAAgB,IAAA;;;;;;;;;;;EA4B5B,YAAA,CAAa,MAAA,UAAgB,KAAA,WAAgB,IAAA;;;;;;;;;;EAuB7C,WAAA,CACL,OAAA,UACA,eAAA;IACG,QAAA;IAAkB,OAAA;EAAA;;;;;;;;;;EAmBhB,aAAA,CAAc,MAAA,UAAgB,UAAA;;;;;;;;EAW9B,UAAA,CACL,IAAA,UACA,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,KAAA;IAAgB,UAAA;EAAA;;;;;;;;;;;;;EA8G5D,cAAA,CAAe,UAAA,EAAY,mBAAA;;;;;;;;;;;;EAkD3B,qBAAA,CAAsB,UAAA,EAAY,gBAAA;;;;;;;;;;;;;;EAoClC,YAAA,CAAa,MAAA,UAAgB,IAAA;AAAA;;;;iBAYtB,gBAAA,CAAiB,KAAa"}
@@ -1,3 +1,5 @@
1
+ import { camelToSnake } from "./naming.mjs";
2
+
1
3
  //#region ../cascade/src/drivers/postgres/postgres-dialect.ts
2
4
  /**
3
5
  * PostgreSQL-specific SQL dialect implementation.
@@ -19,6 +21,10 @@
19
21
  * ```
20
22
  */
21
23
  var PostgresDialect = class {
24
+ naming;
25
+ constructor(options = {}) {
26
+ this.naming = options.naming ?? "preserve";
27
+ }
22
28
  /**
23
29
  * Dialect name identifier.
24
30
  */
@@ -52,6 +58,10 @@ var PostgresDialect = class {
52
58
  * @returns The quoted identifier (e.g., '"user"')
53
59
  */
54
60
  quoteIdentifier(identifier) {
61
+ return identifier.split(".").map((part) => `"${(this.naming === "snake_case" ? camelToSnake(part) : part).replace(/"/g, "\"\"")}"`).join(".");
62
+ }
63
+ /** Quote a table identifier without applying the column naming policy. */
64
+ quoteTableIdentifier(identifier) {
55
65
  return identifier.split(".").map((part) => `"${part.replace(/"/g, "\"\"")}"`).join(".");
56
66
  }
57
67
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-dialect.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-dialect.ts"],"sourcesContent":["/**\n * PostgreSQL Dialect Implementation\n *\n * Implements the SqlDialectContract for PostgreSQL-specific SQL syntax.\n * Handles parameter placeholders ($1, $2), identifier quoting, and\n * PostgreSQL-specific features like JSONB operators.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type { AggregateExpression, ColumnExpression } from \"../../expressions\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\n\n/**\n * PostgreSQL-specific SQL dialect implementation.\n *\n * Provides PostgreSQL syntax for:\n * - Parameter placeholders ($1, $2, $3...)\n * - Identifier quoting with double quotes\n * - JSONB operators (->, ->>, @>)\n * - ILIKE for case-insensitive matching\n * - RETURNING clause support\n *\n * @example\n * ```typescript\n * const dialect = new PostgresDialect();\n *\n * dialect.placeholder(1); // \"$1\"\n * dialect.quoteIdentifier('user'); // '\"user\"'\n * dialect.jsonExtract('data', 'name'); // \"data\"->>'name'\n * ```\n */\nexport class PostgresDialect implements SqlDialectContract {\n /**\n * Dialect name identifier.\n */\n public readonly name = \"postgres\" as const;\n\n /**\n * PostgreSQL supports the RETURNING clause for INSERT/UPDATE/DELETE.\n */\n public readonly supportsReturning = true;\n\n /**\n * PostgreSQL uses ON CONFLICT for upsert operations.\n */\n public readonly upsertKeyword = \"ON CONFLICT\" as const;\n\n /**\n * Generate a PostgreSQL parameter placeholder.\n *\n * PostgreSQL uses numbered placeholders: $1, $2, $3, etc.\n *\n * @param index - The 1-based parameter index\n * @returns The placeholder string (e.g., \"$1\")\n */\n public placeholder(index: number): string {\n return `$${index}`;\n }\n\n /**\n * Quote an identifier using PostgreSQL's double-quote syntax.\n *\n * Handles escaping of embedded double quotes by doubling them.\n * This is necessary for reserved words and special characters.\n *\n * @param identifier - The identifier (table/column name) to quote\n * @returns The quoted identifier (e.g., '\"user\"')\n */\n public quoteIdentifier(identifier: string): string {\n // Split on dots for qualified names (schema.table.column)\n const parts = identifier.split(\".\");\n return parts.map((part) => `\"${part.replace(/\"/g, '\"\"')}\"`).join(\".\");\n }\n\n /**\n * Convert a boolean to PostgreSQL literal.\n *\n * @param value - The boolean value\n * @returns \"TRUE\" or \"FALSE\"\n */\n public booleanLiteral(value: boolean): string {\n return value ? \"TRUE\" : \"FALSE\";\n }\n\n /**\n * Build LIMIT/OFFSET clause for PostgreSQL.\n *\n * @param limit - Maximum rows to return\n * @param offset - Rows to skip\n * @returns The SQL clause (e.g., \"LIMIT 10 OFFSET 20\")\n */\n public limitOffset(limit?: number, offset?: number): string {\n const parts: string[] = [];\n\n if (limit !== undefined) {\n parts.push(`LIMIT ${toSafeInteger(limit, \"LIMIT\")}`);\n }\n\n if (offset !== undefined) {\n parts.push(`OFFSET ${toSafeInteger(offset, \"OFFSET\")}`);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build a JSON path extraction expression for PostgreSQL.\n *\n * Uses the ->> operator for text extraction from JSONB columns.\n * Supports nested paths using chained operators.\n *\n * @param column - The JSONB column name\n * @param path - The path to extract (dot notation: \"user.name\")\n * @returns The SQL expression (e.g., \"data\"->>'user'->>'name')\n */\n public jsonExtract(column: string, path: string): string {\n const quotedColumn = this.quoteIdentifier(column);\n const pathParts = path.split(\".\");\n\n if (pathParts.length === 1) {\n return `${quotedColumn}->>'${escapeSqlLiteral(pathParts[0] as string)}'`;\n }\n\n // For nested paths: data->'user'->>'name' (last one gets text extraction)\n const jsonPath = pathParts\n .slice(0, -1)\n .map((p) => `'${escapeSqlLiteral(p)}'`)\n .join(\"->\");\n const lastKey = pathParts[pathParts.length - 1];\n\n return `${quotedColumn}->${jsonPath}->>'${escapeSqlLiteral(lastKey as string)}'`;\n }\n\n /**\n * Build a JSON contains expression for PostgreSQL.\n *\n * Uses the @> containment operator for JSONB columns.\n *\n * @param column - The JSONB column name\n * @param value - The value to check for\n * @param path - Optional path within the JSON\n * @returns The SQL expression\n */\n public jsonContains(column: string, value: unknown, path?: string): string {\n const quotedColumn = this.quoteIdentifier(column);\n\n if (path) {\n // Check if a specific path contains the value\n const jsonValue = JSON.stringify({ [path]: value });\n return `${quotedColumn} @> '${escapeSqlLiteral(jsonValue)}'::jsonb`;\n }\n\n // Check if the column contains the value (for arrays or objects)\n const jsonValue = JSON.stringify(value);\n return `${quotedColumn} @> '${escapeSqlLiteral(jsonValue)}'::jsonb`;\n }\n\n /**\n * Build a LIKE pattern expression for PostgreSQL.\n *\n * Uses ILIKE for case-insensitive matching, LIKE for case-sensitive.\n *\n * @param pattern - The pattern to match\n * @param caseInsensitive - Whether to use case-insensitive matching\n * @returns Object with operator and pattern\n */\n public likePattern(\n pattern: string,\n caseInsensitive = true,\n ): { operator: string; pattern: string } {\n // Escape special characters in the pattern (%, _, \\)\n const escapedPattern = pattern.replace(/\\\\/g, \"\\\\\\\\\").replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\");\n\n return {\n operator: caseInsensitive ? \"ILIKE\" : \"LIKE\",\n pattern: escapedPattern,\n };\n }\n\n /**\n * Build an array contains expression for PostgreSQL.\n *\n * Uses ANY() for checking if a value is in an array column.\n *\n * @param column - The array column name\n * @param paramIndex - The parameter index\n * @returns The SQL expression\n */\n public arrayContains(column: string, paramIndex: number): string {\n return `${this.placeholder(paramIndex)} = ANY(${this.quoteIdentifier(column)})`;\n }\n\n /**\n * Get the PostgreSQL SQL type for an abstract type.\n *\n * @param type - The abstract type name\n * @param options - Type-specific options\n * @returns The PostgreSQL type string\n */\n public getSqlType(\n type: string,\n options?: { length?: number; precision?: number; scale?: number; dimensions?: number },\n ): string {\n switch (type) {\n case \"string\":\n return options?.length ? `VARCHAR(${options.length})` : \"TEXT\";\n case \"char\":\n return `CHAR(${options?.length ?? 1})`;\n case \"text\":\n return \"TEXT\";\n case \"mediumText\":\n case \"longText\":\n return \"TEXT\"; // PostgreSQL doesn't distinguish text sizes\n case \"integer\":\n return \"INTEGER\";\n case \"smallInteger\":\n return \"SMALLINT\";\n case \"tinyInteger\":\n return \"SMALLINT\"; // PostgreSQL doesn't have TINYINT\n case \"bigInteger\":\n return \"BIGINT\";\n case \"float\":\n return \"REAL\";\n case \"double\":\n return \"DOUBLE PRECISION\";\n case \"decimal\":\n if (options?.precision !== undefined) {\n const scale = options.scale ?? 0;\n return `DECIMAL(${options.precision}, ${scale})`;\n }\n return \"DECIMAL\";\n case \"boolean\":\n return \"BOOLEAN\";\n case \"date\":\n return \"DATE\";\n case \"dateTime\":\n return \"TIMESTAMP\";\n case \"timestamp\":\n return \"TIMESTAMPTZ\"; // With timezone\n case \"time\":\n return \"TIME\";\n case \"year\":\n return \"SMALLINT\"; // PostgreSQL doesn't have YEAR type\n case \"json\":\n return \"JSONB\"; // Prefer JSONB for indexing and operators\n case \"binary\":\n return \"BYTEA\";\n case \"uuid\":\n return \"UUID\";\n case \"ulid\":\n return \"CHAR(26)\"; // ULIDs are 26 characters\n case \"ipAddress\":\n return \"INET\";\n case \"macAddress\":\n return \"MACADDR\";\n case \"point\":\n return \"POINT\";\n case \"polygon\":\n return \"POLYGON\";\n case \"lineString\":\n return \"PATH\";\n case \"geometry\":\n return \"GEOMETRY\"; // Requires PostGIS\n case \"vector\":\n return options?.dimensions ? `VECTOR(${options.dimensions})` : \"VECTOR\"; // Requires pgvector\n case \"enum\":\n return \"TEXT\"; // PostgreSQL enums need CREATE TYPE first\n case \"set\":\n return \"TEXT[]\"; // Use array for set-like behavior\n // PostgreSQL native array types\n case \"arrayInt\":\n return \"INTEGER[]\";\n case \"arrayBigInt\":\n return \"BIGINT[]\";\n case \"arrayFloat\":\n return \"REAL[]\";\n case \"arrayDecimal\":\n if (options?.precision !== undefined) {\n const scale = options.scale ?? 0;\n return `DECIMAL(${options.precision}, ${scale})[]`;\n }\n return \"DECIMAL[]\";\n case \"arrayBoolean\":\n return \"BOOLEAN[]\";\n case \"arrayText\":\n return \"TEXT[]\";\n case \"arrayDate\":\n return \"DATE[]\";\n case \"arrayTimestamp\":\n return \"TIMESTAMPTZ[]\";\n case \"arrayUuid\":\n return \"UUID[]\";\n case \"arrayJson\":\n return \"JSONB[]\";\n default:\n return type.toUpperCase();\n }\n }\n\n /**\n * Translate a database-agnostic aggregate expression to PostgreSQL SQL.\n *\n * The scalar aggregates map to their ANSI SQL function, and `countDistinct`\n * maps to `COUNT(DISTINCT …)`. `distinct`, `floor`, `first` and `last` are\n * MongoDB-only for v1 — none has a single-scalar `GROUP BY` equivalent on\n * PostgreSQL, so they throw instead of emitting a silently-different semantic\n * (the footgun this guards).\n *\n * @param expression - The abstract aggregate (`$agg.*`) to translate\n * @returns The SQL fragment (e.g. `SUM(\"amount\")`, `COUNT(*)`)\n */\n public aggregateToSql(expression: AggregateExpression): string {\n // When a composed column expression is present, the aggregate operates on\n // it instead of a bare column (e.g. SUM(price * quantity)). Only `sum`\n // supports the expression form in v1.\n if (expression.__expr) {\n if (expression.__agg !== \"sum\") {\n throw new Error(\n `$agg.${expression.__agg} does not support a composed column ` +\n `expression yet — only $agg.sum(...) does. Use a bare column name here.`,\n );\n }\n\n return `SUM(${this.columnExpressionToSql(expression.__expr)})`;\n }\n\n const column = expression.__field === null ? \"*\" : this.quoteIdentifier(expression.__field);\n\n switch (expression.__agg) {\n case \"count\":\n return \"COUNT(*)\";\n case \"countDistinct\":\n return `COUNT(DISTINCT ${column})`;\n case \"sum\":\n return `SUM(${column})`;\n case \"avg\":\n return `AVG(${column})`;\n case \"min\":\n return `MIN(${column})`;\n case \"max\":\n return `MAX(${column})`;\n default:\n throw new Error(\n `$agg.${expression.__agg} is MongoDB-only and not supported on a ` +\n `PostgreSQL groupBy. Use selectRaw / havingRaw with the equivalent ` +\n `SQL (window function / DISTINCT / FLOOR) if you need it here.`,\n );\n }\n }\n\n /**\n * Compile a typed {@link ColumnExpression} tree into a SQL fragment.\n *\n * Column references flow through {@link quoteIdentifier} (so user-supplied\n * names are quoted/escaped, never raw-interpolated); literals are emitted as\n * numeric/boolean SQL constants; arithmetic ops are parenthesised. The `raw`\n * node is the only path that emits a string verbatim — it is opt-in by name.\n *\n * @param expression - The expression tree to compile\n * @returns A SQL fragment (e.g. `(\"price\" * \"quantity\")`)\n */\n public columnExpressionToSql(expression: ColumnExpression): string {\n switch (expression.__expr) {\n case \"column\":\n return this.quoteIdentifier(expression.column);\n case \"literal\":\n return typeof expression.value === \"boolean\"\n ? this.booleanLiteral(expression.value)\n : String(toFiniteNumber(expression.value));\n case \"raw\":\n return expression.expression;\n case \"add\":\n case \"subtract\":\n case \"multiply\":\n case \"divide\": {\n const operator = { add: \"+\", subtract: \"-\", multiply: \"*\", divide: \"/\" }[expression.__expr];\n const parts = expression.operands.map((operand) => this.columnExpressionToSql(operand));\n return `(${parts.join(` ${operator} `)})`;\n }\n default:\n throw new Error(`Unsupported column expression node: ${JSON.stringify(expression)}`);\n }\n }\n\n /**\n * Build a `date_trunc` bucket expression for a portable `groupByDate`.\n *\n * @param column - The date/timestamp column to truncate\n * @param unit - The bucket granularity\n * @returns The SQL fragment (e.g. `date_trunc('month', \"created_at\")`)\n *\n * @example\n * ```typescript\n * dialect.dateTruncSql(\"created_at\", \"month\");\n * // date_trunc('month', \"created_at\")\n * ```\n */\n public dateTruncSql(column: string, unit: \"day\" | \"week\" | \"month\" | \"year\"): string {\n if (![\"day\", \"week\", \"month\", \"year\"].includes(unit)) {\n throw new Error(`PostgresDialect: invalid date_trunc unit \"${unit}\"`);\n }\n\n return `date_trunc('${unit}', ${this.quoteIdentifier(column)})`;\n }\n}\n\n/**\n * Escape a string for use inside a single-quoted SQL literal (`'` doubled).\n */\nexport function escapeSqlLiteral(value: string): string {\n return String(value).replace(/'/g, \"''\");\n}\n\n/**\n * Coerce a LIMIT/OFFSET value to a non-negative safe integer or throw.\n * Numeric strings (e.g. from query params) are accepted; anything else is not.\n */\nfunction toSafeInteger(value: unknown, label: string): number {\n const number = typeof value === \"string\" && /^\\d+$/.test(value) ? Number(value) : value;\n\n if (typeof number !== \"number\" || !Number.isSafeInteger(number) || number < 0) {\n throw new Error(`PostgresDialect: ${label} must be a non-negative safe integer`);\n }\n\n return number;\n}\n\n/**\n * Literals in column expressions are emitted verbatim, so only finite numbers pass.\n */\nfunction toFiniteNumber(value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\"PostgresDialect: column expression literal must be a finite number or boolean\");\n }\n\n return value;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-dialect.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-dialect.ts"],"sourcesContent":["/**\n * PostgreSQL Dialect Implementation\n *\n * Implements the SqlDialectContract for PostgreSQL-specific SQL syntax.\n * Handles parameter placeholders ($1, $2), identifier quoting, and\n * PostgreSQL-specific features like JSONB operators.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type { AggregateExpression, ColumnExpression } from \"../../expressions\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\nimport { camelToSnake } from \"./naming\";\n\nexport type PostgresNaming = \"preserve\" | \"snake_case\";\n\nexport type PostgresDialectOptions = { readonly naming?: PostgresNaming };\n\n/**\n * PostgreSQL-specific SQL dialect implementation.\n *\n * Provides PostgreSQL syntax for:\n * - Parameter placeholders ($1, $2, $3...)\n * - Identifier quoting with double quotes\n * - JSONB operators (->, ->>, @>)\n * - ILIKE for case-insensitive matching\n * - RETURNING clause support\n *\n * @example\n * ```typescript\n * const dialect = new PostgresDialect();\n *\n * dialect.placeholder(1); // \"$1\"\n * dialect.quoteIdentifier('user'); // '\"user\"'\n * dialect.jsonExtract('data', 'name'); // \"data\"->>'name'\n * ```\n */\nexport class PostgresDialect implements SqlDialectContract {\n private readonly naming: PostgresNaming;\n\n public constructor(options: PostgresDialectOptions = {}) {\n this.naming = options.naming ?? \"preserve\";\n }\n /**\n * Dialect name identifier.\n */\n public readonly name = \"postgres\" as const;\n\n /**\n * PostgreSQL supports the RETURNING clause for INSERT/UPDATE/DELETE.\n */\n public readonly supportsReturning = true;\n\n /**\n * PostgreSQL uses ON CONFLICT for upsert operations.\n */\n public readonly upsertKeyword = \"ON CONFLICT\" as const;\n\n /**\n * Generate a PostgreSQL parameter placeholder.\n *\n * PostgreSQL uses numbered placeholders: $1, $2, $3, etc.\n *\n * @param index - The 1-based parameter index\n * @returns The placeholder string (e.g., \"$1\")\n */\n public placeholder(index: number): string {\n return `$${index}`;\n }\n\n /**\n * Quote an identifier using PostgreSQL's double-quote syntax.\n *\n * Handles escaping of embedded double quotes by doubling them.\n * This is necessary for reserved words and special characters.\n *\n * @param identifier - The identifier (table/column name) to quote\n * @returns The quoted identifier (e.g., '\"user\"')\n */\n public quoteIdentifier(identifier: string): string {\n // Split on dots for qualified names (schema.table.column)\n const parts = identifier.split(\".\");\n return parts\n .map((part) => `\"${(this.naming === \"snake_case\" ? camelToSnake(part) : part).replace(/\"/g, '\"\"')}\"`)\n .join(\".\");\n }\n\n /** Quote a table identifier without applying the column naming policy. */\n public quoteTableIdentifier(identifier: string): string {\n return identifier.split(\".\").map((part) => `\"${part.replace(/\"/g, '\"\"')}\"`).join(\".\");\n }\n\n /**\n * Convert a boolean to PostgreSQL literal.\n *\n * @param value - The boolean value\n * @returns \"TRUE\" or \"FALSE\"\n */\n public booleanLiteral(value: boolean): string {\n return value ? \"TRUE\" : \"FALSE\";\n }\n\n /**\n * Build LIMIT/OFFSET clause for PostgreSQL.\n *\n * @param limit - Maximum rows to return\n * @param offset - Rows to skip\n * @returns The SQL clause (e.g., \"LIMIT 10 OFFSET 20\")\n */\n public limitOffset(limit?: number, offset?: number): string {\n const parts: string[] = [];\n\n if (limit !== undefined) {\n parts.push(`LIMIT ${toSafeInteger(limit, \"LIMIT\")}`);\n }\n\n if (offset !== undefined) {\n parts.push(`OFFSET ${toSafeInteger(offset, \"OFFSET\")}`);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build a JSON path extraction expression for PostgreSQL.\n *\n * Uses the ->> operator for text extraction from JSONB columns.\n * Supports nested paths using chained operators.\n *\n * @param column - The JSONB column name\n * @param path - The path to extract (dot notation: \"user.name\")\n * @returns The SQL expression (e.g., \"data\"->>'user'->>'name')\n */\n public jsonExtract(column: string, path: string): string {\n const quotedColumn = this.quoteIdentifier(column);\n const pathParts = path.split(\".\");\n\n if (pathParts.length === 1) {\n return `${quotedColumn}->>'${escapeSqlLiteral(pathParts[0] as string)}'`;\n }\n\n // For nested paths: data->'user'->>'name' (last one gets text extraction)\n const jsonPath = pathParts\n .slice(0, -1)\n .map((p) => `'${escapeSqlLiteral(p)}'`)\n .join(\"->\");\n const lastKey = pathParts[pathParts.length - 1];\n\n return `${quotedColumn}->${jsonPath}->>'${escapeSqlLiteral(lastKey as string)}'`;\n }\n\n /**\n * Build a JSON contains expression for PostgreSQL.\n *\n * Uses the @> containment operator for JSONB columns.\n *\n * @param column - The JSONB column name\n * @param value - The value to check for\n * @param path - Optional path within the JSON\n * @returns The SQL expression\n */\n public jsonContains(column: string, value: unknown, path?: string): string {\n const quotedColumn = this.quoteIdentifier(column);\n\n if (path) {\n // Check if a specific path contains the value\n const jsonValue = JSON.stringify({ [path]: value });\n return `${quotedColumn} @> '${escapeSqlLiteral(jsonValue)}'::jsonb`;\n }\n\n // Check if the column contains the value (for arrays or objects)\n const jsonValue = JSON.stringify(value);\n return `${quotedColumn} @> '${escapeSqlLiteral(jsonValue)}'::jsonb`;\n }\n\n /**\n * Build a LIKE pattern expression for PostgreSQL.\n *\n * Uses ILIKE for case-insensitive matching, LIKE for case-sensitive.\n *\n * @param pattern - The pattern to match\n * @param caseInsensitive - Whether to use case-insensitive matching\n * @returns Object with operator and pattern\n */\n public likePattern(\n pattern: string,\n caseInsensitive = true,\n ): { operator: string; pattern: string } {\n // Escape special characters in the pattern (%, _, \\)\n const escapedPattern = pattern.replace(/\\\\/g, \"\\\\\\\\\").replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\");\n\n return {\n operator: caseInsensitive ? \"ILIKE\" : \"LIKE\",\n pattern: escapedPattern,\n };\n }\n\n /**\n * Build an array contains expression for PostgreSQL.\n *\n * Uses ANY() for checking if a value is in an array column.\n *\n * @param column - The array column name\n * @param paramIndex - The parameter index\n * @returns The SQL expression\n */\n public arrayContains(column: string, paramIndex: number): string {\n return `${this.placeholder(paramIndex)} = ANY(${this.quoteIdentifier(column)})`;\n }\n\n /**\n * Get the PostgreSQL SQL type for an abstract type.\n *\n * @param type - The abstract type name\n * @param options - Type-specific options\n * @returns The PostgreSQL type string\n */\n public getSqlType(\n type: string,\n options?: { length?: number; precision?: number; scale?: number; dimensions?: number },\n ): string {\n switch (type) {\n case \"string\":\n return options?.length ? `VARCHAR(${options.length})` : \"TEXT\";\n case \"char\":\n return `CHAR(${options?.length ?? 1})`;\n case \"text\":\n return \"TEXT\";\n case \"mediumText\":\n case \"longText\":\n return \"TEXT\"; // PostgreSQL doesn't distinguish text sizes\n case \"integer\":\n return \"INTEGER\";\n case \"smallInteger\":\n return \"SMALLINT\";\n case \"tinyInteger\":\n return \"SMALLINT\"; // PostgreSQL doesn't have TINYINT\n case \"bigInteger\":\n return \"BIGINT\";\n case \"float\":\n return \"REAL\";\n case \"double\":\n return \"DOUBLE PRECISION\";\n case \"decimal\":\n if (options?.precision !== undefined) {\n const scale = options.scale ?? 0;\n return `DECIMAL(${options.precision}, ${scale})`;\n }\n return \"DECIMAL\";\n case \"boolean\":\n return \"BOOLEAN\";\n case \"date\":\n return \"DATE\";\n case \"dateTime\":\n return \"TIMESTAMP\";\n case \"timestamp\":\n return \"TIMESTAMPTZ\"; // With timezone\n case \"time\":\n return \"TIME\";\n case \"year\":\n return \"SMALLINT\"; // PostgreSQL doesn't have YEAR type\n case \"json\":\n return \"JSONB\"; // Prefer JSONB for indexing and operators\n case \"binary\":\n return \"BYTEA\";\n case \"uuid\":\n return \"UUID\";\n case \"ulid\":\n return \"CHAR(26)\"; // ULIDs are 26 characters\n case \"ipAddress\":\n return \"INET\";\n case \"macAddress\":\n return \"MACADDR\";\n case \"point\":\n return \"POINT\";\n case \"polygon\":\n return \"POLYGON\";\n case \"lineString\":\n return \"PATH\";\n case \"geometry\":\n return \"GEOMETRY\"; // Requires PostGIS\n case \"vector\":\n return options?.dimensions ? `VECTOR(${options.dimensions})` : \"VECTOR\"; // Requires pgvector\n case \"enum\":\n return \"TEXT\"; // PostgreSQL enums need CREATE TYPE first\n case \"set\":\n return \"TEXT[]\"; // Use array for set-like behavior\n // PostgreSQL native array types\n case \"arrayInt\":\n return \"INTEGER[]\";\n case \"arrayBigInt\":\n return \"BIGINT[]\";\n case \"arrayFloat\":\n return \"REAL[]\";\n case \"arrayDecimal\":\n if (options?.precision !== undefined) {\n const scale = options.scale ?? 0;\n return `DECIMAL(${options.precision}, ${scale})[]`;\n }\n return \"DECIMAL[]\";\n case \"arrayBoolean\":\n return \"BOOLEAN[]\";\n case \"arrayText\":\n return \"TEXT[]\";\n case \"arrayDate\":\n return \"DATE[]\";\n case \"arrayTimestamp\":\n return \"TIMESTAMPTZ[]\";\n case \"arrayUuid\":\n return \"UUID[]\";\n case \"arrayJson\":\n return \"JSONB[]\";\n default:\n return type.toUpperCase();\n }\n }\n\n /**\n * Translate a database-agnostic aggregate expression to PostgreSQL SQL.\n *\n * The scalar aggregates map to their ANSI SQL function, and `countDistinct`\n * maps to `COUNT(DISTINCT …)`. `distinct`, `floor`, `first` and `last` are\n * MongoDB-only for v1 — none has a single-scalar `GROUP BY` equivalent on\n * PostgreSQL, so they throw instead of emitting a silently-different semantic\n * (the footgun this guards).\n *\n * @param expression - The abstract aggregate (`$agg.*`) to translate\n * @returns The SQL fragment (e.g. `SUM(\"amount\")`, `COUNT(*)`)\n */\n public aggregateToSql(expression: AggregateExpression): string {\n // When a composed column expression is present, the aggregate operates on\n // it instead of a bare column (e.g. SUM(price * quantity)). Only `sum`\n // supports the expression form in v1.\n if (expression.__expr) {\n if (expression.__agg !== \"sum\") {\n throw new Error(\n `$agg.${expression.__agg} does not support a composed column ` +\n `expression yet — only $agg.sum(...) does. Use a bare column name here.`,\n );\n }\n\n return `SUM(${this.columnExpressionToSql(expression.__expr)})`;\n }\n\n const column = expression.__field === null ? \"*\" : this.quoteIdentifier(expression.__field);\n\n switch (expression.__agg) {\n case \"count\":\n return \"COUNT(*)\";\n case \"countDistinct\":\n return `COUNT(DISTINCT ${column})`;\n case \"sum\":\n return `SUM(${column})`;\n case \"avg\":\n return `AVG(${column})`;\n case \"min\":\n return `MIN(${column})`;\n case \"max\":\n return `MAX(${column})`;\n default:\n throw new Error(\n `$agg.${expression.__agg} is MongoDB-only and not supported on a ` +\n `PostgreSQL groupBy. Use selectRaw / havingRaw with the equivalent ` +\n `SQL (window function / DISTINCT / FLOOR) if you need it here.`,\n );\n }\n }\n\n /**\n * Compile a typed {@link ColumnExpression} tree into a SQL fragment.\n *\n * Column references flow through {@link quoteIdentifier} (so user-supplied\n * names are quoted/escaped, never raw-interpolated); literals are emitted as\n * numeric/boolean SQL constants; arithmetic ops are parenthesised. The `raw`\n * node is the only path that emits a string verbatim — it is opt-in by name.\n *\n * @param expression - The expression tree to compile\n * @returns A SQL fragment (e.g. `(\"price\" * \"quantity\")`)\n */\n public columnExpressionToSql(expression: ColumnExpression): string {\n switch (expression.__expr) {\n case \"column\":\n return this.quoteIdentifier(expression.column);\n case \"literal\":\n return typeof expression.value === \"boolean\"\n ? this.booleanLiteral(expression.value)\n : String(toFiniteNumber(expression.value));\n case \"raw\":\n return expression.expression;\n case \"add\":\n case \"subtract\":\n case \"multiply\":\n case \"divide\": {\n const operator = { add: \"+\", subtract: \"-\", multiply: \"*\", divide: \"/\" }[expression.__expr];\n const parts = expression.operands.map((operand) => this.columnExpressionToSql(operand));\n return `(${parts.join(` ${operator} `)})`;\n }\n default:\n throw new Error(`Unsupported column expression node: ${JSON.stringify(expression)}`);\n }\n }\n\n /**\n * Build a `date_trunc` bucket expression for a portable `groupByDate`.\n *\n * @param column - The date/timestamp column to truncate\n * @param unit - The bucket granularity\n * @returns The SQL fragment (e.g. `date_trunc('month', \"created_at\")`)\n *\n * @example\n * ```typescript\n * dialect.dateTruncSql(\"created_at\", \"month\");\n * // date_trunc('month', \"created_at\")\n * ```\n */\n public dateTruncSql(column: string, unit: \"day\" | \"week\" | \"month\" | \"year\"): string {\n if (![\"day\", \"week\", \"month\", \"year\"].includes(unit)) {\n throw new Error(`PostgresDialect: invalid date_trunc unit \"${unit}\"`);\n }\n\n return `date_trunc('${unit}', ${this.quoteIdentifier(column)})`;\n }\n}\n\n/**\n * Escape a string for use inside a single-quoted SQL literal (`'` doubled).\n */\nexport function escapeSqlLiteral(value: string): string {\n return String(value).replace(/'/g, \"''\");\n}\n\n/**\n * Coerce a LIMIT/OFFSET value to a non-negative safe integer or throw.\n * Numeric strings (e.g. from query params) are accepted; anything else is not.\n */\nfunction toSafeInteger(value: unknown, label: string): number {\n const number = typeof value === \"string\" && /^\\d+$/.test(value) ? Number(value) : value;\n\n if (typeof number !== \"number\" || !Number.isSafeInteger(number) || number < 0) {\n throw new Error(`PostgresDialect: ${label} must be a non-negative safe integer`);\n }\n\n return number;\n}\n\n/**\n * Literals in column expressions are emitted verbatim, so only finite numbers pass.\n */\nfunction toFiniteNumber(value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\"PostgresDialect: column expression literal must be a finite number or boolean\");\n }\n\n return value;\n}\n"],"mappings":"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"}
@@ -17,6 +17,12 @@ import { PostgresPoolConfig, PostgresQueryResult, PostgresTransactionOptions } f
17
17
  type PgPool = import("pg").Pool;
18
18
  type PgPoolClient = import("pg").PoolClient;
19
19
  type PgPoolConfig = import("pg").PoolConfig;
20
+ /**
21
+ * Convert a PostgreSQL int8 result to a number only when it can be represented
22
+ * exactly. PostgreSQL sends int8 values as strings by default to avoid silently
23
+ * losing precision above JavaScript's safe-integer limit.
24
+ */
25
+ declare function parsePostgresInt8(value: string | null): number | string | null;
20
26
  /**
21
27
  * Build pg's `PoolConfig` from cascade's `PostgresPoolConfig`, coercing the
22
28
  * string-typed connection fields.
@@ -601,5 +607,5 @@ declare class PostgresDriver implements DriverContract {
601
607
  dropAllTables(): Promise<void>;
602
608
  }
603
609
  //#endregion
604
- export { PostgresDriver, buildPostgresPoolConfig };
610
+ export { PostgresDriver, buildPostgresPoolConfig, parsePostgresInt8 };
605
611
  //# sourceMappingURL=postgres-driver.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { flushAfterCommit } from "../../transactions/after-commit.mjs";
3
3
  import { TransactionRollbackError } from "../../errors/transaction-rollback.error.mjs";
4
4
  import { UnsupportedUpdateOperationError } from "../../errors/unsupported-update-operation.error.mjs";
5
5
  import { PostgresBlueprint } from "./postgres-blueprint.mjs";
6
+ import { snakeKeysToCamel } from "./naming.mjs";
6
7
  import { PostgresDialect } from "./postgres-dialect.mjs";
7
8
  import { SqlDatabaseDirtyTracker } from "../../sql-database-dirty-tracker.mjs";
8
9
  import { convertPostgresValue } from "./postgres-type-converters.mjs";
@@ -26,6 +27,34 @@ import { colors } from "@mongez/copper";
26
27
  *
27
28
  * @module cascade/drivers/postgres
28
29
  */
30
+ const POSTGRES_INT8_OID = 20;
31
+ const POSTGRES_INT8_ARRAY_OID = 1016;
32
+ /**
33
+ * Convert a PostgreSQL int8 result to a number only when it can be represented
34
+ * exactly. PostgreSQL sends int8 values as strings by default to avoid silently
35
+ * losing precision above JavaScript's safe-integer limit.
36
+ */
37
+ function parsePostgresInt8(value) {
38
+ if (value === null) return null;
39
+ const numberValue = Number(value);
40
+ return Number.isSafeInteger(numberValue) ? numberValue : value;
41
+ }
42
+ /**
43
+ * Build pool-local type overrides without changing pg's process-wide registry.
44
+ */
45
+ function createPostgresTypes(types) {
46
+ return { getTypeParser(oid, format) {
47
+ if (oid === POSTGRES_INT8_OID) return parsePostgresInt8;
48
+ if (oid === POSTGRES_INT8_ARRAY_OID) {
49
+ const parseArray = types.getTypeParser(oid, format);
50
+ return (value) => {
51
+ const array = parseArray(value);
52
+ return Array.isArray(array) ? array.map(parsePostgresInt8) : array;
53
+ };
54
+ }
55
+ return types.getTypeParser(oid, format);
56
+ } };
57
+ }
29
58
  /**
30
59
  * Build pg's `PoolConfig` from cascade's `PostgresPoolConfig`, coercing the
31
60
  * string-typed connection fields.
@@ -126,7 +155,7 @@ var PostgresDriver = class {
126
155
  /**
127
156
  * SQL dialect for PostgreSQL-specific syntax.
128
157
  */
129
- dialect = new PostgresDialect();
158
+ dialect;
130
159
  /**
131
160
  * PostgreSQL driver model defaults.
132
161
  *
@@ -197,6 +226,7 @@ var PostgresDriver = class {
197
226
  */
198
227
  constructor(config) {
199
228
  this.config = config;
229
+ this.dialect = new PostgresDialect({ naming: config.naming });
200
230
  this._nativeArrayColumns = new Set(config.nativeArrayColumns ?? []);
201
231
  }
202
232
  /**
@@ -238,6 +268,7 @@ var PostgresDriver = class {
238
268
  const pg = await loadPg();
239
269
  try {
240
270
  const poolConfig = buildPostgresPoolConfig(this.config);
271
+ poolConfig.types = createPostgresTypes(pg.types);
241
272
  log.info("database.postgres", "connection", `Connecting to database ${colors.bold(colors.yellowBright(poolConfig.database ?? ""))}`);
242
273
  this._pool = new pg.Pool(poolConfig);
243
274
  this._pool.on("error", (error) => {
@@ -419,12 +450,11 @@ var PostgresDriver = class {
419
450
  */
420
451
  deserialize(data) {
421
452
  const columnTypes = this.rowColumnTypes.get(data);
422
- if (!columnTypes) return data;
423
- for (const [key, value] of Object.entries(data)) {
453
+ if (columnTypes) for (const [key, value] of Object.entries(data)) {
424
454
  if (typeof value !== "string") continue;
425
455
  data[key] = convertPostgresValue(value, columnTypes.get(key));
426
456
  }
427
- return data;
457
+ return this.config.naming === "snake_case" ? snakeKeysToCamel(data) : data;
428
458
  }
429
459
  /**
430
460
  * Insert a single row into a table.
@@ -447,7 +477,7 @@ var PostgresDriver = class {
447
477
  if (columns.length === 0) throw new Error("Cannot insert empty document");
448
478
  const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
449
479
  const placeholders = columns.map((_, i) => this.dialect.placeholder(i + 1)).join(", ");
450
- const sql = `INSERT INTO ${this.dialect.quoteIdentifier(table)} (${quotedColumns}) VALUES (${placeholders}) RETURNING *`;
480
+ const sql = `INSERT INTO ${this.dialect.quoteTableIdentifier(table)} (${quotedColumns}) VALUES (${placeholders}) RETURNING *`;
451
481
  return { document: (await this.query(sql, values)).rows[0] };
452
482
  }
453
483
  /**
@@ -469,7 +499,7 @@ var PostgresDriver = class {
469
499
  }
470
500
  const columns = Array.from(allColumns);
471
501
  const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
472
- const quotedTable = this.dialect.quoteIdentifier(table);
502
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
473
503
  const valueSets = [];
474
504
  const params = [];
475
505
  let paramIndex = 1;
@@ -541,7 +571,7 @@ var PostgresDriver = class {
541
571
  const { sql, params } = this.buildUpdateQuery(table, filter, operations, 1);
542
572
  return (await this.query(`${sql} RETURNING *`, params)).rows[0] ?? null;
543
573
  }
544
- const quotedTable = this.dialect.quoteIdentifier(table);
574
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
545
575
  const keyList = (qualifier) => primaryKey.map((column) => (qualifier ? `${qualifier}.` : "") + this.dialect.quoteIdentifier(column)).join(", ");
546
576
  const set = this.buildSetClauses(table, operations, 1, returnBefore ? quotedTable : void 0);
547
577
  if (set.clauses.length === 0) throw new Error("No update operations specified");
@@ -587,7 +617,7 @@ var PostgresDriver = class {
587
617
  for (const key of Object.keys(operations.$unset ?? {})) delete insertRow[key];
588
618
  const columns = Object.keys(insertRow).filter((key) => insertRow[key] !== void 0);
589
619
  const params = columns.map((key) => this.serializeValue(key, insertRow[key], table));
590
- const quotedTable = this.dialect.quoteIdentifier(table);
620
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
591
621
  const quotedColumns = columns.map((column) => this.dialect.quoteIdentifier(column)).join(", ");
592
622
  const placeholders = columns.map((_, index) => this.dialect.placeholder(index + 1)).join(", ");
593
623
  const set = this.buildSetClauses(table, operations, columns.length + 1, quotedTable);
@@ -655,7 +685,7 @@ var PostgresDriver = class {
655
685
  const serialized = this.serialize(document, table);
656
686
  const columns = Object.keys(serialized);
657
687
  const values = Object.values(serialized);
658
- const quotedTable = this.dialect.quoteIdentifier(table);
688
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
659
689
  const setClauses = columns.map((col, i) => `${this.dialect.quoteIdentifier(col)} = ${this.dialect.placeholder(i + 1)}`).join(", ");
660
690
  const { whereClause, whereParams } = this.buildWhereClause(filter, columns.length + 1);
661
691
  const sql = `UPDATE ${quotedTable} SET ${setClauses} ${whereClause} RETURNING *`;
@@ -678,7 +708,7 @@ var PostgresDriver = class {
678
708
  const columns = Object.keys(serialized);
679
709
  const values = Object.values(serialized);
680
710
  if (columns.length === 0) throw new Error("Cannot upsert empty document");
681
- const quotedTable = this.dialect.quoteIdentifier(table);
711
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
682
712
  const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(", ");
683
713
  const placeholders = columns.map((_, i) => this.dialect.placeholder(i + 1)).join(", ");
684
714
  const conflictColumns = options?.conflictColumns ?? Object.keys(filter);
@@ -701,7 +731,7 @@ var PostgresDriver = class {
701
731
  * @returns The deleted row or null
702
732
  */
703
733
  async findOneAndDelete(table, filter, _options) {
704
- const quotedTable = this.dialect.quoteIdentifier(table);
734
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
705
735
  const { whereClause, whereParams } = this.buildWhereClause(filter, 1);
706
736
  const sql = `${this.singleRowTarget("DELETE FROM " + quotedTable, quotedTable, filter, whereClause)} RETURNING *`;
707
737
  const result = await this.query(sql, whereParams);
@@ -716,7 +746,7 @@ var PostgresDriver = class {
716
746
  * @returns Number of deleted rows (0 or 1)
717
747
  */
718
748
  async delete(table, filter, _options) {
719
- const quotedTable = this.dialect.quoteIdentifier(table);
749
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
720
750
  const { whereClause, whereParams } = this.buildWhereClause(filter ?? {}, 1);
721
751
  const sql = this.singleRowTarget("DELETE FROM " + quotedTable, quotedTable, filter ?? {}, whereClause);
722
752
  return (await this.query(sql, whereParams)).rowCount ?? 0;
@@ -730,7 +760,7 @@ var PostgresDriver = class {
730
760
  * @returns Number of deleted rows
731
761
  */
732
762
  async deleteMany(table, filter, _options) {
733
- const quotedTable = this.dialect.quoteIdentifier(table);
763
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
734
764
  const { whereClause, whereParams } = this.buildWhereClause(filter ?? {}, 1);
735
765
  const sql = `DELETE FROM ${quotedTable} ${whereClause}`;
736
766
  return (await this.query(sql, whereParams)).rowCount ?? 0;
@@ -746,7 +776,7 @@ var PostgresDriver = class {
746
776
  * @returns Number of deleted rows (always 0 for TRUNCATE)
747
777
  */
748
778
  async truncateTable(table, options) {
749
- const quotedTable = this.dialect.quoteIdentifier(table);
779
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
750
780
  const cascadeClause = options?.cascade ? " CASCADE" : "";
751
781
  await this.query(`TRUNCATE TABLE ${quotedTable} RESTART IDENTITY${cascadeClause}`);
752
782
  return 0;
@@ -1203,7 +1233,7 @@ var PostgresDriver = class {
1203
1233
  const set = this.buildSetClauses(table, assertPostgresUpdate(update), 1);
1204
1234
  if (set.clauses.length === 0) throw new Error("No update operations specified");
1205
1235
  const params = [...set.params];
1206
- const quotedTable = this.dialect.quoteIdentifier(table);
1236
+ const quotedTable = this.dialect.quoteTableIdentifier(table);
1207
1237
  const { whereClause, whereParams } = this.buildWhereClause(filter, set.nextIndex);
1208
1238
  params.push(...whereParams);
1209
1239
  const head = `UPDATE ${quotedTable} SET ${set.clauses.join(", ")}`;
@@ -1336,5 +1366,5 @@ var PostgresDriver = class {
1336
1366
  };
1337
1367
 
1338
1368
  //#endregion
1339
- export { PostgresDriver, buildPostgresPoolConfig };
1369
+ export { PostgresDriver, buildPostgresPoolConfig, parsePostgresInt8 };
1340
1370
  //# sourceMappingURL=postgres-driver.mjs.map