relq 1.0.1 → 1.0.3

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 (194) hide show
  1. package/dist/cjs/addon/buffer/index.cjs +1881 -0
  2. package/dist/cjs/addon/pg/index.cjs +4812 -0
  3. package/dist/cjs/addon/pg-cursor/index.cjs +1451 -0
  4. package/dist/cjs/addon/pg-format/index.cjs +2270 -0
  5. package/dist/cjs/cli/commands/add.cjs +430 -25
  6. package/dist/cjs/cli/commands/branch.cjs +131 -0
  7. package/dist/cjs/cli/commands/checkout.cjs +121 -0
  8. package/dist/cjs/cli/commands/cherry-pick.cjs +282 -0
  9. package/dist/cjs/cli/commands/commit.cjs +21 -29
  10. package/dist/cjs/cli/commands/diff.cjs +144 -69
  11. package/dist/cjs/cli/commands/export.cjs +70 -11
  12. package/dist/cjs/cli/commands/fetch.cjs +42 -18
  13. package/dist/cjs/cli/commands/generate.cjs +28 -54
  14. package/dist/cjs/cli/commands/history.cjs +19 -40
  15. package/dist/cjs/cli/commands/import.cjs +305 -41
  16. package/dist/cjs/cli/commands/init.cjs +69 -59
  17. package/dist/cjs/cli/commands/introspect.cjs +4 -8
  18. package/dist/cjs/cli/commands/log.cjs +84 -15
  19. package/dist/cjs/cli/commands/merge.cjs +207 -0
  20. package/dist/cjs/cli/commands/migrate.cjs +13 -26
  21. package/dist/cjs/cli/commands/pull.cjs +321 -95
  22. package/dist/cjs/cli/commands/push.cjs +228 -52
  23. package/dist/cjs/cli/commands/remote.cjs +17 -0
  24. package/dist/cjs/cli/commands/reset.cjs +148 -0
  25. package/dist/cjs/cli/commands/resolve.cjs +191 -0
  26. package/dist/cjs/cli/commands/rollback.cjs +17 -39
  27. package/dist/cjs/cli/commands/stash.cjs +152 -0
  28. package/dist/cjs/cli/commands/status.cjs +52 -9
  29. package/dist/cjs/cli/commands/sync.cjs +30 -50
  30. package/dist/cjs/cli/commands/tag.cjs +146 -0
  31. package/dist/cjs/cli/index.cjs +117 -10
  32. package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
  33. package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
  34. package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
  35. package/dist/cjs/cli/utils/config-loader.cjs +34 -8
  36. package/dist/cjs/cli/utils/env-loader.cjs +3 -2
  37. package/dist/cjs/cli/utils/fast-introspect.cjs +110 -4
  38. package/dist/cjs/cli/utils/git-utils.cjs +42 -161
  39. package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
  40. package/dist/cjs/cli/utils/project-root.cjs +107 -0
  41. package/dist/cjs/cli/utils/relqignore.cjs +297 -38
  42. package/dist/cjs/cli/utils/repo-manager.cjs +92 -3
  43. package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
  44. package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
  45. package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
  46. package/dist/cjs/cli/utils/schema-introspect.cjs +9 -5
  47. package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
  48. package/dist/cjs/cli/utils/spinner.cjs +14 -106
  49. package/dist/cjs/cli/utils/sql-generator.cjs +2 -2
  50. package/dist/cjs/cli/utils/sql-parser.cjs +94 -7
  51. package/dist/cjs/cli/utils/type-generator.cjs +28 -16
  52. package/dist/cjs/condition/array-condition-builder.cjs +1 -1
  53. package/dist/cjs/condition/condition-collector.cjs +1 -1
  54. package/dist/cjs/condition/fulltext-condition-builder.cjs +1 -1
  55. package/dist/cjs/condition/geometric-condition-builder.cjs +1 -1
  56. package/dist/cjs/condition/jsonb-condition-builder.cjs +1 -1
  57. package/dist/cjs/condition/network-condition-builder.cjs +1 -1
  58. package/dist/cjs/condition/range-condition-builder.cjs +1 -1
  59. package/dist/cjs/copy/copy-builder.cjs +1 -1
  60. package/dist/cjs/core/query-builder.cjs +1 -1
  61. package/dist/cjs/core/relq-client.cjs +2 -2
  62. package/dist/cjs/count/count-builder.cjs +1 -1
  63. package/dist/cjs/cte/cte-builder.cjs +1 -1
  64. package/dist/cjs/delete/delete-builder.cjs +1 -1
  65. package/dist/cjs/function/create-function-builder.cjs +1 -1
  66. package/dist/cjs/functions/advanced-functions.cjs +1 -1
  67. package/dist/cjs/functions/case-builder.cjs +1 -1
  68. package/dist/cjs/functions/geometric-functions.cjs +1 -1
  69. package/dist/cjs/functions/network-functions.cjs +1 -1
  70. package/dist/cjs/functions/sql-functions.cjs +1 -1
  71. package/dist/cjs/indexing/create-index-builder.cjs +1 -1
  72. package/dist/cjs/indexing/drop-index-builder.cjs +1 -1
  73. package/dist/cjs/insert/conflict-builder.cjs +1 -1
  74. package/dist/cjs/insert/insert-builder.cjs +1 -1
  75. package/dist/cjs/maintenance/vacuum-builder.cjs +1 -1
  76. package/dist/cjs/pubsub/listen-notify-builder.cjs +1 -1
  77. package/dist/cjs/pubsub/listener-connection.cjs +2 -2
  78. package/dist/cjs/raw/raw-query-builder.cjs +1 -1
  79. package/dist/cjs/schema/schema-builder.cjs +1 -1
  80. package/dist/cjs/schema-definition/table-definition.cjs +1 -1
  81. package/dist/cjs/select/aggregate-builder.cjs +1 -1
  82. package/dist/cjs/select/select-builder.cjs +1 -1
  83. package/dist/cjs/sequence/sequence-builder.cjs +1 -1
  84. package/dist/cjs/table/alter-table-builder.cjs +1 -1
  85. package/dist/cjs/table/constraint-builder.cjs +1 -1
  86. package/dist/cjs/table/create-table-builder.cjs +1 -1
  87. package/dist/cjs/table/partition-builder.cjs +1 -1
  88. package/dist/cjs/table/truncate-builder.cjs +1 -1
  89. package/dist/cjs/transaction/transaction-builder.cjs +1 -1
  90. package/dist/cjs/trigger/create-trigger-builder.cjs +1 -1
  91. package/dist/cjs/update/array-update-builder.cjs +1 -1
  92. package/dist/cjs/update/update-builder.cjs +1 -1
  93. package/dist/cjs/utils/index.cjs +1 -1
  94. package/dist/cjs/view/create-view-builder.cjs +1 -1
  95. package/dist/cjs/window/window-builder.cjs +1 -1
  96. package/dist/config.d.ts +16 -25
  97. package/dist/esm/cli/commands/add.js +399 -27
  98. package/dist/esm/cli/commands/branch.js +95 -0
  99. package/dist/esm/cli/commands/checkout.js +85 -0
  100. package/dist/esm/cli/commands/cherry-pick.js +246 -0
  101. package/dist/esm/cli/commands/commit.js +22 -30
  102. package/dist/esm/cli/commands/diff.js +144 -69
  103. package/dist/esm/cli/commands/export.js +71 -12
  104. package/dist/esm/cli/commands/fetch.js +42 -18
  105. package/dist/esm/cli/commands/generate.js +28 -54
  106. package/dist/esm/cli/commands/history.js +11 -32
  107. package/dist/esm/cli/commands/import.js +306 -42
  108. package/dist/esm/cli/commands/init.js +65 -55
  109. package/dist/esm/cli/commands/introspect.js +4 -8
  110. package/dist/esm/cli/commands/log.js +78 -10
  111. package/dist/esm/cli/commands/merge.js +171 -0
  112. package/dist/esm/cli/commands/migrate.js +13 -26
  113. package/dist/esm/cli/commands/pull.js +313 -87
  114. package/dist/esm/cli/commands/push.js +223 -47
  115. package/dist/esm/cli/commands/remote.js +14 -0
  116. package/dist/esm/cli/commands/reset.js +112 -0
  117. package/dist/esm/cli/commands/resolve.js +155 -0
  118. package/dist/esm/cli/commands/rollback.js +17 -39
  119. package/dist/esm/cli/commands/stash.js +116 -0
  120. package/dist/esm/cli/commands/status.js +20 -10
  121. package/dist/esm/cli/commands/sync.js +30 -50
  122. package/dist/esm/cli/commands/tag.js +110 -0
  123. package/dist/esm/cli/index.js +118 -11
  124. package/dist/esm/cli/utils/change-tracker.js +107 -3
  125. package/dist/esm/cli/utils/cli-utils.js +169 -0
  126. package/dist/esm/cli/utils/commit-manager.js +3 -3
  127. package/dist/esm/cli/utils/config-loader.js +34 -8
  128. package/dist/esm/cli/utils/env-loader.js +3 -2
  129. package/dist/esm/cli/utils/fast-introspect.js +110 -4
  130. package/dist/esm/cli/utils/git-utils.js +2 -124
  131. package/dist/esm/cli/utils/pool-manager.js +114 -0
  132. package/dist/esm/cli/utils/project-root.js +69 -0
  133. package/dist/esm/cli/utils/relqignore.js +278 -37
  134. package/dist/esm/cli/utils/repo-manager.js +83 -3
  135. package/dist/esm/cli/utils/schema-comparator.js +301 -11
  136. package/dist/esm/cli/utils/schema-diff.js +202 -1
  137. package/dist/esm/cli/utils/schema-hash.js +2 -1
  138. package/dist/esm/cli/utils/schema-introspect.js +9 -5
  139. package/dist/esm/cli/utils/snapshot-manager.js +1 -0
  140. package/dist/esm/cli/utils/spinner.js +1 -101
  141. package/dist/esm/cli/utils/sql-generator.js +2 -2
  142. package/dist/esm/cli/utils/sql-parser.js +94 -7
  143. package/dist/esm/cli/utils/type-generator.js +28 -16
  144. package/dist/esm/condition/array-condition-builder.js +1 -1
  145. package/dist/esm/condition/condition-collector.js +1 -1
  146. package/dist/esm/condition/fulltext-condition-builder.js +1 -1
  147. package/dist/esm/condition/geometric-condition-builder.js +1 -1
  148. package/dist/esm/condition/jsonb-condition-builder.js +1 -1
  149. package/dist/esm/condition/network-condition-builder.js +1 -1
  150. package/dist/esm/condition/range-condition-builder.js +1 -1
  151. package/dist/esm/copy/copy-builder.js +1 -1
  152. package/dist/esm/core/query-builder.js +1 -1
  153. package/dist/esm/core/relq-client.js +2 -2
  154. package/dist/esm/count/count-builder.js +1 -1
  155. package/dist/esm/cte/cte-builder.js +1 -1
  156. package/dist/esm/delete/delete-builder.js +1 -1
  157. package/dist/esm/function/create-function-builder.js +1 -1
  158. package/dist/esm/functions/advanced-functions.js +1 -1
  159. package/dist/esm/functions/case-builder.js +1 -1
  160. package/dist/esm/functions/geometric-functions.js +1 -1
  161. package/dist/esm/functions/network-functions.js +1 -1
  162. package/dist/esm/functions/sql-functions.js +1 -1
  163. package/dist/esm/indexing/create-index-builder.js +1 -1
  164. package/dist/esm/indexing/drop-index-builder.js +1 -1
  165. package/dist/esm/insert/conflict-builder.js +1 -1
  166. package/dist/esm/insert/insert-builder.js +1 -1
  167. package/dist/esm/maintenance/vacuum-builder.js +1 -1
  168. package/dist/esm/pubsub/listen-notify-builder.js +1 -1
  169. package/dist/esm/pubsub/listener-connection.js +2 -2
  170. package/dist/esm/raw/raw-query-builder.js +1 -1
  171. package/dist/esm/schema/schema-builder.js +1 -1
  172. package/dist/esm/schema-definition/table-definition.js +1 -1
  173. package/dist/esm/select/aggregate-builder.js +1 -1
  174. package/dist/esm/select/select-builder.js +1 -1
  175. package/dist/esm/sequence/sequence-builder.js +1 -1
  176. package/dist/esm/table/alter-table-builder.js +1 -1
  177. package/dist/esm/table/constraint-builder.js +1 -1
  178. package/dist/esm/table/create-table-builder.js +1 -1
  179. package/dist/esm/table/partition-builder.js +1 -1
  180. package/dist/esm/table/truncate-builder.js +1 -1
  181. package/dist/esm/transaction/transaction-builder.js +1 -1
  182. package/dist/esm/trigger/create-trigger-builder.js +1 -1
  183. package/dist/esm/update/array-update-builder.js +1 -1
  184. package/dist/esm/update/update-builder.js +1 -1
  185. package/dist/esm/utils/index.js +1 -1
  186. package/dist/esm/view/create-view-builder.js +1 -1
  187. package/dist/esm/window/window-builder.js +1 -1
  188. package/dist/index.d.ts +25 -8
  189. package/dist/schema-builder.d.ts +16 -6
  190. package/package.json +1 -1
  191. /package/dist/{addons/buffer.js → esm/addon/buffer/index.js} +0 -0
  192. /package/dist/{addons/pg.js → esm/addon/pg/index.js} +0 -0
  193. /package/dist/{addons/pg-cursor.js → esm/addon/pg-cursor/index.js} +0 -0
  194. /package/dist/{addons/pg-format.js → esm/addon/pg-format/index.js} +0 -0
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { initCommand } from "./commands/init.js";
3
2
  import { pullCommand } from "./commands/pull.js";
