spine-framework 0.1.19 → 0.1.21

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.
@@ -30,9 +30,6 @@ const PACKAGE_ROOT = resolve(__dirname, '../../..')
30
30
 
31
31
  interface InitOptions {
32
32
  dryRun: boolean
33
- url?: string
34
- anonKey?: string
35
- serviceRoleKey?: string
36
33
  }
37
34
 
38
35
  function scaffoldCustomWorkspace(dryRun: boolean): void {
@@ -58,33 +55,44 @@ function scaffoldCustomWorkspace(dryRun: boolean): void {
58
55
  }
59
56
  }
60
57
 
61
- function writeEnvFile(url: string, anonKey: string, serviceRoleKey: string, dryRun: boolean): void {
58
+ function writeEnvFile(dryRun: boolean): void {
62
59
  const envPath = resolve(PROJECT_ROOT, '.env')
60
+
61
+ if (existsSync(envPath)) {
62
+ console.log(' ⏭️ .env already exists, skipping')
63
+ return
64
+ }
65
+
63
66
  const envContent = [
64
- `VITE_SUPABASE_URL=${url}`,
65
- `VITE_SUPABASE_ANON_KEY=${anonKey}`,
66
- `SUPABASE_URL=${url}`,
67
- `SUPABASE_ANON_KEY=${anonKey}`,
68
- `SUPABASE_SERVICE_ROLE_KEY=${serviceRoleKey}`,
69
- `VITE_APP_NAME=Spine`,
70
- `DB_SCHEMA=public`,
67
+ '# ─── Supabase ────────────────────────────────────────────────────────────────',
68
+ '# Get these from: https://supabase.com/dashboard/project/_/settings/api',
69
+ 'SUPABASE_URL=https://your-project.supabase.co',
70
+ 'SUPABASE_ANON_KEY=your-anon-key',
71
+ 'SUPABASE_SERVICE_ROLE_KEY=your-service-role-key',
72
+ '',
73
+ '# DB password for running migrations (direct Postgres connection)',
74
+ '# Get from: https://supabase.com/dashboard/project/_/settings/database',
75
+ 'SUPABASE_DB_PASSWORD=your-db-password',
76
+ '',
77
+ '# ─── Vite / Frontend ─────────────────────────────────────────────────────────',
78
+ 'VITE_SUPABASE_URL=https://your-project.supabase.co',
79
+ 'VITE_SUPABASE_ANON_KEY=your-anon-key',
80
+ 'VITE_APP_NAME=Spine',
81
+ '',
82
+ '# ─── OpenAI ──────────────────────────────────────────────────────────────────',
83
+ 'OPENAI_API_KEY=your-openai-api-key',
84
+ '',
85
+ '# ─── App config ──────────────────────────────────────────────────────────────',
86
+ 'DB_SCHEMA=public',
71
87
  ].join('\n') + '\n'
72
88
 
73
89
  if (dryRun) {
74
- console.log(` [dry-run] Would write .env with SUPABASE_URL=${url}`)
90
+ console.log(' [dry-run] Would write .env with placeholder values')
75
91
  return
76
92
  }
77
93
 
78
- const existed = existsSync(envPath)
79
94
  writeFileSync(envPath, envContent, 'utf8')
80
- console.log(`${existed ? 'Updated' : 'Created'} .env`)
81
-
82
- // Also inject into process.env for the rest of this run
83
- process.env.SUPABASE_URL = url
84
- process.env.SUPABASE_ANON_KEY = anonKey
85
- process.env.SUPABASE_SERVICE_ROLE_KEY = serviceRoleKey
86
- process.env.VITE_SUPABASE_URL = url
87
- process.env.VITE_SUPABASE_ANON_KEY = anonKey
95
+ console.log(' ✓ Created .env')
88
96
  }
89
97
 
90
98
  function copyFrameworkFiles(dryRun: boolean): void {
@@ -129,40 +137,30 @@ function copyFrameworkFiles(dryRun: boolean): void {
129
137
  async function initCommand(options: InitOptions): Promise<void> {
130
138
  console.log('\n🚀 Spine Framework — Init\n')
131
139
 
132
- // Step 1: Write .env
133
- if (options.url && options.anonKey && options.serviceRoleKey) {
134
- console.log('🔑 Step 1: Writing environment configuration...')
135
- writeEnvFile(options.url, options.anonKey, options.serviceRoleKey, options.dryRun)
136
- } else if (!process.env.SUPABASE_URL) {
137
- console.error('❌ No Supabase credentials found.')
138
- console.error(' spine-framework init --url <url> --anon-key <key> --service-role-key <key>')
139
- process.exit(1)
140
- } else {
141
- console.log(' ✓ Using existing .env credentials')
142
- }
143
-
144
- // Step 2: Copy framework files to project root
145
- console.log('\n📦 Step 2: Installing framework files...')
140
+ // Step 1: Copy framework files to project root
141
+ console.log('📦 Step 1: Installing framework files...')
146
142
  copyFrameworkFiles(options.dryRun)
147
143
 
148
- // Step 3: Scaffold custom workspace
149
- console.log('\n📁 Step 3: Scaffolding custom workspace...')
144
+ // Step 2: Scaffold custom workspace
145
+ console.log('\n📁 Step 2: Scaffolding custom workspace...')
150
146
  scaffoldCustomWorkspace(options.dryRun)
151
147
 
148
+ // Step 3: Write .env with placeholders
149
+ console.log('\n🔑 Step 3: Writing environment configuration...')
150
+ writeEnvFile(options.dryRun)
151
+
152
152
  console.log('\n✅ Project initialized!')
153
- console.log('\n Next: apply database migrations:')
154
- console.log(' npx spine-framework migrate --db-password <your-db-password>')
155
- console.log('\n Find your DB password at:')
156
- console.log(' https://supabase.com/dashboard/project/_/settings/database')
153
+ console.log('\n Next steps:')
154
+ console.log(' 1. Fill in your Supabase credentials in .env')
155
+ console.log(' (get them from https://supabase.com/dashboard/project/_/settings/api)')
156
+ console.log(' 2. Run: npx spine-framework migrate --db-password <your-db-password>')
157
+ console.log(' (get DB password from https://supabase.com/dashboard/project/_/settings/database)')
157
158
  }
158
159
 
159
160
  export function registerInitCommands(program: Command) {
160
161
  program
161
162
  .command('init')
162
- .description('Write .env and scaffold the custom/ workspace (no DB changes)')
163
- .option('--url <url>', 'Supabase project URL')
164
- .option('--anon-key <key>', 'Supabase anon key')
165
- .option('--service-role-key <key>', 'Supabase service role key')
163
+ .description('Install framework files, scaffold custom/ workspace, and write .env template')
166
164
  .option('--dry-run', 'Show what would happen without making changes', false)
167
165
  .action(async (opts) => {
168
166
  try {
@@ -28,7 +28,7 @@ const __dirname = dirname(__filename)
28
28
  const MIGRATIONS_DIR = resolve(__dirname, '../../migrations')
29
29
 
30
30
  interface MigrateOptions {
31
- dbPassword: string
31
+ dbPassword?: string
32
32
  dryRun: boolean
33
33
  }
34
34
 
@@ -36,16 +36,21 @@ async function migrateCommand(options: MigrateOptions): Promise<void> {
36
36
  console.log('\n🗄️ Spine Framework — Migrate\n')
37
37
 
38
38
  const supabaseUrl = process.env.SUPABASE_URL
39
- if (!supabaseUrl) {
40
- console.error('❌ SUPABASE_URL not set. Run `spine-framework init` first.')
39
+ if (!supabaseUrl || supabaseUrl === 'https://your-project.supabase.co') {
40
+ console.error('❌ SUPABASE_URL not set. Fill in your .env file first.')
41
+ process.exit(1)
42
+ }
43
+
44
+ const dbPassword = options.dbPassword || process.env.SUPABASE_DB_PASSWORD
45
+ if (!dbPassword || dbPassword === 'your-db-password') {
46
+ console.error('❌ SUPABASE_DB_PASSWORD not set.')
47
+ console.error(' Add it to .env or pass --db-password <password>')
48
+ console.error(' Get it from: https://supabase.com/dashboard/project/_/settings/database')
41
49
  process.exit(1)
42
50
  }
43
51
 
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
52
  const projectRef = supabaseUrl.replace('https://', '').split('.')[0]
48
- const connectionString = `postgresql://postgres:${options.dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
53
+ const connectionString = `postgresql://postgres:${dbPassword}@db.${projectRef}.supabase.co:5432/postgres`
49
54
 
50
55
  if (!existsSync(MIGRATIONS_DIR)) {
51
56
  console.error(`❌ Migrations directory not found: ${MIGRATIONS_DIR}`)
@@ -120,7 +125,7 @@ export function registerMigrateCommands(program: Command) {
120
125
  program
121
126
  .command('migrate')
122
127
  .description('Apply SQL migrations via direct Postgres connection')
123
- .requiredOption('--db-password <password>', 'Supabase database password (from dashboard Settings → Database)')
128
+ .option('--db-password <password>', 'DB password override (default: reads SUPABASE_DB_PASSWORD from .env)')
124
129
  .option('--dry-run', 'Show what would happen without making changes', false)
125
130
  .action(async (opts) => {
126
131
  try {
@@ -14,10 +14,27 @@ if (process.argv.length === 2) {
14
14
  process.exit(0)
15
15
  }
16
16
 
17
- // Pre-parse credential flags so db.ts sees them as env vars at module load time.
17
+ // Pre-parse credential flags and load .env so db.ts sees env vars at module load time.
18
18
  // tsx evaluates db.ts before Commander parses args, so we must inject here.
19
19
  const args = process.argv.slice(2)
20
20
  const childEnv = { ...process.env }
21
+
22
+ // Load .env from the consumer project root (process.cwd())
23
+ const dotenvPath = resolve(process.cwd(), '.env')
24
+ if (fs.existsSync(dotenvPath)) {
25
+ const lines = fs.readFileSync(dotenvPath, 'utf8').split('\n')
26
+ for (const line of lines) {
27
+ const trimmed = line.trim()
28
+ if (!trimmed || trimmed.startsWith('#')) continue
29
+ const eq = trimmed.indexOf('=')
30
+ if (eq === -1) continue
31
+ const key = trimmed.slice(0, eq).trim()
32
+ const val = trimmed.slice(eq + 1).trim()
33
+ if (key && !(key in childEnv)) childEnv[key] = val
34
+ }
35
+ }
36
+
37
+ // CLI flags override .env values
21
38
  for (let i = 0; i < args.length; i++) {
22
39
  if (args[i] === '--url' && args[i + 1]) childEnv.SUPABASE_URL = args[++i]
23
40
  if (args[i] === '--anon-key' && args[i + 1]) childEnv.SUPABASE_ANON_KEY = args[++i]
@@ -1 +1 @@
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;AA2IxC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAiBpD"}
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;AA4IxC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAcpD"}
@@ -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;AAkGxC,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;AAuGxC,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.19",
3
+ "version": "0.1.21",
4
4
  "private": false,
5
5
  "description": "Spine — enterprise application framework built on Supabase + Netlify + React",
6
6
  "type": "module",