You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds exposure, flag evaluation, and RUM tracking hooks for DatadogCoreProvider, written in TypeScript (no native bridge). Import them from @datadog/mobile-react-native-openfeature/rules-based:
The hooks are ported from the browser SDK and send requests in the same format.
It also changes core: RUM resource tracking now ignores these uploads, so they don't show up as RUM resources.
Motivation
FFL-3355. This completes the building-blocks model for DatadogCoreProvider without waiting for native SDK changes (the plan in #1456). Each hook can be enabled on its own.
Additional Notes
Use these hooks only with DatadogCoreProvider. The other providers already track natively, so adding the hooks would count every evaluation twice.
Configure them separately. The hooks take their own options and don't read the DdSdkReactNative configuration.
Upgrade core in the same release. With an older @datadog/mobile-react-native, the uploads appear as RUM resources.
Known gaps compared with the browser SDK: failed requests aren't retried, exposure deduplication resets when the app restarts, and events have no RUM view URL.
Tests: unit tests for the transport and each hook, end-to-end tests with the real OpenFeature client, and tests for the core filter. The openfeature and core suites pass.
Review checklist (to be filled by reviewers)
Feature or bugfix MUST have appropriate tests
Make sure you discussed the feature or bugfix with the maintaining team in an Issue
Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
If this PR is auto-generated, please make sure also to manually update the code related to the change
…355)
The OpenFeature package's JavaScript tracking hooks send exposures and
flag evaluations with fetch. RUM resource tracking proxies fetch and XHR,
so those uploads would be reported as RUM resources. Drop resources for
/api/v2/exposures and /api/v2/flagevaluation on the browser intake host,
and for the same paths forwarded through a ddforward proxy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…der (FFL-3355)
Port the browser SDK's building-block tracking hooks to TypeScript and
export them from the /rules-based entry:
- createDatadogExposureLoggingHook sends exposures to the intake, with
in-memory deduplication scoped to the core configuration identity.
- createDatadogEvaluationLoggingHook aggregates evaluations with
flagging-core's FlagEvaluationAggregator and sends them to the
flagevaluation intake.
- createDatadogRumTrackingHook adds evaluated variants to RUM with
DdRum.addFeatureFlagEvaluation, loading the native SDK lazily so the
/rules-based entry does not require it.
- composeDatadogTrackingHooks combines hooks and lifecycle methods.
The hooks take explicit options and do not use DdFlags or the native
trackEvaluation bridge. Events are batched as newline-delimited JSON and
sent when a batch fills, after a timeout, when the app leaves the
foreground, and on shutdown.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…FL-3355)
The browser and native Feature Flags SDKs send dd-evp-origin-version with
exposure and flag evaluation uploads, and the intake workers record it as
the event's source version. Send the package version the same way.
The version comes from src/version.ts, generated by genversion from the
package's package.json in the root prepare, test, and lint scripts, as
core's version module is. Publishing runs prepare, so the published
package always reports its own version.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(FFL-3355)
The flagging intake filter dropped every RUM resource for
/api/v2/exposures and /api/v2/flagevaluation, which could hide
application requests to the same paths. The OpenFeature transport always
sends ddsource=react-native as the first parameter, so require that
marker in both the direct and ddforward patterns.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This generated package manifest omits every artifact produced by the newly added src/version.ts (lib/commonjs/version.js{,.map}, lib/module/version.js{,.map}, the declaration files, and package/src/version.ts). Since transport.ts imports that module, these files will be present in the packed package, so the checked-in release-content snapshot is incomplete; please regenerate it from the package tarball.
…tervals (FFL-3355)
NaN passed the number type and survived the clamp, so the evaluation
aggregator's timers fired almost immediately. Use the documented 10s
default for NaN and Infinity before clamping.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…FFL-3355)
The generated src/version.ts adds compiled, declaration, and source files
to the package. Regenerate release-content.txt from a fresh package build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Package manifest omits generated version artifacts (packages/react-native-openfeature/release-content.txt)
Fixed in chore(openfeature): add version module artifacts to release content (FFL-3355) (a42f0c4). I regenerated the manifest from a fresh package build. It now lists the version CommonJS and module builds, the declaration files, and src/version.ts.
The batch is bounded only by event count, so valid evaluations with large context values can produce an arbitrarily large request; one oversized payload can then fail and drop all 50 events. The browser transport being matched also flushes before 16 KiB and discards a single message above 256 KiB. Please track UTF-8 payload bytes and flush/drop at equivalent limits rather than relying only on maxEvents.
Batches were bounded only by event count, so events with large evaluation
contexts could build an oversized request, and one failed request dropped
up to 50 events. Match browser-core's batch limits:
- Send the batch before an event would bring it to 16 KiB or more, and
once it reaches 16 KiB.
- Drop single events of 256 KiB or more.
- Count UTF-8 bytes without TextEncoder, which older Hermes and JSC lack.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
I understand the limitation discussed in #1456: the current native call (FlagsClient.track() → NativeDdFlags.trackEvaluation() → native iOS/Android tracking) combines tracking behaviors, controlled by native configuration. Calling it from multiple independent hooks could double-track. So we either need to expose a single combined native tracking hook or extend the native API to support independently selectable tracking operations.
Overall, not loving introducing a second upload pipeline as this duplicates existing functionality, but I think I'm okay with this JS implementation as a temporary measure until we extend the native API to separate tracking operations. It's hard to say right now if a single combined native tracking hook might become problematic in the future, so I'm less inclined in that direction
The reason will be displayed to describe this comment to others. Learn more.
The JavaScript uploads bypass tracking consent, which the native SDK normally enforces, and the hook options provide no separate consent API. An application using PENDING or NOT_GRANTED can still upload targeting IDs and attributes. shutdown() also flushes pending data, so it cannot safely serve as consent revocation.
Is there a way for these hooks to reuse native consent handling or provide an explicit equivalent? Pending events should also be discarded when consent is revoked, rather than flushed.
…(FFL-3355)
OpenFeature skips the `after` stage for failed evaluations, such as
FLAG_NOT_FOUND and TYPE_MISMATCH, so the evaluation logging hook never
recorded them. Record evaluations in `finally`, which runs for both
successful and failed evaluations, as the Datadog server SDKs do. Pass
the error message, or the error code when there is no message, to the
aggregator. createTrackingHookController now forwards `finally` as well
as `after`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🟢 Approval recommended
The tracking lifecycle, transport behavior, resource filtering, public exports, documentation, and edge cases are consistently implemented and covered by tests.
I understand the limitation discussed in #1456: the current native call (FlagsClient.track() → NativeDdFlags.trackEvaluation() → native iOS/Android tracking) combines tracking behaviors, controlled by native configuration. Calling it from multiple independent hooks could double-track. So we either need to expose a single combined native tracking hook or extend the native API to support independently selectable tracking operations.
Overall, not loving introducing a second upload pipeline as this duplicates existing functionality, but I think I'm okay with this JS implementation as a temporary measure until we extend the native API to separate tracking operations. It's hard to say right now if a single combined native tracking hook might become problematic in the future, so I'm less inclined in that direction
@sameerank Yeah, I'm also less inclined to have a single hook. I think feature parity with browser is important from a dev experience perspective. We can treat this as a stop gap until granular track functionality is implemented in ios and android.
Closing this out. Creating a way to observe consent through the react native bridge is getting a little messy. We'll just implement the granular hooks in the native SDKs first
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
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.
What does this PR do?
Adds exposure, flag evaluation, and RUM tracking hooks for
DatadogCoreProvider, written in TypeScript (no native bridge). Import them from@datadog/mobile-react-native-openfeature/rules-based:createDatadogExposureLoggingHook/api/v2/exposurescreateDatadogEvaluationLoggingHook/api/v2/flagevaluationcreateDatadogRumTrackingHookDdRum.addFeatureFlagEvaluation(flagKey, variant)composeDatadogTrackingHooksinitialize/shutdownThe hooks are ported from the browser SDK and send requests in the same format.
It also changes core: RUM resource tracking now ignores these uploads, so they don't show up as RUM resources.
Motivation
FFL-3355. This completes the building-blocks model for
DatadogCoreProviderwithout waiting for native SDK changes (the plan in #1456). Each hook can be enabled on its own.Additional Notes
DatadogCoreProvider. The other providers already track natively, so adding the hooks would count every evaluation twice.DdSdkReactNativeconfiguration.@datadog/mobile-react-native, the uploads appear as RUM resources.Review checklist (to be filled by reviewers)