Skip to content

Conversation

nicklasl
Copy link
Member

@nicklasl nicklasl commented Oct 10, 2025

Summary

Adds sticky assignment support to the JavaScript OpenFeature provider using remote resolver fallback.

How it works

  • Local WASM resolver attempts to resolve flags first
  • If sticky assignment data is needed, falls back to Confidence cloud resolvers
  • Materializations stored on Confidence servers with 90-day TTL (auto-renewed)
  • Zero configuration required - works out of the box

Key changes

  • Implemented ResolveWithStickyRequest support in provider
  • Added remote resolver fallback for missing materializations
  • Updated README with sticky assignments documentation
  • Added comprehensive tests for sticky resolve behavior

Future work

Custom MaterializationRepository support is planned for a future release to allow users to provide their own storage backend (Redis, database, file system, etc.). The interface and utilities are already in place but marked as WIP.

🤖 Generated with Claude Code

nicklasl and others added 5 commits October 10, 2025 13:45
Implements sticky resolve functionality to ensure consistent variant
assignments even when user context changes or new assignments are paused.

Key changes:
- Add proto definitions for ResolveWithStickyRequest/Response
- Add StickyResolveStrategy interface with MaterializationRepository and
  ResolverFallback implementations
- Refactor evaluate() to always use sticky resolve via
  resolveWithStickyInternal()
- Add materialization utility functions for loading/storing assignments
- Make evaluate() async to support async storage operations
- Add comprehensive test coverage (25 tests)

The implementation matches the Java SDK pattern, with fire-and-forget
storage updates and recursive retry logic for missing materializations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements RemoteResolverFallback which delegates to the remote Confidence
API when materializations are missing. This is now the default strategy,
simplifying setup for users while maintaining extensibility.

Changes:
- Add RemoteResolverFallback class with remote API resolution
- Default stickyResolveStrategy to RemoteResolverFallback
- Remove non-null assertions for stickyResolveStrategy
- Export RemoteResolverFallback in public API
- Add comprehensive tests (16 tests, all passing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
nicklasl and others added 8 commits October 14, 2025 18:46
…ation

Refactor FileBackedMaterializationRepo to use InMemoryMaterializationRepo as backing store, reading from file only on init and writing only on close.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Remove resolveFlags method from LocalResolver interface and WasmResolver implementation. Update all tests to use resolveWithSticky instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Remove ResolverFallback interface from exports as users should use the concrete RemoteResolverFallback implementation instead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Remove ResolveFlagsRequest and ResolveFlagsResponse type exports as they are only used internally by ResolverFallback which is no longer public.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Change `stickyResolveStrategy` to `materializationRepository` parameter accepting either MaterializationRepository or RemoteResolverFallback. Remove StickyResolveStrategy from public exports.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Simplify API by removing intermediate interfaces:
- Remove StickyResolveStrategy base interface
- Remove ResolverFallback interface and type guards
- Make MaterializationRepository standalone with close() method
- Rename StickyResolveStrategy.ts to MaterializationRepository.ts
- Update ConfidenceServerProviderLocal to use optional MaterializationRepository
- RemoteResolverFallback is now created internally when no repository provided

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@nicklasl nicklasl marked this pull request as ready for review October 16, 2025 09:18
* @returns Map of materialization ID to MaterializationInfo for this unit
*/
loadMaterializedAssignmentsForUnit(
unit: string,
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be possible to load for multiple units?

* @param assignments - Map of materialization ID to MaterializationInfo
*/
storeAssignment(
unit: string,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same, should be possible to store multiple units?

* }
* ```
*/
export interface MaterializationRepository {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should think a bit about this interface. Is it only us who should implement it? Do we need it to specify the full model of data? Could it for instance be a simple KV store with bytebuffer values? Probably not, since we actually update the values rather than overwriting them. It could be a flattened key space though like type Record = [key:[unit:string, materialization:string, rule:string], variant:string]. Don't feel confident we should use the PB generated types in the interface anyhow.

Copy link
Contributor

Choose a reason for hiding this comment

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

In general, do we expect the implementation to be super low latency and we can afford to load materializations one by one, or does it need to support loading in bulk?

Copy link
Contributor

Choose a reason for hiding this comment

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

Here is an API proposal:

type Variant = string;
type MaterializationInfo = {
  [unit:string]: {
    [materialization:string]: {
      [rule:string]: Variant
    }
  }
}
type RuleList = string[]
type MaterializationQuery = {
  [unit:string]: {
    [materialization:string]: RuleList
  }
}

interface MaterializationRepository {

  loadMaterializations(query:MaterializationQuery):Promise<MaterializationInfo>
  storeMaterializations(materializations:MaterializationInfo):Promise<void>
}

nicklasl and others added 2 commits October 16, 2025 16:47
Remove MaterializationRepository from public API for now:
- Removed materializationRepository option from ProviderOptions
- Updated provider to always use remote resolver fallback for sticky assignments
- Materializations stored on Confidence servers with 90-day TTL
- Marked MaterializationRepository interface and utilities as WIP

Documentation updates:
- Updated README to explain remote fallback approach
- Added note about upcoming custom storage feature
- Marked MAT_REPO_EXAMPLES.md as work in progress
- Updated tests to reflect remote fallback behavior

The MaterializationRepository feature will be added in a future release
to allow users to connect their own storage (Redis, database, etc.).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@nicklasl nicklasl changed the title feat: JS sticky resolve support with materialized assignments feat(js): add sticky assignment support with remote resolver fallback Oct 17, 2025
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