@soulbatical/tetra-dev-toolkit 1.8.2 → 1.8.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.
|
@@ -47,6 +47,7 @@ function scanDatabaseNaming(projectPath, options = {}) {
|
|
|
47
47
|
|
|
48
48
|
// First pass: collect all renames and late-added columns from ALL migration files.
|
|
49
49
|
// These are used to suppress violations from earlier migrations.
|
|
50
|
+
const renamedTables = new Map() // "old_table" → "new_table"
|
|
50
51
|
const renamedColumns = new Map() // "table.old_col" → "new_col"
|
|
51
52
|
const renamedIndexes = new Map() // "old_index" → "new_index"
|
|
52
53
|
const lateAddedColumns = new Map() // "table.col" → true (columns added via ALTER TABLE ADD COLUMN)
|
|
@@ -55,9 +56,15 @@ function scanDatabaseNaming(projectPath, options = {}) {
|
|
|
55
56
|
let content
|
|
56
57
|
try { content = readFileSync(filePath, 'utf-8') } catch { continue }
|
|
57
58
|
|
|
59
|
+
// Track RENAME TABLE: ALTER TABLE old RENAME TO new
|
|
60
|
+
const renameTableRegex = /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:public\.)?["']?(\w+)["']?\s+RENAME\s+TO\s+["']?(\w+)["']?/gi
|
|
61
|
+
let m
|
|
62
|
+
while ((m = renameTableRegex.exec(content)) !== null) {
|
|
63
|
+
renamedTables.set(m[1], m[2])
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
// Track RENAME COLUMN: ALTER TABLE x RENAME COLUMN old TO new
|
|
59
67
|
const renameColRegex = /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:public\.)?["']?(\w+)["']?\s+RENAME\s+COLUMN\s+["']?(\w+)["']?\s+TO\s+["']?(\w+)["']?/gi
|
|
60
|
-
let m
|
|
61
68
|
while ((m = renameColRegex.exec(content)) !== null) {
|
|
62
69
|
renamedColumns.set(`${m[1]}.${m[2]}`, m[3])
|
|
63
70
|
}
|
|
@@ -175,44 +182,81 @@ function scanDatabaseNaming(projectPath, options = {}) {
|
|
|
175
182
|
|
|
176
183
|
// ── Filter violations resolved by later migrations ──
|
|
177
184
|
|
|
178
|
-
//
|
|
185
|
+
// Resolve a table name through renames: la_reflections → stella_reflections
|
|
186
|
+
const resolveTable = (table) => {
|
|
187
|
+
return renamedTables.get(table) || table
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Helper: check if a column was renamed (on original or renamed table)
|
|
179
191
|
const isColumnRenamed = (table, col) => {
|
|
180
|
-
|
|
181
|
-
|
|
192
|
+
if (renamedColumns.has(`${table}.${col}`)) return true
|
|
193
|
+
const resolved = resolveTable(table)
|
|
194
|
+
if (resolved !== table && renamedColumns.has(`${resolved}.${col}`)) return true
|
|
195
|
+
return false
|
|
182
196
|
}
|
|
183
197
|
|
|
184
|
-
// Helper: check if an index was renamed
|
|
198
|
+
// Helper: check if an index was renamed
|
|
185
199
|
const isIndexRenamed = (indexName) => {
|
|
186
200
|
return renamedIndexes.has(indexName)
|
|
187
201
|
}
|
|
188
202
|
|
|
189
|
-
// Helper: check if a missing timestamp was added
|
|
203
|
+
// Helper: check if a missing timestamp was added (on original or renamed table)
|
|
190
204
|
const isTimestampAddedLater = (table, col) => {
|
|
191
|
-
|
|
205
|
+
if (lateAddedColumns.has(`${table}.${col}`)) return true
|
|
206
|
+
const resolved = resolveTable(table)
|
|
207
|
+
if (resolved !== table && lateAddedColumns.has(`${resolved}.${col}`)) return true
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Helper: check if a table was renamed (violations on old name are resolved)
|
|
212
|
+
const isTableRenamed = (table) => {
|
|
213
|
+
return renamedTables.has(table)
|
|
192
214
|
}
|
|
193
215
|
|
|
194
216
|
// Filter out resolved violations
|
|
195
217
|
const unresolvedViolations = violations.filter(v => {
|
|
196
|
-
//
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
218
|
+
// Extract table name from any violation that mentions "table.col" or Table "name"
|
|
219
|
+
const tableColMatch = v.match(/"(\w+)\.(\w+)"/)
|
|
220
|
+
const tableOnlyMatch = v.match(/Table\s+"(\w+)"/)
|
|
221
|
+
const table = tableColMatch ? tableColMatch[1] : (tableOnlyMatch ? tableOnlyMatch[1] : null)
|
|
222
|
+
|
|
223
|
+
// If the table itself was renamed, ALL violations on the old name are resolved
|
|
224
|
+
// (the new table name will be checked separately if it appears in a CREATE TABLE)
|
|
225
|
+
if (table && isTableRenamed(table)) {
|
|
226
|
+
compliant++
|
|
200
227
|
return false
|
|
201
228
|
}
|
|
202
229
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
if (idxMatch && isIndexRenamed(idxMatch[1])) {
|
|
230
|
+
// Column violations: "Boolean/JSON/Column/PK/FK "table.col" ..."
|
|
231
|
+
if (tableColMatch && isColumnRenamed(tableColMatch[1], tableColMatch[2])) {
|
|
206
232
|
compliant++
|
|
207
233
|
return false
|
|
208
234
|
}
|
|
209
235
|
|
|
236
|
+
// Index violations: 'Index "name" should use idx_...'
|
|
237
|
+
const idxMatch = v.match(/Index\s+"(\w+)"\s+should/)
|
|
238
|
+
if (idxMatch) {
|
|
239
|
+
const idxName = idxMatch[1]
|
|
240
|
+
// Direct rename match
|
|
241
|
+
if (isIndexRenamed(idxName)) {
|
|
242
|
+
compliant++
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
// If the index name starts with a renamed table prefix, it's implicitly resolved
|
|
246
|
+
for (const [oldTable] of renamedTables) {
|
|
247
|
+
if (idxName.startsWith(oldTable + '_')) {
|
|
248
|
+
compliant++
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
210
254
|
// Missing timestamp violations: 'Table "x" missing created_at, updated_at'
|
|
211
255
|
const tsMatch = v.match(/Table\s+"(\w+)"\s+missing\s+(.+?)(?:\s+\()/)
|
|
212
256
|
if (tsMatch) {
|
|
213
|
-
const
|
|
257
|
+
const tbl = tsMatch[1]
|
|
214
258
|
const missingCols = tsMatch[2].split(/,\s*/)
|
|
215
|
-
const allFixed = missingCols.every(col => isTimestampAddedLater(
|
|
259
|
+
const allFixed = missingCols.every(col => isTimestampAddedLater(tbl, col.trim()))
|
|
216
260
|
if (allFixed) {
|
|
217
261
|
compliant++
|
|
218
262
|
return false
|