relq 1.0.120 → 1.0.122
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/diff.cjs +143 -60
- package/dist/cjs/cli/commands/fix.cjs +298 -0
- package/dist/cjs/cli/commands/generate.cjs +217 -59
- package/dist/cjs/cli/commands/import.cjs +1 -0
- package/dist/cjs/cli/commands/pull.cjs +28 -4
- package/dist/cjs/cli/commands/push.cjs +493 -222
- package/dist/cjs/cli/commands/validate.cjs +2 -2
- package/dist/cjs/cli/index.cjs +3 -2
- package/dist/cjs/cli/utils/ast/codegen/constraints.cjs +12 -7
- package/dist/cjs/cli/utils/ast/codegen/defaults.cjs +17 -7
- package/dist/cjs/cli/utils/ast-codegen.cjs +135 -6
- package/dist/cjs/cli/utils/ast-transformer.cjs +122 -60
- package/dist/cjs/cli/utils/config-loader.cjs +3 -3
- package/dist/cjs/cli/utils/migration-generator.cjs +727 -531
- package/dist/cjs/cli/utils/postgres/introspect.cjs +2 -0
- package/dist/cjs/cli/utils/schema-diff.cjs +545 -661
- package/dist/cjs/cli/utils/schema-loader.cjs +101 -1
- package/dist/cjs/cli/utils/schema-surgeon.cjs +493 -0
- package/dist/cjs/cli/utils/schema-to-ast.cjs +66 -15
- package/dist/cjs/cli/utils/snapshot-manager.cjs +20 -1
- package/dist/cjs/cli/utils/source-id-validator.cjs +266 -0
- package/dist/cjs/cli/utils/tracking-id-validator.cjs +180 -0
- package/dist/cjs/cli/utils/type-generator.cjs +9 -1
- package/dist/cjs/cli/utils/types-manager.cjs +45 -9
- package/dist/cjs/cli/utils/ui.cjs +9 -1
- package/dist/cjs/cockroachdb-builder.cjs +9 -2
- package/dist/cjs/errors/relq-errors.cjs +14 -2
- package/dist/cjs/insert/conflict-builder.cjs +44 -29
- package/dist/cjs/insert/insert-builder.cjs +5 -2
- package/dist/cjs/schema-definition/pg-schema/defaults.cjs +1 -0
- package/dist/cjs/schema-definition/pg-schema/index.cjs +13 -2
- package/dist/cjs/schema-definition/pg-schema/pg-policy.cjs +103 -0
- package/dist/cjs/schema-definition/pg-schema/pg-sequence.cjs +5 -4
- package/dist/cjs/schema-definition/pg-schema/pg-view.cjs +79 -10
- package/dist/cjs/schema-definition/pg-schema/table-definition/constraints-builder.cjs +27 -11
- package/dist/cjs/update/update-builder.cjs +1 -1
- package/dist/cockroachdb-builder.d.ts +209 -8
- package/dist/config.d.ts +840 -839
- package/dist/dsql-builder.d.ts +53 -7
- package/dist/esm/cli/commands/diff.js +147 -64
- package/dist/esm/cli/commands/fix.js +263 -0
- package/dist/esm/cli/commands/generate.js +219 -61
- package/dist/esm/cli/commands/import.js +1 -0
- package/dist/esm/cli/commands/pull.js +28 -4
- package/dist/esm/cli/commands/push.js +495 -224
- package/dist/esm/cli/commands/validate.js +2 -2
- package/dist/esm/cli/index.js +3 -2
- package/dist/esm/cli/utils/ast/codegen/constraints.js +12 -7
- package/dist/esm/cli/utils/ast/codegen/defaults.js +17 -7
- package/dist/esm/cli/utils/ast-codegen.js +135 -6
- package/dist/esm/cli/utils/ast-transformer.js +122 -60
- package/dist/esm/cli/utils/config-loader.js +3 -3
- package/dist/esm/cli/utils/migration-generator.js +725 -528
- package/dist/esm/cli/utils/postgres/introspect.js +2 -0
- package/dist/esm/cli/utils/schema-diff.js +545 -653
- package/dist/esm/cli/utils/schema-loader.js +102 -2
- package/dist/esm/cli/utils/schema-surgeon.js +488 -0
- package/dist/esm/cli/utils/schema-to-ast.js +64 -15
- package/dist/esm/cli/utils/snapshot-manager.js +20 -1
- package/dist/esm/cli/utils/source-id-validator.js +263 -0
- package/dist/esm/cli/utils/tracking-id-validator.js +176 -0
- package/dist/esm/cli/utils/type-generator.js +9 -1
- package/dist/esm/cli/utils/types-manager.js +45 -9
- package/dist/esm/cli/utils/ui.js +8 -1
- package/dist/esm/cockroachdb-builder.js +1 -0
- package/dist/esm/errors/relq-errors.js +14 -2
- package/dist/esm/insert/conflict-builder.js +44 -29
- package/dist/esm/insert/insert-builder.js +5 -2
- package/dist/esm/schema-definition/pg-schema/defaults.js +1 -0
- package/dist/esm/schema-definition/pg-schema/index.js +2 -1
- package/dist/esm/schema-definition/pg-schema/pg-policy.js +95 -0
- package/dist/esm/schema-definition/pg-schema/pg-sequence.js +5 -4
- package/dist/esm/schema-definition/pg-schema/pg-view.js +75 -10
- package/dist/esm/schema-definition/pg-schema/table-definition/constraints-builder.js +27 -11
- package/dist/esm/update/update-builder.js +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/nile-builder.d.ts +225 -8
- package/dist/pg-builder.d.ts +225 -8
- package/package.json +14 -15
|
@@ -44,9 +44,69 @@ const env_loader_1 = require("../utils/env-loader.cjs");
|
|
|
44
44
|
const repo_manager_1 = require("../utils/repo-manager.cjs");
|
|
45
45
|
const snapshot_manager_1 = require("../utils/snapshot-manager.cjs");
|
|
46
46
|
const schema_diff_1 = require("../utils/schema-diff.cjs");
|
|
47
|
-
const schema_hash_1 = require("../utils/schema-hash.cjs");
|
|
48
47
|
const migration_generator_1 = require("../utils/migration-generator.cjs");
|
|
49
48
|
const relqignore_1 = require("../utils/relqignore.cjs");
|
|
49
|
+
const schema_loader_1 = require("../utils/schema-loader.cjs");
|
|
50
|
+
const ast_transformer_1 = require("../utils/ast-transformer.cjs");
|
|
51
|
+
function applyIgnorePatterns(comparison, patterns) {
|
|
52
|
+
if (patterns.length === 0)
|
|
53
|
+
return;
|
|
54
|
+
const shouldIgnore = (name) => {
|
|
55
|
+
return patterns.some(pattern => {
|
|
56
|
+
if (pattern.includes('*')) {
|
|
57
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
58
|
+
return regex.test(name);
|
|
59
|
+
}
|
|
60
|
+
return name === pattern;
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
comparison.added.tables = comparison.added.tables.filter(t => !shouldIgnore(t.name));
|
|
64
|
+
comparison.removed.tables = comparison.removed.tables.filter(t => !shouldIgnore(t.name));
|
|
65
|
+
comparison.modified.tables = comparison.modified.tables.filter(t => !shouldIgnore(t.name));
|
|
66
|
+
comparison.renamed.tables = comparison.renamed.tables.filter(t => !shouldIgnore(t.from) && !shouldIgnore(t.to));
|
|
67
|
+
comparison.added.columns = comparison.added.columns.filter(c => !shouldIgnore(c.table));
|
|
68
|
+
comparison.removed.columns = comparison.removed.columns.filter(c => !shouldIgnore(c.table));
|
|
69
|
+
comparison.modified.columns = comparison.modified.columns.filter(c => !shouldIgnore(c.table));
|
|
70
|
+
comparison.renamed.columns = comparison.renamed.columns.filter(c => !shouldIgnore(c.table));
|
|
71
|
+
comparison.added.indexes = comparison.added.indexes.filter(i => !shouldIgnore(i.table));
|
|
72
|
+
comparison.removed.indexes = comparison.removed.indexes.filter(i => !shouldIgnore(i.table));
|
|
73
|
+
comparison.modified.indexes = comparison.modified.indexes.filter(i => !shouldIgnore(i.table));
|
|
74
|
+
comparison.renamed.indexes = comparison.renamed.indexes.filter(i => !shouldIgnore(i.table));
|
|
75
|
+
comparison.added.constraints = comparison.added.constraints.filter(c => !shouldIgnore(c.table));
|
|
76
|
+
comparison.removed.constraints = comparison.removed.constraints.filter(c => !shouldIgnore(c.table));
|
|
77
|
+
comparison.modified.constraints = comparison.modified.constraints.filter(c => !shouldIgnore(c.table));
|
|
78
|
+
comparison.renamed.constraints = comparison.renamed.constraints.filter(c => !shouldIgnore(c.table));
|
|
79
|
+
comparison.added.triggers = comparison.added.triggers.filter(t => !shouldIgnore(t.table));
|
|
80
|
+
comparison.removed.triggers = comparison.removed.triggers.filter(t => !shouldIgnore(t.table));
|
|
81
|
+
comparison.modified.triggers = comparison.modified.triggers.filter(t => !shouldIgnore(t.oldTrigger.table));
|
|
82
|
+
}
|
|
83
|
+
function countComparison(comparison) {
|
|
84
|
+
return {
|
|
85
|
+
tables: { added: comparison.added.tables.length, removed: comparison.removed.tables.length, modified: comparison.modified.tables.length, renamed: comparison.renamed.tables.length },
|
|
86
|
+
columns: { added: comparison.added.columns.length, removed: comparison.removed.columns.length, modified: comparison.modified.columns.length, renamed: comparison.renamed.columns.length },
|
|
87
|
+
indexes: { added: comparison.added.indexes.length, removed: comparison.removed.indexes.length, modified: comparison.modified.indexes.length, renamed: comparison.renamed.indexes.length },
|
|
88
|
+
constraints: { added: comparison.added.constraints.length, removed: comparison.removed.constraints.length, modified: comparison.modified.constraints.length, renamed: comparison.renamed.constraints.length },
|
|
89
|
+
enums: { added: comparison.added.enums.length, removed: comparison.removed.enums.length, modified: comparison.modified.enums.length, renamed: comparison.renamed.enums.length },
|
|
90
|
+
sequences: { added: comparison.added.sequences.length, removed: comparison.removed.sequences.length, modified: comparison.modified.sequences.length, renamed: comparison.renamed.sequences.length },
|
|
91
|
+
functions: { added: comparison.added.functions.length, removed: comparison.removed.functions.length, modified: comparison.modified.functions.length, renamed: comparison.renamed.functions.length },
|
|
92
|
+
views: { added: comparison.added.views.length, removed: comparison.removed.views.length, modified: comparison.modified.views.length },
|
|
93
|
+
triggers: { added: comparison.added.triggers.length, removed: comparison.removed.triggers.length, modified: comparison.modified.triggers.length },
|
|
94
|
+
domains: { added: comparison.added.domains.length, removed: comparison.removed.domains.length, modified: comparison.modified.domains.length },
|
|
95
|
+
compositeTypes: { added: comparison.added.compositeTypes.length, removed: comparison.removed.compositeTypes.length, modified: comparison.modified.compositeTypes.length },
|
|
96
|
+
extensions: { added: comparison.added.extensions.length, removed: comparison.removed.extensions.length },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function renderCategoryLines(label, counts, pushMode) {
|
|
100
|
+
const { added, removed, modified = 0, renamed = 0 } = counts;
|
|
101
|
+
if (added > 0)
|
|
102
|
+
console.log(` ${colors_1.colors.green('+')} ${added} ${label} ${pushMode ? 'to create' : 'in database only'}`);
|
|
103
|
+
if (removed > 0)
|
|
104
|
+
console.log(` ${colors_1.colors.red('-')} ${removed} ${label} ${pushMode ? 'to drop' : 'in local only'}`);
|
|
105
|
+
if (modified > 0)
|
|
106
|
+
console.log(` ${colors_1.colors.yellow('~')} ${modified} ${label} ${pushMode ? 'modified' : 'differ'}`);
|
|
107
|
+
if (renamed > 0)
|
|
108
|
+
console.log(` ${colors_1.colors.cyan('→')} ${renamed} ${label} renamed`);
|
|
109
|
+
}
|
|
50
110
|
exports.default = (0, citty_1.defineCommand)({
|
|
51
111
|
meta: { name: 'diff', description: 'Show schema differences' },
|
|
52
112
|
args: {
|
|
@@ -63,95 +123,118 @@ exports.default = (0, citty_1.defineCommand)({
|
|
|
63
123
|
const connection = config.connection;
|
|
64
124
|
const includeFunctions = config.includeFunctions ?? false;
|
|
65
125
|
const includeTriggers = config.includeTriggers ?? false;
|
|
126
|
+
const schemaPath = (0, config_loader_1.getSchemaPath)(config);
|
|
66
127
|
const snapshotPath = config.sync?.snapshot || '.relq/snapshot.json';
|
|
67
|
-
const snapshot = (0, snapshot_manager_1.loadSnapshot)(snapshotPath);
|
|
68
|
-
if (!snapshot) {
|
|
69
|
-
console.log(`${colors_1.colors.yellow('No local snapshot.')}`);
|
|
70
|
-
console.log(`${colors_1.colors.muted('Run')} ${colors_1.colors.cyan('relq pull')} ${colors_1.colors.muted('first.')}`);
|
|
71
|
-
console.log('');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
128
|
const ignorePatterns = (0, relqignore_1.loadRelqignore)(projectRoot);
|
|
129
|
+
const rawPatterns = ignorePatterns.map(pat => pat.raw);
|
|
75
130
|
const spin = p.spinner();
|
|
76
131
|
try {
|
|
132
|
+
spin.start('Loading local schema file...');
|
|
133
|
+
const { ast: desiredAST } = await (0, schema_loader_1.loadSchemaFile)(schemaPath, projectRoot, config);
|
|
134
|
+
spin.stop('Schema file loaded');
|
|
77
135
|
spin.start(`Connecting to ${(0, env_loader_1.getConnectionDescription)(connection)}...`);
|
|
78
136
|
const dbSchema = await (0, dialect_introspect_1.dialectIntrospect)(config, {
|
|
79
137
|
includeFunctions,
|
|
80
138
|
includeTriggers,
|
|
81
139
|
});
|
|
82
140
|
spin.stop(`Connected — ${dbSchema.tables.length} tables in database`);
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
141
|
+
spin.start('Analyzing database schema...');
|
|
142
|
+
const dbParsedSchema = await (0, ast_transformer_1.introspectedToParsedSchema)(dbSchema);
|
|
143
|
+
const snapshot = (0, snapshot_manager_1.loadSnapshot)(snapshotPath);
|
|
144
|
+
if (snapshot && snapshot.tables) {
|
|
145
|
+
for (const dbTable of dbParsedSchema.tables) {
|
|
146
|
+
const snapTable = snapshot.tables[dbTable.name];
|
|
147
|
+
if (!snapTable)
|
|
148
|
+
continue;
|
|
149
|
+
if (snapTable.trackingId)
|
|
150
|
+
dbTable.trackingId = snapTable.trackingId;
|
|
151
|
+
for (const dbCol of dbTable.columns) {
|
|
152
|
+
const snapCol = snapTable.columns?.[dbCol.name];
|
|
153
|
+
if (snapCol?.trackingId)
|
|
154
|
+
dbCol.trackingId = snapCol.trackingId;
|
|
155
|
+
}
|
|
156
|
+
for (const dbIdx of dbTable.indexes) {
|
|
157
|
+
const snapIdx = snapTable.indexes?.[dbIdx.name];
|
|
158
|
+
if (snapIdx?.trackingId)
|
|
159
|
+
dbIdx.trackingId = snapIdx.trackingId;
|
|
160
|
+
}
|
|
161
|
+
for (const dbCon of dbTable.constraints) {
|
|
162
|
+
const snapCon = snapTable.constraints?.[dbCon.name];
|
|
163
|
+
if (snapCon?.trackingId)
|
|
164
|
+
dbCon.trackingId = snapCon.trackingId;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
spin.stop('Schema analyzed');
|
|
169
|
+
const pushComparison = (0, schema_diff_1.compareSchemas)(dbParsedSchema, desiredAST);
|
|
170
|
+
applyIgnorePatterns(pushComparison, rawPatterns);
|
|
171
|
+
const pullComparison = (0, schema_diff_1.compareSchemas)(desiredAST, dbParsedSchema);
|
|
172
|
+
applyIgnorePatterns(pullComparison, rawPatterns);
|
|
89
173
|
if (args.json) {
|
|
90
174
|
console.log(JSON.stringify({
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
hasPushChanges:
|
|
94
|
-
hasPullChanges:
|
|
175
|
+
push: countComparison(pushComparison),
|
|
176
|
+
pull: countComparison(pullComparison),
|
|
177
|
+
hasPushChanges: pushComparison.hasChanges,
|
|
178
|
+
hasPullChanges: pullComparison.hasChanges,
|
|
95
179
|
}, null, 2));
|
|
96
180
|
return;
|
|
97
181
|
}
|
|
98
|
-
if (!
|
|
182
|
+
if (!pushComparison.hasChanges && !pullComparison.hasChanges) {
|
|
99
183
|
console.log('');
|
|
100
184
|
console.log(`${colors_1.colors.green('Local and database are in sync.')}`);
|
|
101
185
|
console.log('');
|
|
102
186
|
return;
|
|
103
187
|
}
|
|
104
|
-
if (
|
|
105
|
-
const
|
|
188
|
+
if (pushComparison.hasChanges) {
|
|
189
|
+
const c = countComparison(pushComparison);
|
|
106
190
|
console.log('');
|
|
107
191
|
console.log(`${colors_1.colors.bold('Local changes')} ${colors_1.colors.muted('(push to apply)')}`);
|
|
108
192
|
console.log('');
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
193
|
+
renderCategoryLines('table(s)', c.tables, true);
|
|
194
|
+
renderCategoryLines('column(s)', c.columns, true);
|
|
195
|
+
renderCategoryLines('index(es)', c.indexes, true);
|
|
196
|
+
renderCategoryLines('constraint(s)', c.constraints, true);
|
|
197
|
+
renderCategoryLines('enum(s)', c.enums, true);
|
|
198
|
+
renderCategoryLines('sequence(s)', c.sequences, true);
|
|
199
|
+
renderCategoryLines('function(s)', c.functions, true);
|
|
200
|
+
renderCategoryLines('view(s)', c.views, true);
|
|
201
|
+
renderCategoryLines('trigger(s)', c.triggers, true);
|
|
202
|
+
renderCategoryLines('domain(s)', c.domains, true);
|
|
203
|
+
renderCategoryLines('composite type(s)', c.compositeTypes, true);
|
|
204
|
+
renderCategoryLines('extension(s)', c.extensions, true);
|
|
121
205
|
}
|
|
122
|
-
if (
|
|
123
|
-
const
|
|
206
|
+
if (pullComparison.hasChanges) {
|
|
207
|
+
const c = countComparison(pullComparison);
|
|
124
208
|
console.log('');
|
|
125
209
|
console.log(`${colors_1.colors.bold('Database drift')} ${colors_1.colors.muted('(pull to sync)')}`);
|
|
126
210
|
console.log('');
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
211
|
+
renderCategoryLines('table(s)', c.tables, false);
|
|
212
|
+
renderCategoryLines('column(s)', c.columns, false);
|
|
213
|
+
renderCategoryLines('index(es)', c.indexes, false);
|
|
214
|
+
renderCategoryLines('constraint(s)', c.constraints, false);
|
|
215
|
+
renderCategoryLines('enum(s)', c.enums, false);
|
|
216
|
+
renderCategoryLines('sequence(s)', c.sequences, false);
|
|
217
|
+
renderCategoryLines('function(s)', c.functions, false);
|
|
218
|
+
renderCategoryLines('view(s)', c.views, false);
|
|
219
|
+
renderCategoryLines('trigger(s)', c.triggers, false);
|
|
220
|
+
renderCategoryLines('domain(s)', c.domains, false);
|
|
221
|
+
renderCategoryLines('composite type(s)', c.compositeTypes, false);
|
|
222
|
+
renderCategoryLines('extension(s)', c.extensions, false);
|
|
139
223
|
}
|
|
140
|
-
if (args.sql &&
|
|
224
|
+
if (args.sql && pushComparison.hasChanges) {
|
|
141
225
|
console.log('');
|
|
142
226
|
console.log(`${colors_1.colors.bold('SQL Preview')} ${colors_1.colors.muted('(what push would execute)')}`);
|
|
143
227
|
console.log('');
|
|
144
|
-
const migration = (0, migration_generator_1.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const statements = migration.content.split(';').filter(s => s.trim());
|
|
150
|
-
for (const stmt of statements.slice(0, 20)) {
|
|
151
|
-
console.log(` ${colors_1.colors.cyan(stmt.trim())};`);
|
|
228
|
+
const migration = (0, migration_generator_1.generateMigrationFromComparison)(pushComparison);
|
|
229
|
+
const formatted = (0, migration_generator_1.formatMigrationStatements)(migration.up);
|
|
230
|
+
if (formatted.length > 0) {
|
|
231
|
+
for (const line of formatted.slice(0, 30)) {
|
|
232
|
+
console.log(` ${line.startsWith('--') ? colors_1.colors.muted(line) : colors_1.colors.cyan(line)}`);
|
|
152
233
|
}
|
|
153
|
-
|
|
154
|
-
|
|
234
|
+
const stmtCount = formatted.filter(l => l.trim() && !l.trim().startsWith('--')).length;
|
|
235
|
+
const totalStmts = migration.up.length;
|
|
236
|
+
if (totalStmts > 20) {
|
|
237
|
+
console.log(` ${colors_1.colors.muted(`... and ${totalStmts - 20} more statements`)}`);
|
|
155
238
|
}
|
|
156
239
|
}
|
|
157
240
|
else {
|
|
@@ -159,10 +242,10 @@ exports.default = (0, citty_1.defineCommand)({
|
|
|
159
242
|
}
|
|
160
243
|
}
|
|
161
244
|
console.log('');
|
|
162
|
-
if (
|
|
245
|
+
if (pushComparison.hasChanges) {
|
|
163
246
|
console.log(`${colors_1.colors.muted('Run')} ${colors_1.colors.cyan('relq push')} ${colors_1.colors.muted('to apply local changes to database.')}`);
|
|
164
247
|
}
|
|
165
|
-
if (
|
|
248
|
+
if (pullComparison.hasChanges) {
|
|
166
249
|
console.log(`${colors_1.colors.muted('Run')} ${colors_1.colors.cyan('relq pull')} ${colors_1.colors.muted('to sync local with database.')}`);
|
|
167
250
|
}
|
|
168
251
|
console.log('');
|
|
@@ -0,0 +1,298 @@
|
|
|
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
|
+
const citty_1 = require("citty");
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const context_1 = require("../utils/context.cjs");
|
|
40
|
+
const colors_1 = require("../utils/colors.cjs");
|
|
41
|
+
const ui_1 = require("../utils/ui.cjs");
|
|
42
|
+
const config_loader_1 = require("../utils/config-loader.cjs");
|
|
43
|
+
const schema_loader_1 = require("../utils/schema-loader.cjs");
|
|
44
|
+
const tracking_id_validator_1 = require("../utils/tracking-id-validator.cjs");
|
|
45
|
+
const source_id_validator_1 = require("../utils/source-id-validator.cjs");
|
|
46
|
+
const ast_codegen_1 = require("../utils/ast-codegen.cjs");
|
|
47
|
+
const repo_manager_1 = require("../utils/repo-manager.cjs");
|
|
48
|
+
const schema_surgeon_1 = require("../utils/schema-surgeon.cjs");
|
|
49
|
+
function clearDuplicateTrackingIds(schema) {
|
|
50
|
+
let fixCount = 0;
|
|
51
|
+
const tableIds = new Map();
|
|
52
|
+
for (const table of schema.tables) {
|
|
53
|
+
if (table.partitionOf)
|
|
54
|
+
continue;
|
|
55
|
+
if (table.trackingId) {
|
|
56
|
+
if (tableIds.has(table.trackingId)) {
|
|
57
|
+
table.trackingId = undefined;
|
|
58
|
+
fixCount++;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
tableIds.set(table.trackingId, true);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const table of schema.tables) {
|
|
66
|
+
if (table.partitionOf)
|
|
67
|
+
continue;
|
|
68
|
+
const colIds = new Map();
|
|
69
|
+
for (const col of table.columns) {
|
|
70
|
+
if (col.trackingId) {
|
|
71
|
+
if (colIds.has(col.trackingId)) {
|
|
72
|
+
col.trackingId = undefined;
|
|
73
|
+
fixCount++;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
colIds.set(col.trackingId, true);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const idxIds = new Map();
|
|
81
|
+
for (const idx of table.indexes) {
|
|
82
|
+
if (idx.trackingId) {
|
|
83
|
+
if (idxIds.has(idx.trackingId)) {
|
|
84
|
+
idx.trackingId = undefined;
|
|
85
|
+
fixCount++;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
idxIds.set(idx.trackingId, true);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const conIds = new Map();
|
|
93
|
+
for (const con of table.constraints) {
|
|
94
|
+
if (con.trackingId) {
|
|
95
|
+
if (colIds.has(con.trackingId))
|
|
96
|
+
continue;
|
|
97
|
+
if (conIds.has(con.trackingId)) {
|
|
98
|
+
con.trackingId = undefined;
|
|
99
|
+
fixCount++;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
conIds.set(con.trackingId, true);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return fixCount;
|
|
108
|
+
}
|
|
109
|
+
function buildExportMap(schemaModule) {
|
|
110
|
+
const map = new Map();
|
|
111
|
+
for (const [key, value] of Object.entries(schemaModule)) {
|
|
112
|
+
if (!value || typeof value !== 'object')
|
|
113
|
+
continue;
|
|
114
|
+
if (value.$name && typeof value.$name === 'string') {
|
|
115
|
+
map.set(value.$name, key);
|
|
116
|
+
}
|
|
117
|
+
else if (value.$enumName && typeof value.$enumName === 'string') {
|
|
118
|
+
map.set(value.$enumName, key);
|
|
119
|
+
}
|
|
120
|
+
else if (value.$domainName && typeof value.$domainName === 'string') {
|
|
121
|
+
map.set(value.$domainName, key);
|
|
122
|
+
}
|
|
123
|
+
else if (value.$sequenceName && typeof value.$sequenceName === 'string') {
|
|
124
|
+
map.set(value.$sequenceName, key);
|
|
125
|
+
}
|
|
126
|
+
else if (value.$functionName && typeof value.$functionName === 'string') {
|
|
127
|
+
map.set(value.$functionName, key);
|
|
128
|
+
}
|
|
129
|
+
else if (value.$triggerName && typeof value.$triggerName === 'string') {
|
|
130
|
+
map.set(value.$triggerName, key);
|
|
131
|
+
}
|
|
132
|
+
else if (value.$viewName && typeof value.$viewName === 'string') {
|
|
133
|
+
map.set(value.$viewName, key);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return map;
|
|
137
|
+
}
|
|
138
|
+
exports.default = (0, citty_1.defineCommand)({
|
|
139
|
+
meta: { name: 'fix', description: 'Auto-repair tracking ID issues in schema' },
|
|
140
|
+
args: {
|
|
141
|
+
schema: { type: 'positional', description: 'Schema file path (optional)', required: false },
|
|
142
|
+
dryRun: { type: 'boolean', alias: 'dry-run', description: 'Show what would be fixed without writing' },
|
|
143
|
+
},
|
|
144
|
+
async run({ args }) {
|
|
145
|
+
const { config, projectRoot } = await (0, context_1.buildContext)({ requireConfig: true });
|
|
146
|
+
if (!config)
|
|
147
|
+
return (0, ui_1.fatal)('No relq.config.ts found. Run `relq init` first.');
|
|
148
|
+
console.log('');
|
|
149
|
+
let schemaPath;
|
|
150
|
+
if (args.schema) {
|
|
151
|
+
schemaPath = path.resolve(projectRoot, args.schema);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
schemaPath = path.resolve(projectRoot, (0, config_loader_1.getSchemaPath)(config));
|
|
155
|
+
}
|
|
156
|
+
const relativePath = path.relative(process.cwd(), schemaPath);
|
|
157
|
+
if (!fs.existsSync(schemaPath)) {
|
|
158
|
+
return (0, ui_1.fatal)(`Schema file not found: ${relativePath}`);
|
|
159
|
+
}
|
|
160
|
+
console.log(` Scanning ${colors_1.colors.cyan(relativePath)}...`);
|
|
161
|
+
const rawContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
162
|
+
const sourceIssues = (0, source_id_validator_1.validateSourceTrackingIds)(schemaPath, rawContent);
|
|
163
|
+
let result;
|
|
164
|
+
try {
|
|
165
|
+
result = await (0, schema_loader_1.loadSchemaFile)(schemaPath, projectRoot, config);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return (0, ui_1.fatal)(`Failed to load schema: ${err.message}`);
|
|
169
|
+
}
|
|
170
|
+
const { ast: parsedSchema } = result;
|
|
171
|
+
const runtimeIssues = (0, tracking_id_validator_1.validateTrackingIds)(parsedSchema);
|
|
172
|
+
const runtimeDuplicates = runtimeIssues.filter(i => i.kind === 'duplicate');
|
|
173
|
+
const runtimeMissing = runtimeIssues.filter(i => i.kind === 'missing');
|
|
174
|
+
const sourceLocations = new Set(sourceIssues.map(i => i.location));
|
|
175
|
+
const extraRuntimeMissing = runtimeMissing.filter(i => !sourceLocations.has(i.location));
|
|
176
|
+
const issuesBefore = [...sourceIssues, ...extraRuntimeMissing, ...runtimeDuplicates];
|
|
177
|
+
if (issuesBefore.length === 0) {
|
|
178
|
+
console.log('');
|
|
179
|
+
console.log(` ${colors_1.colors.green('✔')} ${colors_1.colors.bold('No issues found')} — schema tracking IDs are clean.`);
|
|
180
|
+
console.log('');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const missingIssues = issuesBefore.filter(i => i.kind === 'missing');
|
|
184
|
+
const duplicateIssues = issuesBefore.filter(i => i.kind === 'duplicate');
|
|
185
|
+
if (args.dryRun) {
|
|
186
|
+
console.log('');
|
|
187
|
+
console.log((0, tracking_id_validator_1.formatTrackingIdIssues)(issuesBefore));
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log(colors_1.colors.dim(' Dry run — no changes written.'));
|
|
190
|
+
console.log('');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
for (const issue of sourceIssues) {
|
|
194
|
+
if (issue.kind !== 'missing')
|
|
195
|
+
continue;
|
|
196
|
+
const parts = issue.location.split('.');
|
|
197
|
+
const tableName = parts[0];
|
|
198
|
+
const entityName = parts.slice(1).join('.');
|
|
199
|
+
const table = parsedSchema.tables.find(t => t.name === tableName);
|
|
200
|
+
if (!table)
|
|
201
|
+
continue;
|
|
202
|
+
if (issue.entity === 'index' && entityName) {
|
|
203
|
+
const idx = table.indexes.find(i => i.name === entityName);
|
|
204
|
+
if (idx)
|
|
205
|
+
idx.trackingId = undefined;
|
|
206
|
+
}
|
|
207
|
+
else if (issue.entity === 'constraint' && entityName) {
|
|
208
|
+
const con = table.constraints.find(c => c.name === entityName);
|
|
209
|
+
if (con)
|
|
210
|
+
con.trackingId = undefined;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const dupsFixed = clearDuplicateTrackingIds(parsedSchema);
|
|
214
|
+
(0, ast_codegen_1.assignTrackingIds)(parsedSchema);
|
|
215
|
+
const { createJiti } = await Promise.resolve().then(() => __importStar(require('jiti')));
|
|
216
|
+
const jiti = createJiti(projectRoot, { interopDefault: true });
|
|
217
|
+
const schemaModule = await jiti.import(schemaPath);
|
|
218
|
+
const exportMap = buildExportMap(schemaModule);
|
|
219
|
+
const tableIssues = issuesBefore.filter(i => i.entity === 'table' || i.entity === 'column' || i.entity === 'index' || i.entity === 'constraint');
|
|
220
|
+
const edits = (0, schema_surgeon_1.computeTrackingIdEdits)(schemaPath, rawContent, tableIssues, parsedSchema, exportMap);
|
|
221
|
+
const enumNames = new Set(parsedSchema.enums.map(e => e.name));
|
|
222
|
+
const domainNames = new Set(parsedSchema.domains.map(d => d.name));
|
|
223
|
+
const seqNames = new Set(parsedSchema.sequences.map(s => s.name));
|
|
224
|
+
for (const issue of issuesBefore) {
|
|
225
|
+
if (issue.entity !== 'table')
|
|
226
|
+
continue;
|
|
227
|
+
const name = issue.location;
|
|
228
|
+
if (enumNames.has(name)) {
|
|
229
|
+
const enumEdits = (0, schema_surgeon_1.computeTopLevelEdits)(schemaPath, rawContent, 'enum', parsedSchema.enums, exportMap, [issue]);
|
|
230
|
+
edits.push(...enumEdits);
|
|
231
|
+
}
|
|
232
|
+
else if (domainNames.has(name)) {
|
|
233
|
+
const domainEdits = (0, schema_surgeon_1.computeTopLevelEdits)(schemaPath, rawContent, 'domain', parsedSchema.domains, exportMap, [issue]);
|
|
234
|
+
edits.push(...domainEdits);
|
|
235
|
+
}
|
|
236
|
+
else if (seqNames.has(name)) {
|
|
237
|
+
const seqEdits = (0, schema_surgeon_1.computeTopLevelEdits)(schemaPath, rawContent, 'sequence', parsedSchema.sequences, exportMap, [issue]);
|
|
238
|
+
edits.push(...seqEdits);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (edits.length === 0) {
|
|
242
|
+
console.log('');
|
|
243
|
+
console.log(colors_1.colors.dim(' Could not locate all entities in source — falling back to regeneration.'));
|
|
244
|
+
const { generateTypeScriptFromAST } = await Promise.resolve().then(() => __importStar(require("../utils/ast-codegen.cjs")));
|
|
245
|
+
const { detectDialect, getBuilderImportPath } = await Promise.resolve().then(() => __importStar(require("../utils/dialect-router.cjs")));
|
|
246
|
+
const dialect = detectDialect(config);
|
|
247
|
+
const builderImportPath = getBuilderImportPath(dialect);
|
|
248
|
+
const schemaBaseName = path.basename(schemaPath, '.ts');
|
|
249
|
+
const typesImportPath = `./${schemaBaseName}.types`;
|
|
250
|
+
const typescript = generateTypeScriptFromAST(parsedSchema, {
|
|
251
|
+
camelCase: config.generate?.camelCase ?? true,
|
|
252
|
+
importPath: builderImportPath,
|
|
253
|
+
includeFunctions: false,
|
|
254
|
+
includeTriggers: false,
|
|
255
|
+
columnTypeMap: {},
|
|
256
|
+
typesImportPath,
|
|
257
|
+
});
|
|
258
|
+
fs.writeFileSync(schemaPath, typescript, 'utf-8');
|
|
259
|
+
const fileHash = (0, repo_manager_1.hashFileContent)(typescript);
|
|
260
|
+
(0, repo_manager_1.saveFileHash)(fileHash, projectRoot);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const fixed = (0, schema_surgeon_1.applyEdits)(rawContent, edits);
|
|
264
|
+
fs.writeFileSync(schemaPath, fixed, 'utf-8');
|
|
265
|
+
const fileHash = (0, repo_manager_1.hashFileContent)(fixed);
|
|
266
|
+
(0, repo_manager_1.saveFileHash)(fileHash, projectRoot);
|
|
267
|
+
}
|
|
268
|
+
const fixedContent = fs.readFileSync(schemaPath, 'utf-8');
|
|
269
|
+
const sourceIssuesAfter = (0, source_id_validator_1.validateSourceTrackingIds)(schemaPath, fixedContent);
|
|
270
|
+
const reloaded = await (0, schema_loader_1.loadSchemaFile)(schemaPath, projectRoot, config);
|
|
271
|
+
const runtimeIssuesAfter = (0, tracking_id_validator_1.validateTrackingIds)(reloaded.ast);
|
|
272
|
+
const remainingErrors = [
|
|
273
|
+
...sourceIssuesAfter,
|
|
274
|
+
...runtimeIssuesAfter.filter(i => i.severity === 'error'),
|
|
275
|
+
];
|
|
276
|
+
if (remainingErrors.length > 0) {
|
|
277
|
+
console.log('');
|
|
278
|
+
console.log((0, tracking_id_validator_1.formatTrackingIdIssues)(remainingErrors));
|
|
279
|
+
console.log('');
|
|
280
|
+
return (0, ui_1.fatal)('Some issues could not be auto-fixed.');
|
|
281
|
+
}
|
|
282
|
+
console.log('');
|
|
283
|
+
console.log(` ${colors_1.colors.green('✔')} ${colors_1.colors.bold('All issues fixed')}`);
|
|
284
|
+
console.log(colors_1.colors.dim(' ───────────────────────────────────'));
|
|
285
|
+
if (edits.length > 0) {
|
|
286
|
+
console.log(` ${colors_1.colors.green('●')} ${edits.length} surgical edit(s) applied`);
|
|
287
|
+
}
|
|
288
|
+
if (missingIssues.length > 0) {
|
|
289
|
+
console.log(` ${colors_1.colors.green('+')} ${missingIssues.length} missing ID(s) generated`);
|
|
290
|
+
}
|
|
291
|
+
if (dupsFixed > 0) {
|
|
292
|
+
console.log(` ${colors_1.colors.green('~')} ${dupsFixed} duplicate ID(s) regenerated`);
|
|
293
|
+
}
|
|
294
|
+
console.log('');
|
|
295
|
+
console.log(` Updated ${colors_1.colors.cyan(relativePath)}`);
|
|
296
|
+
console.log('');
|
|
297
|
+
},
|
|
298
|
+
});
|