relq 1.0.2 → 1.0.4

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 (92) hide show
  1. package/dist/cjs/cli/commands/add.cjs +403 -27
  2. package/dist/cjs/cli/commands/branch.cjs +13 -23
  3. package/dist/cjs/cli/commands/checkout.cjs +16 -29
  4. package/dist/cjs/cli/commands/cherry-pick.cjs +3 -4
  5. package/dist/cjs/cli/commands/commit.cjs +21 -29
  6. package/dist/cjs/cli/commands/diff.cjs +28 -32
  7. package/dist/cjs/cli/commands/export.cjs +7 -7
  8. package/dist/cjs/cli/commands/fetch.cjs +15 -21
  9. package/dist/cjs/cli/commands/generate.cjs +28 -54
  10. package/dist/cjs/cli/commands/history.cjs +19 -40
  11. package/dist/cjs/cli/commands/import.cjs +34 -41
  12. package/dist/cjs/cli/commands/init.cjs +69 -59
  13. package/dist/cjs/cli/commands/introspect.cjs +4 -8
  14. package/dist/cjs/cli/commands/log.cjs +26 -32
  15. package/dist/cjs/cli/commands/merge.cjs +24 -41
  16. package/dist/cjs/cli/commands/migrate.cjs +12 -25
  17. package/dist/cjs/cli/commands/pull.cjs +216 -106
  18. package/dist/cjs/cli/commands/push.cjs +35 -75
  19. package/dist/cjs/cli/commands/remote.cjs +2 -1
  20. package/dist/cjs/cli/commands/reset.cjs +22 -43
  21. package/dist/cjs/cli/commands/resolve.cjs +12 -14
  22. package/dist/cjs/cli/commands/rollback.cjs +16 -38
  23. package/dist/cjs/cli/commands/stash.cjs +5 -7
  24. package/dist/cjs/cli/commands/status.cjs +5 -10
  25. package/dist/cjs/cli/commands/sync.cjs +30 -50
  26. package/dist/cjs/cli/commands/tag.cjs +3 -4
  27. package/dist/cjs/cli/index.cjs +72 -9
  28. package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
  29. package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
  30. package/dist/cjs/cli/utils/config-loader.cjs +34 -8
  31. package/dist/cjs/cli/utils/fast-introspect.cjs +109 -3
  32. package/dist/cjs/cli/utils/git-utils.cjs +42 -161
  33. package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
  34. package/dist/cjs/cli/utils/project-root.cjs +56 -5
  35. package/dist/cjs/cli/utils/relqignore.cjs +1 -0
  36. package/dist/cjs/cli/utils/repo-manager.cjs +47 -0
  37. package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
  38. package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
  39. package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
  40. package/dist/cjs/cli/utils/schema-introspect.cjs +7 -3
  41. package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
  42. package/dist/cjs/cli/utils/spinner.cjs +14 -106
  43. package/dist/cjs/cli/utils/sql-generator.cjs +10 -2
  44. package/dist/cjs/cli/utils/type-generator.cjs +28 -16
  45. package/dist/config.d.ts +16 -6
  46. package/dist/esm/cli/commands/add.js +372 -29
  47. package/dist/esm/cli/commands/branch.js +14 -24
  48. package/dist/esm/cli/commands/checkout.js +16 -29
  49. package/dist/esm/cli/commands/cherry-pick.js +3 -4
  50. package/dist/esm/cli/commands/commit.js +22 -30
  51. package/dist/esm/cli/commands/diff.js +6 -10
  52. package/dist/esm/cli/commands/export.js +8 -8
  53. package/dist/esm/cli/commands/fetch.js +14 -20
  54. package/dist/esm/cli/commands/generate.js +28 -54
  55. package/dist/esm/cli/commands/history.js +11 -32
  56. package/dist/esm/cli/commands/import.js +35 -42
  57. package/dist/esm/cli/commands/init.js +65 -55
  58. package/dist/esm/cli/commands/introspect.js +4 -8
  59. package/dist/esm/cli/commands/log.js +6 -12
  60. package/dist/esm/cli/commands/merge.js +20 -37
  61. package/dist/esm/cli/commands/migrate.js +12 -25
  62. package/dist/esm/cli/commands/pull.js +204 -94
  63. package/dist/esm/cli/commands/push.js +21 -61
  64. package/dist/esm/cli/commands/remote.js +2 -1
  65. package/dist/esm/cli/commands/reset.js +16 -37
  66. package/dist/esm/cli/commands/resolve.js +13 -15
  67. package/dist/esm/cli/commands/rollback.js +16 -38
  68. package/dist/esm/cli/commands/stash.js +6 -8
  69. package/dist/esm/cli/commands/status.js +6 -11
  70. package/dist/esm/cli/commands/sync.js +30 -50
  71. package/dist/esm/cli/commands/tag.js +3 -4
  72. package/dist/esm/cli/index.js +72 -9
  73. package/dist/esm/cli/utils/change-tracker.js +107 -3
  74. package/dist/esm/cli/utils/cli-utils.js +169 -0
  75. package/dist/esm/cli/utils/config-loader.js +34 -8
  76. package/dist/esm/cli/utils/fast-introspect.js +109 -3
  77. package/dist/esm/cli/utils/git-utils.js +2 -124
  78. package/dist/esm/cli/utils/pool-manager.js +114 -0
  79. package/dist/esm/cli/utils/project-root.js +55 -5
  80. package/dist/esm/cli/utils/relqignore.js +1 -0
  81. package/dist/esm/cli/utils/repo-manager.js +42 -0
  82. package/dist/esm/cli/utils/schema-comparator.js +301 -11
  83. package/dist/esm/cli/utils/schema-diff.js +202 -1
  84. package/dist/esm/cli/utils/schema-hash.js +2 -1
  85. package/dist/esm/cli/utils/schema-introspect.js +7 -3
  86. package/dist/esm/cli/utils/snapshot-manager.js +1 -0
  87. package/dist/esm/cli/utils/spinner.js +1 -101
  88. package/dist/esm/cli/utils/sql-generator.js +10 -2
  89. package/dist/esm/cli/utils/type-generator.js +28 -16
  90. package/dist/index.d.ts +25 -8
  91. package/dist/schema-builder.d.ts +18 -7
  92. package/package.json +1 -1
