feat(statsig): enhance Statsig client initialization and configuration#2933
feat(statsig): enhance Statsig client initialization and configuration#2933
Conversation
- Added STATSIG_ENVIRONMENT variable to .env.example for environment configuration. - Introduced shutdown method in StatsigBrowserClient for graceful client termination. - Improved Statsig client initialization to include environment tier and stable user ID handling. - Updated server-side Statsig client to utilize environment options for better configuration. - Modified layout and marketing page to pass stable user ID to client initialization, ensuring consistency. Made-with: Cursor
Greptile SummaryThis PR enhances the Statsig integration by adding environment-tier configuration ( The core logic in Confidence Score: 4/5Safe to merge — both findings are edge-case race conditions in P2 territory that self-correct on the next navigation, with no data loss or security impact. All findings are P2. The bootstrap-overwrite race requires very rapid navigation and self-heals on the next src/lib/statsig/client.ts — the Important Files Changed
Reviews (1): Last reviewed commit: "feat(statsig): enhance Statsig client in..." | Re-trigger Greptile |
| pendingBootstrapJson = | ||
| typeof clientBootstrapJson === 'string' && clientBootstrapJson.length > 0 | ||
| ? clientBootstrapJson | ||
| : undefined; |
There was a problem hiding this comment.
Bootstrap payload silently cleared on rapid navigation
initStatsig unconditionally overwrites pendingBootstrapJson with undefined when called with a null/empty bootstrap (e.g., navigating away from / before the in-flight runStatsigInit has consumed the value). If the user navigates / → /docs quickly, the second afterNavigate call clears the bootstrap before runStatsigInit reaches line 123. The in-flight init then sees bootstrap = undefined and falls through to initializeAsync without the SSR data. The reinit-on-next-navigate path self-corrects, but the initial homepage render loses the bootstrap alignment.
A safer pattern would be to only update the pending values when the init hasn't yet consumed them (e.g., guard with if (!initPromise || client) before overwriting).
| if (client && hasBootstrap && appliedBootstrapPayload == null) { | ||
| const previous = client as unknown as StatsigBrowserClient; | ||
| client = null; | ||
| initPromise = wrapInitFailure( | ||
| (async (): Promise<StatsigBrowserClient | null> => { | ||
| try { | ||
| await previous.shutdown(); | ||
| } catch { | ||
| /* ignore */ | ||
| } | ||
| return runStatsigInit(); | ||
| })() | ||
| ); | ||
| return whenStatsigNetworkReady(); | ||
| } |
There was a problem hiding this comment.
Reinit triggered on every navigation to
/ after non-bootstrap start
The condition client && hasBootstrap && appliedBootstrapPayload == null triggers a full shutdown() + reinit every time a navigation brings bootstrap data if appliedBootstrapPayload is still null. This is intentional for the /docs → / case, but if afterNavigate fires twice in quick succession (e.g., hash change then real navigation) for the homepage, two concurrent reinit chains could run — the second one shutting down the client the first one is still initializing. appliedBootstrapPayload is only set after runStatsigInit returns successfully, so a slow init leaves the window open.
Consider setting appliedBootstrapPayload to a sentinel value (e.g., 'pending') at the top of the reinit path to prevent double-triggering while the shutdown/reinit is in-flight.
Made-with: Cursor
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)