Conversation
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>
8bec22c to
eeb7b25
Compare
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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} /> |
There was a problem hiding this comment.
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 👍 / 👎.
| widgetIdRef: WidgetIdRef | ||
| onToken: (token: string, reason: string, extra?: Record<string, unknown>) => void | ||
| }): string { | ||
| pendingByWidget.set(options.widgetIdRef, options.onToken) |
There was a problem hiding this comment.
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>
What this PR does
Rebased onto
mainafter #313 (stale examplegetCode()). 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:
turnstile.ready()thenrender(container, { sitekey, execution: "execute", appearance: "interaction-only", callbacks })reset(widgetId)thenexecute(widgetId)display: none) so an interactive challenge can appearlocalhost/127.0.0.1(CI uses the production sitekey and an empty secret)Codex follow-up (
e63d943):waitForTurnstileTokencalls reuse the in-flight promise instead of replacing the callbackFirefox
/runFirefox aborts
fetchif the response has no bytes until the worker finishes. Control now flushes JSON headers immediately and heartbeats\nevery second (valid JSON leading whitespace). Caddy uses 2m timeouts andflush_interval -1.Worker failures after the preamble stay HTTP 200 with JSON
success: false/errorbecause the status line is already sent.Test plan
npx playwright test src/turnstile.spec.ts