spine-framework 0.2.4 → 0.2.6

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.
@@ -95,59 +95,69 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
95
95
  const sql = readFileSync(resolve(MIGRATIONS_DIR, file), 'utf8')
96
96
  process.stdout.write(` Applying ${file}... `)
97
97
 
98
- // Split into individual statements so each executes in its own round-trip.
99
- // Running the whole file as one query string causes pg to abort the entire
100
- // batch on the first error, preventing PL/pgSQL EXCEPTION handlers from
101
- // catching expected errors like "relation already exists".
102
- // MUST preserve dollar-quoted blocks ($$ ... $$) which contain semicolons.
103
- const statements: string[] = []
104
- let current = ''
105
- let inDollarQuote = false
106
- let dollarTag = ''
107
- let i = 0
108
- while (i < sql.length) {
109
- if (!inDollarQuote) {
110
- // Check for start of dollar quote: $tag$ or $$
111
- const dollarMatch = sql.slice(i).match(/^(\$[A-Za-z_]*\$)/)
112
- if (dollarMatch) {
113
- inDollarQuote = true
114
- dollarTag = dollarMatch[1]
115
- current += dollarMatch[1]
116
- i += dollarMatch[1].length
117
- continue
118
- }
119
- // Check for statement terminator
120
- if (sql[i] === ';') {
121
- const stmt = current.trim()
122
- if (stmt.length > 0 && !stmt.startsWith('--')) {
123
- statements.push(stmt)
98
+ // Split SQL into statements, being careful to preserve dollar-quoted blocks.
99
+ // Dollar-quoted strings ($$ ... $$ or $tag$ ... $tag$) can contain semicolons
100
+ // that are NOT statement terminators.
101
+ function splitSqlStatements(sql: string): string[] {
102
+ const statements: string[] = []
103
+ let current = ''
104
+ let inDollarQuote = false
105
+ let dollarTag = ''
106
+ let i = 0
107
+
108
+ while (i < sql.length) {
109
+ if (!inDollarQuote) {
110
+ // Check for start of dollar quote: $tag$ or $$
111
+ const dollarMatch = sql.slice(i).match(/^(\$[A-Za-z_]*\$)/)
112
+ if (dollarMatch) {
113
+ inDollarQuote = true
114
+ dollarTag = dollarMatch[1]
115
+ current += dollarMatch[1]
116
+ i += dollarMatch[1].length
117
+ continue
118
+ }
119
+ // Check for statement terminator (semicolon outside dollar quote)
120
+ if (sql[i] === ';') {
121
+ const stmt = current.trim()
122
+ if (stmt.length > 0 && !stmt.startsWith('--')) {
123
+ statements.push(stmt + ';')
124
+ }
125
+ current = ''
126
+ i++
127
+ continue
128
+ }
129
+ } else {
130
+ // We're inside a dollar-quoted string
131
+ // Check for end of dollar quote
132
+ if (sql.slice(i, i + dollarTag.length) === dollarTag) {
133
+ inDollarQuote = false
134
+ current += dollarTag
135
+ i += dollarTag.length
136
+ continue
124
137
  }
125
- current = ''
126
- i++
127
- continue
128
- }
129
- } else {
130
- // Check for end of dollar quote
131
- const endTag = dollarTag
132
- if (sql.slice(i, i + endTag.length) === endTag) {
133
- inDollarQuote = false
134
- current += endTag
135
- i += endTag.length
136
- continue
137
138
  }
139
+ current += sql[i]
140
+ i++
138
141
  }
139
- current += sql[i]
140
- i++
141
- }
142
- // Don't forget the last statement
143
- const last = current.trim()
144
- if (last.length > 0 && !last.startsWith('--')) {
145
- statements.push(last)
142
+
143
+ // Don't forget the last statement (might not have trailing semicolon)
144
+ const last = current.trim()
145
+ if (last.length > 0 && !last.startsWith('--')) {
146
+ statements.push(last + (last.endsWith(';') ? '' : ';'))
147
+ }
148
+
149
+ return statements
146
150
  }
147
151
 
152
+ const statements = splitSqlStatements(sql)
153
+
148
154
  try {
149
- for (const stmt of statements) {
150
- await client.query(stmt + ';')
155
+ for (let i = 0; i < statements.length; i++) {
156
+ const stmt = statements[i]
157
+ if (process.env.SPINE_DEBUG_MIGRATE) {
158
+ console.log(`\n [${i + 1}/${statements.length}] ${stmt.substring(0, 60).replace(/\n/g, ' ')}...`)
159
+ }
160
+ await client.query(stmt)
151
161
  }
152
162
  console.log('✓')
153
163
  applied++
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/migrate.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA4JxC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/migrate.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAsKxC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAevD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "private": false,
5
5
  "description": "Spine — enterprise application framework built on Supabase + Netlify + React",
6
6
  "type": "module",