Skip to content

Fix Turnstile execute-on-Run and Firefox /run stalls - #311

Closed
mxschmitt wants to merge 2 commits into
mainfrom
cursor/fix-e2e-turnstile-flake-7b47
Closed

mxschmitt wants to merge 2 commits into
mainfrom
cursor/fix-e2e-turnstile-flake-7b47

Conversation

@mxschmitt

@mxschmitt mxschmitt commented Sep 2, 2026

Copy link
Copy Markdown
Owner

What this PR does

Rebased onto main after #313 (stale example getCode()). This PR no longer includes that fix.

Turnstile (execute-on-Run)

The playground should verify when the user clicks Run, not on page load. Tokens last 5 minutes; people linger.

Client now matches explicit rendering:

  1. turnstile.ready() then render(container, { sitekey, execution: "execute", appearance: "interaction-only", callbacks })
  2. On Run: reset(widgetId) then execute(widgetId)
  3. Widget is laid out next to Run (not display: none) so an interactive challenge can appear
  4. Skip the widget on localhost / 127.0.0.1 (CI uses the production sitekey and an empty secret)
  5. 8s client timeout if callbacks never fire

Codex follow-up (e63d943):

  • Editor loading backdrop starts after the token, so an interactive challenge is not covered
  • Overlapping Run / Ctrl+Enter is ignored while one execution is in flight
  • Concurrent waitForTurnstileToken calls reuse the in-flight promise instead of replacing the callback

Firefox /run

Firefox aborts fetch if the response has no bytes until the worker finishes. Control now flushes JSON headers immediately and heartbeats \n every second (valid JSON leading whitespace). Caddy uses 2m timeouts and flush_interval -1.

Worker failures after the preamble stay HTTP 200 with JSON success: false / error because the status line is already sent.

Test plan

  • npx playwright test src/turnstile.spec.ts
  • CI e2e against k3s
Open in Web Open in Cursor 

@cursor cursor Bot changed the title Fix flaky e2e tests hanging on Turnstile and running the wrong example Fix flaky e2e: skip Turnstile in CI and stop running stale example code Sep 2, 2026
@cursor cursor Bot changed the title Fix flaky e2e: skip Turnstile in CI and stop running stale example code Fix proven example-selection race; log Turnstile to prove the hang Sep 2, 2026
@cursor cursor Bot changed the title Fix proven example-selection race; log Turnstile to prove the hang Fix e2e flakes: stale example code and Turnstile hangs on localhost Sep 2, 2026
Use the documented explicit widget lifecycle: render once with
execution=execute and appearance=interaction-only, then reset and
execute the widget id on Run. Skip the widget on loopback so CI is
not blocked by a production sitekey. Keep the challenge off page
load so tokens are not minted until Run.

Flush /run JSON headers and heartbeat newlines so Firefox does not
abort while the worker is silent. Disable Caddy buffering on the
control proxy.

Co-authored-by: Max Schmitt <max@schmitt.mx>
@cursor
cursor Bot force-pushed the cursor/fix-e2e-turnstile-flake-7b47 branch from 8bec22c to eeb7b25 Compare September 3, 2026 02:22
@cursor cursor Bot changed the title Fix e2e flakes: stale example code and Turnstile hangs on localhost Fix Turnstile execute-on-Run and Firefox /run stalls Sep 3, 2026
@mxschmitt
mxschmitt marked this pull request as ready for review September 3, 2026 02:22
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T02:26:23.870088Z eeb7b25 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eeb7b255ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

<>
Editor
<div className={styles.codeHeaderButtons}>
<div ref={turnstileRef} className={styles.turnstile} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the interactive challenge above the loading backdrop

When Turnstile requires user interaction, this container is rendered inside the same Col where loading mounts a backdrop loader; that loader has z-index 10, while the new widget has no stacking override. Because loading is set before waitForTurnstileToken, the backdrop covers the challenge and prevents the user from completing it, so challenged users time out after eight seconds and submit an empty token. Render the challenge outside the covered region, raise it above the backdrop, or defer the backdrop until verification finishes.

Useful? React with 👍 / 👎.

Comment thread frontend/src/turnstile.ts
widgetIdRef: WidgetIdRef
onToken: (token: string, reason: string, extra?: Record<string, unknown>) => void
}): string {
pendingByWidget.set(options.widgetIdRef, options.onToken)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize executions before replacing the widget callback

If Run is invoked twice before the first challenge completes—such as by a double-click or repeated Ctrl/Cmd+Enter—this assignment replaces the first invocation's resolver, and the widget's deliver callback dynamically reads only the replacement. The second invocation also resets the shared widget, so the first promise can only reach its timeout and then sends an empty token; its unauthorized result may race with and overwrite the successful run. Ignore or queue subsequent executions while one token request is pending, rather than replacing the callback.

Useful? React with 👍 / 👎.

Defer the editor loading backdrop until after a token so an interactive
widget is not covered by the loader. Ignore overlapping Run clicks and
reuse the in-flight Turnstile promise instead of replacing the callback.

Co-authored-by: Max Schmitt <max@schmitt.mx>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants