relq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +862 -0
- package/dist/addons/buffer.js +1869 -0
- package/dist/addons/pg-cursor.js +1425 -0
- package/dist/addons/pg-format.js +2248 -0
- package/dist/addons/pg.js +4790 -0
- package/dist/bin/relq.js +2 -0
- package/dist/cjs/cache/index.cjs +9 -0
- package/dist/cjs/cache/query-cache.cjs +311 -0
- package/dist/cjs/cli/commands/add.cjs +82 -0
- package/dist/cjs/cli/commands/commit.cjs +145 -0
- package/dist/cjs/cli/commands/diff.cjs +84 -0
- package/dist/cjs/cli/commands/export.cjs +333 -0
- package/dist/cjs/cli/commands/fetch.cjs +59 -0
- package/dist/cjs/cli/commands/generate.cjs +242 -0
- package/dist/cjs/cli/commands/history.cjs +165 -0
- package/dist/cjs/cli/commands/import.cjs +524 -0
- package/dist/cjs/cli/commands/init.cjs +437 -0
- package/dist/cjs/cli/commands/introspect.cjs +142 -0
- package/dist/cjs/cli/commands/log.cjs +62 -0
- package/dist/cjs/cli/commands/migrate.cjs +167 -0
- package/dist/cjs/cli/commands/pull.cjs +410 -0
- package/dist/cjs/cli/commands/push.cjs +165 -0
- package/dist/cjs/cli/commands/rollback.cjs +169 -0
- package/dist/cjs/cli/commands/status.cjs +110 -0
- package/dist/cjs/cli/commands/sync.cjs +79 -0
- package/dist/cjs/cli/index.cjs +275 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +446 -0
- package/dist/cjs/cli/utils/commit-manager.cjs +239 -0
- package/dist/cjs/cli/utils/config-loader.cjs +127 -0
- package/dist/cjs/cli/utils/env-loader.cjs +62 -0
- package/dist/cjs/cli/utils/fast-introspect.cjs +398 -0
- package/dist/cjs/cli/utils/git-utils.cjs +404 -0
- package/dist/cjs/cli/utils/migration-generator.cjs +269 -0
- package/dist/cjs/cli/utils/relqignore.cjs +114 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +515 -0
- package/dist/cjs/cli/utils/schema-comparator.cjs +313 -0
- package/dist/cjs/cli/utils/schema-diff.cjs +284 -0
- package/dist/cjs/cli/utils/schema-hash.cjs +108 -0
- package/dist/cjs/cli/utils/schema-introspect.cjs +455 -0
- package/dist/cjs/cli/utils/snapshot-manager.cjs +223 -0
- package/dist/cjs/cli/utils/spinner.cjs +108 -0
- package/dist/cjs/cli/utils/sql-generator.cjs +520 -0
- package/dist/cjs/cli/utils/sql-parser.cjs +999 -0
- package/dist/cjs/cli/utils/type-generator.cjs +2061 -0
- package/dist/cjs/condition/array-condition-builder.cjs +503 -0
- package/dist/cjs/condition/array-numeric-condition-builder.cjs +186 -0
- package/dist/cjs/condition/array-specialized-condition-builder.cjs +206 -0
- package/dist/cjs/condition/array-string-condition-builder.cjs +146 -0
- package/dist/cjs/condition/base-condition-builder.cjs +2 -0
- package/dist/cjs/condition/condition-collector.cjs +284 -0
- package/dist/cjs/condition/fulltext-condition-builder.cjs +61 -0
- package/dist/cjs/condition/geometric-condition-builder.cjs +208 -0
- package/dist/cjs/condition/index.cjs +25 -0
- package/dist/cjs/condition/jsonb-condition-builder.cjs +160 -0
- package/dist/cjs/condition/network-condition-builder.cjs +230 -0
- package/dist/cjs/condition/range-condition-builder.cjs +82 -0
- package/dist/cjs/config/config.cjs +190 -0
- package/dist/cjs/config/index.cjs +9 -0
- package/dist/cjs/constants/pg-values.cjs +68 -0
- package/dist/cjs/copy/copy-builder.cjs +316 -0
- package/dist/cjs/copy/index.cjs +6 -0
- package/dist/cjs/core/query-builder.cjs +440 -0
- package/dist/cjs/core/relq-client.cjs +1831 -0
- package/dist/cjs/core/typed-kuery-client.cjs +2 -0
- package/dist/cjs/count/count-builder.cjs +88 -0
- package/dist/cjs/count/index.cjs +5 -0
- package/dist/cjs/cte/cte-builder.cjs +89 -0
- package/dist/cjs/cte/index.cjs +5 -0
- package/dist/cjs/ddl/function.cjs +48 -0
- package/dist/cjs/ddl/index.cjs +7 -0
- package/dist/cjs/ddl/sql.cjs +54 -0
- package/dist/cjs/delete/delete-builder.cjs +135 -0
- package/dist/cjs/delete/index.cjs +5 -0
- package/dist/cjs/errors/relq-errors.cjs +329 -0
- package/dist/cjs/examples/fulltext-search-test.cjs +122 -0
- package/dist/cjs/explain/explain-builder.cjs +99 -0
- package/dist/cjs/explain/index.cjs +5 -0
- package/dist/cjs/function/create-function-builder.cjs +196 -0
- package/dist/cjs/function/index.cjs +6 -0
- package/dist/cjs/functions/advanced-functions.cjs +241 -0
- package/dist/cjs/functions/case-builder.cjs +66 -0
- package/dist/cjs/functions/geometric-functions.cjs +104 -0
- package/dist/cjs/functions/index.cjs +184 -0
- package/dist/cjs/functions/network-functions.cjs +86 -0
- package/dist/cjs/functions/sql-functions.cjs +431 -0
- package/dist/cjs/index.cjs +164 -0
- package/dist/cjs/indexing/create-index-builder.cjs +187 -0
- package/dist/cjs/indexing/drop-index-builder.cjs +89 -0
- package/dist/cjs/indexing/index-types.cjs +2 -0
- package/dist/cjs/indexing/index.cjs +8 -0
- package/dist/cjs/insert/conflict-builder.cjs +173 -0
- package/dist/cjs/insert/index.cjs +5 -0
- package/dist/cjs/insert/insert-builder.cjs +254 -0
- package/dist/cjs/introspect/index.cjs +229 -0
- package/dist/cjs/maintenance/index.cjs +6 -0
- package/dist/cjs/maintenance/vacuum-builder.cjs +166 -0
- package/dist/cjs/pubsub/index.cjs +7 -0
- package/dist/cjs/pubsub/listen-notify-builder.cjs +57 -0
- package/dist/cjs/pubsub/listener-connection.cjs +180 -0
- package/dist/cjs/raw/index.cjs +5 -0
- package/dist/cjs/raw/raw-query-builder.cjs +27 -0
- package/dist/cjs/schema/index.cjs +15 -0
- package/dist/cjs/schema/schema-builder.cjs +1167 -0
- package/dist/cjs/schema-builder.cjs +21 -0
- package/dist/cjs/schema-definition/column-types.cjs +829 -0
- package/dist/cjs/schema-definition/index.cjs +62 -0
- package/dist/cjs/schema-definition/introspection.cjs +620 -0
- package/dist/cjs/schema-definition/partitions.cjs +129 -0
- package/dist/cjs/schema-definition/pg-enum.cjs +76 -0
- package/dist/cjs/schema-definition/pg-function.cjs +91 -0
- package/dist/cjs/schema-definition/pg-sequence.cjs +56 -0
- package/dist/cjs/schema-definition/pg-trigger.cjs +108 -0
- package/dist/cjs/schema-definition/relations.cjs +98 -0
- package/dist/cjs/schema-definition/sql-expressions.cjs +202 -0
- package/dist/cjs/schema-definition/table-definition.cjs +636 -0
- package/dist/cjs/select/aggregate-builder.cjs +179 -0
- package/dist/cjs/select/index.cjs +5 -0
- package/dist/cjs/select/select-builder.cjs +233 -0
- package/dist/cjs/sequence/index.cjs +7 -0
- package/dist/cjs/sequence/sequence-builder.cjs +264 -0
- package/dist/cjs/table/alter-table-builder.cjs +146 -0
- package/dist/cjs/table/constraint-builder.cjs +102 -0
- package/dist/cjs/table/create-table-builder.cjs +248 -0
- package/dist/cjs/table/index.cjs +17 -0
- package/dist/cjs/table/partition-builder.cjs +131 -0
- package/dist/cjs/table/truncate-builder.cjs +70 -0
- package/dist/cjs/transaction/index.cjs +6 -0
- package/dist/cjs/transaction/transaction-builder.cjs +78 -0
- package/dist/cjs/trigger/create-trigger-builder.cjs +174 -0
- package/dist/cjs/trigger/index.cjs +6 -0
- package/dist/cjs/types/aggregate-types.cjs +2 -0
- package/dist/cjs/types/config-types.cjs +40 -0
- package/dist/cjs/types/inference-types.cjs +18 -0
- package/dist/cjs/types/pagination-types.cjs +7 -0
- package/dist/cjs/types/result-types.cjs +2 -0
- package/dist/cjs/types/schema-types.cjs +2 -0
- package/dist/cjs/types/subscription-types.cjs +2 -0
- package/dist/cjs/types.cjs +2 -0
- package/dist/cjs/update/array-update-builder.cjs +205 -0
- package/dist/cjs/update/index.cjs +13 -0
- package/dist/cjs/update/update-builder.cjs +195 -0
- package/dist/cjs/utils/case-converter.cjs +58 -0
- package/dist/cjs/utils/environment-detection.cjs +120 -0
- package/dist/cjs/utils/index.cjs +10 -0
- package/dist/cjs/utils/pool-defaults.cjs +106 -0
- package/dist/cjs/utils/type-coercion.cjs +118 -0
- package/dist/cjs/view/create-view-builder.cjs +180 -0
- package/dist/cjs/view/index.cjs +7 -0
- package/dist/cjs/window/index.cjs +5 -0
- package/dist/cjs/window/window-builder.cjs +80 -0
- package/dist/config.cjs +1 -0
- package/dist/config.d.ts +655 -0
- package/dist/config.js +1 -0
- package/dist/esm/cache/index.js +1 -0
- package/dist/esm/cache/query-cache.js +303 -0
- package/dist/esm/cli/commands/add.js +78 -0
- package/dist/esm/cli/commands/commit.js +109 -0
- package/dist/esm/cli/commands/diff.js +81 -0
- package/dist/esm/cli/commands/export.js +297 -0
- package/dist/esm/cli/commands/fetch.js +56 -0
- package/dist/esm/cli/commands/generate.js +206 -0
- package/dist/esm/cli/commands/history.js +129 -0
- package/dist/esm/cli/commands/import.js +488 -0
- package/dist/esm/cli/commands/init.js +401 -0
- package/dist/esm/cli/commands/introspect.js +106 -0
- package/dist/esm/cli/commands/log.js +59 -0
- package/dist/esm/cli/commands/migrate.js +131 -0
- package/dist/esm/cli/commands/pull.js +374 -0
- package/dist/esm/cli/commands/push.js +129 -0
- package/dist/esm/cli/commands/rollback.js +133 -0
- package/dist/esm/cli/commands/status.js +107 -0
- package/dist/esm/cli/commands/sync.js +76 -0
- package/dist/esm/cli/index.js +240 -0
- package/dist/esm/cli/utils/change-tracker.js +405 -0
- package/dist/esm/cli/utils/commit-manager.js +191 -0
- package/dist/esm/cli/utils/config-loader.js +86 -0
- package/dist/esm/cli/utils/env-loader.js +57 -0
- package/dist/esm/cli/utils/fast-introspect.js +362 -0
- package/dist/esm/cli/utils/git-utils.js +347 -0
- package/dist/esm/cli/utils/migration-generator.js +263 -0
- package/dist/esm/cli/utils/relqignore.js +74 -0
- package/dist/esm/cli/utils/repo-manager.js +444 -0
- package/dist/esm/cli/utils/schema-comparator.js +307 -0
- package/dist/esm/cli/utils/schema-diff.js +276 -0
- package/dist/esm/cli/utils/schema-hash.js +69 -0
- package/dist/esm/cli/utils/schema-introspect.js +418 -0
- package/dist/esm/cli/utils/snapshot-manager.js +179 -0
- package/dist/esm/cli/utils/spinner.js +101 -0
- package/dist/esm/cli/utils/sql-generator.js +504 -0
- package/dist/esm/cli/utils/sql-parser.js +992 -0
- package/dist/esm/cli/utils/type-generator.js +2058 -0
- package/dist/esm/condition/array-condition-builder.js +495 -0
- package/dist/esm/condition/array-numeric-condition-builder.js +182 -0
- package/dist/esm/condition/array-specialized-condition-builder.js +200 -0
- package/dist/esm/condition/array-string-condition-builder.js +142 -0
- package/dist/esm/condition/base-condition-builder.js +1 -0
- package/dist/esm/condition/condition-collector.js +275 -0
- package/dist/esm/condition/fulltext-condition-builder.js +53 -0
- package/dist/esm/condition/geometric-condition-builder.js +200 -0
- package/dist/esm/condition/index.js +7 -0
- package/dist/esm/condition/jsonb-condition-builder.js +152 -0
- package/dist/esm/condition/network-condition-builder.js +222 -0
- package/dist/esm/condition/range-condition-builder.js +74 -0
- package/dist/esm/config/config.js +150 -0
- package/dist/esm/config/index.js +1 -0
- package/dist/esm/constants/pg-values.js +63 -0
- package/dist/esm/copy/copy-builder.js +308 -0
- package/dist/esm/copy/index.js +1 -0
- package/dist/esm/core/query-builder.js +426 -0
- package/dist/esm/core/relq-client.js +1791 -0
- package/dist/esm/core/typed-kuery-client.js +1 -0
- package/dist/esm/count/count-builder.js +81 -0
- package/dist/esm/count/index.js +1 -0
- package/dist/esm/cte/cte-builder.js +82 -0
- package/dist/esm/cte/index.js +1 -0
- package/dist/esm/ddl/function.js +45 -0
- package/dist/esm/ddl/index.js +2 -0
- package/dist/esm/ddl/sql.js +51 -0
- package/dist/esm/delete/delete-builder.js +128 -0
- package/dist/esm/delete/index.js +1 -0
- package/dist/esm/errors/relq-errors.js +310 -0
- package/dist/esm/examples/fulltext-search-test.js +117 -0
- package/dist/esm/explain/explain-builder.js +95 -0
- package/dist/esm/explain/index.js +1 -0
- package/dist/esm/function/create-function-builder.js +188 -0
- package/dist/esm/function/index.js +1 -0
- package/dist/esm/functions/advanced-functions.js +231 -0
- package/dist/esm/functions/case-builder.js +58 -0
- package/dist/esm/functions/geometric-functions.js +97 -0
- package/dist/esm/functions/index.js +171 -0
- package/dist/esm/functions/network-functions.js +79 -0
- package/dist/esm/functions/sql-functions.js +421 -0
- package/dist/esm/index.js +34 -0
- package/dist/esm/indexing/create-index-builder.js +180 -0
- package/dist/esm/indexing/drop-index-builder.js +81 -0
- package/dist/esm/indexing/index-types.js +1 -0
- package/dist/esm/indexing/index.js +2 -0
- package/dist/esm/insert/conflict-builder.js +162 -0
- package/dist/esm/insert/index.js +1 -0
- package/dist/esm/insert/insert-builder.js +247 -0
- package/dist/esm/introspect/index.js +224 -0
- package/dist/esm/maintenance/index.js +1 -0
- package/dist/esm/maintenance/vacuum-builder.js +158 -0
- package/dist/esm/pubsub/index.js +1 -0
- package/dist/esm/pubsub/listen-notify-builder.js +48 -0
- package/dist/esm/pubsub/listener-connection.js +173 -0
- package/dist/esm/raw/index.js +1 -0
- package/dist/esm/raw/raw-query-builder.js +20 -0
- package/dist/esm/schema/index.js +1 -0
- package/dist/esm/schema/schema-builder.js +1150 -0
- package/dist/esm/schema-builder.js +2 -0
- package/dist/esm/schema-definition/column-types.js +738 -0
- package/dist/esm/schema-definition/index.js +10 -0
- package/dist/esm/schema-definition/introspection.js +614 -0
- package/dist/esm/schema-definition/partitions.js +123 -0
- package/dist/esm/schema-definition/pg-enum.js +70 -0
- package/dist/esm/schema-definition/pg-function.js +85 -0
- package/dist/esm/schema-definition/pg-sequence.js +50 -0
- package/dist/esm/schema-definition/pg-trigger.js +102 -0
- package/dist/esm/schema-definition/relations.js +90 -0
- package/dist/esm/schema-definition/sql-expressions.js +193 -0
- package/dist/esm/schema-definition/table-definition.js +630 -0
- package/dist/esm/select/aggregate-builder.js +172 -0
- package/dist/esm/select/index.js +1 -0
- package/dist/esm/select/select-builder.js +226 -0
- package/dist/esm/sequence/index.js +1 -0
- package/dist/esm/sequence/sequence-builder.js +255 -0
- package/dist/esm/table/alter-table-builder.js +138 -0
- package/dist/esm/table/constraint-builder.js +95 -0
- package/dist/esm/table/create-table-builder.js +241 -0
- package/dist/esm/table/index.js +5 -0
- package/dist/esm/table/partition-builder.js +121 -0
- package/dist/esm/table/truncate-builder.js +63 -0
- package/dist/esm/transaction/index.js +1 -0
- package/dist/esm/transaction/transaction-builder.js +70 -0
- package/dist/esm/trigger/create-trigger-builder.js +166 -0
- package/dist/esm/trigger/index.js +1 -0
- package/dist/esm/types/aggregate-types.js +1 -0
- package/dist/esm/types/config-types.js +36 -0
- package/dist/esm/types/inference-types.js +12 -0
- package/dist/esm/types/pagination-types.js +4 -0
- package/dist/esm/types/result-types.js +1 -0
- package/dist/esm/types/schema-types.js +1 -0
- package/dist/esm/types/subscription-types.js +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/update/array-update-builder.js +192 -0
- package/dist/esm/update/index.js +2 -0
- package/dist/esm/update/update-builder.js +188 -0
- package/dist/esm/utils/case-converter.js +55 -0
- package/dist/esm/utils/environment-detection.js +113 -0
- package/dist/esm/utils/index.js +2 -0
- package/dist/esm/utils/pool-defaults.js +100 -0
- package/dist/esm/utils/type-coercion.js +110 -0
- package/dist/esm/view/create-view-builder.js +171 -0
- package/dist/esm/view/index.js +1 -0
- package/dist/esm/window/index.js +1 -0
- package/dist/esm/window/window-builder.js +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +10341 -0
- package/dist/index.js +1 -0
- package/dist/schema-builder.cjs +1 -0
- package/dist/schema-builder.d.ts +2272 -0
- package/dist/schema-builder.js +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as readline from 'readline';
|
|
4
|
+
import { requireValidConfig } from "../utils/config-loader.js";
|
|
5
|
+
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
6
|
+
const colors = {
|
|
7
|
+
reset: '\x1b[0m',
|
|
8
|
+
bold: '\x1b[1m',
|
|
9
|
+
dim: '\x1b[2m',
|
|
10
|
+
red: '\x1b[31m',
|
|
11
|
+
green: '\x1b[32m',
|
|
12
|
+
yellow: '\x1b[33m',
|
|
13
|
+
cyan: '\x1b[36m',
|
|
14
|
+
};
|
|
15
|
+
function parseMigration(content) {
|
|
16
|
+
const upMatch = content.match(/--\s*UP\s*\n([\s\S]*?)(?=--\s*DOWN|$)/i);
|
|
17
|
+
const downMatch = content.match(/--\s*DOWN\s*\n([\s\S]*?)$/i);
|
|
18
|
+
return {
|
|
19
|
+
up: upMatch?.[1]?.trim() || '',
|
|
20
|
+
down: downMatch?.[1]?.trim() || '',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function askConfirm(question) {
|
|
24
|
+
const rl = readline.createInterface({
|
|
25
|
+
input: process.stdin,
|
|
26
|
+
output: process.stdout,
|
|
27
|
+
});
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
rl.question(`${question} [y/N]: `, (answer) => {
|
|
30
|
+
rl.close();
|
|
31
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export async function rollbackCommand(context) {
|
|
36
|
+
const { config, args, flags } = context;
|
|
37
|
+
if (!config) {
|
|
38
|
+
console.error('Error: No configuration found.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
requireValidConfig(config);
|
|
42
|
+
const connection = config.connection;
|
|
43
|
+
const migrationsDir = config.migrations?.directory || './migrations';
|
|
44
|
+
const tableName = config.migrations?.tableName || '_relq_migrations';
|
|
45
|
+
const count = parseInt(args[0]) || 1;
|
|
46
|
+
const dryRun = flags['dry-run'] === true;
|
|
47
|
+
const force = flags['force'] === true;
|
|
48
|
+
console.log(`${colors.bold}Rolling back ${count} migration(s)...${colors.reset}`);
|
|
49
|
+
console.log(` Connection: ${getConnectionDescription(connection)}`);
|
|
50
|
+
console.log('');
|
|
51
|
+
try {
|
|
52
|
+
const { Pool } = await import("../../addon/pg.js");
|
|
53
|
+
const pool = new Pool({
|
|
54
|
+
host: connection.host,
|
|
55
|
+
port: connection.port || 5432,
|
|
56
|
+
database: connection.database,
|
|
57
|
+
user: connection.user,
|
|
58
|
+
password: connection.password,
|
|
59
|
+
connectionString: connection.url,
|
|
60
|
+
});
|
|
61
|
+
try {
|
|
62
|
+
const result = await pool.query(`
|
|
63
|
+
SELECT name FROM "${tableName}"
|
|
64
|
+
ORDER BY id DESC
|
|
65
|
+
LIMIT $1;
|
|
66
|
+
`, [count]);
|
|
67
|
+
if (result.rows.length === 0) {
|
|
68
|
+
console.log(`${colors.yellow}No migrations to rollback.${colors.reset}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const toRollback = result.rows.map(r => r.name);
|
|
72
|
+
console.log(`${colors.bold}Migrations to rollback:${colors.reset}`);
|
|
73
|
+
for (const name of toRollback) {
|
|
74
|
+
console.log(` ${colors.red}• ${name}${colors.reset}`);
|
|
75
|
+
}
|
|
76
|
+
console.log('');
|
|
77
|
+
if (!force && !dryRun) {
|
|
78
|
+
const proceed = await askConfirm(`${colors.red}This will undo ${toRollback.length} migration(s). Continue?${colors.reset}`);
|
|
79
|
+
if (!proceed) {
|
|
80
|
+
console.log('Cancelled.');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const name of toRollback) {
|
|
85
|
+
const filePath = path.join(migrationsDir, name);
|
|
86
|
+
if (!fs.existsSync(filePath)) {
|
|
87
|
+
console.log(`${colors.yellow}⚠️ Migration file not found: ${name}${colors.reset}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
91
|
+
const { down } = parseMigration(content);
|
|
92
|
+
if (!down) {
|
|
93
|
+
console.log(`${colors.yellow}⚠️ No DOWN section in: ${name}${colors.reset}`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (dryRun) {
|
|
97
|
+
console.log(`${colors.cyan}[dry-run] Would rollback: ${name}${colors.reset}`);
|
|
98
|
+
console.log(`${colors.dim}${down}${colors.reset}`);
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log(`Rolling back: ${name}...`);
|
|
103
|
+
const client = await pool.connect();
|
|
104
|
+
try {
|
|
105
|
+
await client.query('BEGIN');
|
|
106
|
+
await client.query(down);
|
|
107
|
+
await client.query(`DELETE FROM "${tableName}" WHERE name = $1`, [name]);
|
|
108
|
+
await client.query('COMMIT');
|
|
109
|
+
console.log(` ${colors.green}✓ Rolled back${colors.reset}`);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
await client.query('ROLLBACK');
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
client.release();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!dryRun) {
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(`${colors.green}✓ Rolled back ${toRollback.length} migration(s).${colors.reset}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
await pool.end();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
2
|
+
import { colors } from "../utils/spinner.js";
|
|
3
|
+
import { isInitialized, getHead, loadCommit, shortHash, getStagedChanges, getUnstagedChanges, } from "../utils/repo-manager.js";
|
|
4
|
+
export async function statusCommand(context) {
|
|
5
|
+
const { config, flags } = context;
|
|
6
|
+
const projectRoot = process.cwd();
|
|
7
|
+
const connection = config?.connection;
|
|
8
|
+
console.log('');
|
|
9
|
+
if (!isInitialized(projectRoot)) {
|
|
10
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('to initialize.')}`);
|
|
13
|
+
console.log('');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const head = getHead(projectRoot);
|
|
17
|
+
const staged = getStagedChanges(projectRoot);
|
|
18
|
+
const unstaged = getUnstagedChanges(projectRoot);
|
|
19
|
+
if (head) {
|
|
20
|
+
const headCommit = loadCommit(head, projectRoot);
|
|
21
|
+
console.log(`On commit ${colors.yellow(shortHash(head))}`);
|
|
22
|
+
if (headCommit) {
|
|
23
|
+
console.log(`${colors.muted(`"${headCommit.message}"`)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log(`${colors.muted('No commits yet')}`);
|
|
28
|
+
}
|
|
29
|
+
if (connection) {
|
|
30
|
+
console.log(`Database: ${colors.cyan(getConnectionDescription(connection))}`);
|
|
31
|
+
}
|
|
32
|
+
console.log('');
|
|
33
|
+
if (staged.length > 0) {
|
|
34
|
+
console.log(`${colors.green('Changes to be committed:')}`);
|
|
35
|
+
console.log(` ${colors.muted('(use "relq restore --staged <name>..." to unstage)')}`);
|
|
36
|
+
console.log('');
|
|
37
|
+
displayChanges(staged, ' ');
|
|
38
|
+
console.log('');
|
|
39
|
+
}
|
|
40
|
+
if (unstaged.length > 0) {
|
|
41
|
+
console.log(`${colors.red('Changes not staged for commit:')}`);
|
|
42
|
+
console.log(` ${colors.muted('(use "relq add <name>..." to stage)')}`);
|
|
43
|
+
console.log(` ${colors.muted('(use "relq restore <name>..." to discard)')}`);
|
|
44
|
+
console.log('');
|
|
45
|
+
displayChanges(unstaged, ' ');
|
|
46
|
+
console.log('');
|
|
47
|
+
}
|
|
48
|
+
if (staged.length === 0 && unstaged.length === 0) {
|
|
49
|
+
console.log(`${colors.green('nothing to commit, working tree clean')}`);
|
|
50
|
+
console.log('');
|
|
51
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq pull')} ${colors.muted('to sync with database.')}`);
|
|
52
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq import <file>')} ${colors.muted('to import SQL schema.')}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
if (staged.length > 0 && unstaged.length === 0) {
|
|
56
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq commit -m "message"')} ${colors.muted('to commit.')}`);
|
|
57
|
+
}
|
|
58
|
+
else if (unstaged.length > 0) {
|
|
59
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq add .')} ${colors.muted('to stage all changes.')}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
console.log('');
|
|
63
|
+
}
|
|
64
|
+
function displayChanges(changes, indent = '') {
|
|
65
|
+
const byType = new Map();
|
|
66
|
+
for (const change of changes) {
|
|
67
|
+
const key = change.objectType;
|
|
68
|
+
if (!byType.has(key)) {
|
|
69
|
+
byType.set(key, []);
|
|
70
|
+
}
|
|
71
|
+
byType.get(key).push(change);
|
|
72
|
+
}
|
|
73
|
+
const order = ['TABLE', 'COLUMN', 'INDEX', 'CONSTRAINT', 'PRIMARY_KEY', 'FOREIGN_KEY', 'CHECK', 'EXCLUSION', 'EXTENSION', 'ENUM', 'DOMAIN', 'FUNCTION', 'TRIGGER', 'VIEW'];
|
|
74
|
+
const sorted = [...byType.entries()].sort((a, b) => {
|
|
75
|
+
const aIdx = order.indexOf(a[0]);
|
|
76
|
+
const bIdx = order.indexOf(b[0]);
|
|
77
|
+
return (aIdx === -1 ? 999 : aIdx) - (bIdx === -1 ? 999 : bIdx);
|
|
78
|
+
});
|
|
79
|
+
for (const [type, typeChanges] of sorted) {
|
|
80
|
+
for (const change of typeChanges) {
|
|
81
|
+
const color = change.type === 'CREATE' ? colors.green :
|
|
82
|
+
change.type === 'DROP' ? colors.red :
|
|
83
|
+
colors.yellow;
|
|
84
|
+
const typeLabel = getTypeLabel(change.type, change.objectType);
|
|
85
|
+
const objectName = change.parentName
|
|
86
|
+
? `${change.parentName}.${change.objectName}`
|
|
87
|
+
: change.objectName;
|
|
88
|
+
console.log(`${indent}${color(typeLabel.padEnd(16))} ${objectName}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function getTypeLabel(changeType, objectType) {
|
|
93
|
+
const objectName = objectType.toLowerCase().replace(/_/g, ' ');
|
|
94
|
+
switch (changeType) {
|
|
95
|
+
case 'CREATE':
|
|
96
|
+
return `new ${objectName}:`;
|
|
97
|
+
case 'DROP':
|
|
98
|
+
return `deleted:`;
|
|
99
|
+
case 'ALTER':
|
|
100
|
+
return `modified:`;
|
|
101
|
+
case 'RENAME':
|
|
102
|
+
return `renamed:`;
|
|
103
|
+
default:
|
|
104
|
+
return `${changeType.toLowerCase()}:`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export default statusCommand;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { requireValidConfig } from "../utils/config-loader.js";
|
|
2
|
+
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
3
|
+
import { colors, createSpinner } from "../utils/spinner.js";
|
|
4
|
+
import { isInitialized, fetchRemoteCommits, pushCommit, ensureRemoteTable, getAllCommits, getRepoStatus, } from "../utils/repo-manager.js";
|
|
5
|
+
import { pullCommand } from "./pull.js";
|
|
6
|
+
export async function syncCommand(context) {
|
|
7
|
+
const { config, flags } = context;
|
|
8
|
+
if (!config) {
|
|
9
|
+
console.error('Error: No configuration found.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
requireValidConfig(config);
|
|
13
|
+
const connection = config.connection;
|
|
14
|
+
const projectRoot = process.cwd();
|
|
15
|
+
console.log('');
|
|
16
|
+
if (!isInitialized(projectRoot)) {
|
|
17
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(`${colors.muted('Run')} ${colors.cyan('relq init')} ${colors.muted('to initialize.')}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const spinner = createSpinner();
|
|
23
|
+
try {
|
|
24
|
+
spinner.start('Checking sync status...');
|
|
25
|
+
const status = await getRepoStatus(connection, projectRoot);
|
|
26
|
+
spinner.stop();
|
|
27
|
+
console.log(`On database: ${colors.cyan(getConnectionDescription(connection))}`);
|
|
28
|
+
console.log('');
|
|
29
|
+
if (status.behindBy > 0) {
|
|
30
|
+
console.log(`${colors.yellow('↓')} ${status.behindBy} commit(s) to pull`);
|
|
31
|
+
}
|
|
32
|
+
if (status.aheadBy > 0) {
|
|
33
|
+
console.log(`${colors.green('↑')} ${status.aheadBy} commit(s) to push`);
|
|
34
|
+
}
|
|
35
|
+
if (status.behindBy === 0 && status.aheadBy === 0) {
|
|
36
|
+
console.log(`${colors.green('✓')} Already in sync`);
|
|
37
|
+
console.log('');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.log('');
|
|
41
|
+
if (status.behindBy > 0) {
|
|
42
|
+
console.log(`${colors.cyan('●')} Pulling changes...`);
|
|
43
|
+
console.log('');
|
|
44
|
+
const pullContext = {
|
|
45
|
+
...context,
|
|
46
|
+
flags: { ...flags, force: true },
|
|
47
|
+
};
|
|
48
|
+
await pullCommand(pullContext);
|
|
49
|
+
}
|
|
50
|
+
if (status.aheadBy > 0) {
|
|
51
|
+
console.log(`${colors.cyan('●')} Pushing commits...`);
|
|
52
|
+
console.log('');
|
|
53
|
+
await ensureRemoteTable(connection);
|
|
54
|
+
const localCommits = getAllCommits(projectRoot);
|
|
55
|
+
const remoteCommits = await fetchRemoteCommits(connection, 100);
|
|
56
|
+
const remoteHashes = new Set(remoteCommits.map(c => c.hash));
|
|
57
|
+
const toPush = localCommits.filter(c => !remoteHashes.has(c.hash));
|
|
58
|
+
spinner.start(`Pushing ${toPush.length} commit(s)...`);
|
|
59
|
+
for (const commit of toPush.reverse()) {
|
|
60
|
+
await pushCommit(connection, commit);
|
|
61
|
+
}
|
|
62
|
+
spinner.succeed(`Pushed ${toPush.length} commit(s)`);
|
|
63
|
+
}
|
|
64
|
+
console.log('');
|
|
65
|
+
const pulledText = status.behindBy > 0 ? `${colors.yellow(`↓ ${status.behindBy}`)} pulled` : '';
|
|
66
|
+
const pushedText = status.aheadBy > 0 ? `${colors.green(`↑ ${status.aheadBy}`)} pushed` : '';
|
|
67
|
+
const separator = pulledText && pushedText ? ' | ' : '';
|
|
68
|
+
console.log(`${colors.green('✓')} Sync complete: ${pulledText}${separator}${pushedText}`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
spinner.fail('Sync failed');
|
|
72
|
+
console.error(colors.red(`Error: ${error instanceof Error ? error.message : error}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { initCommand } from "./commands/init.js";
|
|
3
|
+
import { pullCommand } from "./commands/pull.js";
|
|
4
|
+
import { pushCommand } from "./commands/push.js";
|
|
5
|
+
import { generateCommand } from "./commands/generate.js";
|
|
6
|
+
import { introspectCommand } from "./commands/introspect.js";
|
|
7
|
+
import { syncCommand } from "./commands/sync.js";
|
|
8
|
+
import { statusCommand } from "./commands/status.js";
|
|
9
|
+
import { diffCommand } from "./commands/diff.js";
|
|
10
|
+
import { logCommand } from "./commands/log.js";
|
|
11
|
+
import { rollbackCommand } from "./commands/rollback.js";
|
|
12
|
+
import { commitCommand } from "./commands/commit.js";
|
|
13
|
+
import { fetchCommand } from "./commands/fetch.js";
|
|
14
|
+
import { addCommand } from "./commands/add.js";
|
|
15
|
+
import { importCommand } from "./commands/import.js";
|
|
16
|
+
import { exportCommand } from "./commands/export.js";
|
|
17
|
+
const VERSION = '1.1.0';
|
|
18
|
+
function parseArgs(argv) {
|
|
19
|
+
const args = [];
|
|
20
|
+
const flags = {};
|
|
21
|
+
let command = '';
|
|
22
|
+
for (let i = 2; i < argv.length; i++) {
|
|
23
|
+
const arg = argv[i];
|
|
24
|
+
if (arg.startsWith('--')) {
|
|
25
|
+
const eqIndex = arg.indexOf('=');
|
|
26
|
+
if (eqIndex > -1) {
|
|
27
|
+
flags[arg.slice(2, eqIndex)] = arg.slice(eqIndex + 1);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
flags[arg.slice(2)] = true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (arg.startsWith('-') && arg.length === 2) {
|
|
34
|
+
const flag = arg.slice(1);
|
|
35
|
+
const nextArg = argv[i + 1];
|
|
36
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
37
|
+
flags[flag] = nextArg;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
flags[flag] = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (arg.startsWith('-') && arg.length > 2) {
|
|
45
|
+
const combined = arg.slice(1);
|
|
46
|
+
const nextArg = argv[i + 1];
|
|
47
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
48
|
+
flags[combined] = nextArg;
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
for (const char of combined) {
|
|
53
|
+
flags[char] = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (!command) {
|
|
58
|
+
command = arg;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
args.push(arg);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { command, args, flags };
|
|
65
|
+
}
|
|
66
|
+
function printHelp() {
|
|
67
|
+
console.log(`
|
|
68
|
+
Relq CLI v${VERSION} - Database Schema Version Control
|
|
69
|
+
|
|
70
|
+
Usage: relq <command> [options]
|
|
71
|
+
|
|
72
|
+
Schema Commands:
|
|
73
|
+
init Initialize relq for your project
|
|
74
|
+
status [--check] Show current schema state
|
|
75
|
+
add <table>|. Stage changes for commit
|
|
76
|
+
commit -m "msg" Record schema snapshot
|
|
77
|
+
log [--oneline] Show commit history
|
|
78
|
+
|
|
79
|
+
Sync Commands:
|
|
80
|
+
fetch Download remote commits
|
|
81
|
+
pull [--force] Pull schema from database
|
|
82
|
+
push [--dry-run] Push commits to database
|
|
83
|
+
sync Pull + push in one command
|
|
84
|
+
|
|
85
|
+
Other Commands:
|
|
86
|
+
diff [--sql] Show schema differences
|
|
87
|
+
generate Generate TypeScript types
|
|
88
|
+
introspect Parse database schema
|
|
89
|
+
import <sql-file> Import SQL file to schema
|
|
90
|
+
export [file] Export schema to SQL file
|
|
91
|
+
|
|
92
|
+
Options:
|
|
93
|
+
--help, -h Show this help
|
|
94
|
+
--version, -v Show version
|
|
95
|
+
--config <path> Config file path
|
|
96
|
+
--dry-run Preview changes
|
|
97
|
+
--force Skip confirmations
|
|
98
|
+
--force Skip confirmation prompts
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
relq status
|
|
102
|
+
relq diff --sql
|
|
103
|
+
relq generate -m "add users table"
|
|
104
|
+
relq push
|
|
105
|
+
relq rollback 2
|
|
106
|
+
relq sync
|
|
107
|
+
|
|
108
|
+
Environment Variables:
|
|
109
|
+
DATABASE_CONNECTION_STRING Full connection URL
|
|
110
|
+
DATABASE_HOST/PORT/NAME Individual connection parts
|
|
111
|
+
DATABASE_USER/PASSWORD Credentials
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
function printVersion() {
|
|
115
|
+
console.log(`relq v${VERSION}`);
|
|
116
|
+
}
|
|
117
|
+
function requiresConfig(command) {
|
|
118
|
+
return !['init', 'introspect', 'import', 'help', 'version'].includes(command);
|
|
119
|
+
}
|
|
120
|
+
function requiresDbConnection(command, flags) {
|
|
121
|
+
const alwaysNeedDb = ['pull', 'push', 'fetch', 'introspect'];
|
|
122
|
+
if (alwaysNeedDb.includes(command))
|
|
123
|
+
return true;
|
|
124
|
+
if (command === 'export' && flags['db'])
|
|
125
|
+
return true;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
async function main() {
|
|
129
|
+
const { command, args, flags } = parseArgs(process.argv);
|
|
130
|
+
if (flags.help || flags.h || command === 'help') {
|
|
131
|
+
printHelp();
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
if (flags.version || flags.v || command === 'version') {
|
|
135
|
+
printVersion();
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
if (!command) {
|
|
139
|
+
console.error('Error: No command provided\n');
|
|
140
|
+
printHelp();
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
let config = null;
|
|
144
|
+
if (requiresConfig(command)) {
|
|
145
|
+
const configPath = flags.config;
|
|
146
|
+
try {
|
|
147
|
+
const { loadConfigWithEnv, findConfigFile } = await import("./utils/config-loader.js");
|
|
148
|
+
const foundConfig = configPath || findConfigFile();
|
|
149
|
+
if (!foundConfig) {
|
|
150
|
+
console.error('Error: relq.config.ts not found.');
|
|
151
|
+
console.error('Run "relq init" to create one or use --config to specify a path.');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
config = await loadConfigWithEnv(configPath);
|
|
155
|
+
if (requiresDbConnection(command, flags) && !config.connection?.host && !config.connection?.url) {
|
|
156
|
+
console.error('Error: No database connection configured.');
|
|
157
|
+
console.error('Run "relq init" or set DATABASE_* environment variables.');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error('Error loading config:', error instanceof Error ? error.message : error);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const context = { config, args, flags };
|
|
167
|
+
try {
|
|
168
|
+
switch (command) {
|
|
169
|
+
case 'init':
|
|
170
|
+
await initCommand(context);
|
|
171
|
+
break;
|
|
172
|
+
case 'status':
|
|
173
|
+
await statusCommand(context);
|
|
174
|
+
break;
|
|
175
|
+
case 'diff':
|
|
176
|
+
await diffCommand(context);
|
|
177
|
+
break;
|
|
178
|
+
case 'pull':
|
|
179
|
+
await pullCommand(context);
|
|
180
|
+
break;
|
|
181
|
+
case 'commit':
|
|
182
|
+
await commitCommand(context);
|
|
183
|
+
break;
|
|
184
|
+
case 'add':
|
|
185
|
+
await addCommand(context);
|
|
186
|
+
break;
|
|
187
|
+
case 'generate':
|
|
188
|
+
await generateCommand(context);
|
|
189
|
+
break;
|
|
190
|
+
case 'push':
|
|
191
|
+
await pushCommand(context);
|
|
192
|
+
break;
|
|
193
|
+
case 'history':
|
|
194
|
+
case 'log':
|
|
195
|
+
await logCommand(context);
|
|
196
|
+
break;
|
|
197
|
+
case 'fetch':
|
|
198
|
+
await fetchCommand(context);
|
|
199
|
+
break;
|
|
200
|
+
case 'rollback':
|
|
201
|
+
case 'revert':
|
|
202
|
+
await rollbackCommand(context);
|
|
203
|
+
break;
|
|
204
|
+
case 'introspect':
|
|
205
|
+
await introspectCommand(context);
|
|
206
|
+
break;
|
|
207
|
+
case 'sync':
|
|
208
|
+
await syncCommand(context);
|
|
209
|
+
break;
|
|
210
|
+
case 'import':
|
|
211
|
+
await importCommand(args[0], {
|
|
212
|
+
output: flags.output,
|
|
213
|
+
includeFunctions: Boolean(flags['include-functions']),
|
|
214
|
+
includeTriggers: Boolean(flags['include-triggers']),
|
|
215
|
+
force: Boolean(flags.force),
|
|
216
|
+
dryRun: Boolean(flags['dry-run']),
|
|
217
|
+
theirs: Boolean(flags.theirs),
|
|
218
|
+
ours: Boolean(flags.ours),
|
|
219
|
+
abort: Boolean(flags.abort),
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
case 'export':
|
|
223
|
+
await exportCommand(context);
|
|
224
|
+
break;
|
|
225
|
+
case 'migrate':
|
|
226
|
+
console.log('Note: "migrate" is deprecated, use "push" instead.\n');
|
|
227
|
+
await pushCommand(context);
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
console.error(`Error: Unknown command "${command}"\n`);
|
|
231
|
+
printHelp();
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
main().catch(console.error);
|