diff --git a/frontend/src/turnstile.spec.ts b/frontend/src/turnstile.spec.ts index 259e4dc..9a5c8b5 100644 --- a/frontend/src/turnstile.spec.ts +++ b/frontend/src/turnstile.spec.ts @@ -62,6 +62,19 @@ test('cloudflare gate returns empty when the API is missing', async () => { expect(await gate.getToken(container)).toBe('') }) +test('cloudflare gate ignores ready() throwing from async/defer api.js', async () => { + const { api, options } = mockApi({ + ready: () => { + throw new Error('[Cloudflare Turnstile] Remove async/defer from the Turnstile api.js script tag before using turnstile.ready().') + }, + }) + api.execute = () => { + options.at(-1)?.callback?.('tok') + } + const gate = new CloudflareTurnstileGate('sitekey', { getApi: () => api }) + expect(await gate.getToken(container)).toBe('tok') +}) + test('cloudflare gate does not hang when render throws', async () => { const gate = new CloudflareTurnstileGate('sitekey', { getApi: () => ({ diff --git a/frontend/src/turnstile.ts b/frontend/src/turnstile.ts index 8c968d3..683c675 100644 --- a/frontend/src/turnstile.ts +++ b/frontend/src/turnstile.ts @@ -48,16 +48,6 @@ function isUsableApi(api: TurnstileApi | null | undefined): api is TurnstileApi return typeof api?.render === 'function' && typeof api?.execute === 'function' } -function whenReady(turnstile: TurnstileApi): Promise { - return new Promise((resolve) => { - if (typeof turnstile.ready === 'function') { - turnstile.ready(() => resolve()) - return - } - resolve() - }) -} - export class CloudflareTurnstileGate implements TurnstileGate { readonly mode = 'cloudflare' as const private widgetId: string | null = null @@ -166,10 +156,9 @@ export class CloudflareTurnstileGate implements TurnstileGate { done('') return } - await whenReady(api) - if (settled) { - return - } + // Do not call turnstile.ready(): with async/defer api.js it throws + // "Remove async/defer ... before using turnstile.ready()", which + // used to settle this promise with an empty token on production. const alreadyRendered = Boolean(this.widgetId) if (!this.widgetId) { this.widgetId = api.render(container, {