@@ -76,9 +76,15 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
7676 const { siteKey, widgetType, invisibleSiteKey, nonce } = opts ;
7777 const { modalContainerQuerySelector, modalWrapperQuerySelector, closeModal, openModal } = opts ;
7878 const captcha : Turnstile . Turnstile = await loadCaptcha ( nonce ) ;
79- const errorCodes : ( string | number ) [ ] = [ ] ;
79+
80+ // Timing and error tracking for diagnostics
81+ const startTime = Date . now ( ) ;
82+ const errorTimeline : Array < { code : string | number ; t : number } > = [ ] ;
8083 // Unique ID to correlate log entries for this captcha attempt
81- const captchaAttemptId = Math . random ( ) . toString ( 36 ) . substring ( 2 , 9 ) ;
84+ const captchaAttemptId =
85+ typeof crypto !== 'undefined' && typeof crypto . randomUUID === 'function'
86+ ? crypto . randomUUID ( )
87+ : Math . random ( ) . toString ( 36 ) . substring ( 2 , 9 ) ;
8288
8389 let captchaToken = '' ;
8490 let id = '' ;
@@ -184,7 +190,7 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
184190 }
185191 } ,
186192 'error-callback' : function ( errorCode ) {
187- errorCodes . push ( errorCode ) ;
193+ errorTimeline . push ( { code : errorCode , t : Date . now ( ) - startTime } ) ;
188194 /**
189195 * By setting retry to 'never' the responsibility for implementing retrying is ours
190196 * https://developers.cloudflare.com/turnstile/reference/client-side-errors/#retrying
@@ -196,7 +202,7 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
196202 } , 250 ) ;
197203 return ;
198204 }
199- reject ( [ errorCodes . join ( ',' ) , id ] ) ;
205+ reject ( [ errorTimeline . map ( e => e . code ) . join ( ',' ) , id ] ) ;
200206 } ,
201207 'unsupported-callback' : function ( ) {
202208 reject ( [ 'This browser is not supported by the CAPTCHA.' , id ] ) ;
@@ -224,13 +230,21 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
224230 captcha . remove ( id ) ;
225231 }
226232
233+ // Check if widget container exists at failure time (helps diagnose 200100 race conditions)
234+ const containerExistsAtFailure = widgetContainerQuerySelector
235+ ? ! ! document . querySelector ( widgetContainerQuerySelector )
236+ : false ;
237+
227238 // Log failure with full error history for debugging
228239 debugLogger . error ( 'Turnstile captcha challenge failed' , {
229240 captchaAttemptId,
230- errorCodesHistory : [ ...errorCodes ] ,
241+ errorTimeline,
242+ lastErrorCode : errorTimeline . length > 0 ? errorTimeline [ errorTimeline . length - 1 ] . code : null ,
231243 finalError : String ( e ) ,
232244 retriesAttempted : retries ,
233245 widgetType : captchaTypeUsed ,
246+ containerExistsAtFailure,
247+ totalDurationMs : Date . now ( ) - startTime ,
234248 } , 'captcha' ) ;
235249
236250 // eslint-disable-next-line @typescript-eslint/only-throw-error
0 commit comments