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.
- package/dist/cjs/addon/buffer/index.cjs +1881 -0
- package/dist/cjs/addon/pg/index.cjs +4812 -0
- package/dist/cjs/addon/pg-cursor/index.cjs +1451 -0
- package/dist/cjs/addon/pg-format/index.cjs +2270 -0
- package/dist/cjs/cli/commands/add.cjs +430 -25
- package/dist/cjs/cli/commands/branch.cjs +131 -0
- package/dist/cjs/cli/commands/checkout.cjs +121 -0
- package/dist/cjs/cli/commands/cherry-pick.cjs +282 -0
- package/dist/cjs/cli/commands/commit.cjs +21 -29
- package/dist/cjs/cli/commands/diff.cjs +144 -69
- package/dist/cjs/cli/commands/export.cjs +70 -11
- package/dist/cjs/cli/commands/fetch.cjs +42 -18
- package/dist/cjs/cli/commands/generate.cjs +28 -54
- package/dist/cjs/cli/commands/history.cjs +19 -40
- package/dist/cjs/cli/commands/import.cjs +305 -41
- package/dist/cjs/cli/commands/init.cjs +69 -59
- package/dist/cjs/cli/commands/introspect.cjs +4 -8
- package/dist/cjs/cli/commands/log.cjs +84 -15
- package/dist/cjs/cli/commands/merge.cjs +207 -0
- package/dist/cjs/cli/commands/migrate.cjs +13 -26
- package/dist/cjs/cli/commands/pull.cjs +321 -95
- package/dist/cjs/cli/commands/push.cjs +228 -52
- package/dist/cjs/cli/commands/remote.cjs +17 -0
- package/dist/cjs/cli/commands/reset.cjs +148 -0
- package/dist/cjs/cli/commands/resolve.cjs +191 -0
- package/dist/cjs/cli/commands/rollback.cjs +17 -39
- package/dist/cjs/cli/commands/stash.cjs +152 -0
- package/dist/cjs/cli/commands/status.cjs +52 -9
- package/dist/cjs/cli/commands/sync.cjs +30 -50
- package/dist/cjs/cli/commands/tag.cjs +146 -0
- package/dist/cjs/cli/index.cjs +117 -10
- package/dist/cjs/cli/utils/change-tracker.cjs +107 -3
- package/dist/cjs/cli/utils/cli-utils.cjs +217 -0
- package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
- package/dist/cjs/cli/utils/config-loader.cjs +34 -8
- package/dist/cjs/cli/utils/env-loader.cjs +3 -2
- package/dist/cjs/cli/utils/fast-introspect.cjs +110 -4
- package/dist/cjs/cli/utils/git-utils.cjs +42 -161
- package/dist/cjs/cli/utils/pool-manager.cjs +156 -0
- package/dist/cjs/cli/utils/project-root.cjs +107 -0
- package/dist/cjs/cli/utils/relqignore.cjs +297 -38
- package/dist/cjs/cli/utils/repo-manager.cjs +92 -3
- package/dist/cjs/cli/utils/schema-comparator.cjs +301 -11
- package/dist/cjs/cli/utils/schema-diff.cjs +202 -1
- package/dist/cjs/cli/utils/schema-hash.cjs +2 -1
- package/dist/cjs/cli/utils/schema-introspect.cjs +9 -5
- package/dist/cjs/cli/utils/snapshot-manager.cjs +1 -0
- package/dist/cjs/cli/utils/spinner.cjs +14 -106
- package/dist/cjs/cli/utils/sql-generator.cjs +2 -2
- package/dist/cjs/cli/utils/sql-parser.cjs +94 -7
- package/dist/cjs/cli/utils/type-generator.cjs +28 -16
- package/dist/cjs/condition/array-condition-builder.cjs +1 -1
- package/dist/cjs/condition/condition-collector.cjs +1 -1
- package/dist/cjs/condition/fulltext-condition-builder.cjs +1 -1
- package/dist/cjs/condition/geometric-condition-builder.cjs +1 -1
- package/dist/cjs/condition/jsonb-condition-builder.cjs +1 -1
- package/dist/cjs/condition/network-condition-builder.cjs +1 -1
- package/dist/cjs/condition/range-condition-builder.cjs +1 -1
- package/dist/cjs/copy/copy-builder.cjs +1 -1
- package/dist/cjs/core/query-builder.cjs +1 -1
- package/dist/cjs/core/relq-client.cjs +2 -2
- package/dist/cjs/count/count-builder.cjs +1 -1
- package/dist/cjs/cte/cte-builder.cjs +1 -1
- package/dist/cjs/delete/delete-builder.cjs +1 -1
- package/dist/cjs/function/create-function-builder.cjs +1 -1
- package/dist/cjs/functions/advanced-functions.cjs +1 -1
- package/dist/cjs/functions/case-builder.cjs +1 -1
- package/dist/cjs/functions/geometric-functions.cjs +1 -1
- package/dist/cjs/functions/network-functions.cjs +1 -1
- package/dist/cjs/functions/sql-functions.cjs +1 -1
- package/dist/cjs/indexing/create-index-builder.cjs +1 -1
- package/dist/cjs/indexing/drop-index-builder.cjs +1 -1
- package/dist/cjs/insert/conflict-builder.cjs +1 -1
- package/dist/cjs/insert/insert-builder.cjs +1 -1
- package/dist/cjs/maintenance/vacuum-builder.cjs +1 -1
- package/dist/cjs/pubsub/listen-notify-builder.cjs +1 -1
- package/dist/cjs/pubsub/listener-connection.cjs +2 -2
- package/dist/cjs/raw/raw-query-builder.cjs +1 -1
- package/dist/cjs/schema/schema-builder.cjs +1 -1
- package/dist/cjs/schema-definition/table-definition.cjs +1 -1
- package/dist/cjs/select/aggregate-builder.cjs +1 -1
- package/dist/cjs/select/select-builder.cjs +1 -1
- package/dist/cjs/sequence/sequence-builder.cjs +1 -1
- package/dist/cjs/table/alter-table-builder.cjs +1 -1
- package/dist/cjs/table/constraint-builder.cjs +1 -1
- package/dist/cjs/table/create-table-builder.cjs +1 -1
- package/dist/cjs/table/partition-builder.cjs +1 -1
- package/dist/cjs/table/truncate-builder.cjs +1 -1
- package/dist/cjs/transaction/transaction-builder.cjs +1 -1
- package/dist/cjs/trigger/create-trigger-builder.cjs +1 -1
- package/dist/cjs/update/array-update-builder.cjs +1 -1
- package/dist/cjs/update/update-builder.cjs +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/view/create-view-builder.cjs +1 -1
- package/dist/cjs/window/window-builder.cjs +1 -1
- package/dist/config.d.ts +16 -25
- package/dist/esm/cli/commands/add.js +399 -27
- package/dist/esm/cli/commands/branch.js +95 -0
- package/dist/esm/cli/commands/checkout.js +85 -0
- package/dist/esm/cli/commands/cherry-pick.js +246 -0
- package/dist/esm/cli/commands/commit.js +22 -30
- package/dist/esm/cli/commands/diff.js +144 -69
- package/dist/esm/cli/commands/export.js +71 -12
- package/dist/esm/cli/commands/fetch.js +42 -18
- package/dist/esm/cli/commands/generate.js +28 -54
- package/dist/esm/cli/commands/history.js +11 -32
- package/dist/esm/cli/commands/import.js +306 -42
- package/dist/esm/cli/commands/init.js +65 -55
- package/dist/esm/cli/commands/introspect.js +4 -8
- package/dist/esm/cli/commands/log.js +78 -10
- package/dist/esm/cli/commands/merge.js +171 -0
- package/dist/esm/cli/commands/migrate.js +13 -26
- package/dist/esm/cli/commands/pull.js +313 -87
- package/dist/esm/cli/commands/push.js +223 -47
- package/dist/esm/cli/commands/remote.js +14 -0
- package/dist/esm/cli/commands/reset.js +112 -0
- package/dist/esm/cli/commands/resolve.js +155 -0
- package/dist/esm/cli/commands/rollback.js +17 -39
- package/dist/esm/cli/commands/stash.js +116 -0
- package/dist/esm/cli/commands/status.js +20 -10
- package/dist/esm/cli/commands/sync.js +30 -50
- package/dist/esm/cli/commands/tag.js +110 -0
- package/dist/esm/cli/index.js +118 -11
- package/dist/esm/cli/utils/change-tracker.js +107 -3
- package/dist/esm/cli/utils/cli-utils.js +169 -0
- package/dist/esm/cli/utils/commit-manager.js +3 -3
- package/dist/esm/cli/utils/config-loader.js +34 -8
- package/dist/esm/cli/utils/env-loader.js +3 -2
- package/dist/esm/cli/utils/fast-introspect.js +110 -4
- package/dist/esm/cli/utils/git-utils.js +2 -124
- package/dist/esm/cli/utils/pool-manager.js +114 -0
- package/dist/esm/cli/utils/project-root.js +69 -0
- package/dist/esm/cli/utils/relqignore.js +278 -37
- package/dist/esm/cli/utils/repo-manager.js +83 -3
- package/dist/esm/cli/utils/schema-comparator.js +301 -11
- package/dist/esm/cli/utils/schema-diff.js +202 -1
- package/dist/esm/cli/utils/schema-hash.js +2 -1
- package/dist/esm/cli/utils/schema-introspect.js +9 -5
- package/dist/esm/cli/utils/snapshot-manager.js +1 -0
- package/dist/esm/cli/utils/spinner.js +1 -101
- package/dist/esm/cli/utils/sql-generator.js +2 -2
- package/dist/esm/cli/utils/sql-parser.js +94 -7
- package/dist/esm/cli/utils/type-generator.js +28 -16
- package/dist/esm/condition/array-condition-builder.js +1 -1
- package/dist/esm/condition/condition-collector.js +1 -1
- package/dist/esm/condition/fulltext-condition-builder.js +1 -1
- package/dist/esm/condition/geometric-condition-builder.js +1 -1
- package/dist/esm/condition/jsonb-condition-builder.js +1 -1
- package/dist/esm/condition/network-condition-builder.js +1 -1
- package/dist/esm/condition/range-condition-builder.js +1 -1
- package/dist/esm/copy/copy-builder.js +1 -1
- package/dist/esm/core/query-builder.js +1 -1
- package/dist/esm/core/relq-client.js +2 -2
- package/dist/esm/count/count-builder.js +1 -1
- package/dist/esm/cte/cte-builder.js +1 -1
- package/dist/esm/delete/delete-builder.js +1 -1
- package/dist/esm/function/create-function-builder.js +1 -1
- package/dist/esm/functions/advanced-functions.js +1 -1
- package/dist/esm/functions/case-builder.js +1 -1
- package/dist/esm/functions/geometric-functions.js +1 -1
- package/dist/esm/functions/network-functions.js +1 -1
- package/dist/esm/functions/sql-functions.js +1 -1
- package/dist/esm/indexing/create-index-builder.js +1 -1
- package/dist/esm/indexing/drop-index-builder.js +1 -1
- package/dist/esm/insert/conflict-builder.js +1 -1
- package/dist/esm/insert/insert-builder.js +1 -1
- package/dist/esm/maintenance/vacuum-builder.js +1 -1
- package/dist/esm/pubsub/listen-notify-builder.js +1 -1
- package/dist/esm/pubsub/listener-connection.js +2 -2
- package/dist/esm/raw/raw-query-builder.js +1 -1
- package/dist/esm/schema/schema-builder.js +1 -1
- package/dist/esm/schema-definition/table-definition.js +1 -1
- package/dist/esm/select/aggregate-builder.js +1 -1
- package/dist/esm/select/select-builder.js +1 -1
- package/dist/esm/sequence/sequence-builder.js +1 -1
- package/dist/esm/table/alter-table-builder.js +1 -1
- package/dist/esm/table/constraint-builder.js +1 -1
- package/dist/esm/table/create-table-builder.js +1 -1
- package/dist/esm/table/partition-builder.js +1 -1
- package/dist/esm/table/truncate-builder.js +1 -1
- package/dist/esm/transaction/transaction-builder.js +1 -1
- package/dist/esm/trigger/create-trigger-builder.js +1 -1
- package/dist/esm/update/array-update-builder.js +1 -1
- package/dist/esm/update/update-builder.js +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/view/create-view-builder.js +1 -1
- package/dist/esm/window/window-builder.js +1 -1
- package/dist/index.d.ts +25 -8
- package/dist/schema-builder.d.ts +16 -6
- package/package.json +1 -1
- /package/dist/{addons/buffer.js → esm/addon/buffer/index.js} +0 -0
- /package/dist/{addons/pg.js → esm/addon/pg/index.js} +0 -0
- /package/dist/{addons/pg-cursor.js → esm/addon/pg-cursor/index.js} +0 -0
- /package/dist/{addons/pg-format.js → esm/addon/pg-format/index.js} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { introspectDatabase } from "../utils/schema-introspect.js";
|
|
4
|
-
import { createSpinner, colors } from "../utils/spinner.js";
|
|
4
|
+
import { createSpinner, colors, warning } from "../utils/spinner.js";
|
|
5
5
|
import { isInitialized, getStagedChanges, getUnstagedChanges, loadSnapshot, } from "../utils/repo-manager.js";
|
|
6
6
|
import { generateCombinedSQL, sortChangesByDependency, getChangeDisplayName, } from "../utils/change-tracker.js";
|
|
7
7
|
import { generateFullSchemaSQL } from "../utils/sql-generator.js";
|
|
8
|
+
import { loadRelqignore, isTableIgnored, isColumnIgnored, isIndexIgnored, isConstraintIgnored, isEnumIgnored, isDomainIgnored, isSequenceIgnored, isCompositeTypeIgnored, isFunctionIgnored, } from "../utils/relqignore.js";
|
|
9
|
+
import { loadConfig } from "../../config/config.js";
|
|
8
10
|
export async function exportCommand(context) {
|
|
9
11
|
const spinner = createSpinner();
|
|
10
|
-
const { args, flags } = context;
|
|
11
|
-
const projectRoot = process.cwd();
|
|
12
|
+
const { args, flags, projectRoot } = context;
|
|
12
13
|
const changesOnly = Boolean(flags['changes']);
|
|
13
14
|
const stagedOnly = Boolean(flags['staged']);
|
|
14
15
|
const fromDb = Boolean(flags['db']);
|
|
@@ -46,7 +47,18 @@ async function exportFromSnapshot(projectRoot, absoluteOutputPath, options) {
|
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
spinner.succeed('Loaded snapshot');
|
|
49
|
-
const
|
|
50
|
+
const config = await loadConfig();
|
|
51
|
+
const ignorePatterns = loadRelqignore(projectRoot);
|
|
52
|
+
const filteredSnapshot = filterNormalizedSchema(snapshot, ignorePatterns, {
|
|
53
|
+
includeFunctions: config.includeFunctions ?? options.includeFunctions ?? false,
|
|
54
|
+
includeTriggers: config.includeTriggers ?? options.includeTriggers ?? false,
|
|
55
|
+
includeViews: config.includeViews ?? false,
|
|
56
|
+
includeFDW: config.includeFDW ?? false,
|
|
57
|
+
});
|
|
58
|
+
const schema = normalizedToDbSchema(filteredSnapshot);
|
|
59
|
+
const ignoredCount = (snapshot.tables.length - filteredSnapshot.tables.length) +
|
|
60
|
+
(snapshot.enums.length - filteredSnapshot.enums.length) +
|
|
61
|
+
((snapshot.functions?.length || 0) - (filteredSnapshot.functions?.length || 0));
|
|
50
62
|
console.log('');
|
|
51
63
|
console.log(colors.cyan('Schema Summary:'));
|
|
52
64
|
console.log(` ${colors.green('•')} Tables: ${schema.tables.length}`);
|
|
@@ -55,11 +67,14 @@ async function exportFromSnapshot(projectRoot, absoluteOutputPath, options) {
|
|
|
55
67
|
console.log(` ${colors.green('•')} Composite Types: ${schema.compositeTypes.length}`);
|
|
56
68
|
console.log(` ${colors.green('•')} Sequences: ${schema.sequences?.length || 0}`);
|
|
57
69
|
console.log(` ${colors.green('•')} Extensions: ${schema.extensions.length}`);
|
|
58
|
-
if (
|
|
59
|
-
console.log(` ${colors.green('•')} Functions: ${
|
|
70
|
+
if (filteredSnapshot.functions?.length) {
|
|
71
|
+
console.log(` ${colors.green('•')} Functions: ${filteredSnapshot.functions.length}`);
|
|
72
|
+
}
|
|
73
|
+
if (filteredSnapshot.triggers?.length) {
|
|
74
|
+
console.log(` ${colors.green('•')} Triggers: ${filteredSnapshot.triggers.length}`);
|
|
60
75
|
}
|
|
61
|
-
if (
|
|
62
|
-
console.log(` ${colors.
|
|
76
|
+
if (ignoredCount > 0) {
|
|
77
|
+
console.log(` ${colors.muted(`${ignoredCount} object(s) filtered by .relqignore`)}`);
|
|
63
78
|
}
|
|
64
79
|
spinner.start('Generating SQL statements');
|
|
65
80
|
const sqlContent = generateFullSQL(schema, options);
|
|
@@ -71,7 +86,7 @@ async function exportFromSnapshot(projectRoot, absoluteOutputPath, options) {
|
|
|
71
86
|
fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
|
|
72
87
|
spinner.succeed(`Written ${options.output} (${(sqlContent.length / 1024).toFixed(1)} KB)`);
|
|
73
88
|
console.log('');
|
|
74
|
-
console.log(
|
|
89
|
+
console.log('Export completed');
|
|
75
90
|
console.log(` Source: ${colors.muted('snapshot (local)')}`);
|
|
76
91
|
console.log(` Output: ${colors.muted(options.output || '')}`);
|
|
77
92
|
}
|
|
@@ -111,7 +126,7 @@ async function exportFromDatabase(context, absoluteOutputPath, options) {
|
|
|
111
126
|
fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
|
|
112
127
|
spinner.succeed(`Written ${options.output} (${(sqlContent.length / 1024).toFixed(1)} KB)`);
|
|
113
128
|
console.log('');
|
|
114
|
-
console.log(
|
|
129
|
+
console.log('Export completed');
|
|
115
130
|
console.log(` Source: ${colors.muted('database (live)')}`);
|
|
116
131
|
console.log(` Output: ${colors.muted(options.output || '')}`);
|
|
117
132
|
}
|
|
@@ -197,6 +212,7 @@ function normalizedToDbSchema(normalized) {
|
|
|
197
212
|
cycle: s.cycle,
|
|
198
213
|
ownedBy: s.ownedBy || undefined,
|
|
199
214
|
})),
|
|
215
|
+
collations: [],
|
|
200
216
|
extensions: normalized.extensions.map(e => e.name),
|
|
201
217
|
functions: (normalized.functions || []).map(f => ({
|
|
202
218
|
name: f.name,
|
|
@@ -244,7 +260,7 @@ async function exportChanges(projectRoot, flags, args, stagedOnly) {
|
|
|
244
260
|
modeLabel = 'uncommitted';
|
|
245
261
|
}
|
|
246
262
|
if (changes.length === 0) {
|
|
247
|
-
|
|
263
|
+
warning(`No ${modeLabel} changes to export`);
|
|
248
264
|
console.log('');
|
|
249
265
|
if (stagedOnly) {
|
|
250
266
|
console.log(`${colors.muted('Run')} ${colors.cyan('relq add .')} ${colors.muted('to stage changes.')}`);
|
|
@@ -273,7 +289,7 @@ async function exportChanges(projectRoot, flags, args, stagedOnly) {
|
|
|
273
289
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
274
290
|
}
|
|
275
291
|
fs.writeFileSync(absoluteOutputPath, sqlContent, 'utf-8');
|
|
276
|
-
console.log(
|
|
292
|
+
console.log(`Exported ${changes.length} ${modeLabel} change(s) to ${outputPath}`);
|
|
277
293
|
console.log(`${colors.muted(`${(sqlContent.length / 1024).toFixed(1)} KB`)}`);
|
|
278
294
|
console.log('');
|
|
279
295
|
}
|
|
@@ -294,4 +310,47 @@ function generateFullSQL(schema, options) {
|
|
|
294
310
|
headerComment: header,
|
|
295
311
|
});
|
|
296
312
|
}
|
|
313
|
+
function filterNormalizedSchema(schema, patterns, options) {
|
|
314
|
+
const filteredTables = schema.tables
|
|
315
|
+
.filter(table => !isTableIgnored(table.name, patterns).ignored)
|
|
316
|
+
.map(table => ({
|
|
317
|
+
...table,
|
|
318
|
+
columns: table.columns.filter(col => !isColumnIgnored(table.name, col.name, patterns).ignored),
|
|
319
|
+
indexes: table.indexes.filter(idx => !isIndexIgnored(table.name, idx.name, patterns).ignored),
|
|
320
|
+
constraints: table.constraints?.filter(con => !isConstraintIgnored(table.name, con.name, patterns).ignored) || [],
|
|
321
|
+
}));
|
|
322
|
+
const filteredEnums = schema.enums.filter(e => !isEnumIgnored(e.name, patterns).ignored);
|
|
323
|
+
const filteredDomains = schema.domains.filter(d => !isDomainIgnored(d.name, patterns).ignored);
|
|
324
|
+
const filteredCompositeTypes = schema.compositeTypes.filter(c => !isCompositeTypeIgnored(c.name, patterns).ignored);
|
|
325
|
+
const filteredSequences = schema.sequences.filter(s => !isSequenceIgnored(s.name, patterns).ignored);
|
|
326
|
+
const filteredFunctions = options.includeFunctions
|
|
327
|
+
? (schema.functions || []).filter(f => !isFunctionIgnored(f.name, patterns).ignored)
|
|
328
|
+
: [];
|
|
329
|
+
const filteredTriggers = options.includeTriggers
|
|
330
|
+
? schema.triggers || []
|
|
331
|
+
: [];
|
|
332
|
+
const filteredViews = options.includeViews
|
|
333
|
+
? schema.views || []
|
|
334
|
+
: [];
|
|
335
|
+
const filteredMaterializedViews = options.includeViews
|
|
336
|
+
? schema.materializedViews || []
|
|
337
|
+
: [];
|
|
338
|
+
const filteredForeignTables = options.includeFDW
|
|
339
|
+
? schema.foreignTables || []
|
|
340
|
+
: [];
|
|
341
|
+
return {
|
|
342
|
+
extensions: schema.extensions,
|
|
343
|
+
enums: filteredEnums,
|
|
344
|
+
domains: filteredDomains,
|
|
345
|
+
compositeTypes: filteredCompositeTypes,
|
|
346
|
+
sequences: filteredSequences,
|
|
347
|
+
collations: schema.collations,
|
|
348
|
+
tables: filteredTables,
|
|
349
|
+
functions: filteredFunctions,
|
|
350
|
+
triggers: filteredTriggers,
|
|
351
|
+
views: filteredViews,
|
|
352
|
+
materializedViews: filteredMaterializedViews,
|
|
353
|
+
foreignTables: filteredForeignTables,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
297
356
|
export default exportCommand;
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
2
2
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
3
|
-
import { colors, createSpinner } from "../utils/
|
|
4
|
-
import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead,
|
|
3
|
+
import { colors, createSpinner, fatal, hint } from "../utils/cli-utils.js";
|
|
4
|
+
import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead, fetchRemoteCommits, ensureRemoteTable, } from "../utils/repo-manager.js";
|
|
5
5
|
export async function fetchCommand(context) {
|
|
6
6
|
const { config, flags } = context;
|
|
7
7
|
if (!config) {
|
|
8
|
-
|
|
9
|
-
process.exit(1);
|
|
8
|
+
fatal('No configuration found', `run ${colors.cyan('relq init')} to create a configuration file`);
|
|
10
9
|
}
|
|
11
|
-
requireValidConfig(config);
|
|
10
|
+
await requireValidConfig(config, { calledFrom: 'fetch' });
|
|
12
11
|
const connection = config.connection;
|
|
13
|
-
const projectRoot =
|
|
12
|
+
const { projectRoot } = context;
|
|
14
13
|
console.log('');
|
|
15
14
|
if (!isInitialized(projectRoot)) {
|
|
16
|
-
|
|
17
|
-
console.log('');
|
|
18
|
-
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('to initialize.')}`);
|
|
19
|
-
return;
|
|
15
|
+
fatal('not a relq repository (or any parent directories): .relq', "run 'relq init' to initialize");
|
|
20
16
|
}
|
|
21
17
|
const spinner = createSpinner();
|
|
22
18
|
spinner.start(`Fetching from ${getConnectionDescription(connection)}...`);
|
|
@@ -25,8 +21,7 @@ export async function fetchCommand(context) {
|
|
|
25
21
|
const remoteCommits = await fetchRemoteCommits(connection, 100);
|
|
26
22
|
if (remoteCommits.length === 0) {
|
|
27
23
|
spinner.succeed('No remote commits found');
|
|
28
|
-
|
|
29
|
-
console.log(`${colors.muted('Run')} ${colors.cyan('relq push')} ${colors.muted('to push your commits.')}`);
|
|
24
|
+
hint("run 'relq push' to push your commits");
|
|
30
25
|
return;
|
|
31
26
|
}
|
|
32
27
|
let newCommits = 0;
|
|
@@ -41,16 +36,45 @@ export async function fetchCommand(context) {
|
|
|
41
36
|
setFetchHead(latestRemote.hash, projectRoot);
|
|
42
37
|
spinner.succeed(`Fetched ${remoteCommits.length} commits (${newCommits} new)`);
|
|
43
38
|
const localHead = getHead(projectRoot);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
const localCommits = localHead ? getAllLocalCommitHashes(projectRoot) : new Set();
|
|
40
|
+
const remoteHashes = new Set(remoteCommits.map(c => c.hash));
|
|
41
|
+
const behind = remoteCommits.filter(c => !localCommits.has(c.hash)).length;
|
|
42
|
+
const ahead = [...localCommits].filter(h => !remoteHashes.has(h)).length;
|
|
43
|
+
console.log('');
|
|
44
|
+
if (behind > 0 && ahead > 0) {
|
|
45
|
+
console.log(`Your branch and 'origin/main' have diverged,`);
|
|
46
|
+
console.log(`and have ${ahead} and ${behind} different commits each, respectively.`);
|
|
47
|
+
}
|
|
48
|
+
else if (behind > 0) {
|
|
49
|
+
console.log(`Your branch is behind 'origin/main' by ${behind} commit(s).`);
|
|
50
|
+
hint("run 'relq pull' to update your local branch");
|
|
51
|
+
}
|
|
52
|
+
else if (ahead > 0) {
|
|
53
|
+
console.log(`Your branch is ahead of 'origin/main' by ${ahead} commit(s).`);
|
|
54
|
+
hint("run 'relq push' to publish your local commits");
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log("Your branch is up to date with 'origin/main'.");
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
catch (error) {
|
|
51
61
|
spinner.fail('Fetch failed');
|
|
52
|
-
|
|
53
|
-
process.exit(1);
|
|
62
|
+
fatal('Fetch failed', error instanceof Error ? error.message : String(error));
|
|
54
63
|
}
|
|
55
64
|
console.log('');
|
|
56
65
|
}
|
|
66
|
+
function getAllLocalCommitHashes(projectRoot) {
|
|
67
|
+
const fs = require('fs');
|
|
68
|
+
const path = require('path');
|
|
69
|
+
const commitsDir = path.join(projectRoot, '.relq', 'commits');
|
|
70
|
+
const hashes = new Set();
|
|
71
|
+
if (fs.existsSync(commitsDir)) {
|
|
72
|
+
const files = fs.readdirSync(commitsDir);
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
if (file.endsWith('.json')) {
|
|
75
|
+
hashes.add(file.replace('.json', ''));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return hashes;
|
|
80
|
+
}
|
|
@@ -8,32 +8,7 @@ import { diffSchemas, filterDiff, hasDestructiveChanges, getDestructiveTables }
|
|
|
8
8
|
import { normalizeSchema } from "../utils/schema-hash.js";
|
|
9
9
|
import { generateMigrationFile, getNextMigrationNumber, generateTimestampedName } from "../utils/migration-generator.js";
|
|
10
10
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
11
|
-
|
|
12
|
-
reset: '\x1b[0m',
|
|
13
|
-
bold: '\x1b[1m',
|
|
14
|
-
dim: '\x1b[2m',
|
|
15
|
-
red: '\x1b[31m',
|
|
16
|
-
green: '\x1b[32m',
|
|
17
|
-
yellow: '\x1b[33m',
|
|
18
|
-
cyan: '\x1b[36m',
|
|
19
|
-
};
|
|
20
|
-
function askConfirm(question, defaultYes = false) {
|
|
21
|
-
const rl = readline.createInterface({
|
|
22
|
-
input: process.stdin,
|
|
23
|
-
output: process.stdout,
|
|
24
|
-
});
|
|
25
|
-
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
rl.question(`${question} ${suffix}: `, (answer) => {
|
|
28
|
-
rl.close();
|
|
29
|
-
const a = answer.trim().toLowerCase();
|
|
30
|
-
if (!a)
|
|
31
|
-
resolve(defaultYes);
|
|
32
|
-
else
|
|
33
|
-
resolve(a === 'y' || a === 'yes');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
}
|
|
11
|
+
import { colors, confirm, fatal, warning } from "../utils/cli-utils.js";
|
|
37
12
|
function askInput(question) {
|
|
38
13
|
const rl = readline.createInterface({
|
|
39
14
|
input: process.stdin,
|
|
@@ -49,10 +24,10 @@ function askInput(question) {
|
|
|
49
24
|
export async function generateCommand(context) {
|
|
50
25
|
const { config, args, flags } = context;
|
|
51
26
|
if (!config) {
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
fatal('No configuration found', `run ${colors.cyan('relq init')} to create a configuration file`);
|
|
28
|
+
return;
|
|
54
29
|
}
|
|
55
|
-
requireValidConfig(config);
|
|
30
|
+
await requireValidConfig(config, { calledFrom: 'generate' });
|
|
56
31
|
const connection = config.connection;
|
|
57
32
|
const migrationsDir = config.migrations?.directory || './migrations';
|
|
58
33
|
const snapshotPath = config.sync?.snapshot || '.relq/snapshot.json';
|
|
@@ -75,10 +50,10 @@ export async function generateCommand(context) {
|
|
|
75
50
|
migrationName = message.replace(/\s+/g, '_').toLowerCase();
|
|
76
51
|
}
|
|
77
52
|
if (!migrationName && !isEmpty) {
|
|
78
|
-
migrationName = await askInput(
|
|
53
|
+
migrationName = await askInput('Migration name: ');
|
|
79
54
|
if (!migrationName) {
|
|
80
|
-
|
|
81
|
-
|
|
55
|
+
fatal('Migration name is required');
|
|
56
|
+
return;
|
|
82
57
|
}
|
|
83
58
|
}
|
|
84
59
|
if (isEmpty) {
|
|
@@ -86,48 +61,48 @@ export async function generateCommand(context) {
|
|
|
86
61
|
await createEmptyMigration(migrationsDir, migrationName, format, dryRun);
|
|
87
62
|
return;
|
|
88
63
|
}
|
|
89
|
-
console.log(
|
|
64
|
+
console.log('Generating migration...');
|
|
90
65
|
console.log(` Connection: ${getConnectionDescription(connection)}`);
|
|
91
66
|
console.log('');
|
|
92
67
|
try {
|
|
93
68
|
const dbSchema = await introspectDatabase(connection);
|
|
94
69
|
const snapshot = loadSnapshot(snapshotPath);
|
|
95
70
|
if (!snapshot) {
|
|
96
|
-
|
|
97
|
-
console.log(
|
|
71
|
+
warning('No snapshot found.');
|
|
72
|
+
console.log('Run "relq pull" first to create initial snapshot.');
|
|
98
73
|
return;
|
|
99
74
|
}
|
|
100
75
|
const localSchema = snapshotToDatabaseSchema(snapshot);
|
|
101
76
|
const diff = diffSchemas(normalizeSchema(localSchema), normalizeSchema(dbSchema));
|
|
102
77
|
const filteredDiff = filterDiff(diff, ignorePatterns);
|
|
103
78
|
if (!filteredDiff.hasChanges) {
|
|
104
|
-
console.log(
|
|
79
|
+
console.log('No changes to generate.');
|
|
105
80
|
return;
|
|
106
81
|
}
|
|
107
82
|
const s = filteredDiff.summary;
|
|
108
|
-
console.log(
|
|
83
|
+
console.log('Changes to include:');
|
|
109
84
|
if (s.tablesAdded > 0)
|
|
110
|
-
console.log(`
|
|
85
|
+
console.log(` + ${s.tablesAdded} table(s)`);
|
|
111
86
|
if (s.tablesRemoved > 0)
|
|
112
|
-
console.log(`
|
|
87
|
+
console.log(` - ${s.tablesRemoved} table(s)`);
|
|
113
88
|
if (s.tablesModified > 0)
|
|
114
|
-
console.log(`
|
|
89
|
+
console.log(` ~ ${s.tablesModified} table(s) modified`);
|
|
115
90
|
if (s.columnsAdded > 0)
|
|
116
|
-
console.log(`
|
|
91
|
+
console.log(` + ${s.columnsAdded} column(s)`);
|
|
117
92
|
if (s.columnsRemoved > 0)
|
|
118
|
-
console.log(`
|
|
93
|
+
console.log(` - ${s.columnsRemoved} column(s)`);
|
|
119
94
|
if (s.columnsModified > 0)
|
|
120
|
-
console.log(`
|
|
95
|
+
console.log(` ~ ${s.columnsModified} column(s)`);
|
|
121
96
|
console.log('');
|
|
122
97
|
if (hasDestructiveChanges(filteredDiff)) {
|
|
123
98
|
const tables = getDestructiveTables(filteredDiff);
|
|
124
|
-
|
|
99
|
+
warning('Destructive changes:');
|
|
125
100
|
for (const t of tables) {
|
|
126
|
-
console.log(`
|
|
101
|
+
console.log(` - ${t}`);
|
|
127
102
|
}
|
|
128
103
|
console.log('');
|
|
129
104
|
if (!autoStage) {
|
|
130
|
-
const proceed = await
|
|
105
|
+
const proceed = await confirm('Include destructive changes?', false);
|
|
131
106
|
if (!proceed) {
|
|
132
107
|
console.log('Cancelled.');
|
|
133
108
|
return;
|
|
@@ -149,7 +124,7 @@ export async function generateCommand(context) {
|
|
|
149
124
|
}
|
|
150
125
|
const filePath = path.join(migrationsDir, fileName);
|
|
151
126
|
if (dryRun) {
|
|
152
|
-
console.log(
|
|
127
|
+
console.log(`[dry-run] Would create: ${filePath}`);
|
|
153
128
|
console.log('');
|
|
154
129
|
console.log('--- Generated SQL ---');
|
|
155
130
|
console.log(migrationFile.content);
|
|
@@ -160,16 +135,15 @@ export async function generateCommand(context) {
|
|
|
160
135
|
fs.mkdirSync(migrationsDir, { recursive: true });
|
|
161
136
|
}
|
|
162
137
|
fs.writeFileSync(filePath, migrationFile.content, 'utf-8');
|
|
163
|
-
console.log(
|
|
138
|
+
console.log(`Created: ${filePath}`);
|
|
164
139
|
saveSnapshot(dbSchema, snapshotPath, connection.database);
|
|
165
|
-
console.log(
|
|
140
|
+
console.log('Updated snapshot.');
|
|
166
141
|
}
|
|
167
142
|
console.log('');
|
|
168
|
-
console.log(
|
|
143
|
+
console.log('Run "relq push" to apply this migration.');
|
|
169
144
|
}
|
|
170
145
|
catch (error) {
|
|
171
|
-
|
|
172
|
-
process.exit(1);
|
|
146
|
+
fatal('Generation failed', error instanceof Error ? error.message : String(error));
|
|
173
147
|
}
|
|
174
148
|
}
|
|
175
149
|
async function createEmptyMigration(migrationsDir, name, format, dryRun) {
|
|
@@ -194,13 +168,13 @@ async function createEmptyMigration(migrationsDir, name, format, dryRun) {
|
|
|
194
168
|
|
|
195
169
|
`;
|
|
196
170
|
if (dryRun) {
|
|
197
|
-
console.log(
|
|
171
|
+
console.log(`[dry-run] Would create: ${filePath}`);
|
|
198
172
|
}
|
|
199
173
|
else {
|
|
200
174
|
if (!fs.existsSync(migrationsDir)) {
|
|
201
175
|
fs.mkdirSync(migrationsDir, { recursive: true });
|
|
202
176
|
}
|
|
203
177
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
204
|
-
console.log(
|
|
178
|
+
console.log(`Created empty migration: ${filePath}`);
|
|
205
179
|
}
|
|
206
180
|
}
|
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
3
3
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
bold: '\x1b[1m',
|
|
7
|
-
dim: '\x1b[2m',
|
|
8
|
-
red: '\x1b[31m',
|
|
9
|
-
green: '\x1b[32m',
|
|
10
|
-
yellow: '\x1b[33m',
|
|
11
|
-
cyan: '\x1b[36m',
|
|
12
|
-
};
|
|
4
|
+
import { colors, fatal } from "../utils/cli-utils.js";
|
|
5
|
+
import { withPool } from "../utils/pool-manager.js";
|
|
13
6
|
export async function historyCommand(context) {
|
|
14
7
|
const { config, flags } = context;
|
|
15
8
|
if (!config) {
|
|
16
|
-
|
|
17
|
-
process.exit(1);
|
|
9
|
+
fatal('No configuration found', `run ${colors.cyan('relq init')} to create a configuration file`);
|
|
18
10
|
}
|
|
19
|
-
requireValidConfig(config);
|
|
11
|
+
await requireValidConfig(config, { calledFrom: 'history' });
|
|
20
12
|
const connection = config.connection;
|
|
21
13
|
const migrationsDir = config.migrations?.directory || './migrations';
|
|
22
14
|
const tableName = config.migrations?.tableName || '_relq_migrations';
|
|
23
15
|
const limit = parseInt(flags['n']) || 20;
|
|
24
|
-
console.log(
|
|
16
|
+
console.log('Migration History');
|
|
25
17
|
console.log(`Database: ${getConnectionDescription(connection)}`);
|
|
26
18
|
console.log('');
|
|
27
19
|
try {
|
|
@@ -40,8 +32,8 @@ export async function historyCommand(context) {
|
|
|
40
32
|
const toShow = history.slice(0, limit);
|
|
41
33
|
const pendingCount = history.filter(h => h.pending).length;
|
|
42
34
|
const appliedCount = history.filter(h => !h.pending).length;
|
|
43
|
-
console.log(
|
|
44
|
-
console.log(`${
|
|
35
|
+
console.log(`Showing ${toShow.length} of ${history.length} migrations`);
|
|
36
|
+
console.log(`${appliedCount} applied, ${pendingCount} pending`);
|
|
45
37
|
console.log('');
|
|
46
38
|
for (const record of toShow) {
|
|
47
39
|
if (record.pending) {
|
|
@@ -74,8 +66,7 @@ export async function historyCommand(context) {
|
|
|
74
66
|
}
|
|
75
67
|
return;
|
|
76
68
|
}
|
|
77
|
-
|
|
78
|
-
process.exit(1);
|
|
69
|
+
fatal('Failed to load history', error instanceof Error ? error.message : String(error));
|
|
79
70
|
}
|
|
80
71
|
}
|
|
81
72
|
function getMigrationFiles(migrationsDir) {
|
|
@@ -87,29 +78,17 @@ function getMigrationFiles(migrationsDir) {
|
|
|
87
78
|
.sort();
|
|
88
79
|
}
|
|
89
80
|
async function getAppliedMigrations(connection, tableName) {
|
|
90
|
-
|
|
91
|
-
const pool = new Pool({
|
|
92
|
-
host: connection.host,
|
|
93
|
-
port: connection.port || 5432,
|
|
94
|
-
database: connection.database,
|
|
95
|
-
user: connection.user,
|
|
96
|
-
password: connection.password,
|
|
97
|
-
connectionString: connection.url,
|
|
98
|
-
});
|
|
99
|
-
try {
|
|
81
|
+
return withPool(connection, async (pool) => {
|
|
100
82
|
const result = await pool.query(`
|
|
101
83
|
SELECT name, applied_at
|
|
102
84
|
FROM "${tableName}"
|
|
103
85
|
ORDER BY id DESC;
|
|
104
86
|
`);
|
|
105
|
-
return result.rows.map(r => ({
|
|
87
|
+
return result.rows.map((r) => ({
|
|
106
88
|
name: r.name,
|
|
107
89
|
appliedAt: new Date(r.applied_at),
|
|
108
90
|
}));
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
await pool.end();
|
|
112
|
-
}
|
|
91
|
+
});
|
|
113
92
|
}
|
|
114
93
|
function formatDate(date) {
|
|
115
94
|
const now = new Date();
|