11import { type Command } from 'commander' ;
2- import { intro , group , cancel , outro } from '@clack/prompts' ;
2+ import { intro , log , confirm , cancel , outro } from '@clack/prompts' ;
33import chalk from 'chalk' ;
44import { copyMigrations } from './copy-migrations.js' ;
55import { updateConfigToml } from './update-config-toml.js' ;
@@ -16,84 +16,81 @@ export default (program: Command) => {
1616 . action ( async ( options ) => {
1717 intro ( 'Installing pgflow in your Supabase project' ) ;
1818
19- // Use the group feature to organize installation steps
20- const results = await group (
21- {
22- // Step 1: Determine Supabase path
23- supabasePath : ( ) =>
24- supabasePathPrompt ( { supabasePath : options . supabasePath } ) ,
25-
26- // Step 2: Update config.toml
27- configUpdate : async ( { results : { supabasePath } } ) => {
28- if ( ! supabasePath ) return false ;
29-
30- return await updateConfigToml ( {
31- supabasePath,
32- autoConfirm : options . yes ,
33- } ) ;
34- } ,
35-
36- // Step 3: Copy migrations
37- migrations : async ( { results : { supabasePath } } ) => {
38- if ( ! supabasePath ) return false ;
39-
40- return await copyMigrations ( {
41- supabasePath,
42- autoConfirm : options . yes ,
43- } ) ;
44- } ,
45-
46- // Step 4: Create ControlPlane edge function
47- edgeFunction : async ( { results : { supabasePath } } ) => {
48- if ( ! supabasePath ) return false ;
49-
50- return await createEdgeFunction ( {
51- supabasePath,
52- autoConfirm : options . yes ,
53- } ) ;
54- } ,
55-
56- // Step 5: Update environment variables
57- envFile : async ( { results : { supabasePath } } ) => {
58- if ( ! supabasePath ) return false ;
59-
60- return await updateEnvFile ( {
61- supabasePath,
62- autoConfirm : options . yes ,
63- } ) ;
64- } ,
65- } ,
66- {
67- // Handle cancellation
68- onCancel : ( ) => {
69- cancel ( 'Installation cancelled' ) ;
70- process . exit ( 1 ) ;
71- } ,
19+ // Step 1: Get supabase path
20+ const supabasePathResult = await supabasePathPrompt ( {
21+ supabasePath : options . supabasePath ,
22+ } ) ;
23+
24+ if ( ! supabasePathResult || typeof supabasePathResult === 'symbol' ) {
25+ cancel ( 'Installation cancelled - valid Supabase path is required' ) ;
26+ process . exit ( 1 ) ;
27+ }
28+
29+ const supabasePath = supabasePathResult ;
30+
31+ // Step 2: Show summary and get single confirmation
32+ const summaryMsg = [
33+ 'This will:' ,
34+ '' ,
35+ ` • Update ${ chalk . cyan ( 'supabase/config.toml' ) } ${ chalk . dim ( '(enable pooler, per_worker runtime)' ) } ` ,
36+ ` • Add pgflow migrations to ${ chalk . cyan ( 'supabase/migrations/' ) } ` ,
37+ ` • Create Control Plane in ${ chalk . cyan ( 'supabase/functions/pgflow/' ) } ` ,
38+ ` • Configure ${ chalk . cyan ( 'supabase/functions/.env' ) } ` ,
39+ '' ,
40+ ` ${ chalk . green ( '✓ Safe to re-run - completed steps will be skipped' ) } ` ,
41+ ] . join ( '\n' ) ;
42+
43+ log . info ( summaryMsg ) ;
44+
45+ let shouldProceed = options . yes ;
46+
47+ if ( ! options . yes ) {
48+ const confirmResult = await confirm ( {
49+ message : 'Proceed?' ,
50+ } ) ;
51+
52+ if ( confirmResult !== true ) {
53+ cancel ( 'Installation cancelled' ) ;
54+ process . exit ( 1 ) ;
7255 }
73- ) ;
7456
75- // Extract the results from the group operation
76- const supabasePath = results . supabasePath ;
77- const configUpdate = results . configUpdate ;
78- const migrations = results . migrations ;
79- const edgeFunction = results . edgeFunction ;
80- const envFile = results . envFile ;
57+ shouldProceed = true ;
58+ }
8159
82- // Exit if supabasePath is null (validation failed or user cancelled)
83- if ( ! supabasePath ) {
84- cancel ( 'Installation cancelled - valid Supabase path is required' ) ;
60+ if ( ! shouldProceed ) {
61+ cancel ( 'Installation cancelled' ) ;
8562 process . exit ( 1 ) ;
8663 }
8764
88- // Show completion message
65+ // Step 3: Run all installation steps with autoConfirm
66+ const configUpdate = await updateConfigToml ( {
67+ supabasePath,
68+ autoConfirm : true ,
69+ } ) ;
70+
71+ const migrations = await copyMigrations ( {
72+ supabasePath,
73+ autoConfirm : true ,
74+ } ) ;
75+
76+ const edgeFunction = await createEdgeFunction ( {
77+ supabasePath,
78+ autoConfirm : true ,
79+ } ) ;
80+
81+ const envFile = await updateEnvFile ( {
82+ supabasePath,
83+ autoConfirm : true ,
84+ } ) ;
85+
86+ // Step 4: Show completion message
8987 const outroMessages : string [ ] = [ ] ;
9088
91- // Always start with a bolded acknowledgement
9289 if ( migrations || configUpdate || edgeFunction || envFile ) {
93- outroMessages . push ( chalk . bold ( 'Installation complete!' ) ) ;
90+ outroMessages . push ( chalk . green . bold ( '✓ Installation complete!' ) ) ;
9491 } else {
9592 outroMessages . push (
96- chalk . bold ( 'pgflow is already installed - no changes needed!' )
93+ chalk . green . bold ( '✓ pgflow is already installed - no changes needed!' )
9794 ) ;
9895 }
9996
@@ -121,7 +118,6 @@ export default (program: Command) => {
121118 ` ${ stepNumber } . Create your first flow: ${ chalk . blue . underline ( 'https://pgflow.dev/getting-started/create-first-flow/' ) } `
122119 ) ;
123120
124- // Single outro for all paths
125121 outro ( outroMessages . join ( '\n' ) ) ;
126122 } ) ;
127123} ;
0 commit comments