Skip to content

feat(flags): ConfigurationWire parse + precomputed decode [PR1] (FFL-2686, FFL-2687)#1330

Open
btthomas wants to merge 3 commits into
blake.thomas/FFL-2666from
blake.thomas/FFL-2666-PR1
Open

feat(flags): ConfigurationWire parse + precomputed decode [PR1] (FFL-2686, FFL-2687)#1330
btthomas wants to merge 3 commits into
blake.thomas/FFL-2666from
blake.thomas/FFL-2666-PR1

Conversation

@btthomas

@btthomas btthomas commented Jul 7, 2026

Copy link
Copy Markdown

Offline feature flags — related PRs


Summary

Adds the pure-JS building blocks for offline feature-flag initialization in the React Native SDK (FFL-2686, FFL-2687): parsing a portable ConfigurationWire string and decoding its precomputed assignments into the SDK's internal flag-cache shape. These let a customer load a configuration they fetched themselves and evaluate flags without a network request.

New internal module packages/core/src/flags/configuration/ (not yet exported):

  • wire.tsconfigurationFromString(wire): ParsedFlagsConfiguration and configurationToString(config). Parses the ConfigurationWire v1 envelope and is lenient: it returns an empty configuration on malformed input or an unsupported version rather than throwing. ParsedFlagsConfiguration is a distinct type (not the enable() FlagsConfiguration) and reserves a server/rules branch for the future.
  • precomputed.tsdecodePrecomputedFlags(response)Record<string, FlagCacheEntry> (the shape the SDK already caches and evaluates against). The typed variationValue becomes the flag value plus a stringified form used by native Android exposure tracking; integer/float decode to a JS number while preserving the original variationType; unknown variation types are omitted; obfuscated payloads fail predictably.
  • types.ts / index.ts — types and the internal module barrier.

Tests

Unit tests cover wire parse + round-trip, unsupported version / invalid JSON, decode across every variation type, prototype-safe key handling, and obfuscation rejection. Lint and tsc are clean.

@datadog-prod-us1-4

datadog-prod-us1-4 Bot commented Jul 7, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 2d20825 | Docs | Datadog PR Page | Give us feedback!

btthomas and others added 3 commits July 7, 2026 20:55
…86, FFL-2687)

Internal, pure-JS building blocks for offline init (kept un-exported until the
exports step):

- configurationFromString / configurationToString for the ConfigurationWire v1
  envelope, with a ParsedFlagsConfiguration type (distinct from the enable()
  FlagsConfiguration). Lenient parse: returns {} on malformed input or an
  unsupported version; accepts a known set of versions.
- decodePrecomputedFlags: maps a precomputed CDN response to the existing
  FlagCacheEntry map. Injects key from the flag map key; value = typed
  variationValue (integer/float -> JS number, variationType string preserved);
  derives the string variationValue (JSON for objects, lowercase booleans) for
  Android exposure round-trip; validates variationType and omits unknowns; throws
  UnsupportedConfigurationError on obfuscated payloads.

Unit tests cover parse/round-trip, unsupported version/invalid JSON, and decode
across all variation types incl. obfuscation and mismatch handling.

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

Review follow-ups on PR1:
- H1: accumulate decoded flags in a Map and materialize with Object.fromEntries,
  so a flag keyed "__proto__" is stored as data instead of hitting the
  Object.prototype setter (which silently dropped it and reassigned the proto).
- H2: reject array values for object-typed flags (object flags are a JSON object
  at the root). Arrays are omitted plus logged, like other type mismatches.

Adds regression tests for both.

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

Remaining PR1 review follow-ups:
- M1: integer flags require a whole number; number/float require a finite value
  (reject NaN/Infinity) so native parsers round-trip.
- M2: document that serialId is intentionally not propagated (no FlagCacheEntry
  slot; native snapshot omits it too).
- L3: comment that only flags (and obfuscated) are load-bearing; the rest of the
  response attributes are optional/ignored on purpose.
- L1/L5: broaden round-trip fixture (number + nested object flags) and add tests
  for fractional integer, non-finite number, and a structurally broken response.

L2 (distinguishing parse-failure from valid-but-empty) is intentionally left to
PR2 (FFL-2688). L4 (configurationToString vs the reference's bug) is correct as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@btthomas btthomas force-pushed the blake.thomas/FFL-2666-PR1 branch from de2a6ec to 2d20825 Compare July 8, 2026 00:55
@btthomas btthomas marked this pull request as ready for review July 8, 2026 01:33
@btthomas btthomas requested a review from a team as a code owner July 8, 2026 01:33
@aarsilv aarsilv requested a review from Copilot July 8, 2026 04:39

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 introduces an internal (non-exported) flags/configuration module that enables offline feature-flag initialization by parsing a portable ConfigurationWire string and decoding precomputed CDN assignments into the SDK’s existing FlagCacheEntry shape.

Changes:

  • Added configurationFromString / configurationToString for lenient parsing/serialization of ConfigurationWire v1.
  • Added decodePrecomputedFlags (+ UnsupportedConfigurationError) to map precomputed CDN assignments to Record<string, FlagCacheEntry>, including variation-type validation and prototype-safe key handling.
  • Added types and unit tests for wire round-trip, unsupported/malformed inputs, variation-type decoding, and obfuscation rejection.

Reviewed changes

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

Show a summary per file
File Description
packages/core/src/flags/configuration/wire.ts Lenient parse/serialize helpers for ConfigurationWire v1 and embedded precomputed response JSON.
packages/core/src/flags/configuration/types.ts Defines wire envelope types, supported versions/variation types, and parsed configuration shapes.
packages/core/src/flags/configuration/precomputed.ts Decoder from precomputed CDN response to FlagCacheEntry map, with validation and predictable failures.
packages/core/src/flags/configuration/index.ts Internal module boundary + re-exports for configuration parsing/decoding primitives.
packages/core/src/flags/configuration/tests/wire.test.ts Unit tests for wire parsing behavior and serialization round-trip.
packages/core/src/flags/configuration/tests/precomputed.test.ts Unit tests for decoding across variation types, obfuscation rejection, and prototype-safe keys.

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

Comment on lines +71 to +76
const toFlagCacheEntry = (
key: string,
flag: PrecomputedFlag
): FlagCacheEntry | null => {
const { variationType, variationValue } = flag;

Comment on lines +93 to +106
// `serialId` is intentionally not propagated: `FlagCacheEntry` has no slot for it
// and the native CDN-fetched snapshot omits it too, so dropping it keeps
// offline/online parity.
return {
key,
value: variationValue,
allocationKey: flag.allocationKey,
variationKey: flag.variationKey,
variationType,
variationValue: stringifyValue(variationValue),
reason: flag.reason,
doLog: flag.doLog,
extraLogging: flag.extraLogging ?? {}
};
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