@warlock.js/cascade 5.28.0 → 5.30.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 (393) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/cjs/index.cjs +101 -101
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/context/database-data-source-context.d.mts +1 -1
  5. package/esm/context/database-data-source-context.d.mts.map +1 -1
  6. package/esm/context/database-data-source-context.mjs +1 -1
  7. package/esm/context/database-data-source-context.mjs.map +1 -1
  8. package/esm/context/database-transaction-context.d.mts +1 -1
  9. package/esm/context/database-transaction-context.d.mts.map +1 -1
  10. package/esm/context/database-transaction-context.mjs +1 -1
  11. package/esm/context/database-transaction-context.mjs.map +1 -1
  12. package/esm/contracts/database-driver.contract.d.mts +1 -1
  13. package/esm/contracts/database-driver.contract.d.mts.map +1 -1
  14. package/esm/contracts/database-id-generator.contract.d.mts +1 -1
  15. package/esm/contracts/database-id-generator.contract.d.mts.map +1 -1
  16. package/esm/contracts/database-remover.contract.d.mts +1 -1
  17. package/esm/contracts/database-remover.contract.d.mts.map +1 -1
  18. package/esm/contracts/database-restorer.contract.d.mts +1 -1
  19. package/esm/contracts/database-restorer.contract.d.mts.map +1 -1
  20. package/esm/contracts/database-writer.contract.d.mts +1 -1
  21. package/esm/contracts/database-writer.contract.d.mts.map +1 -1
  22. package/esm/contracts/driver-blueprint.contract.d.mts +1 -1
  23. package/esm/contracts/driver-blueprint.contract.d.mts.map +1 -1
  24. package/esm/contracts/migration-driver.contract.d.mts +1 -1
  25. package/esm/contracts/migration-driver.contract.d.mts.map +1 -1
  26. package/esm/contracts/query-builder.contract.d.mts +1 -1
  27. package/esm/contracts/query-builder.contract.d.mts.map +1 -1
  28. package/esm/contracts/sync-adapter.contract.d.mts +1 -1
  29. package/esm/contracts/sync-adapter.contract.d.mts.map +1 -1
  30. package/esm/data-source/data-source-registry.d.mts +1 -1
  31. package/esm/data-source/data-source-registry.d.mts.map +1 -1
  32. package/esm/data-source/data-source-registry.mjs +1 -1
  33. package/esm/data-source/data-source-registry.mjs.map +1 -1
  34. package/esm/data-source/data-source.d.mts +1 -1
  35. package/esm/data-source/data-source.d.mts.map +1 -1
  36. package/esm/data-source/data-source.mjs +1 -1
  37. package/esm/data-source/data-source.mjs.map +1 -1
  38. package/esm/database-dirty-tracker.d.mts +1 -1
  39. package/esm/database-dirty-tracker.d.mts.map +1 -1
  40. package/esm/database-dirty-tracker.mjs +1 -1
  41. package/esm/database-dirty-tracker.mjs.map +1 -1
  42. package/esm/drivers/mongodb/mongodb-blueprint.mjs +1 -1
  43. package/esm/drivers/mongodb/mongodb-blueprint.mjs.map +1 -1
  44. package/esm/drivers/mongodb/mongodb-driver.d.mts +1 -1
  45. package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
  46. package/esm/drivers/mongodb/mongodb-driver.mjs +1 -1
  47. package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
  48. package/esm/drivers/mongodb/mongodb-id-generator.d.mts +1 -1
  49. package/esm/drivers/mongodb/mongodb-id-generator.d.mts.map +1 -1
  50. package/esm/drivers/mongodb/mongodb-id-generator.mjs +1 -1
  51. package/esm/drivers/mongodb/mongodb-id-generator.mjs.map +1 -1
  52. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +1 -1
  53. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
  54. package/esm/drivers/mongodb/mongodb-migration-driver.mjs +1 -1
  55. package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
  56. package/esm/drivers/mongodb/mongodb-query-builder.d.mts +1 -1
  57. package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
  58. package/esm/drivers/mongodb/mongodb-query-builder.mjs +1 -1
  59. package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
  60. package/esm/drivers/mongodb/mongodb-query-operations.d.mts +1 -1
  61. package/esm/drivers/mongodb/mongodb-query-operations.d.mts.map +1 -1
  62. package/esm/drivers/mongodb/mongodb-query-operations.mjs +1 -1
  63. package/esm/drivers/mongodb/mongodb-query-operations.mjs.map +1 -1
  64. package/esm/drivers/mongodb/mongodb-query-parser.d.mts +1 -1
  65. package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
  66. package/esm/drivers/mongodb/mongodb-query-parser.mjs +1 -1
  67. package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
  68. package/esm/drivers/mongodb/mongodb-sync-adapter.d.mts +1 -1
  69. package/esm/drivers/mongodb/mongodb-sync-adapter.d.mts.map +1 -1
  70. package/esm/drivers/mongodb/mongodb-sync-adapter.mjs +1 -1
  71. package/esm/drivers/mongodb/mongodb-sync-adapter.mjs.map +1 -1
  72. package/esm/drivers/mongodb/mongodb-update-translator.mjs +1 -1
  73. package/esm/drivers/mongodb/mongodb-update-translator.mjs.map +1 -1
  74. package/esm/drivers/mongodb/pipeline-stage-object.mjs +1 -1
  75. package/esm/drivers/mongodb/pipeline-stage-object.mjs.map +1 -1
  76. package/esm/drivers/mongodb/types.d.mts +1 -1
  77. package/esm/drivers/mongodb/types.d.mts.map +1 -1
  78. package/esm/drivers/postgres/naming.mjs +1 -1
  79. package/esm/drivers/postgres/naming.mjs.map +1 -1
  80. package/esm/drivers/postgres/postgres-blueprint.d.mts +1 -1
  81. package/esm/drivers/postgres/postgres-blueprint.d.mts.map +1 -1
  82. package/esm/drivers/postgres/postgres-blueprint.mjs +1 -1
  83. package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
  84. package/esm/drivers/postgres/postgres-dialect.d.mts +1 -1
  85. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  86. package/esm/drivers/postgres/postgres-dialect.mjs +1 -1
  87. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  88. package/esm/drivers/postgres/postgres-driver.d.mts +1 -1
  89. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  90. package/esm/drivers/postgres/postgres-driver.mjs +1 -1
  91. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  92. package/esm/drivers/postgres/postgres-migration-driver.d.mts +1 -1
  93. package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
  94. package/esm/drivers/postgres/postgres-migration-driver.mjs +1 -1
  95. package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
  96. package/esm/drivers/postgres/postgres-query-builder.d.mts +1 -1
  97. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  98. package/esm/drivers/postgres/postgres-query-builder.mjs +1 -1
  99. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  100. package/esm/drivers/postgres/postgres-query-parser.d.mts +1 -1
  101. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  102. package/esm/drivers/postgres/postgres-query-parser.mjs +1 -1
  103. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  104. package/esm/drivers/postgres/postgres-sql-serializer.mjs +1 -1
  105. package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
  106. package/esm/drivers/postgres/postgres-ttl-purge.mjs +1 -1
  107. package/esm/drivers/postgres/postgres-ttl-purge.mjs.map +1 -1
  108. package/esm/drivers/postgres/postgres-type-converters.mjs +1 -1
  109. package/esm/drivers/postgres/postgres-type-converters.mjs.map +1 -1
  110. package/esm/drivers/postgres/postgres-update-validator.mjs +1 -1
  111. package/esm/drivers/postgres/postgres-update-validator.mjs.map +1 -1
  112. package/esm/drivers/postgres/types.d.mts +1 -1
  113. package/esm/drivers/postgres/types.d.mts.map +1 -1
  114. package/esm/drivers/sql/sql-dialect.contract.d.mts +1 -1
  115. package/esm/drivers/sql/sql-dialect.contract.d.mts.map +1 -1
  116. package/esm/drivers/sql/sql-types.d.mts +1 -1
  117. package/esm/drivers/sql/sql-types.d.mts.map +1 -1
  118. package/esm/errors/missing-data-source.error.d.mts +1 -1
  119. package/esm/errors/missing-data-source.error.d.mts.map +1 -1
  120. package/esm/errors/missing-data-source.error.mjs +1 -1
  121. package/esm/errors/missing-data-source.error.mjs.map +1 -1
  122. package/esm/errors/transaction-rollback.error.d.mts +1 -1
  123. package/esm/errors/transaction-rollback.error.d.mts.map +1 -1
  124. package/esm/errors/transaction-rollback.error.mjs +1 -1
  125. package/esm/errors/transaction-rollback.error.mjs.map +1 -1
  126. package/esm/errors/undefined-where-value.error.d.mts +1 -1
  127. package/esm/errors/undefined-where-value.error.d.mts.map +1 -1
  128. package/esm/errors/undefined-where-value.error.mjs +1 -1
  129. package/esm/errors/undefined-where-value.error.mjs.map +1 -1
  130. package/esm/errors/unsafe-filter.error.d.mts +1 -1
  131. package/esm/errors/unsafe-filter.error.d.mts.map +1 -1
  132. package/esm/errors/unsafe-filter.error.mjs +1 -1
  133. package/esm/errors/unsafe-filter.error.mjs.map +1 -1
  134. package/esm/errors/unsafe-raw-expression.error.d.mts +1 -1
  135. package/esm/errors/unsafe-raw-expression.error.d.mts.map +1 -1
  136. package/esm/errors/unsafe-raw-expression.error.mjs +1 -1
  137. package/esm/errors/unsafe-raw-expression.error.mjs.map +1 -1
  138. package/esm/errors/unsupported-lean-operation.error.d.mts +1 -1
  139. package/esm/errors/unsupported-lean-operation.error.d.mts.map +1 -1
  140. package/esm/errors/unsupported-lean-operation.error.mjs +1 -1
  141. package/esm/errors/unsupported-lean-operation.error.mjs.map +1 -1
  142. package/esm/errors/unsupported-lock-for-update.error.d.mts +1 -1
  143. package/esm/errors/unsupported-lock-for-update.error.d.mts.map +1 -1
  144. package/esm/errors/unsupported-lock-for-update.error.mjs +1 -1
  145. package/esm/errors/unsupported-lock-for-update.error.mjs.map +1 -1
  146. package/esm/errors/unsupported-query-operation.error.d.mts +1 -1
  147. package/esm/errors/unsupported-query-operation.error.d.mts.map +1 -1
  148. package/esm/errors/unsupported-query-operation.error.mjs +1 -1
  149. package/esm/errors/unsupported-query-operation.error.mjs.map +1 -1
  150. package/esm/errors/unsupported-update-operation.error.d.mts +1 -1
  151. package/esm/errors/unsupported-update-operation.error.d.mts.map +1 -1
  152. package/esm/errors/unsupported-update-operation.error.mjs +1 -1
  153. package/esm/errors/unsupported-update-operation.error.mjs.map +1 -1
  154. package/esm/events/model-events.d.mts +1 -1
  155. package/esm/events/model-events.d.mts.map +1 -1
  156. package/esm/events/model-events.mjs +1 -1
  157. package/esm/events/model-events.mjs.map +1 -1
  158. package/esm/expressions/aggregate-expressions.d.mts +1 -1
  159. package/esm/expressions/aggregate-expressions.d.mts.map +1 -1
  160. package/esm/expressions/aggregate-expressions.mjs +1 -1
  161. package/esm/expressions/aggregate-expressions.mjs.map +1 -1
  162. package/esm/expressions/column-expressions.d.mts +1 -1
  163. package/esm/expressions/column-expressions.d.mts.map +1 -1
  164. package/esm/expressions/column-expressions.mjs +1 -1
  165. package/esm/expressions/column-expressions.mjs.map +1 -1
  166. package/esm/migration/column-builder.d.mts +1 -1
  167. package/esm/migration/column-builder.d.mts.map +1 -1
  168. package/esm/migration/column-builder.mjs +1 -1
  169. package/esm/migration/column-builder.mjs.map +1 -1
  170. package/esm/migration/column-helpers.d.mts +1 -1
  171. package/esm/migration/column-helpers.d.mts.map +1 -1
  172. package/esm/migration/column-helpers.mjs +1 -1
  173. package/esm/migration/column-helpers.mjs.map +1 -1
  174. package/esm/migration/foreign-key-builder.d.mts +1 -1
  175. package/esm/migration/foreign-key-builder.d.mts.map +1 -1
  176. package/esm/migration/foreign-key-builder.mjs +1 -1
  177. package/esm/migration/foreign-key-builder.mjs.map +1 -1
  178. package/esm/migration/migration-order.mjs +1 -1
  179. package/esm/migration/migration-order.mjs.map +1 -1
  180. package/esm/migration/migration-runner.d.mts +1 -1
  181. package/esm/migration/migration-runner.d.mts.map +1 -1
  182. package/esm/migration/migration-runner.mjs +1 -1
  183. package/esm/migration/migration-runner.mjs.map +1 -1
  184. package/esm/migration/migration.d.mts +1 -1
  185. package/esm/migration/migration.d.mts.map +1 -1
  186. package/esm/migration/migration.mjs +1 -1
  187. package/esm/migration/migration.mjs.map +1 -1
  188. package/esm/migration/parse-created-at.mjs +1 -1
  189. package/esm/migration/parse-created-at.mjs.map +1 -1
  190. package/esm/migration/sql-grammar.mjs +1 -1
  191. package/esm/migration/sql-grammar.mjs.map +1 -1
  192. package/esm/migration/sql-serializer.d.mts +1 -1
  193. package/esm/migration/sql-serializer.d.mts.map +1 -1
  194. package/esm/migration/sql-serializer.mjs +1 -1
  195. package/esm/migration/sql-serializer.mjs.map +1 -1
  196. package/esm/migration/types.d.mts +1 -1
  197. package/esm/migration/types.d.mts.map +1 -1
  198. package/esm/model/generate-model-id.mjs +1 -1
  199. package/esm/model/generate-model-id.mjs.map +1 -1
  200. package/esm/model/methods/accessor-methods.mjs +1 -1
  201. package/esm/model/methods/accessor-methods.mjs.map +1 -1
  202. package/esm/model/methods/delete-methods.mjs +1 -1
  203. package/esm/model/methods/delete-methods.mjs.map +1 -1
  204. package/esm/model/methods/dirty-methods.mjs +1 -1
  205. package/esm/model/methods/dirty-methods.mjs.map +1 -1
  206. package/esm/model/methods/hydration-methods.mjs +1 -1
  207. package/esm/model/methods/hydration-methods.mjs.map +1 -1
  208. package/esm/model/methods/instance-event-methods.mjs +1 -1
  209. package/esm/model/methods/instance-event-methods.mjs.map +1 -1
  210. package/esm/model/methods/meta-methods.mjs +1 -1
  211. package/esm/model/methods/meta-methods.mjs.map +1 -1
  212. package/esm/model/methods/pivot-methods.mjs +1 -1
  213. package/esm/model/methods/pivot-methods.mjs.map +1 -1
  214. package/esm/model/methods/query-methods.mjs +1 -1
  215. package/esm/model/methods/query-methods.mjs.map +1 -1
  216. package/esm/model/methods/restore-methods.mjs +1 -1
  217. package/esm/model/methods/restore-methods.mjs.map +1 -1
  218. package/esm/model/methods/scope-methods.mjs +1 -1
  219. package/esm/model/methods/scope-methods.mjs.map +1 -1
  220. package/esm/model/methods/serialization-methods.mjs +1 -1
  221. package/esm/model/methods/serialization-methods.mjs.map +1 -1
  222. package/esm/model/methods/static-event-methods.mjs +1 -1
  223. package/esm/model/methods/static-event-methods.mjs.map +1 -1
  224. package/esm/model/methods/write-methods.d.mts +1 -1
  225. package/esm/model/methods/write-methods.d.mts.map +1 -1
  226. package/esm/model/methods/write-methods.mjs +1 -1
  227. package/esm/model/methods/write-methods.mjs.map +1 -1
  228. package/esm/model/model.d.mts +1 -1
  229. package/esm/model/model.d.mts.map +1 -1
  230. package/esm/model/model.mjs +1 -1
  231. package/esm/model/model.mjs.map +1 -1
  232. package/esm/model/model.types.d.mts +1 -1
  233. package/esm/model/model.types.d.mts.map +1 -1
  234. package/esm/model/register-model.d.mts +1 -1
  235. package/esm/model/register-model.d.mts.map +1 -1
  236. package/esm/model/register-model.mjs +1 -1
  237. package/esm/model/register-model.mjs.map +1 -1
  238. package/esm/model/relation-decorators.d.mts +1 -1
  239. package/esm/model/relation-decorators.d.mts.map +1 -1
  240. package/esm/model/relation-decorators.mjs +1 -1
  241. package/esm/model/relation-decorators.mjs.map +1 -1
  242. package/esm/operations/database.d.mts +1 -1
  243. package/esm/operations/database.d.mts.map +1 -1
  244. package/esm/operations/database.mjs +1 -1
  245. package/esm/operations/database.mjs.map +1 -1
  246. package/esm/operations/migrations.d.mts +1 -1
  247. package/esm/operations/migrations.d.mts.map +1 -1
  248. package/esm/operations/migrations.mjs +1 -1
  249. package/esm/operations/migrations.mjs.map +1 -1
  250. package/esm/query-builder/lean-records.mjs +1 -1
  251. package/esm/query-builder/lean-records.mjs.map +1 -1
  252. package/esm/query-builder/query-builder.d.mts +1 -1
  253. package/esm/query-builder/query-builder.d.mts.map +1 -1
  254. package/esm/query-builder/query-builder.mjs +1 -1
  255. package/esm/query-builder/query-builder.mjs.map +1 -1
  256. package/esm/relations/key-conventions.mjs +1 -1
  257. package/esm/relations/key-conventions.mjs.map +1 -1
  258. package/esm/relations/pivot-operations.d.mts +1 -1
  259. package/esm/relations/pivot-operations.d.mts.map +1 -1
  260. package/esm/relations/pivot-operations.mjs +1 -1
  261. package/esm/relations/pivot-operations.mjs.map +1 -1
  262. package/esm/relations/relation-hydrator.d.mts +1 -1
  263. package/esm/relations/relation-hydrator.d.mts.map +1 -1
  264. package/esm/relations/relation-hydrator.mjs +1 -1
  265. package/esm/relations/relation-hydrator.mjs.map +1 -1
  266. package/esm/relations/relation-loader.d.mts +1 -1
  267. package/esm/relations/relation-loader.d.mts.map +1 -1
  268. package/esm/relations/relation-loader.mjs +1 -1
  269. package/esm/relations/relation-loader.mjs.map +1 -1
  270. package/esm/relations/types.d.mts +1 -1
  271. package/esm/relations/types.d.mts.map +1 -1
  272. package/esm/remover/database-remover.d.mts +1 -1
  273. package/esm/remover/database-remover.d.mts.map +1 -1
  274. package/esm/remover/database-remover.mjs +1 -1
  275. package/esm/remover/database-remover.mjs.map +1 -1
  276. package/esm/restorer/database-restorer.d.mts +1 -1
  277. package/esm/restorer/database-restorer.d.mts.map +1 -1
  278. package/esm/restorer/database-restorer.mjs +1 -1
  279. package/esm/restorer/database-restorer.mjs.map +1 -1
  280. package/esm/sql-database-dirty-tracker.d.mts +1 -1
  281. package/esm/sql-database-dirty-tracker.d.mts.map +1 -1
  282. package/esm/sql-database-dirty-tracker.mjs +1 -1
  283. package/esm/sql-database-dirty-tracker.mjs.map +1 -1
  284. package/esm/sync/model-events.mjs +1 -1
  285. package/esm/sync/model-events.mjs.map +1 -1
  286. package/esm/sync/model-sync-operation.d.mts +1 -1
  287. package/esm/sync/model-sync-operation.d.mts.map +1 -1
  288. package/esm/sync/model-sync-operation.mjs +1 -1
  289. package/esm/sync/model-sync-operation.mjs.map +1 -1
  290. package/esm/sync/model-sync.d.mts +1 -1
  291. package/esm/sync/model-sync.d.mts.map +1 -1
  292. package/esm/sync/model-sync.mjs +1 -1
  293. package/esm/sync/model-sync.mjs.map +1 -1
  294. package/esm/sync/sync-context.d.mts +1 -1
  295. package/esm/sync/sync-context.d.mts.map +1 -1
  296. package/esm/sync/sync-context.mjs +1 -1
  297. package/esm/sync/sync-context.mjs.map +1 -1
  298. package/esm/sync/sync-fanout.mjs +1 -1
  299. package/esm/sync/sync-fanout.mjs.map +1 -1
  300. package/esm/sync/sync-manager.d.mts +1 -1
  301. package/esm/sync/sync-manager.d.mts.map +1 -1
  302. package/esm/sync/sync-manager.mjs +1 -1
  303. package/esm/sync/sync-manager.mjs.map +1 -1
  304. package/esm/sync/types.d.mts +1 -1
  305. package/esm/sync/types.d.mts.map +1 -1
  306. package/esm/transactions/after-commit.d.mts +1 -1
  307. package/esm/transactions/after-commit.d.mts.map +1 -1
  308. package/esm/transactions/after-commit.mjs +1 -1
  309. package/esm/transactions/after-commit.mjs.map +1 -1
  310. package/esm/types.d.mts +1 -1
  311. package/esm/types.d.mts.map +1 -1
  312. package/esm/utils/connect-to-database.d.mts +1 -1
  313. package/esm/utils/connect-to-database.d.mts.map +1 -1
  314. package/esm/utils/connect-to-database.mjs +1 -1
  315. package/esm/utils/connect-to-database.mjs.map +1 -1
  316. package/esm/utils/database-writer.utils.d.mts +1 -1
  317. package/esm/utils/database-writer.utils.d.mts.map +1 -1
  318. package/esm/utils/database-writer.utils.mjs +1 -1
  319. package/esm/utils/database-writer.utils.mjs.map +1 -1
  320. package/esm/utils/define-model.d.mts +1 -1
  321. package/esm/utils/define-model.d.mts.map +1 -1
  322. package/esm/utils/define-model.mjs +1 -1
  323. package/esm/utils/define-model.mjs.map +1 -1
  324. package/esm/utils/escape-regex.d.mts +1 -1
  325. package/esm/utils/escape-regex.d.mts.map +1 -1
  326. package/esm/utils/escape-regex.mjs +1 -1
  327. package/esm/utils/escape-regex.mjs.map +1 -1
  328. package/esm/utils/is-valid-date-value.mjs +1 -1
  329. package/esm/utils/is-valid-date-value.mjs.map +1 -1
  330. package/esm/utils/once-connected.d.mts +1 -1
  331. package/esm/utils/once-connected.d.mts.map +1 -1
  332. package/esm/utils/once-connected.mjs +1 -1
  333. package/esm/utils/once-connected.mjs.map +1 -1
  334. package/esm/utils/prune-empty-parents.mjs +1 -1
  335. package/esm/utils/prune-empty-parents.mjs.map +1 -1
  336. package/esm/utils/sanitize-filter.d.mts +1 -1
  337. package/esm/utils/sanitize-filter.d.mts.map +1 -1
  338. package/esm/utils/sanitize-filter.mjs +1 -1
  339. package/esm/utils/sanitize-filter.mjs.map +1 -1
  340. package/esm/validation/database-seal-plugins.mjs +1 -1
  341. package/esm/validation/database-seal-plugins.mjs.map +1 -1
  342. package/esm/validation/database-writer-validation-error.d.mts +1 -1
  343. package/esm/validation/database-writer-validation-error.d.mts.map +1 -1
  344. package/esm/validation/database-writer-validation-error.mjs +1 -1
  345. package/esm/validation/database-writer-validation-error.mjs.map +1 -1
  346. package/esm/validation/mutators/embed-mutator.mjs +1 -1
  347. package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
  348. package/esm/validation/plugins/database-rules-plugin.d.mts +1 -1
  349. package/esm/validation/plugins/database-rules-plugin.d.mts.map +1 -1
  350. package/esm/validation/plugins/database-rules-plugin.mjs +1 -1
  351. package/esm/validation/plugins/database-rules-plugin.mjs.map +1 -1
  352. package/esm/validation/plugins/embed-validator-plugin.d.mts +1 -1
  353. package/esm/validation/plugins/embed-validator-plugin.d.mts.map +1 -1
  354. package/esm/validation/plugins/embed-validator-plugin.mjs +1 -1
  355. package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
  356. package/esm/validation/rules/database-model-rule.mjs +1 -1
  357. package/esm/validation/rules/database-model-rule.mjs.map +1 -1
  358. package/esm/validation/rules/exists-rule.mjs +1 -1
  359. package/esm/validation/rules/exists-rule.mjs.map +1 -1
  360. package/esm/validation/rules/unique-rule.mjs +1 -1
  361. package/esm/validation/rules/unique-rule.mjs.map +1 -1
  362. package/esm/validation/transformers/embed-model-transformer.mjs +1 -1
  363. package/esm/validation/transformers/embed-model-transformer.mjs.map +1 -1
  364. package/esm/validation/types.d.mts +1 -1
  365. package/esm/validation/types.d.mts.map +1 -1
  366. package/esm/validation/validators/embed-validator.d.mts +1 -1
  367. package/esm/validation/validators/embed-validator.d.mts.map +1 -1
  368. package/esm/validation/validators/embed-validator.mjs +1 -1
  369. package/esm/validation/validators/embed-validator.mjs.map +1 -1
  370. package/esm/writer/database-writer.d.mts +1 -1
  371. package/esm/writer/database-writer.d.mts.map +1 -1
  372. package/esm/writer/database-writer.mjs +1 -1
  373. package/esm/writer/database-writer.mjs.map +1 -1
  374. package/llms-full.txt +93 -93
  375. package/llms.txt +15 -15
  376. package/package.json +7 -6
  377. package/skills/README.md +17 -17
  378. package/skills/aggregate-data/SKILL.md +3 -3
  379. package/skills/alter-migration/SKILL.md +4 -4
  380. package/skills/cascade-basics/SKILL.md +21 -21
  381. package/skills/configure-delete-strategy/SKILL.md +6 -6
  382. package/skills/define-model/SKILL.md +8 -8
  383. package/skills/define-relations/SKILL.md +3 -3
  384. package/skills/manage-data-sources/SKILL.md +4 -4
  385. package/skills/manage-transactions/SKILL.md +4 -4
  386. package/skills/paginate-results/SKILL.md +4 -4
  387. package/skills/perform-atomic-ops/SKILL.md +5 -5
  388. package/skills/query-data/SKILL.md +10 -10
  389. package/skills/run-cascade-cli/SKILL.md +3 -3
  390. package/skills/search-by-vector/SKILL.md +3 -3
  391. package/skills/subscribe-to-model-events/SKILL.md +5 -5
  392. package/skills/track-changes/SKILL.md +3 -3
  393. package/skills/write-migration/SKILL.md +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-query-parser.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-query-parser.ts"],"sourcesContent":["/**\n * PostgreSQL Query Parser\n *\n * Translates Cascade query operations into PostgreSQL SQL queries.\n * Mirrors the MongoQueryParser pattern but generates SQL instead\n * of MongoDB aggregation pipelines.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverQuery,\n JoinOptions,\n WhereOperator,\n} from \"../../contracts/query-builder.contract\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\n\nimport { escapeSqlLiteral, PostgresDialect } from \"./postgres-dialect\";\n\n/** Allowed `::cast` type names, e.g. `int`, `numeric(10,2)`, `text[]`. */\nconst SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_ ]*(\\(\\d+(,\\d+)?\\))?(\\[\\])?$/i;\n\n/** Allowed orderBy direction, with optional NULLS placement. */\nconst SAFE_DIRECTION_PATTERN = /^(asc|desc)(\\s+nulls\\s+(first|last))?$/i;\n\n/**\n * Operation types supported by the query parser.\n */\nexport type PostgresOperationType =\n // WHERE operations\n | \"where\"\n | \"orWhere\"\n | \"whereRaw\"\n | \"orWhereRaw\"\n | \"whereIn\"\n | \"whereNotIn\"\n | \"whereNull\"\n | \"whereNotNull\"\n | \"whereBetween\"\n | \"whereNotBetween\"\n | \"orWhereIn\"\n | \"orWhereNotIn\"\n | \"orWhereNull\"\n | \"orWhereNotNull\"\n | \"orWhereBetween\"\n | \"whereLike\"\n | \"whereNotLike\"\n | \"whereColumn\"\n | \"orWhereColumn\"\n | \"whereExists\"\n | \"whereNotExists\"\n | \"whereNot\"\n | \"orWhereNot\"\n | \"whereDate\"\n | \"whereDateBefore\"\n | \"whereDateAfter\"\n | \"whereDateBetween\"\n | \"whereJsonContains\"\n | \"whereJsonDoesntContain\"\n | \"whereFullText\"\n // SELECT operations\n | \"select\"\n | \"selectRaw\"\n | \"deselect\"\n // JOIN operations\n | \"join\"\n | \"leftJoin\"\n | \"rightJoin\"\n | \"innerJoin\"\n | \"fullJoin\"\n | \"crossJoin\"\n | \"joinRaw\"\n // ORDER operations\n | \"orderBy\"\n | \"orderByRaw\"\n // GROUP operations\n | \"groupBy\"\n | \"groupByRaw\"\n | \"having\"\n | \"havingRaw\"\n // LIMIT operations\n | \"limit\"\n | \"offset\"\n // Row locking\n | \"lock\"\n // RELATION operations\n | \"has\"\n | \"whereHas\"\n | \"doesntHave\"\n | \"whereDoesntHave\"\n // joinWith operations\n | \"selectRelatedColumns\"\n // Builder-level metadata (consumed before parsing)\n | \"joinWith\"\n | \"cursor\"\n // Other\n | \"distinct\";\n\n/**\n * Internal operation representation.\n */\nexport type PostgresParserOperation = {\n /** Operation type */\n readonly type: PostgresOperationType;\n /** Operation data/parameters */\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Parser configuration options.\n */\nexport type PostgresParserOptions = {\n /** Target table name */\n readonly table: string;\n /** Table alias (optional) */\n readonly alias?: string;\n /** Operations to parse */\n readonly operations: PostgresParserOperation[];\n /** SQL dialect for syntax generation */\n readonly dialect?: SqlDialectContract;\n /** Factory for creating sub-parsers (for nested queries) */\n readonly createSubParser?: (table: string) => PostgresQueryParser;\n};\n\n/**\n * PostgreSQL Query Parser.\n *\n * Converts a list of query operations into a SQL query string with parameters.\n * Handles SELECT, WHERE, JOIN, ORDER BY, GROUP BY, LIMIT/OFFSET clauses.\n *\n * @example\n * ```typescript\n * const parser = new PostgresQueryParser({\n * table: 'users',\n * operations: [\n * { type: 'where', data: { field: 'name', operator: '=', value: 'Alice' } },\n * { type: 'orderBy', data: { field: 'createdAt', direction: 'desc' } },\n * { type: 'limit', data: { value: 10 } }\n * ]\n * });\n *\n * const { sql, params } = parser.parse();\n * // sql: 'SELECT * FROM \"users\" WHERE \"name\" = $1 ORDER BY \"createdAt\" DESC LIMIT 10'\n * // params: ['Alice']\n * ```\n */\nexport class PostgresQueryParser {\n /**\n * Target table name.\n */\n private readonly table: string;\n\n /**\n * Table alias.\n */\n private readonly alias?: string;\n\n /**\n * Operations to process.\n */\n private readonly operations: PostgresParserOperation[];\n\n /**\n * SQL dialect for syntax.\n */\n private readonly dialect: SqlDialectContract;\n\n /**\n * Current parameter index (1-based for PostgreSQL).\n */\n private paramIndex = 1;\n\n /**\n * Collected parameters.\n */\n private readonly params: unknown[] = [];\n\n /**\n * SELECT columns.\n */\n public selectColumns: string[] = [];\n\n /**\n * Deselected (excluded) columns.\n */\n private deselectColumns: string[] = [];\n\n /**\n * Raw SELECT expressions.\n */\n private selectRaw: string[] = [];\n\n /**\n * WHERE clauses.\n */\n public whereClauses: string[] = [];\n\n /**\n * JOIN clauses.\n */\n private joinClauses: string[] = [];\n\n /**\n * ORDER BY clauses.\n */\n public orderClauses: string[] = [];\n\n /**\n * GROUP BY columns (bare identifiers — get quoted).\n */\n private groupColumns: string[] = [];\n\n /**\n * Raw GROUP BY expressions (emitted verbatim — e.g. a `date_trunc(...)`\n * bucket). Kept separate from `groupColumns` so they bypass identifier\n * quoting while still landing in the same `GROUP BY` clause.\n */\n private groupRawExpressions: string[] = [];\n\n /**\n * HAVING clauses.\n */\n private havingClauses: string[] = [];\n\n /**\n * LIMIT value.\n */\n public limitValue?: number;\n\n /**\n * OFFSET value.\n */\n public offsetValue?: number;\n\n /**\n * DISTINCT flag.\n */\n private isDistinct = false;\n\n /**\n * Row-locking clause (`FOR UPDATE [SKIP LOCKED | NOWAIT]`). Appended after\n * LIMIT/OFFSET — the locking clause is last in PostgreSQL's SELECT grammar.\n */\n private lockClause = \"\";\n\n /**\n * Whether the query has any JOIN operations (pre-scanned before processing).\n * Used by qualifyColumn() to decide whether to prefix columns with the main table.\n */\n private hasJoins = false;\n\n /**\n * Tracked joined tables (for table reference detection).\n */\n private readonly joinedTables = new Set<string>();\n\n /**\n * Create a new query parser.\n *\n * @param options - Parser configuration\n */\n public constructor(options: PostgresParserOptions) {\n this.table = options.table;\n this.alias = options.alias;\n this.operations = options.operations;\n this.dialect = options.dialect ?? new PostgresDialect();\n }\n\n /**\n * Parse all operations and build the SQL query.\n *\n * @returns DriverQuery with `query` (SQL string) and `bindings` (parameter values)\n */\n public parse(): DriverQuery {\n // Pre-scan for any join operations so qualifyColumn() can prefix columns\n // correctly even when WHERE clauses appear before JOINs in the operations list.\n const JOIN_TYPES = new Set([\n \"join\",\n \"leftJoin\",\n \"rightJoin\",\n \"innerJoin\",\n \"fullJoin\",\n \"crossJoin\",\n \"joinRaw\",\n ]);\n\n this.hasJoins = false;\n\n // First pass: locate all joins and populate joinedTables for accurate JSON path detection\n for (const operation of this.operations) {\n if (JOIN_TYPES.has(operation.type)) {\n this.hasJoins = true;\n const data = operation.data as any;\n const joinTable = data.table as string;\n const alias = data.alias as string;\n\n if (joinTable) this.joinedTables.add(joinTable);\n if (alias) this.joinedTables.add(alias);\n }\n }\n\n // Process each operation\n for (const operation of this.operations) {\n this.processOperation(operation);\n }\n\n // Build the final SQL query\n const query = this.buildSql();\n\n return { query, bindings: this.params };\n }\n\n /**\n * Get a formatted string representation of the query.\n *\n * @returns Formatted SQL with bindings\n */\n public toPrettyString(): string {\n const { query = \"\", bindings } = this.parse();\n return `${query}\\n-- Bindings: ${JSON.stringify(bindings ?? [])}`;\n }\n\n /**\n * Process a single operation.\n *\n * @param operation - The operation to process\n */\n private processOperation(operation: PostgresParserOperation): void {\n const { type, data } = operation;\n\n switch (type) {\n // WHERE operations\n case \"where\":\n this.processWhere(data, \"AND\");\n break;\n case \"orWhere\":\n this.processWhere(data, \"OR\");\n break;\n case \"whereRaw\":\n this.processWhereRaw(data, \"AND\");\n break;\n case \"orWhereRaw\":\n this.processWhereRaw(data, \"OR\");\n break;\n case \"whereIn\":\n this.processWhereIn(data, false);\n break;\n case \"whereNotIn\":\n this.processWhereIn(data, true);\n break;\n case \"orWhereIn\":\n this.processWhereIn(data, false, \"OR\");\n break;\n case \"orWhereNotIn\":\n this.processWhereIn(data, true, \"OR\");\n break;\n case \"whereNull\":\n this.processWhereNull(data, false);\n break;\n case \"whereNotNull\":\n this.processWhereNull(data, true);\n break;\n case \"orWhereNull\":\n this.processWhereNull(data, false, \"OR\");\n break;\n case \"orWhereNotNull\":\n this.processWhereNull(data, true, \"OR\");\n break;\n case \"orWhereBetween\":\n this.processWhereBetween(data, false, \"OR\");\n break;\n case \"whereBetween\":\n this.processWhereBetween(data, false);\n break;\n case \"whereNotBetween\":\n this.processWhereBetween(data, true);\n break;\n case \"whereLike\":\n this.processWhereLike(data, false);\n break;\n case \"whereNotLike\":\n this.processWhereLike(data, true);\n break;\n case \"whereColumn\":\n this.processWhereColumn(data, \"AND\");\n break;\n case \"orWhereColumn\":\n this.processWhereColumn(data, \"OR\");\n break;\n case \"whereJsonContains\":\n this.processWhereJsonContains(data, false);\n break;\n case \"whereJsonDoesntContain\":\n this.processWhereJsonContains(data, true);\n break;\n case \"whereFullText\":\n this.processWhereFullText(data);\n break;\n case \"whereNot\":\n this.processNested(data.nested, \"AND\", true, type);\n break;\n case \"orWhereNot\":\n this.processNested(data.nested, \"OR\", true, type);\n break;\n // Same semantics as the Mongo driver: the callback conditions are applied\n // inline (exists) or negated (notExists).\n case \"whereExists\":\n this.processNested(data.subquery, \"AND\", false, type);\n break;\n case \"whereNotExists\":\n this.processNested(data.subquery, \"AND\", true, type);\n break;\n case \"whereDate\":\n this.processWhereDate(data, \"=\");\n break;\n case \"whereDateBefore\":\n this.processWhereDate(data, \"<\");\n break;\n case \"whereDateAfter\":\n this.processWhereDate(data, \">\");\n break;\n case \"whereDateBetween\":\n this.processWhereDateBetween(data);\n break;\n\n // SELECT operations\n case \"select\":\n this.processSelect(data);\n break;\n case \"selectRaw\":\n this.processSelectRaw(data);\n break;\n case \"deselect\":\n this.processDeselect(data);\n break;\n\n // JOIN operations\n case \"join\":\n case \"innerJoin\":\n this.processJoin(data, \"INNER\");\n break;\n case \"leftJoin\":\n this.processJoin(data, \"LEFT\");\n break;\n case \"rightJoin\":\n this.processJoin(data, \"RIGHT\");\n break;\n case \"fullJoin\":\n this.processJoin(data, \"FULL OUTER\");\n break;\n case \"crossJoin\":\n this.processCrossJoin(data);\n break;\n case \"joinRaw\":\n this.processJoinRaw(data);\n break;\n\n // ORDER operations\n case \"orderBy\":\n this.processOrderBy(data);\n break;\n case \"orderByRaw\":\n this.processOrderByRaw(data);\n break;\n\n // GROUP operations\n case \"groupBy\":\n this.processGroupBy(data);\n break;\n case \"groupByRaw\":\n this.processGroupByRaw(data);\n break;\n case \"having\":\n this.processHaving(data);\n break;\n case \"havingRaw\":\n this.processHavingRaw(data);\n break;\n\n // LIMIT operations\n case \"limit\":\n this.limitValue = data.value as number;\n break;\n case \"offset\":\n this.offsetValue = data.value as number;\n break;\n\n // Row locking\n case \"lock\": {\n let clause = \"FOR UPDATE\";\n if (data.skipLocked) {\n clause += \" SKIP LOCKED\";\n } else if (data.noWait) {\n clause += \" NOWAIT\";\n }\n this.lockClause = clause;\n break;\n }\n\n // Other\n case \"distinct\":\n this.isDistinct = true;\n break;\n\n // joinWith select related columns\n case \"selectRelatedColumns\":\n this.processSelectRelatedColumns(data);\n break;\n\n // Builder-level metadata consumed before parsing; carries no SQL of its own\n case \"cursor\":\n case \"joinWith\":\n break;\n\n default:\n // An unknown filter must never silently disappear: write paths\n // (delete/update) reuse this parser, so a dropped filter widens the\n // statement to every row.\n throw new Error(`PostgresQueryParser: unsupported operation type \"${type}\"`);\n }\n }\n\n /**\n * Build the final SQL query from collected clauses.\n *\n * @returns Complete SQL query string\n */\n private buildSql(): string {\n const parts: string[] = [];\n\n // SELECT clause\n parts.push(this.buildSelectClause());\n\n // FROM clause\n const quotedTable = this.quoteTable(this.table);\n const fromClause = this.alias\n ? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}`\n : `FROM ${quotedTable}`;\n parts.push(fromClause);\n\n // JOIN clauses\n if (this.joinClauses.length > 0) {\n parts.push(this.joinClauses.join(\" \"));\n }\n\n // WHERE clause\n if (this.whereClauses.length > 0) {\n parts.push(`WHERE ${this.whereClauses.join(\" \")}`);\n }\n\n // GROUP BY clause — bare columns are quoted; raw expressions (e.g.\n // date_trunc buckets) are emitted verbatim. Both share one clause.\n if (this.groupColumns.length > 0 || this.groupRawExpressions.length > 0) {\n const quotedCols = this.groupColumns.map((c) => this.dialect.quoteIdentifier(c));\n const allGroupTerms = [...quotedCols, ...this.groupRawExpressions];\n parts.push(`GROUP BY ${allGroupTerms.join(\", \")}`);\n }\n\n // HAVING clause\n if (this.havingClauses.length > 0) {\n parts.push(`HAVING ${this.havingClauses.join(\" AND \")}`);\n }\n\n // ORDER BY clause\n if (this.orderClauses.length > 0) {\n parts.push(`ORDER BY ${this.orderClauses.join(\", \")}`);\n }\n\n // LIMIT/OFFSET\n const limitOffset = this.dialect.limitOffset(this.limitValue, this.offsetValue);\n if (limitOffset) {\n parts.push(limitOffset);\n }\n\n // Row-locking clause — last in PostgreSQL's SELECT grammar\n if (this.lockClause) {\n parts.push(this.lockClause);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build the SELECT clause.\n *\n * @returns SELECT clause string\n */\n private buildSelectClause(): string {\n const distinct = this.isDistinct ? \"DISTINCT \" : \"\";\n\n // If no specific columns, select all — qualify with main table when joins present\n if (this.selectColumns.length === 0 && this.selectRaw.length === 0) {\n return this.hasJoins\n ? `SELECT ${distinct}${this.quoteTable(this.table)}.*`\n : `SELECT ${distinct}*`;\n }\n\n const columns: string[] = [];\n\n // Add selected columns — prefix with main table when joins present to avoid ambiguity\n for (const col of this.selectColumns) {\n if (!this.deselectColumns.includes(col)) {\n columns.push(this.parseColumnIdentifier(col, this.table, this.alias));\n }\n }\n\n // Add raw expressions\n columns.push(...this.selectRaw);\n\n return `SELECT ${distinct}${columns.join(\", \")}`;\n }\n\n /**\n * Add a placeholder and parameter.\n *\n * @param value - Parameter value\n * @returns Placeholder string ($1, $2, etc.)\n */\n private addParam(value: unknown): string {\n this.params.push(value);\n return this.dialect.placeholder(this.paramIndex++);\n }\n\n /**\n * Absorb a sub-parser's params into this parser, returning a rewriter\n * function that translates the sub-parser's `$N` placeholders into freshly\n * numbered placeholders in this parser's namespace.\n *\n * Required whenever sub-parser SQL fragments (e.g. WHERE clauses produced\n * for a `joinWith` constraint) are spliced into the outer query — without\n * the rewrite the embedded `$N` references point at parameters this\n * parser has no knowledge of, leaving them dangling in the final SQL.\n *\n * The sub-parser's `params[i]` corresponds to its `$(i+1)` placeholder\n * (its `paramIndex` always starts at 1), so we map sequentially.\n *\n * @example\n * const renumber = this.absorbSubParserParams(subParser);\n * const fragment = renumber(subParser.whereClauses.join(\" \"));\n */\n private absorbSubParserParams(subParser: PostgresQueryParser): (sql: string) => string {\n if (subParser.params.length === 0) {\n return (sql) => sql;\n }\n\n const oldToNew = new Map<string, string>();\n\n for (let index = 0; index < subParser.params.length; index++) {\n const newPlaceholder = this.addParam(subParser.params[index]);\n oldToNew.set(`$${index + 1}`, newPlaceholder);\n }\n\n return (sql) => sql.replace(/\\$\\d+/g, (match) => oldToNew.get(match) ?? match);\n }\n\n /**\n * Process a basic WHERE operation.\n *\n * Delegates to specialised processors for operators that require more than a\n * single placeholder (between, in, like-variants, exists, etc.).\n */\n private processWhere(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n // `where(q => ...)` / `orWhere(q => ...)` record a nested group\n if (data.nested !== undefined) {\n return this.processNested(data.nested, boolean, false, \"where\");\n }\n\n const field = data.field as string;\n const operator = (data.operator as WhereOperator) ?? \"=\";\n const value = data.value;\n\n // Delegate to specialised processors for operators that need it. The\n // `boolean` connective is forwarded so `orWhere(f, \"in\", …)` stays an OR.\n const range = value as [unknown, unknown];\n const like = { field, pattern: value as string };\n\n switch (operator) {\n case \"between\":\n return this.processWhereBetween({ field, range }, false, boolean);\n case \"notBetween\":\n return this.processWhereBetween({ field, range }, true, boolean);\n case \"in\":\n return this.processWhereIn({ field, values: value as unknown[] }, false, boolean);\n case \"notIn\":\n return this.processWhereIn({ field, values: value as unknown[] }, true, boolean);\n case \"like\":\n case \"ilike\":\n return this.processWhereLike(like, false, boolean);\n case \"startsWith\":\n case \"endsWith\":\n return this.processWhereLike(like, false, boolean, operator);\n case \"notLike\":\n return this.processWhereLike(like, true, boolean);\n case \"notStartsWith\":\n return this.processWhereLike(like, true, boolean, \"startsWith\");\n case \"notEndsWith\":\n return this.processWhereLike(like, true, boolean, \"endsWith\");\n case \"exists\":\n // Raw strings are never inlined into SQL (injection); a sub-query must\n // arrive as an already-parameterised builder, not text.\n throw new Error(\n 'PostgresQueryParser: where(field, \"exists\", value) does not accept a raw SQL string; use a query builder or callback',\n );\n }\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n\n if (value === null) {\n const nullOperator = operator === \"!=\" ? \"IS NOT NULL\" : \"IS NULL\";\n return this.addWhereClause(`${quotedField} ${nullOperator}`, boolean);\n }\n\n // Simple single-value operator — fall through to generic path\n const placeholder = this.addParam(value);\n this.addWhereClause(`${quotedField} ${this.mapOperator(operator)} ${placeholder}`, boolean);\n }\n\n /**\n * Process a nested group of operations as `(<group>)` / `NOT (<group>)`.\n *\n * The group is parsed by a sub-parser; its params are renumbered into this\n * parser's namespace.\n */\n private processNested(\n nested: unknown,\n boolean: \"AND\" | \"OR\",\n negate: boolean,\n opType: string,\n ): void {\n const subParser = new PostgresQueryParser({\n table: this.table,\n alias: this.alias,\n dialect: this.dialect,\n operations: (nested as PostgresParserOperation[] | undefined) ?? [],\n });\n subParser.parse();\n\n if (subParser.whereClauses.length === 0) {\n throw new Error(`PostgresQueryParser: \"${opType}\" received an empty condition group`);\n }\n\n const renumber = this.absorbSubParserParams(subParser);\n const group = `(${renumber(subParser.whereClauses.join(\" \"))})`;\n\n this.addWhereClause(negate ? `NOT ${group}` : group, boolean);\n }\n\n /**\n * Compare the date portion of a column (time ignored).\n */\n private processWhereDate(data: Record<string, unknown>, operator: string): void {\n const quotedField = this.parseColumnIdentifier(data.field as string, this.table, this.alias);\n const placeholder = this.addParam(data.value);\n\n this.addWhereClause(`${quotedField}::date ${operator} ${placeholder}::date`, \"AND\");\n }\n\n /**\n * Date portion of a column within an inclusive [from, to] range.\n */\n private processWhereDateBetween(data: Record<string, unknown>): void {\n const range = data.range as [unknown, unknown];\n const quotedField = this.parseColumnIdentifier(data.field as string, this.table, this.alias);\n const from = this.addParam(range[0]);\n const to = this.addParam(range[1]);\n\n this.addWhereClause(`${quotedField}::date BETWEEN ${from}::date AND ${to}::date`, \"AND\");\n }\n\n /**\n * Process a raw WHERE operation.\n */\n private processWhereRaw(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.addWhereClause(processed, boolean);\n }\n\n /**\n * Process WHERE IN / NOT IN.\n */\n private processWhereIn(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const values = data.values as unknown[];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const operator = negate ? \"!= ALL\" : \"= ANY\";\n const placeholder = this.addParam(values);\n\n this.addWhereClause(`${quotedField} ${operator}(${placeholder})`, boolean);\n }\n\n /**\n * Process WHERE NULL / NOT NULL.\n */\n private processWhereNull(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const clause = negate ? `${quotedField} IS NOT NULL` : `${quotedField} IS NULL`;\n this.addWhereClause(clause, boolean);\n }\n\n /**\n * Process WHERE BETWEEN / NOT BETWEEN.\n */\n private processWhereBetween(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const range = data.range as [unknown, unknown];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const placeholder1 = this.addParam(range[0]);\n const placeholder2 = this.addParam(range[1]);\n const keyword = negate ? \"NOT BETWEEN\" : \"BETWEEN\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder1} AND ${placeholder2}`, boolean);\n }\n\n /**\n * Process WHERE LIKE / NOT LIKE.\n *\n * Cross-driver semantics (matches MongoDB): `%` is the only wildcard, `_`\n * and backslash are literal, and a pattern with no `%` matches as a\n * substring. `startsWith` / `endsWith` anchor the escaped text explicitly.\n * A `RegExp` has no SQL equivalent and is rejected.\n */\n private processWhereLike(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n modeArg?: \"startsWith\" | \"endsWith\",\n ): void {\n const field = data.field as string;\n const mode = modeArg ?? (data.mode as \"startsWith\" | \"endsWith\" | undefined);\n\n // A RegExp maps to the POSIX case-insensitive match operators\n if (data.isRegExp === true || (data.pattern as unknown) instanceof RegExp) {\n const source =\n (data.pattern as unknown) instanceof RegExp\n ? (data.pattern as unknown as RegExp).source\n : String(data.pattern);\n const regexField = this.parseColumnIdentifier(field, this.table, this.alias);\n const regexPlaceholder = this.addParam(source);\n\n return this.addWhereClause(\n `${regexField} ${negate ? \"!~*\" : \"~*\"} ${regexPlaceholder}`,\n boolean,\n );\n }\n // Mode ops carry the raw user value; `pattern` is only the display shape\n const pattern = String(mode && data.value !== undefined ? data.value : data.pattern);\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const { operator } = this.dialect.likePattern(pattern, true);\n // Escape `\\` and `_` (literal). `%` stays the wildcard of a plain LIKE\n // pattern, but startsWith / endsWith values are literal text, so it is\n // escaped there too.\n let escaped = pattern.replace(/\\\\/g, \"\\\\\\\\\").replace(/_/g, \"\\\\_\");\n\n if (mode) {\n escaped = escaped.replace(/%/g, \"\\\\%\");\n }\n\n let finalPattern = escaped;\n\n if (mode === \"startsWith\") {\n finalPattern = `${escaped}%`;\n } else if (mode === \"endsWith\") {\n finalPattern = `%${escaped}`;\n } else if (!escaped.includes(\"%\")) {\n finalPattern = `%${escaped}%`;\n }\n\n const placeholder = this.addParam(finalPattern);\n const keyword = negate ? `NOT ${operator}` : operator;\n\n // Mode variants state the escape character explicitly\n const escapeClause = mode ? \" ESCAPE '\\\\'\" : \"\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder}${escapeClause}`, boolean);\n }\n\n /**\n * Process WHERE column comparison.\n */\n private processWhereColumn(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const first = data.first as string;\n const operator = this.mapOperator(((data.operator as string) ?? \"=\") as WhereOperator);\n const second = data.second as string;\n\n // Both sides may need qualification if unambiguous table is unclear\n const quotedFirst = this.parseColumnIdentifier(first, this.table, this.alias);\n const quotedSecond = this.parseColumnIdentifier(second, this.table, this.alias);\n\n this.addWhereClause(`${quotedFirst} ${operator} ${quotedSecond}`, boolean);\n }\n\n /**\n * Process WHERE JSON contains.\n */\n private processWhereJsonContains(data: Record<string, unknown>, negate: boolean): void {\n const path = data.path as string;\n const value = data.value;\n\n const quotedPath = this.parseColumnIdentifier(path, this.table, this.alias);\n const operator = negate ? \"NOT @>\" : \"@>\";\n const placeholder = this.addParam(JSON.stringify(value));\n\n this.addWhereClause(`${quotedPath} ${operator} ${placeholder}::jsonb`, \"AND\");\n }\n\n /** Text search config name; anything but a plain identifier falls back to english. */\n private safeLanguage(language: unknown): string {\n return typeof language === \"string\" && /^[a-z_]+$/i.test(language) ? language : \"english\";\n }\n\n /**\n * Process full-text search WHERE.\n */\n private processWhereFullText(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n const query = data.query as string;\n const language = this.safeLanguage(data.language);\n const weights = (data.weights as Record<string, string> | undefined) ?? {};\n\n // Same expression as `createFullTextIndex`, so the GIN index is used and a\n // NULL column doesn't null the whole vector.\n const tsVectors = fields\n .map((f) => {\n const weight = /^[A-D]$/.test(weights[f] ?? \"\") ? weights[f] : \"A\";\n\n return `setweight(to_tsvector('${language}', COALESCE(${this.dialect.quoteIdentifier(f)}, '')), '${weight}')`;\n })\n .join(\" || \");\n\n const placeholder = this.addParam(query);\n this.addWhereClause(`(${tsVectors}) @@ plainto_tsquery('${language}', ${placeholder})`, \"AND\");\n }\n\n /**\n * Process SELECT operation.\n */\n private processSelect(data: Record<string, unknown>): void {\n const fields = data.fields as string[] | Record<string, string>;\n\n if (Array.isArray(fields)) {\n this.selectColumns.push(...fields);\n } else {\n // Handle aliases: { field: 'alias' }\n for (const [field, alias] of Object.entries(fields)) {\n const quotedField = this.dialect.quoteIdentifier(field);\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n this.selectRaw.push(`${quotedField} AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process raw SELECT expression.\n */\n private processSelectRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string | Record<string, unknown>;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n if (typeof expression === \"string\") {\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.selectRaw.push(processed);\n } else {\n // Handle object expressions (for compatibility)\n for (const [alias, expr] of Object.entries(expression)) {\n this.selectRaw.push(`${expr} AS ${this.dialect.quoteIdentifier(alias)}`);\n }\n }\n }\n\n /**\n * Process DESELECT operation.\n */\n private processDeselect(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n this.deselectColumns.push(...fields);\n }\n\n /**\n * Process SELECT for related columns (joinWith).\n *\n * - hasOne / belongsTo → LEFT JOIN + row_to_json (single object)\n * - hasMany → correlated subquery with json_agg (array, no row explosion)\n *\n * @example hasMany correlated subquery:\n * (SELECT json_agg(row_to_json(a.*))\n * FROM \"chat_message_actions\" a\n * WHERE a.\"chat_message_id\" = \"chat_messages\".\"id\") AS \"actions\"\n *\n * @example hasOne/belongsTo row_to_json:\n * row_to_json(\"organizationAiModel\".*) AS \"organizationAiModel\"\n */\n private processSelectRelatedColumns(data: Record<string, unknown>): void {\n const alias = data.alias as string;\n const select = data.select as string[] | undefined;\n const relationType = data.type as string | undefined;\n const constraintOps = data.constraintOps as PostgresParserOperation[] | undefined;\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n const quotedTable = this.quoteTable(this.table);\n\n const hasExplicitSelect = this.selectColumns.length > 0;\n if (!hasExplicitSelect && !this.selectRaw.includes(`${quotedTable}.*`)) {\n this.selectRaw.unshift(`${quotedTable}.*`);\n }\n\n if (relationType === \"hasMany\") {\n // Correlated subquery — no JOIN, no row explosion, returns a JSON array.\n const relatedTable = data.table as string;\n const foreignKey = data.foreignKey as string;\n const localKey = data.localKey as string;\n const quotedRelatedTable = this.quoteTable(relatedTable);\n const quotedForeignKey = this.dialect.quoteIdentifier(foreignKey);\n const quotedLocalKey = this.dialect.quoteIdentifier(localKey);\n const quotedMainTable = this.quoteTable(this.table);\n\n let innerSelect: string;\n if (select && select.length > 0) {\n // Pick specific columns: json_agg(json_build_object('id', a.\"id\", ...))\n const fields = select\n .map((col) => `'${escapeSqlLiteral(col)}', a.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n innerSelect = `json_agg(json_build_object(${fields}))`;\n } else {\n innerSelect = `json_agg(row_to_json(a.*))`;\n }\n\n // Build the base FK condition\n const fkCondition = `a.${quotedForeignKey} = ${quotedMainTable}.${quotedLocalKey}`;\n\n // Merge constraint ops (where / order / limit) from a sub-parser.\n // The sub-parser numbers its own placeholders starting at $1; we must\n // absorb its params into this parser and renumber the embedded SQL,\n // otherwise $N references in the merged clauses dangle.\n let extraWhere = \"\";\n let orderBy = \"\";\n let limitClause = \"\";\n\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: relatedTable,\n alias: \"a\",\n dialect: this.dialect,\n operations: constraintOps,\n });\n subParser.parse();\n\n const renumber = this.absorbSubParserParams(subParser);\n\n if (subParser.whereClauses.length > 0) {\n extraWhere = ` AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n if (subParser.orderClauses.length > 0) {\n orderBy = ` ORDER BY ${renumber(subParser.orderClauses.join(\", \"))}`;\n }\n if (subParser.limitValue !== undefined) {\n limitClause = ` ${this.dialect.limitOffset(subParser.limitValue)}`;\n }\n }\n\n // Wrap with ORDER/LIMIT only when constraints are present (json_agg ignores ORDER)\n const innerQuery =\n orderBy || limitClause\n ? `SELECT row_to_json(sub.*) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub`\n : `SELECT ${innerSelect.replace(\"json_agg(\", \"\").replace(/\\)$/, \"\")} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}`;\n\n const aggregated =\n orderBy || limitClause\n ? `(SELECT json_agg(row_to_json(sub.*)) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub) AS ${quotedAlias}`\n : `(SELECT ${innerSelect} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}) AS ${quotedAlias}`;\n\n this.selectRaw.push(aggregated);\n } else {\n // hasOne / belongsTo — single object via LEFT JOIN + row_to_json.\n // If constraint provides an explicit select list, prefer it over data.select\n let effectiveSelect = select;\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: (data.table as string) ?? alias,\n alias,\n dialect: this.dialect,\n operations: constraintOps,\n });\n subParser.parse();\n if (subParser.selectColumns.length > 0) {\n effectiveSelect = subParser.selectColumns;\n }\n }\n\n if (effectiveSelect && effectiveSelect.length > 0) {\n const selectedColumns = effectiveSelect\n .map((col) => `${quotedAlias}.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n this.selectRaw.push(\n `row_to_json((SELECT d FROM (SELECT ${selectedColumns}) d)) AS ${quotedAlias}`,\n );\n } else {\n this.selectRaw.push(`row_to_json(${quotedAlias}.*) AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process JOIN operation with smart field detection.\n *\n * Handles both regular columns and JSONB nested paths:\n * - \"id\" → \"table\".\"id\" (auto-prefixed)\n * - \"users.id\" → \"users\".\"id\" (explicit table)\n * - \"createdBy.id\" → \"table\".\"createdBy\"->>'id' (JSONB path)\n * - \"posts.createdBy.id\" → \"posts\".\"createdBy\"->>'id' (JSONB with table)\n */\n private processJoin(data: Record<string, unknown>, type: string): void {\n const options = data as unknown as\n | JoinOptions\n | { table: string; localField: string; foreignField: string };\n\n const joinTable = \"table\" in options ? options.table : \"\";\n const localField = \"localField\" in options ? options.localField : \"\";\n const foreignField = \"foreignField\" in options ? options.foreignField : \"\";\n const alias = \"alias\" in options ? options.alias : undefined;\n const constraintOps = (data.constraintOps as PostgresParserOperation[] | undefined) ?? [];\n\n const quotedTable = this.quoteTable(joinTable);\n const tableRef = alias\n ? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}`\n : quotedTable;\n const tableAlias = alias ?? joinTable;\n\n // Track the joined table for reference detection\n this.joinedTables.add(joinTable);\n if (alias) {\n this.joinedTables.add(alias);\n }\n\n // Parse local field (belongs to main table)\n const quotedLocal = this.parseColumnIdentifier(localField!, this.table, this.alias);\n\n // Parse foreign field (belongs to join table)\n const quotedForeign = this.parseColumnIdentifier(foreignField!, joinTable, tableAlias);\n\n // Build the basic ON condition. For hasOne/belongsTo joinWith with a\n // constraint callback, append the constraint's where-clauses to the ON\n // clause (preserves LEFT JOIN semantics — main rows stay even when no\n // related row passes the constraint). orderBy/limit on the constraint\n // are silently dropped — they have no meaning on a single-row LEFT JOIN.\n let onClause = `${quotedLocal} = ${quotedForeign}`;\n\n if (constraintOps.length > 0) {\n const whereOps = constraintOps.filter(\n (op) => op.type.startsWith(\"where\") || op.type.startsWith(\"orWhere\"),\n );\n\n if (whereOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: joinTable,\n alias: tableAlias,\n dialect: this.dialect,\n operations: whereOps,\n });\n subParser.parse();\n\n if (subParser.whereClauses.length > 0) {\n const renumber = this.absorbSubParserParams(subParser);\n onClause = `${onClause} AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n }\n }\n\n this.joinClauses.push(`${type} JOIN ${tableRef} ON ${onClause}`);\n }\n\n /**\n * Parse a column identifier with smart detection for table prefixes and JSONB paths.\n *\n * Supports an optional explicit cast suffix using PostgreSQL's `::type` syntax,\n * which is the only way to coerce a JSONB-extracted value (always returned as\n * `text`) into another type. The parser does NOT infer casts from key names —\n * coercion is always opt-in by the caller.\n *\n * @param field - The field string. Examples:\n * - \"id\" → `\"table\".\"id\"`\n * - \"users.id\" → `\"users\".\"id\"`\n * - \"createdBy.id\" → `\"table\".\"createdBy\"->>'id'`\n * - \"createdBy.id::int\" → `(\"table\".\"createdBy\"->>'id')::int`\n * - \"meta.score::numeric\" → `(\"table\".\"meta\"->>'score')::numeric`\n * - \"posts.createdBy.id::uuid\" → `(\"posts\".\"createdBy\"->>'id')::uuid`\n * @param defaultTable - Default table to use if no prefix\n * @param tableAlias - Table alias to use if provided\n * @returns Properly quoted SQL expression\n */\n private parseColumnIdentifier(field: string, defaultTable: string, tableAlias?: string): string {\n if (!field) return \"\";\n\n // Extract optional explicit cast suffix: \"createdBy.id::int\" → path \"createdBy.id\", cast \"int\"\n let cast: string | undefined;\n const castIndex = field.indexOf(\"::\");\n if (castIndex !== -1) {\n cast = field.slice(castIndex + 2).trim();\n field = field.slice(0, castIndex);\n }\n\n const effectiveTable = tableAlias ?? defaultTable;\n const parts = field.split(\".\");\n\n let expression: string;\n\n if (parts.length === 1) {\n // Single part: just a column name, prefix with default table\n expression = `${this.quoteTable(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;\n } else if (parts.length === 2) {\n // Two parts: could be \"table.column\" or \"jsonbColumn.key\"\n const [first, second] = parts;\n\n // `parts.length === 2` above guarantees both, but the compiler does not\n // narrow a destructured element from a length check. Throwing rather\n // than defaulting to \"\": every branch below feeds `quoteIdentifier` or a\n // JSONB path, so an empty segment would emit SYNTACTICALLY VALID SQL\n // against the wrong identifier — a query that silently returns the wrong\n // rows is far worse than one that refuses to build.\n if (first === undefined || second === undefined) {\n throw new Error(`Cannot parse field path \"${field}\": expected two non-empty segments.`);\n }\n\n if (this.isTableReference(first)) {\n // It's table.column - regular column reference\n expression = `${this.quoteTable(first)}.${this.dialect.quoteIdentifier(second)}`;\n } else {\n // It's jsonbColumn.key - JSONB path\n expression = this.buildJsonbPath(effectiveTable, first, [second]);\n }\n } else {\n // Three or more parts: \"table.jsonbColumn.key...\" or \"jsonbColumn.key1.key2...\"\n const [first, second, ...rest] = parts;\n\n // Same reasoning as the two-part branch: this arm runs only for three or\n // more parts, so both exist — and an empty identifier reaching the SQL\n // is a wrong-rows bug, not a crash.\n if (first === undefined || second === undefined) {\n throw new Error(\n `Cannot parse field path \"${field}\": expected at least three non-empty segments.`,\n );\n }\n\n if (this.isTableReference(first)) {\n // First part is table: \"posts.createdBy.id\" → table is \"posts\", JSONB is \"createdBy.id\"\n expression = this.buildJsonbPath(first, second, rest);\n } else {\n // No table prefix: \"createdBy.address.city\" → use default table\n expression = this.buildJsonbPath(effectiveTable, first, [second, ...rest]);\n }\n }\n\n if (cast) {\n if (!SAFE_CAST_PATTERN.test(cast)) {\n throw new Error(`PostgresQueryParser: unsafe cast type \"${cast}\"`);\n }\n\n return `(${expression})::${cast}`;\n }\n\n return expression;\n }\n\n /**\n * Check if a string is a table reference (main table or join table).\n */\n private isTableReference(name: string): boolean {\n if (name === this.table || name === this.alias) {\n return true;\n }\n\n if (this.joinedTables.has(name)) {\n return true;\n }\n\n return false;\n }\n\n private quoteTable(table: string): string {\n return this.dialect instanceof PostgresDialect\n ? this.dialect.quoteTableIdentifier(table)\n : this.dialect.quoteIdentifier(table);\n }\n\n /**\n * Build a JSONB path expression.\n *\n * @param table - Table name\n * @param column - JSONB column name\n * @param path - Array of nested keys\n * @returns PostgreSQL JSONB path expression\n *\n * Returns the raw JSONB path expression — extracted values are always `text`\n * (`->>` returns text). Callers that need a different type must request an\n * explicit cast via the `::type` suffix in `parseColumnIdentifier`.\n *\n * @example\n * buildJsonbPath(\"posts\", \"createdBy\", [\"id\"])\n * // Returns: \"posts\".\"createdBy\"->>'id'\n *\n * buildJsonbPath(\"posts\", \"createdBy\", [\"address\", \"city\"])\n * // Returns: \"posts\".\"createdBy\"->'address'->>'city'\n */\n private buildJsonbPath(table: string, column: string, path: string[]): string {\n const quotedTable = this.quoteTable(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n\n if (path.length === 0) {\n return `${quotedTable}.${quotedColumn}`;\n }\n\n // Build JSONB path: use -> for intermediate keys, ->> for final key (returns text)\n let expression = `${quotedTable}.${quotedColumn}`;\n\n for (let i = 0; i < path.length; i++) {\n const isLast = i === path.length - 1;\n const operator = isLast ? \"->>\" : \"->\";\n expression += `${operator}'${escapeSqlLiteral(path[i] as string)}'`;\n }\n\n return expression;\n }\n\n /**\n * Process CROSS JOIN operation.\n */\n private processCrossJoin(data: Record<string, unknown>): void {\n const table = data.table as string;\n const quotedTable = this.quoteTable(table);\n this.joinClauses.push(`CROSS JOIN ${quotedTable}`);\n }\n\n /**\n * Process raw JOIN expression.\n */\n private processJoinRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.joinClauses.push(processed);\n }\n\n /**\n * Process ORDER BY operation.\n */\n private processOrderBy(data: Record<string, unknown>): void {\n const field = data.field as string;\n const rawDirection = String(data.direction ?? \"asc\").trim();\n\n if (!SAFE_DIRECTION_PATTERN.test(rawDirection)) {\n throw new Error(`PostgresQueryParser: invalid orderBy direction \"${rawDirection}\"`);\n }\n\n const direction = rawDirection.replace(/\\s+/g, \" \").toUpperCase();\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n this.orderClauses.push(`${quotedField} ${direction}`);\n }\n\n /**\n * Process raw ORDER BY expression.\n */\n private processOrderByRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.orderClauses.push(processed);\n }\n\n /**\n * Process GROUP BY operation.\n */\n private processGroupBy(data: Record<string, unknown>): void {\n const fields = data.fields as string | string[];\n const columns = Array.isArray(fields) ? fields : [fields];\n this.groupColumns.push(...columns);\n }\n\n /** Process a raw GROUP BY expression, binding `?` placeholders positionally. */\n private processGroupByRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.groupRawExpressions.push(expression);\n }\n\n /**\n * Process HAVING operation.\n */\n private processHaving(data: Record<string, unknown>): void {\n const field = data.field as string;\n const operator = this.mapOperator(((data.operator as string) ?? \"=\") as WhereOperator);\n const value = data.value;\n\n const quotedField = this.dialect.quoteIdentifier(field);\n const placeholder = this.addParam(value);\n\n this.havingClauses.push(`${quotedField} ${operator} ${placeholder}`);\n }\n\n /**\n * Process a raw HAVING expression, threading `?` placeholders into\n * positional params the same way `processWhereRaw` / `processSelectRaw` do.\n * Without this, bindings on a `havingRaw` op are silently dropped.\n */\n private processHavingRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.havingClauses.push(expression);\n }\n\n /**\n * Add a WHERE clause with boolean operator.\n */\n private addWhereClause(clause: string, boolean: \"AND\" | \"OR\"): void {\n if (this.whereClauses.length === 0) {\n this.whereClauses.push(clause);\n } else {\n this.whereClauses.push(`${boolean} ${clause}`);\n }\n }\n\n /**\n * Map simple Cascade operators to their SQL equivalents.\n *\n * Complex operators (between, in, like-variants, exists) are handled by\n * dedicated processors and should never reach this method.\n */\n private mapOperator(operator: WhereOperator): string {\n const mapping: Record<string, string> = {\n \"=\": \"=\",\n \"!=\": \"!=\",\n \"<>\": \"<>\",\n \">\": \">\",\n \">=\": \">=\",\n \"<\": \"<\",\n \"<=\": \"<=\",\n like: \"LIKE\",\n notlike: \"NOT LIKE\",\n ilike: \"ILIKE\",\n };\n\n const mapped = mapping[String(operator).toLowerCase()];\n\n if (mapped === undefined) {\n throw new Error(`PostgresQueryParser: unsupported operator \"${operator}\"`);\n }\n\n return mapped;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-query-parser.mjs","names":[],"sources":["../../../../../../../../../packages/cascade/src/drivers/postgres/postgres-query-parser.ts"],"sourcesContent":["/**\n * PostgreSQL Query Parser\n *\n * Translates Cascade query operations into PostgreSQL SQL queries.\n * Mirrors the MongoQueryParser pattern but generates SQL instead\n * of MongoDB aggregation pipelines.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverQuery,\n JoinOptions,\n WhereOperator,\n} from \"../../contracts/query-builder.contract\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\n\nimport { escapeSqlLiteral, PostgresDialect } from \"./postgres-dialect\";\n\n/** Allowed `::cast` type names, e.g. `int`, `numeric(10,2)`, `text[]`. */\nconst SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_ ]*(\\(\\d+(,\\d+)?\\))?(\\[\\])?$/i;\n\n/** Allowed orderBy direction, with optional NULLS placement. */\nconst SAFE_DIRECTION_PATTERN = /^(asc|desc)(\\s+nulls\\s+(first|last))?$/i;\n\n/**\n * Operation types supported by the query parser.\n */\nexport type PostgresOperationType =\n // WHERE operations\n | \"where\"\n | \"orWhere\"\n | \"whereRaw\"\n | \"orWhereRaw\"\n | \"whereIn\"\n | \"whereNotIn\"\n | \"whereNull\"\n | \"whereNotNull\"\n | \"whereBetween\"\n | \"whereNotBetween\"\n | \"orWhereIn\"\n | \"orWhereNotIn\"\n | \"orWhereNull\"\n | \"orWhereNotNull\"\n | \"orWhereBetween\"\n | \"whereLike\"\n | \"whereNotLike\"\n | \"whereColumn\"\n | \"orWhereColumn\"\n | \"whereExists\"\n | \"whereNotExists\"\n | \"whereNot\"\n | \"orWhereNot\"\n | \"whereDate\"\n | \"whereDateBefore\"\n | \"whereDateAfter\"\n | \"whereDateBetween\"\n | \"whereJsonContains\"\n | \"whereJsonDoesntContain\"\n | \"whereFullText\"\n // SELECT operations\n | \"select\"\n | \"selectRaw\"\n | \"deselect\"\n // JOIN operations\n | \"join\"\n | \"leftJoin\"\n | \"rightJoin\"\n | \"innerJoin\"\n | \"fullJoin\"\n | \"crossJoin\"\n | \"joinRaw\"\n // ORDER operations\n | \"orderBy\"\n | \"orderByRaw\"\n // GROUP operations\n | \"groupBy\"\n | \"groupByRaw\"\n | \"having\"\n | \"havingRaw\"\n // LIMIT operations\n | \"limit\"\n | \"offset\"\n // Row locking\n | \"lock\"\n // RELATION operations\n | \"has\"\n | \"whereHas\"\n | \"doesntHave\"\n | \"whereDoesntHave\"\n // joinWith operations\n | \"selectRelatedColumns\"\n // Builder-level metadata (consumed before parsing)\n | \"joinWith\"\n | \"cursor\"\n // Other\n | \"distinct\";\n\n/**\n * Internal operation representation.\n */\nexport type PostgresParserOperation = {\n /** Operation type */\n readonly type: PostgresOperationType;\n /** Operation data/parameters */\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Parser configuration options.\n */\nexport type PostgresParserOptions = {\n /** Target table name */\n readonly table: string;\n /** Table alias (optional) */\n readonly alias?: string;\n /** Operations to parse */\n readonly operations: PostgresParserOperation[];\n /** SQL dialect for syntax generation */\n readonly dialect?: SqlDialectContract;\n /** Factory for creating sub-parsers (for nested queries) */\n readonly createSubParser?: (table: string) => PostgresQueryParser;\n};\n\n/**\n * PostgreSQL Query Parser.\n *\n * Converts a list of query operations into a SQL query string with parameters.\n * Handles SELECT, WHERE, JOIN, ORDER BY, GROUP BY, LIMIT/OFFSET clauses.\n *\n * @example\n * ```typescript\n * const parser = new PostgresQueryParser({\n * table: 'users',\n * operations: [\n * { type: 'where', data: { field: 'name', operator: '=', value: 'Alice' } },\n * { type: 'orderBy', data: { field: 'createdAt', direction: 'desc' } },\n * { type: 'limit', data: { value: 10 } }\n * ]\n * });\n *\n * const { sql, params } = parser.parse();\n * // sql: 'SELECT * FROM \"users\" WHERE \"name\" = $1 ORDER BY \"createdAt\" DESC LIMIT 10'\n * // params: ['Alice']\n * ```\n */\nexport class PostgresQueryParser {\n /**\n * Target table name.\n */\n private readonly table: string;\n\n /**\n * Table alias.\n */\n private readonly alias?: string;\n\n /**\n * Operations to process.\n */\n private readonly operations: PostgresParserOperation[];\n\n /**\n * SQL dialect for syntax.\n */\n private readonly dialect: SqlDialectContract;\n\n /**\n * Current parameter index (1-based for PostgreSQL).\n */\n private paramIndex = 1;\n\n /**\n * Collected parameters.\n */\n private readonly params: unknown[] = [];\n\n /**\n * SELECT columns.\n */\n public selectColumns: string[] = [];\n\n /**\n * Deselected (excluded) columns.\n */\n private deselectColumns: string[] = [];\n\n /**\n * Raw SELECT expressions.\n */\n private selectRaw: string[] = [];\n\n /**\n * WHERE clauses.\n */\n public whereClauses: string[] = [];\n\n /**\n * JOIN clauses.\n */\n private joinClauses: string[] = [];\n\n /**\n * ORDER BY clauses.\n */\n public orderClauses: string[] = [];\n\n /**\n * GROUP BY columns (bare identifiers — get quoted).\n */\n private groupColumns: string[] = [];\n\n /**\n * Raw GROUP BY expressions (emitted verbatim — e.g. a `date_trunc(...)`\n * bucket). Kept separate from `groupColumns` so they bypass identifier\n * quoting while still landing in the same `GROUP BY` clause.\n */\n private groupRawExpressions: string[] = [];\n\n /**\n * HAVING clauses.\n */\n private havingClauses: string[] = [];\n\n /**\n * LIMIT value.\n */\n public limitValue?: number;\n\n /**\n * OFFSET value.\n */\n public offsetValue?: number;\n\n /**\n * DISTINCT flag.\n */\n private isDistinct = false;\n\n /**\n * Row-locking clause (`FOR UPDATE [SKIP LOCKED | NOWAIT]`). Appended after\n * LIMIT/OFFSET — the locking clause is last in PostgreSQL's SELECT grammar.\n */\n private lockClause = \"\";\n\n /**\n * Whether the query has any JOIN operations (pre-scanned before processing).\n * Used by qualifyColumn() to decide whether to prefix columns with the main table.\n */\n private hasJoins = false;\n\n /**\n * Tracked joined tables (for table reference detection).\n */\n private readonly joinedTables = new Set<string>();\n\n /**\n * Create a new query parser.\n *\n * @param options - Parser configuration\n */\n public constructor(options: PostgresParserOptions) {\n this.table = options.table;\n this.alias = options.alias;\n this.operations = options.operations;\n this.dialect = options.dialect ?? new PostgresDialect();\n }\n\n /**\n * Parse all operations and build the SQL query.\n *\n * @returns DriverQuery with `query` (SQL string) and `bindings` (parameter values)\n */\n public parse(): DriverQuery {\n // Pre-scan for any join operations so qualifyColumn() can prefix columns\n // correctly even when WHERE clauses appear before JOINs in the operations list.\n const JOIN_TYPES = new Set([\n \"join\",\n \"leftJoin\",\n \"rightJoin\",\n \"innerJoin\",\n \"fullJoin\",\n \"crossJoin\",\n \"joinRaw\",\n ]);\n\n this.hasJoins = false;\n\n // First pass: locate all joins and populate joinedTables for accurate JSON path detection\n for (const operation of this.operations) {\n if (JOIN_TYPES.has(operation.type)) {\n this.hasJoins = true;\n const data = operation.data as any;\n const joinTable = data.table as string;\n const alias = data.alias as string;\n\n if (joinTable) this.joinedTables.add(joinTable);\n if (alias) this.joinedTables.add(alias);\n }\n }\n\n // Process each operation\n for (const operation of this.operations) {\n this.processOperation(operation);\n }\n\n // Build the final SQL query\n const query = this.buildSql();\n\n return { query, bindings: this.params };\n }\n\n /**\n * Get a formatted string representation of the query.\n *\n * @returns Formatted SQL with bindings\n */\n public toPrettyString(): string {\n const { query = \"\", bindings } = this.parse();\n return `${query}\\n-- Bindings: ${JSON.stringify(bindings ?? [])}`;\n }\n\n /**\n * Process a single operation.\n *\n * @param operation - The operation to process\n */\n private processOperation(operation: PostgresParserOperation): void {\n const { type, data } = operation;\n\n switch (type) {\n // WHERE operations\n case \"where\":\n this.processWhere(data, \"AND\");\n break;\n case \"orWhere\":\n this.processWhere(data, \"OR\");\n break;\n case \"whereRaw\":\n this.processWhereRaw(data, \"AND\");\n break;\n case \"orWhereRaw\":\n this.processWhereRaw(data, \"OR\");\n break;\n case \"whereIn\":\n this.processWhereIn(data, false);\n break;\n case \"whereNotIn\":\n this.processWhereIn(data, true);\n break;\n case \"orWhereIn\":\n this.processWhereIn(data, false, \"OR\");\n break;\n case \"orWhereNotIn\":\n this.processWhereIn(data, true, \"OR\");\n break;\n case \"whereNull\":\n this.processWhereNull(data, false);\n break;\n case \"whereNotNull\":\n this.processWhereNull(data, true);\n break;\n case \"orWhereNull\":\n this.processWhereNull(data, false, \"OR\");\n break;\n case \"orWhereNotNull\":\n this.processWhereNull(data, true, \"OR\");\n break;\n case \"orWhereBetween\":\n this.processWhereBetween(data, false, \"OR\");\n break;\n case \"whereBetween\":\n this.processWhereBetween(data, false);\n break;\n case \"whereNotBetween\":\n this.processWhereBetween(data, true);\n break;\n case \"whereLike\":\n this.processWhereLike(data, false);\n break;\n case \"whereNotLike\":\n this.processWhereLike(data, true);\n break;\n case \"whereColumn\":\n this.processWhereColumn(data, \"AND\");\n break;\n case \"orWhereColumn\":\n this.processWhereColumn(data, \"OR\");\n break;\n case \"whereJsonContains\":\n this.processWhereJsonContains(data, false);\n break;\n case \"whereJsonDoesntContain\":\n this.processWhereJsonContains(data, true);\n break;\n case \"whereFullText\":\n this.processWhereFullText(data);\n break;\n case \"whereNot\":\n this.processNested(data.nested, \"AND\", true, type);\n break;\n case \"orWhereNot\":\n this.processNested(data.nested, \"OR\", true, type);\n break;\n // Same semantics as the Mongo driver: the callback conditions are applied\n // inline (exists) or negated (notExists).\n case \"whereExists\":\n this.processNested(data.subquery, \"AND\", false, type);\n break;\n case \"whereNotExists\":\n this.processNested(data.subquery, \"AND\", true, type);\n break;\n case \"whereDate\":\n this.processWhereDate(data, \"=\");\n break;\n case \"whereDateBefore\":\n this.processWhereDate(data, \"<\");\n break;\n case \"whereDateAfter\":\n this.processWhereDate(data, \">\");\n break;\n case \"whereDateBetween\":\n this.processWhereDateBetween(data);\n break;\n\n // SELECT operations\n case \"select\":\n this.processSelect(data);\n break;\n case \"selectRaw\":\n this.processSelectRaw(data);\n break;\n case \"deselect\":\n this.processDeselect(data);\n break;\n\n // JOIN operations\n case \"join\":\n case \"innerJoin\":\n this.processJoin(data, \"INNER\");\n break;\n case \"leftJoin\":\n this.processJoin(data, \"LEFT\");\n break;\n case \"rightJoin\":\n this.processJoin(data, \"RIGHT\");\n break;\n case \"fullJoin\":\n this.processJoin(data, \"FULL OUTER\");\n break;\n case \"crossJoin\":\n this.processCrossJoin(data);\n break;\n case \"joinRaw\":\n this.processJoinRaw(data);\n break;\n\n // ORDER operations\n case \"orderBy\":\n this.processOrderBy(data);\n break;\n case \"orderByRaw\":\n this.processOrderByRaw(data);\n break;\n\n // GROUP operations\n case \"groupBy\":\n this.processGroupBy(data);\n break;\n case \"groupByRaw\":\n this.processGroupByRaw(data);\n break;\n case \"having\":\n this.processHaving(data);\n break;\n case \"havingRaw\":\n this.processHavingRaw(data);\n break;\n\n // LIMIT operations\n case \"limit\":\n this.limitValue = data.value as number;\n break;\n case \"offset\":\n this.offsetValue = data.value as number;\n break;\n\n // Row locking\n case \"lock\": {\n let clause = \"FOR UPDATE\";\n if (data.skipLocked) {\n clause += \" SKIP LOCKED\";\n } else if (data.noWait) {\n clause += \" NOWAIT\";\n }\n this.lockClause = clause;\n break;\n }\n\n // Other\n case \"distinct\":\n this.isDistinct = true;\n break;\n\n // joinWith select related columns\n case \"selectRelatedColumns\":\n this.processSelectRelatedColumns(data);\n break;\n\n // Builder-level metadata consumed before parsing; carries no SQL of its own\n case \"cursor\":\n case \"joinWith\":\n break;\n\n default:\n // An unknown filter must never silently disappear: write paths\n // (delete/update) reuse this parser, so a dropped filter widens the\n // statement to every row.\n throw new Error(`PostgresQueryParser: unsupported operation type \"${type}\"`);\n }\n }\n\n /**\n * Build the final SQL query from collected clauses.\n *\n * @returns Complete SQL query string\n */\n private buildSql(): string {\n const parts: string[] = [];\n\n // SELECT clause\n parts.push(this.buildSelectClause());\n\n // FROM clause\n const quotedTable = this.quoteTable(this.table);\n const fromClause = this.alias\n ? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}`\n : `FROM ${quotedTable}`;\n parts.push(fromClause);\n\n // JOIN clauses\n if (this.joinClauses.length > 0) {\n parts.push(this.joinClauses.join(\" \"));\n }\n\n // WHERE clause\n if (this.whereClauses.length > 0) {\n parts.push(`WHERE ${this.whereClauses.join(\" \")}`);\n }\n\n // GROUP BY clause — bare columns are quoted; raw expressions (e.g.\n // date_trunc buckets) are emitted verbatim. Both share one clause.\n if (this.groupColumns.length > 0 || this.groupRawExpressions.length > 0) {\n const quotedCols = this.groupColumns.map((c) => this.dialect.quoteIdentifier(c));\n const allGroupTerms = [...quotedCols, ...this.groupRawExpressions];\n parts.push(`GROUP BY ${allGroupTerms.join(\", \")}`);\n }\n\n // HAVING clause\n if (this.havingClauses.length > 0) {\n parts.push(`HAVING ${this.havingClauses.join(\" AND \")}`);\n }\n\n // ORDER BY clause\n if (this.orderClauses.length > 0) {\n parts.push(`ORDER BY ${this.orderClauses.join(\", \")}`);\n }\n\n // LIMIT/OFFSET\n const limitOffset = this.dialect.limitOffset(this.limitValue, this.offsetValue);\n if (limitOffset) {\n parts.push(limitOffset);\n }\n\n // Row-locking clause — last in PostgreSQL's SELECT grammar\n if (this.lockClause) {\n parts.push(this.lockClause);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build the SELECT clause.\n *\n * @returns SELECT clause string\n */\n private buildSelectClause(): string {\n const distinct = this.isDistinct ? \"DISTINCT \" : \"\";\n\n // If no specific columns, select all — qualify with main table when joins present\n if (this.selectColumns.length === 0 && this.selectRaw.length === 0) {\n return this.hasJoins\n ? `SELECT ${distinct}${this.quoteTable(this.table)}.*`\n : `SELECT ${distinct}*`;\n }\n\n const columns: string[] = [];\n\n // Add selected columns — prefix with main table when joins present to avoid ambiguity\n for (const col of this.selectColumns) {\n if (!this.deselectColumns.includes(col)) {\n columns.push(this.parseColumnIdentifier(col, this.table, this.alias));\n }\n }\n\n // Add raw expressions\n columns.push(...this.selectRaw);\n\n return `SELECT ${distinct}${columns.join(\", \")}`;\n }\n\n /**\n * Add a placeholder and parameter.\n *\n * @param value - Parameter value\n * @returns Placeholder string ($1, $2, etc.)\n */\n private addParam(value: unknown): string {\n this.params.push(value);\n return this.dialect.placeholder(this.paramIndex++);\n }\n\n /**\n * Absorb a sub-parser's params into this parser, returning a rewriter\n * function that translates the sub-parser's `$N` placeholders into freshly\n * numbered placeholders in this parser's namespace.\n *\n * Required whenever sub-parser SQL fragments (e.g. WHERE clauses produced\n * for a `joinWith` constraint) are spliced into the outer query — without\n * the rewrite the embedded `$N` references point at parameters this\n * parser has no knowledge of, leaving them dangling in the final SQL.\n *\n * The sub-parser's `params[i]` corresponds to its `$(i+1)` placeholder\n * (its `paramIndex` always starts at 1), so we map sequentially.\n *\n * @example\n * const renumber = this.absorbSubParserParams(subParser);\n * const fragment = renumber(subParser.whereClauses.join(\" \"));\n */\n private absorbSubParserParams(subParser: PostgresQueryParser): (sql: string) => string {\n if (subParser.params.length === 0) {\n return (sql) => sql;\n }\n\n const oldToNew = new Map<string, string>();\n\n for (let index = 0; index < subParser.params.length; index++) {\n const newPlaceholder = this.addParam(subParser.params[index]);\n oldToNew.set(`$${index + 1}`, newPlaceholder);\n }\n\n return (sql) => sql.replace(/\\$\\d+/g, (match) => oldToNew.get(match) ?? match);\n }\n\n /**\n * Process a basic WHERE operation.\n *\n * Delegates to specialised processors for operators that require more than a\n * single placeholder (between, in, like-variants, exists, etc.).\n */\n private processWhere(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n // `where(q => ...)` / `orWhere(q => ...)` record a nested group\n if (data.nested !== undefined) {\n return this.processNested(data.nested, boolean, false, \"where\");\n }\n\n const field = data.field as string;\n const operator = (data.operator as WhereOperator) ?? \"=\";\n const value = data.value;\n\n // Delegate to specialised processors for operators that need it. The\n // `boolean` connective is forwarded so `orWhere(f, \"in\", …)` stays an OR.\n const range = value as [unknown, unknown];\n const like = { field, pattern: value as string };\n\n switch (operator) {\n case \"between\":\n return this.processWhereBetween({ field, range }, false, boolean);\n case \"notBetween\":\n return this.processWhereBetween({ field, range }, true, boolean);\n case \"in\":\n return this.processWhereIn({ field, values: value as unknown[] }, false, boolean);\n case \"notIn\":\n return this.processWhereIn({ field, values: value as unknown[] }, true, boolean);\n case \"like\":\n case \"ilike\":\n return this.processWhereLike(like, false, boolean);\n case \"startsWith\":\n case \"endsWith\":\n return this.processWhereLike(like, false, boolean, operator);\n case \"notLike\":\n return this.processWhereLike(like, true, boolean);\n case \"notStartsWith\":\n return this.processWhereLike(like, true, boolean, \"startsWith\");\n case \"notEndsWith\":\n return this.processWhereLike(like, true, boolean, \"endsWith\");\n case \"exists\":\n // Raw strings are never inlined into SQL (injection); a sub-query must\n // arrive as an already-parameterised builder, not text.\n throw new Error(\n 'PostgresQueryParser: where(field, \"exists\", value) does not accept a raw SQL string; use a query builder or callback',\n );\n }\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n\n if (value === null) {\n const nullOperator = operator === \"!=\" ? \"IS NOT NULL\" : \"IS NULL\";\n return this.addWhereClause(`${quotedField} ${nullOperator}`, boolean);\n }\n\n // Simple single-value operator — fall through to generic path\n const placeholder = this.addParam(value);\n this.addWhereClause(`${quotedField} ${this.mapOperator(operator)} ${placeholder}`, boolean);\n }\n\n /**\n * Process a nested group of operations as `(<group>)` / `NOT (<group>)`.\n *\n * The group is parsed by a sub-parser; its params are renumbered into this\n * parser's namespace.\n */\n private processNested(\n nested: unknown,\n boolean: \"AND\" | \"OR\",\n negate: boolean,\n opType: string,\n ): void {\n const subParser = new PostgresQueryParser({\n table: this.table,\n alias: this.alias,\n dialect: this.dialect,\n operations: (nested as PostgresParserOperation[] | undefined) ?? [],\n });\n subParser.parse();\n\n if (subParser.whereClauses.length === 0) {\n throw new Error(`PostgresQueryParser: \"${opType}\" received an empty condition group`);\n }\n\n const renumber = this.absorbSubParserParams(subParser);\n const group = `(${renumber(subParser.whereClauses.join(\" \"))})`;\n\n this.addWhereClause(negate ? `NOT ${group}` : group, boolean);\n }\n\n /**\n * Compare the date portion of a column (time ignored).\n */\n private processWhereDate(data: Record<string, unknown>, operator: string): void {\n const quotedField = this.parseColumnIdentifier(data.field as string, this.table, this.alias);\n const placeholder = this.addParam(data.value);\n\n this.addWhereClause(`${quotedField}::date ${operator} ${placeholder}::date`, \"AND\");\n }\n\n /**\n * Date portion of a column within an inclusive [from, to] range.\n */\n private processWhereDateBetween(data: Record<string, unknown>): void {\n const range = data.range as [unknown, unknown];\n const quotedField = this.parseColumnIdentifier(data.field as string, this.table, this.alias);\n const from = this.addParam(range[0]);\n const to = this.addParam(range[1]);\n\n this.addWhereClause(`${quotedField}::date BETWEEN ${from}::date AND ${to}::date`, \"AND\");\n }\n\n /**\n * Process a raw WHERE operation.\n */\n private processWhereRaw(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.addWhereClause(processed, boolean);\n }\n\n /**\n * Process WHERE IN / NOT IN.\n */\n private processWhereIn(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const values = data.values as unknown[];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const operator = negate ? \"!= ALL\" : \"= ANY\";\n const placeholder = this.addParam(values);\n\n this.addWhereClause(`${quotedField} ${operator}(${placeholder})`, boolean);\n }\n\n /**\n * Process WHERE NULL / NOT NULL.\n */\n private processWhereNull(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const clause = negate ? `${quotedField} IS NOT NULL` : `${quotedField} IS NULL`;\n this.addWhereClause(clause, boolean);\n }\n\n /**\n * Process WHERE BETWEEN / NOT BETWEEN.\n */\n private processWhereBetween(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n ): void {\n const field = data.field as string;\n const range = data.range as [unknown, unknown];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const placeholder1 = this.addParam(range[0]);\n const placeholder2 = this.addParam(range[1]);\n const keyword = negate ? \"NOT BETWEEN\" : \"BETWEEN\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder1} AND ${placeholder2}`, boolean);\n }\n\n /**\n * Process WHERE LIKE / NOT LIKE.\n *\n * Cross-driver semantics (matches MongoDB): `%` is the only wildcard, `_`\n * and backslash are literal, and a pattern with no `%` matches as a\n * substring. `startsWith` / `endsWith` anchor the escaped text explicitly.\n * A `RegExp` has no SQL equivalent and is rejected.\n */\n private processWhereLike(\n data: Record<string, unknown>,\n negate: boolean,\n boolean: \"AND\" | \"OR\" = \"AND\",\n modeArg?: \"startsWith\" | \"endsWith\",\n ): void {\n const field = data.field as string;\n const mode = modeArg ?? (data.mode as \"startsWith\" | \"endsWith\" | undefined);\n\n // A RegExp maps to the POSIX case-insensitive match operators\n if (data.isRegExp === true || (data.pattern as unknown) instanceof RegExp) {\n const source =\n (data.pattern as unknown) instanceof RegExp\n ? (data.pattern as unknown as RegExp).source\n : String(data.pattern);\n const regexField = this.parseColumnIdentifier(field, this.table, this.alias);\n const regexPlaceholder = this.addParam(source);\n\n return this.addWhereClause(\n `${regexField} ${negate ? \"!~*\" : \"~*\"} ${regexPlaceholder}`,\n boolean,\n );\n }\n // Mode ops carry the raw user value; `pattern` is only the display shape\n const pattern = String(mode && data.value !== undefined ? data.value : data.pattern);\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const { operator } = this.dialect.likePattern(pattern, true);\n // Escape `\\` and `_` (literal). `%` stays the wildcard of a plain LIKE\n // pattern, but startsWith / endsWith values are literal text, so it is\n // escaped there too.\n let escaped = pattern.replace(/\\\\/g, \"\\\\\\\\\").replace(/_/g, \"\\\\_\");\n\n if (mode) {\n escaped = escaped.replace(/%/g, \"\\\\%\");\n }\n\n let finalPattern = escaped;\n\n if (mode === \"startsWith\") {\n finalPattern = `${escaped}%`;\n } else if (mode === \"endsWith\") {\n finalPattern = `%${escaped}`;\n } else if (!escaped.includes(\"%\")) {\n finalPattern = `%${escaped}%`;\n }\n\n const placeholder = this.addParam(finalPattern);\n const keyword = negate ? `NOT ${operator}` : operator;\n\n // Mode variants state the escape character explicitly\n const escapeClause = mode ? \" ESCAPE '\\\\'\" : \"\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder}${escapeClause}`, boolean);\n }\n\n /**\n * Process WHERE column comparison.\n */\n private processWhereColumn(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const first = data.first as string;\n const operator = this.mapOperator(((data.operator as string) ?? \"=\") as WhereOperator);\n const second = data.second as string;\n\n // Both sides may need qualification if unambiguous table is unclear\n const quotedFirst = this.parseColumnIdentifier(first, this.table, this.alias);\n const quotedSecond = this.parseColumnIdentifier(second, this.table, this.alias);\n\n this.addWhereClause(`${quotedFirst} ${operator} ${quotedSecond}`, boolean);\n }\n\n /**\n * Process WHERE JSON contains.\n */\n private processWhereJsonContains(data: Record<string, unknown>, negate: boolean): void {\n const path = data.path as string;\n const value = data.value;\n\n const quotedPath = this.parseColumnIdentifier(path, this.table, this.alias);\n const operator = negate ? \"NOT @>\" : \"@>\";\n const placeholder = this.addParam(JSON.stringify(value));\n\n this.addWhereClause(`${quotedPath} ${operator} ${placeholder}::jsonb`, \"AND\");\n }\n\n /** Text search config name; anything but a plain identifier falls back to english. */\n private safeLanguage(language: unknown): string {\n return typeof language === \"string\" && /^[a-z_]+$/i.test(language) ? language : \"english\";\n }\n\n /**\n * Process full-text search WHERE.\n */\n private processWhereFullText(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n const query = data.query as string;\n const language = this.safeLanguage(data.language);\n const weights = (data.weights as Record<string, string> | undefined) ?? {};\n\n // Same expression as `createFullTextIndex`, so the GIN index is used and a\n // NULL column doesn't null the whole vector.\n const tsVectors = fields\n .map((f) => {\n const weight = /^[A-D]$/.test(weights[f] ?? \"\") ? weights[f] : \"A\";\n\n return `setweight(to_tsvector('${language}', COALESCE(${this.dialect.quoteIdentifier(f)}, '')), '${weight}')`;\n })\n .join(\" || \");\n\n const placeholder = this.addParam(query);\n this.addWhereClause(`(${tsVectors}) @@ plainto_tsquery('${language}', ${placeholder})`, \"AND\");\n }\n\n /**\n * Process SELECT operation.\n */\n private processSelect(data: Record<string, unknown>): void {\n const fields = data.fields as string[] | Record<string, string>;\n\n if (Array.isArray(fields)) {\n this.selectColumns.push(...fields);\n } else {\n // Handle aliases: { field: 'alias' }\n for (const [field, alias] of Object.entries(fields)) {\n const quotedField = this.dialect.quoteIdentifier(field);\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n this.selectRaw.push(`${quotedField} AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process raw SELECT expression.\n */\n private processSelectRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string | Record<string, unknown>;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n if (typeof expression === \"string\") {\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.selectRaw.push(processed);\n } else {\n // Handle object expressions (for compatibility)\n for (const [alias, expr] of Object.entries(expression)) {\n this.selectRaw.push(`${expr} AS ${this.dialect.quoteIdentifier(alias)}`);\n }\n }\n }\n\n /**\n * Process DESELECT operation.\n */\n private processDeselect(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n this.deselectColumns.push(...fields);\n }\n\n /**\n * Process SELECT for related columns (joinWith).\n *\n * - hasOne / belongsTo → LEFT JOIN + row_to_json (single object)\n * - hasMany → correlated subquery with json_agg (array, no row explosion)\n *\n * @example hasMany correlated subquery:\n * (SELECT json_agg(row_to_json(a.*))\n * FROM \"chat_message_actions\" a\n * WHERE a.\"chat_message_id\" = \"chat_messages\".\"id\") AS \"actions\"\n *\n * @example hasOne/belongsTo row_to_json:\n * row_to_json(\"organizationAiModel\".*) AS \"organizationAiModel\"\n */\n private processSelectRelatedColumns(data: Record<string, unknown>): void {\n const alias = data.alias as string;\n const select = data.select as string[] | undefined;\n const relationType = data.type as string | undefined;\n const constraintOps = data.constraintOps as PostgresParserOperation[] | undefined;\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n const quotedTable = this.quoteTable(this.table);\n\n const hasExplicitSelect = this.selectColumns.length > 0;\n if (!hasExplicitSelect && !this.selectRaw.includes(`${quotedTable}.*`)) {\n this.selectRaw.unshift(`${quotedTable}.*`);\n }\n\n if (relationType === \"hasMany\") {\n // Correlated subquery — no JOIN, no row explosion, returns a JSON array.\n const relatedTable = data.table as string;\n const foreignKey = data.foreignKey as string;\n const localKey = data.localKey as string;\n const quotedRelatedTable = this.quoteTable(relatedTable);\n const quotedForeignKey = this.dialect.quoteIdentifier(foreignKey);\n const quotedLocalKey = this.dialect.quoteIdentifier(localKey);\n const quotedMainTable = this.quoteTable(this.table);\n\n let innerSelect: string;\n if (select && select.length > 0) {\n // Pick specific columns: json_agg(json_build_object('id', a.\"id\", ...))\n const fields = select\n .map((col) => `'${escapeSqlLiteral(col)}', a.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n innerSelect = `json_agg(json_build_object(${fields}))`;\n } else {\n innerSelect = `json_agg(row_to_json(a.*))`;\n }\n\n // Build the base FK condition\n const fkCondition = `a.${quotedForeignKey} = ${quotedMainTable}.${quotedLocalKey}`;\n\n // Merge constraint ops (where / order / limit) from a sub-parser.\n // The sub-parser numbers its own placeholders starting at $1; we must\n // absorb its params into this parser and renumber the embedded SQL,\n // otherwise $N references in the merged clauses dangle.\n let extraWhere = \"\";\n let orderBy = \"\";\n let limitClause = \"\";\n\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: relatedTable,\n alias: \"a\",\n dialect: this.dialect,\n operations: constraintOps,\n });\n subParser.parse();\n\n const renumber = this.absorbSubParserParams(subParser);\n\n if (subParser.whereClauses.length > 0) {\n extraWhere = ` AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n if (subParser.orderClauses.length > 0) {\n orderBy = ` ORDER BY ${renumber(subParser.orderClauses.join(\", \"))}`;\n }\n if (subParser.limitValue !== undefined) {\n limitClause = ` ${this.dialect.limitOffset(subParser.limitValue)}`;\n }\n }\n\n // Wrap with ORDER/LIMIT only when constraints are present (json_agg ignores ORDER)\n const innerQuery =\n orderBy || limitClause\n ? `SELECT row_to_json(sub.*) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub`\n : `SELECT ${innerSelect.replace(\"json_agg(\", \"\").replace(/\\)$/, \"\")} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}`;\n\n const aggregated =\n orderBy || limitClause\n ? `(SELECT json_agg(row_to_json(sub.*)) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub) AS ${quotedAlias}`\n : `(SELECT ${innerSelect} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}) AS ${quotedAlias}`;\n\n this.selectRaw.push(aggregated);\n } else {\n // hasOne / belongsTo — single object via LEFT JOIN + row_to_json.\n // If constraint provides an explicit select list, prefer it over data.select\n let effectiveSelect = select;\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: (data.table as string) ?? alias,\n alias,\n dialect: this.dialect,\n operations: constraintOps,\n });\n subParser.parse();\n if (subParser.selectColumns.length > 0) {\n effectiveSelect = subParser.selectColumns;\n }\n }\n\n if (effectiveSelect && effectiveSelect.length > 0) {\n const selectedColumns = effectiveSelect\n .map((col) => `${quotedAlias}.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n this.selectRaw.push(\n `row_to_json((SELECT d FROM (SELECT ${selectedColumns}) d)) AS ${quotedAlias}`,\n );\n } else {\n this.selectRaw.push(`row_to_json(${quotedAlias}.*) AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process JOIN operation with smart field detection.\n *\n * Handles both regular columns and JSONB nested paths:\n * - \"id\" → \"table\".\"id\" (auto-prefixed)\n * - \"users.id\" → \"users\".\"id\" (explicit table)\n * - \"createdBy.id\" → \"table\".\"createdBy\"->>'id' (JSONB path)\n * - \"posts.createdBy.id\" → \"posts\".\"createdBy\"->>'id' (JSONB with table)\n */\n private processJoin(data: Record<string, unknown>, type: string): void {\n const options = data as unknown as\n | JoinOptions\n | { table: string; localField: string; foreignField: string };\n\n const joinTable = \"table\" in options ? options.table : \"\";\n const localField = \"localField\" in options ? options.localField : \"\";\n const foreignField = \"foreignField\" in options ? options.foreignField : \"\";\n const alias = \"alias\" in options ? options.alias : undefined;\n const constraintOps = (data.constraintOps as PostgresParserOperation[] | undefined) ?? [];\n\n const quotedTable = this.quoteTable(joinTable);\n const tableRef = alias\n ? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}`\n : quotedTable;\n const tableAlias = alias ?? joinTable;\n\n // Track the joined table for reference detection\n this.joinedTables.add(joinTable);\n if (alias) {\n this.joinedTables.add(alias);\n }\n\n // Parse local field (belongs to main table)\n const quotedLocal = this.parseColumnIdentifier(localField!, this.table, this.alias);\n\n // Parse foreign field (belongs to join table)\n const quotedForeign = this.parseColumnIdentifier(foreignField!, joinTable, tableAlias);\n\n // Build the basic ON condition. For hasOne/belongsTo joinWith with a\n // constraint callback, append the constraint's where-clauses to the ON\n // clause (preserves LEFT JOIN semantics — main rows stay even when no\n // related row passes the constraint). orderBy/limit on the constraint\n // are silently dropped — they have no meaning on a single-row LEFT JOIN.\n let onClause = `${quotedLocal} = ${quotedForeign}`;\n\n if (constraintOps.length > 0) {\n const whereOps = constraintOps.filter(\n (op) => op.type.startsWith(\"where\") || op.type.startsWith(\"orWhere\"),\n );\n\n if (whereOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: joinTable,\n alias: tableAlias,\n dialect: this.dialect,\n operations: whereOps,\n });\n subParser.parse();\n\n if (subParser.whereClauses.length > 0) {\n const renumber = this.absorbSubParserParams(subParser);\n onClause = `${onClause} AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n }\n }\n\n this.joinClauses.push(`${type} JOIN ${tableRef} ON ${onClause}`);\n }\n\n /**\n * Parse a column identifier with smart detection for table prefixes and JSONB paths.\n *\n * Supports an optional explicit cast suffix using PostgreSQL's `::type` syntax,\n * which is the only way to coerce a JSONB-extracted value (always returned as\n * `text`) into another type. The parser does NOT infer casts from key names —\n * coercion is always opt-in by the caller.\n *\n * @param field - The field string. Examples:\n * - \"id\" → `\"table\".\"id\"`\n * - \"users.id\" → `\"users\".\"id\"`\n * - \"createdBy.id\" → `\"table\".\"createdBy\"->>'id'`\n * - \"createdBy.id::int\" → `(\"table\".\"createdBy\"->>'id')::int`\n * - \"meta.score::numeric\" → `(\"table\".\"meta\"->>'score')::numeric`\n * - \"posts.createdBy.id::uuid\" → `(\"posts\".\"createdBy\"->>'id')::uuid`\n * @param defaultTable - Default table to use if no prefix\n * @param tableAlias - Table alias to use if provided\n * @returns Properly quoted SQL expression\n */\n private parseColumnIdentifier(field: string, defaultTable: string, tableAlias?: string): string {\n if (!field) return \"\";\n\n // Extract optional explicit cast suffix: \"createdBy.id::int\" → path \"createdBy.id\", cast \"int\"\n let cast: string | undefined;\n const castIndex = field.indexOf(\"::\");\n if (castIndex !== -1) {\n cast = field.slice(castIndex + 2).trim();\n field = field.slice(0, castIndex);\n }\n\n const effectiveTable = tableAlias ?? defaultTable;\n const parts = field.split(\".\");\n\n let expression: string;\n\n if (parts.length === 1) {\n // Single part: just a column name, prefix with default table\n expression = `${this.quoteTable(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;\n } else if (parts.length === 2) {\n // Two parts: could be \"table.column\" or \"jsonbColumn.key\"\n const [first, second] = parts;\n\n // `parts.length === 2` above guarantees both, but the compiler does not\n // narrow a destructured element from a length check. Throwing rather\n // than defaulting to \"\": every branch below feeds `quoteIdentifier` or a\n // JSONB path, so an empty segment would emit SYNTACTICALLY VALID SQL\n // against the wrong identifier — a query that silently returns the wrong\n // rows is far worse than one that refuses to build.\n if (first === undefined || second === undefined) {\n throw new Error(`Cannot parse field path \"${field}\": expected two non-empty segments.`);\n }\n\n if (this.isTableReference(first)) {\n // It's table.column - regular column reference\n expression = `${this.quoteTable(first)}.${this.dialect.quoteIdentifier(second)}`;\n } else {\n // It's jsonbColumn.key - JSONB path\n expression = this.buildJsonbPath(effectiveTable, first, [second]);\n }\n } else {\n // Three or more parts: \"table.jsonbColumn.key...\" or \"jsonbColumn.key1.key2...\"\n const [first, second, ...rest] = parts;\n\n // Same reasoning as the two-part branch: this arm runs only for three or\n // more parts, so both exist — and an empty identifier reaching the SQL\n // is a wrong-rows bug, not a crash.\n if (first === undefined || second === undefined) {\n throw new Error(\n `Cannot parse field path \"${field}\": expected at least three non-empty segments.`,\n );\n }\n\n if (this.isTableReference(first)) {\n // First part is table: \"posts.createdBy.id\" → table is \"posts\", JSONB is \"createdBy.id\"\n expression = this.buildJsonbPath(first, second, rest);\n } else {\n // No table prefix: \"createdBy.address.city\" → use default table\n expression = this.buildJsonbPath(effectiveTable, first, [second, ...rest]);\n }\n }\n\n if (cast) {\n if (!SAFE_CAST_PATTERN.test(cast)) {\n throw new Error(`PostgresQueryParser: unsafe cast type \"${cast}\"`);\n }\n\n return `(${expression})::${cast}`;\n }\n\n return expression;\n }\n\n /**\n * Check if a string is a table reference (main table or join table).\n */\n private isTableReference(name: string): boolean {\n if (name === this.table || name === this.alias) {\n return true;\n }\n\n if (this.joinedTables.has(name)) {\n return true;\n }\n\n return false;\n }\n\n private quoteTable(table: string): string {\n return this.dialect instanceof PostgresDialect\n ? this.dialect.quoteTableIdentifier(table)\n : this.dialect.quoteIdentifier(table);\n }\n\n /**\n * Build a JSONB path expression.\n *\n * @param table - Table name\n * @param column - JSONB column name\n * @param path - Array of nested keys\n * @returns PostgreSQL JSONB path expression\n *\n * Returns the raw JSONB path expression — extracted values are always `text`\n * (`->>` returns text). Callers that need a different type must request an\n * explicit cast via the `::type` suffix in `parseColumnIdentifier`.\n *\n * @example\n * buildJsonbPath(\"posts\", \"createdBy\", [\"id\"])\n * // Returns: \"posts\".\"createdBy\"->>'id'\n *\n * buildJsonbPath(\"posts\", \"createdBy\", [\"address\", \"city\"])\n * // Returns: \"posts\".\"createdBy\"->'address'->>'city'\n */\n private buildJsonbPath(table: string, column: string, path: string[]): string {\n const quotedTable = this.quoteTable(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n\n if (path.length === 0) {\n return `${quotedTable}.${quotedColumn}`;\n }\n\n // Build JSONB path: use -> for intermediate keys, ->> for final key (returns text)\n let expression = `${quotedTable}.${quotedColumn}`;\n\n for (let i = 0; i < path.length; i++) {\n const isLast = i === path.length - 1;\n const operator = isLast ? \"->>\" : \"->\";\n expression += `${operator}'${escapeSqlLiteral(path[i] as string)}'`;\n }\n\n return expression;\n }\n\n /**\n * Process CROSS JOIN operation.\n */\n private processCrossJoin(data: Record<string, unknown>): void {\n const table = data.table as string;\n const quotedTable = this.quoteTable(table);\n this.joinClauses.push(`CROSS JOIN ${quotedTable}`);\n }\n\n /**\n * Process raw JOIN expression.\n */\n private processJoinRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.joinClauses.push(processed);\n }\n\n /**\n * Process ORDER BY operation.\n */\n private processOrderBy(data: Record<string, unknown>): void {\n const field = data.field as string;\n const rawDirection = String(data.direction ?? \"asc\").trim();\n\n if (!SAFE_DIRECTION_PATTERN.test(rawDirection)) {\n throw new Error(`PostgresQueryParser: invalid orderBy direction \"${rawDirection}\"`);\n }\n\n const direction = rawDirection.replace(/\\s+/g, \" \").toUpperCase();\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n this.orderClauses.push(`${quotedField} ${direction}`);\n }\n\n /**\n * Process raw ORDER BY expression.\n */\n private processOrderByRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.orderClauses.push(processed);\n }\n\n /**\n * Process GROUP BY operation.\n */\n private processGroupBy(data: Record<string, unknown>): void {\n const fields = data.fields as string | string[];\n const columns = Array.isArray(fields) ? fields : [fields];\n this.groupColumns.push(...columns);\n }\n\n /** Process a raw GROUP BY expression, binding `?` placeholders positionally. */\n private processGroupByRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.groupRawExpressions.push(expression);\n }\n\n /**\n * Process HAVING operation.\n */\n private processHaving(data: Record<string, unknown>): void {\n const field = data.field as string;\n const operator = this.mapOperator(((data.operator as string) ?? \"=\") as WhereOperator);\n const value = data.value;\n\n const quotedField = this.dialect.quoteIdentifier(field);\n const placeholder = this.addParam(value);\n\n this.havingClauses.push(`${quotedField} ${operator} ${placeholder}`);\n }\n\n /**\n * Process a raw HAVING expression, threading `?` placeholders into\n * positional params the same way `processWhereRaw` / `processSelectRaw` do.\n * Without this, bindings on a `havingRaw` op are silently dropped.\n */\n private processHavingRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.havingClauses.push(expression);\n }\n\n /**\n * Add a WHERE clause with boolean operator.\n */\n private addWhereClause(clause: string, boolean: \"AND\" | \"OR\"): void {\n if (this.whereClauses.length === 0) {\n this.whereClauses.push(clause);\n } else {\n this.whereClauses.push(`${boolean} ${clause}`);\n }\n }\n\n /**\n * Map simple Cascade operators to their SQL equivalents.\n *\n * Complex operators (between, in, like-variants, exists) are handled by\n * dedicated processors and should never reach this method.\n */\n private mapOperator(operator: WhereOperator): string {\n const mapping: Record<string, string> = {\n \"=\": \"=\",\n \"!=\": \"!=\",\n \"<>\": \"<>\",\n \">\": \">\",\n \">=\": \">=\",\n \"<\": \"<\",\n \"<=\": \"<=\",\n like: \"LIKE\",\n notlike: \"NOT LIKE\",\n ilike: \"ILIKE\",\n };\n\n const mapped = mapping[String(operator).toLowerCase()];\n\n if (mapped === undefined) {\n throw new Error(`PostgresQueryParser: unsupported operator \"${operator}\"`);\n }\n\n return mapped;\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { SQLSerializer } from "../../migration/sql-serializer.mjs";
