@@ -40,8 +40,8 @@ function extractUrls(obj, path = "") {
4040 return urls ;
4141}
4242
43- // Check if URL is accessible
44- function checkUrl ( urlInfo ) {
43+ // Check if URL is accessible (single attempt)
44+ function checkUrlOnce ( urlInfo ) {
4545 return new Promise ( ( resolve ) => {
4646 const url = new URL ( urlInfo . url ) ;
4747 const protocol = url . protocol === "https:" ? https : http ;
@@ -76,6 +76,58 @@ function checkUrl(urlInfo) {
7676 } ) ;
7777}
7878
79+ // Retry timed out URLs with exponential backoff
80+ async function retryTimeouts ( timedOutResults ) {
81+ if ( timedOutResults . length === 0 ) return [ ] ;
82+
83+ const maxRetries = 10 ;
84+ const baseDelay = 1000 ; // Start with 1 second
85+ let remaining = [ ...timedOutResults ] ;
86+ const successful = [ ] ;
87+
88+ for (
89+ let attempt = 1 ;
90+ attempt <= maxRetries && remaining . length > 0 ;
91+ attempt ++
92+ ) {
93+ // Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s (capped), 60s, 60s, 60s
94+ const delay = Math . min ( baseDelay * Math . pow ( 2 , attempt - 1 ) , 60000 ) ;
95+
96+ console . log (
97+ `\n${ colors . yellow } Retry attempt ${ attempt } /${ maxRetries } for ${ remaining . length } timed out URL(s) (waiting ${ delay / 1000 } s)...${ colors . reset } `
98+ ) ;
99+
100+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
101+
102+ const retryResults = await Promise . all (
103+ remaining . map ( ( r ) => checkUrlOnce ( { url : r . url , path : r . path } ) )
104+ ) ;
105+
106+ const stillTimedOut = [ ] ;
107+ for ( const result of retryResults ) {
108+ if ( result . status === "TIMEOUT" ) {
109+ stillTimedOut . push ( result ) ;
110+ } else {
111+ successful . push ( result ) ;
112+ console . log (
113+ ` ${ colors . green } ✓${ colors . reset } ${ result . url } - ${ result . status === "OK" ? "OK" : result . status } `
114+ ) ;
115+ }
116+ }
117+
118+ remaining = stillTimedOut ;
119+
120+ if ( remaining . length === 0 ) {
121+ console . log (
122+ `${ colors . green } All timed out URLs recovered after ${ attempt } retry attempt(s)!${ colors . reset } `
123+ ) ;
124+ }
125+ }
126+
127+ // Return both successful retries and still-failed ones
128+ return [ ...successful , ...remaining ] ;
129+ }
130+
79131// Progress bar
80132function updateProgress ( current , total ) {
81133 const percentage = Math . round ( ( current / total ) * 100 ) ;
@@ -106,7 +158,7 @@ async function main() {
106158 // Process in batches to avoid overwhelming servers
107159 for ( let i = 0 ; i < urls . length ; i += batchSize ) {
108160 const batch = urls . slice ( i , i + batchSize ) ;
109- const batchResults = await Promise . all ( batch . map ( checkUrl ) ) ;
161+ const batchResults = await Promise . all ( batch . map ( checkUrlOnce ) ) ;
110162 results . push ( ...batchResults ) ;
111163
112164 processed += batch . length ;
@@ -121,16 +173,30 @@ async function main() {
121173 // Clear progress bar
122174 process . stdout . write ( "\r" + " " . repeat ( 80 ) + "\r" ) ;
123175
176+ // Separate timeouts from other failures for retry logic
177+ const timedOut = results . filter ( ( r ) => r . status === "TIMEOUT" ) ;
178+ const otherResults = results . filter ( ( r ) => r . status !== "TIMEOUT" ) ;
179+
180+ // Retry timed out URLs with exponential backoff
181+ let finalResults = [ ...otherResults ] ;
182+ if ( timedOut . length > 0 ) {
183+ console . log (
184+ `\n${ colors . yellow } ${ timedOut . length } URL(s) timed out. Retrying with exponential backoff...${ colors . reset } `
185+ ) ;
186+ const retryResults = await retryTimeouts ( timedOut ) ;
187+ finalResults = [ ...finalResults , ...retryResults ] ;
188+ }
189+
124190 // Report results
125- const failed = results . filter ( ( r ) => r . status !== "OK" ) ;
126- const successful = results . filter ( ( r ) => r . status === "OK" ) ;
191+ const failed = finalResults . filter ( ( r ) => r . status !== "OK" ) ;
192+ const successful = finalResults . filter ( ( r ) => r . status === "OK" ) ;
127193
128194 console . log ( "\n" + colors . blue + "=" + "=" . repeat ( 50 ) + colors . reset ) ;
129195 console . log ( `${ colors . blue } URL Check Results${ colors . reset } ` ) ;
130196 console . log ( colors . blue + "=" + "=" . repeat ( 50 ) + colors . reset + "\n" ) ;
131197
132198 console . log (
133- `Total URLs checked: ${ colors . yellow } ${ results . length } ${ colors . reset } `
199+ `Total URLs checked: ${ colors . yellow } ${ finalResults . length } ${ colors . reset } `
134200 ) ;
135201 console . log ( `Successful: ${ colors . green } ${ successful . length } ${ colors . reset } ` ) ;
136202 console . log ( `Failed: ${ colors . red } ${ failed . length } ${ colors . reset } \n` ) ;
0 commit comments