Skip to content

Comments

feat: move bootstrap capability to js-client-common (SDK-1874)#1113

Open
joker23 wants to merge 7 commits intomainfrom
devin/1771526008-sdk-1874-bootstrap-to-common
Open

feat: move bootstrap capability to js-client-common (SDK-1874)#1113
joker23 wants to merge 7 commits intomainfrom
devin/1771526008-sdk-1874-bootstrap-to-common

Conversation

@joker23
Copy link
Contributor

@joker23 joker23 commented Feb 19, 2026

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

SDK-1874

Describe the solution you've provided

Moves bootstrap field definitions from platform-specific identify options into the shared LDIdentifyOptions base 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:

  • Added bootstrap and bootstrapParsed fields to the base LDIdentifyOptions interface
  • Removed duplicate bootstrap/bootstrapParsed field declarations from BrowserIdentifyOptions (now inherited from base)
  • Deleted ElectronIdentifyOptions.ts entirely and replaced all internal usages with LDIdentifyOptions from @launchdarkly/js-client-sdk-common (in ElectronClient, ElectronDataManager, LDClient, LDCommon, and test files)
  • Removed the unnecessary identifyOptions as ElectronIdentifyOptions cast in ElectronDataManager.identify() since LDIdentifyOptions now has the bootstrap fields directly
  • Added _finishIdentifyFromBootstrap() to React Native's MobileDataManager, following the same pattern as BrowserDataManager and ElectronDataManager — resolves identify immediately from bootstrap, then falls through to start the network connection
  • Browser and Electron start() methods retain their synchronous presetFlags() calls so bootstrap flags are available immediately after start() returns
  • Added React Native bootstrap unit tests covering bootstrap via identify(), flag evaluation, re-identify with new bootstrap data, and defaults without bootstrap

⚠️ Suggested review checklist:

  1. MobileDataManager._finishIdentifyFromBootstrap() follows the same pattern as ElectronDataManager — confirm the bootstrap → resolve → connect flow is correct, particularly that identifyResolve() is not called twice when bootstrap is provided (the resolvedFromBootstrap guard prevents duplicate resolves from the cache/offline paths).
  2. React Native bootstrap tests use offline mode only, so the interaction between bootstrap and live streaming/polling connections is not directly tested. The pattern mirrors the Electron data manager which is already tested with non-offline bootstrap.
  3. ElectronIdentifyOptions fully 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.
  4. No bootstrap logic in LDClientImpl.identifyResult() — bootstrap execution is intentionally handled by each data manager (not in common code) to avoid presetFlags() suppressing change events when setBootstrap() runs afterward. The common layer provides the interface fields; data managers provide the execution.

Describe alternatives you've considered

Initially added presetFlags() in LDClientImpl.identifyResult() as centralized bootstrap handling, but this caused two issues: (1) it suppressed change events on re-identify because setBootstrap() in data managers would see identical old/new flags, and (2) React Native's MobileDataManager had 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

  • Pre-existing lint errors in Requestor.ts and BrowserDataManager.ts (@typescript-eslint/return-await) are unrelated to this PR.
  • All 98 Browser SDK tests pass, all 115 Electron SDK tests pass, all 21 React Native SDK tests pass (17 existing + 4 new bootstrap tests).
  • Cursor Bugbot identified the original issues with centralized bootstrap handling; both have been addressed in commit 93e83c4.

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 LDIdentifyOptions type by adding bootstrap/bootstrapParsed to js-client-sdk-common, and removing duplicate per-platform declarations (Browser’s BrowserIdentifyOptions no longer defines bootstrap fields; Electron deletes ElectronIdentifyOptions).

Electron is updated to use LDIdentifyOptions everywhere (client/start/identify APIs and data manager), simplifying bootstrap handling and removing casting; ElectronDataManager still resolves identify immediately from bootstrap while preventing double-resolves from cache/offline paths.

React Native gains equivalent bootstrap behavior in MobileDataManager (parse bootstrap via readFlagsFromBootstrap, 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.

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-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link
Contributor

@launchdarkly/js-sdk-common size report
This is the brotli compressed size of the ESM build.
Compressed size: 25394 bytes
Compressed size limit: 26000
Uncompressed size: 124693 bytes

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

@launchdarkly/browser size report
This is the brotli compressed size of the ESM build.
Compressed size: 171261 bytes
Compressed size limit: 200000
Uncompressed size: 798175 bytes

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

@launchdarkly/js-client-sdk-common size report
This is the brotli compressed size of the ESM build.
Compressed size: 19331 bytes
Compressed size limit: 20000
Uncompressed size: 99578 bytes

…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>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

@launchdarkly/js-client-sdk size report
This is the brotli compressed size of the ESM build.
Compressed size: 23336 bytes
Compressed size limit: 25000
Uncompressed size: 81070 bytes

devin-ai-integration bot and others added 2 commits February 19, 2026 20:28
…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>
@joker23
Copy link
Contributor Author

joker23 commented Feb 19, 2026

does this mean React Native also gets bootstrap capabilities? If so then I think we will need to add some unit tests...

@devin-ai-integration
Copy link
Contributor

Yes — React Native now gets bootstrap capabilities through the common identifyResult() path. Adding unit tests for it now.

devin-ai-integration bot and others added 2 commits February 19, 2026 21:51
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
Co-Authored-By: Steven Zhang <szhang@launchdarkly.com>
@joker23 joker23 marked this pull request as ready for review February 20, 2026 15:28
@joker23 joker23 requested a review from a team as a code owner February 20, 2026 15:28
…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>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

this._debugLog('Identify - Initialization completed from bootstrap');

identifyResolve();
}
Copy link

Choose a reason for hiding this comment

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

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)

Fix in Cursor Fix in Web

Copy link
Contributor

Choose a reason for hiding this comment

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

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.

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.

1 participant