@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
- // Helper: check if a column was renamed to a compliant name in a later migration
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
- const key = `${table}.${col}`
181
- return renamedColumns.has(key)
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 to a compliant name in a later migration
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 in a later migration
203
+ // Helper: check if a missing timestamp was added (on original or renamed table)
190
204
  const isTimestampAddedLater = (table, col) => {
191
- return lateAddedColumns.has(`${table}.${col}`)
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
- // Column violations: "Boolean/JSON/Column "table.col" ..."
197
- const colMatch = v.match(/(?:Boolean|JSON|Column|PK|FK)\s+"(\w+)\.(\w+)"/)
198
- if (colMatch && isColumnRenamed(colMatch[1], colMatch[2])) {
199
- compliant++ // was a violation, now fixed
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
- // Index violations: 'Index "name" should use idx_...'
204
- const idxMatch = v.match(/Index\s+"(\w+)"\s+should/)
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 table = tsMatch[1]
257
+ const tbl = tsMatch[1]
214
258
  const missingCols = tsMatch[2].split(/,\s*/)
215
- const allFixed = missingCols.every(col => isTimestampAddedLater(table, col.trim()))
259
+ const allFixed = missingCols.every(col => isTimestampAddedLater(tbl, col.trim()))
216
260
  if (allFixed) {
217
261
  compliant++
218
262
  return false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-dev-toolkit",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },