@@ -52,18 +52,58 @@ export class WorkloadHttpClient {
5252 } ) ;
5353 }
5454
55+ private isConnectionError ( error : string ) : boolean {
56+ const connectionErrors = [
57+ "Connection error" ,
58+ "ECONNREFUSED" ,
59+ "ETIMEDOUT" ,
60+ "ENOTFOUND" ,
61+ "ECONNRESET" ,
62+ "EHOSTUNREACH" ,
63+ "ENETUNREACH" ,
64+ "EPIPE" ,
65+ "ECONNABORTED" ,
66+ ] ;
67+ return connectionErrors . some ( ( errType ) => error . includes ( errType ) ) ;
68+ }
69+
70+ private async withConnectionErrorDetection < T > (
71+ operation : ( ) => Promise < { success : true ; data : T } | { success : false ; error : string } >
72+ ) : Promise <
73+ { success : true ; data : T } | { success : false ; error : string ; isConnectionError ?: boolean }
74+ > {
75+ const result = await operation ( ) ;
76+
77+ if ( result . success ) {
78+ return result ;
79+ }
80+
81+ // Check if this is a connection error
82+ if ( this . isConnectionError ( result . error ) ) {
83+ return {
84+ ...result ,
85+ isConnectionError : true ,
86+ } ;
87+ }
88+
89+ return result ;
90+ }
91+
5592 async heartbeatRun ( runId : string , snapshotId : string , body ?: WorkloadHeartbeatRequestBody ) {
56- return wrapZodFetch (
57- WorkloadHeartbeatResponseBody ,
58- `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/${ snapshotId } /heartbeat` ,
59- {
60- method : "POST" ,
61- headers : {
62- ...this . defaultHeaders ( ) ,
63- "Content-Type" : "application/json" ,
64- } ,
65- body : JSON . stringify ( body ?? { } ) ,
66- }
93+ return this . withConnectionErrorDetection ( ( ) =>
94+ wrapZodFetch (
95+ WorkloadHeartbeatResponseBody ,
96+ `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/${ snapshotId } /heartbeat` ,
97+ {
98+ method : "POST" ,
99+ headers : {
100+ ...this . defaultHeaders ( ) ,
101+ "Content-Type" : "application/json" ,
102+ } ,
103+ body : JSON . stringify ( body ?? { } ) ,
104+ signal : AbortSignal . timeout ( 10_000 ) , // 10 second timeout
105+ }
106+ )
67107 ) ;
68108 }
69109
@@ -81,15 +121,17 @@ export class WorkloadHttpClient {
81121 }
82122
83123 async continueRunExecution ( runId : string , snapshotId : string ) {
84- return wrapZodFetch (
85- WorkloadContinueRunExecutionResponseBody ,
86- `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/${ snapshotId } /continue` ,
87- {
88- method : "GET" ,
89- headers : {
90- ...this . defaultHeaders ( ) ,
91- } ,
92- }
124+ return this . withConnectionErrorDetection ( ( ) =>
125+ wrapZodFetch (
126+ WorkloadContinueRunExecutionResponseBody ,
127+ `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/${ snapshotId } /continue` ,
128+ {
129+ method : "GET" ,
130+ headers : {
131+ ...this . defaultHeaders ( ) ,
132+ } ,
133+ }
134+ )
93135 ) ;
94136 }
95137
@@ -130,15 +172,18 @@ export class WorkloadHttpClient {
130172 }
131173
132174 async getSnapshotsSince ( runId : string , snapshotId : string ) {
133- return wrapZodFetch (
134- WorkloadRunSnapshotsSinceResponseBody ,
135- `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/since/${ snapshotId } ` ,
136- {
137- method : "GET" ,
138- headers : {
139- ...this . defaultHeaders ( ) ,
140- } ,
141- }
175+ return this . withConnectionErrorDetection ( ( ) =>
176+ wrapZodFetch (
177+ WorkloadRunSnapshotsSinceResponseBody ,
178+ `${ this . apiUrl } /api/v1/workload-actions/runs/${ runId } /snapshots/since/${ snapshotId } ` ,
179+ {
180+ method : "GET" ,
181+ headers : {
182+ ...this . defaultHeaders ( ) ,
183+ } ,
184+ signal : AbortSignal . timeout ( 10_000 ) , // 10 second timeout
185+ }
186+ )
142187 ) ;
143188 }
144189
0 commit comments