spine-framework 0.1.13 → 0.1.15
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.
- package/.framework/cli/commands/init.ts +20 -165
- package/.framework/cli/commands/migrate.ts +134 -0
- package/.framework/cli/index.ts +3 -0
- package/dist/cli/commands/init.d.ts +4 -14
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/migrate.d.ts +21 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/package.json +3 -2
|
@@ -5,106 +5,34 @@
|
|
|
5
5
|
* @layer cli
|
|
6
6
|
* @stability stable
|
|
7
7
|
*
|
|
8
|
-
* `spine-framework init` —
|
|
8
|
+
* `spine-framework init` — Write .env and scaffold the custom/ workspace.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* if it doesn't already exist.
|
|
10
|
+
* Intentionally does NOT touch the database — run `spine-framework migrate`
|
|
11
|
+
* after init to apply SQL migrations via a direct Postgres connection.
|
|
13
12
|
*
|
|
14
13
|
* **Usage:**
|
|
15
14
|
* ```bash
|
|
16
|
-
* # Fresh install (agent provides credentials from Supabase dashboard)
|
|
17
15
|
* spine-framework init --url https://xyz.supabase.co --anon-key eyJ... --service-role-key eyJ...
|
|
18
|
-
*
|
|
19
|
-
* # Already have .env configured
|
|
20
|
-
* spine-framework init
|
|
21
|
-
*
|
|
22
|
-
* # DB only, no filesystem changes
|
|
23
|
-
* spine-framework init --skip-scaffold
|
|
16
|
+
* spine-framework migrate --db-password <password>
|
|
24
17
|
* ```
|
|
25
|
-
*
|
|
26
|
-
* When --url/--anon-key/--service-role-key are provided, init writes .env before
|
|
27
|
-
* running migrations — so it works on a completely fresh checkout with no prior config.
|
|
28
18
|
*/
|
|
29
19
|
|
|
30
20
|
import type { Command } from 'commander'
|
|
31
|
-
import { existsSync, mkdirSync,
|
|
21
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs'
|
|
32
22
|
import { resolve, dirname } from 'path'
|
|
33
23
|
import { fileURLToPath } from 'url'
|
|
34
24
|
|
|
35
25
|
const __filename = fileURLToPath(import.meta.url)
|
|
36
26
|
const __dirname = dirname(__filename)
|
|
37
27
|
const PROJECT_ROOT = resolve(__dirname, '../../../')
|
|
38
|
-
const MIGRATIONS_DIR = resolve(__dirname, '../../migrations')
|
|
39
28
|
|
|
40
29
|
interface InitOptions {
|
|
41
|
-
skipScaffold: boolean
|
|
42
30
|
dryRun: boolean
|
|
43
31
|
url?: string
|
|
44
32
|
anonKey?: string
|
|
45
33
|
serviceRoleKey?: string
|
|
46
34
|
}
|
|
47
35
|
|
|
48
|
-
async function runMigration(filename: string, dryRun: boolean): Promise<boolean> {
|
|
49
|
-
const filePath = resolve(MIGRATIONS_DIR, filename)
|
|
50
|
-
if (!existsSync(filePath)) {
|
|
51
|
-
console.error(` ❌ Migration file not found: ${filename}`)
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const sql = readFileSync(filePath, 'utf8')
|
|
56
|
-
|
|
57
|
-
if (dryRun) {
|
|
58
|
-
console.log(` [dry-run] Would execute: ${filename} (${sql.length} chars)`)
|
|
59
|
-
return true
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Use the Supabase SQL endpoint to execute raw DDL.
|
|
63
|
-
// This works on blank projects (no exec_sql RPC needed).
|
|
64
|
-
const supabaseUrl = process.env.SUPABASE_URL
|
|
65
|
-
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
|
|
66
|
-
|
|
67
|
-
if (!supabaseUrl || !serviceKey) {
|
|
68
|
-
console.error(` ❌ Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY`)
|
|
69
|
-
return false
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Use the Supabase /pg/query endpoint for raw SQL execution
|
|
73
|
-
const pgResponse = await fetch(`${supabaseUrl}/pg/query`, {
|
|
74
|
-
method: 'POST',
|
|
75
|
-
headers: {
|
|
76
|
-
'Content-Type': 'application/json',
|
|
77
|
-
'apikey': serviceKey,
|
|
78
|
-
'Authorization': `Bearer ${serviceKey}`,
|
|
79
|
-
},
|
|
80
|
-
body: JSON.stringify({ query: sql })
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
if (pgResponse.ok) return true
|
|
84
|
-
|
|
85
|
-
// Second fallback: use the Supabase Management API SQL endpoint
|
|
86
|
-
// Extract project ref from URL (e.g. https://abcdef.supabase.co → abcdef)
|
|
87
|
-
const projectRef = supabaseUrl.replace('https://', '').split('.')[0]
|
|
88
|
-
|
|
89
|
-
const mgmtResponse = await fetch(
|
|
90
|
-
`https://api.supabase.com/v1/projects/${projectRef}/database/query`,
|
|
91
|
-
{
|
|
92
|
-
method: 'POST',
|
|
93
|
-
headers: {
|
|
94
|
-
'Content-Type': 'application/json',
|
|
95
|
-
'Authorization': `Bearer ${serviceKey}`,
|
|
96
|
-
},
|
|
97
|
-
body: JSON.stringify({ query: sql })
|
|
98
|
-
}
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
if (mgmtResponse.ok) return true
|
|
102
|
-
|
|
103
|
-
const text = await mgmtResponse.text()
|
|
104
|
-
console.error(` ❌ Migration ${filename} failed: ${text}`)
|
|
105
|
-
return false
|
|
106
|
-
}
|
|
107
|
-
|
|
108
36
|
function scaffoldCustomWorkspace(dryRun: boolean): void {
|
|
109
37
|
const dirs = [
|
|
110
38
|
'custom/apps',
|
|
@@ -128,22 +56,6 @@ function scaffoldCustomWorkspace(dryRun: boolean): void {
|
|
|
128
56
|
}
|
|
129
57
|
}
|
|
130
58
|
|
|
131
|
-
async function checkAlreadyInitialized(): Promise<boolean> {
|
|
132
|
-
try {
|
|
133
|
-
const supabaseUrl = process.env.SUPABASE_URL
|
|
134
|
-
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
|
|
135
|
-
if (!supabaseUrl || !serviceKey) return false
|
|
136
|
-
const res = await fetch(
|
|
137
|
-
`${supabaseUrl}/rest/v1/apps?slug=eq.spine-core&select=slug&limit=1`,
|
|
138
|
-
{ headers: { apikey: serviceKey, Authorization: `Bearer ${serviceKey}` } }
|
|
139
|
-
)
|
|
140
|
-
const rows = await res.json() as any[]
|
|
141
|
-
return Array.isArray(rows) && rows.length > 0
|
|
142
|
-
} catch {
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
59
|
function writeEnvFile(url: string, anonKey: string, serviceRoleKey: string, dryRun: boolean): void {
|
|
148
60
|
const envPath = resolve(PROJECT_ROOT, '.env')
|
|
149
61
|
const envContent = [
|
|
@@ -176,93 +88,36 @@ function writeEnvFile(url: string, anonKey: string, serviceRoleKey: string, dryR
|
|
|
176
88
|
async function initCommand(options: InitOptions): Promise<void> {
|
|
177
89
|
console.log('\n🚀 Spine Framework — Init\n')
|
|
178
90
|
|
|
179
|
-
// Step
|
|
91
|
+
// Step 1: Write .env
|
|
180
92
|
if (options.url && options.anonKey && options.serviceRoleKey) {
|
|
181
|
-
console.log('🔑 Step
|
|
93
|
+
console.log('🔑 Step 1: Writing environment configuration...')
|
|
182
94
|
writeEnvFile(options.url, options.anonKey, options.serviceRoleKey, options.dryRun)
|
|
183
|
-
} else if (!process.env.SUPABASE_URL
|
|
95
|
+
} else if (!process.env.SUPABASE_URL) {
|
|
184
96
|
console.error('❌ No Supabase credentials found.')
|
|
185
|
-
console.error(' Provide them via flags:')
|
|
186
97
|
console.error(' spine-framework init --url <url> --anon-key <key> --service-role-key <key>')
|
|
187
|
-
console.error(' Or set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in .env')
|
|
188
98
|
process.exit(1)
|
|
99
|
+
} else {
|
|
100
|
+
console.log(' ✓ Using existing .env credentials')
|
|
189
101
|
}
|
|
190
102
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
console.log(' ⚠️ Database already initialized (spine-core app exists)')
|
|
195
|
-
console.log(' Skipping schema migration. Use "spine-framework migrations" to manage updates.\n')
|
|
196
|
-
|
|
197
|
-
if (!options.skipScaffold) {
|
|
198
|
-
console.log('📁 Checking workspace scaffold...')
|
|
199
|
-
scaffoldCustomWorkspace(options.dryRun)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
console.log('\n✅ Init complete (already initialized)')
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Step 1: Foundation schema
|
|
207
|
-
console.log('📦 Step 1: Applying foundation schema (000_foundation.sql)...')
|
|
208
|
-
const foundationOk = await runMigration('000_foundation.sql', options.dryRun)
|
|
209
|
-
if (!foundationOk && !options.dryRun) {
|
|
210
|
-
console.error('\n❌ Init failed at foundation schema. Check your Supabase connection.')
|
|
211
|
-
process.exit(1)
|
|
212
|
-
}
|
|
213
|
-
console.log(' ✓ Foundation schema applied')
|
|
214
|
-
|
|
215
|
-
// Step 2: Seed data
|
|
216
|
-
console.log('\n🌱 Step 2: Applying seed data (001_seed.sql)...')
|
|
217
|
-
const seedOk = await runMigration('001_seed.sql', options.dryRun)
|
|
218
|
-
if (!seedOk && !options.dryRun) {
|
|
219
|
-
console.error('\n❌ Init failed at seed data.')
|
|
220
|
-
process.exit(1)
|
|
221
|
-
}
|
|
222
|
-
console.log(' ✓ Seed data applied')
|
|
223
|
-
|
|
224
|
-
// Step 3: Record migration versions
|
|
225
|
-
if (!options.dryRun) {
|
|
226
|
-
console.log('\n📝 Step 3: Recording migration versions...')
|
|
227
|
-
const supabaseUrl = process.env.SUPABASE_URL!
|
|
228
|
-
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY!
|
|
229
|
-
await fetch(`${supabaseUrl}/rest/v1/schema_migrations`, {
|
|
230
|
-
method: 'POST',
|
|
231
|
-
headers: {
|
|
232
|
-
'Content-Type': 'application/json',
|
|
233
|
-
'apikey': serviceKey,
|
|
234
|
-
'Authorization': `Bearer ${serviceKey}`,
|
|
235
|
-
'Prefer': 'resolution=merge-duplicates',
|
|
236
|
-
},
|
|
237
|
-
body: JSON.stringify([
|
|
238
|
-
{ version: '000_foundation', applied_at: new Date().toISOString() },
|
|
239
|
-
{ version: '001_seed', applied_at: new Date().toISOString() },
|
|
240
|
-
])
|
|
241
|
-
})
|
|
242
|
-
console.log(' ✓ Migration versions recorded')
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Step 4: Scaffold custom workspace
|
|
246
|
-
if (!options.skipScaffold) {
|
|
247
|
-
console.log('\n📁 Step 4: Scaffolding custom workspace...')
|
|
248
|
-
scaffoldCustomWorkspace(options.dryRun)
|
|
249
|
-
}
|
|
103
|
+
// Step 2: Scaffold custom workspace
|
|
104
|
+
console.log('\n📁 Step 2: Scaffolding custom workspace...')
|
|
105
|
+
scaffoldCustomWorkspace(options.dryRun)
|
|
250
106
|
|
|
251
|
-
console.log('\n✅
|
|
252
|
-
console.log('\n Next
|
|
253
|
-
console.log('
|
|
254
|
-
console.log('
|
|
255
|
-
console.log('
|
|
107
|
+
console.log('\n✅ Project initialized!')
|
|
108
|
+
console.log('\n Next: apply database migrations:')
|
|
109
|
+
console.log(' spine-framework migrate --db-password <your-db-password>')
|
|
110
|
+
console.log('\n Find your DB password at:')
|
|
111
|
+
console.log(' https://supabase.com/dashboard/project/_/settings/database')
|
|
256
112
|
}
|
|
257
113
|
|
|
258
114
|
export function registerInitCommands(program: Command) {
|
|
259
115
|
program
|
|
260
116
|
.command('init')
|
|
261
|
-
.description('
|
|
262
|
-
.option('--url <url>', 'Supabase project URL
|
|
117
|
+
.description('Write .env and scaffold the custom/ workspace (no DB changes)')
|
|
118
|
+
.option('--url <url>', 'Supabase project URL')
|
|
263
119
|
.option('--anon-key <key>', 'Supabase anon key')
|
|
264
120
|
.option('--service-role-key <key>', 'Supabase service role key')
|
|
265
|
-
.option('--skip-scaffold', 'Skip creating custom/ workspace directories', false)
|
|
266
121
|
.option('--dry-run', 'Show what would happen without making changes', false)
|
|
267
122
|
.action(async (opts) => {
|
|
268
123
|
try {
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* @module cli/commands/migrate
|
|
4
|
+
* @audience installer
|
|
5
|
+
* @layer cli
|
|
6
|
+
* @stability stable
|
|
7
|
+
*
|
|
8
|
+
* `spine-framework migrate` — Apply SQL migrations to a Supabase project
|
|
9
|
+
* via a direct Postgres connection.
|
|
10
|
+
*
|
|
11
|
+
* Requires --db-password (the Supabase database password).
|
|
12
|
+
* Get it from: https://supabase.com/dashboard/project/_/settings/database
|
|
13
|
+
*
|
|
14
|
+
* **Usage:**
|
|
15
|
+
* ```bash
|
|
16
|
+
* spine-framework migrate --db-password <password>
|
|
17
|
+
* spine-framework migrate --db-password <password> --dry-run
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { Command } from 'commander'
|
|
22
|
+
import { existsSync, readdirSync, readFileSync } from 'fs'
|
|
23
|
+
import { resolve, dirname } from 'path'
|
|
24
|
+
import { fileURLToPath } from 'url'
|
|
25
|
+
|
|
26
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
27
|
+
const __dirname = dirname(__filename)
|
|
28
|
+
const MIGRATIONS_DIR = resolve(__dirname, '../../migrations')
|
|
29
|
+
|
|
30
|
+
interface MigrateOptions {
|
|
31
|
+
dbPassword: string
|
|
32
|
+
dryRun: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function migrateCommand(options: MigrateOptions): Promise<void> {
|
|
36
|
+
console.log('\n🗄️ Spine Framework — Migrate\n')
|
|
37
|
+
|
|
38
|
+
const supabaseUrl = process.env.SUPABASE_URL
|
|
39
|
+
if (!supabaseUrl) {
|
|
40
|
+
console.error('❌ SUPABASE_URL not set. Run `spine-framework init` first.')
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Derive connection string from Supabase URL
|
|
45
|
+
// Format: https://<ref>.supabase.co → postgres://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres
|
|
46
|
+
// Use direct connection (port 5432) via the db.<ref>.supabase.co host
|
|
47
|
+
const projectRef = supabaseUrl.replace('https://', '').split('.')[0]
|
|
48
|
+
const connectionString = `postgresql://postgres:${options.dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
|
|
49
|
+
|
|
50
|
+
if (!existsSync(MIGRATIONS_DIR)) {
|
|
51
|
+
console.error(`❌ Migrations directory not found: ${MIGRATIONS_DIR}`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const files = readdirSync(MIGRATIONS_DIR)
|
|
56
|
+
.filter(f => f.endsWith('.sql'))
|
|
57
|
+
.sort()
|
|
58
|
+
|
|
59
|
+
if (files.length === 0) {
|
|
60
|
+
console.log(' No migration files found.')
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(` Found ${files.length} migration file(s)\n`)
|
|
65
|
+
|
|
66
|
+
if (options.dryRun) {
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
console.log(` [dry-run] Would apply: ${file}`)
|
|
69
|
+
}
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Dynamically import pg to avoid loading it at module init time
|
|
74
|
+
const { default: pg } = await import('pg')
|
|
75
|
+
const client = new pg.Client({ connectionString })
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await client.connect()
|
|
79
|
+
console.log(' ✓ Connected to database\n')
|
|
80
|
+
} catch (err: any) {
|
|
81
|
+
console.error(`❌ Could not connect to database: ${err.message}`)
|
|
82
|
+
console.error(' Check your --db-password and that the project is active.')
|
|
83
|
+
process.exit(1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let applied = 0
|
|
87
|
+
let failed = 0
|
|
88
|
+
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const sql = readFileSync(resolve(MIGRATIONS_DIR, file), 'utf8')
|
|
91
|
+
process.stdout.write(` Applying ${file}... `)
|
|
92
|
+
try {
|
|
93
|
+
await client.query(sql)
|
|
94
|
+
console.log('✓')
|
|
95
|
+
applied++
|
|
96
|
+
} catch (err: any) {
|
|
97
|
+
console.log('❌')
|
|
98
|
+
console.error(` ${err.message}`)
|
|
99
|
+
failed++
|
|
100
|
+
// Continue applying remaining migrations
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await client.end()
|
|
105
|
+
|
|
106
|
+
console.log(`\n ${applied} applied, ${failed} failed`)
|
|
107
|
+
|
|
108
|
+
if (failed > 0) {
|
|
109
|
+
console.error('\n❌ Some migrations failed. Check errors above.')
|
|
110
|
+
process.exit(1)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log('\n✅ All migrations applied successfully!')
|
|
114
|
+
console.log('\n Next steps:')
|
|
115
|
+
console.log(' 1. spine-framework install-app <app-slug>')
|
|
116
|
+
console.log(' 2. npm run assemble && netlify dev')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function registerMigrateCommands(program: Command) {
|
|
120
|
+
program
|
|
121
|
+
.command('migrate')
|
|
122
|
+
.description('Apply SQL migrations via direct Postgres connection')
|
|
123
|
+
.requiredOption('--db-password <password>', 'Supabase database password (from dashboard → Settings → Database)')
|
|
124
|
+
.option('--dry-run', 'Show what would happen without making changes', false)
|
|
125
|
+
.action(async (opts) => {
|
|
126
|
+
try {
|
|
127
|
+
await migrateCommand(opts)
|
|
128
|
+
} catch (err: any) {
|
|
129
|
+
console.error('Error:', err.message)
|
|
130
|
+
if (process.env.SPINE_CLI_DEBUG) console.error(err.stack)
|
|
131
|
+
process.exit(1)
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
package/.framework/cli/index.ts
CHANGED
|
@@ -66,6 +66,7 @@ const [
|
|
|
66
66
|
{ registerInstallAppCommands },
|
|
67
67
|
{ registerStatusCommands },
|
|
68
68
|
{ registerUninstallAppCommands },
|
|
69
|
+
{ registerMigrateCommands },
|
|
69
70
|
] = await Promise.all([
|
|
70
71
|
import('./commands/auth.ts'),
|
|
71
72
|
import('./commands/pipelines.ts'),
|
|
@@ -82,6 +83,7 @@ const [
|
|
|
82
83
|
import('./commands/install-app.ts'),
|
|
83
84
|
import('./commands/status.ts'),
|
|
84
85
|
import('./commands/uninstall-app.ts'),
|
|
86
|
+
import('./commands/migrate.ts'),
|
|
85
87
|
])
|
|
86
88
|
|
|
87
89
|
registerAuthCommands(program)
|
|
@@ -99,6 +101,7 @@ registerInitCommands(program)
|
|
|
99
101
|
registerInstallAppCommands(program)
|
|
100
102
|
registerStatusCommands(program)
|
|
101
103
|
registerUninstallAppCommands(program)
|
|
104
|
+
registerMigrateCommands(program)
|
|
102
105
|
|
|
103
106
|
program.parseAsync(process.argv).catch((err) => {
|
|
104
107
|
console.error('Error:', err.message)
|
|
@@ -4,26 +4,16 @@
|
|
|
4
4
|
* @layer cli
|
|
5
5
|
* @stability stable
|
|
6
6
|
*
|
|
7
|
-
* `spine-framework init` —
|
|
7
|
+
* `spine-framework init` — Write .env and scaffold the custom/ workspace.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* if it doesn't already exist.
|
|
9
|
+
* Intentionally does NOT touch the database — run `spine-framework migrate`
|
|
10
|
+
* after init to apply SQL migrations via a direct Postgres connection.
|
|
12
11
|
*
|
|
13
12
|
* **Usage:**
|
|
14
13
|
* ```bash
|
|
15
|
-
* # Fresh install (agent provides credentials from Supabase dashboard)
|
|
16
14
|
* spine-framework init --url https://xyz.supabase.co --anon-key eyJ... --service-role-key eyJ...
|
|
17
|
-
*
|
|
18
|
-
* # Already have .env configured
|
|
19
|
-
* spine-framework init
|
|
20
|
-
*
|
|
21
|
-
* # DB only, no filesystem changes
|
|
22
|
-
* spine-framework init --skip-scaffold
|
|
15
|
+
* spine-framework migrate --db-password <password>
|
|
23
16
|
* ```
|
|
24
|
-
*
|
|
25
|
-
* When --url/--anon-key/--service-role-key are provided, init writes .env before
|
|
26
|
-
* running migrations — so it works on a completely fresh checkout with no prior config.
|
|
27
17
|
*/
|
|
28
18
|
import type { Command } from 'commander';
|
|
29
19
|
export declare function registerInitCommands(program: Command): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/init.ts"],"names":[],"mappings":"AACA
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../.framework/cli/commands/init.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA8FxC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAiBpD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cli/commands/migrate
|
|
3
|
+
* @audience installer
|
|
4
|
+
* @layer cli
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* `spine-framework migrate` — Apply SQL migrations to a Supabase project
|
|
8
|
+
* via a direct Postgres connection.
|
|
9
|
+
*
|
|
10
|
+
* Requires --db-password (the Supabase database password).
|
|
11
|
+
* Get it from: https://supabase.com/dashboard/project/_/settings/database
|
|
12
|
+
*
|
|
13
|
+
* **Usage:**
|
|
14
|
+
* ```bash
|
|
15
|
+
* spine-framework migrate --db-password <password>
|
|
16
|
+
* spine-framework migrate --db-password <password> --dry-run
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import type { Command } from 'commander';
|
|
20
|
+
export declare function registerMigrateCommands(program: Command): void;
|
|
21
|
+
//# sourceMappingURL=migrate.d.ts.map
|
|
@@ -0,0 +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;AAkGxC,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.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Spine — enterprise application framework built on Supabase + Netlify + React",
|
|
6
6
|
"type": "module",
|
|
@@ -90,7 +90,8 @@
|
|
|
90
90
|
"jsonwebtoken": "^9.0.3",
|
|
91
91
|
"jwt-decode": "^4.0.0",
|
|
92
92
|
"tsx": "^4.21.0",
|
|
93
|
-
"ws": "^8.21.0"
|
|
93
|
+
"ws": "^8.21.0",
|
|
94
|
+
"pg": "^8.13.0"
|
|
94
95
|
},
|
|
95
96
|
"peerDependencies": {
|
|
96
97
|
"@netlify/functions": "^2.0.0",
|