4
3
  import { pushCommand } from "./commands/push.js";
@@ -7,13 +6,49 @@ import { introspectCommand } from "./commands/introspect.js";
7
6
  import { syncCommand } from "./commands/sync.js";
8
7
  import { statusCommand } from "./commands/status.js";
9
8
  import { diffCommand } from "./commands/diff.js";
10
- import { logCommand } from "./commands/log.js";
9
+ import { logCommand, showCommand } from "./commands/log.js";
11
10
  import { rollbackCommand } from "./commands/rollback.js";
12
11
  import { commitCommand } from "./commands/commit.js";
13
12
  import { fetchCommand } from "./commands/fetch.js";
14
13
  import { addCommand } from "./commands/add.js";
15
14
  import { importCommand } from "./commands/import.js";
16
15
  import { exportCommand } from "./commands/export.js";
16
+ import { resolveCommand } from "./commands/resolve.js";
17
+ import { resetCommand } from "./commands/reset.js";
18
+ import { stashCommand } from "./commands/stash.js";
19
+ import { branchCommand } from "./commands/branch.js";
20
+ import { mergeCommand } from "./commands/merge.js";
21
+ import { tagCommand } from "./commands/tag.js";
22
+ import { cherryPickCommand } from "./commands/cherry-pick.js";
23
+ import { remoteCommand } from "./commands/remote.js";
24
+ import * as fs from 'fs';
25
+ import * as path from 'path';
26
+ function loadEnvFile() {
27
+ let currentDir = process.cwd();
28
+ const root = path.parse(currentDir).root;
29
+ while (currentDir !== root) {
30
+ const envPath = path.join(currentDir, '.env');
31
+ if (fs.existsSync(envPath)) {
32
+ const envContent = fs.readFileSync(envPath, 'utf-8');
33
+ for (const line of envContent.split('\n')) {
34
+ const trimmed = line.trim();
35
+ if (!trimmed || trimmed.startsWith('#'))
36
+ continue;
37
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
38
+ if (match) {
39
+ const key = match[1].trim();
40
+ const value = match[2].trim().replace(/^["']|["']$/g, '');
41
+ if (!process.env[key]) {
42
+ process.env[key] = value;
43
+ }
44
+ }
45
+ }
46
+ return;
47
+ }
48
+ currentDir = path.dirname(currentDir);
49
+ }
50
+ }
51
+ loadEnvFile();
17
52
  const VERSION = '1.1.0';
18
53
  function parseArgs(argv) {
19
54
  const args = [];
@@ -141,20 +176,56 @@ async function main() {
141
176
  process.exit(1);
142
177
  }
143
178
  let config = null;
179
+ let resolvedProjectRoot = process.cwd();
144
180
  if (requiresConfig(command)) {
145
181
  const configPath = flags.config;
146
182
  try {
147
- const { loadConfigWithEnv, findConfigFile } = await import("./utils/config-loader.js");
148
- const foundConfig = configPath || findConfigFile();
183
+ const { loadConfigWithEnv, findConfigFileRecursive } = await import("./utils/config-loader.js");
184
+ const { findProjectRoot } = await import("./utils/project-root.js");
185
+ const configResult = configPath ? { path: configPath, directory: path.dirname(path.resolve(configPath)) } : findConfigFileRecursive();
186
+ if (!configResult) {
187
+ const projectRoot = findProjectRoot();
188
+ if (!projectRoot) {
189
+ const colors = {
190
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
191
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
192
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
193
+ };
194
+ console.error('');
195
+ console.error(colors.red('fatal:') + ' not a relq project (or any of the parent directories)');
196
+ console.error('');
197
+ console.error(colors.yellow('hint:') + ` Run ${colors.cyan('relq init')} in your project directory to initialize relq.`);
198
+ console.error('');
199
+ process.exit(128);
200
+ }
201
+ }
202
+ resolvedProjectRoot = configResult?.directory || findProjectRoot() || process.cwd();
203
+ const foundConfig = configResult?.path;
149
204
  if (!foundConfig) {
150
- console.error('Error: relq.config.ts not found.');
151
- console.error('Run "relq init" to create one or use --config to specify a path.');
205
+ const colors = {
206
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
207
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
208
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
209
+ };
210
+ console.error('');
211
+ console.error(colors.red('error:') + ' relq.config.ts not found');
212
+ console.error('');
213
+ console.error(colors.yellow('hint:') + ` Run ${colors.cyan('relq init')} to create one or use ${colors.cyan('--config')} to specify a path.`);
214
+ console.error('');
152
215
  process.exit(1);
153
216
  }
154
217
  config = await loadConfigWithEnv(configPath);
155
218
  if (requiresDbConnection(command, flags) && !config.connection?.host && !config.connection?.url) {
156
- console.error('Error: No database connection configured.');
157
- console.error('Run "relq init" or set DATABASE_* environment variables.');
219
+ const colors = {
220
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
221
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
222
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
223
+ };
224
+ console.error('');
225
+ console.error(colors.red('error:') + ' No database connection configured');
226
+ console.error('');
227
+ console.error(colors.yellow('hint:') + ` Run ${colors.cyan('relq init')} or set DATABASE_* environment variables.`);
228
+ console.error('');
158
229
  process.exit(1);
159
230
  }
160
231
  }
@@ -163,7 +234,7 @@ async function main() {
163
234
  process.exit(1);
164
235
  }
165
236
  }
166
- const context = { config, args, flags };
237
+ const context = { config, args, flags, projectRoot: resolvedProjectRoot };
167
238
  try {
168
239
  switch (command) {
169
240
  case 'init':
@@ -194,6 +265,9 @@ async function main() {
194
265
  case 'log':
195
266
  await logCommand(context);
196
267
  break;
268
+ case 'show':
269
+ await showCommand(context);
270
+ break;
197
271
  case 'fetch':
198
272
  await fetchCommand(context);
199
273
  break;
@@ -201,6 +275,39 @@ async function main() {
201
275
  case 'revert':
202
276
  await rollbackCommand(context);
203
277
  break;
278
+ case 'resolve':
279
+ await resolveCommand(context);
280
+ break;
281
+ case 'reset':
282
+ await resetCommand(context);
283
+ break;
284
+ case 'stash':
285
+ await stashCommand(context);
286
+ break;
287
+ case 'branch':
288
+ await branchCommand(context);
289
+ break;
290
+ case 'checkout':
291
+ case 'switch':
292
+ console.log('');
293
+ console.log('⚠️ Branch switching is currently disabled.');
294
+ console.log('');
295
+ console.log('This feature will be available when Relq Postgres Native is officially launched.');
296
+ console.log('For now, work on a single branch and use merge to combine schemas.');
297
+ console.log('');
298
+ break;
299
+ case 'merge':
300
+ await mergeCommand(context);
301
+ break;
302
+ case 'tag':
303
+ await tagCommand(context);
304
+ break;
305
+ case 'cherry-pick':
306
+ await cherryPickCommand(context);
307
+ break;
308
+ case 'remote':
309
+ await remoteCommand(context);
310
+ break;
204
311
  case 'introspect':
205
312
  await introspectCommand(context);
206
313
  break;
@@ -217,7 +324,7 @@ async function main() {
217
324
  theirs: Boolean(flags.theirs),
218
325
  ours: Boolean(flags.ours),
219
326
  abort: Boolean(flags.abort),
220
- });
327
+ }, resolvedProjectRoot);
221
328
  break;
222
329
  case 'export':
223
330
  await exportCommand(context);
@@ -237,4 +344,4 @@ async function main() {
237
344
  process.exit(1);
238
345
  }
239
346
  }
240
- main().catch(console.error);
347
+ export { main };
@@ -31,6 +31,12 @@ export function generateChangeSQL(change) {
31
31
  return generateConstraintSQL(change);
32
32
  case 'PARTITION':
33
33
  return generatePartitionSQL(change);
34
+ case 'PARTITION_CHILD':
35
+ return generatePartitionChildSQL(change);
36
+ case 'TABLE_COMMENT':
37
+ return generateTableCommentSQL(change);
38
+ case 'COLUMN_COMMENT':
39
+ return generateColumnCommentSQL(change);
34
40
  case 'VIEW':
35
41
  return generateViewSQL(change);
36
42
  case 'MATERIALIZED_VIEW':
@@ -44,6 +50,8 @@ export function generateChangeSQL(change) {
44
50
  return generateForeignServerSQL(change);
45
51
  case 'FOREIGN_TABLE':
46
52
  return generateForeignTableSQL(change);
53
+ case 'COLLATION':
54
+ return generateCollationSQL(change);
47
55
  default:
48
56
  return `-- Unsupported object type: ${change.objectType}`;
49
57
  }
@@ -144,8 +152,11 @@ function generateTableSQL(change) {
144
152
  }
145
153
  const allDefs = [...colDefs, ...constraintDefs].join(',\n');
146
154
  let sql = `CREATE TABLE "${data.name}" (\n${allDefs}\n)`;
147
- if (data.isPartitioned && data.partitionType && data.partitionKey?.length) {
148
- sql += ` PARTITION BY ${data.partitionType} (${data.partitionKey.join(', ')})`;
155
+ if (data.isPartitioned && data.partitionType && data.partitionKey) {
156
+ const keyArr = Array.isArray(data.partitionKey) ? data.partitionKey : [data.partitionKey];
157
+ if (keyArr.length) {
158
+ sql += ` PARTITION BY ${data.partitionType} (${keyArr.join(', ')})`;
159
+ }
149
160
  }
150
161
  return sql + ';';
151
162
  }
@@ -226,6 +237,9 @@ function generateConstraintSQL(change) {
226
237
  return '';
227
238
  }
228
239
  function generatePartitionSQL(change) {
240
+ return '';
241
+ }
242
+ function generatePartitionChildSQL(change) {
229
243
  const data = change.after;
230
244
  if (change.type === 'CREATE' && data) {
231
245
  return `CREATE TABLE "${data.name}" PARTITION OF "${data.parentTable}" ${data.bound};`;
@@ -235,6 +249,33 @@ function generatePartitionSQL(change) {
235
249
  }
236
250
  return '';
237
251
  }
252
+ function generateTableCommentSQL(change) {
253
+ const data = change.after;
254
+ if ((change.type === 'CREATE' || change.type === 'ALTER') && data) {
255
+ const escaped = data.comment.replace(/'/g, "''");
256
+ return `COMMENT ON TABLE "${data.tableName}" IS '${escaped}';`;
257
+ }
258
+ else if (change.type === 'DROP') {
259
+ const beforeData = change.before;
260
+ const tableName = beforeData?.tableName || change.objectName;
261
+ return `COMMENT ON TABLE "${tableName}" IS NULL;`;
262
+ }
263
+ return '';
264
+ }
265
+ function generateColumnCommentSQL(change) {
266
+ const data = change.after;
267
+ if ((change.type === 'CREATE' || change.type === 'ALTER') && data) {
268
+ const escaped = data.comment.replace(/'/g, "''");
269
+ return `COMMENT ON COLUMN "${data.tableName}"."${data.columnName}" IS '${escaped}';`;
270
+ }
271
+ else if (change.type === 'DROP') {
272
+ const beforeData = change.before;
273
+ const tableName = beforeData?.tableName || change.parentName || '';
274
+ const columnName = beforeData?.columnName || change.objectName;
275
+ return `COMMENT ON COLUMN "${tableName}"."${columnName}" IS NULL;`;
276
+ }
277
+ return '';
278
+ }
238
279
  function generateViewSQL(change) {
239
280
  const data = change.after;
240
281
  if (change.type === 'CREATE' && data) {
@@ -320,6 +361,28 @@ function generateForeignTableSQL(change) {
320
361
  }
321
362
  return '';
322
363
  }
364
+ function generateCollationSQL(change) {
365
+ const data = change.after;
366
+ if (change.type === 'CREATE' && data) {
367
+ const options = [];
368
+ if (data.locale)
369
+ options.push(`LOCALE = '${data.locale}'`);
370
+ if (data.lcCollate)
371
+ options.push(`LC_COLLATE = '${data.lcCollate}'`);
372
+ if (data.lcCtype)
373
+ options.push(`LC_CTYPE = '${data.lcCtype}'`);
374
+ if (data.provider)
375
+ options.push(`PROVIDER = ${data.provider}`);
376
+ if (data.deterministic !== undefined) {
377
+ options.push(`DETERMINISTIC = ${data.deterministic ? 'TRUE' : 'FALSE'}`);
378
+ }
379
+ return `CREATE COLLATION IF NOT EXISTS "${data.name}" (${options.join(', ')});`;
380
+ }
381
+ else if (change.type === 'DROP') {
382
+ return `DROP COLLATION IF EXISTS "${change.objectName}";`;
383
+ }
384
+ return '';
385
+ }
323
386
  export function createChange(type, objectType, objectName, before, after, parentName) {
324
387
  const id = generateChangeId(type, objectType, objectName, parentName);
325
388
  const change = {
@@ -341,6 +404,42 @@ export function getChangeDisplayName(change) {
341
404
  change.type === 'DROP' ? '-' :
342
405
  change.type === 'ALTER' ? '~' :
343
406
  '>';
407
+ if (change.objectType === 'INDEX') {
408
+ const data = change.after;
409
+ const tableName = data?.tableName || change.parentName;
410
+ if (tableName) {
411
+ return `${prefix} ${change.objectType} ${change.objectName} on ${tableName}`;
412
+ }
413
+ }
414
+ if (change.objectType === 'PARTITION') {
415
+ const data = change.after;
416
+ if (data?.tableName && data?.type && data?.key) {
417
+ let keyStr = Array.isArray(data.key) ? data.key.join(', ') : data.key;
418
+ keyStr = keyStr.replace(/[{}]/g, '');
419
+ return `${prefix} PARTITIONED ${data.tableName} by ${data.type.toUpperCase()}(${keyStr})`;
420
+ }
421
+ }
422
+ if (change.objectType === 'PARTITION_CHILD') {
423
+ const data = change.after;
424
+ if (data?.name && data?.parentTable) {
425
+ const bound = data.bound || '';
426
+ return `${prefix} PARTITION ${data.name} of ${data.parentTable} ${bound}`;
427
+ }
428
+ }
429
+ if (change.objectType === 'CHECK') {
430
+ const actionWord = change.type === 'CREATE' ? 'ADD' : change.type === 'DROP' ? 'DROP' : 'ALTER';
431
+ if (change.parentName) {
432
+ return `${prefix} ${actionWord} CHECK ${change.objectName} on ${change.parentName}`;
433
+ }
434
+ return `${prefix} ${actionWord} CHECK ${change.objectName}`;
435
+ }
436
+ if (change.objectType === 'PRIMARY_KEY' || change.objectType === 'FOREIGN_KEY' || change.objectType === 'EXCLUSION') {
437
+ const actionWord = change.type === 'CREATE' ? 'ADD' : change.type === 'DROP' ? 'DROP' : 'ALTER';
438
+ if (change.parentName) {
439
+ return `${prefix} ${actionWord} ${change.objectType.replace('_', ' ')} ${change.objectName} on ${change.parentName}`;
440
+ }
441
+ return `${prefix} ${actionWord} ${change.objectType.replace('_', ' ')} ${change.objectName}`;
442
+ }
344
443
  if (change.parentName) {
345
444
  return `${prefix} ${change.objectType} ${change.parentName}.${change.objectName}`;
346
445
  }
@@ -350,14 +449,18 @@ export function sortChangesByDependency(changes) {
350
449
  const order = {
351
450
  'EXTENSION': 1,
352
451
  'SCHEMA': 2,
452
+ 'SCHEMA_FILE': 0,
353
453
  'ENUM': 3,
354
454
  'DOMAIN': 4,
355
455
  'COMPOSITE_TYPE': 5,
356
456
  'SEQUENCE': 6,
357
457
  'FOREIGN_SERVER': 7,
358
458
  'TABLE': 10,
459
+ 'TABLE_COMMENT': 10,
359
460
  'PARTITION': 11,
360
- 'COLUMN': 12,
461
+ 'PARTITION_CHILD': 12,
462
+ 'COLUMN': 13,
463
+ 'COLUMN_COMMENT': 12,
361
464
  'INDEX': 13,
362
465
  'CONSTRAINT': 14,
363
466
  'PRIMARY_KEY': 14,
@@ -370,6 +473,7 @@ export function sortChangesByDependency(changes) {
370
473
  'PROCEDURE': 31,
371
474
  'TRIGGER': 40,
372
475
  'ENUM_VALUE': 3,
476
+ 'COLLATION': 4,
373
477
  'FOREIGN_TABLE': 8,
374
478
  };
375
479
  return [...changes].sort((a, b) => {
@@ -0,0 +1,169 @@
1
+ import * as readline from 'readline';
2
+ const isColorSupported = process.stdout.isTTY && !process.env.NO_COLOR;
3
+ export const colors = {
4
+ reset: '\x1b[0m',
5
+ red: (s) => isColorSupported ? `\x1b[31m${s}\x1b[0m` : s,
6
+ green: (s) => isColorSupported ? `\x1b[32m${s}\x1b[0m` : s,
7
+ yellow: (s) => isColorSupported ? `\x1b[33m${s}\x1b[0m` : s,
8
+ blue: (s) => isColorSupported ? `\x1b[34m${s}\x1b[0m` : s,
9
+ magenta: (s) => isColorSupported ? `\x1b[35m${s}\x1b[0m` : s,
10
+ cyan: (s) => isColorSupported ? `\x1b[36m${s}\x1b[0m` : s,
11
+ white: (s) => isColorSupported ? `\x1b[37m${s}\x1b[0m` : s,
12
+ gray: (s) => isColorSupported ? `\x1b[90m${s}\x1b[0m` : s,
13
+ bold: (s) => isColorSupported ? `\x1b[1m${s}\x1b[0m` : s,
14
+ dim: (s) => isColorSupported ? `\x1b[2m${s}\x1b[0m` : s,
15
+ muted: (s) => isColorSupported ? `\x1b[90m${s}\x1b[0m` : s,
16
+ success: (s) => isColorSupported ? `\x1b[32m${s}\x1b[0m` : s,
17
+ error: (s) => isColorSupported ? `\x1b[31m${s}\x1b[0m` : s,
18
+ warning: (s) => isColorSupported ? `\x1b[33m${s}\x1b[0m` : s,
19
+ info: (s) => isColorSupported ? `\x1b[34m${s}\x1b[0m` : s,
20
+ };
21
+ export function createSpinner() {
22
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
23
+ let frameIndex = 0;
24
+ let interval = null;
25
+ let currentMessage = '';
26
+ let isSpinning = false;
27
+ const isTTY = process.stdout.isTTY;
28
+ const clearLine = () => {
29
+ if (isTTY)
30
+ process.stdout.write('\r\x1b[K');
31
+ };
32
+ const render = () => {
33
+ if (!isSpinning)
34
+ return;
35
+ clearLine();
36
+ process.stdout.write(`${colors.cyan(frames[frameIndex])} ${currentMessage}`);
37
+ frameIndex = (frameIndex + 1) % frames.length;
38
+ };
39
+ return {
40
+ start(message) {
41
+ if (isSpinning)
42
+ this.stop();
43
+ currentMessage = message;
44
+ isSpinning = true;
45
+ frameIndex = 0;
46
+ if (isTTY) {
47
+ interval = setInterval(render, 80);
48
+ render();
49
+ }
50
+ else {
51
+ console.log(`${message}...`);
52
+ }
53
+ },
54
+ update(message) {
55
+ currentMessage = message;
56
+ },
57
+ succeed(message) {
58
+ this.stop();
59
+ console.log(message || currentMessage);
60
+ },
61
+ fail(message) {
62
+ this.stop();
63
+ },
64
+ info(message) {
65
+ this.stop();
66
+ console.log(message || currentMessage);
67
+ },
68
+ warn(message) {
69
+ this.stop();
70
+ warning(message || currentMessage);
71
+ },
72
+ stop() {
73
+ if (interval) {
74
+ clearInterval(interval);
75
+ interval = null;
76
+ }
77
+ if (isSpinning && isTTY) {
78
+ clearLine();
79
+ }
80
+ isSpinning = false;
81
+ }
82
+ };
83
+ }
84
+ export function fatal(message, hintMessage) {
85
+ console.error(`${colors.red('fatal:')} ${message}`);
86
+ if (hintMessage) {
87
+ hint(hintMessage);
88
+ }
89
+ process.exit(1);
90
+ }
91
+ export function error(message, hintMessage) {
92
+ console.error(`${colors.red('error:')} ${message}`);
93
+ if (hintMessage) {
94
+ hint(hintMessage);
95
+ }
96
+ }
97
+ export function warning(message) {
98
+ console.error(`${colors.yellow('warning:')} ${message}`);
99
+ }
100
+ export function hint(message) {
101
+ console.error(`${colors.yellow('hint:')} ${message}`);
102
+ }
103
+ export function success(message) {
104
+ console.log(colors.green(message));
105
+ }
106
+ export function confirm(question, defaultYes = true) {
107
+ const rl = readline.createInterface({
108
+ input: process.stdin,
109
+ output: process.stdout,
110
+ });
111
+ const suffix = defaultYes ? '[Y/n]' : '[y/N]';
112
+ return new Promise((resolve) => {
113
+ rl.question(`${question} ${suffix} `, (answer) => {
114
+ rl.close();
115
+ const a = answer.trim().toLowerCase();
116
+ if (!a)
117
+ resolve(defaultYes);
118
+ else
119
+ resolve(a === 'y' || a === 'yes');
120
+ });
121
+ });
122
+ }
123
+ export function select(question, options) {
124
+ const rl = readline.createInterface({
125
+ input: process.stdin,
126
+ output: process.stdout,
127
+ });
128
+ console.log(question);
129
+ options.forEach((opt, i) => {
130
+ console.log(` ${i + 1}) ${opt}`);
131
+ });
132
+ return new Promise((resolve) => {
133
+ rl.question(`Select [1-${options.length}]: `, (answer) => {
134
+ rl.close();
135
+ const num = parseInt(answer.trim(), 10);
136
+ if (num >= 1 && num <= options.length) {
137
+ resolve(num - 1);
138
+ }
139
+ else {
140
+ resolve(0);
141
+ }
142
+ });
143
+ });
144
+ }
145
+ export function formatBytes(bytes) {
146
+ if (bytes < 1024)
147
+ return `${bytes} B`;
148
+ if (bytes < 1024 * 1024)
149
+ return `${(bytes / 1024).toFixed(1)} KB`;
150
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
151
+ }
152
+ export function formatDuration(ms) {
153
+ if (ms < 1000)
154
+ return `${ms}ms`;
155
+ if (ms < 60000)
156
+ return `${(ms / 1000).toFixed(1)}s`;
157
+ return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
158
+ }
159
+ export function progressBar(current, total, width = 30) {
160
+ const percentage = Math.min(100, Math.round((current / total) * 100));
161
+ const filled = Math.round((percentage / 100) * width);
162
+ const empty = width - filled;
163
+ return `${'#'.repeat(filled)}${'.'.repeat(empty)} ${percentage}%`;
164
+ }
165
+ export function requireInit(isInitialized, projectRoot) {
166
+ if (!isInitialized) {
167
+ fatal('not a relq repository (or any of the parent directories): .relq', "run 'relq init' to initialize a repository");
168
+ }
169
+ }
@@ -69,7 +69,7 @@ CREATE INDEX IF NOT EXISTS idx_relq_commits_created
69
69
  ON _relq_commits(created_at DESC);
70
70
  `;
71
71
  export async function ensureCommitsTable(connection) {
72
- const { Pool } = await import("../../addon/pg.js");
72
+ const { Pool } = await import("../../addon/pg/index.js");
73
73
  const pool = new Pool({
74
74
  host: connection.host,
75
75
  port: connection.port || 5432,
@@ -87,7 +87,7 @@ export async function ensureCommitsTable(connection) {
87
87
  }
88
88
  }
89
89
  export async function getRemoteCommits(connection, limit = 100) {
90
- const { Pool } = await import("../../addon/pg.js");
90
+ const { Pool } = await import("../../addon/pg/index.js");
91
91
  const pool = new Pool({
92
92
  host: connection.host,
93
93
  port: connection.port || 5432,
@@ -124,7 +124,7 @@ export async function getLatestRemoteCommit(connection) {
124
124
  return commits.length > 0 ? commits[0] : null;
125
125
  }
126
126
  export async function addRemoteCommit(connection, commit, limit = 1000) {
127
- const { Pool } = await import("../../addon/pg.js");
127
+ const { Pool } = await import("../../addon/pg/index.js");
128
128
  const pool = new Pool({
129
129
  host: connection.host,
130
130
  port: connection.port || 5432,
@@ -64,7 +64,7 @@ export function validateConfig(config) {
64
64
  if (!config.connection?.host && !config.connection?.url) {
65
65
  errors.push('No database connection configured. Set connection in relq.config.ts or use DATABASE_* env vars.');
66
66
  }
67
- const hasSchemaPath = typeof config.schema === 'string';
67
+ const hasSchemaPath = typeof config.schema === 'string' && config.schema.length > 0;
68
68
  const hasSchemaDir = typeof config.schema === 'object' && config.schema?.directory;
69
69
  const hasTypeGenOutput = config.typeGeneration?.output;
70
70
  const hasGenerateOutDir = config.generate?.outDir;
@@ -73,14 +73,40 @@ export function validateConfig(config) {
73
73
  }
74
74
  return errors;
75
75
  }
76
- export function requireValidConfig(config) {
76
+ export async function requireValidConfig(config, options) {
77
77
  const errors = validateConfig(config);
78
- if (errors.length > 0) {
79
- console.error('Configuration errors:');
80
- for (const error of errors) {
81
- console.error(` • ${error}`);
78
+ if (errors.length === 0)
79
+ return;
80
+ const colors = {
81
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
82
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
83
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
84
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
85
+ };
86
+ const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
87
+ if (options?.autoComplete !== false && isInteractive) {
88
+ try {
89
+ const { initCommand } = await import("../commands/init.js");
90
+ const { findProjectRoot } = await import("./project-root.js");
91
+ const projectRoot = findProjectRoot() || process.cwd();
92
+ const flags = {};
93
+ if (options?.calledFrom) {
94
+ flags['called-from'] = options.calledFrom;
95
+ }
96
+ await initCommand({ args: [], flags, config, projectRoot });
97
+ return;
82
98
  }
83
- console.error('\nRun "relq init" to create a configuration file.');
84
- process.exit(1);
99
+ catch (e) {
100
+ process.exit(1);
101
+ }
102
+ }
103
+ console.error('');
104
+ console.error(colors.red('error:') + ' Configuration errors:');
105
+ for (const error of errors) {
106
+ console.error(` ${colors.yellow('•')} ${error}`);
85
107
  }
108
+ console.error('');
109
+ console.error(colors.yellow('hint:') + ` Run ${colors.cyan('relq init')} to create a configuration file or check your config settings.`);
110
+ console.error('');
111
+ process.exit(1);
86
112
  }
@@ -1,5 +1,5 @@
1
1
  export function loadEnvConfig() {
2
- const connectionString = process.env.DATABASE_CONNECTION_STRING;
2
+ const connectionString = process.env.RELQ_PG_CONN_URL || process.env.DATABASE_CONNECTION_STRING;
3
3
  if (connectionString) {
4
4
  return parseConnectionString(connectionString);
5
5
  }
@@ -37,7 +37,8 @@ function parseConnectionString(url) {
37
37
  }
38
38
  }
39
39
  export function hasEnvConfig() {
40
- return !!(process.env.DATABASE_CONNECTION_STRING ||
40
+ return !!(process.env.RELQ_PG_CONN_URL ||
41
+ process.env.DATABASE_CONNECTION_STRING ||
41
42
  process.env.DATABASE_HOST);
42
43
  }
43
44
  export function getConnectionDescription(config) {