@soulbatical/tetra-dev-toolkit 1.8.1 → 1.8.2

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.
@@ -42,6 +42,39 @@ function scanDatabaseNaming(projectPath, options = {}) {
42
42
 
43
43
  if (sqlFiles.length === 0) return { totalFields: 0, compliant: 0, violations: [], compliancePercent: 100 }
44
44
 
45
+ // Sort migration files chronologically so we can track renames
46
+ sqlFiles.sort()
47
+
48
+ // First pass: collect all renames and late-added columns from ALL migration files.
49
+ // These are used to suppress violations from earlier migrations.
50
+ const renamedColumns = new Map() // "table.old_col" → "new_col"
51
+ const renamedIndexes = new Map() // "old_index" → "new_index"
52
+ const lateAddedColumns = new Map() // "table.col" → true (columns added via ALTER TABLE ADD COLUMN)
53
+
54
+ for (const filePath of sqlFiles) {
55
+ let content
56
+ try { content = readFileSync(filePath, 'utf-8') } catch { continue }
57
+
58
+ // Track RENAME COLUMN: ALTER TABLE x RENAME COLUMN old TO new
59
+ 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
+ while ((m = renameColRegex.exec(content)) !== null) {
62
+ renamedColumns.set(`${m[1]}.${m[2]}`, m[3])
63
+ }
64
+
65
+ // Track RENAME INDEX: ALTER INDEX old RENAME TO new
66
+ const renameIdxRegex = /ALTER\s+INDEX\s+(?:IF\s+EXISTS\s+)?["']?(\w+)["']?\s+RENAME\s+TO\s+["']?(\w+)["']?/gi
67
+ while ((m = renameIdxRegex.exec(content)) !== null) {
68
+ renamedIndexes.set(m[1], m[2])
69
+ }
70
+
71
+ // Track ADD COLUMN (timestamps added later fix "missing timestamps" violations)
72
+ const addColRegex = /ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:public\.)?["']?(\w+)["']?\s+ADD\s+(?:COLUMN\s+)?(?:IF\s+NOT\s+EXISTS\s+)?["']?(\w+)["']?\s+\w+/gi
73
+ while ((m = addColRegex.exec(content)) !== null) {
74
+ lateAddedColumns.set(`${m[1]}.${m[2]}`, true)
75
+ }
76
+ }
77
+
45
78
  const globalColumns = {}
46
79
 
47
80
  const checkColumn = (tableName, colName, colType, body, fileName) => {
@@ -140,6 +173,59 @@ function scanDatabaseNaming(projectPath, options = {}) {
140
173
  }
141
174
  }
142
175
 
176
+ // ── Filter violations resolved by later migrations ──
177
+
178
+ // Helper: check if a column was renamed to a compliant name in a later migration
179
+ const isColumnRenamed = (table, col) => {
180
+ const key = `${table}.${col}`
181
+ return renamedColumns.has(key)
182
+ }
183
+
184
+ // Helper: check if an index was renamed to a compliant name in a later migration
185
+ const isIndexRenamed = (indexName) => {
186
+ return renamedIndexes.has(indexName)
187
+ }
188
+
189
+ // Helper: check if a missing timestamp was added in a later migration
190
+ const isTimestampAddedLater = (table, col) => {
191
+ return lateAddedColumns.has(`${table}.${col}`)
192
+ }
193
+
194
+ // Filter out resolved violations
195
+ 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
200
+ return false
201
+ }
202
+
203
+ // Index violations: 'Index "name" should use idx_...'
204
+ const idxMatch = v.match(/Index\s+"(\w+)"\s+should/)
205
+ if (idxMatch && isIndexRenamed(idxMatch[1])) {
206
+ compliant++
207
+ return false
208
+ }
209
+
210
+ // Missing timestamp violations: 'Table "x" missing created_at, updated_at'
211
+ const tsMatch = v.match(/Table\s+"(\w+)"\s+missing\s+(.+?)(?:\s+\()/)
212
+ if (tsMatch) {
213
+ const table = tsMatch[1]
214
+ const missingCols = tsMatch[2].split(/,\s*/)
215
+ const allFixed = missingCols.every(col => isTimestampAddedLater(table, col.trim()))
216
+ if (allFixed) {
217
+ compliant++
218
+ return false
219
+ }
220
+ }
221
+
222
+ return true
223
+ })
224
+
225
+ // Replace violations array with filtered version
226
+ violations.length = 0
227
+ violations.push(...unresolvedViolations)
228
+
143
229
  // Consistency checks
144
230
  const consistencyGroups = [
145
231
  { concept: 'creation timestamp', preferred: 'created_at', alternatives: ['created_on', 'creation_date', 'inserted_at', 'date_created'] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-dev-toolkit",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },