relq 1.0.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 (305) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +862 -0
  3. package/dist/addons/buffer.js +1869 -0
  4. package/dist/addons/pg-cursor.js +1425 -0
  5. package/dist/addons/pg-format.js +2248 -0
  6. package/dist/addons/pg.js +4790 -0
  7. package/dist/bin/relq.js +2 -0
  8. package/dist/cjs/cache/index.cjs +9 -0
  9. package/dist/cjs/cache/query-cache.cjs +311 -0
  10. package/dist/cjs/cli/commands/add.cjs +82 -0
  11. package/dist/cjs/cli/commands/commit.cjs +145 -0
  12. package/dist/cjs/cli/commands/diff.cjs +84 -0
  13. package/dist/cjs/cli/commands/export.cjs +333 -0
  14. package/dist/cjs/cli/commands/fetch.cjs +59 -0
  15. package/dist/cjs/cli/commands/generate.cjs +242 -0
  16. package/dist/cjs/cli/commands/history.cjs +165 -0
  17. package/dist/cjs/cli/commands/import.cjs +524 -0
  18. package/dist/cjs/cli/commands/init.cjs +437 -0
  19. package/dist/cjs/cli/commands/introspect.cjs +142 -0
  20. package/dist/cjs/cli/commands/log.cjs +62 -0
  21. package/dist/cjs/cli/commands/migrate.cjs +167 -0
  22. package/dist/cjs/cli/commands/pull.cjs +410 -0
  23. package/dist/cjs/cli/commands/push.cjs +165 -0
  24. package/dist/cjs/cli/commands/rollback.cjs +169 -0
  25. package/dist/cjs/cli/commands/status.cjs +110 -0
  26. package/dist/cjs/cli/commands/sync.cjs +79 -0
  27. package/dist/cjs/cli/index.cjs +275 -0
  28. package/dist/cjs/cli/utils/change-tracker.cjs +446 -0
  29. package/dist/cjs/cli/utils/commit-manager.cjs +239 -0
  30. package/dist/cjs/cli/utils/config-loader.cjs +127 -0
  31. package/dist/cjs/cli/utils/env-loader.cjs +62 -0
  32. package/dist/cjs/cli/utils/fast-introspect.cjs +398 -0
  33. package/dist/cjs/cli/utils/git-utils.cjs +404 -0
  34. package/dist/cjs/cli/utils/migration-generator.cjs +269 -0
  35. package/dist/cjs/cli/utils/relqignore.cjs +114 -0
  36. package/dist/cjs/cli/utils/repo-manager.cjs +515 -0
  37. package/dist/cjs/cli/utils/schema-comparator.cjs +313 -0
  38. package/dist/cjs/cli/utils/schema-diff.cjs +284 -0
  39. package/dist/cjs/cli/utils/schema-hash.cjs +108 -0
  40. package/dist/cjs/cli/utils/schema-introspect.cjs +455 -0
  41. package/dist/cjs/cli/utils/snapshot-manager.cjs +223 -0
  42. package/dist/cjs/cli/utils/spinner.cjs +108 -0
  43. package/dist/cjs/cli/utils/sql-generator.cjs +520 -0
  44. package/dist/cjs/cli/utils/sql-parser.cjs +999 -0
  45. package/dist/cjs/cli/utils/type-generator.cjs +2061 -0
  46. package/dist/cjs/condition/array-condition-builder.cjs +503 -0
  47. package/dist/cjs/condition/array-numeric-condition-builder.cjs +186 -0
  48. package/dist/cjs/condition/array-specialized-condition-builder.cjs +206 -0
  49. package/dist/cjs/condition/array-string-condition-builder.cjs +146 -0
  50. package/dist/cjs/condition/base-condition-builder.cjs +2 -0
  51. package/dist/cjs/condition/condition-collector.cjs +284 -0
  52. package/dist/cjs/condition/fulltext-condition-builder.cjs +61 -0
  53. package/dist/cjs/condition/geometric-condition-builder.cjs +208 -0
  54. package/dist/cjs/condition/index.cjs +25 -0
  55. package/dist/cjs/condition/jsonb-condition-builder.cjs +160 -0
  56. package/dist/cjs/condition/network-condition-builder.cjs +230 -0
  57. package/dist/cjs/condition/range-condition-builder.cjs +82 -0
  58. package/dist/cjs/config/config.cjs +190 -0
  59. package/dist/cjs/config/index.cjs +9 -0
  60. package/dist/cjs/constants/pg-values.cjs +68 -0
  61. package/dist/cjs/copy/copy-builder.cjs +316 -0
  62. package/dist/cjs/copy/index.cjs +6 -0
  63. package/dist/cjs/core/query-builder.cjs +440 -0
  64. package/dist/cjs/core/relq-client.cjs +1831 -0
  65. package/dist/cjs/core/typed-kuery-client.cjs +2 -0
  66. package/dist/cjs/count/count-builder.cjs +88 -0
  67. package/dist/cjs/count/index.cjs +5 -0
  68. package/dist/cjs/cte/cte-builder.cjs +89 -0
  69. package/dist/cjs/cte/index.cjs +5 -0
  70. package/dist/cjs/ddl/function.cjs +48 -0
  71. package/dist/cjs/ddl/index.cjs +7 -0
  72. package/dist/cjs/ddl/sql.cjs +54 -0
  73. package/dist/cjs/delete/delete-builder.cjs +135 -0
  74. package/dist/cjs/delete/index.cjs +5 -0
  75. package/dist/cjs/errors/relq-errors.cjs +329 -0
  76. package/dist/cjs/examples/fulltext-search-test.cjs +122 -0
  77. package/dist/cjs/explain/explain-builder.cjs +99 -0
  78. package/dist/cjs/explain/index.cjs +5 -0
  79. package/dist/cjs/function/create-function-builder.cjs +196 -0
  80. package/dist/cjs/function/index.cjs +6 -0
  81. package/dist/cjs/functions/advanced-functions.cjs +241 -0
  82. package/dist/cjs/functions/case-builder.cjs +66 -0
  83. package/dist/cjs/functions/geometric-functions.cjs +104 -0
  84. package/dist/cjs/functions/index.cjs +184 -0
  85. package/dist/cjs/functions/network-functions.cjs +86 -0
  86. package/dist/cjs/functions/sql-functions.cjs +431 -0
  87. package/dist/cjs/index.cjs +164 -0
  88. package/dist/cjs/indexing/create-index-builder.cjs +187 -0
  89. package/dist/cjs/indexing/drop-index-builder.cjs +89 -0
  90. package/dist/cjs/indexing/index-types.cjs +2 -0
  91. package/dist/cjs/indexing/index.cjs +8 -0
  92. package/dist/cjs/insert/conflict-builder.cjs +173 -0
  93. package/dist/cjs/insert/index.cjs +5 -0
  94. package/dist/cjs/insert/insert-builder.cjs +254 -0
  95. package/dist/cjs/introspect/index.cjs +229 -0
  96. package/dist/cjs/maintenance/index.cjs +6 -0
  97. package/dist/cjs/maintenance/vacuum-builder.cjs +166 -0
  98. package/dist/cjs/pubsub/index.cjs +7 -0
  99. package/dist/cjs/pubsub/listen-notify-builder.cjs +57 -0
  100. package/dist/cjs/pubsub/listener-connection.cjs +180 -0
  101. package/dist/cjs/raw/index.cjs +5 -0
  102. package/dist/cjs/raw/raw-query-builder.cjs +27 -0
  103. package/dist/cjs/schema/index.cjs +15 -0
  104. package/dist/cjs/schema/schema-builder.cjs +1167 -0
  105. package/dist/cjs/schema-builder.cjs +21 -0
  106. package/dist/cjs/schema-definition/column-types.cjs +829 -0
  107. package/dist/cjs/schema-definition/index.cjs +62 -0
  108. package/dist/cjs/schema-definition/introspection.cjs +620 -0
  109. package/dist/cjs/schema-definition/partitions.cjs +129 -0
  110. package/dist/cjs/schema-definition/pg-enum.cjs +76 -0
  111. package/dist/cjs/schema-definition/pg-function.cjs +91 -0
  112. package/dist/cjs/schema-definition/pg-sequence.cjs +56 -0
  113. package/dist/cjs/schema-definition/pg-trigger.cjs +108 -0
  114. package/dist/cjs/schema-definition/relations.cjs +98 -0
  115. package/dist/cjs/schema-definition/sql-expressions.cjs +202 -0
  116. package/dist/cjs/schema-definition/table-definition.cjs +636 -0
  117. package/dist/cjs/select/aggregate-builder.cjs +179 -0
  118. package/dist/cjs/select/index.cjs +5 -0
  119. package/dist/cjs/select/select-builder.cjs +233 -0
  120. package/dist/cjs/sequence/index.cjs +7 -0
  121. package/dist/cjs/sequence/sequence-builder.cjs +264 -0
  122. package/dist/cjs/table/alter-table-builder.cjs +146 -0
  123. package/dist/cjs/table/constraint-builder.cjs +102 -0
  124. package/dist/cjs/table/create-table-builder.cjs +248 -0
  125. package/dist/cjs/table/index.cjs +17 -0
  126. package/dist/cjs/table/partition-builder.cjs +131 -0
  127. package/dist/cjs/table/truncate-builder.cjs +70 -0
  128. package/dist/cjs/transaction/index.cjs +6 -0
  129. package/dist/cjs/transaction/transaction-builder.cjs +78 -0
  130. package/dist/cjs/trigger/create-trigger-builder.cjs +174 -0
  131. package/dist/cjs/trigger/index.cjs +6 -0
  132. package/dist/cjs/types/aggregate-types.cjs +2 -0
  133. package/dist/cjs/types/config-types.cjs +40 -0
  134. package/dist/cjs/types/inference-types.cjs +18 -0
  135. package/dist/cjs/types/pagination-types.cjs +7 -0
  136. package/dist/cjs/types/result-types.cjs +2 -0
  137. package/dist/cjs/types/schema-types.cjs +2 -0
  138. package/dist/cjs/types/subscription-types.cjs +2 -0
  139. package/dist/cjs/types.cjs +2 -0
  140. package/dist/cjs/update/array-update-builder.cjs +205 -0
  141. package/dist/cjs/update/index.cjs +13 -0
  142. package/dist/cjs/update/update-builder.cjs +195 -0
  143. package/dist/cjs/utils/case-converter.cjs +58 -0
  144. package/dist/cjs/utils/environment-detection.cjs +120 -0
  145. package/dist/cjs/utils/index.cjs +10 -0
  146. package/dist/cjs/utils/pool-defaults.cjs +106 -0
  147. package/dist/cjs/utils/type-coercion.cjs +118 -0
  148. package/dist/cjs/view/create-view-builder.cjs +180 -0
  149. package/dist/cjs/view/index.cjs +7 -0
  150. package/dist/cjs/window/index.cjs +5 -0
  151. package/dist/cjs/window/window-builder.cjs +80 -0
  152. package/dist/config.cjs +1 -0
  153. package/dist/config.d.ts +655 -0
  154. package/dist/config.js +1 -0
  155. package/dist/esm/cache/index.js +1 -0
  156. package/dist/esm/cache/query-cache.js +303 -0
  157. package/dist/esm/cli/commands/add.js +78 -0
  158. package/dist/esm/cli/commands/commit.js +109 -0
  159. package/dist/esm/cli/commands/diff.js +81 -0
  160. package/dist/esm/cli/commands/export.js +297 -0
  161. package/dist/esm/cli/commands/fetch.js +56 -0
  162. package/dist/esm/cli/commands/generate.js +206 -0
  163. package/dist/esm/cli/commands/history.js +129 -0
  164. package/dist/esm/cli/commands/import.js +488 -0
  165. package/dist/esm/cli/commands/init.js +401 -0
  166. package/dist/esm/cli/commands/introspect.js +106 -0
  167. package/dist/esm/cli/commands/log.js +59 -0
  168. package/dist/esm/cli/commands/migrate.js +131 -0
  169. package/dist/esm/cli/commands/pull.js +374 -0
  170. package/dist/esm/cli/commands/push.js +129 -0
  171. package/dist/esm/cli/commands/rollback.js +133 -0
  172. package/dist/esm/cli/commands/status.js +107 -0
  173. package/dist/esm/cli/commands/sync.js +76 -0
  174. package/dist/esm/cli/index.js +240 -0
  175. package/dist/esm/cli/utils/change-tracker.js +405 -0
  176. package/dist/esm/cli/utils/commit-manager.js +191 -0
  177. package/dist/esm/cli/utils/config-loader.js +86 -0
  178. package/dist/esm/cli/utils/env-loader.js +57 -0
  179. package/dist/esm/cli/utils/fast-introspect.js +362 -0
  180. package/dist/esm/cli/utils/git-utils.js +347 -0
  181. package/dist/esm/cli/utils/migration-generator.js +263 -0
  182. package/dist/esm/cli/utils/relqignore.js +74 -0
  183. package/dist/esm/cli/utils/repo-manager.js +444 -0
  184. package/dist/esm/cli/utils/schema-comparator.js +307 -0
  185. package/dist/esm/cli/utils/schema-diff.js +276 -0
  186. package/dist/esm/cli/utils/schema-hash.js +69 -0
  187. package/dist/esm/cli/utils/schema-introspect.js +418 -0
  188. package/dist/esm/cli/utils/snapshot-manager.js +179 -0
  189. package/dist/esm/cli/utils/spinner.js +101 -0
  190. package/dist/esm/cli/utils/sql-generator.js +504 -0
  191. package/dist/esm/cli/utils/sql-parser.js +992 -0
  192. package/dist/esm/cli/utils/type-generator.js +2058 -0
  193. package/dist/esm/condition/array-condition-builder.js +495 -0
  194. package/dist/esm/condition/array-numeric-condition-builder.js +182 -0
  195. package/dist/esm/condition/array-specialized-condition-builder.js +200 -0
  196. package/dist/esm/condition/array-string-condition-builder.js +142 -0
  197. package/dist/esm/condition/base-condition-builder.js +1 -0
  198. package/dist/esm/condition/condition-collector.js +275 -0
  199. package/dist/esm/condition/fulltext-condition-builder.js +53 -0
  200. package/dist/esm/condition/geometric-condition-builder.js +200 -0
  201. package/dist/esm/condition/index.js +7 -0
  202. package/dist/esm/condition/jsonb-condition-builder.js +152 -0
  203. package/dist/esm/condition/network-condition-builder.js +222 -0
  204. package/dist/esm/condition/range-condition-builder.js +74 -0
  205. package/dist/esm/config/config.js +150 -0
  206. package/dist/esm/config/index.js +1 -0
  207. package/dist/esm/constants/pg-values.js +63 -0
  208. package/dist/esm/copy/copy-builder.js +308 -0
  209. package/dist/esm/copy/index.js +1 -0
  210. package/dist/esm/core/query-builder.js +426 -0
  211. package/dist/esm/core/relq-client.js +1791 -0
  212. package/dist/esm/core/typed-kuery-client.js +1 -0
  213. package/dist/esm/count/count-builder.js +81 -0
  214. package/dist/esm/count/index.js +1 -0
  215. package/dist/esm/cte/cte-builder.js +82 -0
  216. package/dist/esm/cte/index.js +1 -0
  217. package/dist/esm/ddl/function.js +45 -0
  218. package/dist/esm/ddl/index.js +2 -0
  219. package/dist/esm/ddl/sql.js +51 -0
  220. package/dist/esm/delete/delete-builder.js +128 -0
  221. package/dist/esm/delete/index.js +1 -0
  222. package/dist/esm/errors/relq-errors.js +310 -0
  223. package/dist/esm/examples/fulltext-search-test.js +117 -0
  224. package/dist/esm/explain/explain-builder.js +95 -0
  225. package/dist/esm/explain/index.js +1 -0
  226. package/dist/esm/function/create-function-builder.js +188 -0
  227. package/dist/esm/function/index.js +1 -0
  228. package/dist/esm/functions/advanced-functions.js +231 -0
  229. package/dist/esm/functions/case-builder.js +58 -0
  230. package/dist/esm/functions/geometric-functions.js +97 -0
  231. package/dist/esm/functions/index.js +171 -0
  232. package/dist/esm/functions/network-functions.js +79 -0
  233. package/dist/esm/functions/sql-functions.js +421 -0
  234. package/dist/esm/index.js +34 -0
  235. package/dist/esm/indexing/create-index-builder.js +180 -0
  236. package/dist/esm/indexing/drop-index-builder.js +81 -0
  237. package/dist/esm/indexing/index-types.js +1 -0
  238. package/dist/esm/indexing/index.js +2 -0
  239. package/dist/esm/insert/conflict-builder.js +162 -0
  240. package/dist/esm/insert/index.js +1 -0
  241. package/dist/esm/insert/insert-builder.js +247 -0
  242. package/dist/esm/introspect/index.js +224 -0
  243. package/dist/esm/maintenance/index.js +1 -0
  244. package/dist/esm/maintenance/vacuum-builder.js +158 -0
  245. package/dist/esm/pubsub/index.js +1 -0
  246. package/dist/esm/pubsub/listen-notify-builder.js +48 -0
  247. package/dist/esm/pubsub/listener-connection.js +173 -0
  248. package/dist/esm/raw/index.js +1 -0
  249. package/dist/esm/raw/raw-query-builder.js +20 -0
  250. package/dist/esm/schema/index.js +1 -0
  251. package/dist/esm/schema/schema-builder.js +1150 -0
  252. package/dist/esm/schema-builder.js +2 -0
  253. package/dist/esm/schema-definition/column-types.js +738 -0
  254. package/dist/esm/schema-definition/index.js +10 -0
  255. package/dist/esm/schema-definition/introspection.js +614 -0
  256. package/dist/esm/schema-definition/partitions.js +123 -0
  257. package/dist/esm/schema-definition/pg-enum.js +70 -0
  258. package/dist/esm/schema-definition/pg-function.js +85 -0
  259. package/dist/esm/schema-definition/pg-sequence.js +50 -0
  260. package/dist/esm/schema-definition/pg-trigger.js +102 -0
  261. package/dist/esm/schema-definition/relations.js +90 -0
  262. package/dist/esm/schema-definition/sql-expressions.js +193 -0
  263. package/dist/esm/schema-definition/table-definition.js +630 -0
  264. package/dist/esm/select/aggregate-builder.js +172 -0
  265. package/dist/esm/select/index.js +1 -0
  266. package/dist/esm/select/select-builder.js +226 -0
  267. package/dist/esm/sequence/index.js +1 -0
  268. package/dist/esm/sequence/sequence-builder.js +255 -0
  269. package/dist/esm/table/alter-table-builder.js +138 -0
  270. package/dist/esm/table/constraint-builder.js +95 -0
  271. package/dist/esm/table/create-table-builder.js +241 -0
  272. package/dist/esm/table/index.js +5 -0
  273. package/dist/esm/table/partition-builder.js +121 -0
  274. package/dist/esm/table/truncate-builder.js +63 -0
  275. package/dist/esm/transaction/index.js +1 -0
  276. package/dist/esm/transaction/transaction-builder.js +70 -0
  277. package/dist/esm/trigger/create-trigger-builder.js +166 -0
  278. package/dist/esm/trigger/index.js +1 -0
  279. package/dist/esm/types/aggregate-types.js +1 -0
  280. package/dist/esm/types/config-types.js +36 -0
  281. package/dist/esm/types/inference-types.js +12 -0
  282. package/dist/esm/types/pagination-types.js +4 -0
  283. package/dist/esm/types/result-types.js +1 -0
  284. package/dist/esm/types/schema-types.js +1 -0
  285. package/dist/esm/types/subscription-types.js +1 -0
  286. package/dist/esm/types.js +1 -0
  287. package/dist/esm/update/array-update-builder.js +192 -0
  288. package/dist/esm/update/index.js +2 -0
  289. package/dist/esm/update/update-builder.js +188 -0
  290. package/dist/esm/utils/case-converter.js +55 -0
  291. package/dist/esm/utils/environment-detection.js +113 -0
  292. package/dist/esm/utils/index.js +2 -0
  293. package/dist/esm/utils/pool-defaults.js +100 -0
  294. package/dist/esm/utils/type-coercion.js +110 -0
  295. package/dist/esm/view/create-view-builder.js +171 -0
  296. package/dist/esm/view/index.js +1 -0
  297. package/dist/esm/window/index.js +1 -0
  298. package/dist/esm/window/window-builder.js +73 -0
  299. package/dist/index.cjs +1 -0
  300. package/dist/index.d.ts +10341 -0
  301. package/dist/index.js +1 -0
  302. package/dist/schema-builder.cjs +1 -0
  303. package/dist/schema-builder.d.ts +2272 -0
  304. package/dist/schema-builder.js +1 -0
  305. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,862 @@