2
2
 
3
- //#region ../cascade/src/drivers/postgres/postgres-sql-serializer.ts
3
+ //#region ../../packages/cascade/src/drivers/postgres/postgres-sql-serializer.ts
4
4
  /**
5
5
  * PostgreSQL-specific SQL serializer.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-sql-serializer.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-sql-serializer.ts"],"sourcesContent":["import type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\nimport type { PendingOperation } from \"../../migration/migration\";\nimport { SQLSerializer } from \"../../migration/sql-serializer\";\nimport type {\n ColumnDefinition,\n ForeignKeyDefinition,\n FullTextIndexOptions,\n GeoIndexOptions,\n IndexDefinition,\n VectorIndexOptions,\n} from \"../../contracts/migration-driver.contract\";\n\n/**\n * PostgreSQL-specific SQL serializer.\n *\n * Converts pending migration operations into valid PostgreSQL DDL statements.\n */\nexport class PostgresSQLSerializer extends SQLSerializer {\n public constructor(private readonly dialect: SqlDialectContract) {\n super();\n }\n\n public serialize(operation: PendingOperation, table: string): string | string[] | null {\n switch (operation.type) {\n case \"createTable\":\n return this.createTable(table);\n case \"createTableIfNotExists\":\n return this.createTableIfNotExists(table);\n case \"dropTable\":\n return this.dropTable(table);\n case \"dropTableIfExists\":\n return this.dropTableIfExists(table);\n case \"renameTable\":\n return this.renameTable(table, operation.payload as string);\n case \"truncateTable\":\n return this.truncateTable(table);\n case \"addColumn\":\n return this.addColumn(table, operation.payload as ColumnDefinition);\n case \"dropColumn\":\n return this.dropColumn(table, operation.payload as string);\n case \"dropColumns\":\n // This is handled via map in serializeAll or loop, but Postgres supports multiple DROP COLUMN\n // For simplicity, we can serialize multiple statements if we want, or handle it here\n // Actually, dropColumns payload is string[]. We can join them.\n return this.dropColumns(table, operation.payload as string[]);\n case \"renameColumn\": {\n const payload = operation.payload as { from: string; to: string };\n return this.renameColumn(table, payload.from, payload.to);\n }\n case \"modifyColumn\":\n // Modify column might require multiple statements in Postgres (type, nullability, default).\n // SQLSerializer's serialize return is just a string, so we'll separate them by semicolon\n // Or wait, Postgres allows multiple ALTER COLUMN in one ALTER TABLE statement.\n return this.modifyColumn(table, operation.payload as ColumnDefinition);\n case \"createIndex\":\n return this.createIndex(table, operation.payload as IndexDefinition);\n case \"dropIndex\":\n return this.dropIndex(table, operation.payload as string | string[]);\n case \"createUniqueIndex\": {\n const payload = operation.payload as { columns: string[]; name?: string };\n return this.createIndex(table, { columns: payload.columns, name: payload.name, unique: true });\n }\n case \"dropUniqueIndex\":\n return this.dropIndex(table, operation.payload as string[]);\n case \"createFullTextIndex\": {\n const payload = operation.payload as { columns: string[]; options?: FullTextIndexOptions };\n return this.createFullTextIndex(table, payload.columns, payload.options);\n }\n case \"dropFullTextIndex\":\n return this.dropIndex(table, operation.payload as string);\n case \"createGeoIndex\": {\n const payload = operation.payload as { column: string; options?: GeoIndexOptions };\n return this.createGeoIndex(table, payload.column, payload.options);\n }\n case \"dropGeoIndex\":\n return this.dropIndex(table, `idx_${table}_geo_${operation.payload}`);\n case \"createVectorIndex\": {\n const payload = operation.payload as { column: string; options: VectorIndexOptions };\n return this.createVectorIndex(table, payload.column, payload.options);\n }\n case \"dropVectorIndex\":\n return this.dropIndex(table, `idx_${table}_vector_${operation.payload}`);\n case \"createTTLIndex\": {\n const payload = operation.payload as { column: string; expireAfterSeconds: number };\n return this.createTTLIndex(table, payload.column, payload.expireAfterSeconds);\n }\n case \"dropTTLIndex\":\n return this.dropIndex(table, `idx_${table}_ttl_${operation.payload}`);\n case \"addForeignKey\":\n return this.addForeignKey(table, operation.payload as ForeignKeyDefinition);\n case \"dropForeignKey\":\n return this.dropForeignKey(table, operation.payload as string);\n case \"addPrimaryKey\":\n return this.addPrimaryKey(table, operation.payload as string[]);\n case \"dropPrimaryKey\":\n return this.dropPrimaryKey(table);\n case \"addCheck\": {\n const payload = operation.payload as { name: string; expression: string };\n return this.addCheck(table, payload.name, payload.expression);\n }\n case \"dropCheck\":\n return this.dropCheck(table, operation.payload as string);\n case \"createTimestamps\":\n // createTimestamps needs to inject two columns. We return two statements.\n return this.createTimestamps(table);\n case \"rawStatement\": {\n const payload = operation.payload as string;\n // ensure string doesn't end with a semicolon if it's already one statement\n return payload;\n }\n case \"setSchemaValidation\":\n case \"removeSchemaValidation\":\n // Not implemented in PostgreSQL\n return null;\n default:\n return null;\n }\n }\n\n // ============================================================================\n // TABLE OPERATIONS\n // ============================================================================\n\n private createTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `CREATE TABLE ${quotedTable} ()`;\n }\n\n private createTableIfNotExists(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `CREATE TABLE IF NOT EXISTS ${quotedTable} ()`;\n }\n\n private dropTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `DROP TABLE ${quotedTable} CASCADE`;\n }\n\n private dropTableIfExists(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `DROP TABLE IF EXISTS ${quotedTable} CASCADE`;\n }\n\n private renameTable(from: string, to: string): string {\n const quotedFrom = this.dialect.quoteIdentifier(from);\n const quotedTo = this.dialect.quoteIdentifier(to);\n return `ALTER TABLE ${quotedFrom} RENAME TO ${quotedTo}`;\n }\n\n private truncateTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `TRUNCATE TABLE ${quotedTable}`;\n }\n\n // ============================================================================\n // COLUMN OPERATIONS\n // ============================================================================\n\n private addColumn(table: string, column: ColumnDefinition): string | string[] {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column.name);\n\n let sqlType: string;\n if (column.autoIncrement) {\n if (column.type === \"bigInteger\") {\n sqlType = \"BIGSERIAL\";\n } else {\n sqlType = \"SERIAL\";\n }\n } else {\n sqlType = this.dialect.getSqlType(column.type, {\n length: column.length,\n precision: column.precision,\n scale: column.scale,\n dimensions: column.dimensions,\n });\n }\n\n let sql = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedColumn} ${sqlType}`;\n\n if (column.generated) {\n sql += ` GENERATED ALWAYS AS (${column.generated.expression})`;\n if (column.generated.stored) {\n sql += \" STORED\";\n }\n } else {\n if (!column.autoIncrement && column.nullable === false) {\n sql += \" NOT NULL\";\n }\n\n if (column.defaultValue !== undefined) {\n if (\n typeof column.defaultValue === \"object\" &&\n column.defaultValue !== null &&\n (column.defaultValue as any).__type === \"CURRENT_TIMESTAMP\"\n ) {\n sql += \" DEFAULT NOW()\";\n } else if (column.isRawDefault === false) {\n const escaped = String(column.defaultValue).replace(/'/g, \"''\");\n sql += ` DEFAULT '${escaped}'`;\n } else if (typeof column.defaultValue === \"boolean\") {\n sql += ` DEFAULT ${column.defaultValue ? \"TRUE\" : \"FALSE\"}`;\n } else if (typeof column.defaultValue === \"number\") {\n sql += ` DEFAULT ${column.defaultValue}`;\n } else {\n sql += ` DEFAULT ${column.defaultValue}`; // raw default\n }\n }\n\n if (column.primary) {\n sql += \" PRIMARY KEY\";\n }\n\n if (column.unique) {\n sql += \" UNIQUE\";\n }\n }\n\n const statements: string[] = [sql];\n\n // Column-level `.check(...)` becomes a table CHECK constraint emitted as a\n // follow-up ALTER — mirrors the direct driver path (Migration.execute →\n // driver.addColumn + driver.addCheck).\n if (column.checkConstraint) {\n statements.push(\n this.addCheck(table, column.checkConstraint.name, column.checkConstraint.expression),\n );\n }\n\n // pgvector: ensure the extension is active before the vector column is created.\n // SQLGrammar classifies CREATE EXTENSION as Phase 1, so this will always\n // run before any CREATE TABLE / ADD COLUMN statement regardless of batch order.\n if (column.type === \"vector\") {\n return [\"CREATE EXTENSION IF NOT EXISTS vector\", ...statements];\n }\n\n // The length test does not narrow the index read. `?? statements` keeps\n // the same two outcomes: the single statement, or the list.\n return statements.length === 1 ? (statements[0] ?? statements) : statements;\n }\n\n private dropColumn(table: string, column: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n return `ALTER TABLE ${quotedTable} DROP COLUMN ${quotedColumn}`;\n }\n\n private dropColumns(table: string, columns: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const drops = columns.map((col) => `DROP COLUMN ${this.dialect.quoteIdentifier(col)}`).join(\", \");\n return `ALTER TABLE ${quotedTable} ${drops}`;\n }\n\n private renameColumn(table: string, from: string, to: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedFrom = this.dialect.quoteIdentifier(from);\n const quotedTo = this.dialect.quoteIdentifier(to);\n return `ALTER TABLE ${quotedTable} RENAME COLUMN ${quotedFrom} TO ${quotedTo}`;\n }\n\n private modifyColumn(table: string, column: ColumnDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column.name);\n const sqlType = this.dialect.getSqlType(column.type, {\n length: column.length,\n precision: column.precision,\n scale: column.scale,\n dimensions: column.dimensions,\n });\n\n const alters: string[] = [];\n alters.push(`ALTER COLUMN ${quotedColumn} TYPE ${sqlType}`);\n\n if (column.nullable === false) {\n alters.push(`ALTER COLUMN ${quotedColumn} SET NOT NULL`);\n } else if (column.nullable === true) {\n alters.push(`ALTER COLUMN ${quotedColumn} DROP NOT NULL`);\n }\n\n if (column.defaultValue !== undefined) {\n let defaultVal: string;\n if (\n typeof column.defaultValue === \"object\" &&\n column.defaultValue !== null &&\n (column.defaultValue as any).__type === \"CURRENT_TIMESTAMP\"\n ) {\n defaultVal = \"NOW()\";\n } else if (column.isRawDefault === false) {\n defaultVal = `'${String(column.defaultValue).replace(/'/g, \"''\")}'`;\n } else if (typeof column.defaultValue === \"boolean\") {\n defaultVal = column.defaultValue ? \"TRUE\" : \"FALSE\";\n } else {\n defaultVal = String(column.defaultValue);\n }\n alters.push(`ALTER COLUMN ${quotedColumn} SET DEFAULT ${defaultVal}`);\n }\n\n return `ALTER TABLE ${quotedTable} ${alters.join(\", \")}`;\n }\n\n private createTimestamps(table: string): string[] {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return [\n `ALTER TABLE ${quotedTable} ADD COLUMN \"created_at\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n `ALTER TABLE ${quotedTable} ADD COLUMN \"updated_at\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n ];\n }\n\n // ============================================================================\n // INDEX OPERATIONS\n // ============================================================================\n\n private createIndex(table: string, index: IndexDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n let indexName: string;\n if (index.name) {\n indexName = index.name;\n } else {\n const colStr = Array.isArray(index.columns) ? index.columns.join(\"_\") : index.columns;\n indexName = `idx_${table}_${colStr}`;\n }\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n const uniqueKeyword = index.unique ? \"UNIQUE \" : \"\";\n const concurrentlyKeyword = index.concurrently ? \"CONCURRENTLY \" : \"\";\n\n let columnsPart: string;\n\n if (index.expressions && index.expressions.length > 0) {\n columnsPart = index.expressions.map((expr) => `(${expr})`).join(\", \");\n } else {\n const columns = index.columns.map((col, i) => {\n const quotedCol = this.dialect.quoteIdentifier(col);\n const direction = index.directions?.[i]?.toUpperCase() ?? \"\";\n return direction ? `${quotedCol} ${direction}` : quotedCol;\n });\n columnsPart = columns.join(\", \");\n }\n\n let sql = `CREATE ${uniqueKeyword}INDEX ${concurrentlyKeyword}${quotedIndexName} ON ${quotedTable} (${columnsPart})`;\n\n if (index.include && index.include.length > 0) {\n const includeCols = index.include.map((col) => this.dialect.quoteIdentifier(col)).join(\", \");\n sql += ` INCLUDE (${includeCols})`;\n }\n\n if (index.where && Object.keys(index.where).length > 0) {\n const conditions = Object.entries(index.where)\n .map(([key, value]) => {\n const quotedKey = this.dialect.quoteIdentifier(key);\n return typeof value === \"string\" ? `${quotedKey} = '${value}'` : `${quotedKey} = ${value}`;\n })\n .join(\" AND \");\n sql += ` WHERE ${conditions}`;\n }\n\n return sql;\n }\n\n private dropIndex(table: string, indexNameOrColumns: string | string[]): string {\n let indexName: string;\n if (typeof indexNameOrColumns === \"string\") {\n indexName = indexNameOrColumns;\n } else {\n indexName = `idx_${table}_${indexNameOrColumns.join(\"_\")}`;\n }\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n return `DROP INDEX IF EXISTS ${quotedIndexName}`;\n }\n\n private createFullTextIndex(table: string, columns: string[], options?: FullTextIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const indexName = options?.name ?? `idx_${table}_fulltext_${columns.join(\"_\")}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n const language = options?.language ?? \"english\";\n\n const tsvectors = columns.map((col) => {\n const weight = options?.weights?.[col] ?? \"A\";\n return `setweight(to_tsvector('${language}', COALESCE(${this.dialect.quoteIdentifier(col)}, '')), '${weight}')`;\n });\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING GIN ((${tsvectors.join(\" || \")}))`;\n }\n\n private createGeoIndex(table: string, column: string, options?: GeoIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = options?.name ?? `idx_${table}_geo_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING GIST (${quotedColumn})`;\n }\n\n private createVectorIndex(table: string, column: string, options: VectorIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = options.name ?? `idx_${table}_vector_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n const opClass =\n options.similarity === \"euclidean\"\n ? \"vector_l2_ops\"\n : options.similarity === \"dotProduct\"\n ? \"vector_ip_ops\"\n : \"vector_cosine_ops\";\n\n const lists = options.lists ?? 100;\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING ivfflat (${quotedColumn} ${opClass}) WITH (lists = ${lists})`;\n }\n\n private createTTLIndex(table: string, column: string, expireAfterSeconds: number): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = `idx_${table}_ttl_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} (${quotedColumn})`;\n }\n\n // ============================================================================\n // CONSTRAINTS\n // ============================================================================\n\n private addForeignKey(table: string, foreignKey: ForeignKeyDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(foreignKey.column);\n const quotedRefTable = this.dialect.quoteIdentifier(foreignKey.referencesTable);\n const quotedRefColumn = this.dialect.quoteIdentifier(foreignKey.referencesColumn);\n\n const constraintName =\n foreignKey.name ?? `fk_${table}_${foreignKey.column}_${foreignKey.referencesTable}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n let sql = `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedConstraint} FOREIGN KEY (${quotedColumn}) REFERENCES ${quotedRefTable} (${quotedRefColumn})`;\n\n if (foreignKey.onDelete) {\n sql += ` ON DELETE ${this.mapForeignKeyAction(foreignKey.onDelete)}`;\n }\n\n if (foreignKey.onUpdate) {\n sql += ` ON UPDATE ${this.mapForeignKeyAction(foreignKey.onUpdate)}`;\n }\n\n return sql;\n }\n\n private dropForeignKey(table: string, name: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedConstraint = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedConstraint}`;\n }\n\n private addPrimaryKey(table: string, columns: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(\", \");\n const constraintName = `pk_${table}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n return `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedConstraint} PRIMARY KEY (${quotedColumns})`;\n }\n\n private dropPrimaryKey(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const constraintName = `pk_${table}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedConstraint}`;\n }\n\n private addCheck(table: string, name: string, expression: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedName = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedName} CHECK (${expression})`;\n }\n\n private dropCheck(table: string, name: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedName = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedName}`;\n }\n\n private mapForeignKeyAction(\n action: \"cascade\" | \"restrict\" | \"setNull\" | \"noAction\",\n ): string {\n switch (action) {\n case \"cascade\": return \"CASCADE\";\n case \"restrict\": return \"RESTRICT\";\n case \"setNull\": return \"SET NULL\";\n case \"noAction\": return \"NO ACTION\";\n default: return \"NO ACTION\";\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-sql-serializer.mjs","names":[],"sources":["../../../../../../../../../packages/cascade/src/drivers/postgres/postgres-sql-serializer.ts"],"sourcesContent":["import type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\nimport type { PendingOperation } from \"../../migration/migration\";\nimport { SQLSerializer } from \"../../migration/sql-serializer\";\nimport type {\n ColumnDefinition,\n ForeignKeyDefinition,\n FullTextIndexOptions,\n GeoIndexOptions,\n IndexDefinition,\n VectorIndexOptions,\n} from \"../../contracts/migration-driver.contract\";\n\n/**\n * PostgreSQL-specific SQL serializer.\n *\n * Converts pending migration operations into valid PostgreSQL DDL statements.\n */\nexport class PostgresSQLSerializer extends SQLSerializer {\n public constructor(private readonly dialect: SqlDialectContract) {\n super();\n }\n\n public serialize(operation: PendingOperation, table: string): string | string[] | null {\n switch (operation.type) {\n case \"createTable\":\n return this.createTable(table);\n case \"createTableIfNotExists\":\n return this.createTableIfNotExists(table);\n case \"dropTable\":\n return this.dropTable(table);\n case \"dropTableIfExists\":\n return this.dropTableIfExists(table);\n case \"renameTable\":\n return this.renameTable(table, operation.payload as string);\n case \"truncateTable\":\n return this.truncateTable(table);\n case \"addColumn\":\n return this.addColumn(table, operation.payload as ColumnDefinition);\n case \"dropColumn\":\n return this.dropColumn(table, operation.payload as string);\n case \"dropColumns\":\n // This is handled via map in serializeAll or loop, but Postgres supports multiple DROP COLUMN\n // For simplicity, we can serialize multiple statements if we want, or handle it here\n // Actually, dropColumns payload is string[]. We can join them.\n return this.dropColumns(table, operation.payload as string[]);\n case \"renameColumn\": {\n const payload = operation.payload as { from: string; to: string };\n return this.renameColumn(table, payload.from, payload.to);\n }\n case \"modifyColumn\":\n // Modify column might require multiple statements in Postgres (type, nullability, default).\n // SQLSerializer's serialize return is just a string, so we'll separate them by semicolon\n // Or wait, Postgres allows multiple ALTER COLUMN in one ALTER TABLE statement.\n return this.modifyColumn(table, operation.payload as ColumnDefinition);\n case \"createIndex\":\n return this.createIndex(table, operation.payload as IndexDefinition);\n case \"dropIndex\":\n return this.dropIndex(table, operation.payload as string | string[]);\n case \"createUniqueIndex\": {\n const payload = operation.payload as { columns: string[]; name?: string };\n return this.createIndex(table, { columns: payload.columns, name: payload.name, unique: true });\n }\n case \"dropUniqueIndex\":\n return this.dropIndex(table, operation.payload as string[]);\n case \"createFullTextIndex\": {\n const payload = operation.payload as { columns: string[]; options?: FullTextIndexOptions };\n return this.createFullTextIndex(table, payload.columns, payload.options);\n }\n case \"dropFullTextIndex\":\n return this.dropIndex(table, operation.payload as string);\n case \"createGeoIndex\": {\n const payload = operation.payload as { column: string; options?: GeoIndexOptions };\n return this.createGeoIndex(table, payload.column, payload.options);\n }\n case \"dropGeoIndex\":\n return this.dropIndex(table, `idx_${table}_geo_${operation.payload}`);\n case \"createVectorIndex\": {\n const payload = operation.payload as { column: string; options: VectorIndexOptions };\n return this.createVectorIndex(table, payload.column, payload.options);\n }\n case \"dropVectorIndex\":\n return this.dropIndex(table, `idx_${table}_vector_${operation.payload}`);\n case \"createTTLIndex\": {\n const payload = operation.payload as { column: string; expireAfterSeconds: number };\n return this.createTTLIndex(table, payload.column, payload.expireAfterSeconds);\n }\n case \"dropTTLIndex\":\n return this.dropIndex(table, `idx_${table}_ttl_${operation.payload}`);\n case \"addForeignKey\":\n return this.addForeignKey(table, operation.payload as ForeignKeyDefinition);\n case \"dropForeignKey\":\n return this.dropForeignKey(table, operation.payload as string);\n case \"addPrimaryKey\":\n return this.addPrimaryKey(table, operation.payload as string[]);\n case \"dropPrimaryKey\":\n return this.dropPrimaryKey(table);\n case \"addCheck\": {\n const payload = operation.payload as { name: string; expression: string };\n return this.addCheck(table, payload.name, payload.expression);\n }\n case \"dropCheck\":\n return this.dropCheck(table, operation.payload as string);\n case \"createTimestamps\":\n // createTimestamps needs to inject two columns. We return two statements.\n return this.createTimestamps(table);\n case \"rawStatement\": {\n const payload = operation.payload as string;\n // ensure string doesn't end with a semicolon if it's already one statement\n return payload;\n }\n case \"setSchemaValidation\":\n case \"removeSchemaValidation\":\n // Not implemented in PostgreSQL\n return null;\n default:\n return null;\n }\n }\n\n // ============================================================================\n // TABLE OPERATIONS\n // ============================================================================\n\n private createTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `CREATE TABLE ${quotedTable} ()`;\n }\n\n private createTableIfNotExists(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `CREATE TABLE IF NOT EXISTS ${quotedTable} ()`;\n }\n\n private dropTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `DROP TABLE ${quotedTable} CASCADE`;\n }\n\n private dropTableIfExists(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `DROP TABLE IF EXISTS ${quotedTable} CASCADE`;\n }\n\n private renameTable(from: string, to: string): string {\n const quotedFrom = this.dialect.quoteIdentifier(from);\n const quotedTo = this.dialect.quoteIdentifier(to);\n return `ALTER TABLE ${quotedFrom} RENAME TO ${quotedTo}`;\n }\n\n private truncateTable(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return `TRUNCATE TABLE ${quotedTable}`;\n }\n\n // ============================================================================\n // COLUMN OPERATIONS\n // ============================================================================\n\n private addColumn(table: string, column: ColumnDefinition): string | string[] {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column.name);\n\n let sqlType: string;\n if (column.autoIncrement) {\n if (column.type === \"bigInteger\") {\n sqlType = \"BIGSERIAL\";\n } else {\n sqlType = \"SERIAL\";\n }\n } else {\n sqlType = this.dialect.getSqlType(column.type, {\n length: column.length,\n precision: column.precision,\n scale: column.scale,\n dimensions: column.dimensions,\n });\n }\n\n let sql = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedColumn} ${sqlType}`;\n\n if (column.generated) {\n sql += ` GENERATED ALWAYS AS (${column.generated.expression})`;\n if (column.generated.stored) {\n sql += \" STORED\";\n }\n } else {\n if (!column.autoIncrement && column.nullable === false) {\n sql += \" NOT NULL\";\n }\n\n if (column.defaultValue !== undefined) {\n if (\n typeof column.defaultValue === \"object\" &&\n column.defaultValue !== null &&\n (column.defaultValue as any).__type === \"CURRENT_TIMESTAMP\"\n ) {\n sql += \" DEFAULT NOW()\";\n } else if (column.isRawDefault === false) {\n const escaped = String(column.defaultValue).replace(/'/g, \"''\");\n sql += ` DEFAULT '${escaped}'`;\n } else if (typeof column.defaultValue === \"boolean\") {\n sql += ` DEFAULT ${column.defaultValue ? \"TRUE\" : \"FALSE\"}`;\n } else if (typeof column.defaultValue === \"number\") {\n sql += ` DEFAULT ${column.defaultValue}`;\n } else {\n sql += ` DEFAULT ${column.defaultValue}`; // raw default\n }\n }\n\n if (column.primary) {\n sql += \" PRIMARY KEY\";\n }\n\n if (column.unique) {\n sql += \" UNIQUE\";\n }\n }\n\n const statements: string[] = [sql];\n\n // Column-level `.check(...)` becomes a table CHECK constraint emitted as a\n // follow-up ALTER — mirrors the direct driver path (Migration.execute →\n // driver.addColumn + driver.addCheck).\n if (column.checkConstraint) {\n statements.push(\n this.addCheck(table, column.checkConstraint.name, column.checkConstraint.expression),\n );\n }\n\n // pgvector: ensure the extension is active before the vector column is created.\n // SQLGrammar classifies CREATE EXTENSION as Phase 1, so this will always\n // run before any CREATE TABLE / ADD COLUMN statement regardless of batch order.\n if (column.type === \"vector\") {\n return [\"CREATE EXTENSION IF NOT EXISTS vector\", ...statements];\n }\n\n // The length test does not narrow the index read. `?? statements` keeps\n // the same two outcomes: the single statement, or the list.\n return statements.length === 1 ? (statements[0] ?? statements) : statements;\n }\n\n private dropColumn(table: string, column: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n return `ALTER TABLE ${quotedTable} DROP COLUMN ${quotedColumn}`;\n }\n\n private dropColumns(table: string, columns: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const drops = columns.map((col) => `DROP COLUMN ${this.dialect.quoteIdentifier(col)}`).join(\", \");\n return `ALTER TABLE ${quotedTable} ${drops}`;\n }\n\n private renameColumn(table: string, from: string, to: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedFrom = this.dialect.quoteIdentifier(from);\n const quotedTo = this.dialect.quoteIdentifier(to);\n return `ALTER TABLE ${quotedTable} RENAME COLUMN ${quotedFrom} TO ${quotedTo}`;\n }\n\n private modifyColumn(table: string, column: ColumnDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column.name);\n const sqlType = this.dialect.getSqlType(column.type, {\n length: column.length,\n precision: column.precision,\n scale: column.scale,\n dimensions: column.dimensions,\n });\n\n const alters: string[] = [];\n alters.push(`ALTER COLUMN ${quotedColumn} TYPE ${sqlType}`);\n\n if (column.nullable === false) {\n alters.push(`ALTER COLUMN ${quotedColumn} SET NOT NULL`);\n } else if (column.nullable === true) {\n alters.push(`ALTER COLUMN ${quotedColumn} DROP NOT NULL`);\n }\n\n if (column.defaultValue !== undefined) {\n let defaultVal: string;\n if (\n typeof column.defaultValue === \"object\" &&\n column.defaultValue !== null &&\n (column.defaultValue as any).__type === \"CURRENT_TIMESTAMP\"\n ) {\n defaultVal = \"NOW()\";\n } else if (column.isRawDefault === false) {\n defaultVal = `'${String(column.defaultValue).replace(/'/g, \"''\")}'`;\n } else if (typeof column.defaultValue === \"boolean\") {\n defaultVal = column.defaultValue ? \"TRUE\" : \"FALSE\";\n } else {\n defaultVal = String(column.defaultValue);\n }\n alters.push(`ALTER COLUMN ${quotedColumn} SET DEFAULT ${defaultVal}`);\n }\n\n return `ALTER TABLE ${quotedTable} ${alters.join(\", \")}`;\n }\n\n private createTimestamps(table: string): string[] {\n const quotedTable = this.dialect.quoteIdentifier(table);\n return [\n `ALTER TABLE ${quotedTable} ADD COLUMN \"created_at\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n `ALTER TABLE ${quotedTable} ADD COLUMN \"updated_at\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n ];\n }\n\n // ============================================================================\n // INDEX OPERATIONS\n // ============================================================================\n\n private createIndex(table: string, index: IndexDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n let indexName: string;\n if (index.name) {\n indexName = index.name;\n } else {\n const colStr = Array.isArray(index.columns) ? index.columns.join(\"_\") : index.columns;\n indexName = `idx_${table}_${colStr}`;\n }\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n const uniqueKeyword = index.unique ? \"UNIQUE \" : \"\";\n const concurrentlyKeyword = index.concurrently ? \"CONCURRENTLY \" : \"\";\n\n let columnsPart: string;\n\n if (index.expressions && index.expressions.length > 0) {\n columnsPart = index.expressions.map((expr) => `(${expr})`).join(\", \");\n } else {\n const columns = index.columns.map((col, i) => {\n const quotedCol = this.dialect.quoteIdentifier(col);\n const direction = index.directions?.[i]?.toUpperCase() ?? \"\";\n return direction ? `${quotedCol} ${direction}` : quotedCol;\n });\n columnsPart = columns.join(\", \");\n }\n\n let sql = `CREATE ${uniqueKeyword}INDEX ${concurrentlyKeyword}${quotedIndexName} ON ${quotedTable} (${columnsPart})`;\n\n if (index.include && index.include.length > 0) {\n const includeCols = index.include.map((col) => this.dialect.quoteIdentifier(col)).join(\", \");\n sql += ` INCLUDE (${includeCols})`;\n }\n\n if (index.where && Object.keys(index.where).length > 0) {\n const conditions = Object.entries(index.where)\n .map(([key, value]) => {\n const quotedKey = this.dialect.quoteIdentifier(key);\n return typeof value === \"string\" ? `${quotedKey} = '${value}'` : `${quotedKey} = ${value}`;\n })\n .join(\" AND \");\n sql += ` WHERE ${conditions}`;\n }\n\n return sql;\n }\n\n private dropIndex(table: string, indexNameOrColumns: string | string[]): string {\n let indexName: string;\n if (typeof indexNameOrColumns === \"string\") {\n indexName = indexNameOrColumns;\n } else {\n indexName = `idx_${table}_${indexNameOrColumns.join(\"_\")}`;\n }\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n return `DROP INDEX IF EXISTS ${quotedIndexName}`;\n }\n\n private createFullTextIndex(table: string, columns: string[], options?: FullTextIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const indexName = options?.name ?? `idx_${table}_fulltext_${columns.join(\"_\")}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n const language = options?.language ?? \"english\";\n\n const tsvectors = columns.map((col) => {\n const weight = options?.weights?.[col] ?? \"A\";\n return `setweight(to_tsvector('${language}', COALESCE(${this.dialect.quoteIdentifier(col)}, '')), '${weight}')`;\n });\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING GIN ((${tsvectors.join(\" || \")}))`;\n }\n\n private createGeoIndex(table: string, column: string, options?: GeoIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = options?.name ?? `idx_${table}_geo_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING GIST (${quotedColumn})`;\n }\n\n private createVectorIndex(table: string, column: string, options: VectorIndexOptions): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = options.name ?? `idx_${table}_vector_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n const opClass =\n options.similarity === \"euclidean\"\n ? \"vector_l2_ops\"\n : options.similarity === \"dotProduct\"\n ? \"vector_ip_ops\"\n : \"vector_cosine_ops\";\n\n const lists = options.lists ?? 100;\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} USING ivfflat (${quotedColumn} ${opClass}) WITH (lists = ${lists})`;\n }\n\n private createTTLIndex(table: string, column: string, expireAfterSeconds: number): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n const indexName = `idx_${table}_ttl_${column}`;\n const quotedIndexName = this.dialect.quoteIdentifier(indexName);\n\n return `CREATE INDEX ${quotedIndexName} ON ${quotedTable} (${quotedColumn})`;\n }\n\n // ============================================================================\n // CONSTRAINTS\n // ============================================================================\n\n private addForeignKey(table: string, foreignKey: ForeignKeyDefinition): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(foreignKey.column);\n const quotedRefTable = this.dialect.quoteIdentifier(foreignKey.referencesTable);\n const quotedRefColumn = this.dialect.quoteIdentifier(foreignKey.referencesColumn);\n\n const constraintName =\n foreignKey.name ?? `fk_${table}_${foreignKey.column}_${foreignKey.referencesTable}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n let sql = `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedConstraint} FOREIGN KEY (${quotedColumn}) REFERENCES ${quotedRefTable} (${quotedRefColumn})`;\n\n if (foreignKey.onDelete) {\n sql += ` ON DELETE ${this.mapForeignKeyAction(foreignKey.onDelete)}`;\n }\n\n if (foreignKey.onUpdate) {\n sql += ` ON UPDATE ${this.mapForeignKeyAction(foreignKey.onUpdate)}`;\n }\n\n return sql;\n }\n\n private dropForeignKey(table: string, name: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedConstraint = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedConstraint}`;\n }\n\n private addPrimaryKey(table: string, columns: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumns = columns.map((c) => this.dialect.quoteIdentifier(c)).join(\", \");\n const constraintName = `pk_${table}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n return `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedConstraint} PRIMARY KEY (${quotedColumns})`;\n }\n\n private dropPrimaryKey(table: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const constraintName = `pk_${table}`;\n const quotedConstraint = this.dialect.quoteIdentifier(constraintName);\n\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedConstraint}`;\n }\n\n private addCheck(table: string, name: string, expression: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedName = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} ADD CONSTRAINT ${quotedName} CHECK (${expression})`;\n }\n\n private dropCheck(table: string, name: string): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedName = this.dialect.quoteIdentifier(name);\n return `ALTER TABLE ${quotedTable} DROP CONSTRAINT ${quotedName}`;\n }\n\n private mapForeignKeyAction(\n action: \"cascade\" | \"restrict\" | \"setNull\" | \"noAction\",\n ): string {\n switch (action) {\n case \"cascade\": return \"CASCADE\";\n case \"restrict\": return \"RESTRICT\";\n case \"setNull\": return \"SET NULL\";\n case \"noAction\": return \"NO ACTION\";\n default: return \"NO ACTION\";\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { job, scheduler } from "@warlock.js/scheduler";
2
2
 
3
- //#region ../cascade/src/drivers/postgres/postgres-ttl-purge.ts
3
+ //#region ../../packages/cascade/src/drivers/postgres/postgres-ttl-purge.ts
4
4
  const TTL_PURGE_INTERVAL_MINUTES = 1;
5
5
  const TTL_COMMENT_PREFIX = "warlock_ttl:";
6
6
  let nextDriverId = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-ttl-purge.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-ttl-purge.ts"],"sourcesContent":["import { job, scheduler } from \"@warlock.js/scheduler\";\nimport type { PostgresDriver } from \"./postgres-driver\";\n\nconst TTL_PURGE_INTERVAL_MINUTES = 1;\nconst TTL_COMMENT_PREFIX = \"warlock_ttl:\";\n\nlet nextDriverId = 0;\nconst driverIds = new WeakMap<object, number>();\nconst driverJobs = new WeakMap<object, Set<string>>();\n\ntype PostgresTTLPurgeDriver = Pick<PostgresDriver, \"dialect\"> & {\n query(sql: string, params?: unknown[]): Promise<unknown>;\n};\n\nfunction getDriverId(driver: object): number {\n let id = driverIds.get(driver);\n\n if (id === undefined) {\n id = nextDriverId++;\n driverIds.set(driver, id);\n }\n\n return id;\n}\n\n/** Persist the retention rule with the index so a restarted process can restore its job. */\nexport function postgresTTLPurgeComment(table: string, column: string, expireAfterSeconds: number): string {\n return TTL_COMMENT_PREFIX + JSON.stringify({ table, column, expireAfterSeconds });\n}\n\n/**\n * Return the scheduler job name for one PostgreSQL TTL index.\n *\n * The driver id prevents two data sources with identically named tables from\n * replacing each other's purge jobs on the shared scheduler singleton.\n */\nexport function postgresTTLPurgeJobName(driver: object, table: string, column: string): string {\n return `cascade:postgres:${getDriverId(driver)}:ttl:${table}:${column}`;\n}\n\n/**\n * Delete rows which have outlived a PostgreSQL TTL index's retention period.\n *\n * Identifiers are produced by the dialect, while the retention interval stays\n * parameterized so a migration value can never become executable SQL.\n */\nexport async function purgeExpiredPostgresRows(\n driver: PostgresTTLPurgeDriver,\n table: string,\n column: string,\n expireAfterSeconds: number,\n): Promise<void> {\n const quotedTable = driver.dialect.quoteIdentifier(table);\n const quotedColumn = driver.dialect.quoteIdentifier(column);\n\n await driver.query(\n `DELETE FROM ${quotedTable} WHERE ${quotedColumn} < NOW() - ($1 * INTERVAL '1 second')`,\n [expireAfterSeconds],\n );\n}\n\n/**\n * Register the in-process scheduler purge job for a PostgreSQL TTL index.\n *\n * The application owns the scheduler lifecycle: callers must start the shared\n * scheduler during bootstrap. Re-registering replaces a previous job for the\n * same driver/index so repeated migration setup cannot leak callbacks.\n */\nexport function registerPostgresTTLPurgeJob(\n driver: PostgresTTLPurgeDriver,\n table: string,\n column: string,\n expireAfterSeconds: number,\n): void {\n const name = postgresTTLPurgeJobName(driver, table, column);\n\n scheduler.removeJob(name);\n scheduler.addJob(\n job(name, () => purgeExpiredPostgresRows(driver, table, column, expireAfterSeconds)).everyMinutes(\n TTL_PURGE_INTERVAL_MINUTES,\n ),\n );\n let names = driverJobs.get(driver);\n if (!names) {\n names = new Set();\n driverJobs.set(driver, names);\n }\n names.add(name);\n}\n\n/** Remove the scheduler callback associated with a dropped PostgreSQL TTL index. */\nexport function unregisterPostgresTTLPurgeJob(driver: object, table: string, column: string): void {\n const name = postgresTTLPurgeJobName(driver, table, column);\n scheduler.removeJob(name);\n driverJobs.get(driver)?.delete(name);\n}\n\n/** Remove a driver's callbacks before its pool is closed. */\nexport function unregisterAllPostgresTTLPurgeJobs(driver: object): void {\n for (const name of driverJobs.get(driver) ?? []) scheduler.removeJob(name);\n driverJobs.delete(driver);\n}\n\n/** Restore registered retention rules from index comments on every connection. */\nexport async function loadPostgresTTLPurgeJobs(driver: PostgresTTLPurgeDriver): Promise<void> {\n const result = await driver.query(\n `SELECT d.description FROM pg_catalog.pg_description d\n JOIN pg_catalog.pg_class c ON c.oid = d.objoid\n JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'i' AND n.nspname = current_schema()\n AND d.description LIKE 'warlock_ttl:%'`,\n ) as { rows?: Array<{ description?: unknown }> };\n\n for (const row of result.rows ?? []) {\n if (typeof row.description !== \"string\" || !row.description.startsWith(TTL_COMMENT_PREFIX)) continue;\n let rule: unknown;\n try {\n rule = JSON.parse(row.description.slice(TTL_COMMENT_PREFIX.length));\n } catch {\n continue;\n }\n if (!rule || typeof rule !== \"object\") continue;\n const { table, column, expireAfterSeconds } = rule as Record<string, unknown>;\n if (\n typeof table !== \"string\" || table.length === 0 ||\n typeof column !== \"string\" || column.length === 0 ||\n !Number.isSafeInteger(expireAfterSeconds) || (expireAfterSeconds as number) < 0\n ) continue;\n registerPostgresTTLPurgeJob(driver, table, column, expireAfterSeconds as number);\n }\n}\n"],"mappings":";;;AAGA,MAAM,6BAA6B;AACnC,MAAM,qBAAqB;AAE3B,IAAI,eAAe;AACnB,MAAM,4BAAY,IAAI,QAAwB;AAC9C,MAAM,6BAAa,IAAI,QAA6B;AAMpD,SAAS,YAAY,QAAwB;CAC3C,IAAI,KAAK,UAAU,IAAI,MAAM;CAE7B,IAAI,OAAO,QAAW;EACpB,KAAK;EACL,UAAU,IAAI,QAAQ,EAAE;CAC1B;CAEA,OAAO;AACT;;AAGA,SAAgB,wBAAwB,OAAe,QAAgB,oBAAoC;CACzG,OAAO,qBAAqB,KAAK,UAAU;EAAE;EAAO;EAAQ;CAAmB,CAAC;AAClF;;;;;;;AAQA,SAAgB,wBAAwB,QAAgB,OAAe,QAAwB;CAC7F,OAAO,oBAAoB,YAAY,MAAM,EAAE,OAAO,MAAM,GAAG;AACjE;;;;;;;AAQA,eAAsB,yBACpB,QACA,OACA,QACA,oBACe;CACf,MAAM,cAAc,OAAO,QAAQ,gBAAgB,KAAK;CACxD,MAAM,eAAe,OAAO,QAAQ,gBAAgB,MAAM;CAE1D,MAAM,OAAO,MACX,eAAe,YAAY,SAAS,aAAa,wCACjD,CAAC,kBAAkB,CACrB;AACF;;;;;;;;AASA,SAAgB,4BACd,QACA,OACA,QACA,oBACM;CACN,MAAM,OAAO,wBAAwB,QAAQ,OAAO,MAAM;CAE1D,UAAU,UAAU,IAAI;CACxB,UAAU,OACR,IAAI,YAAY,yBAAyB,QAAQ,OAAO,QAAQ,kBAAkB,CAAC,CAAC,CAAC,aACnF,0BACF,CACF;CACA,IAAI,QAAQ,WAAW,IAAI,MAAM;CACjC,IAAI,CAAC,OAAO;EACV,wBAAQ,IAAI,IAAI;EAChB,WAAW,IAAI,QAAQ,KAAK;CAC9B;CACA,MAAM,IAAI,IAAI;AAChB;;AAGA,SAAgB,8BAA8B,QAAgB,OAAe,QAAsB;CACjG,MAAM,OAAO,wBAAwB,QAAQ,OAAO,MAAM;CAC1D,UAAU,UAAU,IAAI;CACxB,WAAW,IAAI,MAAM,CAAC,EAAE,OAAO,IAAI;AACrC;;AAGA,SAAgB,kCAAkC,QAAsB;CACtE,KAAK,MAAM,QAAQ,WAAW,IAAI,MAAM,KAAK,CAAC,GAAG,UAAU,UAAU,IAAI;CACzE,WAAW,OAAO,MAAM;AAC1B;;AAGA,eAAsB,yBAAyB,QAA+C;CAC5F,MAAM,SAAS,MAAM,OAAO,MAC1B;;;;+CAKF;CAEA,KAAK,MAAM,OAAO,OAAO,QAAQ,CAAC,GAAG;EACnC,IAAI,OAAO,IAAI,gBAAgB,YAAY,CAAC,IAAI,YAAY,WAAW,kBAAkB,GAAG;EAC5F,IAAI;EACJ,IAAI;GACF,OAAO,KAAK,MAAM,IAAI,YAAY,MAAM,EAAyB,CAAC;EACpE,QAAQ;GACN;EACF;EACA,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EACvC,MAAM,EAAE,OAAO,QAAQ,uBAAuB;EAC9C,IACE,OAAO,UAAU,YAAY,MAAM,WAAW,KAC9C,OAAO,WAAW,YAAY,OAAO,WAAW,KAChD,CAAC,OAAO,cAAc,kBAAkB,KAAM,qBAAgC,GAC9E;EACF,4BAA4B,QAAQ,OAAO,QAAQ,kBAA4B;CACjF;AACF"}
1
+ {"version":3,"file":"postgres-ttl-purge.mjs","names":[],"sources":["../../../../../../../../../packages/cascade/src/drivers/postgres/postgres-ttl-purge.ts"],"sourcesContent":["import { job, scheduler } from \"@warlock.js/scheduler\";\nimport type { PostgresDriver } from \"./postgres-driver\";\n\nconst TTL_PURGE_INTERVAL_MINUTES = 1;\nconst TTL_COMMENT_PREFIX = \"warlock_ttl:\";\n\nlet nextDriverId = 0;\nconst driverIds = new WeakMap<object, number>();\nconst driverJobs = new WeakMap<object, Set<string>>();\n\ntype PostgresTTLPurgeDriver = Pick<PostgresDriver, \"dialect\"> & {\n query(sql: string, params?: unknown[]): Promise<unknown>;\n};\n\nfunction getDriverId(driver: object): number {\n let id = driverIds.get(driver);\n\n if (id === undefined) {\n id = nextDriverId++;\n driverIds.set(driver, id);\n }\n\n return id;\n}\n\n/** Persist the retention rule with the index so a restarted process can restore its job. */\nexport function postgresTTLPurgeComment(table: string, column: string, expireAfterSeconds: number): string {\n return TTL_COMMENT_PREFIX + JSON.stringify({ table, column, expireAfterSeconds });\n}\n\n/**\n * Return the scheduler job name for one PostgreSQL TTL index.\n *\n * The driver id prevents two data sources with identically named tables from\n * replacing each other's purge jobs on the shared scheduler singleton.\n */\nexport function postgresTTLPurgeJobName(driver: object, table: string, column: string): string {\n return `cascade:postgres:${getDriverId(driver)}:ttl:${table}:${column}`;\n}\n\n/**\n * Delete rows which have outlived a PostgreSQL TTL index's retention period.\n *\n * Identifiers are produced by the dialect, while the retention interval stays\n * parameterized so a migration value can never become executable SQL.\n */\nexport async function purgeExpiredPostgresRows(\n driver: PostgresTTLPurgeDriver,\n table: string,\n column: string,\n expireAfterSeconds: number,\n): Promise<void> {\n const quotedTable = driver.dialect.quoteIdentifier(table);\n const quotedColumn = driver.dialect.quoteIdentifier(column);\n\n await driver.query(\n `DELETE FROM ${quotedTable} WHERE ${quotedColumn} < NOW() - ($1 * INTERVAL '1 second')`,\n [expireAfterSeconds],\n );\n}\n\n/**\n * Register the in-process scheduler purge job for a PostgreSQL TTL index.\n *\n * The application owns the scheduler lifecycle: callers must start the shared\n * scheduler during bootstrap. Re-registering replaces a previous job for the\n * same driver/index so repeated migration setup cannot leak callbacks.\n */\nexport function registerPostgresTTLPurgeJob(\n driver: PostgresTTLPurgeDriver,\n table: string,\n column: string,\n expireAfterSeconds: number,\n): void {\n const name = postgresTTLPurgeJobName(driver, table, column);\n\n scheduler.removeJob(name);\n scheduler.addJob(\n job(name, () => purgeExpiredPostgresRows(driver, table, column, expireAfterSeconds)).everyMinutes(\n TTL_PURGE_INTERVAL_MINUTES,\n ),\n );\n let names = driverJobs.get(driver);\n if (!names) {\n names = new Set();\n driverJobs.set(driver, names);\n }\n names.add(name);\n}\n\n/** Remove the scheduler callback associated with a dropped PostgreSQL TTL index. */\nexport function unregisterPostgresTTLPurgeJob(driver: object, table: string, column: string): void {\n const name = postgresTTLPurgeJobName(driver, table, column);\n scheduler.removeJob(name);\n driverJobs.get(driver)?.delete(name);\n}\n\n/** Remove a driver's callbacks before its pool is closed. */\nexport function unregisterAllPostgresTTLPurgeJobs(driver: object): void {\n for (const name of driverJobs.get(driver) ?? []) scheduler.removeJob(name);\n driverJobs.delete(driver);\n}\n\n/** Restore registered retention rules from index comments on every connection. */\nexport async function loadPostgresTTLPurgeJobs(driver: PostgresTTLPurgeDriver): Promise<void> {\n const result = await driver.query(\n `SELECT d.description FROM pg_catalog.pg_description d\n JOIN pg_catalog.pg_class c ON c.oid = d.objoid\n JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'i' AND n.nspname = current_schema()\n AND d.description LIKE 'warlock_ttl:%'`,\n ) as { rows?: Array<{ description?: unknown }> };\n\n for (const row of result.rows ?? []) {\n if (typeof row.description !== \"string\" || !row.description.startsWith(TTL_COMMENT_PREFIX)) continue;\n let rule: unknown;\n try {\n rule = JSON.parse(row.description.slice(TTL_COMMENT_PREFIX.length));\n } catch {\n continue;\n }\n if (!rule || typeof rule !== \"object\") continue;\n const { table, column, expireAfterSeconds } = rule as Record<string, unknown>;\n if (\n typeof table !== \"string\" || table.length === 0 ||\n typeof column !== \"string\" || column.length === 0 ||\n !Number.isSafeInteger(expireAfterSeconds) || (expireAfterSeconds as number) < 0\n ) continue;\n registerPostgresTTLPurgeJob(driver, table, column, expireAfterSeconds as number);\n }\n}\n"],"mappings":";;;AAGA,MAAM,6BAA6B;AACnC,MAAM,qBAAqB;AAE3B,IAAI,eAAe;AACnB,MAAM,4BAAY,IAAI,QAAwB;AAC9C,MAAM,6BAAa,IAAI,QAA6B;AAMpD,SAAS,YAAY,QAAwB;CAC3C,IAAI,KAAK,UAAU,IAAI,MAAM;CAE7B,IAAI,OAAO,QAAW;EACpB,KAAK;EACL,UAAU,IAAI,QAAQ,EAAE;CAC1B;CAEA,OAAO;AACT;;AAGA,SAAgB,wBAAwB,OAAe,QAAgB,oBAAoC;CACzG,OAAO,qBAAqB,KAAK,UAAU;EAAE;EAAO;EAAQ;CAAmB,CAAC;AAClF;;;;;;;AAQA,SAAgB,wBAAwB,QAAgB,OAAe,QAAwB;CAC7F,OAAO,oBAAoB,YAAY,MAAM,EAAE,OAAO,MAAM,GAAG;AACjE;;;;;;;AAQA,eAAsB,yBACpB,QACA,OACA,QACA,oBACe;CACf,MAAM,cAAc,OAAO,QAAQ,gBAAgB,KAAK;CACxD,MAAM,eAAe,OAAO,QAAQ,gBAAgB,MAAM;CAE1D,MAAM,OAAO,MACX,eAAe,YAAY,SAAS,aAAa,wCACjD,CAAC,kBAAkB,CACrB;AACF;;;;;;;;AASA,SAAgB,4BACd,QACA,OACA,QACA,oBACM;CACN,MAAM,OAAO,wBAAwB,QAAQ,OAAO,MAAM;CAE1D,UAAU,UAAU,IAAI;CACxB,UAAU,OACR,IAAI,YAAY,yBAAyB,QAAQ,OAAO,QAAQ,kBAAkB,CAAC,CAAC,CAAC,aACnF,0BACF,CACF;CACA,IAAI,QAAQ,WAAW,IAAI,MAAM;CACjC,IAAI,CAAC,OAAO;EACV,wBAAQ,IAAI,IAAI;EAChB,WAAW,IAAI,QAAQ,KAAK;CAC9B;CACA,MAAM,IAAI,IAAI;AAChB;;AAGA,SAAgB,8BAA8B,QAAgB,OAAe,QAAsB;CACjG,MAAM,OAAO,wBAAwB,QAAQ,OAAO,MAAM;CAC1D,UAAU,UAAU,IAAI;CACxB,WAAW,IAAI,MAAM,CAAC,EAAE,OAAO,IAAI;AACrC;;AAGA,SAAgB,kCAAkC,QAAsB;CACtE,KAAK,MAAM,QAAQ,WAAW,IAAI,MAAM,KAAK,CAAC,GAAG,UAAU,UAAU,IAAI;CACzE,WAAW,OAAO,MAAM;AAC1B;;AAGA,eAAsB,yBAAyB,QAA+C;CAC5F,MAAM,SAAS,MAAM,OAAO,MAC1B;;;;+CAKF;CAEA,KAAK,MAAM,OAAO,OAAO,QAAQ,CAAC,GAAG;EACnC,IAAI,OAAO,IAAI,gBAAgB,YAAY,CAAC,IAAI,YAAY,WAAW,kBAAkB,GAAG;EAC5F,IAAI;EACJ,IAAI;GACF,OAAO,KAAK,MAAM,IAAI,YAAY,MAAM,EAAyB,CAAC;EACpE,QAAQ;GACN;EACF;EACA,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;EACvC,MAAM,EAAE,OAAO,QAAQ,uBAAuB;EAC9C,IACE,OAAO,UAAU,YAAY,MAAM,WAAW,KAC9C,OAAO,WAAW,YAAY,OAAO,WAAW,KAChD,CAAC,OAAO,cAAc,kBAAkB,KAAM,qBAAgC,GAC9E;EACF,4BAA4B,QAAQ,OAAO,QAAQ,kBAA4B;CACjF;AACF"}
@@ -1,4 +1,4 @@
1
- //#region ../cascade/src/drivers/postgres/postgres-type-converters.ts
1
+ //#region ../../packages/cascade/src/drivers/postgres/postgres-type-converters.ts
2
2
  const toDate = (value) => new Date(value);
3
3
  const toSafeInteger = (value) => {
4
4
  const n = Number(value);
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-type-converters.mjs","names":[],"sources":["../../../../../../../../cascade/src/drivers/postgres/postgres-type-converters.ts"],"sourcesContent":["/**\n * Column-type driven conversion of PostgreSQL result values (K1:B29).\n *\n * Conversion is keyed on the column's `dataTypeID` from the pg result\n * `fields`, never on what a string value looks like.\n */\n\nexport type PostgresColumnTypes = ReadonlyMap<string, number>;\n\ntype Converter = (value: string) => unknown;\n\nconst toDate: Converter = (value) => new Date(value);\n\nconst toSafeInteger: Converter = (value) => {\n const n = Number(value);\n // Beyond 2^53 a number would silently lose digits; keep the exact string.\n return Number.isSafeInteger(n) ? n : value;\n};\n\nconst toFiniteNumber: Converter = (value) => {\n const n = Number(value);\n return Number.isFinite(n) ? n : value;\n};\n\n/** OID → converter. json/jsonb are already parsed by pg, so they are absent. */\nconst CONVERTERS = new Map<number, Converter>([\n [20, toSafeInteger], // int8\n [1700, toFiniteNumber], // numeric\n [1082, toDate], // date\n [1114, toDate], // timestamp\n [1184, toDate], // timestamptz\n]);\n\n/** Built-in OIDs whose text must never be reinterpreted (text, varchar, bpchar, name, json, jsonb, uuid). */\nconst LITERAL_TEXT_OIDS = new Set([18, 19, 25, 114, 142, 1042, 1043, 2950, 3802]);\n\n/** pgvector columns use an extension OID (not fixed), returned as '[n1,n2,...]'. */\nfunction parseVector(value: string): number[] | undefined {\n // '[' = 91, ']' = 93\n if (value.charCodeAt(0) !== 91 || value.charCodeAt(value.length - 1) !== 93) return undefined;\n\n const parts = value.slice(1, -1).split(\",\");\n const nums = new Array<number>(parts.length);\n\n for (let i = 0; i < parts.length; i++) {\n const n = +(parts[i] ?? \"\");\n if (!Number.isFinite(n)) return undefined;\n nums[i] = n;\n }\n\n return nums;\n}\n\n/**\n * Convert one value by its column type. Values of a known text type, and\n * non-string values, are returned untouched.\n */\nexport function convertPostgresValue(value: unknown, dataTypeID: number | undefined): unknown {\n if (typeof value !== \"string\" || dataTypeID === undefined) return value;\n\n const converter = CONVERTERS.get(dataTypeID);\n if (converter) return converter(value);\n\n if (LITERAL_TEXT_OIDS.has(dataTypeID)) return value;\n\n // Unknown (extension) type: only pgvector's bracketed list is re-inflated.\n return parseVector(value) ?? value;\n}\n"],"mappings":";AAWA,MAAM,UAAqB,UAAU,IAAI,KAAK,KAAK;AAEnD,MAAM,iBAA4B,UAAU;CAC1C,MAAM,IAAI,OAAO,KAAK;CAEtB,OAAO,OAAO,cAAc,CAAC,IAAI,IAAI;AACvC;AAEA,MAAM,kBAA6B,UAAU;CAC3C,MAAM,IAAI,OAAO,KAAK;CACtB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;;AAGA,MAAM,aAAa,IAAI,IAAuB;CAC5C,CAAC,IAAI,aAAa;CAClB,CAAC,MAAM,cAAc;CACrB,CAAC,MAAM,MAAM;CACb,CAAC,MAAM,MAAM;CACb,CAAC,MAAM,MAAM;AACf,CAAC;;AAGD,MAAM,oBAAoB,IAAI,IAAI;CAAC;CAAI;CAAI;CAAI;CAAK;CAAK;CAAM;CAAM;CAAM;AAAI,CAAC;;AAGhF,SAAS,YAAY,OAAqC;CAExD,IAAI,MAAM,WAAW,CAAC,MAAM,MAAM,MAAM,WAAW,MAAM,SAAS,CAAC,MAAM,IAAI,OAAO;CAEpF,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM,GAAG;CAC1C,MAAM,OAAO,IAAI,MAAc,MAAM,MAAM;CAE3C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,IAAI,EAAE,MAAM,MAAM;EACxB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;EAChC,KAAK,KAAK;CACZ;CAEA,OAAO;AACT;;;;;AAMA,SAAgB,qBAAqB,OAAgB,YAAyC;CAC5F,IAAI,OAAO,UAAU,YAAY,eAAe,QAAW,OAAO;CAElE,MAAM,YAAY,WAAW,IAAI,UAAU;CAC3C,IAAI,WAAW,OAAO,UAAU,KAAK;CAErC,IAAI,kBAAkB,IAAI,UAAU,GAAG,OAAO;CAG9C,OAAO,YAAY,KAAK,KAAK;AAC/B"}
1
+ {"version":3,"file":"postgres-type-converters.mjs","names":[],"sources":["../../../../../../../../../packages/cascade/src/drivers/postgres/postgres-type-converters.ts"],"sourcesContent":["/**\n * Column-type driven conversion of PostgreSQL result values (K1:B29).\n *\n * Conversion is keyed on the column's `dataTypeID` from the pg result\n * `fields`, never on what a string value looks like.\n */\n\nexport type PostgresColumnTypes = ReadonlyMap<string, number>;\n\ntype Converter = (value: string) => unknown;\n\nconst toDate: Converter = (value) => new Date(value);\n\nconst toSafeInteger: Converter = (value) => {\n const n = Number(value);\n // Beyond 2^53 a number would silently lose digits; keep the exact string.\n return Number.isSafeInteger(n) ? n : value;\n};\n\nconst toFiniteNumber: Converter = (value) => {\n const n = Number(value);\n return Number.isFinite(n) ? n : value;\n};\n\n/** OID → converter. json/jsonb are already parsed by pg, so they are absent. */\nconst CONVERTERS = new Map<number, Converter>([\n [20, toSafeInteger], // int8\n [1700, toFiniteNumber], // numeric\n [1082, toDate], // date\n [1114, toDate], // timestamp\n [1184, toDate], // timestamptz\n]);\n\n/** Built-in OIDs whose text must never be reinterpreted (text, varchar, bpchar, name, json, jsonb, uuid). */\nconst LITERAL_TEXT_OIDS = new Set([18, 19, 25, 114, 142, 1042, 1043, 2950, 3802]);\n\n/** pgvector columns use an extension OID (not fixed), returned as '[n1,n2,...]'. */\nfunction parseVector(value: string): number[] | undefined {\n // '[' = 91, ']' = 93\n if (value.charCodeAt(0) !== 91 || value.charCodeAt(value.length - 1) !== 93) return undefined;\n\n const parts = value.slice(1, -1).split(\",\");\n const nums = new Array<number>(parts.length);\n\n for (let i = 0; i < parts.length; i++) {\n const n = +(parts[i] ?? \"\");\n if (!Number.isFinite(n)) return undefined;\n nums[i] = n;\n }\n\n return nums;\n}\n\n/**\n * Convert one value by its column type. Values of a known text type, and\n * non-string values, are returned untouched.\n */\nexport function convertPostgresValue(value: unknown, dataTypeID: number | undefined): unknown {\n if (typeof value !== \"string\" || dataTypeID === undefined) return value;\n\n const converter = CONVERTERS.get(dataTypeID);\n if (converter) return converter(value);\n\n if (LITERAL_TEXT_OIDS.has(dataTypeID)) return value;\n\n // Unknown (extension) type: only pgvector's bracketed list is re-inflated.\n return parseVector(value) ?? value;\n}\n"],"mappings":";AAWA,MAAM,UAAqB,UAAU,IAAI,KAAK,KAAK;AAEnD,MAAM,iBAA4B,UAAU;CAC1C,MAAM,IAAI,OAAO,KAAK;CAEtB,OAAO,OAAO,cAAc,CAAC,IAAI,IAAI;AACvC;AAEA,MAAM,kBAA6B,UAAU;CAC3C,MAAM,IAAI,OAAO,KAAK;CACtB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;;AAGA,MAAM,aAAa,IAAI,IAAuB;CAC5C,CAAC,IAAI,aAAa;CAClB,CAAC,MAAM,cAAc;CACrB,CAAC,MAAM,MAAM;CACb,CAAC,MAAM,MAAM;CACb,CAAC,MAAM,MAAM;AACf,CAAC;;AAGD,MAAM,oBAAoB,IAAI,IAAI;CAAC;CAAI;CAAI;CAAI;CAAK;CAAK;CAAM;CAAM;CAAM;AAAI,CAAC;;AAGhF,SAAS,YAAY,OAAqC;CAExD,IAAI,MAAM,WAAW,CAAC,MAAM,MAAM,MAAM,WAAW,MAAM,SAAS,CAAC,MAAM,IAAI,OAAO;CAEpF,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM,GAAG;CAC1C,MAAM,OAAO,IAAI,MAAc,MAAM,MAAM;CAE3C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,IAAI,EAAE,MAAM,MAAM;EACxB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;EAChC,KAAK,KAAK;CACZ;CAEA,OAAO;AACT;;;;;AAMA,SAAgB,qBAAqB,OAAgB,YAAyC;CAC5F,IAAI,OAAO,UAAU,YAAY,eAAe,QAAW,OAAO;CAElE,MAAM,YAAY,WAAW,IAAI,UAAU;CAC3C,IAAI,WAAW,OAAO,UAAU,KAAK;CAErC,IAAI,kBAAkB,IAAI,UAAU,GAAG,OAAO;CAG9C,OAAO,YAAY,KAAK,KAAK;AAC/B"}
@@ -1,6 +1,6 @@
1
1
  import { UnsupportedUpdateOperationError } from "../../errors/unsupported-update-operation.error.mjs";
2
2
 
3
- //#region ../cascade/src/drivers/postgres/postgres-update-validator.ts
3
+ //#region ../../packages/cascade/src/drivers/postgres/postgres-update-validator.ts
4
4
  /**
5
5
  * Update operators the Postgres driver can express in SQL.
6
6
  *