Skip to content

feat(openfeature): DatadogOfflineOpenFeatureProvider [PR3] (FFL-2689, FFL-2690)#1332

Draft
btthomas wants to merge 3 commits into
blake.thomas/FFL-2666-PR2from
blake.thomas/FFL-2666-PR3
Draft

feat(openfeature): DatadogOfflineOpenFeatureProvider [PR3] (FFL-2689, FFL-2690)#1332
btthomas wants to merge 3 commits into
blake.thomas/FFL-2666-PR2from
blake.thomas/FFL-2666-PR3

Conversation

@btthomas

@btthomas btthomas commented Jul 7, 2026

Copy link
Copy Markdown

Offline feature flags — related PRs


Summary

Adds DatadogOfflineOpenFeatureProvider — a never-fetch OpenFeature provider for offline initialization — and the public exports for the offline API.

The offline provider evaluates flags and reports exposures/RUM exactly like DatadogOpenFeatureProvider, but never fetches configuration from the network: initialize/onContextChange set the evaluation context without a request, and the configuration is supplied via setConfiguration. A precomputed configuration carries the context it was computed for, so no separate OpenFeature.setContext call is needed.

  • Both providers share an internal DatadogCoreOpenFeatureProvider base that owns the flag client, event emitter, and flag evaluation; only the context-sourcing (initialize/onContextChange) differs.
  • Provider events: a loaded configuration emits PROVIDER_CONFIGURATION_CHANGED; an unusable or context-mismatched configuration emits PROVIDER_ERROR.
  • Public exports — core: configurationFromString, configurationToString, ParsedFlagsConfiguration. OpenFeature: DatadogOfflineOpenFeatureProvider and a re-exported configurationFromString. The README documents offline usage.

No native changes: the offline path uses the existing native enable + exposure tracking and skips the native fetch; evaluation is all JS.

const provider = new DatadogOfflineOpenFeatureProvider();
provider.setConfiguration(configurationFromString(wire)); // no network
await OpenFeature.setProviderAndWait(provider);

const enabled = OpenFeature.getClient().getBooleanValue('new-feature', false);

Tests

react-native-openfeature (13 tests) covers both providers — the offline provider never fetches, honors the configuration's context, emits the right events, and (via a real-FlagsClient integration test) serves flags end to end; the online provider still fetches on context. The core flags/ suite (53 tests) passes. Lint and tsc are clean on both packages.

Example App

iOS

Screenshot 2026-07-07 at 9 34 43 PM

Android

Screenshot 2026-07-07 at 9 44 29 PM

btthomas and others added 3 commits July 7, 2026 20:55
…FL-2690)

A never-fetch OpenFeature provider for offline initialization. It behaves like
DatadogOpenFeatureProvider — same evaluation and exposure/RUM tracking — except
initialize/onContextChange use FlagsClient.setEvaluationContextWithoutFetching
instead of the fetching setEvaluationContext, so it never hits the CDN. It
exposes setConfiguration (delegating to the FlagsClient) and emits provider
events from the configuration outcome: first config -> READY, subsequent ->
CONFIGURATION_CHANGED, mismatch/invalid -> ERROR.

- Core: setConfiguration and setEvaluationContextWithoutFetching now return a
  ConfigurationStatus so the provider can map outcome -> event; export
  configurationFromString/configurationToString, ParsedFlagsConfiguration, and
  ConfigurationStatus.
- react-native-openfeature: make flagsClient protected and export toDdContext so
  the offline provider can reuse the base; export DatadogOfflineOpenFeatureProvider
  and re-export configurationFromString; README offline-init section.

No native changes — the offline path uses existing native enable + trackEvaluation
and skips native setEvaluationContext; evaluation is all JS.

Tests: provider never calls the fetching path, delegates setConfiguration, emits
the right events, and resolves through the client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both the online and offline providers now extend a shared, internal (un-exported)
DatadogCoreOpenFeatureProvider that owns the FlagsClient, event emitter, and
resolveX evaluation. DatadogOpenFeatureProvider keeps its fetch-on-context
initialize/onContextChange, and DatadogOfflineOpenFeatureProvider extends the
base directly instead of the online provider, so it no longer inherits the
online fetch/promise-chain machinery. Public API and online behavior are
unchanged and the base is not exported. Matches the Portable RFC's CoreProvider.

