@relq/orm 0.1.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 (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +236 -0
  3. package/dist/cjs/__test-types.cjs +17 -0
  4. package/dist/cjs/addon/cursor.cjs +1473 -0
  5. package/dist/cjs/addon/pg.cjs +4969 -0
  6. package/dist/cjs/cache/index.cjs +9 -0
  7. package/dist/cjs/cache/query-cache.cjs +311 -0
  8. package/dist/cjs/condition/array-condition-builder.cjs +527 -0
  9. package/dist/cjs/condition/array-numeric-condition-builder.cjs +186 -0
  10. package/dist/cjs/condition/array-specialized-condition-builder.cjs +206 -0
  11. package/dist/cjs/condition/array-string-condition-builder.cjs +146 -0
  12. package/dist/cjs/condition/base-condition-builder.cjs +2 -0
  13. package/dist/cjs/condition/condition-collector.cjs +461 -0
  14. package/dist/cjs/condition/fulltext-condition-builder.cjs +61 -0
  15. package/dist/cjs/condition/geometric-condition-builder.cjs +228 -0
  16. package/dist/cjs/condition/index.cjs +29 -0
  17. package/dist/cjs/condition/jsonb-condition-builder.cjs +448 -0
  18. package/dist/cjs/condition/network-condition-builder.cjs +237 -0
  19. package/dist/cjs/condition/postgis-condition-builder.cjs +188 -0
  20. package/dist/cjs/condition/range-condition-builder.cjs +98 -0
  21. package/dist/cjs/core/helpers/ConnectedAggregateBuilder.cjs +132 -0
  22. package/dist/cjs/core/helpers/ConnectedCTEBuilder.cjs +53 -0
  23. package/dist/cjs/core/helpers/ConnectedCountBuilder.cjs +73 -0
  24. package/dist/cjs/core/helpers/ConnectedDeleteBuilder.cjs +65 -0
  25. package/dist/cjs/core/helpers/ConnectedInsertBuilder.cjs +112 -0
  26. package/dist/cjs/core/helpers/ConnectedInsertFromSelectBuilder.cjs +66 -0
  27. package/dist/cjs/core/helpers/ConnectedQueryBuilder.cjs +146 -0
  28. package/dist/cjs/core/helpers/ConnectedRawQueryBuilder.cjs +46 -0
  29. package/dist/cjs/core/helpers/ConnectedSelectBuilder.cjs +331 -0
  30. package/dist/cjs/core/helpers/ConnectedTransactionBuilder.cjs +105 -0
  31. package/dist/cjs/core/helpers/ConnectedUpdateBuilder.cjs +79 -0
  32. package/dist/cjs/core/helpers/PaginateBuilder.cjs +178 -0
  33. package/dist/cjs/core/helpers/ReturningExecutor.cjs +70 -0
  34. package/dist/cjs/core/helpers/capability-guard.cjs +10 -0
  35. package/dist/cjs/core/helpers/index.cjs +31 -0
  36. package/dist/cjs/core/helpers/methods.cjs +10 -0
  37. package/dist/cjs/core/helpers/query-convenience.cjs +238 -0
  38. package/dist/cjs/core/helpers/select-joins.cjs +251 -0
  39. package/dist/cjs/core/helpers/select-pagination.cjs +233 -0
  40. package/dist/cjs/core/helpers/select-types.cjs +2 -0
  41. package/dist/cjs/core/pg-family/cockroachdb-client/capabilities.cjs +31 -0
  42. package/dist/cjs/core/pg-family/cockroachdb-client/index.cjs +7 -0
  43. package/dist/cjs/core/pg-family/cockroachdb-client/relq-cockroach.cjs +16 -0
  44. package/dist/cjs/core/pg-family/dsql-client/capabilities.cjs +31 -0
  45. package/dist/cjs/core/pg-family/dsql-client/index.cjs +7 -0
  46. package/dist/cjs/core/pg-family/dsql-client/relq-dsql.cjs +16 -0
  47. package/dist/cjs/core/pg-family/index.cjs +19 -0
  48. package/dist/cjs/core/pg-family/nile-client/capabilities.cjs +31 -0
  49. package/dist/cjs/core/pg-family/nile-client/index.cjs +7 -0
  50. package/dist/cjs/core/pg-family/nile-client/relq-nile.cjs +36 -0
  51. package/dist/cjs/core/pg-family/nile-client/tenant-context.cjs +44 -0
  52. package/dist/cjs/core/pg-family/pg-client/capabilities.cjs +31 -0
  53. package/dist/cjs/core/pg-family/pg-client/index.cjs +7 -0
  54. package/dist/cjs/core/pg-family/pg-client/relq-postgres.cjs +43 -0
  55. package/dist/cjs/core/pg-family/shared/pg-base.cjs +385 -0
  56. package/dist/cjs/core/pg-family/shared/pg-dialect.cjs +67 -0
  57. package/dist/cjs/core/pg-family/shared/pg-error-parser.cjs +34 -0
  58. package/dist/cjs/core/pg-family/shared/pg-type-coercion.cjs +14 -0
  59. package/dist/cjs/core/relq-base.cjs +307 -0
  60. package/dist/cjs/core/relq-client.cjs +56 -0
  61. package/dist/cjs/core/shared/cleanup.cjs +36 -0
  62. package/dist/cjs/core/shared/column-mapping.cjs +97 -0
  63. package/dist/cjs/core/shared/errors.cjs +17 -0
  64. package/dist/cjs/core/shared/index.cjs +24 -0
  65. package/dist/cjs/core/shared/table-accessor.cjs +22 -0
  66. package/dist/cjs/core/shared/transform.cjs +35 -0
  67. package/dist/cjs/core/shared/types.cjs +2 -0
  68. package/dist/cjs/core/shared/validation.cjs +140 -0
  69. package/dist/cjs/core/types/core.types.cjs +2 -0
  70. package/dist/cjs/count/count-builder.cjs +88 -0
  71. package/dist/cjs/count/index.cjs +5 -0
  72. package/dist/cjs/delete/delete-builder.cjs +176 -0
  73. package/dist/cjs/delete/index.cjs +5 -0
  74. package/dist/cjs/explain/explain-builder.cjs +99 -0
  75. package/dist/cjs/explain/index.cjs +5 -0
  76. package/dist/cjs/index.cjs +26 -0
  77. package/dist/cjs/insert/conflict-builder.cjs +213 -0
  78. package/dist/cjs/insert/index.cjs +5 -0
  79. package/dist/cjs/insert/insert-builder.cjs +320 -0
  80. package/dist/cjs/insert/insert-from-select-builder.cjs +86 -0
  81. package/dist/cjs/pubsub/index.cjs +7 -0
  82. package/dist/cjs/pubsub/listen-notify-builder.cjs +57 -0
  83. package/dist/cjs/pubsub/listener-connection.cjs +180 -0
  84. package/dist/cjs/raw/index.cjs +8 -0
  85. package/dist/cjs/raw/raw-query-builder.cjs +27 -0
  86. package/dist/cjs/raw/sql-template.cjs +73 -0
  87. package/dist/cjs/select/aggregate-builder.cjs +179 -0
  88. package/dist/cjs/select/index.cjs +16 -0
  89. package/dist/cjs/select/join-builder.cjs +192 -0
  90. package/dist/cjs/select/join-condition-builder.cjs +189 -0
  91. package/dist/cjs/select/join-internals.cjs +5 -0
  92. package/dist/cjs/select/join-many-condition-builder.cjs +159 -0
  93. package/dist/cjs/select/scalar-query-builder.cjs +134 -0
  94. package/dist/cjs/select/scalar-select-builder.cjs +78 -0
  95. package/dist/cjs/select/select-builder.cjs +426 -0
  96. package/dist/cjs/select/sql-expression.cjs +38 -0
  97. package/dist/cjs/select/table-proxy.cjs +99 -0
  98. package/dist/cjs/shared/aws-dsql.cjs +181 -0
  99. package/dist/cjs/shared/errors/relq-errors.cjs +361 -0
  100. package/dist/cjs/shared/pg-format.cjs +383 -0
  101. package/dist/cjs/shared/types/config-types.cjs +51 -0
  102. package/dist/cjs/transaction/index.cjs +6 -0
  103. package/dist/cjs/transaction/transaction-builder.cjs +78 -0
  104. package/dist/cjs/types/aggregate-types.cjs +2 -0
  105. package/dist/cjs/types/inference-types.cjs +18 -0
  106. package/dist/cjs/types/pagination-types.cjs +7 -0
  107. package/dist/cjs/types/result-types.cjs +2 -0
  108. package/dist/cjs/types/scalar-types.cjs +2 -0
  109. package/dist/cjs/types/schema-types.cjs +2 -0
  110. package/dist/cjs/types/subscription-types.cjs +2 -0
  111. package/dist/cjs/types.cjs +2 -0
  112. package/dist/cjs/update/array-update-builder.cjs +232 -0
  113. package/dist/cjs/update/index.cjs +16 -0
  114. package/dist/cjs/update/jsonb-update-builder.cjs +219 -0
  115. package/dist/cjs/update/update-builder.cjs +274 -0
  116. package/dist/cjs/utils/addon/pg/cursor.cjs +8 -0
  117. package/dist/cjs/utils/addon/pg/pg.cjs +23 -0
  118. package/dist/cjs/utils/case-converter.cjs +58 -0
  119. package/dist/cjs/utils/env-resolver.cjs +226 -0
  120. package/dist/cjs/utils/environment-detection.cjs +124 -0
  121. package/dist/cjs/utils/fk-resolver.cjs +186 -0
  122. package/dist/cjs/utils/index.cjs +25 -0
  123. package/dist/cjs/utils/pool-defaults.cjs +91 -0
  124. package/dist/cjs/utils/type-coercion.cjs +120 -0
  125. package/dist/cjs/window/index.cjs +5 -0
  126. package/dist/cjs/window/window-builder.cjs +80 -0
  127. package/dist/esm/__test-types.js +15 -0
  128. package/dist/esm/addon/cursor.js +1440 -0
  129. package/dist/esm/addon/pg.js +4931 -0
  130. package/dist/esm/cache/index.js +1 -0
  131. package/dist/esm/cache/query-cache.js +303 -0
  132. package/dist/esm/condition/array-condition-builder.js +519 -0
  133. package/dist/esm/condition/array-numeric-condition-builder.js +182 -0
  134. package/dist/esm/condition/array-specialized-condition-builder.js +200 -0
  135. package/dist/esm/condition/array-string-condition-builder.js +142 -0
  136. package/dist/esm/condition/base-condition-builder.js +1 -0
  137. package/dist/esm/condition/condition-collector.js +452 -0
  138. package/dist/esm/condition/fulltext-condition-builder.js +53 -0
  139. package/dist/esm/condition/geometric-condition-builder.js +220 -0
  140. package/dist/esm/condition/index.js +8 -0
  141. package/dist/esm/condition/jsonb-condition-builder.js +439 -0
  142. package/dist/esm/condition/network-condition-builder.js +229 -0
  143. package/dist/esm/condition/postgis-condition-builder.js +180 -0
  144. package/dist/esm/condition/range-condition-builder.js +90 -0
  145. package/dist/esm/core/helpers/ConnectedAggregateBuilder.js +128 -0
  146. package/dist/esm/core/helpers/ConnectedCTEBuilder.js +49 -0
  147. package/dist/esm/core/helpers/ConnectedCountBuilder.js +69 -0
  148. package/dist/esm/core/helpers/ConnectedDeleteBuilder.js +61 -0
  149. package/dist/esm/core/helpers/ConnectedInsertBuilder.js +108 -0
  150. package/dist/esm/core/helpers/ConnectedInsertFromSelectBuilder.js +62 -0
  151. package/dist/esm/core/helpers/ConnectedQueryBuilder.js +142 -0
  152. package/dist/esm/core/helpers/ConnectedRawQueryBuilder.js +42 -0
  153. package/dist/esm/core/helpers/ConnectedSelectBuilder.js +327 -0
  154. package/dist/esm/core/helpers/ConnectedTransactionBuilder.js +100 -0
  155. package/dist/esm/core/helpers/ConnectedUpdateBuilder.js +75 -0
  156. package/dist/esm/core/helpers/PaginateBuilder.js +174 -0
  157. package/dist/esm/core/helpers/ReturningExecutor.js +66 -0
  158. package/dist/esm/core/helpers/capability-guard.js +7 -0
  159. package/dist/esm/core/helpers/index.js +13 -0
  160. package/dist/esm/core/helpers/methods.js +6 -0
  161. package/dist/esm/core/helpers/query-convenience.js +194 -0
  162. package/dist/esm/core/helpers/select-joins.js +246 -0
  163. package/dist/esm/core/helpers/select-pagination.js +226 -0
  164. package/dist/esm/core/helpers/select-types.js +1 -0
  165. package/dist/esm/core/pg-family/cockroachdb-client/capabilities.js +28 -0
  166. package/dist/esm/core/pg-family/cockroachdb-client/index.js +2 -0
  167. package/dist/esm/core/pg-family/cockroachdb-client/relq-cockroach.js +12 -0
  168. package/dist/esm/core/pg-family/dsql-client/capabilities.js +28 -0
  169. package/dist/esm/core/pg-family/dsql-client/index.js +2 -0
  170. package/dist/esm/core/pg-family/dsql-client/relq-dsql.js +12 -0
  171. package/dist/esm/core/pg-family/index.js +6 -0
  172. package/dist/esm/core/pg-family/nile-client/capabilities.js +28 -0
  173. package/dist/esm/core/pg-family/nile-client/index.js +2 -0
  174. package/dist/esm/core/pg-family/nile-client/relq-nile.js +32 -0
  175. package/dist/esm/core/pg-family/nile-client/tenant-context.js +40 -0
  176. package/dist/esm/core/pg-family/pg-client/capabilities.js +28 -0
  177. package/dist/esm/core/pg-family/pg-client/index.js +2 -0
  178. package/dist/esm/core/pg-family/pg-client/relq-postgres.js +39 -0
  179. package/dist/esm/core/pg-family/shared/pg-base.js +347 -0
  180. package/dist/esm/core/pg-family/shared/pg-dialect.js +63 -0
  181. package/dist/esm/core/pg-family/shared/pg-error-parser.js +29 -0
  182. package/dist/esm/core/pg-family/shared/pg-type-coercion.js +6 -0
  183. package/dist/esm/core/relq-base.js +270 -0
  184. package/dist/esm/core/relq-client.js +48 -0
  185. package/dist/esm/core/shared/cleanup.js +27 -0
  186. package/dist/esm/core/shared/column-mapping.js +90 -0
  187. package/dist/esm/core/shared/errors.js +13 -0
  188. package/dist/esm/core/shared/index.js +6 -0
  189. package/dist/esm/core/shared/table-accessor.js +19 -0
  190. package/dist/esm/core/shared/transform.js +30 -0
  191. package/dist/esm/core/shared/types.js +1 -0
  192. package/dist/esm/core/shared/validation.js +136 -0
  193. package/dist/esm/core/types/core.types.js +1 -0
  194. package/dist/esm/count/count-builder.js +81 -0
  195. package/dist/esm/count/index.js +1 -0
  196. package/dist/esm/delete/delete-builder.js +169 -0
  197. package/dist/esm/delete/index.js +1 -0
  198. package/dist/esm/explain/explain-builder.js +95 -0
  199. package/dist/esm/explain/index.js +1 -0
  200. package/dist/esm/index.js +7 -0
  201. package/dist/esm/insert/conflict-builder.js +202 -0
  202. package/dist/esm/insert/index.js +1 -0
  203. package/dist/esm/insert/insert-builder.js +313 -0
  204. package/dist/esm/insert/insert-from-select-builder.js +79 -0
  205. package/dist/esm/pubsub/index.js +1 -0
  206. package/dist/esm/pubsub/listen-notify-builder.js +48 -0
  207. package/dist/esm/pubsub/listener-connection.js +173 -0
  208. package/dist/esm/raw/index.js +2 -0
  209. package/dist/esm/raw/raw-query-builder.js +20 -0
  210. package/dist/esm/raw/sql-template.js +66 -0
  211. package/dist/esm/select/aggregate-builder.js +172 -0
  212. package/dist/esm/select/index.js +4 -0
  213. package/dist/esm/select/join-builder.js +184 -0
  214. package/dist/esm/select/join-condition-builder.js +181 -0
  215. package/dist/esm/select/join-internals.js +2 -0
  216. package/dist/esm/select/join-many-condition-builder.js +151 -0
  217. package/dist/esm/select/scalar-query-builder.js +126 -0
  218. package/dist/esm/select/scalar-select-builder.js +70 -0
  219. package/dist/esm/select/select-builder.js +419 -0
  220. package/dist/esm/select/sql-expression.js +33 -0
  221. package/dist/esm/select/table-proxy.js +91 -0
  222. package/dist/esm/shared/aws-dsql.js +140 -0
  223. package/dist/esm/shared/errors/relq-errors.js +339 -0
  224. package/dist/esm/shared/pg-format.js +375 -0
  225. package/dist/esm/shared/types/config-types.js +46 -0
  226. package/dist/esm/transaction/index.js +1 -0
  227. package/dist/esm/transaction/transaction-builder.js +70 -0
  228. package/dist/esm/types/aggregate-types.js +1 -0
  229. package/dist/esm/types/inference-types.js +12 -0
  230. package/dist/esm/types/pagination-types.js +4 -0
  231. package/dist/esm/types/result-types.js +1 -0
  232. package/dist/esm/types/scalar-types.js +1 -0
  233. package/dist/esm/types/schema-types.js +1 -0
  234. package/dist/esm/types/subscription-types.js +1 -0
  235. package/dist/esm/types.js +1 -0
  236. package/dist/esm/update/array-update-builder.js +219 -0
  237. package/dist/esm/update/index.js +3 -0
  238. package/dist/esm/update/jsonb-update-builder.js +211 -0
  239. package/dist/esm/update/update-builder.js +267 -0
  240. package/dist/esm/utils/addon/pg/cursor.js +1 -0
  241. package/dist/esm/utils/addon/pg/pg.js +2 -0
  242. package/dist/esm/utils/case-converter.js +55 -0
  243. package/dist/esm/utils/env-resolver.js +213 -0
  244. package/dist/esm/utils/environment-detection.js +114 -0
  245. package/dist/esm/utils/fk-resolver.js +178 -0
  246. package/dist/esm/utils/index.js +4 -0
  247. package/dist/esm/utils/pool-defaults.js +85 -0
  248. package/dist/esm/utils/type-coercion.js +112 -0
  249. package/dist/esm/window/index.js +1 -0
  250. package/dist/esm/window/window-builder.js +73 -0
  251. package/dist/index.cjs +1 -0
  252. package/dist/index.d.ts +7281 -0
  253. package/dist/index.js +1 -0
  254. package/package.json +52 -0
@@ -0,0 +1,270 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { RelqConnectionError } from "../shared/errors/relq-errors.js";
3
+ import { ConditionCollector } from "../condition/condition-collector.js";
4
+ import { ConnectedCTEBuilder, ConnectedRawQueryBuilder, INTERNAL, debugLog } from "./helpers/index.js";
5
+ import { ConnectedScalarSelectBuilder } from "../select/scalar-select-builder.js";
6
+ import { buildColumnMappings, transformToDbColumns, transformFromDbColumns, transformResultsFromDb, hasColumnMapping } from "./shared/column-mapping.js";
7
+ import { validateData } from "./shared/validation.js";
8
+ import { createTableAccessor } from "./shared/table-accessor.js";
9
+ import { registerInstance, unregisterInstance } from "./shared/cleanup.js";
10
+ export class RelqBase {
11
+ config;
12
+ schema;
13
+ emitter = new EventEmitter();
14
+ _defaultErrorHandler = () => { };
15
+ columnMappings = new Map();
16
+ initialized = false;
17
+ initPromise;
18
+ _isClosed = false;
19
+ constructor(schema, config) {
20
+ this.config = config;
21
+ this.schema = schema;
22
+ this.emitter.on('error', this._defaultErrorHandler);
23
+ if (schema) {
24
+ const log = config.logLevel === 'debug'
25
+ ? (...args) => debugLog(config, ...args)
26
+ : undefined;
27
+ buildColumnMappings(schema, this.columnMappings, log);
28
+ }
29
+ registerInstance(this);
30
+ }
31
+ async ensureInitialized() {
32
+ if (this.initialized)
33
+ return;
34
+ if (this.initPromise)
35
+ return this.initPromise;
36
+ this.initPromise = (async () => {
37
+ await this._initialize();
38
+ this.initialized = true;
39
+ })();
40
+ return this.initPromise;
41
+ }
42
+ _transformToDbColumns(tableName, data) {
43
+ return transformToDbColumns(tableName, data, this.columnMappings);
44
+ }
45
+ _transformFromDbColumns(tableName, data) {
46
+ return transformFromDbColumns(tableName, data, this.columnMappings);
47
+ }
48
+ _transformResultsFromDb(tableName, rows) {
49
+ return transformResultsFromDb(tableName, rows, this.columnMappings);
50
+ }
51
+ _hasColumnMapping() {
52
+ return hasColumnMapping(this.columnMappings);
53
+ }
54
+ _validateData(tableName, data, operation) {
55
+ validateData(tableName, data, operation, this.columnMappings, this.config);
56
+ }
57
+ _getSchema() {
58
+ if (!this.schema)
59
+ return undefined;
60
+ return this.schema.tables || this.schema;
61
+ }
62
+ _getRelations() {
63
+ return this.config.relations;
64
+ }
65
+ _getTableDef(tableKey) {
66
+ const schema = this._getSchema();
67
+ if (!schema)
68
+ return undefined;
69
+ return schema[tableKey];
70
+ }
71
+ get [INTERNAL]() {
72
+ return {
73
+ executeSelect: this._executeSelect.bind(this),
74
+ executeSelectOne: this._executeSelectOne.bind(this),
75
+ executeCount: this._executeCount.bind(this),
76
+ executeRun: this._executeRun.bind(this),
77
+ executeQuery: this._executeQuery.bind(this),
78
+ transformToDbColumns: this._transformToDbColumns.bind(this),
79
+ transformFromDbColumns: this._transformFromDbColumns.bind(this),
80
+ transformResultsFromDb: this._transformResultsFromDb.bind(this),
81
+ hasColumnMapping: this._hasColumnMapping.bind(this),
82
+ getClientForCursor: this._acquireClient.bind(this),
83
+ validateData: this._validateData.bind(this),
84
+ getSchema: this._getSchema.bind(this),
85
+ getRelations: this._getRelations.bind(this),
86
+ getTableDef: this._getTableDef.bind(this)
87
+ };
88
+ }
89
+ async _executeQuery(sql) {
90
+ if (!sql || typeof sql !== 'string' || sql.trim() === '') {
91
+ throw new RelqConnectionError(`Invalid SQL query: ${sql === null ? 'null' : sql === undefined ? 'undefined' : 'empty string'}`);
92
+ }
93
+ await this.ensureInitialized();
94
+ const startTime = performance.now();
95
+ const retryConfig = this.config.retry;
96
+ if (!retryConfig) {
97
+ const result = await this._query(sql);
98
+ return { result, duration: performance.now() - startTime };
99
+ }
100
+ const opts = typeof retryConfig === 'object'
101
+ ? { maxRetries: retryConfig.maxRetries ?? 3, initialDelayMs: retryConfig.initialDelayMs ?? 250, maxDelayMs: retryConfig.maxDelayMs ?? 5000 }
102
+ : { maxRetries: 3, initialDelayMs: 250, maxDelayMs: 5000 };
103
+ let lastError;
104
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
105
+ try {
106
+ const result = await this._query(sql);
107
+ return { result, duration: performance.now() - startTime };
108
+ }
109
+ catch (error) {
110
+ lastError = error;
111
+ if (attempt < opts.maxRetries && isTransientError(error)) {
112
+ const delay = Math.min(opts.initialDelayMs * 2 ** attempt, opts.maxDelayMs);
113
+ debugLog(this.config, `Transient error (attempt ${attempt + 1}/${opts.maxRetries}), retrying in ${delay}ms: ${error.code || error.message}`);
114
+ await sleep(delay);
115
+ continue;
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ throw lastError;
121
+ }
122
+ buildMetadata(result, duration) {
123
+ return {
124
+ rowCount: result.rowCount,
125
+ command: result.command,
126
+ duration,
127
+ fields: result.fields
128
+ };
129
+ }
130
+ async _executeSelect(sql, tableName) {
131
+ const { result, duration } = await this._executeQuery(sql);
132
+ const rows = tableName
133
+ ? this._transformResultsFromDb(tableName, result.rows)
134
+ : result.rows;
135
+ return {
136
+ data: rows,
137
+ metadata: this.buildMetadata(result, duration)
138
+ };
139
+ }
140
+ async _executeSelectOne(sql, tableName) {
141
+ const { result, duration } = await this._executeQuery(sql);
142
+ const row = result.rows[0]
143
+ ? (tableName ? this._transformFromDbColumns(tableName, result.rows[0]) : result.rows[0])
144
+ : null;
145
+ return {
146
+ data: row,
147
+ metadata: this.buildMetadata(result, duration)
148
+ };
149
+ }
150
+ async _executeCount(sql) {
151
+ const { result, duration } = await this._executeQuery(sql);
152
+ const count = result.rows[0]?.count
153
+ ? parseInt(result.rows[0].count, 10)
154
+ : 0;
155
+ return {
156
+ count,
157
+ metadata: this.buildMetadata(result, duration)
158
+ };
159
+ }
160
+ async _executeRun(sql) {
161
+ const { result, duration } = await this._executeQuery(sql);
162
+ return {
163
+ success: true,
164
+ metadata: this.buildMetadata(result, duration)
165
+ };
166
+ }
167
+ get table() {
168
+ return createTableAccessor(this, this.schema);
169
+ }
170
+ raw(query, ...params) {
171
+ return new ConnectedRawQueryBuilder(query, params, this);
172
+ }
173
+ async transaction(callback) {
174
+ const { executeTransaction } = await import("./helpers/ConnectedTransactionBuilder.js");
175
+ return executeTransaction(this, callback);
176
+ }
177
+ with(name, query) {
178
+ return new ConnectedCTEBuilder(this).with(name, query);
179
+ }
180
+ async ctAs(tableName, query, options = {}) {
181
+ const queryStr = typeof query === 'string' ? query : query.toString();
182
+ const temp = options.temporary ? 'TEMPORARY ' : '';
183
+ const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
184
+ const sql = `CREATE ${temp}TABLE ${ifNotExists}"${tableName}" AS ${queryStr}`;
185
+ await this[INTERNAL].executeQuery(sql);
186
+ }
187
+ async explain(query, options = {}) {
188
+ const queryStr = typeof query === 'string' ? query : query.toString();
189
+ const parts = ['EXPLAIN'];
190
+ if (options.format)
191
+ parts.push(`(FORMAT ${options.format.toUpperCase()}`);
192
+ if (options.analyze)
193
+ parts.push(options.format ? ', ANALYZE' : '(ANALYZE');
194
+ if (options.verbose)
195
+ parts.push(options.format || options.analyze ? ', VERBOSE' : '(VERBOSE');
196
+ if (options.format || options.analyze || options.verbose)
197
+ parts.push(')');
198
+ const sql = `${parts.join(' ')} ${queryStr}`;
199
+ const { result } = await this._executeQuery(sql);
200
+ if (options.format === 'json') {
201
+ return result.rows;
202
+ }
203
+ return result.rows.map((r) => r['QUERY PLAN'] || Object.values(r)[0]).join('\n');
204
+ }
205
+ scalar(scalars) {
206
+ const schema = this._getSchema();
207
+ return new ConnectedScalarSelectBuilder(scalars, schema, {
208
+ executeSelectOne: this[INTERNAL].executeSelect.bind(this),
209
+ hasColumnMapping: this._hasColumnMapping.bind(this),
210
+ transformToDbColumns: this._transformToDbColumns.bind(this)
211
+ });
212
+ }
213
+ where(_tableName) {
214
+ return new ConditionCollector();
215
+ }
216
+ on(event, listener) {
217
+ this.emitter.on(event, listener);
218
+ return this;
219
+ }
220
+ once(event, listener) {
221
+ this.emitter.once(event, listener);
222
+ return this;
223
+ }
224
+ off(event, listener) {
225
+ this.emitter.off(event, listener);
226
+ return this;
227
+ }
228
+ async close() {
229
+ if (!this.initialized || this._isClosed)
230
+ return;
231
+ try {
232
+ await this._close();
233
+ this._isClosed = true;
234
+ unregisterInstance(this);
235
+ }
236
+ catch (error) {
237
+ console.error('Error closing database connection:', error);
238
+ throw error;
239
+ }
240
+ }
241
+ get closed() {
242
+ return this._isClosed;
243
+ }
244
+ }
245
+ const TRANSIENT_NETWORK_CODES = new Set([
246
+ 'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ESERVFAIL',
247
+ 'ETIMEDOUT', 'EPIPE', 'EAI_AGAIN', 'EHOSTUNREACH',
248
+ 'CONNECTION_LOST', 'PROTOCOL_CONNECTION_LOST',
249
+ ]);
250
+ const TRANSIENT_PG_CODES = new Set([
251
+ '40P01',
252
+ '40001',
253
+ '08006',
254
+ '08001',
255
+ '08004',
256
+ '57P01',
257
+ '57P03',
258
+ ]);
259
+ function isTransientError(error) {
260
+ if (TRANSIENT_NETWORK_CODES.has(error.code))
261
+ return true;
262
+ if (error.cause && TRANSIENT_NETWORK_CODES.has(error.cause.code))
263
+ return true;
264
+ if (TRANSIENT_PG_CODES.has(error.code))
265
+ return true;
266
+ return false;
267
+ }
268
+ function sleep(ms) {
269
+ return new Promise(resolve => setTimeout(resolve, ms));
270
+ }
@@ -0,0 +1,48 @@
1
+ import { RelqPostgres } from "./pg-family/pg-client/index.js";
2
+ import { RelqNile } from "./pg-family/nile-client/index.js";
3
+ import { RelqDsql } from "./pg-family/dsql-client/index.js";
4
+ import { RelqCockroachDB } from "./pg-family/cockroachdb-client/index.js";
5
+ import { RelqConfigError } from "../shared/errors/relq-errors.js";
6
+ class RelqImpl {
7
+ constructor(schema, dialect, options) {
8
+ const opts = options ?? {};
9
+ switch (dialect) {
10
+ case 'postgres':
11
+ return new RelqPostgres(schema, { ...opts, dialect: 'postgres' });
12
+ case 'nile':
13
+ return new RelqNile(schema, { ...opts, dialect: 'nile' });
14
+ case 'cockroachdb':
15
+ return new RelqCockroachDB(schema, { ...opts, dialect: 'cockroachdb' });
16
+ case 'awsdsql':
17
+ return new RelqDsql(schema, { ...opts, dialect: 'awsdsql' });
18
+ case 'sqlite':
19
+ throw new RelqConfigError(`Dialect "sqlite" is not yet implemented. ` +
20
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
21
+ case 'turso':
22
+ throw new RelqConfigError(`Dialect "turso" is not yet implemented. ` +
23
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
24
+ case 'mysql':
25
+ throw new RelqConfigError(`Dialect "mysql" is not yet implemented. ` +
26
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
27
+ case 'mariadb':
28
+ throw new RelqConfigError(`Dialect "mariadb" is not yet implemented. ` +
29
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
30
+ case 'planetscale':
31
+ throw new RelqConfigError(`Dialect "planetscale" is not yet implemented. ` +
32
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
33
+ case 'xata':
34
+ throw new RelqConfigError(`Dialect "xata" is not yet implemented. ` +
35
+ `Currently supported: postgres, nile, cockroachdb, awsdsql`, { field: 'dialect', value: dialect });
36
+ default:
37
+ throw new RelqConfigError(`Unknown dialect: "${dialect}". ` +
38
+ `Supported dialects: postgres, nile, cockroachdb, awsdsql, sqlite, turso, mysql, mariadb, planetscale, xata`, { field: 'dialect', value: dialect });
39
+ }
40
+ }
41
+ }
42
+ export const Relq = RelqImpl;
43
+ export { RelqPostgres } from "./pg-family/pg-client/index.js";
44
+ export { RelqNile } from "./pg-family/nile-client/index.js";
45
+ export { RelqDsql } from "./pg-family/dsql-client/index.js";
46
+ export { RelqCockroachDB } from "./pg-family/cockroachdb-client/index.js";
47
+ export { RelqBase } from "./relq-base.js";
48
+ export default Relq;
@@ -0,0 +1,27 @@
1
+ import process from 'node:process';
2
+ export const activeInstances = new Set();
3
+ let cleanupHandlersRegistered = false;
4
+ export function registerGlobalCleanupHandlers() {
5
+ if (cleanupHandlersRegistered)
6
+ return;
7
+ if (typeof process === 'undefined' || !process.on)
8
+ return;
9
+ try {
10
+ if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers')
11
+ return;
12
+ }
13
+ catch { }
14
+ process.on('beforeExit', async () => {
15
+ if (activeInstances.size === 0)
16
+ return;
17
+ await Promise.all(Array.from(activeInstances).map(instance => instance.close().catch(err => console.error('Error closing database connection:', err))));
18
+ });
19
+ cleanupHandlersRegistered = true;
20
+ }
21
+ export function registerInstance(instance) {
22
+ activeInstances.add(instance);
23
+ registerGlobalCleanupHandlers();
24
+ }
25
+ export function unregisterInstance(instance) {
26
+ activeInstances.delete(instance);
27
+ }
@@ -0,0 +1,90 @@
1
+ import { serializeValue, deserializeValue } from "../../utils/type-coercion.js";
2
+ export function buildColumnMappings(schema, mappings, debugLog) {
3
+ if (!schema || typeof schema !== 'object')
4
+ return;
5
+ const tables = schema.tables || schema;
6
+ for (const [tableName, tableDef] of Object.entries(tables)) {
7
+ if (!tableDef || typeof tableDef !== 'object')
8
+ continue;
9
+ const columns = tableDef.$columns;
10
+ if (!columns || typeof columns !== 'object')
11
+ continue;
12
+ const propToDb = new Map();
13
+ const dbToProp = new Map();
14
+ const propToType = new Map();
15
+ const propToCheckValues = new Map();
16
+ const propToValidate = new Map();
17
+ const propToFields = new Map();
18
+ for (const [propName, colDef] of Object.entries(columns)) {
19
+ const dbColName = colDef?.$columnName ?? propName;
20
+ const colType = colDef?.$sqlType || (typeof colDef?.$type === 'string' ? colDef.$type : undefined) || 'TEXT';
21
+ propToDb.set(propName, dbColName);
22
+ dbToProp.set(dbColName, propName);
23
+ propToType.set(propName, colType);
24
+ if (debugLog) {
25
+ debugLog(`buildColumnMappings: ${tableName}.${propName} -> type=${colType}, $validate=${!!colDef?.$validate}, $fields=${!!colDef?.$fields}, $checkValues=${!!colDef?.$checkValues}`);
26
+ }
27
+ if (colDef?.$checkValues && Array.isArray(colDef.$checkValues)) {
28
+ propToCheckValues.set(propName, colDef.$checkValues);
29
+ }
30
+ if (colDef?.$validate && typeof colDef.$validate === 'function') {
31
+ propToValidate.set(propName, colDef.$validate);
32
+ }
33
+ if (colDef?.$fields && typeof colDef.$fields === 'object') {
34
+ propToFields.set(propName, colDef.$fields);
35
+ }
36
+ }
37
+ const dbTableName = tableDef.$name ?? tableName;
38
+ mappings.set(dbTableName, {
39
+ propToDb,
40
+ dbToProp,
41
+ propToType,
42
+ propToCheckValues,
43
+ propToValidate,
44
+ propToFields
45
+ });
46
+ }
47
+ }
48
+ export function transformToDbColumns(tableName, data, mappings) {
49
+ const mapping = mappings.get(tableName);
50
+ if (!mapping)
51
+ return data;
52
+ const result = {};
53
+ for (const [key, value] of Object.entries(data)) {
54
+ const dbColName = mapping.propToDb.get(key) ?? key;
55
+ const colType = mapping.propToType.get(key);
56
+ if (colType && typeof colType === 'string') {
57
+ result[dbColName] = serializeValue(value, colType);
58
+ }
59
+ else {
60
+ result[dbColName] = value;
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ export function transformFromDbColumns(tableName, data, mappings) {
66
+ const mapping = mappings.get(tableName);
67
+ if (!mapping)
68
+ return data;
69
+ const result = {};
70
+ for (const [key, value] of Object.entries(data)) {
71
+ const propName = mapping.dbToProp.get(key) ?? key;
72
+ const colType = mapping.propToType.get(propName);
73
+ if (colType && typeof colType === 'string') {
74
+ result[propName] = deserializeValue(value, colType);
75
+ }
76
+ else {
77
+ result[propName] = value;
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ export function transformResultsFromDb(tableName, rows, mappings) {
83
+ const mapping = mappings.get(tableName);
84
+ if (!mapping)
85
+ return rows;
86
+ return rows.map(row => transformFromDbColumns(tableName, row, mappings));
87
+ }
88
+ export function hasColumnMapping(mappings) {
89
+ return mappings.size > 0;
90
+ }
@@ -0,0 +1,13 @@
1
+ import { RelqConfigError } from "../../../shared/errors/relq-errors.js";
2
+ export class RelqDialectError extends RelqConfigError {
3
+ dialect;
4
+ suggestion;
5
+ constructor(message, dialect, suggestion) {
6
+ super(`${message}\n` +
7
+ ` Dialect: ${dialect}\n` +
8
+ (suggestion ? ` Suggestion: ${suggestion}` : ''));
9
+ this.name = 'RelqDialectError';
10
+ this.dialect = dialect;
11
+ this.suggestion = suggestion;
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ export { RelqDialectError } from "./errors.js";
2
+ export { buildColumnMappings, transformToDbColumns, transformFromDbColumns, transformResultsFromDb, hasColumnMapping } from "./column-mapping.js";
3
+ export { validateData, validateComposite } from "./validation.js";
4
+ export { deserializeRow as transformDeserializeRow, serializeRow as transformSerializeRow, buildColumnTypeMap } from "./transform.js";
5
+ export { createTableAccessor } from "./table-accessor.js";
6
+ export { registerInstance, unregisterInstance, activeInstances } from "./cleanup.js";
@@ -0,0 +1,19 @@
1
+ import { ConnectedQueryBuilder } from "../helpers/ConnectedQueryBuilder.js";
2
+ export function createTableAccessor(relq, schema) {
3
+ const tableFunction = (tableName) => {
4
+ return new ConnectedQueryBuilder(tableName, relq);
5
+ };
6
+ return new Proxy(tableFunction, {
7
+ get(target, prop, receiver) {
8
+ if (prop in target) {
9
+ return Reflect.get(target, prop, receiver);
10
+ }
11
+ if (typeof prop === 'string' && schema && prop in schema) {
12
+ const tableDef = schema[prop];
13
+ const sqlTableName = tableDef?.$name || prop;
14
+ return new ConnectedQueryBuilder(sqlTableName, relq, prop);
15
+ }
16
+ return undefined;
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,30 @@
1
+ export function deserializeRow(row, columnTypes, coercion) {
2
+ const result = {};
3
+ for (const [key, value] of Object.entries(row)) {
4
+ const dbType = columnTypes.get(key);
5
+ result[key] = dbType ? coercion.deserializeValue(value, dbType) : value;
6
+ }
7
+ return result;
8
+ }
9
+ export function serializeRow(row, columnTypes, coercion) {
10
+ const result = {};
11
+ for (const [key, value] of Object.entries(row)) {
12
+ const dbType = columnTypes.get(key);
13
+ result[key] = dbType ? coercion.serializeValue(value, dbType) : value;
14
+ }
15
+ return result;
16
+ }
17
+ export function buildColumnTypeMap(tableDef) {
18
+ const typeMap = new Map();
19
+ const columns = tableDef.$columns || tableDef;
20
+ for (const [key, colDef] of Object.entries(columns)) {
21
+ if (colDef && typeof colDef === 'object') {
22
+ const col = colDef;
23
+ const sqlType = col.$sqlType || (typeof col.$type === 'string' ? col.$type : undefined);
24
+ if (sqlType) {
25
+ typeMap.set(key, sqlType);
26
+ }
27
+ }
28
+ }
29
+ return typeMap;
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,136 @@
1
+ import { RelqQueryError } from "../../../shared/errors/relq-errors.js";
2
+ import { debugLog } from "../helpers/methods.js";
3
+ export function validateData(tableName, data, operation, mappings, config) {
4
+ const validation = config?.validation;
5
+ if (validation?.enabled === false)
6
+ return;
7
+ const mapping = mappings.get(tableName);
8
+ if (!mapping) {
9
+ debugLog(config, `No mapping found for table: ${tableName}. Available:`, Array.from(mappings.keys()));
10
+ return;
11
+ }
12
+ const errors = [];
13
+ const validateLength = validation?.validateLength !== false;
14
+ const validateTypes = validation?.validateTypes !== false;
15
+ const onError = validation?.onError ?? 'throw';
16
+ debugLog(config, `Validating ${operation} on ${tableName}. Fields:`, Object.keys(data));
17
+ for (const [propName, value] of Object.entries(data)) {
18
+ if (value === null || value === undefined)
19
+ continue;
20
+ const colType = mapping.propToType.get(propName);
21
+ const colTypeStr = typeof colType === 'string' ? colType : null;
22
+ if (validateTypes && colTypeStr) {
23
+ const jsType = typeof value;
24
+ const upperType = colTypeStr.toUpperCase();
25
+ if (['INTEGER', 'INT', 'SMALLINT', 'BIGINT', 'INT2', 'INT4', 'INT8', 'SERIAL'].some(t => upperType.includes(t))) {
26
+ if (jsType !== 'number' && jsType !== 'bigint' && jsType !== 'string') {
27
+ errors.push(`${propName}: expected number for ${colType}, got ${jsType}`);
28
+ }
29
+ }
30
+ else if (['REAL', 'DOUBLE', 'FLOAT', 'NUMERIC', 'DECIMAL'].some(t => upperType.includes(t))) {
31
+ if (jsType !== 'number' && jsType !== 'string') {
32
+ errors.push(`${propName}: expected number for ${colType}, got ${jsType}`);
33
+ }
34
+ }
35
+ else if (upperType.includes('BOOL')) {
36
+ if (jsType !== 'boolean') {
37
+ errors.push(`${propName}: expected boolean for ${colType}, got ${jsType}`);
38
+ }
39
+ }
40
+ else if (['VARCHAR', 'CHAR', 'TEXT', 'UUID'].some(t => upperType.includes(t))) {
41
+ if (jsType !== 'string') {
42
+ errors.push(`${propName}: expected string for ${colType}, got ${jsType}`);
43
+ }
44
+ }
45
+ }
46
+ if (validateLength && typeof value === 'string' && colTypeStr) {
47
+ const lengthMatch = colTypeStr.match(/(?:var)?char\((\d+)\)/i);
48
+ if (lengthMatch) {
49
+ const maxLength = parseInt(lengthMatch[1], 10);
50
+ if (value.length > maxLength) {
51
+ errors.push(`${propName}: string length ${value.length} exceeds ${colTypeStr} limit of ${maxLength}`);
52
+ }
53
+ }
54
+ }
55
+ const validateFn = mapping.propToValidate.get(propName);
56
+ if (validateFn) {
57
+ const isValid = validateFn(value);
58
+ debugLog(config, `Domain validation for '${propName}' with value '${value}': isValid=${isValid}`);
59
+ if (!isValid) {
60
+ errors.push(`${propName}: value '${value}' failed domain validation`);
61
+ }
62
+ }
63
+ const compositeFields = mapping.propToFields.get(propName);
64
+ if (compositeFields && value && typeof value === 'object') {
65
+ validateComposite(propName, value, compositeFields, errors, config);
66
+ }
67
+ const checkValues = mapping.propToCheckValues.get(propName);
68
+ if (checkValues && checkValues.length > 0 && typeof value === 'string') {
69
+ if (!checkValues.includes(value)) {
70
+ errors.push(`${propName}: value '${value}' not in allowed values [${checkValues.join(', ')}]`);
71
+ }
72
+ }
73
+ }
74
+ if (errors.length > 0) {
75
+ const message = `Validation failed for ${operation} on ${tableName}:\n - ${errors.join('\n - ')}`;
76
+ debugLog(config, message);
77
+ if (onError === 'throw') {
78
+ throw new RelqQueryError(message);
79
+ }
80
+ else if (onError === 'warn') {
81
+ console.warn('[Relq]', message);
82
+ }
83
+ else if (onError === 'log') {
84
+ console.log('[Relq]', message);
85
+ }
86
+ }
87
+ }
88
+ export function validateComposite(propName, value, fields, errors, config) {
89
+ const validation = config?.validation;
90
+ const validateLength = validation?.validateLength !== false;
91
+ const validateTypes = validation?.validateTypes !== false;
92
+ for (const [fieldName, colDef] of Object.entries(fields)) {
93
+ const colConfig = colDef;
94
+ const fieldValue = value[fieldName];
95
+ const colType = colConfig.$sqlType ?? colConfig.$config?.$type ?? (typeof colConfig.$type === 'string' ? colConfig.$type : 'TEXT');
96
+ const upperType = colType.toUpperCase();
97
+ const isNullable = colConfig.$config?.$nullable ?? colConfig.$nullable;
98
+ debugLog(config, `Composite field '${propName}.${fieldName}': value=${JSON.stringify(fieldValue)}, colType=${colType}, nullable=${isNullable}`);
99
+ if (fieldValue === null || fieldValue === undefined) {
100
+ if (isNullable === false) {
101
+ errors.push(`${propName}.${fieldName}: cannot be null`);
102
+ }
103
+ continue;
104
+ }
105
+ if (validateTypes && upperType) {
106
+ const jsType = typeof fieldValue;
107
+ if (['INTEGER', 'INT', 'SMALLINT', 'BIGINT', 'SERIAL'].some(t => upperType.includes(t))) {
108
+ if (jsType !== 'number' && jsType !== 'bigint' && jsType !== 'string') {
109
+ errors.push(`${propName}.${fieldName}: expected number, got ${jsType}`);
110
+ }
111
+ }
112
+ else if (['VARCHAR', 'CHAR', 'TEXT'].some(t => upperType.includes(t))) {
113
+ if (jsType !== 'string') {
114
+ errors.push(`${propName}.${fieldName}: expected string, got ${jsType}`);
115
+ }
116
+ }
117
+ }
118
+ if (validateLength && typeof fieldValue === 'string' && typeof colType === 'string') {
119
+ const lengthMatch = colType.match(/(?:var)?char\((\d+)\)/i);
120
+ if (lengthMatch) {
121
+ const maxLength = parseInt(lengthMatch[1], 10);
122
+ if (fieldValue.length > maxLength) {
123
+ errors.push(`${propName}.${fieldName}: length ${fieldValue.length} exceeds ${maxLength}`);
124
+ }
125
+ }
126
+ }
127
+ if (colConfig.$validate && typeof colConfig.$validate === 'function') {
128
+ if (!colConfig.$validate(fieldValue)) {
129
+ errors.push(`${propName}.${fieldName}: failed domain validation`);
130
+ }
131
+ }
132
+ if (colConfig.$fields && fieldValue && typeof fieldValue === 'object') {
133
+ validateComposite(`${propName}.${fieldName}`, fieldValue, colConfig.$fields, errors, config);
134
+ }
135
+ }
136
+ }
@@ -0,0 +1 @@
1
+ export {};