spine-framework 0.1.14 → 0.1.16

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.
@@ -24,7 +24,7 @@ import { fileURLToPath } from 'url'
24
24
 
25
25
  const __filename = fileURLToPath(import.meta.url)
26
26
  const __dirname = dirname(__filename)
27
- const PROJECT_ROOT = resolve(__dirname, '../../../')
27
+ const PROJECT_ROOT = process.cwd()
28
28
 
29
29
  interface InitOptions {
30
30
  dryRun: boolean
@@ -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
+ }
@@ -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)
@@ -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.14",
3
+ "version": "0.1.16",
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",