feat: FDv2DataManagerBase for mode switching and data source lifecycle#1210
feat: FDv2DataManagerBase for mode switching and data source lifecycle#1210kinyoklion merged 33 commits intomainfrom
Conversation
…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.
…data-manager-base
…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.
… 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.
…data-manager-base
…er-apply-changes # Conflicts: # packages/shared/sdk-client/src/datasource/fdv2/FDv2DataSource.ts
…er-base # Conflicts: # packages/shared/sdk-client/src/index.ts
Account for new FDv2 applyChanges code in FlagUpdater/FlagStore that flows into the browser SDK bundle.
…data-manager-base
…data-manager-base
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.
…data-manager-base
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
|
@launchdarkly/browser size report |
|
@launchdarkly/js-client-sdk-common size report |
…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', |
There was a problem hiding this comment.
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.
packages/shared/sdk-client/src/api/datasource/LDClientDataSystemOptions.ts
Outdated
Show resolved
Hide resolved
packages/shared/sdk-client/src/datasource/FDv2DataManagerBase.ts
Outdated
Show resolved
Hide resolved
packages/shared/sdk-client/src/datasource/FDv2DataManagerBase.ts
Outdated
Show resolved
Hide 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.
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.
…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
…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.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
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'; |
There was a problem hiding this comment.
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.
## 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 -->


Summary
FDv2DataManagerBase— shared data manager orchestrating FDv2 connection mode switching, state debouncing, and data source lifecycleDataManagerinterface with foreground/background mode support, forced/automatic streaming control, and flush callbacksFlagManager.applyChangesfrom feat: FlagManager.applyChanges for FDv2 full/partial/none semantics #1208 for full/partial/none flag update semanticsStacked 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
dataSystemoption 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, managesFDv2DataSourcecreation/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
dataSystemconfiguration model by renaminginitialConnectionModetoforegroundConnectionModein platform defaults and by makingautomaticModeSwitchinga discriminated union ({type:'automatic'}vs{type:'manual', initialConnectionMode}), with validator support viavalidatorOf(..., { is }); tests are updated and expanded accordingly, including a large new test suite forFDv2DataManagerBase.Written by Cursor Bugbot for commit 0786b64. This will update automatically on new commits. Configure here.