-
Notifications
You must be signed in to change notification settings - Fork 2
Offline Init Part 1 of 4: Add getFlagsConfiguration() to export flag configuration as JSON #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Offline Init Part 1 of 4: Add getFlagsConfiguration() to export flag configuration as JSON #116
Conversation
This function returns the current flags configuration as a JSON string that can be used to bootstrap another SDK instance. It includes flags, format, createdAt, environment, and banditReferences when available. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove jest.resetModules() from read-only file system tests (not needed) - Add td.reset() afterEach in initialization errors tests to clean up mocks - Move getFlagsConfiguration tests inside main EppoClient E2E test block - Consolidate getFlagsConfiguration tests into single test for better isolation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use afterAll with null check for more robust client cleanup. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Verify exact metadata (format, createdAt, environment) - Check exact number of flags (22) from flags-v1.json - Thoroughly test new-user-onboarding flag structure including: - All 6 variations - All 4 allocations with their rules and conditions - Various operators (MATCHES, NOT_ONE_OF, ONE_OF) - Extra logging configuration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Explain why configuration stores are kept at module level (EppoClient doesn't expose getters for store metadata or bandit configurations) - Change "Returns" to "Reconstructs" in getFlagsConfiguration JSDoc Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| // We keep references to the configuration stores at module level because EppoClient | ||
| // does not expose public getters for store metadata (format, createdAt, environment) | ||
| // or bandit configurations. These references are needed by getFlagsConfiguration() | ||
| // and getBanditsConfiguration() to reconstruct exportable configuration JSON. | ||
| let flagConfigurationStore: MemoryOnlyConfigurationStore<Flag>; | ||
| let banditVariationConfigurationStore: MemoryOnlyConfigurationStore<BanditVariation[]>; | ||
| let banditModelConfigurationStore: MemoryOnlyConfigurationStore<BanditParameters>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying not to mess with the core SDK for now, but future us can expose these which would tidy this up a bit.
There was a problem hiding this comment.
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 a getFlagsConfiguration() function to export the current flags configuration as JSON, enabling offline initialization of SDK instances in future PRs.
Changes:
- Adds
getFlagsConfiguration()public API that returns JSON representation of current flag configuration - Stores configuration stores at module level to enable access to metadata and bandit configurations
- Includes comprehensive tests validating the exported JSON structure matches the expected format
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/index.ts | Adds module-level configuration store references, implements getFlagsConfiguration() and helper function to reconstruct bandit references |
| src/index.spec.ts | Adds test suite validating getFlagsConfiguration() returns correct JSON structure with flags metadata |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| afterEach(() => { | ||
| td.reset(); | ||
| }); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The afterEach block calls td.reset() but the corresponding test 'retries initial configuration request before resolving' uses td.replace() on HttpClient.prototype. This cleanup should be in the existing test's cleanup (or a dedicated beforeEach/afterEach within that specific test), not added at the describe block level where it affects all tests in the 'Configuration Request' suite.
| // Save original implementation | ||
| let isReadOnlyFsSpy: SpyInstance; | ||
|
|
||
| beforeEach(() => { |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removed comment 'Reset the module before each test' and the removed jest.resetModules() call may have been important for test isolation. If module-level state (like the new flagConfigurationStore variables) persists between tests, this could cause test interdependencies. Verify that removing this reset doesn't cause tests to fail when run in different orders.
| beforeEach(() => { | |
| beforeEach(() => { | |
| // Reset the module before each test to avoid shared module-level state between tests | |
| jest.resetModules(); |
| if (createdAt) { | ||
| configuration.createdAt = createdAt; | ||
| } | ||
| if (format) { | ||
| configuration.format = format; | ||
| } | ||
| if (environment) { | ||
| configuration.environment = environment; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If some of these values were null or undefined, it would be an invalid configuration. It might be better to return defaults instead. This would also allow you to reuse the IUniversalFlagConfigResponse type from common.
So you could do something like this:
const config: IUniversalFlagConfigResponse = {
createdAt: createdAt ?? new Date().toISOString(),
format: format ?? FormatEnum.SERVER,
environment: environment ?? { name: 'UNKNOWN' },
flags,
banditReferences: reconstructBanditReferences() ?? {},
};
return JSON.stringify(config);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also related to this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not blocking, but I'd fall back to default values to prevent an invalid configuration object, and I'd also reuse the existing configuration type. See comment for more info.
Summary
getFlagsConfiguration()function to export the current flags configuration as JSONStacked PRs
getFlagsConfiguration()(this PR)getBanditsConfiguration()offlineInit()Test plan
getFlagsConfiguration()returns null before initialization🤖 Generated with Claude Code