@@ -39,8 +39,7 @@ const path = __importStar(require("path"));
39
39
  const spinner_1 = require("../utils/spinner.cjs");
40
40
  const repo_manager_1 = require("../utils/repo-manager.cjs");
41
41
  async function cherryPickCommand(context) {
42
- const { config, args, flags } = context;
43
- const projectRoot = process.cwd();
42
+ const { config, args, flags, projectRoot } = context;
44
43
  console.log('');
45
44
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
46
45
  console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
@@ -51,10 +50,10 @@ async function cherryPickCommand(context) {
51
50
  if (abort) {
52
51
  if (fs.existsSync(cherryPickStatePath)) {
53
52
  fs.unlinkSync(cherryPickStatePath);
54
- console.log(`${spinner_1.colors.green('✓')} Cherry-pick aborted`);
53
+ console.log('Cherry-pick aborted');
55
54
  }
56
55
  else {
57
- console.log(`${spinner_1.colors.muted('No cherry-pick in progress.')}`);
56
+ console.log('No cherry-pick in progress.');
58
57
  }
59
58
  console.log('');
60
59
  return;
@@ -35,33 +35,29 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.commitCommand = commitCommand;
37
37
  const crypto = __importStar(require("crypto"));
38
- const spinner_1 = require("../utils/spinner.cjs");
38
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
39
39
  const repo_manager_1 = require("../utils/repo-manager.cjs");
40
+ const config_1 = require("../../config/config.cjs");
40
41
  const change_tracker_1 = require("../utils/change-tracker.cjs");
41
42
  const fs = __importStar(require("fs"));
42
43
  const path = __importStar(require("path"));
43
44
  async function commitCommand(context) {
44
- const { config, flags, args } = context;
45
- const projectRoot = process.cwd();
45
+ const { config, flags, args, projectRoot } = context;
46
46
  const author = config?.author || 'Developer <dev@example.com>';
47
47
  console.log('');
48
48
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
49
- console.log(`${spinner_1.colors.red('error:')} relq not initialized`);
50
- console.log('');
51
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq init')} ${spinner_1.colors.muted('first.')}`);
52
- return;
49
+ (0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', "run 'relq init' to initialize");
53
50
  }
54
51
  const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
55
52
  if (staged.length === 0) {
56
- console.log(`${spinner_1.colors.yellow('Nothing to commit')}`);
57
- console.log('');
53
+ console.log('nothing to commit, working tree clean');
58
54
  const unstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
59
55
  if (unstaged.length > 0) {
60
- console.log(`${spinner_1.colors.muted(`${unstaged.length} unstaged change(s).`)}`);
61
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq add .')} ${spinner_1.colors.muted('to stage all changes.')}`);
56
+ console.log(`${unstaged.length} unstaged change(s).`);
57
+ (0, cli_utils_1.hint)("run 'relq add .' to stage all changes");
62
58
  }
63
59
  else {
64
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq add <table>')} ${spinner_1.colors.muted('to stage changes.')}`);
60
+ (0, cli_utils_1.hint)("run 'relq add <table>' to stage changes");
65
61
  }
66
62
  return;
67
63
  }
@@ -71,10 +67,7 @@ async function commitCommand(context) {
71
67
  message = args.join(' ');
72
68
  }
73
69
  else {
74
- console.log(`${spinner_1.colors.red('error:')} commit message required`);
75
- console.log('');
76
- console.log(`${spinner_1.colors.muted('Usage:')} relq commit -m "message"`);
77
- return;
70
+ (0, cli_utils_1.fatal)('commit message required', "usage: relq commit -m '<message>'");
78
71
  }
79
72
  }
80
73
  const sortedChanges = (0, change_tracker_1.sortChangesByDependency)(staged);
@@ -113,7 +106,7 @@ async function commitCommand(context) {
113
106
  fs.writeFileSync(path.join(commitsDir, `${hash}.json`), JSON.stringify(commit, null, 2), 'utf-8');
114
107
  fs.writeFileSync(path.join(projectRoot, '.relq', 'HEAD'), hash, 'utf-8');
115
108
  const workingPath = path.join(projectRoot, '.relq', 'working.json');
116
- const unstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
109
+ const unstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot).filter(c => c.objectType !== 'SCHEMA_FILE');
117
110
  if (unstaged.length > 0) {
118
111
  fs.writeFileSync(workingPath, JSON.stringify({
119
112
  timestamp: new Date().toISOString(),
@@ -126,20 +119,19 @@ async function commitCommand(context) {
126
119
  fs.unlinkSync(workingPath);
127
120
  }
128
121
  }
129
- console.log(`${spinner_1.colors.yellow(`[${(0, repo_manager_1.shortHash)(hash)}]`)} ${message}`);
130
- console.log('');
131
- if (creates > 0) {
132
- console.log(` ${spinner_1.colors.green(`${creates} created`)}`);
133
- }
134
- if (alters > 0) {
135
- console.log(` ${spinner_1.colors.yellow(`${alters} altered`)}`);
136
- }
137
- if (drops > 0) {
138
- console.log(` ${spinner_1.colors.red(`${drops} dropped`)}`);
122
+ const commitConfig = await (0, config_1.loadConfig)();
123
+ const schemaPathRaw = typeof commitConfig.schema === 'string' ? commitConfig.schema : './db/schema.ts';
124
+ const schemaFilePath = path.resolve(projectRoot, schemaPathRaw);
125
+ if (fs.existsSync(schemaFilePath)) {
126
+ const currentContent = fs.readFileSync(schemaFilePath, 'utf-8');
127
+ const currentHash = (0, repo_manager_1.hashFileContent)(currentContent);
128
+ (0, repo_manager_1.saveFileHash)(currentHash, projectRoot);
139
129
  }
130
+ console.log(`[${(0, repo_manager_1.shortHash)(hash)}] ${message}`);
131
+ console.log(` ${creates} create(s), ${alters} alter(s), ${drops} drop(s)`);
140
132
  console.log('');
141
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq push')} ${spinner_1.colors.muted('to apply changes to database.')}`);
142
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq export')} ${spinner_1.colors.muted('to export as SQL file.')}`);
133
+ (0, cli_utils_1.hint)("run 'relq push' to apply changes to database");
134
+ (0, cli_utils_1.hint)("run 'relq export' to export as SQL file");
143
135
  console.log('');
144
136
  }
145
137
  exports.default = commitCommand;
@@ -4,17 +4,14 @@ exports.diffCommand = diffCommand;
4
4
  const config_loader_1 = require("../utils/config-loader.cjs");
5
5
  const fast_introspect_1 = require("../utils/fast-introspect.cjs");
6
6
  const env_loader_1 = require("../utils/env-loader.cjs");
7
- const spinner_1 = require("../utils/spinner.cjs");
7
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
8
8
  const repo_manager_1 = require("../utils/repo-manager.cjs");
9
9
  const change_tracker_1 = require("../utils/change-tracker.cjs");
10
10
  async function diffCommand(context) {
11
- const { config, args, flags } = context;
12
- const projectRoot = process.cwd();
11
+ const { config, args, flags, projectRoot } = context;
13
12
  console.log('');
14
13
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
15
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
16
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq init')} ${spinner_1.colors.muted('to initialize.')}`);
17
- return;
14
+ (0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${cli_utils_1.colors.cyan('relq init')} to initialize.`);
18
15
  }
19
16
  const showSQL = flags['sql'] === true;
20
17
  const staged = flags['staged'] === true;
@@ -25,10 +22,9 @@ async function diffCommand(context) {
25
22
  }
26
23
  if (target === 'remote/live' || target === 'remote' || target === 'live' || target === 'origin') {
27
24
  if (!config) {
28
- console.error('Error: No configuration found.');
29
- process.exit(1);
25
+ (0, cli_utils_1.fatal)('No configuration found', `Run ${cli_utils_1.colors.cyan('relq init')} to create one.`);
30
26
  }
31
- (0, config_loader_1.requireValidConfig)(config);
27
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'diff' });
32
28
  await showOriginDiff(config, projectRoot, showSQL);
33
29
  return;
34
30
  }
@@ -36,20 +32,20 @@ async function diffCommand(context) {
36
32
  }
37
33
  function getSymbol(change) {
38
34
  if (change.type === 'CREATE')
39
- return spinner_1.colors.green('+');
35
+ return cli_utils_1.colors.green('+');
40
36
  if (change.type === 'DROP')
41
- return spinner_1.colors.red('-');
42
- return spinner_1.colors.yellow('~');
37
+ return cli_utils_1.colors.red('-');
38
+ return cli_utils_1.colors.yellow('~');
43
39
  }
44
40
  async function showUnstagedDiff(projectRoot, showSQL) {
45
41
  const unstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
46
42
  if (unstaged.length === 0) {
47
- console.log(`${spinner_1.colors.green('No unstaged changes.')}`);
48
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq diff remote/live')} ${spinner_1.colors.muted('to compare with remote.')}`);
43
+ console.log(`${cli_utils_1.colors.green('No unstaged changes.')}`);
44
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq diff remote/live')} ${cli_utils_1.colors.muted('to compare with remote.')}`);
49
45
  console.log('');
50
46
  return;
51
47
  }
52
- console.log(`${spinner_1.colors.bold('Unstaged changes:')}`);
48
+ console.log(`${cli_utils_1.colors.bold('Unstaged changes:')}`);
53
49
  console.log('');
54
50
  const sorted = (0, change_tracker_1.sortChangesByDependency)(unstaged);
55
51
  for (const change of sorted) {
@@ -57,26 +53,26 @@ async function showUnstagedDiff(projectRoot, showSQL) {
57
53
  }
58
54
  if (showSQL) {
59
55
  console.log('');
60
- console.log(`${spinner_1.colors.bold('SQL:')}`);
56
+ console.log(`${cli_utils_1.colors.bold('SQL:')}`);
61
57
  for (const change of sorted) {
62
58
  const sql = (0, change_tracker_1.generateChangeSQL)(change);
63
59
  if (sql)
64
- console.log(`${spinner_1.colors.cyan(sql)}`);
60
+ console.log(`${cli_utils_1.colors.cyan(sql)}`);
65
61
  }
66
62
  }
67
63
  console.log('');
68
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq add .')} ${spinner_1.colors.muted('to stage all.')}`);
64
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq add .')} ${cli_utils_1.colors.muted('to stage all.')}`);
69
65
  console.log('');
70
66
  }
71
67
  async function showStagedDiff(projectRoot, showSQL) {
72
68
  const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
73
69
  if (staged.length === 0) {
74
- console.log(`${spinner_1.colors.green('No staged changes.')}`);
75
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq add .')} ${spinner_1.colors.muted('to stage changes.')}`);
70
+ console.log(`${cli_utils_1.colors.green('No staged changes.')}`);
71
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq add .')} ${cli_utils_1.colors.muted('to stage changes.')}`);
76
72
  console.log('');
77
73
  return;
78
74
  }
79
- console.log(`${spinner_1.colors.bold('Staged changes:')}`);
75
+ console.log(`${cli_utils_1.colors.bold('Staged changes:')}`);
80
76
  console.log('');
81
77
  const sorted = (0, change_tracker_1.sortChangesByDependency)(staged);
82
78
  for (const change of sorted) {
@@ -84,24 +80,24 @@ async function showStagedDiff(projectRoot, showSQL) {
84
80
  }
85
81
  if (showSQL) {
86
82
  console.log('');
87
- console.log(`${spinner_1.colors.bold('SQL:')}`);
83
+ console.log(`${cli_utils_1.colors.bold('SQL:')}`);
88
84
  for (const change of sorted) {
89
85
  const sql = (0, change_tracker_1.generateChangeSQL)(change);
90
86
  if (sql)
91
- console.log(`${spinner_1.colors.cyan(sql)}`);
87
+ console.log(`${cli_utils_1.colors.cyan(sql)}`);
92
88
  }
93
89
  }
94
90
  console.log('');
95
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq commit -m "message"')} ${spinner_1.colors.muted('to commit.')}`);
91
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq commit -m "message"')} ${cli_utils_1.colors.muted('to commit.')}`);
96
92
  console.log('');
97
93
  }
98
94
  async function showOriginDiff(config, projectRoot, showSQL) {
99
95
  const connection = config.connection;
100
- const spinner = (0, spinner_1.createSpinner)();
96
+ const spinner = (0, cli_utils_1.createSpinner)();
101
97
  const snapshot = (0, repo_manager_1.loadSnapshot)(projectRoot);
102
98
  if (!snapshot) {
103
- console.log(`${spinner_1.colors.yellow('No local snapshot.')}`);
104
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('first.')}`);
99
+ console.log(`${cli_utils_1.colors.yellow('No local snapshot.')}`);
100
+ console.log(`${cli_utils_1.colors.muted('Run')} ${cli_utils_1.colors.cyan('relq pull')} ${cli_utils_1.colors.muted('first.')}`);
105
101
  console.log('');
106
102
  return;
107
103
  }
@@ -114,22 +110,22 @@ async function showOriginDiff(config, projectRoot, showSQL) {
114
110
  const diffs = compareSchemas(snapshot, remoteDb);
115
111
  if (diffs.length === 0) {
116
112
  console.log('');
117
- console.log(`${spinner_1.colors.green('Local and remote are in sync.')}`);
113
+ console.log('Local and remote are in sync.');
118
114
  console.log('');
119
115
  return;
120
116
  }
121
117
  console.log('');
122
- console.log(`${spinner_1.colors.bold('Differences:')}`);
118
+ console.log(`${cli_utils_1.colors.bold('Differences:')}`);
123
119
  console.log('');
124
120
  for (const d of diffs.slice(0, 20)) {
125
- const sym = d.type === 'added' ? spinner_1.colors.green('+') : d.type === 'removed' ? spinner_1.colors.red('-') : spinner_1.colors.yellow('~');
121
+ const sym = d.type === 'added' ? cli_utils_1.colors.green('+') : d.type === 'removed' ? cli_utils_1.colors.red('-') : cli_utils_1.colors.yellow('~');
126
122
  console.log(` ${sym} ${d.description}`);
127
123
  }
128
124
  if (diffs.length > 20) {
129
- console.log(` ${spinner_1.colors.muted(`... and ${diffs.length - 20} more`)}`);
125
+ console.log(` ${cli_utils_1.colors.muted(`... and ${diffs.length - 20} more`)}`);
130
126
  }
131
127
  console.log('');
132
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('to sync local with remote.')}`);
128
+ console.log(`${cli_utils_1.colors.muted('Use')} ${cli_utils_1.colors.cyan('relq pull')} ${cli_utils_1.colors.muted('to sync local with remote.')}`);
133
129
  console.log('');
134
130
  }
135
131
  function compareSchemas(local, remote) {
@@ -45,8 +45,7 @@ const relqignore_1 = require("../utils/relqignore.cjs");
45
45
  const config_1 = require("../../config/config.cjs");
46
46
  async function exportCommand(context) {
47
47
  const spinner = (0, spinner_1.createSpinner)();
48
- const { args, flags } = context;
49
- const projectRoot = process.cwd();
48
+ const { args, flags, projectRoot } = context;
50
49
  const changesOnly = Boolean(flags['changes']);
51
50
  const stagedOnly = Boolean(flags['staged']);
52
51
  const fromDb = Boolean(flags['db']);
@@ -111,7 +110,7 @@ async function exportFromSnapshot(projectRoot, absoluteOutputPath, options) {
111
110
  console.log(` ${spinner_1.colors.green('•')} Triggers: ${filteredSnapshot.triggers.length}`);
112
111
  }
113
112
  if (ignoredCount > 0) {
114
- console.log(` ${spinner_1.colors.muted(`ℹ ${ignoredCount} object(s) filtered by .relqignore`)}`);
113
+ console.log(` ${spinner_1.colors.muted(`${ignoredCount} object(s) filtered by .relqignore`)}`);
115
114
  }
116
115
  spinner.start('Generating SQL statements');
117
116
  const sqlContent = generateFullSQL(schema, options);
@@ -123,7 +122,7 @@ async function exportFromSnapshot(projectRoot, absoluteOutputPath, options) {
123
122
  fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
124
123
  spinner.succeed(`Written ${options.output} (${(sqlContent.length / 1024).toFixed(1)} KB)`);
125
124
  console.log('');
126
- console.log(spinner_1.colors.green('Export completed!'));
125
+ console.log('Export completed');
127
126
  console.log(` Source: ${spinner_1.colors.muted('snapshot (local)')}`);
128
127
  console.log(` Output: ${spinner_1.colors.muted(options.output || '')}`);
129
128
  }
@@ -163,7 +162,7 @@ async function exportFromDatabase(context, absoluteOutputPath, options) {
163
162
  fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
164
163
  spinner.succeed(`Written ${options.output} (${(sqlContent.length / 1024).toFixed(1)} KB)`);
165
164
  console.log('');
166
- console.log(spinner_1.colors.green('Export completed!'));
165
+ console.log('Export completed');
167
166
  console.log(` Source: ${spinner_1.colors.muted('database (live)')}`);
168
167
  console.log(` Output: ${spinner_1.colors.muted(options.output || '')}`);
169
168
  }
@@ -249,6 +248,7 @@ function normalizedToDbSchema(normalized) {
249
248
  cycle: s.cycle,
250
249
  ownedBy: s.ownedBy || undefined,
251
250
  })),
251
+ collations: [],
252
252
  extensions: normalized.extensions.map(e => e.name),
253
253
  functions: (normalized.functions || []).map(f => ({
254
254
  name: f.name,
@@ -296,7 +296,7 @@ async function exportChanges(projectRoot, flags, args, stagedOnly) {
296
296
  modeLabel = 'uncommitted';
297
297
  }
298
298
  if (changes.length === 0) {
299
- console.log(`${spinner_1.colors.yellow('⚠')} No ${modeLabel} changes to export`);
299
+ (0, spinner_1.warning)(`No ${modeLabel} changes to export`);
300
300
  console.log('');
301
301
  if (stagedOnly) {
302
302
  console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq add .')} ${spinner_1.colors.muted('to stage changes.')}`);
@@ -325,7 +325,7 @@ async function exportChanges(projectRoot, flags, args, stagedOnly) {
325
325
  fs.mkdirSync(outputDir, { recursive: true });
326
326
  }
327
327
  fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
328
- console.log(`${spinner_1.colors.green('✓')} Exported ${changes.length} ${modeLabel} change(s) to ${outputPath}`);
328
+ console.log(`Exported ${changes.length} ${modeLabel} change(s) to ${outputPath}`);
329
329
  console.log(`${spinner_1.colors.muted(`${(sqlContent.length / 1024).toFixed(1)} KB`)}`);
330
330
  console.log('');
331
331
  }
@@ -3,33 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchCommand = fetchCommand;
4
4
  const config_loader_1 = require("../utils/config-loader.cjs");
5
5
  const env_loader_1 = require("../utils/env-loader.cjs");
6
- const spinner_1 = require("../utils/spinner.cjs");
6
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
7
7
  const repo_manager_1 = require("../utils/repo-manager.cjs");
8
8
  async function fetchCommand(context) {
9
9
  const { config, flags } = context;
10
10
  if (!config) {
11
- console.error('Error: No configuration found.');
12
- process.exit(1);
11
+ (0, cli_utils_1.fatal)('No configuration found', `run ${cli_utils_1.colors.cyan('relq init')} to create a configuration file`);
13
12
  }
14
- (0, config_loader_1.requireValidConfig)(config);
13
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'fetch' });
15
14
  const connection = config.connection;
16
- const projectRoot = process.cwd();
15
+ const { projectRoot } = context;
17
16
  console.log('');
18
17
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
19
- console.log(`${spinner_1.colors.red('fatal:')} not a relq repository`);
20
- console.log('');
21
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq init')} ${spinner_1.colors.muted('to initialize.')}`);
22
- return;
18
+ (0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', "run 'relq init' to initialize");
23
19
  }
24
- const spinner = (0, spinner_1.createSpinner)();
20
+ const spinner = (0, cli_utils_1.createSpinner)();
25
21
  spinner.start(`Fetching from ${(0, env_loader_1.getConnectionDescription)(connection)}...`);
26
22
  try {
27
23
  await (0, repo_manager_1.ensureRemoteTable)(connection);
28
24
  const remoteCommits = await (0, repo_manager_1.fetchRemoteCommits)(connection, 100);
29
25
  if (remoteCommits.length === 0) {
30
26
  spinner.succeed('No remote commits found');
31
- console.log('');
32
- console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq push')} ${spinner_1.colors.muted('to push your commits.')}`);
27
+ (0, cli_utils_1.hint)("run 'relq push' to push your commits");
33
28
  return;
34
29
  }
35
30
  let newCommits = 0;
@@ -50,25 +45,24 @@ async function fetchCommand(context) {
50
45
  const ahead = [...localCommits].filter(h => !remoteHashes.has(h)).length;
51
46
  console.log('');
52
47
  if (behind > 0 && ahead > 0) {
53
- console.log(`Your branch and 'origin/main' have diverged.`);
54
- console.log(` ${spinner_1.colors.yellow(String(ahead))} commit(s) ahead, ${spinner_1.colors.yellow(String(behind))} commit(s) behind`);
48
+ console.log(`Your branch and 'origin/main' have diverged,`);
49
+ console.log(`and have ${ahead} and ${behind} different commits each, respectively.`);
55
50
  }
56
51
  else if (behind > 0) {
57
- console.log(`Your branch is ${spinner_1.colors.yellow(String(behind))} commit(s) behind 'origin/main'.`);
58
- console.log(` ${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq pull')} ${spinner_1.colors.muted('to update.')}`);
52
+ console.log(`Your branch is behind 'origin/main' by ${behind} commit(s).`);
53
+ (0, cli_utils_1.hint)("run 'relq pull' to update your local branch");
59
54
  }
60
55
  else if (ahead > 0) {
61
- console.log(`Your branch is ${spinner_1.colors.green(String(ahead))} commit(s) ahead of 'origin/main'.`);
62
- console.log(` ${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq push')} ${spinner_1.colors.muted('to publish.')}`);
56
+ console.log(`Your branch is ahead of 'origin/main' by ${ahead} commit(s).`);
57
+ (0, cli_utils_1.hint)("run 'relq push' to publish your local commits");
63
58
  }
64
59
  else {
65
- console.log(`${spinner_1.colors.green('✓')} Your branch is up to date with 'origin/main'.`);
60
+ console.log("Your branch is up to date with 'origin/main'.");
66
61
  }
67
62
  }
68
63
  catch (error) {
69
64
  spinner.fail('Fetch failed');
70
- console.error(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : error}`));
71
- process.exit(1);
65
+ (0, cli_utils_1.fatal)('Fetch failed', error instanceof Error ? error.message : String(error));
72
66
  }
73
67
  console.log('');
74
68
  }
@@ -44,32 +44,7 @@ const schema_diff_1 = require("../utils/schema-diff.cjs");
44
44
  const schema_hash_1 = require("../utils/schema-hash.cjs");
45
45
  const migration_generator_1 = require("../utils/migration-generator.cjs");
46
46
  const env_loader_1 = require("../utils/env-loader.cjs");
47
- const colors = {
48
- reset: '\x1b[0m',
49
- bold: '\x1b[1m',
50
- dim: '\x1b[2m',
51
- red: '\x1b[31m',
52
- green: '\x1b[32m',
53
- yellow: '\x1b[33m',
54
- cyan: '\x1b[36m',
55
- };
56
- function askConfirm(question, defaultYes = false) {
57
- const rl = readline.createInterface({
58
- input: process.stdin,
59
- output: process.stdout,
60
- });
61
- const suffix = defaultYes ? '[Y/n]' : '[y/N]';
62
- return new Promise((resolve) => {
63
- rl.question(`${question} ${suffix}: `, (answer) => {
64
- rl.close();
65
- const a = answer.trim().toLowerCase();
66
- if (!a)
67
- resolve(defaultYes);
68
- else
69
- resolve(a === 'y' || a === 'yes');
70
- });
71
- });
72
- }
47
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
73
48
  function askInput(question) {
74
49
  const rl = readline.createInterface({
75
50
  input: process.stdin,
@@ -85,10 +60,10 @@ function askInput(question) {
85
60
  async function generateCommand(context) {
86
61
  const { config, args, flags } = context;
87
62
  if (!config) {
88
- console.error('Error: No configuration found.');
89
- process.exit(1);
63
+ (0, cli_utils_1.fatal)('No configuration found', `run ${cli_utils_1.colors.cyan('relq init')} to create a configuration file`);
64
+ return;
90
65
  }
91
- (0, config_loader_1.requireValidConfig)(config);
66
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'generate' });
92
67
  const connection = config.connection;
93
68
  const migrationsDir = config.migrations?.directory || './migrations';
94
69
  const snapshotPath = config.sync?.snapshot || '.relq/snapshot.json';
@@ -111,10 +86,10 @@ async function generateCommand(context) {
111
86
  migrationName = message.replace(/\s+/g, '_').toLowerCase();
112
87
  }
113
88
  if (!migrationName && !isEmpty) {
114
- migrationName = await askInput(`${colors.cyan}Migration name:${colors.reset} `);
89
+ migrationName = await askInput('Migration name: ');
115
90
  if (!migrationName) {
116
- console.error('Error: Migration name is required.');
117
- process.exit(1);
91
+ (0, cli_utils_1.fatal)('Migration name is required');
92
+ return;
118
93
  }
119
94
  }
120
95
  if (isEmpty) {
@@ -122,48 +97,48 @@ async function generateCommand(context) {
122
97
  await createEmptyMigration(migrationsDir, migrationName, format, dryRun);
123
98
  return;
124
99
  }
125
- console.log(`${colors.bold}Generating migration...${colors.reset}`);
100
+ console.log('Generating migration...');
126
101
  console.log(` Connection: ${(0, env_loader_1.getConnectionDescription)(connection)}`);
127
102
  console.log('');
128
103
  try {
129
104
  const dbSchema = await (0, schema_introspect_1.introspectDatabase)(connection);
130
105
  const snapshot = (0, snapshot_manager_1.loadSnapshot)(snapshotPath);
131
106
  if (!snapshot) {
132
- console.log(`${colors.yellow}No snapshot found.${colors.reset}`);
133
- console.log(`Run "${colors.cyan}relq pull${colors.reset}" first to create initial snapshot.`);
107
+ (0, cli_utils_1.warning)('No snapshot found.');
108
+ console.log('Run "relq pull" first to create initial snapshot.');
134
109
  return;
135
110
  }
136
111
  const localSchema = (0, snapshot_manager_1.snapshotToDatabaseSchema)(snapshot);
137
112
  const diff = (0, schema_diff_1.diffSchemas)((0, schema_hash_1.normalizeSchema)(localSchema), (0, schema_hash_1.normalizeSchema)(dbSchema));
138
113
  const filteredDiff = (0, schema_diff_1.filterDiff)(diff, ignorePatterns);
139
114
  if (!filteredDiff.hasChanges) {
140
- console.log(`${colors.green}No changes to generate.${colors.reset}`);
115
+ console.log('No changes to generate.');
141
116
  return;
142
117
  }
143
118
  const s = filteredDiff.summary;
144
- console.log(`${colors.bold}Changes to include:${colors.reset}`);
119
+ console.log('Changes to include:');
145
120
  if (s.tablesAdded > 0)
146
- console.log(` ${colors.green}+ ${s.tablesAdded} table(s)${colors.reset}`);
121
+ console.log(` + ${s.tablesAdded} table(s)`);
147
122
  if (s.tablesRemoved > 0)
148
- console.log(` ${colors.red}- ${s.tablesRemoved} table(s)${colors.reset}`);
123
+ console.log(` - ${s.tablesRemoved} table(s)`);
149
124
  if (s.tablesModified > 0)
150
- console.log(` ${colors.yellow}~ ${s.tablesModified} table(s) modified${colors.reset}`);
125
+ console.log(` ~ ${s.tablesModified} table(s) modified`);
151
126
  if (s.columnsAdded > 0)
152
- console.log(` ${colors.green}+ ${s.columnsAdded} column(s)${colors.reset}`);
127
+ console.log(` + ${s.columnsAdded} column(s)`);
153
128
  if (s.columnsRemoved > 0)
154
- console.log(` ${colors.red}- ${s.columnsRemoved} column(s)${colors.reset}`);
129
+ console.log(` - ${s.columnsRemoved} column(s)`);
155
130
  if (s.columnsModified > 0)
156
- console.log(` ${colors.yellow}~ ${s.columnsModified} column(s)${colors.reset}`);
131
+ console.log(` ~ ${s.columnsModified} column(s)`);
157
132
  console.log('');
158
133
  if ((0, schema_diff_1.hasDestructiveChanges)(filteredDiff)) {
159
134
  const tables = (0, schema_diff_1.getDestructiveTables)(filteredDiff);
160
- console.log(`${colors.red}${colors.bold}⚠️ Destructive changes:${colors.reset}`);
135
+ (0, cli_utils_1.warning)('Destructive changes:');
161
136
  for (const t of tables) {
162
- console.log(` ${colors.red}• ${t}${colors.reset}`);
137
+ console.log(` - ${t}`);
163
138
  }
164
139
  console.log('');
165
140
  if (!autoStage) {
166
- const proceed = await askConfirm('Include destructive changes?', false);
141
+ const proceed = await (0, cli_utils_1.confirm)('Include destructive changes?', false);
167
142
  if (!proceed) {
168
143
  console.log('Cancelled.');
169
144
  return;
@@ -185,7 +160,7 @@ async function generateCommand(context) {
185
160
  }
186
161
  const filePath = path.join(migrationsDir, fileName);
187
162
  if (dryRun) {
188
- console.log(`${colors.cyan}[dry-run] Would create: ${filePath}${colors.reset}`);
163
+ console.log(`[dry-run] Would create: ${filePath}`);
189
164
  console.log('');
190
165
  console.log('--- Generated SQL ---');
191
166
  console.log(migrationFile.content);
@@ -196,16 +171,15 @@ async function generateCommand(context) {
196
171
  fs.mkdirSync(migrationsDir, { recursive: true });
197
172
  }
198
173
  fs.writeFileSync(filePath, migrationFile.content, 'utf-8');
199
- console.log(`${colors.green}✓ Created:${colors.reset} ${filePath}`);
174
+ console.log(`Created: ${filePath}`);
200
175
  (0, snapshot_manager_1.saveSnapshot)(dbSchema, snapshotPath, connection.database);
201
- console.log(`${colors.dim}Updated snapshot.${colors.reset}`);
176
+ console.log('Updated snapshot.');
202
177
  }
203
178
  console.log('');
204
- console.log(`${colors.dim}Run "relq push" to apply this migration.${colors.reset}`);
179
+ console.log('Run "relq push" to apply this migration.');
205
180
  }
206
181
  catch (error) {
207
- console.error('Error:', error instanceof Error ? error.message : error);
208
- process.exit(1);
182
+ (0, cli_utils_1.fatal)('Generation failed', error instanceof Error ? error.message : String(error));
209
183
  }
210
184
  }
211
185
  async function createEmptyMigration(migrationsDir, name, format, dryRun) {
@@ -230,13 +204,13 @@ async function createEmptyMigration(migrationsDir, name, format, dryRun) {
230
204
 
231
205
  `;
232
206
  if (dryRun) {
233
- console.log(`${colors.cyan}[dry-run] Would create: ${filePath}${colors.reset}`);
207
+ console.log(`[dry-run] Would create: ${filePath}`);
234
208
  }
235
209
  else {
236
210
  if (!fs.existsSync(migrationsDir)) {
237
211
  fs.mkdirSync(migrationsDir, { recursive: true });
238
212
  }
239
213
  fs.writeFileSync(filePath, content, 'utf-8');
240
- console.log(`${colors.green}✓ Created empty migration:${colors.reset} ${filePath}`);
214
+ console.log(`Created empty migration: ${filePath}`);
241
215
  }
242
216
  }