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
@@ -36,17 +36,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.pullCommand = pullCommand;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
- const readline = __importStar(require("readline"));
40
39
  const config_loader_1 = require("../utils/config-loader.cjs");
41
40
  const fast_introspect_1 = require("../utils/fast-introspect.cjs");
42
41
  const type_generator_1 = require("../utils/type-generator.cjs");
43
42
  const env_loader_1 = require("../utils/env-loader.cjs");
44
- const spinner_1 = require("../utils/spinner.cjs");
43
+ const cli_utils_1 = require("../utils/cli-utils.cjs");
45
44
  const relqignore_1 = require("../utils/relqignore.cjs");
46
45
  const repo_manager_1 = require("../utils/repo-manager.cjs");
46
+ const schema_comparator_1 = require("../utils/schema-comparator.cjs");
47
47
  function toCamelCase(str) {
48
48
  return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
49
49
  }
50
+ function normalizePartitionKey(partitionKey) {
51
+ if (!partitionKey)
52
+ return undefined;
53
+ if (Array.isArray(partitionKey)) {
54
+ return partitionKey;
55
+ }
56
+ if (typeof partitionKey === 'string') {
57
+ return partitionKey
58
+ .replace(/^\{|\}$/g, '')
59
+ .split(',')
60
+ .map(k => k.trim())
61
+ .filter(Boolean);
62
+ }
63
+ return undefined;
64
+ }
50
65
  function parseSchemaFileForSnapshot(schemaPath) {
51
66
  if (!fs.existsSync(schemaPath)) {
52
67
  return null;
@@ -152,86 +167,70 @@ function parseSchemaFileForSnapshot(schemaPath) {
152
167
  extensions: [],
153
168
  };
154
169
  }
155
- function askConfirm(question, defaultYes = true) {
156
- const suffix = defaultYes ? spinner_1.colors.muted('[Y/n]') : spinner_1.colors.muted('[y/N]');
157
- const rl = readline.createInterface({
158
- input: process.stdin,
159
- output: process.stdout,
160
- });
161
- return new Promise((resolve) => {
162
- rl.question(`${question} ${suffix}: `, (answer) => {
163
- rl.close();
164
- const a = answer.trim().toLowerCase();
165
- if (!a)
166
- resolve(defaultYes);
167
- else
168
- resolve(a === 'y' || a === 'yes');
169
- });
170
- });
171
- }
172
170
  async function pullCommand(context) {
173
- const { config, flags } = context;
171
+ const { config, flags, projectRoot } = context;
174
172
  if (!config) {
175
- console.error('Error: No configuration found.');
176
- process.exit(1);
173
+ (0, cli_utils_1.fatal)('No configuration found', `Run ${cli_utils_1.colors.cyan('relq init')} to create one.`);
177
174
  }
178
- (0, config_loader_1.requireValidConfig)(config);
175
+ await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'pull' });
179
176
  const connection = config.connection;
180
- const projectRoot = process.cwd();
181
177
  const force = flags['force'] === true;
178
+ const merge = flags['merge'] === true;
179
+ const noCommit = flags['no-commit'] === true;
182
180
  const dryRun = flags['dry-run'] === true;
183
181
  const author = config.author || 'Relq CLI';
184
- const schemaPath = typeof config.schema === 'string' ? config.schema : './db/schema.ts';
182
+ const schemaPathRaw = typeof config.schema === 'string' ? config.schema : './db/schema.ts';
183
+ const schemaPath = path.resolve(projectRoot, schemaPathRaw);
185
184
  const includeFunctions = config.generate?.includeFunctions ?? false;
186
185
  const includeTriggers = config.generate?.includeTriggers ?? false;
187
- const spinner = (0, spinner_1.createSpinner)();
186
+ const spinner = (0, cli_utils_1.createSpinner)();
188
187
  const startTime = Date.now();
189
188
  console.log('');
190
189
  if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
191
190
  (0, repo_manager_1.initRepository)(projectRoot);
192
- console.log(`${spinner_1.colors.cyan('ℹ')} Initialized .relq folder`);
191
+ console.log('Initialized .relq folder');
193
192
  }
