spine-framework 0.2.3 → 0.2.5

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,18 +95,65 @@ 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
- const statements = sql
103
- .split(/;\s*$/m)
104
- .map(s => s.trim())
105
- .filter(s => s.length > 0 && !s.startsWith('--'))
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
137
+ }
138
+ }
139
+ current += sql[i]
140
+ i++
141
+ }
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
150
+ }
151
+
152
+ const statements = splitSqlStatements(sql)
106
153
 
107
154
  try {
108
155
  for (const stmt of statements) {
109
- await client.query(stmt + ';')
156
+ await client.query(stmt)
110
157
  }
111
158
  console.log('✓')
112
159
  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;AAmHxC,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;AAkKxC,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.3",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "description": "Spine — enterprise application framework built on Supabase + Netlify + React",
6
6
  "type": "module",