@@ -10,6 +10,7 @@ import {
1010 sortFilesByType ,
1111 withErrorHandler ,
1212} from '../utils/index' ;
13+ import { resolveConfigs } from '../utils/index' ;
1314
1415async function addToXano ( {
1516 componentNames,
@@ -19,17 +20,17 @@ async function addToXano({
1920 componentNames : string [ ] ;
2021 context : CoreContext ;
2122 core : any ;
22- } ) {
23- // [ ] !!! fix: to use the context resolver !!!!
24- const startDir = process . cwd ( ) ;
25- const { instanceConfig , workspaceConfig , branchConfig } = await core . loadAndValidateContext ( {
26- instance : context . instance ,
27- workspace : context . workspace ,
28- branch : context . branch ,
29- startDir ,
23+ } ) : Promise < {
24+ installed : Array < { component : string ; file : string ; response : any } > ;
25+ failed : Array < { component : string ; file : string ; error : string ; response ?: any } > ;
26+ skipped : Array < any > ;
27+ } > {
28+ const { instanceConfig , workspaceConfig , branchConfig } = await resolveConfigs ( {
29+ cliContext : context ,
30+ core ,
3031 } ) ;
3132
32- intro ( 'Add components to your Xano instance' ) ;
33+ intro ( 'Adding components to your Xano instance: ' ) ;
3334
3435 if ( ! componentNames ?. length ) componentNames = ( await promptForComponents ( ) ) as string [ ] ;
3536
@@ -40,39 +41,58 @@ async function addToXano({
4041 const registryItem = await getRegistryItem ( componentName ) ;
4142 const sortedFiles = sortFilesByType ( registryItem . files ) ;
4243 for ( const file of sortedFiles ) {
43- const success = await installComponentToXano (
44+ const installResult = await installComponentToXano (
4445 file ,
45- {
46- instanceConfig,
47- workspaceConfig,
48- branchConfig,
49- } ,
46+ { instanceConfig, workspaceConfig, branchConfig } ,
5047 core
5148 ) ;
52- if ( success )
53- results . installed . push ( { component : componentName , file : file . target || file . path } ) ;
54- else
49+ if ( installResult . success ) {
50+ results . installed . push ( {
51+ component : componentName ,
52+ file : file . target || file . path ,
53+ response : installResult . body ,
54+ } ) ;
55+ } else {
5556 results . failed . push ( {
5657 component : componentName ,
5758 file : file . target || file . path ,
58- error : 'Installation failed' ,
59+ error : installResult . error || 'Installation failed' ,
60+ response : installResult . body ,
5961 } ) ;
62+ }
6063 }
61- log . step ( `Installed: ${ componentName } ` ) ;
6264 } catch ( error ) {
6365 results . failed . push ( { component : componentName , error : error . message } ) ;
6466 }
6567 }
68+
69+ // --- Output summary table ---
70+ if ( results . installed . length ) {
71+ log . success ( 'Installed components:' ) ;
72+ results . installed . forEach ( ( { component, file } ) => {
73+ log . info ( `${ component } \nFile: ${ file } \n---` ) ;
74+ } ) ;
75+ }
76+ if ( results . failed . length ) {
77+ log . error ( 'Failed components:' ) ;
78+ results . failed . forEach ( ( { component, file, error } ) => {
79+ log . warn ( `${ component } \nFile: ${ file } \nError: ${ error } \n---` ) ;
80+ } ) ;
81+ }
82+ if ( ! results . installed . length && ! results . failed . length ) {
83+ log . info ( '\nNo components were installed.' ) ;
84+ }
85+
6686 return results ;
6787}
6888
69- // [ ] CORE
7089/**
71- * Function that creates the required components in Xano.
90+ * Installs a component file to Xano.
7291 *
73- * @param {* } file
74- * @param {* } resolvedContext
75- * @returns {Boolean } - success: true, failure: false
92+ * @param {Object } file - The component file metadata.
93+ * @param {Object } resolvedContext - The resolved context configs.
94+ * @param {any } core - Core utilities.
95+ * @returns {Promise<{ success: boolean, error?: string, body?: any }> }
7696 */
7797async function installComponentToXano ( file , resolvedContext , core ) {
7898 const { instanceConfig, workspaceConfig, branchConfig } = resolvedContext ;
@@ -82,18 +102,12 @@ async function installComponentToXano(file, resolvedContext, core) {
82102 'registry:table' : `workspace/${ workspaceConfig . id } /table` ,
83103 } ;
84104
85- // If query, extend the default urlMapping with the populated query creation API group.
86105 if ( file . type === 'registry:query' ) {
87106 const targetApiGroup = await getApiGroupByName (
88107 file [ 'api-group-name' ] ,
89- {
90- instanceConfig,
91- workspaceConfig,
92- branchConfig,
93- } ,
108+ { instanceConfig, workspaceConfig, branchConfig } ,
94109 core
95110 ) ;
96-
97111 urlMapping [
98112 'registry:query'
99113 ] = `workspace/${ workspaceConfig . id } /apigroup/${ targetApiGroup . id } /api?branch=${ branchConfig . label } ` ;
@@ -103,11 +117,7 @@ async function installComponentToXano(file, resolvedContext, core) {
103117 const xanoApiUrl = `${ instanceConfig . url } /api:meta` ;
104118
105119 try {
106- // [ ] TODO: implement override checking. For now just try the POST and Xano will throw error anyways...
107-
108- // Fetch the text content of the registry file (xano-script)
109120 const content = await fetchRegistryFileContent ( file . path ) ;
110-
111121 const response = await fetch ( `${ xanoApiUrl } /${ urlMapping [ file . type ] } ` , {
112122 method : 'POST' ,
113123 headers : {
@@ -116,11 +126,51 @@ async function installComponentToXano(file, resolvedContext, core) {
116126 } ,
117127 body : content ,
118128 } ) ;
119- if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` ) ;
120- return true ;
129+
130+ let body ;
131+ try {
132+ body = await response . json ( ) ;
133+ } catch ( jsonErr ) {
134+ // If response is not JSON, treat as failure
135+ return {
136+ success : false ,
137+ error : `Invalid JSON response: ${ jsonErr . message } ` ,
138+ } ;
139+ }
140+
141+ // 1. If HTTP error, always fail
142+ if ( ! response . ok ) {
143+ return {
144+ success : false ,
145+ error : `HTTP ${ response . status } : ${ response . statusText } - ${ body ?. message || '' } ` ,
146+ body,
147+ } ;
148+ }
149+
150+ // 2. If "code" and "message" fields are present, treat as error (API-level error)
151+ if ( body && body . code && body . message ) {
152+ return {
153+ success : false ,
154+ error : `${ body . code } : ${ body . message } ` ,
155+ body,
156+ } ;
157+ }
158+
159+ // 3. If "xanoscript" is present and has a non-ok status, treat as error
160+ if ( body && body . xanoscript && body . xanoscript . status !== 'ok' ) {
161+ return {
162+ success : false ,
163+ error : `XanoScript error: ${ body . xanoscript . message || 'Unknown error' } ` ,
164+ body,
165+ } ;
166+ }
167+
168+ // If all checks pass, treat as success
169+ return { success : true , body } ;
121170 } catch ( error ) {
171+ // Only catch truly unexpected errors (network, programming, etc.)
122172 console . error ( `Failed to install ${ file . target || file . path } :` , error ) ;
123- return false ;
173+ return { success : false , error : error . message } ;
124174 }
125175}
126176
@@ -132,28 +182,30 @@ function registerRegistryAddCommand(program, core) {
132182 ) ;
133183
134184 addFullContextOptions ( cmd ) ;
135- cmd . option ( '--components' , 'Comma-separated list of components to add' )
136- . option (
137- '--registry <url>' ,
138- 'URL to the component registry. Default: http://localhost:5500/registry/definitions'
139- )
140- . action (
141- withErrorHandler ( async ( options ) => {
142- if ( options . registry ) {
143- process . env . Caly_REGISTRY_URL = options . registry ;
144- }
145-
146- await addToXano ( {
147- componentNames : options . components ,
148- context : {
149- instance : options . instance ,
150- workspace : options . workspace ,
151- branch : options . branch ,
152- } ,
153- core,
154- } ) ;
155- } )
156- ) ;
185+ cmd . argument (
186+ '<components...>' ,
187+ 'Space delimited list of components to add to your Xano instance.'
188+ ) ;
189+ cmd . option (
190+ '--registry <url>' ,
191+ 'URL to the component registry. Default: http://localhost:5500/registry/definitions'
192+ ) . action (
193+ withErrorHandler ( async ( components , options ) => {
194+ if ( options . registry ) {
195+ console . log ( 'command registry option: ' , options . registry ) ;
196+ process . env . CALY_REGISTRY_URL = options . registry ;
197+ }
198+ await addToXano ( {
199+ componentNames : components ,
200+ context : {
201+ instance : options . instance ,
202+ workspace : options . workspace ,
203+ branch : options . branch ,
204+ } ,
205+ core,
206+ } ) ;
207+ } )
208+ ) ;
157209}
158210
159211function registerRegistryScaffoldCommand ( program , core ) {
0 commit comments