Adds a provider test locking the online provider's fetch-on-initialize behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… exports)

Two-agent review follow-ups on the offline provider:
- C1 (critical): the offline provider no longer stamps an empty OpenFeature
  context in initialize/onContextChange, so OpenFeature's initialize({}) on
  setProviderAndWait can't defeat the configuration's embedded context (which
  previously caused a spurious mismatch -> PROVIDER_ERROR for every real config).
- H1/M1 (events): the provider now emits CONFIGURATION_CHANGED on a successful
  load (OpenFeature already emits READY when initialize resolves) and emits READY
  only to clear a prior error state, removing the premature/duplicate READY and
  fixing error recovery.
- M2/L5: keep ConfigurationStatus internal (dropped from the public core export;
  the provider derives it via ReturnType) and document it may widen for rules.
- L4: move toDdContext (and a new isEmptyContext) into mappers.ts so the base
  module no longer carries an exported free helper.
- L3: fix the offline provider's stale TSDoc link.

Tests: add a real-FlagsClient integration test that would have caught C1
(initialize({}) then setConfiguration serves; explicit mismatch errors), and
update the unit tests for the empty-context and event behavior. Also lock the
online provider's fetch-on-initialize behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@btthomas btthomas force-pushed the blake.thomas/FFL-2666-PR2 branch from 5a7a06e to 8cce4b5 Compare July 8, 2026 00:55
@btthomas btthomas force-pushed the blake.thomas/FFL-2666-PR3 branch from 2ee5c00 to 38a7e7a Compare July 8, 2026 00:56
@aarsilv aarsilv requested a review from Copilot July 8, 2026 04:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an offline OpenFeature provider for the React Native Datadog SDK, enabling feature-flag evaluation from a precomputed configuration supplied by the app (no network fetch), while keeping evaluation + exposure/RUM tracking aligned with the existing online provider.

Changes:

  • Introduces DatadogOfflineOpenFeatureProvider and refactors shared logic into DatadogCoreOpenFeatureProvider.
  • Adds offline configuration plumbing/events (context set without fetch; emit ConfigurationChanged / Error / Ready as appropriate).
  • Publicly exports offline configuration helpers/types from core (configurationFromString, configurationToString, ParsedFlagsConfiguration) and re-exports configurationFromString from the OpenFeature package; updates docs + adds tests.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/react-native-openfeature/src/provider.ts Refactors online provider to extend shared core provider; keeps fetch-on-context behavior.
packages/react-native-openfeature/src/offlineProvider.ts Adds offline provider that never fetches; supports setConfiguration + event mapping.
packages/react-native-openfeature/src/mappers.ts Extracts OF → Datadog context conversion + empty-context detection helper.
packages/react-native-openfeature/src/index.ts Exports offline provider and re-exports configurationFromString.
packages/react-native-openfeature/src/coreProvider.ts Adds shared base provider owning FlagsClient, event emitter, and evaluations.
packages/react-native-openfeature/src/tests/provider.test.ts Adds unit coverage for online provider behavior after refactor.
packages/react-native-openfeature/src/tests/offlineProvider.test.ts Adds unit coverage for offline provider no-fetch behavior + events.
packages/react-native-openfeature/src/tests/offlineProvider.integration.test.ts Adds integration coverage using real FlagsClient for offline flow edge cases.
packages/react-native-openfeature/README.md Documents offline initialization flow and usage.
packages/core/src/index.tsx Exposes configuration parse/serialize utilities and parsed config type publicly.
packages/core/src/flags/FlagsClient.ts Returns configuration outcome status from offline context/config APIs for provider event mapping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +59
constructor(options: DatadogOpenFeatureProviderOptions = {}) {
if (!options.clientName) {
options.clientName = 'default';
}

this.options = options;

this.flagsClient = DdFlags.getClient(this.options.clientName);
}
Comment on lines +34 to +40
* Whether an OpenFeature evaluation context carries no information (no targeting key and no
* attributes). Used by the offline provider to avoid overwriting a configuration's embedded
* context with an empty context stamped by the OpenFeature lifecycle.
*/
export const isEmptyContext = (context: OFEvaluationContext): boolean => {
return Object.keys(context).length === 0;
};
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