perf(procaptcha): start detector assignment at page load - #2971
Merged
Conversation
The detector now comes from the provider-served pool, so the frictionless flow cannot start until /detector/assign returns. That request was issued by customDetectBot, which only runs once React has mounted the widget, so it queued behind the bundle's dynamic-import chain. Measured on the staging demo page: 466 ms procaptcha.bundle.js 632 ms 7 sequential dynamic-import levels (~700 ms) 1166 ms ProviderApi chunk finally loaded 1513 ms detector/assign starts 2448 ms ...935 ms to download the bundle 3817 ms frictionless starts 4928 ms done (production, which has no assign step, is 1744 ms) Nothing in the assign request depends on React, i18n or the widget config — it needs the site key (DOM attribute), the environment (build-time constant) and the ip-mode flags (DOM attributes). The entry now starts it as soon as it has read those, and customDetectBot claims the in-flight promise rather than issuing its own. Loaded by dynamic import so the provider selector and API client stay out of the entry chunk; entry grows ~400 bytes. Fire-and-forget: a failed prefetch is indistinguishable from no prefetch and the existing path still resolves a provider itself, so this cannot turn a working session into a broken one. The cache is single-use and keyed on (environment, ipMode, siteKey). A retry is retrying because the pinned pronode failed, so it must not reuse that pin; and a second widget with different flags must not claim another widget's assignment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XJ7KiTDKiu3mxQ4iuLFK2y
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XJ7KiTDKiu3mxQ4iuLFK2y
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Measured with headless Chromium against the demo pages:
Staging waterfall:
Change
Nothing in the assign request depends on React, i18n or the widget config. It needs the site key (a DOM attribute), the environment (a build-time constant) and the IP-mode flags (DOM attributes) — all available at ~0 ms. The entry now starts it as soon as it has read them, and
customDetectBotclaims the in-flight promise instead of issuing its own.The
ProviderApi+ provider-selector chunks now download in parallel with React/i18n rather than after them, soassignshould start around 400–500 ms instead of 1513 ms.Safety
.catchis attached so it never surfaces as an unhandled rejection in the host page, while the real consumer still sees the rejection and falls back.(environment, ipMode, siteKey). A retry is retrying because the pinned pronode failed, so it must not reuse that pin; and a second widget with different IP flags must not claim another widget's assignment. Both are covered by tests.Verification
procaptcha-frictionless+procaptcha-bundletypecheck cleancustomDetectBotsuite (which this PR modifies)startDetectorPrefetchin the entry chunk — if it landed in a lazy chunk the change would silently do nothingIpModeis derived viaParameters<typeof getProcaptchaRandomActiveProvider>[1]rather than importing@prosopo/load-balancer, avoiding a new dependency plus tsconfig project reference for one type alias.🤖 Generated with Claude Code
https://claude.ai/code/session_01XJ7KiTDKiu3mxQ4iuLFK2y