Skip to content

feat: FDv2DataManagerBase for mode switching and data source lifecycle#1210

Merged
kinyoklion merged 33 commits intomainfrom
rlamb/fdv2-data-manager-base
Mar 25, 2026
Merged

feat: FDv2DataManagerBase for mode switching and data source lifecycle#1210
kinyoklion merged 33 commits intomainfrom
rlamb/fdv2-data-manager-base

Conversation

@kinyoklion
Copy link
Copy Markdown
Member

@kinyoklion kinyoklion commented Mar 19, 2026

Summary

  • Add FDv2DataManagerBase — shared data manager orchestrating FDv2 connection mode switching, state debouncing, and data source lifecycle
  • Implements DataManager interface with foreground/background mode support, forced/automatic streaming control, and flush callbacks
  • Includes FlagManager.applyChanges from feat: FlagManager.applyChanges for FDv2 full/partial/none semantics #1208 for full/partial/none flag update semantics

Stacked on #1209 and #1208.


Note

Medium Risk
Adds a new shared FDv2 data manager that controls connection-mode resolution and data source lifecycle, plus updates public dataSystem option shapes; mistakes here could affect SDK initialization, reconnection behavior, and flag freshness across platforms.

Overview
Introduces createFDv2DataManagerBase (and exported types) as a shared FDv2 orchestration layer that performs mode resolution/debouncing, manages FDv2DataSource creation/teardown across identifies and mode changes, tracks selector state, triggers a background flush callback, and optionally appends a blocked FDv1 polling fallback synchronizer.

Updates the public dataSystem configuration model by renaming initialConnectionMode to foregroundConnectionMode in platform defaults and by making automaticModeSwitching a discriminated union ({type:'automatic'} vs {type:'manual', initialConnectionMode}), with validator support via validatorOf(..., { is }); tests are updated and expanded accordingly, including a large new test suite for FDv2DataManagerBase.

Written by Cursor Bugbot for commit 0786b64. This will update automatically on new commits. Configure here.


Open with Devin

…xtensions

Add InitializerEntry/SynchronizerEntry types for type-safe mode definitions,
refine protocolHandler validation to use isNullish for version/target fields,
add connectionModes config option, split validator arrays for initializers vs
synchronizers, and extend DataManager with optional streaming/flush methods.
Add applyChanges method to FlagManager and FlagPersistence that handles
full replacement, partial upsert, and no-change persistence in a single
call, designed for the FDv2 data path.
Add SourceFactoryProvider that converts declarative InitializerEntry and
SynchronizerEntry config into concrete initializer factories and
synchronizer slots, with support for per-entry endpoint and interval overrides.
…cycle

Add shared data manager that orchestrates FDv2 connection mode switching,
state debouncing, streaming control, and data source lifecycle management.
Implements the DataManager interface with support for foreground/background
modes, forced/automatic streaming, and flush callbacks.
- Fix ping handler to use per-entry endpoint-overridden requestor and
  the factory's selector getter (sg) for fresh selectors on each ping
- Extract duplicated streaming base config into shared buildStreamingBase helper
- Restructure SourceFactoryContext to group polling/streaming config into
  nested objects (polling.paths, polling.intervalSeconds, streaming.paths,
  streaming.initialReconnectDelaySeconds)
- Resolve merge conflict in FDv2DataSource (warn -> debug)
Adapt factory context construction to use nested polling/streaming config
objects and remove selectorGetter from the context (now passed directly
via factory sg parameter).
Account for new FDv2 SourceFactoryProvider and related code paths.
Base automatically changed from rlamb/fdv2-source-factory-provider to main March 20, 2026 20:56
… checks

Push applyChanges down through the flag manager layers so the FDv2 path
no longer routes through FDv1's upsert (which has version checks and
inactive-context guards). FlagStore.applyPartial inserts without version
comparison, and FlagUpdater.applyChanges sets the active context directly
and emits change events.
…er-apply-changes

# Conflicts:
#	packages/shared/sdk-client/src/datasource/fdv2/FDv2DataSource.ts
…er-base

# Conflicts:
#	packages/shared/sdk-client/src/index.ts
@kinyoklion kinyoklion changed the base branch from main to rlamb/fdv2-flag-manager-apply-changes March 20, 2026 21:14
Account for new FDv2 applyChanges code in FlagUpdater/FlagStore that
flows into the browser SDK bundle.
Replace FlagStore.applyPartial with FlagStore.applyChanges that handles
full/partial/none, delegating to init for full. FlagUpdater.applyChanges
now delegates storage to FlagStore.applyChanges and only handles change
event computation.
Base automatically changed from rlamb/fdv2-flag-manager-apply-changes to main March 20, 2026 23:04
@github-actions
Copy link
Copy Markdown
Contributor

@launchdarkly/js-sdk-common size report
This is the brotli compressed size of the ESM build.
Compressed size: 25661 bytes
Compressed size limit: 29000
Uncompressed size: 126143 bytes

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 21, 2026

@launchdarkly/js-client-sdk size report
This is the brotli compressed size of the ESM build.
Compressed size: 25214 bytes
Compressed size limit: 34000
Uncompressed size: 87656 bytes

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 21, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 21, 2026

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

…onnectionMode

