relq 1.0.25 → 1.0.27
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/cli/commands/commit.cjs +80 -0
- package/dist/cjs/cli/commands/import.cjs +1 -0
- package/dist/cjs/cli/commands/pull.cjs +108 -34
- package/dist/cjs/cli/commands/push.cjs +48 -8
- package/dist/cjs/cli/commands/rollback.cjs +205 -84
- package/dist/cjs/cli/commands/schema-ast.cjs +219 -0
- package/dist/cjs/cli/index.cjs +6 -0
- package/dist/cjs/cli/utils/ast-codegen.cjs +95 -3
- package/dist/cjs/cli/utils/ast-transformer.cjs +12 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +135 -0
- package/dist/cjs/cli/utils/commit-manager.cjs +54 -0
- package/dist/cjs/cli/utils/migration-generator.cjs +319 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +99 -3
- package/dist/cjs/cli/utils/schema-diff.cjs +390 -0
- package/dist/cjs/cli/utils/schema-hash.cjs +4 -0
- package/dist/cjs/cli/utils/schema-to-ast.cjs +477 -0
- package/dist/cjs/schema-definition/column-types.cjs +50 -4
- package/dist/cjs/schema-definition/pg-enum.cjs +10 -0
- package/dist/cjs/schema-definition/pg-function.cjs +19 -0
- package/dist/cjs/schema-definition/pg-sequence.cjs +22 -1
- package/dist/cjs/schema-definition/pg-trigger.cjs +39 -0
- package/dist/cjs/schema-definition/pg-view.cjs +17 -0
- package/dist/cjs/schema-definition/sql-expressions.cjs +3 -0
- package/dist/cjs/schema-definition/table-definition.cjs +4 -0
- package/dist/config.d.ts +98 -0
- package/dist/esm/cli/commands/commit.js +83 -3
- package/dist/esm/cli/commands/import.js +1 -0
- package/dist/esm/cli/commands/pull.js +109 -35
- package/dist/esm/cli/commands/push.js +49 -9
- package/dist/esm/cli/commands/rollback.js +206 -85
- package/dist/esm/cli/commands/schema-ast.js +183 -0
- package/dist/esm/cli/index.js +6 -0
- package/dist/esm/cli/utils/ast-codegen.js +93 -3
- package/dist/esm/cli/utils/ast-transformer.js +12 -0
- package/dist/esm/cli/utils/change-tracker.js +134 -0
- package/dist/esm/cli/utils/commit-manager.js +51 -0
- package/dist/esm/cli/utils/migration-generator.js +318 -0
- package/dist/esm/cli/utils/repo-manager.js +96 -3
- package/dist/esm/cli/utils/schema-diff.js +389 -0
- package/dist/esm/cli/utils/schema-hash.js +4 -0
- package/dist/esm/cli/utils/schema-to-ast.js +447 -0
- package/dist/esm/schema-definition/column-types.js +50 -4
- package/dist/esm/schema-definition/pg-enum.js +10 -0
- package/dist/esm/schema-definition/pg-function.js +19 -0
- package/dist/esm/schema-definition/pg-sequence.js +22 -1
- package/dist/esm/schema-definition/pg-trigger.js +39 -0
- package/dist/esm/schema-definition/pg-view.js +17 -0
- package/dist/esm/schema-definition/sql-expressions.js +3 -0
- package/dist/esm/schema-definition/table-definition.js +4 -0
- package/dist/index.d.ts +98 -0
- package/dist/schema-builder.d.ts +223 -24
- package/package.json +1 -1
|
@@ -39,109 +39,230 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const config_loader_1 = require("../utils/config-loader.cjs");
|
|
40
40
|
const env_loader_1 = require("../utils/env-loader.cjs");
|
|
41
41
|
const cli_utils_1 = require("../utils/cli-utils.cjs");
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
up: upMatch?.[1]?.trim() || '',
|
|
47
|
-
down: downMatch?.[1]?.trim() || '',
|
|
48
|
-
};
|
|
49
|
-
}
|
|
42
|
+
const repo_manager_1 = require("../utils/repo-manager.cjs");
|
|
43
|
+
const schema_diff_1 = require("../utils/schema-diff.cjs");
|
|
44
|
+
const migration_generator_1 = require("../utils/migration-generator.cjs");
|
|
50
45
|
async function rollbackCommand(context) {
|
|
51
46
|
const { config, args, flags } = context;
|
|
52
47
|
if (!config) {
|
|
53
|
-
(0, cli_utils_1.fatal)('No configuration found', `
|
|
54
|
-
return;
|
|
48
|
+
(0, cli_utils_1.fatal)('No configuration found', `Run ${cli_utils_1.colors.cyan('relq init')} to create one.`);
|
|
55
49
|
}
|
|
56
50
|
await (0, config_loader_1.requireValidConfig)(config, { calledFrom: 'rollback' });
|
|
57
51
|
const connection = config.connection;
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const dryRun = flags['dry-run'] === true;
|
|
62
|
-
const force = flags['force'] === true;
|
|
63
|
-
console.log(`Rolling back ${count} migration(s)...`);
|
|
64
|
-
console.log(` Connection: ${(0, env_loader_1.getConnectionDescription)(connection)}`);
|
|
52
|
+
const { projectRoot } = context;
|
|
53
|
+
const preview = flags['preview'] === true || flags['dry-run'] === true;
|
|
54
|
+
const skipPrompt = flags['yes'] === true || flags['y'] === true;
|
|
65
55
|
console.log('');
|
|
56
|
+
if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
|
|
57
|
+
(0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${cli_utils_1.colors.cyan('relq init')} to initialize.`);
|
|
58
|
+
}
|
|
59
|
+
const localHead = (0, repo_manager_1.getHead)(projectRoot);
|
|
60
|
+
if (!localHead) {
|
|
61
|
+
(0, cli_utils_1.fatal)('no commits to rollback', 'Repository has no commits.');
|
|
62
|
+
}
|
|
63
|
+
const targetRef = args[0] || 'HEAD~1';
|
|
64
|
+
const target = resolveTarget(projectRoot, targetRef, localHead);
|
|
65
|
+
if (!target) {
|
|
66
|
+
(0, cli_utils_1.fatal)(`invalid rollback target: ${targetRef}`, 'Use a commit hash or HEAD~N notation.');
|
|
67
|
+
}
|
|
68
|
+
if (target.commitsToRollback.length === 0) {
|
|
69
|
+
console.log('Nothing to rollback - already at target commit.');
|
|
70
|
+
console.log('');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const spinner = (0, cli_utils_1.createSpinner)();
|
|
66
74
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
75
|
+
console.log(`Rolling back to ${cli_utils_1.colors.yellow((0, repo_manager_1.shortHash)(target.hash))}`);
|
|
76
|
+
console.log(`${cli_utils_1.colors.muted(target.message)}`);
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(`${cli_utils_1.colors.red('Commits to rollback:')} ${target.commitsToRollback.length}`);
|
|
79
|
+
for (const commit of target.commitsToRollback.slice(0, 5)) {
|
|
80
|
+
console.log(` ${cli_utils_1.colors.red('↩')} ${(0, repo_manager_1.shortHash)(commit.hash)} ${commit.message}`);
|
|
81
|
+
}
|
|
82
|
+
if (target.commitsToRollback.length > 5) {
|
|
83
|
+
console.log(` ${cli_utils_1.colors.muted(`... and ${target.commitsToRollback.length - 5} more`)}`);
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
const currentCommitPath = path.join(projectRoot, '.relq', 'commits', `${localHead}.json`);
|
|
87
|
+
const targetCommitPath = path.join(projectRoot, '.relq', 'commits', `${target.hash}.json`);
|
|
88
|
+
if (!fs.existsSync(currentCommitPath)) {
|
|
89
|
+
(0, cli_utils_1.fatal)(`current commit not found: ${localHead}`);
|
|
90
|
+
}
|
|
91
|
+
if (!fs.existsSync(targetCommitPath)) {
|
|
92
|
+
(0, cli_utils_1.fatal)(`target commit not found: ${target.hash}`);
|
|
93
|
+
}
|
|
94
|
+
const currentCommit = JSON.parse(fs.readFileSync(currentCommitPath, 'utf-8'));
|
|
95
|
+
const targetCommit = JSON.parse(fs.readFileSync(targetCommitPath, 'utf-8'));
|
|
96
|
+
let rollbackSQL = [];
|
|
97
|
+
if (currentCommit.schemaAST && targetCommit.schemaAST) {
|
|
98
|
+
const comparison = (0, schema_diff_1.compareSchemas)(currentCommit.schemaAST, targetCommit.schemaAST);
|
|
99
|
+
const migration = (0, migration_generator_1.generateMigrationFromComparison)(comparison);
|
|
100
|
+
rollbackSQL = migration.down;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
(0, cli_utils_1.warning)('No schema AST found - using commit SQL reversal (may be incomplete)');
|
|
91
104
|
console.log('');
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
for (const commit of target.commitsToRollback) {
|
|
106
|
+
const commitPath = path.join(projectRoot, '.relq', 'commits', `${commit.hash}.json`);
|
|
107
|
+
if (fs.existsSync(commitPath)) {
|
|
108
|
+
const enhancedCommit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
109
|
+
if (enhancedCommit.downSQL) {
|
|
110
|
+
rollbackSQL.push(enhancedCommit.downSQL);
|
|
111
|
+
}
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
}
|
|
115
|
+
if (rollbackSQL.length === 0) {
|
|
116
|
+
(0, cli_utils_1.warning)('No rollback SQL generated - manual intervention may be required');
|
|
117
|
+
console.log('');
|
|
118
|
+
}
|
|
119
|
+
if (preview) {
|
|
120
|
+
console.log(`${cli_utils_1.colors.yellow('Preview')} - showing rollback SQL`);
|
|
121
|
+
console.log('');
|
|
122
|
+
if (rollbackSQL.length > 0) {
|
|
123
|
+
for (const sql of rollbackSQL.slice(0, 10)) {
|
|
124
|
+
console.log(` ${sql.substring(0, 100)}${sql.length > 100 ? '...' : ''}`);
|
|
110
125
|
}
|
|
111
|
-
if (
|
|
112
|
-
console.log(`
|
|
113
|
-
console.log(down);
|
|
114
|
-
console.log('');
|
|
126
|
+
if (rollbackSQL.length > 10) {
|
|
127
|
+
console.log(` ${cli_utils_1.colors.muted(`... and ${rollbackSQL.length - 10} more statements`)}`);
|
|
115
128
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
console.log(` ${cli_utils_1.colors.muted('(no SQL statements)')}`);
|
|
132
|
+
}
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log(`${cli_utils_1.colors.muted('Remove')} ${cli_utils_1.colors.cyan('--preview')} ${cli_utils_1.colors.muted('to execute rollback.')}`);
|
|
135
|
+
console.log('');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!skipPrompt) {
|
|
139
|
+
(0, cli_utils_1.warning)('This will modify your database!');
|
|
140
|
+
console.log('');
|
|
141
|
+
const confirmed = await (0, cli_utils_1.confirm)(`Rollback ${target.commitsToRollback.length} commit(s)?`, false);
|
|
142
|
+
if (!confirmed) {
|
|
143
|
+
(0, cli_utils_1.fatal)('Rollback cancelled by user');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
spinner.start('Connecting to remote...');
|
|
147
|
+
await (0, repo_manager_1.ensureRemoteTable)(connection);
|
|
148
|
+
spinner.succeed(`Connected to ${cli_utils_1.colors.cyan((0, env_loader_1.getConnectionDescription)(connection))}`);
|
|
149
|
+
if (rollbackSQL.length > 0) {
|
|
150
|
+
spinner.start('Executing rollback...');
|
|
151
|
+
const pg = await Promise.resolve().then(() => __importStar(require("../../addon/pg/index.cjs")));
|
|
152
|
+
const client = new pg.Client({
|
|
153
|
+
host: connection.host,
|
|
154
|
+
port: connection.port,
|
|
155
|
+
database: connection.database,
|
|
156
|
+
user: connection.user,
|
|
157
|
+
password: connection.password,
|
|
158
|
+
});
|
|
159
|
+
try {
|
|
160
|
+
await client.connect();
|
|
161
|
+
await client.query('BEGIN');
|
|
162
|
+
let statementsRun = 0;
|
|
163
|
+
for (const sql of rollbackSQL) {
|
|
164
|
+
if (sql.trim()) {
|
|
165
|
+
await client.query(sql);
|
|
166
|
+
statementsRun++;
|
|
132
167
|
}
|
|
133
168
|
}
|
|
169
|
+
await client.query('COMMIT');
|
|
170
|
+
spinner.succeed(`Executed ${statementsRun} rollback statement(s)`);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
try {
|
|
174
|
+
await client.query('ROLLBACK');
|
|
175
|
+
spinner.fail('Rollback failed - transaction rolled back');
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
spinner.fail('Rollback failed');
|
|
179
|
+
}
|
|
180
|
+
throw error;
|
|
134
181
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.log(`Rolled back ${toRollback.length} migration(s).`);
|
|
182
|
+
finally {
|
|
183
|
+
await client.end();
|
|
138
184
|
}
|
|
139
185
|
}
|
|
140
|
-
|
|
141
|
-
|
|
186
|
+
spinner.start('Updating commit status...');
|
|
187
|
+
for (const commit of target.commitsToRollback) {
|
|
188
|
+
await (0, repo_manager_1.markCommitAsRolledBack)(connection, commit.hash);
|
|
189
|
+
}
|
|
190
|
+
spinner.succeed('Marked commits as rolled back');
|
|
191
|
+
const headPath = path.join(projectRoot, '.relq', 'HEAD');
|
|
192
|
+
fs.writeFileSync(headPath, target.hash);
|
|
193
|
+
if (targetCommit.schema) {
|
|
194
|
+
const snapshotPath = path.join(projectRoot, '.relq', 'snapshot.json');
|
|
195
|
+
fs.writeFileSync(snapshotPath, JSON.stringify(targetCommit.schema, null, 2));
|
|
142
196
|
}
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log(`${cli_utils_1.colors.green('Rollback complete')}`);
|
|
199
|
+
console.log(` ${(0, repo_manager_1.shortHash)(localHead)} → ${(0, repo_manager_1.shortHash)(target.hash)}`);
|
|
200
|
+
console.log('');
|
|
143
201
|
}
|
|
144
|
-
catch (
|
|
145
|
-
|
|
202
|
+
catch (err) {
|
|
203
|
+
spinner.fail('Rollback failed');
|
|
204
|
+
(0, cli_utils_1.fatal)(err instanceof Error ? err.message : String(err));
|
|
146
205
|
}
|
|
147
206
|
}
|
|
207
|
+
function resolveTarget(projectRoot, ref, currentHead) {
|
|
208
|
+
const commits = (0, repo_manager_1.getAllCommits)(projectRoot);
|
|
209
|
+
const commitsByHash = new Map(commits.map(c => [c.hash, c]));
|
|
210
|
+
if (ref.startsWith('HEAD~')) {
|
|
211
|
+
const n = parseInt(ref.slice(5), 10);
|
|
212
|
+
if (isNaN(n) || n < 1)
|
|
213
|
+
return null;
|
|
214
|
+
let current = currentHead;
|
|
215
|
+
const commitsToRollback = [];
|
|
216
|
+
for (let i = 0; i < n; i++) {
|
|
217
|
+
const commitPath = path.join(projectRoot, '.relq', 'commits', `${current}.json`);
|
|
218
|
+
if (!fs.existsSync(commitPath))
|
|
219
|
+
return null;
|
|
220
|
+
const commit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
221
|
+
commitsToRollback.push(commit);
|
|
222
|
+
if (!commit.parentHash) {
|
|
223
|
+
if (i < n - 1) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
hash: commit.hash,
|
|
228
|
+
message: '(initial state)',
|
|
229
|
+
commitsToRollback: commitsToRollback.slice(0, -1),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
current = commit.parentHash;
|
|
233
|
+
}
|
|
234
|
+
const targetPath = path.join(projectRoot, '.relq', 'commits', `${current}.json`);
|
|
235
|
+
if (!fs.existsSync(targetPath))
|
|
236
|
+
return null;
|
|
237
|
+
const targetCommit = JSON.parse(fs.readFileSync(targetPath, 'utf-8'));
|
|
238
|
+
return {
|
|
239
|
+
hash: current,
|
|
240
|
+
message: targetCommit.message,
|
|
241
|
+
commitsToRollback,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const normalizedRef = ref.length < 40 ? commits.find(c => c.hash.startsWith(ref))?.hash : ref;
|
|
245
|
+
if (!normalizedRef || !commitsByHash.has(normalizedRef)) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
const commitsToRollback = [];
|
|
249
|
+
let current = currentHead;
|
|
250
|
+
while (current && current !== normalizedRef) {
|
|
251
|
+
const commitPath = path.join(projectRoot, '.relq', 'commits', `${current}.json`);
|
|
252
|
+
if (!fs.existsSync(commitPath))
|
|
253
|
+
break;
|
|
254
|
+
const commit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
255
|
+
commitsToRollback.push(commit);
|
|
256
|
+
current = commit.parentHash || '';
|
|
257
|
+
}
|
|
258
|
+
if (current !== normalizedRef) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const targetCommit = commitsByHash.get(normalizedRef);
|
|
262
|
+
return {
|
|
263
|
+
hash: normalizedRef,
|
|
264
|
+
message: targetCommit?.message || '',
|
|
265
|
+
commitsToRollback,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
exports.default = rollbackCommand;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.schemaAstCommand = schemaAstCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const jiti_1 = require("jiti");
|
|
40
|
+
const config_loader_1 = require("../utils/config-loader.cjs");
|
|
41
|
+
const schema_to_ast_1 = require("../utils/schema-to-ast.cjs");
|
|
42
|
+
const spinner_1 = require("../utils/spinner.cjs");
|
|
43
|
+
async function schemaAstCommand(context) {
|
|
44
|
+
const { config, args, flags, projectRoot } = context;
|
|
45
|
+
const spinner = (0, spinner_1.createSpinner)();
|
|
46
|
+
console.log('');
|
|
47
|
+
const options = {
|
|
48
|
+
json: Boolean(flags.json),
|
|
49
|
+
output: flags.output,
|
|
50
|
+
pretty: flags.pretty !== false,
|
|
51
|
+
};
|
|
52
|
+
let schemaPath;
|
|
53
|
+
if (args.length > 0) {
|
|
54
|
+
schemaPath = path.resolve(projectRoot, args[0]);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
schemaPath = path.resolve(projectRoot, (0, config_loader_1.getSchemaPath)(config ?? undefined));
|
|
58
|
+
}
|
|
59
|
+
const relativePath = path.relative(process.cwd(), schemaPath);
|
|
60
|
+
spinner.start(`Loading schema from ${spinner_1.colors.cyan(relativePath)}`);
|
|
61
|
+
let schemaModule;
|
|
62
|
+
try {
|
|
63
|
+
const jiti = (0, jiti_1.createJiti)(path.dirname(schemaPath), { interopDefault: true });
|
|
64
|
+
const module = await jiti.import(schemaPath);
|
|
65
|
+
if (module && module.default && typeof module.default === 'object') {
|
|
66
|
+
schemaModule = module.default;
|
|
67
|
+
}
|
|
68
|
+
else if (module && typeof module === 'object') {
|
|
69
|
+
schemaModule = module;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error('Schema file must export an object with table/enum definitions');
|
|
73
|
+
}
|
|
74
|
+
spinner.succeed(`Loaded schema from ${spinner_1.colors.cyan(relativePath)}`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
spinner.fail(`Failed to load schema`);
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
80
|
+
console.log('');
|
|
81
|
+
console.log(spinner_1.colors.yellow('hint:') + ` Make sure ${relativePath} is a valid TypeScript schema file.`);
|
|
82
|
+
console.log('');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
spinner.start('Converting schema to AST');
|
|
86
|
+
let ast;
|
|
87
|
+
try {
|
|
88
|
+
ast = (0, schema_to_ast_1.schemaToAST)(schemaModule);
|
|
89
|
+
spinner.succeed('Converted schema to AST');
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
spinner.fail('Failed to convert schema');
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log(spinner_1.colors.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
95
|
+
console.log('');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const indent = options.pretty ? 2 : 0;
|
|
99
|
+
const output = JSON.stringify(ast, null, indent);
|
|
100
|
+
if (options.output) {
|
|
101
|
+
const outputPath = path.resolve(projectRoot, options.output);
|
|
102
|
+
const outputDir = path.dirname(outputPath);
|
|
103
|
+
if (!fs.existsSync(outputDir)) {
|
|
104
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
fs.writeFileSync(outputPath, output, 'utf-8');
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log(`Written AST to ${spinner_1.colors.cyan(options.output)}`);
|
|
109
|
+
}
|
|
110
|
+
else if (options.json) {
|
|
111
|
+
console.log(output);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log('');
|
|
115
|
+
printAstSummary(ast);
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log(spinner_1.colors.muted(`Use ${spinner_1.colors.cyan('--json')} for full AST output or ${spinner_1.colors.cyan('--output <file>')} to write to file.`));
|
|
118
|
+
}
|
|
119
|
+
console.log('');
|
|
120
|
+
}
|
|
121
|
+
function printAstSummary(ast) {
|
|
122
|
+
console.log(spinner_1.colors.bold('Schema AST Summary'));
|
|
123
|
+
console.log('');
|
|
124
|
+
if (ast.extensions.length > 0) {
|
|
125
|
+
console.log(spinner_1.colors.cyan('Extensions:') + ` ${ast.extensions.length}`);
|
|
126
|
+
for (const ext of ast.extensions) {
|
|
127
|
+
console.log(` ${spinner_1.colors.green('•')} ${ext}`);
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
}
|
|
131
|
+
if (ast.enums.length > 0) {
|
|
132
|
+
console.log(spinner_1.colors.cyan('Enums:') + ` ${ast.enums.length}`);
|
|
133
|
+
for (const e of ast.enums) {
|
|
134
|
+
const tid = e.trackingId ? spinner_1.colors.muted(` [${e.trackingId}]`) : '';
|
|
135
|
+
console.log(` ${spinner_1.colors.green('•')} ${e.name}${tid}: (${e.values.join(', ')})`);
|
|
136
|
+
}
|
|
137
|
+
console.log('');
|
|
138
|
+
}
|
|
139
|
+
if (ast.domains.length > 0) {
|
|
140
|
+
console.log(spinner_1.colors.cyan('Domains:') + ` ${ast.domains.length}`);
|
|
141
|
+
for (const d of ast.domains) {
|
|
142
|
+
const tid = d.trackingId ? spinner_1.colors.muted(` [${d.trackingId}]`) : '';
|
|
143
|
+
console.log(` ${spinner_1.colors.green('•')} ${d.name}${tid}: ${d.baseType}`);
|
|
144
|
+
}
|
|
145
|
+
console.log('');
|
|
146
|
+
}
|
|
147
|
+
if (ast.compositeTypes.length > 0) {
|
|
148
|
+
console.log(spinner_1.colors.cyan('Composite Types:') + ` ${ast.compositeTypes.length}`);
|
|
149
|
+
for (const c of ast.compositeTypes) {
|
|
150
|
+
const tid = c.trackingId ? spinner_1.colors.muted(` [${c.trackingId}]`) : '';
|
|
151
|
+
const attrs = c.attributes.map(a => a.name).join(', ');
|
|
152
|
+
console.log(` ${spinner_1.colors.green('•')} ${c.name}${tid}: (${attrs})`);
|
|
153
|
+
}
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
if (ast.sequences.length > 0) {
|
|
157
|
+
console.log(spinner_1.colors.cyan('Sequences:') + ` ${ast.sequences.length}`);
|
|
158
|
+
for (const s of ast.sequences) {
|
|
159
|
+
const tid = s.trackingId ? spinner_1.colors.muted(` [${s.trackingId}]`) : '';
|
|
160
|
+
console.log(` ${spinner_1.colors.green('•')} ${s.name}${tid}`);
|
|
161
|
+
}
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
if (ast.tables.length > 0) {
|
|
165
|
+
console.log(spinner_1.colors.cyan('Tables:') + ` ${ast.tables.length}`);
|
|
166
|
+
for (const t of ast.tables) {
|
|
167
|
+
const tid = t.trackingId ? spinner_1.colors.muted(` [${t.trackingId}]`) : '';
|
|
168
|
+
console.log(` ${spinner_1.colors.green('•')} ${t.name}${tid}`);
|
|
169
|
+
console.log(` Columns: ${t.columns.length}`);
|
|
170
|
+
for (const col of t.columns.slice(0, 5)) {
|
|
171
|
+
const colTid = col.trackingId ? spinner_1.colors.muted(` [${col.trackingId}]`) : '';
|
|
172
|
+
const pk = col.isPrimaryKey ? spinner_1.colors.yellow(' PK') : '';
|
|
173
|
+
const nullable = col.isNullable ? '' : spinner_1.colors.red(' NOT NULL');
|
|
174
|
+
console.log(` - ${col.name}${colTid}: ${col.type}${pk}${nullable}`);
|
|
175
|
+
}
|
|
176
|
+
if (t.columns.length > 5) {
|
|
177
|
+
console.log(` ${spinner_1.colors.muted(`... and ${t.columns.length - 5} more`)}`);
|
|
178
|
+
}
|
|
179
|
+
if (t.indexes.length > 0) {
|
|
180
|
+
console.log(` Indexes: ${t.indexes.length}`);
|
|
181
|
+
}
|
|
182
|
+
if (t.constraints.length > 0) {
|
|
183
|
+
console.log(` Constraints: ${t.constraints.length}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
if (ast.views.length > 0) {
|
|
189
|
+
console.log(spinner_1.colors.cyan('Views:') + ` ${ast.views.length}`);
|
|
190
|
+
for (const v of ast.views) {
|
|
191
|
+
const tid = v.trackingId ? spinner_1.colors.muted(` [${v.trackingId}]`) : '';
|
|
192
|
+
const mat = v.isMaterialized ? spinner_1.colors.yellow(' (materialized)') : '';
|
|
193
|
+
console.log(` ${spinner_1.colors.green('•')} ${v.name}${tid}${mat}`);
|
|
194
|
+
}
|
|
195
|
+
console.log('');
|
|
196
|
+
}
|
|
197
|
+
if (ast.functions.length > 0) {
|
|
198
|
+
console.log(spinner_1.colors.cyan('Functions:') + ` ${ast.functions.length}`);
|
|
199
|
+
for (const f of ast.functions) {
|
|
200
|
+
const tid = f.trackingId ? spinner_1.colors.muted(` [${f.trackingId}]`) : '';
|
|
201
|
+
const args = f.args.map(a => a.type).join(', ');
|
|
202
|
+
console.log(` ${spinner_1.colors.green('•')} ${f.name}${tid}(${args}) -> ${f.returnType}`);
|
|
203
|
+
}
|
|
204
|
+
console.log('');
|
|
205
|
+
}
|
|
206
|
+
if (ast.triggers.length > 0) {
|
|
207
|
+
console.log(spinner_1.colors.cyan('Triggers:') + ` ${ast.triggers.length}`);
|
|
208
|
+
for (const tr of ast.triggers) {
|
|
209
|
+
const tid = tr.trackingId ? spinner_1.colors.muted(` [${tr.trackingId}]`) : '';
|
|
210
|
+
console.log(` ${spinner_1.colors.green('•')} ${tr.name}${tid} on ${tr.table}`);
|
|
211
|
+
}
|
|
212
|
+
console.log('');
|
|
213
|
+
}
|
|
214
|
+
const total = ast.tables.length + ast.enums.length + ast.views.length +
|
|
215
|
+
ast.functions.length + ast.triggers.length + ast.sequences.length +
|
|
216
|
+
ast.domains.length + ast.compositeTypes.length;
|
|
217
|
+
console.log(spinner_1.colors.bold(`Total: ${total} schema objects`));
|
|
218
|
+
}
|
|
219
|
+
exports.default = schemaAstCommand;
|
package/dist/cjs/cli/index.cjs
CHANGED
|
@@ -58,6 +58,7 @@ const tag_1 = require("./commands/tag.cjs");
|
|
|
58
58
|
const cherry_pick_1 = require("./commands/cherry-pick.cjs");
|
|
59
59
|
const remote_1 = require("./commands/remote.cjs");
|
|
60
60
|
const validate_1 = require("./commands/validate.cjs");
|
|
61
|
+
const schema_ast_1 = require("./commands/schema-ast.cjs");
|
|
61
62
|
const fs = __importStar(require("fs"));
|
|
62
63
|
const path = __importStar(require("path"));
|
|
63
64
|
function loadEnvFile() {
|
|
@@ -168,6 +169,7 @@ Other Commands:
|
|
|
168
169
|
introspect Parse database schema
|
|
169
170
|
import <sql-file> Import SQL file to schema
|
|
170
171
|
export [file] Export schema to SQL file
|
|
172
|
+
schema:ast [file] Convert schema to AST (JSON)
|
|
171
173
|
|
|
172
174
|
Options:
|
|
173
175
|
--help, -h Show this help
|
|
@@ -359,6 +361,10 @@ async function main() {
|
|
|
359
361
|
case 'validate':
|
|
360
362
|
await (0, validate_1.validateCommand)(context);
|
|
361
363
|
break;
|
|
364
|
+
case 'schema:ast':
|
|
365
|
+
case 'ast':
|
|
366
|
+
await (0, schema_ast_1.schemaAstCommand)(context);
|
|
367
|
+
break;
|
|
362
368
|
case 'sync':
|
|
363
369
|
await (0, sync_1.syncCommand)(context);
|
|
364
370
|
break;
|