relq 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/commands/add.cjs +403 -27
- package/dist/cjs/cli/commands/branch.cjs +13 -23
- package/dist/cjs/cli/commands/checkout.cjs +16 -29
- package/dist/cjs/cli/commands/cherry-pick.cjs +3 -4
- package/dist/cjs/cli/commands/commit.cjs +21 -29
- package/dist/cjs/cli/commands/diff.cjs +28 -32
- package/dist/cjs/cli/commands/export.cjs +7 -7
- package/dist/cjs/cli/commands/fetch.cjs +15 -21
- 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 +34 -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 +26 -32
- package/dist/cjs/cli/commands/merge.cjs +24 -41
- package/dist/cjs/cli/commands/migrate.cjs +12 -25
- package/dist/cjs/cli/commands/pull.cjs +216 -106
- package/dist/cjs/cli/commands/push.cjs +35 -75
- package/dist/cjs/cli/commands/remote.cjs +2 -1
- package/dist/cjs/cli/commands/reset.cjs +22 -43
- package/dist/cjs/cli/commands/resolve.cjs +12 -14
- package/dist/cjs/cli/commands/rollback.cjs +16 -38
- package/dist/cjs/cli/commands/stash.cjs +5 -7
- package/dist/cjs/cli/commands/status.cjs +5 -10
- package/dist/cjs/cli/commands/sync.cjs +30 -50
- package/dist/cjs/cli/commands/tag.cjs +3 -4
- package/dist/cjs/cli/index.cjs +72 -9
- 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/config-loader.cjs +34 -8
- package/dist/cjs/cli/utils/fast-introspect.cjs +109 -3
- 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 +56 -5
- package/dist/cjs/cli/utils/relqignore.cjs +1 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +47 -0
- 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 +7 -3
- 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 +10 -2
- package/dist/cjs/cli/utils/type-generator.cjs +28 -16
- package/dist/config.d.ts +16 -6
- package/dist/esm/cli/commands/add.js +372 -29
- package/dist/esm/cli/commands/branch.js +14 -24
- package/dist/esm/cli/commands/checkout.js +16 -29
- package/dist/esm/cli/commands/cherry-pick.js +3 -4
- package/dist/esm/cli/commands/commit.js +22 -30
- package/dist/esm/cli/commands/diff.js +6 -10
- package/dist/esm/cli/commands/export.js +8 -8
- package/dist/esm/cli/commands/fetch.js +14 -20
- 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 +35 -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 +6 -12
- package/dist/esm/cli/commands/merge.js +20 -37
- package/dist/esm/cli/commands/migrate.js +12 -25
- package/dist/esm/cli/commands/pull.js +204 -94
- package/dist/esm/cli/commands/push.js +21 -61
- package/dist/esm/cli/commands/remote.js +2 -1
- package/dist/esm/cli/commands/reset.js +16 -37
- package/dist/esm/cli/commands/resolve.js +13 -15
- package/dist/esm/cli/commands/rollback.js +16 -38
- package/dist/esm/cli/commands/stash.js +6 -8
- package/dist/esm/cli/commands/status.js +6 -11
- package/dist/esm/cli/commands/sync.js +30 -50
- package/dist/esm/cli/commands/tag.js +3 -4
- package/dist/esm/cli/index.js +72 -9
- 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/config-loader.js +34 -8
- package/dist/esm/cli/utils/fast-introspect.js +109 -3
- 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 +55 -5
- package/dist/esm/cli/utils/relqignore.js +1 -0
- package/dist/esm/cli/utils/repo-manager.js +42 -0
- 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 +7 -3
- 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 +10 -2
- package/dist/esm/cli/utils/type-generator.js +28 -16
- package/dist/index.d.ts +25 -8
- package/dist/schema-builder.d.ts +18 -7
- package/package.json +1 -1
|
@@ -1,26 +1,413 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.addCommand = addCommand;
|
|
4
37
|
exports.getRelatedChanges = getRelatedChanges;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
5
39
|
const spinner_1 = require("../utils/spinner.cjs");
|
|
6
40
|
const relqignore_1 = require("../utils/relqignore.cjs");
|
|
7
41
|
const config_1 = require("../../config/config.cjs");
|
|
42
|
+
const path = __importStar(require("path"));
|
|
8
43
|
const repo_manager_1 = require("../utils/repo-manager.cjs");
|
|
9
44
|
const change_tracker_1 = require("../utils/change-tracker.cjs");
|
|
45
|
+
const schema_comparator_1 = require("../utils/schema-comparator.cjs");
|
|
46
|
+
function parseSchemaFileForComparison(schemaPath) {
|
|
47
|
+
if (!fs.existsSync(schemaPath)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const content = fs.readFileSync(schemaPath, 'utf-8');
|
|
51
|
+
const tables = [];
|
|
52
|
+
const tableStartRegex = /defineTable\s*\(\s*['"]([^'"]+)['"],\s*\{/g;
|
|
53
|
+
let tableStartMatch;
|
|
54
|
+
while ((tableStartMatch = tableStartRegex.exec(content)) !== null) {
|
|
55
|
+
const tableName = tableStartMatch[1];
|
|
56
|
+
const startIdx = tableStartMatch.index + tableStartMatch[0].length;
|
|
57
|
+
let braceCount = 1;
|
|
58
|
+
let endIdx = startIdx;
|
|
59
|
+
while (braceCount > 0 && endIdx < content.length) {
|
|
60
|
+
const char = content[endIdx];
|
|
61
|
+
if (char === '{')
|
|
62
|
+
braceCount++;
|
|
63
|
+
else if (char === '}')
|
|
64
|
+
braceCount--;
|
|
65
|
+
endIdx++;
|
|
66
|
+
}
|
|
67
|
+
const columnsBlock = content.substring(startIdx, endIdx - 1);
|
|
68
|
+
let optionsBlock = '';
|
|
69
|
+
const afterColumns = content.substring(endIdx);
|
|
70
|
+
const optionsMatch = afterColumns.match(/^\s*,\s*\{/);
|
|
71
|
+
if (optionsMatch) {
|
|
72
|
+
const optStart = endIdx + optionsMatch[0].length;
|
|
73
|
+
braceCount = 1;
|
|
74
|
+
let optEnd = optStart;
|
|
75
|
+
while (braceCount > 0 && optEnd < content.length) {
|
|
76
|
+
const char = content[optEnd];
|
|
77
|
+
if (char === '{')
|
|
78
|
+
braceCount++;
|
|
79
|
+
else if (char === '}')
|
|
80
|
+
braceCount--;
|
|
81
|
+
optEnd++;
|
|
82
|
+
}
|
|
83
|
+
optionsBlock = content.substring(optStart, optEnd - 1);
|
|
84
|
+
}
|
|
85
|
+
const columns = [];
|
|
86
|
+
const constraints = [];
|
|
87
|
+
const tsToDbNameMap = new Map();
|
|
88
|
+
const lines = columnsBlock.split('\n');
|
|
89
|
+
let currentColDef = '';
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const trimmed = line.trim();
|
|
92
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*'))
|
|
93
|
+
continue;
|
|
94
|
+
currentColDef += ' ' + trimmed;
|
|
95
|
+
if (trimmed.endsWith(',') || trimmed.endsWith(')')) {
|
|
96
|
+
const colDef = currentColDef.trim();
|
|
97
|
+
currentColDef = '';
|
|
98
|
+
const typePattern = 'varchar|text|uuid|integer|bigint|boolean|timestamp|date|jsonb|json|numeric|serial|bigserial|smallserial|tsvector|smallint|real|doublePrecision|char|inet|cidr|macaddr|macaddr8|interval|time|point|line|lseg|box|path|polygon|circle|bytea|bit|varbit|money|xml|oid';
|
|
99
|
+
const colMatch = colDef.match(new RegExp(`^(\\w+):\\s*(${typePattern})`));
|
|
100
|
+
if (!colMatch)
|
|
101
|
+
continue;
|
|
102
|
+
const tsName = colMatch[1];
|
|
103
|
+
const type = colMatch[2];
|
|
104
|
+
const explicitNameMatch = colDef.match(new RegExp(`${type}\\s*\\(['\"]([^'"]+)['\"]`));
|
|
105
|
+
const dbColName = explicitNameMatch ? explicitNameMatch[1] : tsName;
|
|
106
|
+
tsToDbNameMap.set(tsName, dbColName);
|
|
107
|
+
let defaultValue = null;
|
|
108
|
+
const isJsonbColumn = type === 'jsonb' || type === 'json';
|
|
109
|
+
const isArrayColumn = colDef.includes('.array()');
|
|
110
|
+
const bigintMatch = colDef.match(/\.default\(\s*BigInt\(\s*(-?\d+)\s*\)\s*\)/);
|
|
111
|
+
if (bigintMatch) {
|
|
112
|
+
defaultValue = bigintMatch[1];
|
|
113
|
+
}
|
|
114
|
+
const funcDefaultMatch = !defaultValue && colDef.match(/\.default\(\s*(genRandomUuid|now|currentDate|currentTimestamp|emptyArray|emptyObject)\s*\(\s*\)\s*\)/);
|
|
115
|
+
if (funcDefaultMatch) {
|
|
116
|
+
const funcName = funcDefaultMatch[1];
|
|
117
|
+
if (funcName === 'emptyArray') {
|
|
118
|
+
defaultValue = isJsonbColumn ? "'[]'::jsonb" : "'{}'::text[]";
|
|
119
|
+
}
|
|
120
|
+
else if (funcName === 'emptyObject') {
|
|
121
|
+
defaultValue = "'{}'::jsonb";
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const funcToSql = {
|
|
125
|
+
'genRandomUuid': 'gen_random_uuid()',
|
|
126
|
+
'now': 'now()',
|
|
127
|
+
'currentDate': 'CURRENT_DATE',
|
|
128
|
+
'currentTimestamp': 'CURRENT_TIMESTAMP',
|
|
129
|
+
};
|
|
130
|
+
defaultValue = funcToSql[funcName] || `${funcName}()`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const strDefaultMatch = colDef.match(/\.default\(\s*(['"])([^'"]*)\1\s*\)/);
|
|
135
|
+
if (strDefaultMatch) {
|
|
136
|
+
defaultValue = strDefaultMatch[2];
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const boolDefaultMatch = colDef.match(/\.default\(\s*(true|false)\s*\)/);
|
|
140
|
+
if (boolDefaultMatch) {
|
|
141
|
+
defaultValue = boolDefaultMatch[1];
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const numDefaultMatch = colDef.match(/\.default\(\s*(-?\d+(?:\.\d+)?)\s*\)/);
|
|
145
|
+
if (numDefaultMatch) {
|
|
146
|
+
defaultValue = numDefaultMatch[1];
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const jsonObjMatch = colDef.match(/\.default\(\s*(\{[^}]+\})\s*\)/);
|
|
150
|
+
if (jsonObjMatch) {
|
|
151
|
+
defaultValue = `'${jsonObjMatch[1]}'::jsonb`;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const arrayLiteralMatch = colDef.match(/\.default\(\s*(\[[^\]]+\])\s*\)/);
|
|
155
|
+
if (arrayLiteralMatch) {
|
|
156
|
+
const arrayStr = arrayLiteralMatch[1];
|
|
157
|
+
defaultValue = `'${arrayStr}'::jsonb`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (colDef.includes('.check(')) {
|
|
165
|
+
const checkValuesMatch = colDef.match(/\.check\(([^)]+)\)/);
|
|
166
|
+
if (checkValuesMatch) {
|
|
167
|
+
const valuesStr = checkValuesMatch[1];
|
|
168
|
+
const values = valuesStr.match(/['"]([^'"]+)['"]/g)?.map(v => v.replace(/['"]/g, '')) || [];
|
|
169
|
+
if (values.length > 0) {
|
|
170
|
+
const constraintName = `${tableName}_${dbColName}_check`;
|
|
171
|
+
constraints.push({
|
|
172
|
+
name: constraintName,
|
|
173
|
+
type: 'CHECK',
|
|
174
|
+
columns: [dbColName],
|
|
175
|
+
definition: '',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
let comment = null;
|
|
181
|
+
const commentMatch = colDef.match(/\.comment\(\s*(['"])([^'"]*)\1\s*\)/);
|
|
182
|
+
if (commentMatch) {
|
|
183
|
+
comment = commentMatch[2];
|
|
184
|
+
}
|
|
185
|
+
if (colDef.includes('.identity()')) {
|
|
186
|
+
defaultValue = defaultValue || 'GENERATED BY DEFAULT AS IDENTITY';
|
|
187
|
+
}
|
|
188
|
+
const isArray = colDef.includes('.array()');
|
|
189
|
+
columns.push({
|
|
190
|
+
name: dbColName,
|
|
191
|
+
dataType: isArray ? `${type}[]` : type,
|
|
192
|
+
isNullable: !colDef.includes('.notNull()') && !colDef.includes('.primaryKey()'),
|
|
193
|
+
defaultValue,
|
|
194
|
+
isPrimaryKey: colDef.includes('.primaryKey()'),
|
|
195
|
+
isUnique: colDef.includes('.unique()'),
|
|
196
|
+
maxLength: null,
|
|
197
|
+
precision: null,
|
|
198
|
+
scale: null,
|
|
199
|
+
references: null,
|
|
200
|
+
comment,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const indexes = [];
|
|
205
|
+
const indexRegex = /index\s*\(\s*['"]([^'"]+)['"]\s*\)\.on\(([^)]+)\)/g;
|
|
206
|
+
let idxMatch;
|
|
207
|
+
while ((idxMatch = indexRegex.exec(optionsBlock)) !== null) {
|
|
208
|
+
const indexName = idxMatch[1];
|
|
209
|
+
const indexCols = idxMatch[2].split(',').map(c => {
|
|
210
|
+
const tsColName = c.trim().replace(/table\.\s*/, '');
|
|
211
|
+
return tsToDbNameMap.get(tsColName) || tsColName;
|
|
212
|
+
});
|
|
213
|
+
const isUnique = optionsBlock.includes(`index('${indexName}')`) &&
|
|
214
|
+
optionsBlock.substring(optionsBlock.indexOf(`index('${indexName}')`)).split('\n')[0].includes('.unique()');
|
|
215
|
+
indexes.push({
|
|
216
|
+
name: indexName,
|
|
217
|
+
columns: indexCols,
|
|
218
|
+
isUnique: isUnique,
|
|
219
|
+
isPrimary: false,
|
|
220
|
+
type: 'btree',
|
|
221
|
+
definition: '',
|
|
222
|
+
whereClause: null,
|
|
223
|
+
expression: null,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const checkRegexLegacy = /check\s*\(\s*['"]([^'"]+)['"]\s*,\s*sql`([^`]+)`\s*\)/g;
|
|
227
|
+
let tableCheckMatch;
|
|
228
|
+
while ((tableCheckMatch = checkRegexLegacy.exec(optionsBlock)) !== null) {
|
|
229
|
+
constraints.push({
|
|
230
|
+
name: tableCheckMatch[1],
|
|
231
|
+
type: 'CHECK',
|
|
232
|
+
columns: [],
|
|
233
|
+
definition: tableCheckMatch[2].trim(),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const checkRegexNew = /check\.constraint\s*\(\s*['"]([^'"]+)['"]/g;
|
|
237
|
+
let newCheckMatch;
|
|
238
|
+
while ((newCheckMatch = checkRegexNew.exec(optionsBlock)) !== null) {
|
|
239
|
+
const constraintName = newCheckMatch[1];
|
|
240
|
+
if (!constraints.some(c => c.name === constraintName)) {
|
|
241
|
+
constraints.push({
|
|
242
|
+
name: constraintName,
|
|
243
|
+
type: 'CHECK',
|
|
244
|
+
columns: [],
|
|
245
|
+
definition: '',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const checkConstraintsBlockMatch = optionsBlock.match(/checkConstraints:\s*\([^)]+\)\s*=>\s*\[([^\]]+)\]/s);
|
|
250
|
+
if (checkConstraintsBlockMatch) {
|
|
251
|
+
const checkBlock = checkConstraintsBlockMatch[1];
|
|
252
|
+
const constraintNameMatches = checkBlock.matchAll(/check\.constraint\s*\(\s*['"]([^'"]+)['"]/g);
|
|
253
|
+
for (const match of constraintNameMatches) {
|
|
254
|
+
const constraintName = match[1];
|
|
255
|
+
if (!constraints.some(c => c.name === constraintName)) {
|
|
256
|
+
constraints.push({
|
|
257
|
+
name: constraintName,
|
|
258
|
+
type: 'CHECK',
|
|
259
|
+
columns: [],
|
|
260
|
+
definition: '',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
let partitionType;
|
|
266
|
+
let partitionKey;
|
|
267
|
+
const partitionByMatch = optionsBlock.match(/partitionBy:\s*\([^)]+\)\s*=>\s*\w+\.(list|range|hash)\(([^)]+)\)/i);
|
|
268
|
+
if (partitionByMatch) {
|
|
269
|
+
partitionType = partitionByMatch[1].toUpperCase();
|
|
270
|
+
const tsPartitionKey = partitionByMatch[2].replace(/table\./, '').trim();
|
|
271
|
+
const dbPartitionKey = tsToDbNameMap.get(tsPartitionKey) || tsPartitionKey;
|
|
272
|
+
partitionKey = [dbPartitionKey];
|
|
273
|
+
}
|
|
274
|
+
tables.push({
|
|
275
|
+
name: tableName,
|
|
276
|
+
schema: 'public',
|
|
277
|
+
columns,
|
|
278
|
+
indexes,
|
|
279
|
+
constraints,
|
|
280
|
+
rowCount: 0,
|
|
281
|
+
isPartitioned: !!partitionType,
|
|
282
|
+
partitionType,
|
|
283
|
+
partitionKey,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const enums = [];
|
|
287
|
+
const enumRegex = /defineEnum\s*\(\s*['"]([^'"]+)['"]\s*,\s*\[([^\]]+)\]/g;
|
|
288
|
+
let enumMatch;
|
|
289
|
+
while ((enumMatch = enumRegex.exec(content)) !== null) {
|
|
290
|
+
const enumName = enumMatch[1];
|
|
291
|
+
const valuesStr = enumMatch[2];
|
|
292
|
+
const values = valuesStr.match(/['"]([^'"]+)['"]/g)?.map(v => v.replace(/['"]/g, '')) || [];
|
|
293
|
+
enums.push({ name: enumName, values });
|
|
294
|
+
}
|
|
295
|
+
const extensions = [];
|
|
296
|
+
const singleExtMatch = content.match(/pgExtensions\s*\(\s*['"]([^'"]+)['"]\s*\)/);
|
|
297
|
+
if (singleExtMatch) {
|
|
298
|
+
extensions.push(singleExtMatch[1]);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const arrayExtMatch = content.match(/pgExtensions\s*\(\s*\[([^\]]+)\]/);
|
|
302
|
+
if (arrayExtMatch) {
|
|
303
|
+
const extList = arrayExtMatch[1].match(/['"]([^'"]+)['"]/g);
|
|
304
|
+
if (extList) {
|
|
305
|
+
extList.forEach(e => extensions.push(e.replace(/['"]/g, '')));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
tables,
|
|
311
|
+
enums,
|
|
312
|
+
domains: [],
|
|
313
|
+
compositeTypes: [],
|
|
314
|
+
sequences: [],
|
|
315
|
+
collations: [],
|
|
316
|
+
functions: [],
|
|
317
|
+
triggers: [],
|
|
318
|
+
policies: [],
|
|
319
|
+
partitions: [],
|
|
320
|
+
foreignServers: [],
|
|
321
|
+
foreignTables: [],
|
|
322
|
+
extensions,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function snapshotToDatabaseSchema(snapshot) {
|
|
326
|
+
const tables = (snapshot.tables || []).map(t => ({
|
|
327
|
+
name: t.name,
|
|
328
|
+
schema: t.schema || 'public',
|
|
329
|
+
columns: (t.columns || []).map(c => ({
|
|
330
|
+
name: c.name,
|
|
331
|
+
dataType: c.type || 'text',
|
|
332
|
+
isNullable: c.nullable !== false,
|
|
333
|
+
defaultValue: c.default || null,
|
|
334
|
+
isPrimaryKey: c.primaryKey || false,
|
|
335
|
+
isUnique: c.unique || false,
|
|
336
|
+
maxLength: null,
|
|
337
|
+
precision: null,
|
|
338
|
+
scale: null,
|
|
339
|
+
references: null,
|
|
340
|
+
comment: c.comment || null,
|
|
341
|
+
})),
|
|
342
|
+
indexes: (t.indexes || []).map(i => ({
|
|
343
|
+
name: i.name,
|
|
344
|
+
columns: Array.isArray(i.columns) ? i.columns : [i.columns],
|
|
345
|
+
isUnique: i.unique || false,
|
|
346
|
+
isPrimary: false,
|
|
347
|
+
type: i.type || 'btree',
|
|
348
|
+
definition: i.definition || '',
|
|
349
|
+
whereClause: i.whereClause || null,
|
|
350
|
+
expression: null,
|
|
351
|
+
})),
|
|
352
|
+
constraints: (t.constraints || []).map(c => ({
|
|
353
|
+
name: c.name,
|
|
354
|
+
type: c.type,
|
|
355
|
+
columns: c.columns || [],
|
|
356
|
+
definition: c.definition || '',
|
|
357
|
+
})),
|
|
358
|
+
rowCount: 0,
|
|
359
|
+
isPartitioned: t.isPartitioned || false,
|
|
360
|
+
partitionType: t.partitionType,
|
|
361
|
+
partitionKey: t.partitionKey ? (Array.isArray(t.partitionKey) ? t.partitionKey : [t.partitionKey]) : undefined,
|
|
362
|
+
}));
|
|
363
|
+
return {
|
|
364
|
+
tables,
|
|
365
|
+
enums: (snapshot.enums || []).map(e => ({ name: e.name, values: e.values })),
|
|
366
|
+
domains: [],
|
|
367
|
+
compositeTypes: [],
|
|
368
|
+
sequences: [],
|
|
369
|
+
collations: [],
|
|
370
|
+
functions: [],
|
|
371
|
+
triggers: [],
|
|
372
|
+
policies: [],
|
|
373
|
+
partitions: [],
|
|
374
|
+
foreignServers: [],
|
|
375
|
+
foreignTables: [],
|
|
376
|
+
extensions: (snapshot.extensions || []).map(e => typeof e === 'string' ? e : e.name),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
10
379
|
async function addCommand(context) {
|
|
11
|
-
const { args } = context;
|
|
12
|
-
const projectRoot = process.cwd();
|
|
380
|
+
const { args, projectRoot } = context;
|
|
13
381
|
console.log('');
|
|
14
382
|
if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
|
|
15
|
-
|
|
16
|
-
console.log('');
|
|
17
|
-
console.log(`${spinner_1.colors.muted('Run')} ${spinner_1.colors.cyan('relq init')} ${spinner_1.colors.muted('first.')}`);
|
|
18
|
-
return;
|
|
383
|
+
(0, spinner_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${spinner_1.colors.cyan('relq init')} to initialize.`);
|
|
19
384
|
}
|
|
20
|
-
const allUnstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
|
|
21
|
-
const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
|
|
22
385
|
const ignorePatterns = (0, relqignore_1.loadRelqignore)(projectRoot);
|
|
23
386
|
const config = await (0, config_1.loadConfig)();
|
|
387
|
+
const schemaPathRaw = typeof config.schema === 'string' ? config.schema : './db/schema.ts';
|
|
388
|
+
const schemaPath = path.resolve(projectRoot, schemaPathRaw);
|
|
389
|
+
const fileChange = (0, repo_manager_1.detectFileChanges)(schemaPath, projectRoot);
|
|
390
|
+
if (fileChange) {
|
|
391
|
+
const currentSchema = parseSchemaFileForComparison(schemaPath);
|
|
392
|
+
const snapshot = (0, repo_manager_1.loadSnapshot)(projectRoot);
|
|
393
|
+
if (currentSchema && snapshot) {
|
|
394
|
+
const snapshotAsDbSchema = snapshotToDatabaseSchema(snapshot);
|
|
395
|
+
const schemaChanges = (0, schema_comparator_1.compareSchemas)(snapshotAsDbSchema, currentSchema);
|
|
396
|
+
if (schemaChanges.length > 0) {
|
|
397
|
+
(0, repo_manager_1.clearUnstagedChanges)(projectRoot);
|
|
398
|
+
(0, repo_manager_1.addUnstagedChanges)(schemaChanges, projectRoot);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
const existingUnstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
|
|
402
|
+
const hasFileChange = existingUnstaged.some(c => c.objectType === 'SCHEMA_FILE');
|
|
403
|
+
if (!hasFileChange) {
|
|
404
|
+
(0, repo_manager_1.addUnstagedChanges)([fileChange], projectRoot);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const allUnstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
|
|
410
|
+
const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
|
|
24
411
|
const unstaged = allUnstaged.filter(change => {
|
|
25
412
|
const objectType = change.objectType;
|
|
26
413
|
const result = (0, relqignore_1.isIgnored)(objectType, change.objectName, change.parentName || null, ignorePatterns);
|
|
@@ -44,41 +431,30 @@ async function addCommand(context) {
|
|
|
44
431
|
const filteredCount = allUnstaged.length - unstaged.length;
|
|
45
432
|
if (unstaged.length === 0) {
|
|
46
433
|
if (staged.length > 0) {
|
|
47
|
-
console.log(
|
|
434
|
+
console.log('All changes are already staged');
|
|
48
435
|
console.log(`${spinner_1.colors.muted(`${staged.length} change(s) ready to commit`)}`);
|
|
49
436
|
console.log('');
|
|
50
|
-
console.log(
|
|
437
|
+
console.log(`hint: run 'relq commit -m "message"' to commit`);
|
|
51
438
|
}
|
|
52
439
|
else if (filteredCount > 0) {
|
|
53
|
-
console.log(
|
|
440
|
+
console.log('No stageable changes');
|
|
54
441
|
console.log(`${spinner_1.colors.muted(`${filteredCount} change(s) filtered by .relqignore or config`)}`);
|
|
55
442
|
}
|
|
56
443
|
else {
|
|
57
|
-
console.log(
|
|
58
|
-
console.log(
|
|
444
|
+
console.log('No changes to stage');
|
|
445
|
+
console.log(`hint: run 'relq pull' or 'relq import' to detect changes`);
|
|
59
446
|
}
|
|
60
447
|
console.log('');
|
|
61
448
|
return;
|
|
62
449
|
}
|
|
63
450
|
const patterns = args.length > 0 ? args : ['.'];
|
|
64
|
-
const addAll = patterns.includes('.') || patterns.includes('*');
|
|
65
|
-
console.log(`${spinner_1.colors.cyan('Unstaged changes:')} ${unstaged.length}`);
|
|
66
|
-
console.log('');
|
|
67
|
-
for (const change of unstaged) {
|
|
68
|
-
const display = (0, change_tracker_1.getChangeDisplayName)(change);
|
|
69
|
-
const color = change.type === 'CREATE' ? spinner_1.colors.green :
|
|
70
|
-
change.type === 'DROP' ? spinner_1.colors.red :
|
|
71
|
-
spinner_1.colors.yellow;
|
|
72
|
-
console.log(` ${color(display)}`);
|
|
73
|
-
}
|
|
74
|
-
console.log('');
|
|
75
451
|
const stagedNow = (0, repo_manager_1.stageChanges)(patterns, projectRoot);
|
|
76
452
|
if (stagedNow.length === 0) {
|
|
77
|
-
|
|
453
|
+
(0, spinner_1.warning)(`No changes matched the pattern(s): ${patterns.join(', ')}`);
|
|
78
454
|
console.log('');
|
|
79
455
|
return;
|
|
80
456
|
}
|
|
81
|
-
console.log(
|
|
457
|
+
console.log(`Staged ${stagedNow.length} change(s):`);
|
|
82
458
|
console.log('');
|
|
83
459
|
for (const change of stagedNow) {
|
|
84
460
|
const display = (0, change_tracker_1.getChangeDisplayName)(change);
|
|
@@ -93,7 +469,7 @@ async function addCommand(context) {
|
|
|
93
469
|
console.log(`${spinner_1.colors.muted(`${remainingUnstaged.length} change(s) still unstaged`)}`);
|
|
94
470
|
console.log('');
|
|
95
471
|
}
|
|
96
|
-
console.log(
|
|
472
|
+
console.log(`hint: run 'relq commit -m "message"' to commit`);
|
|
97
473
|
console.log('');
|
|
98
474
|
}
|
|
99
475
|
function getRelatedChanges(tableName, changes) {
|
|
@@ -54,12 +54,10 @@ function saveBranchState(state, projectRoot) {
|
|
|
54
54
|
fs.writeFileSync(branchPath, JSON.stringify(state, null, 2));
|
|
55
55
|
}
|
|
56
56
|
async function branchCommand(context) {
|
|
57
|
-
const { args, flags } = context;
|
|
58
|
-
const projectRoot = process.cwd();
|
|
57
|
+
const { args, flags, projectRoot } = context;
|
|
59
58
|
console.log('');
|
|
60
59
|
if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
|
|
61
|
-
|
|
62
|
-
return;
|
|
60
|
+
(0, spinner_1.fatal)('not a relq repository (or any parent directories): .relq', `Run ${spinner_1.colors.cyan('relq init')} to initialize.`);
|
|
63
61
|
}
|
|
64
62
|
const state = loadBranchState(projectRoot);
|
|
65
63
|
const deleteFlag = flags['d'] === true || flags['delete'] === true;
|
|
@@ -67,20 +65,17 @@ async function branchCommand(context) {
|
|
|
67
65
|
if (deleteFlag) {
|
|
68
66
|
const branchName = args[0];
|
|
69
67
|
if (!branchName) {
|
|
70
|
-
|
|
71
|
-
return;
|
|
68
|
+
(0, spinner_1.fatal)('Please specify branch name', `Usage: ${spinner_1.colors.cyan('relq branch -d <name>')}`);
|
|
72
69
|
}
|
|
73
70
|
if (branchName === state.current) {
|
|
74
|
-
|
|
75
|
-
return;
|
|
71
|
+
(0, spinner_1.fatal)(`Cannot delete the branch '${branchName}' which you are currently on`);
|
|
76
72
|
}
|
|
77
73
|
if (!state.branches[branchName]) {
|
|
78
|
-
|
|
79
|
-
return;
|
|
74
|
+
(0, spinner_1.fatal)(`Branch '${branchName}' not found`, `Use ${spinner_1.colors.cyan('relq branch')} to list available branches.`);
|
|
80
75
|
}
|
|
81
76
|
delete state.branches[branchName];
|
|
82
77
|
saveBranchState(state, projectRoot);
|
|
83
|
-
console.log(
|
|
78
|
+
console.log(`Deleted branch '${branchName}'`);
|
|
84
79
|
console.log('');
|
|
85
80
|
return;
|
|
86
81
|
}
|
|
@@ -88,40 +83,35 @@ async function branchCommand(context) {
|
|
|
88
83
|
const oldName = args[0];
|
|
89
84
|
const newName = args[1];
|
|
90
85
|
if (!oldName || !newName) {
|
|
91
|
-
|
|
92
|
-
return;
|
|
86
|
+
(0, spinner_1.fatal)('Missing arguments', `Usage: ${spinner_1.colors.cyan('relq branch -m <old> <new>')}`);
|
|
93
87
|
}
|
|
94
88
|
if (!state.branches[oldName]) {
|
|
95
|
-
|
|
96
|
-
return;
|
|
89
|
+
(0, spinner_1.fatal)(`Branch '${oldName}' not found`, `Use ${spinner_1.colors.cyan('relq branch')} to list available branches.`);
|
|
97
90
|
}
|
|
98
91
|
if (state.branches[newName]) {
|
|
99
|
-
|
|
100
|
-
return;
|
|
92
|
+
(0, spinner_1.fatal)(`A branch named '${newName}' already exists`);
|
|
101
93
|
}
|
|
102
94
|
state.branches[newName] = state.branches[oldName];
|
|
103
95
|
delete state.branches[oldName];
|
|
104
96
|
if (state.current === oldName)
|
|
105
97
|
state.current = newName;
|
|
106
98
|
saveBranchState(state, projectRoot);
|
|
107
|
-
console.log(
|
|
99
|
+
console.log(`Renamed '${oldName}' to '${newName}'`);
|
|
108
100
|
console.log('');
|
|
109
101
|
return;
|
|
110
102
|
}
|
|
111
103
|
if (args[0]) {
|
|
112
104
|
const branchName = args[0];
|
|
113
105
|
if (state.branches[branchName]) {
|
|
114
|
-
|
|
115
|
-
return;
|
|
106
|
+
(0, spinner_1.fatal)(`A branch named '${branchName}' already exists`);
|
|
116
107
|
}
|
|
117
108
|
const head = (0, repo_manager_1.getHead)(projectRoot);
|
|
118
109
|
if (!head) {
|
|
119
|
-
|
|
120
|
-
return;
|
|
110
|
+
(0, spinner_1.fatal)('No commits yet', `Run ${spinner_1.colors.cyan('relq pull')} or ${spinner_1.colors.cyan('relq import')} first.`);
|
|
121
111
|
}
|
|
122
112
|
state.branches[branchName] = head;
|
|
123
113
|
saveBranchState(state, projectRoot);
|
|
124
|
-
console.log(
|
|
114
|
+
console.log(`Created branch '${branchName}'`);
|
|
125
115
|
console.log('');
|
|
126
116
|
return;
|
|
127
117
|
}
|
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.checkoutCommand = checkoutCommand;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
const
|
|
39
|
+
const cli_utils_1 = require("../utils/cli-utils.cjs");
|
|
40
40
|
const repo_manager_1 = require("../utils/repo-manager.cjs");
|
|
41
41
|
function loadBranchState(projectRoot) {
|
|
42
42
|
const branchPath = path.join(projectRoot, '.relq', 'branches.json');
|
|
@@ -51,60 +51,48 @@ function saveBranchState(state, projectRoot) {
|
|
|
51
51
|
fs.writeFileSync(branchPath, JSON.stringify(state, null, 2));
|
|
52
52
|
}
|
|
53
53
|
async function checkoutCommand(context) {
|
|
54
|
-
const { args, flags } = context;
|
|
55
|
-
const projectRoot = process.cwd();
|
|
54
|
+
const { args, flags, projectRoot } = context;
|
|
56
55
|
console.log('');
|
|
57
56
|
if (!(0, repo_manager_1.isInitialized)(projectRoot)) {
|
|
58
|
-
|
|
59
|
-
return;
|
|
57
|
+
(0, cli_utils_1.fatal)('not a relq repository (or any parent directories): .relq', "run 'relq init' to initialize");
|
|
60
58
|
}
|
|
61
59
|
const createBranch = flags['b'] === true;
|
|
62
60
|
const branchName = args[0];
|
|
63
61
|
if (!branchName) {
|
|
64
|
-
|
|
65
|
-
console.log(
|
|
66
|
-
console.log(
|
|
67
|
-
console.log(` ${spinner_1.colors.cyan('relq checkout -b <new-branch>')}`);
|
|
68
|
-
console.log('');
|
|
62
|
+
(0, cli_utils_1.error)('please specify a branch');
|
|
63
|
+
console.log("usage: relq checkout <branch>");
|
|
64
|
+
console.log(" relq checkout -b <new-branch>");
|
|
69
65
|
return;
|
|
70
66
|
}
|
|
71
67
|
const staged = (0, repo_manager_1.getStagedChanges)(projectRoot);
|
|
72
68
|
const unstaged = (0, repo_manager_1.getUnstagedChanges)(projectRoot);
|
|
73
69
|
if (staged.length > 0 || unstaged.length > 0) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.log(` ${spinner_1.colors.cyan('relq commit -m "message"')}`);
|
|
78
|
-
console.log(` ${spinner_1.colors.cyan('relq stash')}`);
|
|
79
|
-
console.log('');
|
|
70
|
+
(0, cli_utils_1.error)('you have uncommitted changes');
|
|
71
|
+
(0, cli_utils_1.hint)("run 'relq commit -m <message>' to commit");
|
|
72
|
+
(0, cli_utils_1.hint)("run 'relq stash' to stash changes");
|
|
80
73
|
return;
|
|
81
74
|
}
|
|
82
75
|
const state = loadBranchState(projectRoot);
|
|
83
76
|
if (createBranch) {
|
|
84
77
|
if (state.branches[branchName]) {
|
|
85
|
-
|
|
86
|
-
return;
|
|
78
|
+
(0, cli_utils_1.fatal)(`branch '${branchName}' already exists`);
|
|
87
79
|
}
|
|
88
80
|
const head = (0, repo_manager_1.getHead)(projectRoot);
|
|
89
81
|
if (!head) {
|
|
90
|
-
|
|
91
|
-
return;
|
|
82
|
+
(0, cli_utils_1.fatal)('no commits yet', "run 'relq pull' or 'relq import' first");
|
|
92
83
|
}
|
|
93
84
|
state.branches[branchName] = head;
|
|
94
85
|
state.current = branchName;
|
|
95
86
|
saveBranchState(state, projectRoot);
|
|
96
|
-
console.log(
|
|
97
|
-
console.log('');
|
|
87
|
+
console.log(`Switched to a new branch '${branchName}'`);
|
|
98
88
|
return;
|
|
99
89
|
}
|
|
100
90
|
if (!state.branches[branchName]) {
|
|
101
|
-
|
|
102
|
-
console.log('');
|
|
91
|
+
(0, cli_utils_1.error)(`pathspec '${branchName}' did not match any branch known to relq`);
|
|
103
92
|
console.log('Available branches:');
|
|
104
93
|
for (const name of Object.keys(state.branches)) {
|
|
105
|
-
console.log(`
|
|
94
|
+
console.log(` ${name}`);
|
|
106
95
|
}
|
|
107
|
-
console.log('');
|
|
108
96
|
return;
|
|
109
97
|
}
|
|
110
98
|
if (state.current === branchName) {
|
|
@@ -119,8 +107,7 @@ async function checkoutCommand(context) {
|
|
|
119
107
|
const targetHash = state.branches[branchName];
|
|
120
108
|
const targetCommit = (0, repo_manager_1.loadCommit)(targetHash, projectRoot);
|
|
121
109
|
if (!targetCommit) {
|
|
122
|
-
|
|
123
|
-
return;
|
|
110
|
+
(0, cli_utils_1.fatal)('cannot find commit for branch');
|
|
124
111
|
}
|
|
125
112
|
if (targetCommit.schema) {
|
|
126
113
|
(0, repo_manager_1.saveSnapshot)(targetCommit.schema, projectRoot);
|
|
@@ -128,7 +115,7 @@ async function checkoutCommand(context) {
|
|
|
128
115
|
(0, repo_manager_1.setHead)(targetHash, projectRoot);
|
|
129
116
|
state.current = branchName;
|
|
130
117
|
saveBranchState(state, projectRoot);
|
|
131
|
-
console.log(
|
|
118
|
+
console.log(`Switched to branch '${branchName}'`);
|
|
132
119
|
console.log('');
|
|
133
120
|
}
|
|
134
121
|
exports.default = checkoutCommand;
|