- Remove initialConnectionMode from LDClientDataSystemOptions
- Add ManualModeSwitching interface (type: 'manual' + initialConnectionMode)
- Add type: 'automatic' discriminant to AutomaticModeSwitchingConfig
- Rename PlatformDataSystemDefaults.initialConnectionMode to foregroundConnectionMode
- Update validators to accept boolean | automatic config | manual config
- Update all tests for new shape
credentialType: 'clientSideId',
dataSystemDefaults: {
initialConnectionMode: 'one-shot',
foregroundConnectionMode: 'one-shot',
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is now the platform specific default, and not the same thing as the initial connection mode.

The config field represents the configured foreground mode, not just the
initial one. Internally aliased to configuredForegroundMode to avoid
collision with the mutable foregroundMode state variable.
Clear the selector when an FDv1 synchronizer delivers a payload without
state, preventing stale selectors from being sent on subsequent requests.
@kinyoklion kinyoklion marked this pull request as ready for review March 24, 2026 16:15
@kinyoklion kinyoklion requested a review from a team as a code owner March 24, 2026 16:15
cursor[bot]

This comment was marked as resolved.

…e leak

If close() is called while namespaceForEnvironment is pending, identify
would resume and create a data source and debounce manager that would
never be cleaned up. Add a guard after the await to bail out early.
cursor[bot]

This comment was marked as resolved.

Update automaticModeSwitching docs to reference AutomaticModeSwitchingConfig
and ManualModeSwitching types instead of re-declaring the shape inline.
Add discriminant docs and cross-references to related types.
cursor[bot]

This comment was marked as resolved.

…n table

Replace setRequestedMode and setForegroundMode with setConnectionMode
that sets an explicit connection mode override. When set, it bypasses
the entire transition table (network, lifecycle, streaming logic). Pass
undefined to clear and return to automatic behavior.

Priority hierarchy:
1. connectionModeOverride (setConnectionMode)
2. Transition table with foreground mode from:
   a. forcedStreaming
   b. automaticStreaming
   c. configuredForegroundMode
cursor[bot]

This comment was marked as resolved.

…ction

The synchronous flag updates and change events are the critical path.
The returned promise is only for async cache persistence — intentionally
not awaited so the data source pipeline isn't blocked by storage I/O.
Add optional is parameter to validatorOf so anyOf can discriminate
between object shapes by checking a type field. Split mode switching
validators into separate automatic and manual variants with type-based
is predicates.
Copy link
Copy Markdown

@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.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

NetworkState,
PendingState,
ReconciliationCallback,
} from './datasource/StateDebounceManager';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LifecycleState missing from StateDebounceManager re-exports

Low Severity

NetworkState is re-exported from StateDebounceManager but its sibling type LifecycleState is not, even though both are used symmetrically in the exported FDv2DataManagerControl interface (setNetworkState and setLifecycleState). While LifecycleState is available from the api/datasource export path, the asymmetry in the StateDebounceManager export block is confusing for consumers looking at these related types side-by-side.

Fix in Cursor Fix in Web

@joker23 joker23 self-requested a review March 25, 2026 19:12
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@kinyoklion kinyoklion merged commit 8f8051c into main Mar 25, 2026
46 checks passed
@kinyoklion kinyoklion deleted the rlamb/fdv2-data-manager-base branch March 25, 2026 20:42
@github-actions github-actions bot mentioned this pull request Mar 25, 2026
kinyoklion added a commit that referenced this pull request Mar 25, 2026
## Summary
- Conditionally use `FDv2DataManagerBase` when `dataSystem` config is
present, falling back to `BrowserDataManager` for FDv1
- Derive foreground mode from
`ManualModeSwitching.initialConnectionMode` or browser platform default
- Use optional chaining for
`setForcedStreaming`/`setAutomaticStreamingState`/`setFlushCallback` via
the `DataManager` interface methods
- Update `combined-browser.yml` bundle size limit for FDv2 code paths

Stacked on #1210.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Introduces a conditional switch between FDv1 and FDv2 data paths in
`BrowserClient`, which can affect endpoint selection and streaming
behavior. Risk is mitigated by fallback to FDv1 when `dataSystem` is
unset and added tests covering routing/validation behavior.
> 
> **Overview**
> **BrowserClient now conditionally uses FDv2**: when
`configuration.dataSystem` is present, it instantiates
`createFDv2DataManagerBase` (using `BROWSER_TRANSITION_TABLE`,
`MODE_TABLE`, and FDv2 query params), otherwise it continues using the
existing `BrowserDataManager`/FDv1 endpoints.
> 
> **Streaming/flush integration is generalized** by calling
`setForcedStreaming`, `setAutomaticStreamingState`, and
`setFlushCallback` via optional chaining so the same client logic works
across FDv1/FDv2 managers; explicit `streaming: false` is forwarded to
prevent FDv2 auto-promotion.
> 
> Adds `resolveForegroundMode` to derive FDv2 foreground mode from
`ManualModeSwitching.initialConnectionMode` (or platform defaults),
exports it from `sdk-client`, and extends browser tests to assert FDv1
vs FDv2 endpoint selection and that invalid `dataSystem` sub-options
warn but still enable FDv2.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
04afb4e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

2 participants