Repository navigation
feat(session-replay-react-native): honor isEnabled: false and add initializeSessionReplay - #785
Merged
Merged
Conversation
… 0.56.0 Picks up LaunchDarkly iOS Client SDK 11.6.2. Changes since 0.53.1 are additive apart from ObservabilityOptions.crashReporting now defaulting to .disabled, which the adapter already sets explicitly. Co-authored-by: Cursor <cursoragent@cursor.com>
…tializeSessionReplay startSessionReplay() re-applied the configured isEnabled, so the documented deferred-start pattern (isEnabled: false, then startSessionReplay() later) never recorded — the call wrote false back on iOS and called LDReplay.stop() on Android. startSessionReplay() now always enables recording, and the plugin's auto-start moved to a new initializeSessionReplay() that honors isEnabled. Neither adapter writes false any more, so a re-init (a JS soft/OTA reload, say) no longer silently stops a recording an earlier start began. The plugin feature-detects the new method so an OTA bundle running against an older native binary still initializes. On iOS the outcome is mapped from LDReplay.start(ignoreSampling:), so an unavailable plugin or an unrecoverable error rejects the promise instead of resolving as success. Android keeps plain LDReplay.start() until the launchdarkly-observability-android dependency exposes the same result API. Co-authored-by: Cursor <cursoragent@cursor.com>
…observability-android 0.69.0 Bumps launchdarkly-observability-android 0.66.1 -> 0.69.0 to pick up LDReplay.start(ignoreSampling): SessionReplayStartResult, and maps the result the same way iOS already does: started/alreadyStarted and sampledOut resolve, while unavailable and unrecoverableError reject with the same messages. This clears the TODO left in the previous commit, so both platforms now report why a start did not record instead of always resolving as success. The call runs inline on the main thread rather than hopping, because the adapter is already inside a main-looper post and the core executor runs the block directly when it is already on the main thread. The mapping sits after Handler.post, which the plain JVM unit tests cannot reach; the enum-to-behavior mapping is covered by the core SDK's own tests. Co-authored-by: Cursor <cursoragent@cursor.com>
zack-winchell-ld
approved these changes
Sep 30, 2026
abelonogov-ld
enabled auto-merge (squash)
September 30, 2026 21:22
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort 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.
Reviewed by Cursor Bugbot for commit 6d3a1b8. Configure here.
Merged
abelonogov-ld
pushed a commit
that referenced
this pull request
Sep 30, 2026
🤖 I have created a release *beep* *boop* --- <details><summary>session-replay-react-native: 0.24.0</summary> ## [0.24.0](session-replay-react-native-0.23.0...session-replay-react-native-0.24.0) (2026-09-30) ### Features * **session-replay-react-native:** honor isEnabled: false and add initializeSessionReplay ([#785](#785)) ([9f9ca81](9f9ca81)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > **Release Please** publishes `@launchdarkly/session-replay-react-native` **0.24.0** by bumping the package version, updating `.release-please-manifest.json`, and adding a **0.24.0** changelog entry. > > The release notes document feature work from [#785](#785) (already on main): honoring **`isEnabled: false`** so replay does not auto-start, and exposing **`initializeSessionReplay`** for explicit initialization. This PR does not include additional source changes beyond version and release metadata. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9867231. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

The bug
The README documents deferred recording as
isEnabled: falseplus a laterstartSessionReplay(), but that never recorded.startSessionReplay()re-applied the configuredisEnabled, so for a deferred start it wrote thefalsestraight back:LDReplay.shared.isEnabled = sessionReplayOptions.isEnabledapplyEnabled(localReplayOptions.enabled), which callsLDReplay.stop()onfalseThe same code is the plugin's lazy-initialization path, which is why it read the config at all: one method was doing double duty as "initialize, honoring config" and as the public "start recording". Those wants are contradictory, so this splits them.
The fix
startSessionReplay()initializeSessionReplay()— initializes, honorsisEnabledstartSessionReplay()— re-appliedisEnabledstartSessionReplay()— initializes if needed, then always enablesNeither adapter writes
falseany more;stopSessionReplay()is the only thing that stops recording. That also fixes a quieter bug: a re-init — a JS soft/OTA reload re-running plugin setup, for instance — used to silently stop a recording an earlierstartSessionReplay()had begun, because the native instance outlives the JS context.configureAndStartfeature-detectsinitializeSessionReplay, since a JS-only OTA update can run this bundle against an older native binary. Falling back tostartSessionReplay()there is safe: honoringisEnabledon the first start is exactly the old binary's behavior.Both platforms now also report why a start didn't record, instead of always resolving as success. They call
LDReplay.start(ignoreSampling: false)and map the result identically:started/alreadyStartedandsampledOutresolve — honoringsampleRateis not a failure — whileunavailableandunrecoverableErrorreject.startis used overisEnabled = truebecause the setter's no-change guard would otherwise swallow a start on a session that is enabled but not recording.Dependency bumps
Both are needed for
start(ignoreSampling:)and its result enum:LaunchDarklySessionReplay~> 0.53.1→~> 0.56.0launchdarkly-observability-android0.66.1→0.69.0, which carries #783Verification
0.56.0. A singleconfigure, then both new paths firing as designed:sampleRate: 0logsSession Replay skipped by sampling, and that line is absent atsampleRate: 1.0.initializeSessionReplayand notstartSessionReplay, thatisEnabled: falsedoes not force recording on, and the OTA fallback.configure()never set up.One coverage gap worth flagging: the Android result mapping sits after
Handler.post, which the package's plain JVM unit tests cannot reach, so it is not covered here. The enum-to-behavior mapping itself is covered by the core SDK's tests in #783, and iOS has no adapter-level test for its equivalent either.Other changes
isEnabledrow in the options table, and thesampleRatedescription no longer implies it only applies viaisEnabled.isEnabled: trueworkaround it needed before.Note
Overview
Fixes deferred session replay:
isEnabled: falseplus a laterstartSessionReplay()used to leave recording off because the native start path re-applied the configuredisEnabled(and could even stop an active recording on JS re-init).The plugin now auto-starts via
initializeSessionReplay(), which initializes native replay/observability and only turns on recording whenisEnabledis true.startSessionReplay()still initializes if needed but always enables recording, without anotherconfigurecall. OnlystopSessionReplay()stops recording.iOS and Android adapters take a
forceEnableflag, stop togglingenabledoff on start, and callLDReplay.start(ignoreSampling: false)with mapped outcomes (sampledOutsucceeds;unavailable/ unrecoverable errors reject). Native deps are bumped (LaunchDarklySessionReplay ~> 0.56.0, launchdarkly-observability-android 0.69.0). JS adds OTA fallback wheninitializeSessionReplayis missing, README/docs for delaying recording, example Start replay button, and expanded tests.Reviewed by Cursor Bugbot for commit 6d3a1b8. Bugbot is set up for automated code reviews on this repo. Configure here.