1
+ # Relq
2
+
3
+ **The Fully-Typed PostgreSQL ORM for TypeScript**
4
+
5
+ Relq is a complete, type-safe ORM for PostgreSQL that brings the full power of the database to TypeScript. With support for 100+ PostgreSQL types, advanced features like partitions, domains, composite types, generated columns, and a git-like CLI - all with zero runtime dependencies.
6
+
7
+ ## Why Relq?
8
+
9
+ - **Complete Type Safety** - End-to-end TypeScript inference from schema to query results
10
+ - **Zero Runtime Dependencies** - Everything bundled, no external packages at runtime
11
+ - **Full PostgreSQL Support** - Every PostgreSQL feature you need, properly typed
12
+ - **Tree-Shakeable** - Import only what you use
13
+ - **Schema-First** - Define once, get types everywhere
14
+ - **Git-like CLI** - Familiar commands for schema management
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install relq
20
+ ```
21
+
22
+ ## Entry Points
23
+
24
+ ```typescript
25
+ // Runtime - Client, queries, functions
26
+ import { Relq, F, Case, PG } from 'relq';
27
+
28
+ // Configuration
29
+ import { defineConfig, loadConfig } from 'relq/config';
30
+
31
+ // Schema Builder - Types, tables, DDL
32
+ import {
33
+ defineTable,
34
+ integer, text, uuid, jsonb, timestamp,
35
+ pgEnum, pgDomain, pgComposite,
36
+ one, many
37
+ } from 'relq/schema-builder';
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ### 1. Define Your Schema
43
+
44
+ ```typescript
45
+ // db/schema.ts
46
+ import {
47
+ defineTable,
48
+ uuid, text, timestamp, boolean, integer, jsonb,
49
+ pgEnum
50
+ } from 'relq/schema-builder';
51
+
52
+ // Enums with full type inference
53
+ export const userStatus = pgEnum('user_status', ['active', 'inactive', 'suspended']);
54
+
55
+ // Tables with complete column typing
56
+ export const users = defineTable('users', {
57
+ id: uuid().primaryKey().default('gen_random_uuid()'),
58
+ email: text().notNull().unique(),
59
+ name: text().notNull(),
60
+ status: userStatus().default('active'),
61
+ metadata: jsonb<{ theme?: string; locale?: string }>(),
62
+ createdAt: timestamp('created_at').default('now()'),
63
+ });
64
+
65
+ export const posts = defineTable('posts', {
66
+ id: uuid().primaryKey().default('gen_random_uuid()'),
67
+ title: text().notNull(),
68
+ content: text(),
69
+ authorId: uuid('author_id').notNull().references('users', 'id'),
70
+ published: boolean().default(false),
71
+ viewCount: integer('view_count').default(0),
72
+ createdAt: timestamp('created_at').default('now()'),
73
+ });
74
+
75
+ export const schema = { users, posts };
76
+ ```
77
+
78
+ ### 2. Connect
79
+
80
+ ```typescript
81
+ import { Relq } from 'relq';
82
+ import { schema } from './schema';
83
+
84
+ const db = new Relq(schema, {
85
+ host: 'localhost',
86
+ database: 'myapp',
87
+ user: 'postgres',
88
+ password: 'secret'
89
+ });
90
+
91
+ // Or with connection URL
92
+ const db = new Relq(schema, {
93
+ url: process.env.DATABASE_URL
94
+ });
95
+ ```
96
+
97
+ ### 3. Query with Full Type Safety
98
+
99
+ ```typescript
100
+ // Types flow from schema to results
101
+ const users = await db.table.users
102
+ .select(['id', 'email', 'status'])
103
+ .where(q => q.equal('status', 'active'))
104
+ .all();
105
+ // Type: { id: string; email: string; status: 'active' | 'inactive' | 'suspended' }[]
106
+
107
+ // Convenience methods
108
+ const user = await db.table.users.findById('uuid-here');
109
+ const user = await db.table.users.findOne({ email: 'test@example.com' });
110
+ ```
111
+
112
+ ## Column Types
113
+
114
+ Relq supports 100+ PostgreSQL types with proper TypeScript mapping:
115
+
116
+ ### Numeric Types
117
+ ```typescript
118
+ integer(), int(), int4() // number
119
+ smallint(), int2() // number
120
+ bigint(), int8() // bigint
121
+ serial(), serial4() // number (auto-increment)
122
+ bigserial(), serial8() // bigint (auto-increment)
123
+ numeric(), decimal() // string (precise decimals)
124
+ real(), float4() // number
125
+ doublePrecision(), float8() // number
126
+ money() // string
127
+ ```
128
+
129
+ ### String Types
130
+ ```typescript
131
+ text() // string
132
+ varchar(), char() // string
133
+ citext() // string (case-insensitive, requires extension)
134
+ ```
135
+
136
+ ### Date/Time Types
137
+ ```typescript
138
+ timestamp() // Date
139
+ timestamptz(), timestampWithTimeZone() // Date
140
+ date() // Date | string
141
+ time(), timetz() // string
142
+ interval() // string
143
+ ```
144
+
145
+ ### JSON Types
146
+ ```typescript
147
+ json<T>() // T (typed JSON)
148
+ jsonb<T>() // T (typed JSONB)
149
+
150
+ // Example with type parameter
151
+ metadata: jsonb<{ theme: string; settings: Record<string, boolean> }>()
152
+ ```
153
+
154
+ ### Boolean & UUID
155
+ ```typescript
156
+ boolean(), bool() // boolean
157
+ uuid() // string
158
+ ```
159
+
160
+ ### Array Types
161
+ ```typescript
162
+ // Any column type can be an array
163
+ tags: text().array() // string[]
164
+ matrix: integer().array(2) // number[][] (2D array)
165
+ scores: numeric().array() // string[]
166
+ ```
167
+
168
+ ### Geometric Types
169
+ ```typescript
170
+ point() // { x: number; y: number }
171
+ line() // { a: number; b: number; c: number }
172
+ lseg() // [[number, number], [number, number]]
173
+ box() // [[number, number], [number, number]]
174
+ path() // Array<{ x: number; y: number }>
175
+ polygon() // Array<{ x: number; y: number }>
176
+ circle() // { x: number; y: number; r: number }
177
+ ```
178
+
179
+ ### Network Types
180
+ ```typescript
181
+ inet() // string (IP address)
182
+ cidr() // string (IP network)
183
+ macaddr() // string
184
+ macaddr8() // string
185
+ ```
186
+
187
+ ### Range Types
188
+ ```typescript
189
+ int4range(), int8range() // string
190
+ numrange(), daterange() // string
191
+ tsrange(), tstzrange() // string
192
+ // Multi-range variants also available
193
+ ```
194
+
195
+ ### Full-Text Search
196
+ ```typescript
197
+ tsvector() // string
198
+ tsquery() // string
199
+ ```
200
+
201
+ ### PostGIS (requires extension)
202
+ ```typescript
203
+ geometry('location', 4326, 'POINT') // GeoJSON
204
+ geography('area', 4326, 'POLYGON') // GeoJSON
205
+ geoPoint('coords') // { x, y, srid }
206
+ box2d(), box3d() // string
207
+ ```
208
+
209
+ ### Extension Types
210
+ ```typescript
211
+ ltree() // string (hierarchical labels)
212
+ hstore() // Record<string, string | null>
213
+ cube() // number[]
214
+ semver() // string
215
+ ```
216
+
217
+ ## Query API
218
+
219
+ ### Select
220
+ ```typescript
221
+ // All columns
222
+ const users = await db.table.users.select().all();
223
+
224
+ // Specific columns
225
+ const emails = await db.table.users
226
+ .select(['id', 'email'])
227
+ .all();
228
+
229
+ // With conditions
230
+ const active = await db.table.users
231
+ .select(['id', 'email', 'name'])
232
+ .where(q => q.equal('status', 'active'))
233
+ .orderBy('createdAt', 'DESC')
234
+ .limit(10)
235
+ .all();
236
+
237
+ // Single record
238
+ const user = await db.table.users
239
+ .select()
240
+ .where(q => q.equal('id', userId))
241
+ .one();
242
+
243
+ // With joins
244
+ const postsWithAuthors = await db.table.posts
245
+ .select(['posts.id', 'posts.title', 'users.name'])
246
+ .leftJoin('users', 'users.id = posts.author_id')
247
+ .all();
248
+
249
+ // Distinct
250
+ await db.table.users.select(['status']).distinct().all();
251
+
252
+ // Distinct on (PostgreSQL-specific)
253
+ await db.table.logs
254
+ .select()
255
+ .distinctOn('userId')
256
+ .orderBy('userId')
257
+ .orderBy('createdAt', 'DESC')
258
+ .all();
259
+
260
+ // Locking
261
+ await db.table.jobs
262
+ .select()
263
+ .where(q => q.equal('status', 'pending'))
264
+ .forUpdateSkipLocked()
265
+ .limit(1)
266
+ .one();
267
+ ```
268
+
269
+ ### Insert
270
+ ```typescript
271
+ // Single insert with returning
272
+ const user = await db.table.users
273
+ .insert({ email: 'new@example.com', name: 'New User' })
274
+ .returning(['id', 'createdAt'])
275
+ .one();
276
+
277
+ // Bulk insert
278
+ await db.table.users
279
+ .insert([
280
+ { email: 'user1@example.com', name: 'User 1' },
281
+ { email: 'user2@example.com', name: 'User 2' }
282
+ ])
283
+ .run();
284
+
285
+ // Upsert - ON CONFLICT DO UPDATE
286
+ await db.table.users
287
+ .insert({ email: 'user@example.com', name: 'Updated' })
288
+ .onConflict('email')
289
+ .doUpdate({ name: 'Updated', updatedAt: PG.now() })
290
+ .run();
291
+
292
+ // ON CONFLICT DO NOTHING
293
+ await db.table.users
294
+ .insert({ email: 'user@example.com', name: 'New' })
295
+ .onConflict('email')
296
+ .doNothing()
297
+ .run();
298
+ ```
299
+
300
+ ### Update
301
+ ```typescript
302
+ // Basic update
303
+ await db.table.users
304
+ .update({ status: 'inactive' })
305
+ .where(q => q.equal('id', userId))
306
+ .run();
307
+
308
+ // With returning
309
+ const updated = await db.table.posts
310
+ .update({ viewCount: F.increment('viewCount', 1) })
311
+ .where(q => q.equal('id', postId))
312
+ .returning(['id', 'viewCount'])
313
+ .one();
314
+
315
+ // Bulk update
316
+ await db.table.posts
317
+ .update({ published: true })
318
+ .where(q => q.in('id', postIds))
319
+ .run();
320
+ ```
321
+
322
+ ### Delete
323
+ ```typescript
324
+ // Delete with condition
325
+ await db.table.users
326
+ .delete()
327
+ .where(q => q.equal('id', userId))
328
+ .run();
329
+
330
+ // With returning
331
+ const deleted = await db.table.posts
332
+ .delete()
333
+ .where(q => q.equal('authorId', userId))
334
+ .returning(['id', 'title'])
335
+ .all();
336
+ ```
337
+
338
+ ### Aggregations
339
+ ```typescript
340
+ // Count
341
+ const count = await db.table.users
342
+ .count()
343
+ .where(q => q.equal('status', 'active'))
344
+ .run();
345
+
346
+ // Count with group by
347
+ const byStatus = await db.table.users
348
+ .count()
349
+ .groupBy('status')
350
+ .run();
351
+
352
+ // Multiple aggregations
353
+ const stats = await db.table.orders
354
+ .aggregate()
355
+ .count('id', 'totalOrders')
356
+ .sum('amount', 'totalRevenue')
357
+ .avg('amount', 'avgOrderValue')
358
+ .min('amount', 'minOrder')
359
+ .max('amount', 'maxOrder')
360
+ .one();
361
+ ```
362
+
363
+ ### Pagination
364
+ ```typescript
365
+ // Cursor-based (recommended)
366
+ const page = await db.table.posts
367
+ .select(['id', 'title', 'createdAt'])
368
+ .paginate({ orderBy: ['createdAt', 'DESC'] })
369
+ .paging({ perPage: 20, cursor: lastCursor });
370
+
371
+ // page.data - results
372
+ // page.pagination.next - cursor for next page
373
+ // page.pagination.hasNext - boolean
374
+
375
+ // Offset-based
376
+ const page = await db.table.posts
377
+ .select(['id', 'title'])
378
+ .paginate({ orderBy: ['createdAt', 'DESC'] })
379
+ .offset({ perPage: 20, page: 2 });
380
+
381
+ // page.pagination.totalPages
382
+ // page.pagination.currentPage
383
+ // page.pagination.total
384
+ ```
385
+
386
+ ## Condition Builders
387
+
388
+ ### Basic Comparisons
389
+ ```typescript
390
+ .where(q => q.equal('status', 'active'))
391
+ .where(q => q.notEqual('role', 'guest'))
392
+ .where(q => q.greaterThan('age', 18))
393
+ .where(q => q.greaterThanEqual('score', 100))
394
+ .where(q => q.lessThan('price', 50))
395
+ .where(q => q.lessThanEqual('quantity', 10))
396
+ .where(q => q.between('createdAt', startDate, endDate))
397
+ ```
398
+
399
+ ### Null Checks
400
+ ```typescript
401
+ .where(q => q.isNull('deletedAt'))
402
+ .where(q => q.isNotNull('verifiedAt'))
403
+ ```
404
+
405
+ ### String Matching
406
+ ```typescript
407
+ .where(q => q.startsWith('email', 'admin@'))
408
+ .where(q => q.endsWith('email', '@company.com'))
409
+ .where(q => q.contains('name', 'john'))
410
+ .where(q => q.like('email', '%@%.%'))
411
+ .where(q => q.ilike('name', '%JOHN%')) // case-insensitive
412
+ ```
413
+
414
+ ### Lists
415
+ ```typescript
416
+ .where(q => q.in('status', ['active', 'pending']))
417
+ .where(q => q.notIn('role', ['banned', 'deleted']))
418
+ ```
419
+
420
+ ### Logical Operators
421
+ ```typescript
422
+ // AND (default - conditions chain)
423
+ .where(q => q
424
+ .equal('status', 'active')
425
+ .greaterThan('age', 18)
426
+ )
427
+
428
+ // OR
429
+ .where(q => q
430
+ .equal('status', 'active')
431
+ .or(sub => sub
432
+ .equal('role', 'admin')
433
+ .equal('role', 'moderator')
434
+ )
435
+ )
436
+ ```
437
+
438
+ ### JSONB Conditions
439
+ ```typescript
440
+ .where(q => q.jsonb.contains('metadata', { role: 'admin' }))
441
+ .where(q => q.jsonb.containedBy('tags', ['a', 'b', 'c']))
442
+ .where(q => q.jsonb.hasKey('settings', 'theme'))
443
+ .where(q => q.jsonb.hasAnyKeys('data', ['key1', 'key2']))
444
+ .where(q => q.jsonb.hasAllKeys('config', ['host', 'port']))
445
+ .where(q => q.jsonb.extractEqual('data', ['user', 'id'], userId))
446
+ ```
447
+
448
+ ### Array Conditions
449
+ ```typescript
450
+ .where(q => q.array.contains('tags', ['typescript']))
451
+ .where(q => q.array.containedBy('roles', ['admin', 'user', 'guest']))
452
+ .where(q => q.array.overlaps('categories', ['tech', 'news']))
453
+ .where(q => q.array.length('items', 5))
454
+
455
+ // Typed array conditions
456
+ .where(q => q.array.string.startsWith('emails', 'admin@'))
457
+ .where(q => q.array.numeric.greaterThan('scores', 90))
458
+ .where(q => q.array.date.after('dates', '2024-01-01'))
459
+ ```
460
+
461
+ ### Full-Text Search
462
+ ```typescript
463
+ .where(q => q.fulltext.search('content', 'typescript tutorial'))
464
+ .where(q => q.fulltext.match('title', 'node & express'))
465
+ .where(q => q.fulltext.rank('body', 'search terms', 0.1))
466
+ ```
467
+
468
+ ### Range Conditions
469
+ ```typescript
470
+ .where(q => q.range.contains('dateRange', '2024-06-15'))
471
+ .where(q => q.range.containedBy('priceRange', '[0, 1000]'))
472
+ .where(q => q.range.overlaps('availability', '[2024-01-01, 2024-12-31]'))
473
+ ```
474
+
475
+ ### Geometric Conditions
476
+ ```typescript
477
+ .where(q => q.geometric.contains('area', '(0,0),(10,10)'))
478
+ .where(q => q.geometric.overlaps('region', box))
479
+ .where(q => q.geometric.distanceLessThan('location', '(5,5)', 10))
480
+ ```
481
+
482
+ ### Network Conditions
483
+ ```typescript
484
+ .where(q => q.network.containsOrEqual('subnet', '192.168.1.0/24'))
485
+ .where(q => q.network.isIPv4('address'))
486
+ .where(q => q.network.isIPv6('address'))
487
+ ```
488
+
489
+ ## Advanced Schema Features
490
+
491
+ ### Domains with Validation
492
+ ```typescript
493
+ import { pgDomain, text, numeric } from 'relq/schema-builder';
494
+
495
+ // Email domain with pattern validation
496
+ export const emailDomain = pgDomain('email', text(), (value) => [
497
+ value.matches('^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$')
498
+ ]);
499
+
500
+ // Percentage domain with range validation
501
+ export const percentageDomain = pgDomain('percentage',
502
+ numeric().precision(5).scale(2),
503
+ (value) => [value.gte(0), value.lte(100)]
504
+ );
505
+
506
+ // Use in tables
507
+ const employees = defineTable('employees', {
508
+ email: emailDomain().notNull(),
509
+ bonus: percentageDomain().default(0)
510
+ });
511
+ ```
512
+
513
+ ### Composite Types
514
+ ```typescript
515
+ import { pgComposite, text, varchar, boolean } from 'relq/schema-builder';
516
+
517
+ export const addressType = pgComposite('address_type', {
518
+ line1: text().notNull(),
519
+ line2: text(),
520
+ city: varchar().length(100).notNull(),
521
+ country: varchar().length(100).notNull(),
522
+ postalCode: varchar().length(20),
523
+ verified: boolean().default(false)
524
+ });
525
+
526
+ const customers = defineTable('customers', {
527
+ billingAddress: addressType(),
528
+ shippingAddress: addressType()
529
+ });
530
+ ```
531
+
532
+ ### Generated Columns
533
+ ```typescript
534
+ const orderItems = defineTable('order_items', {
535
+ quantity: integer().notNull(),
536
+ unitPrice: numeric().precision(10).scale(2).notNull(),
537
+ discount: numeric().precision(5).scale(2).default(0),
538
+
539
+ // Computed from other columns
540
+ lineTotal: numeric().precision(12).scale(2).generatedAlwaysAs(
541
+ (table, F) => F(table.unitPrice)
542
+ .multiply(table.quantity)
543
+ .multiply(F.subtract(1, F.divide(table.discount, 100)))
544
+ ),
545
+
546
+ // Using SQL functions
547
+ searchVector: tsvector().generatedAlwaysAs(
548
+ (table, F) => F.toTsvector('english', table.description)
549
+ ),
550
+
551
+ // String concatenation
552
+ fullName: text().generatedAlwaysAs(
553
+ (table, F) => F.concat(table.firstName, ' ', table.lastName)
554
+ )
555
+ });
556
+ ```
557
+
558
+ ### Table Partitioning
559
+ ```typescript
560
+ // Range partitioning
561
+ const events = defineTable('events', {
562
+ id: uuid().primaryKey(),
563
+ eventType: text().notNull(),
564
+ createdAt: timestamp('created_at').notNull()
565
+ }, {
566
+ partitionBy: (table, p) => p.range(table.createdAt),
567
+ partitions: (partition) => [
568
+ partition('events_2024_q1').from('2024-01-01').to('2024-04-01'),
569
+ partition('events_2024_q2').from('2024-04-01').to('2024-07-01'),
570
+ partition('events_2024_q3').from('2024-07-01').to('2024-10-01'),
571
+ partition('events_2024_q4').from('2024-10-01').to('2025-01-01'),
572
+ ]
573
+ });
574
+
575
+ // List partitioning
576
+ const logs = defineTable('logs', {
577
+ id: uuid().primaryKey(),
578
+ level: text().notNull(),
579
+ message: text()
580
+ }, {
581
+ partitionBy: (table, p) => p.list(table.level),
582
+ partitions: (partition) => [
583
+ partition('logs_error').forValues('error', 'fatal'),
584
+ partition('logs_warn').forValues('warn'),
585
+ partition('logs_info').forValues('info', 'debug')
586
+ ]
587
+ });
588
+
589
+ // Hash partitioning
590
+ const sessions = defineTable('sessions', {
591
+ id: uuid().primaryKey(),
592
+ userId: uuid('user_id').notNull()
593
+ }, {
594
+ partitionBy: (table, p) => p.hash(table.userId),
595
+ partitions: (partition) => [
596
+ partition('sessions_0').modulus(4).remainder(0),
597
+ partition('sessions_1').modulus(4).remainder(1),
598
+ partition('sessions_2').modulus(4).remainder(2),
599
+ partition('sessions_3').modulus(4).remainder(3),
600
+ ]
601
+ });
602
+ ```
603
+
604
+ ### Check Constraints
605
+ ```typescript
606
+ const products = defineTable('products', {
607
+ price: numeric().precision(10).scale(2).notNull(),
608
+ salePrice: numeric().precision(10).scale(2),
609
+ stockQuantity: integer().default(0)
610
+ }, {
611
+ checkConstraints: (table, check) => [
612
+ check.constraint('price_positive', table.price.gt(0)),
613
+ check.constraint('sale_price_valid',
614
+ table.salePrice.isNull().or(table.salePrice.lte(table.price))
615
+ ),
616
+ check.constraint('stock_non_negative', table.stockQuantity.gte(0))
617
+ ]
618
+ });
619
+ ```
620
+
621
+ ### Indexes
622
+ ```typescript
623
+ const posts = defineTable('posts', {
624
+ id: uuid().primaryKey(),
625
+ title: text().notNull(),
626
+ authorId: uuid('author_id').notNull(),
627
+ tags: text().array(),
628
+ metadata: jsonb(),
629
+ published: boolean().default(false),
630
+ createdAt: timestamp('created_at').default('now()')
631
+ }, {
632
+ indexes: (table, index) => [
633
+ // B-tree (default)
634
+ index('posts_author_idx').on(table.authorId),
635
+
636
+ // Composite with ordering
637
+ index('posts_author_created_idx')
638
+ .on(table.authorId, table.createdAt.desc()),
639
+
640
+ // Partial index
641
+ index('posts_published_idx')
642
+ .on(table.createdAt)
643
+ .where(table.published.eq(true)),
644
+
645
+ // GIN for arrays
646
+ index('posts_tags_idx').on(table.tags).using('gin'),
647
+
648
+ // GIN for JSONB
649
+ index('posts_metadata_idx').on(table.metadata).using('gin'),
650
+
651
+ // Unique
652
+ index('posts_slug_idx').on(table.slug).unique(),
653
+
654
+ // Expression index
655
+ index('posts_title_lower_idx')
656
+ .on(F => F.lower(table.title)),
657
+
658
+ // With storage options
659
+ index('posts_search_idx')
660
+ .on(table.searchVector)
661
+ .using('gin')
662
+ .with({ fastupdate: false })
663
+ ]
664
+ });
665
+ ```
666
+
667
+ ### Relations
668
+ ```typescript
669
+ import { one, many, manyToMany } from 'relq/schema-builder';
670
+
671
+ export const users = defineTable('users', {
672
+ id: uuid().primaryKey(),
673
+ email: text().notNull().unique()
674
+ }, {
675
+ relations: {
676
+ posts: many('posts', { foreignKey: 'authorId' }),
677
+ profile: one('profiles', { foreignKey: 'userId' })
678
+ }
679
+ });
680
+
681
+ export const posts = defineTable('posts', {
682
+ id: uuid().primaryKey(),
683
+ authorId: uuid('author_id').references('users', 'id')
684
+ }, {
685
+ relations: {
686
+ author: one('users', { foreignKey: 'authorId' }),
687
+ tags: manyToMany('tags', {
688
+ through: 'post_tags',
689
+ foreignKey: 'postId',
690
+ otherKey: 'tagId'
691
+ })
692
+ }
693
+ });
694
+ ```
695
+
696
+ ## SQL Functions
697
+
698
+ ```typescript
699
+ import { F, Case, PG } from 'relq';
700
+
701
+ // String
702
+ F.lower('email'), F.upper('name')
703
+ F.concat('first', ' ', 'last')
704
+ F.substring('text', 1, 10)
705
+ F.trim('value'), F.ltrim('value'), F.rtrim('value')
706
+ F.length('text'), F.replace('text', 'old', 'new')
707
+
708
+ // Date/Time
709
+ F.now(), F.currentDate(), F.currentTimestamp()
710
+ F.extract('year', 'created_at')
711
+ F.dateTrunc('month', 'created_at')
712
+ F.age('birth_date')
713
+
714
+ // Math
715
+ F.abs('value'), F.ceil('value'), F.floor('value')
716
+ F.round('price', 2), F.trunc('value', 2)
717
+ F.power('base', 2), F.sqrt('value')
718
+ F.greatest('a', 'b', 'c'), F.least('a', 'b', 'c')
719
+
720
+ // Aggregates
721
+ F.count('id'), F.sum('amount'), F.avg('rating')
722
+ F.min('price'), F.max('price')
723
+ F.arrayAgg('tag'), F.stringAgg('name', ', ')
724
+
725
+ // JSONB
726
+ F.jsonbSet('data', ['key'], 'value')
727
+ F.jsonbExtract('data', 'key')
728
+ F.jsonbArrayLength('items')
729
+
730
+ // Arrays
731
+ F.arrayAppend('tags', 'new')
732
+ F.arrayRemove('tags', 'old')
733
+ F.arrayLength('items', 1)
734
+ F.unnest('tags')
735
+
736
+ // Conditional
737
+ Case()
738
+ .when(F.gt('price', 100), 'expensive')
739
+ .when(F.gt('price', 50), 'moderate')
740
+ .else('cheap')
741
+ .end()
742
+
743
+ // PostgreSQL values
744
+ PG.now() // NOW()
745
+ PG.currentDate() // CURRENT_DATE
746
+ PG.currentUser() // CURRENT_USER
747
+ PG.null() // NULL
748
+ PG.true() // TRUE
749
+ PG.false() // FALSE
750
+ ```
751
+
752
+ ## Transactions
753
+
754
+ ```typescript
755
+ // Basic transaction
756
+ const result = await db.transaction(async (tx) => {
757
+ const user = await tx.table.users
758
+ .insert({ email: 'new@example.com', name: 'User' })
759
+ .returning(['id'])
760
+ .one();
761
+
762
+ await tx.table.posts
763
+ .insert({ title: 'First Post', authorId: user.id })
764
+ .run();
765
+
766
+ return user;
767
+ });
768
+
769
+ // With savepoints
770
+ await db.transaction(async (tx) => {
771
+ await tx.table.users.insert({ ... }).run();
772
+
773
+ try {
774
+ await tx.savepoint('optional', async (sp) => {
775
+ await sp.table.posts.insert({ ... }).run();
776
+ });
777
+ } catch (e) {
778
+ // Savepoint rolled back, transaction continues
779
+ }
780
+
781
+ await tx.table.logs.insert({ ... }).run();
782
+ });
783
+ ```
784
+
785
+ ## CLI Commands
786
+
787
+ ```bash
788
+ relq init # Initialize project
789
+ relq status # Show pending changes
790
+ relq diff [--sql] # Show differences
791
+ relq pull [--force] # Pull from database
792
+ relq generate -m "message" # Create migration
793
+ relq push [--dry-run] # Apply migrations
794
+ relq log / relq history # View history
795
+ relq rollback [n] # Rollback migrations
796
+ relq sync # Full sync
797
+ relq introspect # Generate schema from DB
798
+ ```
799
+
800
+ ## Configuration
801
+
802
+ ```typescript
803
+ // relq.config.ts
804
+ import { defineConfig } from 'relq/config';
805
+
806
+ export default defineConfig({
807
+ connection: {
808
+ host: process.env.DB_HOST,
809
+ port: 5432,
810
+ database: 'myapp',
811
+ user: 'postgres',
812
+ password: process.env.DB_PASSWORD
813
+ },
814
+ schema: './db/schema.ts',
815
+ migrations: {
816
+ directory: './db/migrations',
817
+ tableName: '_relq_migrations',
818
+ format: 'timestamp'
819
+ },
820
+ generate: {
821
+ outDir: './db/generated',
822
+ camelCase: true
823
+ },
824
+ safety: {
825
+ warnOnDataLoss: true,
826
+ confirmDestructive: true
827
+ }
828
+ });
829
+ ```
830
+
831
+ ## Error Handling
832
+
833
+ ```typescript
834
+ import { RelqError, RelqConnectionError, RelqQueryError, isRelqError } from 'relq';
835
+
836
+ try {
837
+ await db.table.users.insert({ ... }).run();
838
+ } catch (error) {
839
+ if (isRelqError(error)) {
840
+ if (error instanceof RelqConnectionError) {
841
+ // Connection issues
842
+ } else if (error instanceof RelqQueryError) {
843
+ console.error('SQL:', error.sql);
844
+ }
845
+ }
846
+ }
847
+ ```
848
+
849
+ ## Requirements
850
+
851
+ - Node.js 18+ or Bun 1.0+
852
+ - PostgreSQL 12+
853
+ - TypeScript 5.0+
854
+
855
+ ## License
856
+
857
+ MIT
858
+
859
+ ## Links
860
+
861
+ - [GitHub](https://github.com/yuniqsolutions/relq)
862
+ - [npm](https://www.npmjs.com/package/relq)