feat: move bootstrap capability to js-client-common (SDK-1874)#1113
feat: move bootstrap capability to js-client-common (SDK-1874)#1113
Conversation
Move bootstrap handling from platform-specific start() methods to the shared LDClientImpl.identifyResult() method so all client-side SDKs can support flag bootstrapping. - Add bootstrap and bootstrapParsed fields to base LDIdentifyOptions - Add bootstrap parsing/presetFlags logic to identifyResult() execute phase - Remove duplicate bootstrap fields from BrowserIdentifyOptions - Remove duplicate bootstrap fields from ElectronIdentifyOptions - Remove bootstrap logic from BrowserClient.start() - Remove bootstrap logic from ElectronClient.start() - Clean up unused readFlagsFromBootstrap imports from platform SDKs Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/browser size report |
|
@launchdarkly/js-client-sdk-common size report |
…ap availability The start() methods need to call presetFlags() synchronously so that bootstrap flags are available immediately after start() returns, before the async identify queue executes. The common identifyResult() code also calls presetFlags() for SDKs that use identify() directly without start(). presetFlags() is idempotent so both paths running is safe. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
|
@launchdarkly/js-client-sdk size report |
…directly ElectronIdentifyOptions was just a type alias for LDIdentifyOptions after bootstrap fields moved to the base interface. Replace all usages with LDIdentifyOptions from js-client-sdk-common. Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
|
does this mean React Native also gets bootstrap capabilities? If so then I think we will need to add some unit tests... |
|
Yes — React Native now gets bootstrap capabilities through the common |
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
…ove presetFlags from identifyResult - Remove presetFlags() from LDClientImpl.identifyResult() to fix change event suppression on re-identify with bootstrap (Browser/Electron regression) - Add _finishIdentifyFromBootstrap() to MobileDataManager so React Native resolves identify immediately when bootstrap is provided, matching the pattern used by Browser/Electron data managers Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
| this._debugLog('Identify - Initialization completed from bootstrap'); | ||
|
|
||
| identifyResolve(); | ||
| } |
There was a problem hiding this comment.
Identical bootstrap method duplicated across three data managers
Low Severity
The new _finishIdentifyFromBootstrap in MobileDataManager is functionally identical to the implementations in ElectronDataManager and BrowserDataManager. All three parse bootstrap data via readFlagsFromBootstrap, call flagManager.setBootstrap, log, and resolve. Since bootstrap and bootstrapParsed now live on the shared LDIdentifyOptions interface, this method is a natural fit as a protected method on BaseDataManager, which already has access to this.logger and this.flagManager.
Additional Locations (1)
There was a problem hiding this comment.
Agreed this is a good candidate for deduplication. However, moving it to BaseDataManager as a protected method would be a refactor beyond the scope of this PR — leaving it for a follow-up to keep this changeset focused. The three implementations are intentionally identical so the consolidation should be straightforward.


Requirements
Related issues
SDK-1874
Describe the solution you've provided
Moves bootstrap field definitions from platform-specific identify options into the shared
LDIdentifyOptionsbase interface, enabling all client-side SDKs to support flag bootstrapping through identify options. Each SDK's data manager handles bootstrap execution via_finishIdentifyFromBootstrap()→flagManager.setBootstrap().Changes:
bootstrapandbootstrapParsedfields to the baseLDIdentifyOptionsinterfacebootstrap/bootstrapParsedfield declarations fromBrowserIdentifyOptions(now inherited from base)ElectronIdentifyOptions.tsentirely and replaced all internal usages withLDIdentifyOptionsfrom@launchdarkly/js-client-sdk-common(inElectronClient,ElectronDataManager,LDClient,LDCommon, and test files)identifyOptions as ElectronIdentifyOptionscast inElectronDataManager.identify()sinceLDIdentifyOptionsnow has the bootstrap fields directly_finishIdentifyFromBootstrap()to React Native'sMobileDataManager, following the same pattern asBrowserDataManagerandElectronDataManager— resolves identify immediately from bootstrap, then falls through to start the network connectionstart()methods retain their synchronouspresetFlags()calls so bootstrap flags are available immediately afterstart()returnsidentify(), flag evaluation, re-identify with new bootstrap data, and defaults without bootstrapMobileDataManager._finishIdentifyFromBootstrap()follows the same pattern asElectronDataManager— confirm the bootstrap → resolve → connect flow is correct, particularly thatidentifyResolve()is not called twice when bootstrap is provided (theresolvedFromBootstrapguard prevents duplicate resolves from the cache/offline paths).ElectronIdentifyOptionsfully removed — verify no downstream/external code imports this type by name from the Electron package. It was not exported from the package index, so this should be internal-only.LDClientImpl.identifyResult()— bootstrap execution is intentionally handled by each data manager (not in common code) to avoidpresetFlags()suppressing change events whensetBootstrap()runs afterward. The common layer provides the interface fields; data managers provide the execution.Describe alternatives you've considered
Initially added
presetFlags()inLDClientImpl.identifyResult()as centralized bootstrap handling, but this caused two issues: (1) it suppressed change events on re-identify becausesetBootstrap()in data managers would see identical old/new flags, and (2) React Native'sMobileDataManagerhad no bootstrap path to resolve identify immediately. Moved bootstrap execution to each data manager instead, which correctly emits change events and resolves identify without waiting for the network.Additional context
Requestor.tsandBrowserDataManager.ts(@typescript-eslint/return-await) are unrelated to this PR.Link to Devin run: https://app.devin.ai/sessions/a8dd59797f3d4520a3cf10f2a2df1589
Requested by: @joker23
Note
Medium Risk
Changes affect identify/initialization behavior across multiple SDKs and could alter timing of promise resolution or flag state if bootstrap parsing/resolve guards are incorrect.
Overview
Moves flag bootstrap support into the shared
LDIdentifyOptionstype by addingbootstrap/bootstrapParsedtojs-client-sdk-common, and removing duplicate per-platform declarations (Browser’sBrowserIdentifyOptionsno longer defines bootstrap fields; Electron deletesElectronIdentifyOptions).Electron is updated to use
LDIdentifyOptionseverywhere (client/start/identify APIs and data manager), simplifying bootstrap handling and removing casting;ElectronDataManagerstill resolvesidentifyimmediately from bootstrap while preventing double-resolves from cache/offline paths.React Native gains equivalent bootstrap behavior in
MobileDataManager(parse bootstrap viareadFlagsFromBootstrap,setBootstrap, resolve early, then connect), with new unit tests covering bootstrap evaluation, re-identify with new bootstrap, and no-bootstrap defaults.Written by Cursor Bugbot for commit 93e83c4. This will update automatically on new commits. Configure here.