feat(flags): ConfigurationWire parse + precomputed decode [PR1] (FFL-2686, FFL-2687)#1330
Open
btthomas wants to merge 3 commits into
Open
feat(flags): ConfigurationWire parse + precomputed decode [PR1] (FFL-2686, FFL-2687)#1330btthomas wants to merge 3 commits into
btthomas wants to merge 3 commits into
Conversation
🎉 All green!🧪 All tests passed 🔗 Commit SHA: 2d20825 | Docs | Datadog PR Page | Give us feedback! |
…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>
de2a6ec to
2d20825
Compare
There was a problem hiding this comment.
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/configurationToStringfor lenient parsing/serialization ofConfigurationWirev1. - Added
decodePrecomputedFlags(+UnsupportedConfigurationError) to map precomputed CDN assignments toRecord<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 ?? {} | ||
| }; |
4 tasks
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.
Offline feature flags — related PRs
FlagsClient.setConfiguration): feat(flags): FlagsClient.setConfiguration + context matching [PR2] (FFL-2688) #1331Summary
Adds the pure-JS building blocks for offline feature-flag initialization in the React Native SDK (FFL-2686, FFL-2687): parsing a portable
ConfigurationWirestring 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.ts—configurationFromString(wire): ParsedFlagsConfigurationandconfigurationToString(config). Parses theConfigurationWirev1 envelope and is lenient: it returns an empty configuration on malformed input or an unsupported version rather than throwing.ParsedFlagsConfigurationis a distinct type (not theenable()FlagsConfiguration) and reserves aserver/rules branch for the future.precomputed.ts—decodePrecomputedFlags(response)→Record<string, FlagCacheEntry>(the shape the SDK already caches and evaluates against). The typedvariationValuebecomes the flagvalueplus a stringified form used by native Android exposure tracking;integer/floatdecode to a JSnumberwhile preserving the originalvariationType; 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
tscare clean.