@@ -21,7 +21,10 @@ import {
2121import  *  as  commandsGrpcPb  from  './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb' ; 
2222import  {  NotificationServiceServer  }  from  '../common/protocol' ; 
2323import  {  Deferred ,  retry  }  from  '@theia/core/lib/common/promise-util' ; 
24- import  {  Status  as  RpcStatus  }  from  './cli-protocol/google/rpc/status_pb' ; 
24+ import  { 
25+   Status  as  RpcStatus , 
26+   Status , 
27+ }  from  './cli-protocol/google/rpc/status_pb' ; 
2528
2629@injectable ( ) 
2730export  class  CoreClientProvider  extends  GrpcClientProvider < CoreClientProvider . Client >  { 
@@ -90,17 +93,17 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
9093          this . _initialized . resolve ( ) ; 
9194          this . updateIndex ( this . _client ) ;  // Update the indexes asynchronously 
9295        }  catch  ( error : unknown )  { 
93-           if  ( 
94-             this . isPackageIndexMissingError ( error )  || 
95-             this . isDiscoveryNotFoundError ( error ) 
96-           )  { 
96+           console . error ( 
97+             'Error occurred while initializing the core gRPC client provider' , 
98+             error 
99+           ) ; 
100+           if  ( error  instanceof  IndexUpdateRequiredBeforeInitError )  { 
97101            // If it's a first start, IDE2 must run index update before the init request. 
98102            await  this . updateIndexes ( this . _client ) ; 
99103            await  this . initInstance ( this . _client ) ; 
100104            this . _initialized . resolve ( ) ; 
101-           }  else  { 
102-             throw  error ; 
103105          } 
106+           throw  error ; 
104107        } 
105108      } 
106109    } ) ; 
@@ -114,41 +117,6 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
114117    } ) ; 
115118  } 
116119
117-   private  isPackageIndexMissingError ( error : unknown ) : boolean  { 
118-     const  assert  =  ( message : string )  => 
119-       message . includes ( 'loading json index file' ) ; 
120-     // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247 
121-     return  this . isRpcStatusError ( error ,  assert ) ; 
122-   } 
123- 
124-   private  isDiscoveryNotFoundError ( error : unknown ) : boolean  { 
125-     const  assert  =  ( message : string )  => 
126-       message . includes ( 'discovery' )  && 
127-       ( message . includes ( 'not found' )  ||  message . includes ( 'not installed' ) ) ; 
128-     // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L740 
129-     // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L744 
130-     return  this . isRpcStatusError ( error ,  assert ) ; 
131-   } 
132- 
133-   private  isCancelError ( error : unknown ) : boolean  { 
134-     return  ( 
135-       error  instanceof  Error  && 
136-       error . message . toLocaleLowerCase ( ) . includes ( 'cancelled on client' ) 
137-     ) ; 
138-   } 
139- 
140-   // Final error codes are not yet defined by the CLI. Hence, we do string matching in the message RPC status. 
141-   private  isRpcStatusError ( 
142-     error : unknown , 
143-     assert : ( message : string )  =>  boolean 
144-   )  { 
145-     if  ( error  instanceof  RpcStatus )  { 
146-       const  {  message }  =  RpcStatus . toObject ( false ,  error ) ; 
147-       return  assert ( message . toLocaleLowerCase ( ) ) ; 
148-     } 
149-     return  false ; 
150-   } 
151- 
152120  protected  async  createClient ( 
153121    port : string  |  number 
154122  ) : Promise < CoreClientProvider . Client >  { 
@@ -192,7 +160,7 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
192160    initReq . setInstance ( instance ) ; 
193161    return  new  Promise < void > ( ( resolve ,  reject )  =>  { 
194162      const  stream  =  client . init ( initReq ) ; 
195-       const  errorStatus : RpcStatus [ ]  =  [ ] ; 
163+       const  errors : RpcStatus [ ]  =  [ ] ; 
196164      stream . on ( 'data' ,  ( res : InitResponse )  =>  { 
197165        const  progress  =  res . getInitProgress ( ) ; 
198166        if  ( progress )  { 
@@ -210,28 +178,30 @@ export class CoreClientProvider extends GrpcClientProvider<CoreClientProvider.Cl
210178
211179        const  error  =  res . getError ( ) ; 
212180        if  ( error )  { 
213-           console . error ( error . getMessage ( ) ) ; 
214-           errorStatus . push ( error ) ; 
215-           // Cancel the init request. No need to wait until  the end of the event. The init has already failed. 
216-           // Canceling the request will result in a cancel error, but we need to reject with the original error later. 
217-           stream . cancel ( ) ; 
181+           const   {  code ,  message  }   =   Status . toObject ( false ,   error ) ; 
182+           console . error ( 
183+              `Detected an error response during  the gRPC core client initialization: code:  ${ code } , message:  ${ message } ` 
184+           ) ; 
185+           errors . push ( error ) ; 
218186        } 
219187      } ) ; 
220-       stream . on ( 'error' ,  ( error )   =>   { 
221-          // On any error during the init request, the request is canceled. 
222-         // On cancel, the IDE2 ignores the cancel  error and rejects with the original one. 
223-         reject ( 
224-           this . isCancelError ( error )   &&   errorStatus . length 
225-             ?  errorStatus [ 0 ] 
226-             :  error 
227-         ) ; 
188+       stream . on ( 'error' ,  reject ) ; 
189+       stream . on ( 'end' ,   ( )   =>   { 
190+         const   error  =   this . evaluateErrorResponses ( errors ) ; 
191+         if   ( error )   { 
192+           reject ( error ) ; 
193+           return ; 
194+         } 
195+         resolve ( ) ; 
228196      } ) ; 
229-       stream . on ( 'end' ,  ( )  => 
230-         errorStatus . length  ? reject ( errorStatus )  : resolve ( ) 
231-       ) ; 
232197    } ) ; 
233198  } 
234199
200+   private  evaluateErrorResponses ( status : RpcStatus [ ] ) : Error  |  undefined  { 
201+     const  error  =  isIndexUpdateRequiredBeforeInit ( status ) ;  // put future error matching here 
202+     return  error ; 
203+   } 
204+ 
235205  protected  async  updateIndexes ( 
236206    client : CoreClientProvider . Client 
237207  ) : Promise < CoreClientProvider . Client >  { 
@@ -338,3 +308,58 @@ export abstract class CoreClientAware {
338308    ) ; 
339309  } 
340310} 
311+ 
312+ class  IndexUpdateRequiredBeforeInitError  extends  Error  { 
313+   constructor ( causes : RpcStatus . AsObject [ ] )  { 
314+     super ( `The index of the cores and libraries must be updated before initializing the core gRPC client. 
315+ The following were detected during the gRPC client initialization: 
316+ ${ causes 
317+   . map ( ( {  code,  message } )  =>  ` - code: ${ code }  , message: ${ message }  ` )  
318+   . join ( '\n' ) }  
319+ ` ) ; 
320+     Object . setPrototypeOf ( this ,  IndexUpdateRequiredBeforeInitError . prototype ) ; 
321+     if  ( ! causes . length )  { 
322+       throw  new  Error ( `expected non-empty 'causes'` ) ; 
323+     } 
324+   } 
325+ } 
326+ 
327+ function  isIndexUpdateRequiredBeforeInit ( 
328+   status : RpcStatus [ ] 
329+ ) : IndexUpdateRequiredBeforeInitError  |  undefined  { 
330+   const  causes  =  status 
331+     . filter ( ( s )  => 
332+       IndexUpdateRequiredBeforeInit . map ( ( predicate )  =>  predicate ( s ) ) . some ( 
333+         Boolean 
334+       ) 
335+     ) 
336+     . map ( ( s )  =>  RpcStatus . toObject ( false ,  s ) ) ; 
337+   return  causes . length 
338+     ? new  IndexUpdateRequiredBeforeInitError ( causes ) 
339+     : undefined ; 
340+ } 
341+ const  IndexUpdateRequiredBeforeInit  =  [ 
342+   isPackageIndexMissingStatus , 
343+   isDiscoveryNotFoundStatus , 
344+ ] ; 
345+ function  isPackageIndexMissingStatus ( status : RpcStatus ) : boolean  { 
346+   const  predicate  =  ( {  message } : RpcStatus . AsObject )  => 
347+     message . includes ( 'loading json index file' ) ; 
348+   // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/package_manager.go#L247 
349+   return  evaluate ( status ,  predicate ) ; 
350+ } 
351+ function  isDiscoveryNotFoundStatus ( status : RpcStatus ) : boolean  { 
352+   const  predicate  =  ( {  message } : RpcStatus . AsObject )  => 
353+     message . includes ( 'discovery' )  && 
354+     ( message . includes ( 'not found' )  ||  message . includes ( 'not installed' ) ) ; 
355+   // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L740 
356+   // https://github.com/arduino/arduino-cli/blob/f0245bc2da6a56fccea7b2c9ea09e85fdcc52cb8/arduino/cores/packagemanager/loader.go#L744 
357+   return  evaluate ( status ,  predicate ) ; 
358+ } 
359+ function  evaluate ( 
360+   subject : RpcStatus , 
361+   predicate : ( error : RpcStatus . AsObject )  =>  boolean 
362+ ) : boolean  { 
363+   const  status  =  RpcStatus . toObject ( false ,  subject ) ; 
364+   return  predicate ( status ) ; 
365+ } 
0 commit comments