194
- if (!force) {
195
- const stagedChanges = (0, repo_manager_1.getStagedChanges)(projectRoot);
196
- const unstagedChanges = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
197
- if (stagedChanges.length > 0 || unstagedChanges.length > 0) {
193
+ const stagedChanges = (0, repo_manager_1.getStagedChanges)(projectRoot);
194
+ const unstagedChanges = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
195
+ const hasLocalChanges = stagedChanges.length > 0 || unstagedChanges.length > 0;
196
+ let stashedSchemaContent = null;
197
+ const schemaFileExists = fs.existsSync(schemaPath);
198
+ if (hasLocalChanges) {
199
+ if (force) {
200
+ (0, repo_manager_1.clearWorkingState)(projectRoot);
201
+ }
202
+ else if (merge) {
203
+ if (schemaFileExists) {
204
+ stashedSchemaContent = fs.readFileSync(schemaPath, 'utf-8');
205
+ }
206
+ (0, repo_manager_1.clearWorkingState)(projectRoot);
207
+ console.log(`Stashed local file content for restore after pull`);
208
+ }
209
+ else {
198
210
  const hasUnstaged = unstagedChanges.length > 0;
199
211
  const hasStaged = stagedChanges.length > 0;
200
- if (hasUnstaged && hasStaged) {
201
- console.error(spinner_1.colors.red('Error: You have uncommitted and unstaged changes.'));
202
- }
203
- else if (hasStaged) {
204
- console.error(spinner_1.colors.red('Error: You have uncommitted changes.'));
205
- }
206
- else {
207
- console.error(spinner_1.colors.red('Error: You have unstaged changes.'));
208
- }
209
212
  console.log('');
210
213
  if (hasStaged) {
211
- console.log(` ${spinner_1.colors.green('Staged (uncommitted):')} ${stagedChanges.length} change(s)`);
214
+ console.log(` ${cli_utils_1.colors.green('Staged (uncommitted):')} ${stagedChanges.length} change(s)`);
212
215
  }
213
216
  if (hasUnstaged) {
214
- console.log(` ${spinner_1.colors.red('Unstaged:')} ${unstagedChanges.length} change(s)`);
217
+ console.log(` ${cli_utils_1.colors.red('Unstaged:')} ${unstagedChanges.length} change(s)`);
215
218
  }
216
- console.log('');
217
- if (hasUnstaged && !hasStaged) {
218
- console.log('Please stage and commit, or reset your changes:');
219
- console.log(` ${spinner_1.colors.cyan('relq add .')} - stage all changes`);
220
- console.log(` ${spinner_1.colors.cyan('relq commit -m "message"')} - then commit`);
219
+ let errorMsg = 'You have uncommitted changes';
220
+ if (hasUnstaged && hasStaged) {
221
+ errorMsg = 'You have uncommitted and unstaged changes';
221
222
  }
222
- else {
223
- console.log('Please commit or reset your changes:');
224
- console.log(` ${spinner_1.colors.cyan('relq commit -m "message"')} - commit staged changes`);
223
+ else if (hasUnstaged) {
224
+ errorMsg = 'You have unstaged changes';
225
225
  }
226
- console.log(` ${spinner_1.colors.cyan('relq reset')} - discard all changes`);
227
- console.log(` ${spinner_1.colors.cyan('relq pull --force')} - force pull (overwrites local changes)`);
228
- return;
226
+ const hint = `Commit first: ${cli_utils_1.colors.cyan('relq add . && relq commit -m "message"')}\nOr merge: ${cli_utils_1.colors.cyan('relq pull --merge')} (preserve local edits)\nOr force: ${cli_utils_1.colors.cyan('relq pull --force')} (discard & pull)`;
227
+ (0, cli_utils_1.fatal)(errorMsg, hint);
229
228
  }
230
229
  }
231
230
  try {
232
231
  spinner.start('Connecting to database...');
233
232
  await (0, repo_manager_1.ensureRemoteTable)(connection);
234
- spinner.succeed(`Connected to ${spinner_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
233
+ spinner.succeed(`Connected to ${cli_utils_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
235
234
  spinner.start('Fetching remote commits...');
236
235
  const remoteCommits = await (0, repo_manager_1.fetchRemoteCommits)(connection, 100);
237
236
  const remoteHead = remoteCommits.length > 0 ? remoteCommits[0].hash : null;
@@ -290,6 +289,9 @@ async function pullCommand(context) {
290
289
  type: c.type,
291
290
  definition: c.definition,
292
291
  })),
292
+ isPartitioned: t.isPartitioned,
293
+ partitionType: t.partitionType,
294
+ partitionKey: normalizePartitionKey(t.partitionKey),
293
295
  })),
294
296
  enums: filteredEnums.map(e => ({
295
297
  name: e.name,
@@ -330,12 +332,7 @@ async function pullCommand(context) {
330
332
  console.log('');
331
333
  const mergeStatePath = path.join(projectRoot, '.relq', 'MERGE_STATE');
332
334
  if (fs.existsSync(mergeStatePath) && !force) {
333
- console.log(`${spinner_1.colors.red('error:')} You have unresolved merge conflicts`);
334
- console.log('');
335
- console.log(`${spinner_1.colors.muted('Use')} ${spinner_1.colors.cyan('relq resolve')} ${spinner_1.colors.muted('to see and resolve conflicts')}`);
336
- console.log(`${spinner_1.colors.muted('Or use')} ${spinner_1.colors.cyan('relq pull --force')} ${spinner_1.colors.muted('to overwrite local')}`);
337
- console.log('');
338
- return;
335
+ (0, cli_utils_1.fatal)('You have unresolved merge conflicts', `Use ${cli_utils_1.colors.cyan('relq resolve')} to see and resolve conflicts\nOr use ${cli_utils_1.colors.cyan('relq pull --force')} to overwrite local`);
339
336
  }
340
337
  if (schemaExists && localSnapshot && !force) {
341
338
  const localTables = new Set(localSnapshot.tables.map(t => t.name));
@@ -350,75 +347,76 @@ async function pullCommand(context) {
350
347
  createdAt: new Date().toISOString(),
351
348
  };
352
349
  fs.writeFileSync(mergeStatePath, JSON.stringify(mergeState, null, 2));
353
- console.log(`${spinner_1.colors.red('error:')} Merge conflict detected`);
354
350
  console.log('');
355
351
  console.log(`Both local and remote have modified the same objects:`);
356
352
  console.log('');
357
353
  for (const c of conflicts.slice(0, 10)) {
358
354
  const name = c.parentName ? `${c.parentName}.${c.objectName}` : c.objectName;
359
- console.log(` ${spinner_1.colors.red('conflict:')} ${c.objectType.toLowerCase()} ${spinner_1.colors.bold(name)}`);
360
- console.log(` ${spinner_1.colors.muted(c.description)}`);
355
+ console.log(` ${cli_utils_1.colors.red('conflict:')} ${c.objectType.toLowerCase()} ${cli_utils_1.colors.bold(name)}`);
356
+ console.log(` ${cli_utils_1.colors.muted(c.description)}`);
361
357
  }
362
358
  if (conflicts.length > 10) {
363
- console.log(` ${spinner_1.colors.muted(`... and ${conflicts.length - 10} more`)}`);
359
+ console.log(` ${cli_utils_1.colors.muted(`... and ${conflicts.length - 10} more`)}`);
364
360
  }
365
- console.log('');
366
- console.log('To resolve:');
367
- console.log(` ${spinner_1.colors.cyan('relq resolve --theirs <name>')} Take remote version`);
368
- console.log(` ${spinner_1.colors.cyan('relq resolve --ours <name>')} Keep local version`);
369
- console.log(` ${spinner_1.colors.cyan('relq resolve --all-theirs')} Take all remote`);
370
- console.log(` ${spinner_1.colors.cyan('relq pull --force')} Force overwrite local`);
371
- console.log('');
372
- return;
361
+ (0, cli_utils_1.fatal)('Automatic merge failed; fix conflicts and then commit', `${cli_utils_1.colors.cyan('relq resolve --theirs <name>')} Take remote version\n${cli_utils_1.colors.cyan('relq resolve --all-theirs')} Take all remote\n${cli_utils_1.colors.cyan('relq pull --force')} Force overwrite local`);
373
362
  }
374
363
  if (added.length === 0 && removed.length === 0) {
375
- console.log(`${spinner_1.colors.green('✓')} Already up to date with remote`);
364
+ console.log('Already up to date with remote');
376
365
  console.log('');
377
366
  return;
378
367
  }
379
- console.log(`${spinner_1.colors.yellow('Remote has changes:')}`);
368
+ console.log(`${cli_utils_1.colors.yellow('Remote has changes:')}`);
380
369
  if (added.length > 0) {
381
- console.log(` ${spinner_1.colors.green(`+${added.length}`)} tables added`);
370
+ console.log(` ${cli_utils_1.colors.green(`+${added.length}`)} tables added`);
382
371
  }
383
372
  if (removed.length > 0) {
384
- console.log(` ${spinner_1.colors.red(`-${removed.length}`)} tables removed`);
373
+ console.log(` ${cli_utils_1.colors.red(`-${removed.length}`)} tables removed`);
385
374
  }
386
375
  console.log('');
387
376
  if (!dryRun) {
388
- const proceed = await askConfirm(`${spinner_1.colors.bold('Pull these changes?')}`, true);
377
+ const proceed = await (0, cli_utils_1.confirm)(`${cli_utils_1.colors.bold('Pull these changes?')}`, true);
389
378
  if (!proceed) {
390
- console.log('');
391
- console.log(`${spinner_1.colors.muted('Cancelled.')}`);
392
- return;
379
+ (0, cli_utils_1.fatal)('Operation cancelled by user');
393
380
  }
394
381
  console.log('');
395
382
  }
396
383
  }
397
384
  else if (schemaExists && !force) {
398
- console.log(`${spinner_1.colors.yellow('⚠')} ${spinner_1.colors.bold('Local schema exists but not tracked')}`);
385
+ (0, cli_utils_1.warning)('Local schema exists but not tracked');
399
386
  console.log('');
400
- console.log(` ${spinner_1.colors.cyan(schemaPath)}`);
387
+ console.log(` ${cli_utils_1.colors.cyan(schemaPath)}`);
401
388
  console.log('');
402
389
  if (!dryRun) {
403
- const proceed = await askConfirm(`${spinner_1.colors.bold('Overwrite local schema?')}`, false);
390
+ const proceed = await (0, cli_utils_1.confirm)(`${cli_utils_1.colors.bold('Overwrite local schema?')}`, false);
404
391
  if (!proceed) {
405
- console.log('');
406
- console.log(`${spinner_1.colors.muted('Cancelled. Run')} ${spinner_1.colors.cyan('relq status')} ${spinner_1.colors.muted('to see current state.')}`);
407
- return;
392
+ (0, cli_utils_1.fatal)('Operation cancelled by user', `Run ${cli_utils_1.colors.cyan('relq status')} to see current state.`);
408
393
  }
409
394
  console.log('');
410
395
  }
411
396
  }
412
397
  else if (!schemaExists) {
413
- console.log(`${spinner_1.colors.cyan('ℹ')} ${spinner_1.colors.bold('First pull - creating schema')}`);
398
+ console.log('First pull - creating schema');
414
399
  console.log('');
415
- console.log(`📊 ${spinner_1.colors.bold('Schema Summary')}`);
416
- console.log(` ${spinner_1.colors.green(String(filteredTables.length))} tables`);
417
- console.log(` ${spinner_1.colors.green(String(dbSchema.extensions.length))} extensions`);
400
+ const indexCount = filteredTables.reduce((sum, t) => sum + (t.indexes?.filter(i => !i.isPrimary).length || 0), 0);
401
+ const partitionCount = filteredTables.filter(t => t.isPartitioned).length;
402
+ const tableCommentCount = filteredTables.filter(t => t.comment).length;
403
+ const columnCommentCount = filteredTables.reduce((sum, t) => sum + t.columns.filter(c => c.comment).length, 0);
404
+ console.log('Schema Summary:');
405
+ console.log(` ${cli_utils_1.colors.green(String(filteredTables.length))} tables`);
406
+ if (indexCount > 0)
407
+ console.log(` ${cli_utils_1.colors.green(String(indexCount))} indexes`);
408
+ if (partitionCount > 0)
409
+ console.log(` ${cli_utils_1.colors.green(String(partitionCount))} partitioned tables`);
410
+ if (tableCommentCount > 0)
411
+ console.log(` ${cli_utils_1.colors.green(String(tableCommentCount))} table comments`);
412
+ if (columnCommentCount > 0)
413
+ console.log(` ${cli_utils_1.colors.green(String(columnCommentCount))} column comments`);
414
+ if (dbSchema.extensions.length > 0)
415
+ console.log(` ${cli_utils_1.colors.green(String(dbSchema.extensions.length))} extensions`);
418
416
  console.log('');
419
417
  }
420
418
  if (dryRun) {
421
- console.log(`${spinner_1.colors.yellow('⚠')} Dry run - no files written`);
419
+ console.log('Dry run - no files written');
422
420
  console.log('');
423
421
  return;
424
422
  }
@@ -439,27 +437,139 @@ async function pullCommand(context) {
439
437
  spinner.start('Writing schema file...');
440
438
  fs.writeFileSync(schemaPath, typescript, 'utf-8');
441
439
  const fileSize = Buffer.byteLength(typescript, 'utf8');
442
- spinner.succeed(`Written ${spinner_1.colors.cyan(schemaPath)} ${spinner_1.colors.muted(`(${(0, spinner_1.formatBytes)(fileSize)})`)}`);
443
- const snapshotFromFile = parseSchemaFileForSnapshot(schemaPath);
444
- if (snapshotFromFile) {
445
- (0, repo_manager_1.saveSnapshot)(snapshotFromFile, projectRoot);
440
+ spinner.succeed(`Written ${cli_utils_1.colors.cyan(schemaPath)} ${cli_utils_1.colors.muted(`(${(0, cli_utils_1.formatBytes)(fileSize)})`)}`);
441
+ const fileHash = (0, repo_manager_1.hashFileContent)(typescript);
442
+ (0, repo_manager_1.saveFileHash)(fileHash, projectRoot);
443
+ const oldSnapshot = (0, repo_manager_1.loadSnapshot)(projectRoot);
444
+ const beforeSchema = oldSnapshot ? {
445
+ extensions: oldSnapshot.extensions?.map(e => e.name) || [],
446
+ enums: oldSnapshot.enums || [],
447
+ domains: oldSnapshot.domains?.map(d => ({
448
+ name: d.name,
449
+ baseType: d.baseType,
450
+ isNotNull: d.notNull,
451
+ defaultValue: d.default,
452
+ checkExpression: d.check,
453
+ })) || [],
454
+ compositeTypes: oldSnapshot.compositeTypes || [],
455
+ sequences: oldSnapshot.sequences || [],
456
+ tables: oldSnapshot.tables.map(t => ({
457
+ name: t.name,
458
+ schema: t.schema,
459
+ columns: t.columns.map(c => ({
460
+ name: c.name,
461
+ dataType: c.type,
462
+ isNullable: c.nullable,
463
+ defaultValue: c.default,
464
+ isPrimaryKey: c.primaryKey,
465
+ isUnique: c.unique,
466
+ comment: c.comment,
467
+ })),
468
+ indexes: t.indexes.map(i => ({
469
+ name: i.name,
470
+ columns: i.columns,
471
+ isUnique: i.unique,
472
+ type: i.type,
473
+ })),
474
+ constraints: t.constraints || [],
475
+ isPartitioned: t.isPartitioned,
476
+ partitionType: t.partitionType,
477
+ partitionKey: t.partitionKey,
478
+ comment: t.comment,
479
+ })),
480
+ functions: oldSnapshot.functions || [],
481
+ triggers: oldSnapshot.triggers || [],
482
+ } : {
483
+ extensions: [],
484
+ enums: [],
485
+ domains: [],
486
+ compositeTypes: [],
487
+ sequences: [],
488
+ tables: [],
489
+ functions: [],
490
+ triggers: [],
491
+ };
492
+ const afterSchema = {
493
+ extensions: dbSchema.extensions || [],
494
+ enums: filteredEnums || [],
495
+ domains: filteredDomains?.map(d => ({
496
+ name: d.name,
497
+ baseType: d.baseType,
498
+ isNotNull: d.isNotNull,
499
+ defaultValue: d.defaultValue,
500
+ checkExpression: d.checkExpression,
501
+ })) || [],
502
+ compositeTypes: filteredCompositeTypes || [],
503
+ sequences: [],
504
+ tables: filteredTables.map(t => ({
505
+ name: t.name,
506
+ schema: t.schema,
507
+ columns: t.columns.map(c => ({
508
+ name: c.name,
509
+ dataType: c.dataType,
510
+ isNullable: c.isNullable,
511
+ defaultValue: c.defaultValue,
512
+ isPrimaryKey: c.isPrimaryKey,
513
+ isUnique: c.isUnique,
514
+ comment: c.comment,
515
+ })),
516
+ indexes: t.indexes.map(i => ({
517
+ name: i.name,
518
+ columns: i.columns,
519
+ isUnique: i.isUnique,
520
+ type: i.type,
521
+ })),
522
+ constraints: t.constraints || [],
523
+ isPartitioned: t.isPartitioned,
524
+ partitionType: t.partitionType,
525
+ partitionKey: t.partitionKey,
526
+ childPartitions: t.childPartitions,
527
+ comment: t.comment,
528
+ })),
529
+ functions: filteredFunctions || [],
530
+ triggers: filteredTriggers || [],
531
+ };
532
+ const schemaChanges = (0, schema_comparator_1.compareSchemas)(beforeSchema, afterSchema);
533
+ (0, repo_manager_1.saveSnapshot)(currentSchema, projectRoot);
534
+ const duration = Date.now() - startTime;
535
+ if (noCommit) {
536
+ if (schemaChanges.length > 0) {
537
+ (0, repo_manager_1.addUnstagedChanges)(schemaChanges, projectRoot);
538
+ spinner.succeed(`Detected ${schemaChanges.length} schema change(s)`);
539
+ }
540
+ console.log('');
541
+ console.log(`Pull completed in ${(0, cli_utils_1.formatDuration)(duration)}`);
542
+ if (schemaChanges.length > 0) {
543
+ console.log('');
544
+ console.log(`${cli_utils_1.colors.green(String(schemaChanges.length))} change(s) ready to stage`);
545
+ console.log(`hint: run ${cli_utils_1.colors.cyan("'relq add .'")} to stage all changes`);
546
+ }
547
+ else {
548
+ console.log('Already up to date');
549
+ }
446
550
  }
447
551
  else {
448
- (0, repo_manager_1.saveSnapshot)(currentSchema, projectRoot);
552
+ const connectionDesc = (0, env_loader_1.getConnectionDescription)(connection);
553
+ const commitMessage = `pull: sync from ${connectionDesc}`;
554
+ const commit = (0, repo_manager_1.createCommit)(currentSchema, author, commitMessage, projectRoot);
555
+ (0, repo_manager_1.clearWorkingState)(projectRoot);
556
+ console.log('');
557
+ console.log(`Pull completed in ${(0, cli_utils_1.formatDuration)(duration)}`);
558
+ console.log('');
559
+ console.log(`${cli_utils_1.colors.yellow('→')} ${(0, repo_manager_1.shortHash)(commit.hash)} ${commitMessage}`);
560
+ console.log(` ${cli_utils_1.colors.green(String(commit.stats.tables))} tables, ${cli_utils_1.colors.green(String(commit.stats.columns))} columns`);
561
+ if (stashedSchemaContent) {
562
+ fs.writeFileSync(schemaPath, stashedSchemaContent, 'utf-8');
563
+ console.log('');
564
+ console.log(`${cli_utils_1.colors.yellow('Restored')} local schema file content`);
565
+ console.log(`hint: run ${cli_utils_1.colors.cyan("'relq add .'")} to detect changes against the new snapshot`);
566
+ }
449
567
  }
450
- spinner.start('Creating commit...');
451
- const message = localHead ? 'Pulled schema from database' : 'Initial schema pull';
452
- const commit = (0, repo_manager_1.createCommit)(currentSchema, author, message, projectRoot);
453
- spinner.succeed(`Created commit ${spinner_1.colors.yellow((0, repo_manager_1.shortHash)(commit.hash))}`);
454
- const duration = Date.now() - startTime;
455
- console.log('');
456
- console.log(`${spinner_1.colors.green('✓')} Pull completed in ${spinner_1.colors.cyan(`${duration}ms`)}`);
457
568
  console.log('');
458
569
  }
459
- catch (error) {
570
+ catch (err) {
460
571
  spinner.fail('Pull failed');
461
- console.error(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : error}`));
462
- process.exit(1);
572
+ (0, cli_utils_1.fatal)(err instanceof Error ? err.message : String(err));
463
573
  }
464
574
  }
465
575
  function detectObjectConflicts(local, remote) {