@warlock.js/cascade 4.4.0 → 4.6.0

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 (77) hide show
  1. package/CHANGELOG.md +29 -3
  2. package/cjs/index.cjs +933 -90
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/contracts/database-driver.contract.d.mts +27 -4
  5. package/esm/contracts/database-driver.contract.d.mts.map +1 -1
  6. package/esm/contracts/database-id-generator.contract.d.mts +38 -0
  7. package/esm/contracts/database-id-generator.contract.d.mts.map +1 -1
  8. package/esm/contracts/index.d.mts +1 -1
  9. package/esm/contracts/query-builder.contract.d.mts +27 -0
  10. package/esm/contracts/query-builder.contract.d.mts.map +1 -1
  11. package/esm/data-source/data-source.d.mts +21 -1
  12. package/esm/data-source/data-source.d.mts.map +1 -1
  13. package/esm/data-source/data-source.mjs +22 -0
  14. package/esm/data-source/data-source.mjs.map +1 -1
  15. package/esm/drivers/mongodb/mongodb-driver.d.mts +2 -2
  16. package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
  17. package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
  18. package/esm/drivers/mongodb/mongodb-id-generator.d.mts +98 -48
  19. package/esm/drivers/mongodb/mongodb-id-generator.d.mts.map +1 -1
  20. package/esm/drivers/mongodb/mongodb-id-generator.mjs +153 -59
  21. package/esm/drivers/mongodb/mongodb-id-generator.mjs.map +1 -1
  22. package/esm/drivers/mongodb/mongodb-query-builder.d.mts +25 -0
  23. package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
  24. package/esm/drivers/mongodb/mongodb-query-builder.mjs +32 -0
  25. package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
  26. package/esm/drivers/mongodb/mongodb-query-parser.d.mts +36 -0
  27. package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
  28. package/esm/drivers/mongodb/mongodb-query-parser.mjs +80 -1
  29. package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
  30. package/esm/drivers/postgres/postgres-dialect.d.mts +32 -4
  31. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  32. package/esm/drivers/postgres/postgres-dialect.mjs +57 -4
  33. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  34. package/esm/drivers/postgres/postgres-driver.d.mts +42 -0
  35. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  36. package/esm/drivers/postgres/postgres-driver.mjs +55 -6
  37. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  38. package/esm/drivers/postgres/postgres-query-builder.d.mts +32 -0
  39. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  40. package/esm/drivers/postgres/postgres-query-builder.mjs +47 -1
  41. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  42. package/esm/drivers/postgres/postgres-query-parser.d.mts +13 -2
  43. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  44. package/esm/drivers/postgres/postgres-query-parser.mjs +21 -4
  45. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  46. package/esm/drivers/postgres/types.d.mts +15 -0
  47. package/esm/drivers/postgres/types.d.mts.map +1 -1
  48. package/esm/drivers/sql/sql-dialect.contract.d.mts +20 -0
  49. package/esm/drivers/sql/sql-dialect.contract.d.mts.map +1 -1
  50. package/esm/expressions/aggregate-expressions.d.mts +71 -34
  51. package/esm/expressions/aggregate-expressions.d.mts.map +1 -1
  52. package/esm/expressions/aggregate-expressions.mjs +80 -7
  53. package/esm/expressions/aggregate-expressions.mjs.map +1 -1
  54. package/esm/expressions/column-expressions.d.mts +193 -0
  55. package/esm/expressions/column-expressions.d.mts.map +1 -0
  56. package/esm/expressions/column-expressions.mjs +152 -0
  57. package/esm/expressions/column-expressions.mjs.map +1 -0
  58. package/esm/index.d.mts +3 -2
  59. package/esm/index.mjs +2 -1
  60. package/esm/model/methods/write-methods.d.mts +29 -0
  61. package/esm/model/methods/write-methods.d.mts.map +1 -0
  62. package/esm/model/methods/write-methods.mjs +164 -2
  63. package/esm/model/methods/write-methods.mjs.map +1 -1
  64. package/esm/model/model.d.mts +64 -3
  65. package/esm/model/model.d.mts.map +1 -1
  66. package/esm/model/model.mjs +65 -3
  67. package/esm/model/model.mjs.map +1 -1
  68. package/esm/writer/database-writer.d.mts.map +1 -1
  69. package/esm/writer/database-writer.mjs +4 -3
  70. package/esm/writer/database-writer.mjs.map +1 -1
  71. package/llms-full.txt +106 -7
  72. package/llms.txt +3 -3
  73. package/package.json +4 -4
  74. package/skills/README.md +3 -3
  75. package/skills/aggregate-data/SKILL.md +43 -3
  76. package/skills/manage-transactions/SKILL.md +40 -1
  77. package/skills/perform-atomic-ops/SKILL.md +23 -3
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-dialect.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-dialect.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;cAgCa,eAAA,YAA2B,kBAAkB;EA0InD;;;EAAA,SAtIW,IAAA;EAyJqB;;;EAAA,SApJrB,iBAAA;EAiKe;;;EAAA,SA5Jf,aAAA;EAyQT;;;;AAA8C;;;;EA/P9C,WAAA,CAAY,KAAA;;;;;;;;;;EAaZ,eAAA,CAAgB,UAAA;;;;;;;EAYhB,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;;;;;;;;;;;;EA6G5D,cAAA,CAAe,UAAA,EAAY,mBAAA;AAAA"}
1
+ {"version":3,"file":"postgres-dialect.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/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;;;EAzUpE,eAAA,CAAgB,UAAA;;;;;;;EAYhB,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"}
@@ -201,18 +201,24 @@ var PostgresDialect = class {
201
201
  /**
202
202
  * Translate a database-agnostic aggregate expression to PostgreSQL SQL.
203
203
  *
204
- * The five scalar aggregates map to their ANSI SQL function. `distinct`,
205
- * `floor`, `first` and `last` are MongoDB-only for v1 — none has a
206
- * single-scalar `GROUP BY` equivalent on PostgreSQL, so they throw instead
207
- * of emitting a silently-different semantic (the footgun this guards).
204
+ * The scalar aggregates map to their ANSI SQL function, and `countDistinct`
205
+ * maps to `COUNT(DISTINCT …)`. `distinct`, `floor`, `first` and `last` are
206
+ * MongoDB-only for v1 — none has a single-scalar `GROUP BY` equivalent on
207
+ * PostgreSQL, so they throw instead of emitting a silently-different semantic
208
+ * (the footgun this guards).
208
209
  *
209
210
  * @param expression - The abstract aggregate (`$agg.*`) to translate
210
211
  * @returns The SQL fragment (e.g. `SUM("amount")`, `COUNT(*)`)
211
212
  */
212
213
  aggregateToSql(expression) {
214
+ if (expression.__expr) {
215
+ if (expression.__agg !== "sum") throw new Error(`$agg.${expression.__agg} does not support a composed column expression yet — only $agg.sum(...) does. Use a bare column name here.`);
216
+ return `SUM(${this.columnExpressionToSql(expression.__expr)})`;
217
+ }
213
218
  const column = expression.__field === null ? "*" : this.quoteIdentifier(expression.__field);
214
219
  switch (expression.__agg) {
215
220
  case "count": return "COUNT(*)";
221
+ case "countDistinct": return `COUNT(DISTINCT ${column})`;
216
222
  case "sum": return `SUM(${column})`;
217
223
  case "avg": return `AVG(${column})`;
218
224
  case "min": return `MIN(${column})`;
@@ -220,6 +226,53 @@ var PostgresDialect = class {
220
226
  default: throw new Error(`$agg.${expression.__agg} is MongoDB-only and not supported on a PostgreSQL groupBy. Use selectRaw / havingRaw with the equivalent SQL (window function / DISTINCT / FLOOR) if you need it here.`);
221
227
  }
222
228
  }
229
+ /**
230
+ * Compile a typed {@link ColumnExpression} tree into a SQL fragment.
231
+ *
232
+ * Column references flow through {@link quoteIdentifier} (so user-supplied
233
+ * names are quoted/escaped, never raw-interpolated); literals are emitted as
234
+ * numeric/boolean SQL constants; arithmetic ops are parenthesised. The `raw`
235
+ * node is the only path that emits a string verbatim — it is opt-in by name.
236
+ *
237
+ * @param expression - The expression tree to compile
238
+ * @returns A SQL fragment (e.g. `("price" * "quantity")`)
239
+ */
240
+ columnExpressionToSql(expression) {
241
+ switch (expression.__expr) {
242
+ case "column": return this.quoteIdentifier(expression.column);
243
+ case "literal": return typeof expression.value === "boolean" ? this.booleanLiteral(expression.value) : String(expression.value);
244
+ case "raw": return expression.expression;
245
+ case "add":
246
+ case "subtract":
247
+ case "multiply":
248
+ case "divide": {
249
+ const operator = {
250
+ add: "+",
251
+ subtract: "-",
252
+ multiply: "*",
253
+ divide: "/"
254
+ }[expression.__expr];
255
+ return `(${expression.operands.map((operand) => this.columnExpressionToSql(operand)).join(` ${operator} `)})`;
256
+ }
257
+ default: throw new Error(`Unsupported column expression node: ${JSON.stringify(expression)}`);
258
+ }
259
+ }
260
+ /**
261
+ * Build a `date_trunc` bucket expression for a portable `groupByDate`.
262
+ *
263
+ * @param column - The date/timestamp column to truncate
264
+ * @param unit - The bucket granularity
265
+ * @returns The SQL fragment (e.g. `date_trunc('month', "created_at")`)
266
+ *
267
+ * @example
268
+ * ```typescript
269
+ * dialect.dateTruncSql("created_at", "month");
270
+ * // date_trunc('month', "created_at")
271
+ * ```
272
+ */
273
+ dateTruncSql(column, unit) {
274
+ return `date_trunc('${unit}', ${this.quoteIdentifier(column)})`;
275
+ }
223
276
  };
224
277
 
225
278
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-dialect.mjs","names":[],"sources":["../../../../../../../../@warlock.js/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 } 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 ${limit}`);\n }\n\n if (offset !== undefined) {\n parts.push(`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}->>'${pathParts[0]}'`;\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) => `'${p}'`)\n .join(\"->\");\n const lastKey = pathParts[pathParts.length - 1];\n\n return `${quotedColumn}->${jsonPath}->>'${lastKey}'`;\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} @> '${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} @> '${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 five scalar aggregates map to their ANSI SQL function. `distinct`,\n * `floor`, `first` and `last` are MongoDB-only for v1 — none has a\n * single-scalar `GROUP BY` equivalent on PostgreSQL, so they throw instead\n * of emitting a silently-different semantic (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 const column = expression.__field === null ? \"*\" : this.quoteIdentifier(expression.__field);\n\n switch (expression.__agg) {\n case \"count\":\n return \"COUNT(*)\";\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"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-dialect.mjs","names":[],"sources":["../../../../../../../../@warlock.js/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 ${limit}`);\n }\n\n if (offset !== undefined) {\n parts.push(`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}->>'${pathParts[0]}'`;\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) => `'${p}'`)\n .join(\"->\");\n const lastKey = pathParts[pathParts.length - 1];\n\n return `${quotedColumn}->${jsonPath}->>'${lastKey}'`;\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} @> '${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} @> '${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(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 return `date_trunc('${unit}', ${this.quoteIdentifier(column)})`;\n }\n}\n"],"mappings":"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"}
@@ -89,6 +89,13 @@ declare class PostgresDriver implements DriverContract {
89
89
  * Sync adapter instance (lazy-loaded).
90
90
  */
91
91
  private _syncAdapter;
92
+ /**
93
+ * Lookup set of column names that hold native PostgreSQL arrays
94
+ * (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text encoded.
95
+ *
96
+ * @see PostgresPoolConfig.nativeArrayColumns
97
+ */
98
+ private readonly _nativeArrayColumns;
92
99
  /**
93
100
  * Create a new PostgreSQL driver instance.
94
101
  *
@@ -144,6 +151,41 @@ declare class PostgresDriver implements DriverContract {
144
151
  * @returns Serialized data ready for PostgreSQL
145
152
  */
146
153
  serialize(data: Record<string, unknown>): Record<string, unknown>;
154
+ /**
155
+ * Serialize a single column value into a node-pg bindable parameter.
156
+ *
157
+ * Shared by {@link serialize} (INSERT path) and {@link buildUpdateQuery}
158
+ * `$set` (UPDATE path) so both encode `json` / `jsonb` columns identically.
159
+ *
160
+ * Encoding rules (in order):
161
+ * - `Date` → ISO string.
162
+ * - `bigint` → decimal string (node-pg has no native bigint binding).
163
+ * - all-number array → pgvector literal `'[n1,n2,...]'`. node-pg would
164
+ * otherwise emit a `{n1,n2,...}` array literal, which the `vector` type
165
+ * rejects. This branch is preserved exactly.
166
+ * - any other array (object-array, string-array, mixed, empty `[]`) →
167
+ * `JSON.stringify`. node-pg renders a raw JS array as a PostgreSQL array
168
+ * literal `{...}` (and `[]` as `{}`), which a `json` / `jsonb` column
169
+ * rejects — so we bind the value as JSON text instead, the form those
170
+ * columns accept. Columns listed in `nativeArrayColumns` are exempt:
171
+ * their raw array is passed through so node-pg emits the `{...}` literal
172
+ * a genuine `JSONB[]` / `TEXT[]` column needs.
173
+ * - plain object → `JSON.stringify`. Equivalent to node-pg's own object
174
+ * handling, made explicit so both write paths agree.
175
+ * - everything else (scalars: string, number, boolean, null) → untouched.
176
+ *
177
+ * Boundary note: the serializer has no access to the table schema, so it
178
+ * cannot tell a `json` / `jsonb` column from a native-array column purely
179
+ * from the value. `nativeArrayColumns` is the explicit, opt-in escape hatch
180
+ * for the latter. No `::jsonb` placeholder cast is added: a JSON-text string
181
+ * binds correctly to `json` / `jsonb` without one, and a blind cast would
182
+ * misfire on columns we cannot positively identify as jsonb.
183
+ *
184
+ * @param key - Column name (used to honour `nativeArrayColumns`)
185
+ * @param value - The raw value to serialize (never `undefined`)
186
+ * @returns The value ready to bind as a query parameter
187
+ */
188
+ private serializeValue;
147
189
  /**
148
190
  * Get the dirty tracker for this driver.
149
191
  */
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":"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"}
@@ -125,12 +125,20 @@ var PostgresDriver = class {
125
125
  */
126
126
  _syncAdapter;
127
127
  /**
128
+ * Lookup set of column names that hold native PostgreSQL arrays
129
+ * (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text encoded.
130
+ *
131
+ * @see PostgresPoolConfig.nativeArrayColumns
132
+ */
133
+ _nativeArrayColumns;
134
+ /**
128
135
  * Create a new PostgreSQL driver instance.
129
136
  *
130
137
  * @param config - PostgreSQL connection configuration
131
138
  */
132
139
  constructor(config) {
133
140
  this.config = config;
141
+ this._nativeArrayColumns = new Set(config.nativeArrayColumns ?? []);
134
142
  }
135
143
  /**
136
144
  * Get the connection pool instance.
@@ -231,15 +239,56 @@ var PostgresDriver = class {
231
239
  const serialized = {};
232
240
  for (const [key, value] of Object.entries(data)) {
233
241
  if (value === void 0) continue;
234
- if (value instanceof Date) serialized[key] = value.toISOString();
235
- else if (typeof value === "bigint") serialized[key] = value.toString();
236
- else if (typeof value === "object" && value !== null && !Array.isArray(value)) serialized[key] = value;
237
- else if (Array.isArray(value) && value.length > 0 && value.every((v) => typeof v === "number")) serialized[key] = `[${value.join(",")}]`;
238
- else serialized[key] = value;
242
+ serialized[key] = this.serializeValue(key, value);
239
243
  }
240
244
  return serialized;
241
245
  }
242
246
  /**
247
+ * Serialize a single column value into a node-pg bindable parameter.
248
+ *
249
+ * Shared by {@link serialize} (INSERT path) and {@link buildUpdateQuery}
250
+ * `$set` (UPDATE path) so both encode `json` / `jsonb` columns identically.
251
+ *
252
+ * Encoding rules (in order):
253
+ * - `Date` → ISO string.
254
+ * - `bigint` → decimal string (node-pg has no native bigint binding).
255
+ * - all-number array → pgvector literal `'[n1,n2,...]'`. node-pg would
256
+ * otherwise emit a `{n1,n2,...}` array literal, which the `vector` type
257
+ * rejects. This branch is preserved exactly.
258
+ * - any other array (object-array, string-array, mixed, empty `[]`) →
259
+ * `JSON.stringify`. node-pg renders a raw JS array as a PostgreSQL array
260
+ * literal `{...}` (and `[]` as `{}`), which a `json` / `jsonb` column
261
+ * rejects — so we bind the value as JSON text instead, the form those
262
+ * columns accept. Columns listed in `nativeArrayColumns` are exempt:
263
+ * their raw array is passed through so node-pg emits the `{...}` literal
264
+ * a genuine `JSONB[]` / `TEXT[]` column needs.
265
+ * - plain object → `JSON.stringify`. Equivalent to node-pg's own object
266
+ * handling, made explicit so both write paths agree.
267
+ * - everything else (scalars: string, number, boolean, null) → untouched.
268
+ *
269
+ * Boundary note: the serializer has no access to the table schema, so it
270
+ * cannot tell a `json` / `jsonb` column from a native-array column purely
271
+ * from the value. `nativeArrayColumns` is the explicit, opt-in escape hatch
272
+ * for the latter. No `::jsonb` placeholder cast is added: a JSON-text string
273
+ * binds correctly to `json` / `jsonb` without one, and a blind cast would
274
+ * misfire on columns we cannot positively identify as jsonb.
275
+ *
276
+ * @param key - Column name (used to honour `nativeArrayColumns`)
277
+ * @param value - The raw value to serialize (never `undefined`)
278
+ * @returns The value ready to bind as a query parameter
279
+ */
280
+ serializeValue(key, value) {
281
+ if (value instanceof Date) return value.toISOString();
282
+ if (typeof value === "bigint") return value.toString();
283
+ if (Array.isArray(value)) {
284
+ if (value.length > 0 && value.every((v) => typeof v === "number")) return `[${value.join(",")}]`;
285
+ if (this._nativeArrayColumns.has(key)) return value;
286
+ return JSON.stringify(value);
287
+ }
288
+ if (typeof value === "object" && value !== null) return JSON.stringify(value);
289
+ return value;
290
+ }
291
+ /**
243
292
  * Get the dirty tracker for this driver.
244
293
  */
245
294
  getDirtyTracker(data) {
@@ -708,7 +757,7 @@ var PostgresDriver = class {
708
757
  let paramIndex = 1;
709
758
  if (update.$set) for (const [key, value] of Object.entries(update.$set)) {
710
759
  setClauses.push(`${this.dialect.quoteIdentifier(key)} = ${this.dialect.placeholder(paramIndex++)}`);
711
- params.push(value);
760
+ params.push(value === void 0 ? value : this.serializeValue(key, value));
712
761
  }
713
762
  if (update.$unset) for (const key of Object.keys(update.$unset)) setClauses.push(`${this.dialect.quoteIdentifier(key)} = NULL`);
714
763
  if (update.$inc) for (const [key, amount] of Object.entries(update.$inc)) {