@@ -8,6 +8,19 @@ interface UseCodeRunnerOptions {
88 onRunStart ?: ( ) => void ;
99 onRunComplete ?: ( ) => void ;
1010 onRunError ?: ( error : Error ) => void ;
11+ timeoutMs ?: number ;
12+ maxRetries ?: number ;
13+ }
14+
15+ class CodeExecutionError extends Error {
16+ constructor (
17+ message : string ,
18+ public readonly type : 'network' | 'timeout' | 'simulation' | 'unknown' ,
19+ public readonly originalError ?: Error
20+ ) {
21+ super ( message ) ;
22+ this . name = 'CodeExecutionError' ;
23+ }
1124}
1225
1326export function useCodeRunner ( options : UseCodeRunnerOptions = { } ) {
@@ -21,6 +34,11 @@ export function useCodeRunner(options: UseCodeRunnerOptions = {}) {
2134 timestamp : number ;
2235 } | null > ( null ) ;
2336
37+ const {
38+ timeoutMs = 30000 , // 30 seconds default
39+ maxRetries = 2 ,
40+ } = options ;
41+
2442 const updateCode = useCallback ( ( newCode : string ) => {
2543 setCode ( newCode ) ;
2644 } , [ ] ) ;
@@ -40,52 +58,116 @@ export function useCodeRunner(options: UseCodeRunnerOptions = {}) {
4058 setOutputs ( [
4159 {
4260 type : "log" ,
43- content : "Running code..." ,
61+ content : `🚀 Starting execution on ${ network . name } ...` ,
4462 timestamp : Date . now ( ) ,
4563 } ,
4664 ] ) ;
4765
48- try {
49- setProgress ( 30 ) ;
50- await new Promise ( ( resolve ) => setTimeout ( resolve , 300 ) ) ;
51- setProgress ( 50 ) ;
52-
53- const simulatedOutputs = await simulateCodeExecution ( example , network ) ;
54- setProgress ( 90 ) ;
55-
56- await new Promise ( ( resolve ) => setTimeout ( resolve , 200 ) ) ;
57-
58- setOutputs ( simulatedOutputs ) ;
59- setLastRun ( {
60- example,
61- network,
62- timestamp : Date . now ( ) ,
63- } ) ;
64-
65- options . onRunComplete ?.( ) ;
66- } catch ( error ) {
67- setOutputs ( [
68- {
69- type : "error" ,
70- content :
71- error instanceof Error
72- ? `Error: ${ error . message } `
73- : "An unknown error occurred" ,
66+ let attempt = 0 ;
67+ let lastError : Error | null = null ;
68+
69+ while ( attempt <= maxRetries ) {
70+ try {
71+ setProgress ( 20 + ( attempt * 10 ) ) ;
72+
73+ if ( attempt > 0 ) {
74+ setOutputs ( prev => [ ...prev , {
75+ type : "warning" ,
76+ content : `🔄 Retry attempt ${ attempt } /${ maxRetries } ...` ,
77+ timestamp : Date . now ( ) ,
78+ } ] ) ;
79+ }
80+
81+ // Create a timeout promise
82+ const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
83+ setTimeout ( ( ) => {
84+ reject ( new CodeExecutionError (
85+ `Execution timed out after ${ timeoutMs } ms` ,
86+ 'timeout'
87+ ) ) ;
88+ } , timeoutMs ) ;
89+ } ) ;
90+
91+ // Race between simulation and timeout
92+ const simulatedOutputs = await Promise . race ( [
93+ simulateCodeExecution ( example , network ) ,
94+ timeoutPromise
95+ ] ) ;
96+
97+ setProgress ( 90 ) ;
98+
99+ // Add success message
100+ const successOutputs = [
101+ ...simulatedOutputs ,
102+ {
103+ type : "log" as const ,
104+ content : `✅ Execution completed successfully on ${ network . name } ` ,
105+ timestamp : Date . now ( ) ,
106+ }
107+ ] ;
108+
109+ setOutputs ( successOutputs ) ;
110+ setLastRun ( {
111+ example,
112+ network,
74113 timestamp : Date . now ( ) ,
75- } ,
76- ] ) ;
114+ } ) ;
115+
116+ options . onRunComplete ?.( ) ;
117+ return ; // Success, exit retry loop
118+
119+ } catch ( error ) {
120+ lastError = error instanceof Error ? error : new Error ( String ( error ) ) ;
121+ attempt ++ ;
122+
123+ let errorType : CodeExecutionError [ 'type' ] = 'unknown' ;
124+ let errorMessage = lastError . message ;
125+
126+ // Categorize errors
127+ if ( lastError instanceof CodeExecutionError ) {
128+ errorType = lastError . type ;
129+ } else if ( lastError . message . includes ( 'network' ) || lastError . message . includes ( 'connection' ) ) {
130+ errorType = 'network' ;
131+ errorMessage = `Network error: ${ lastError . message } . Please check your internet connection and try again.` ;
132+ } else if ( lastError . message . includes ( 'timeout' ) ) {
133+ errorType = 'timeout' ;
134+ errorMessage = `Execution timed out: ${ lastError . message } . The operation took too long to complete.` ;
135+ } else {
136+ errorType = 'simulation' ;
137+ errorMessage = `Simulation error: ${ lastError . message } ` ;
138+ }
139+
140+ const categorizedError = new CodeExecutionError ( errorMessage , errorType , lastError ) ;
141+
142+ // If this was the last attempt, report the error
143+ if ( attempt > maxRetries ) {
144+ setOutputs ( prev => [ ...prev , {
145+ type : "error" ,
146+ content : `❌ ${ categorizedError . message } ${ errorType === 'network' ? ' (Check network connection)' : '' } ${ errorType === 'timeout' ? ' (Try simplifying the code)' : '' } ` ,
147+ timestamp : Date . now ( ) ,
148+ } ] ) ;
149+
150+ options . onRunError ?.( categorizedError ) ;
151+ }
152+
153+ // Wait before retry (exponential backoff)
154+ if ( attempt <= maxRetries ) {
155+ const delay = Math . min ( 1000 * Math . pow ( 2 , attempt - 1 ) , 5000 ) ;
156+ await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
157+ }
158+ }
159+ }
77160
78- options . onRunError ?. (
79- error instanceof Error ? error : new Error ( "Unknown error" ) ,
80- ) ;
81- } finally {
82- setIsRunning ( false ) ;
83- setProgress ( 100 ) ;
161+ // If we get here, all retries failed
162+ setOutputs ( prev => [ ... prev , {
163+ type : "error" ,
164+ content : `❌ All ${ maxRetries + 1 } attempts failed. Last error: ${ lastError ?. message || 'Unknown error' } ` ,
165+ timestamp : Date . now ( ) ,
166+ } ] ) ;
84167
85- setTimeout ( ( ) => setProgress ( 0 ) , 500 ) ;
86- }
168+ options . onRunError ?.( lastError || new Error ( 'All retry attempts failed' ) ) ;
87169 } ,
88- [ options ] ,
170+ [ options , timeoutMs , maxRetries ] ,
89171 ) ;
90172
91173 const clearOutput = useCallback ( ( ) => {
0 commit comments