Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
1e9c7c3
refactor: Update mockgen to write to mocks directory & package
jblacker May 7, 2025
6b6ba4c
chore: generate new mocks
jblacker May 7, 2025
5195e80
fix: Update test imports to use ubermock & newly generated mocks
jblacker May 7, 2025
db9cc14
chore: go mod tidy
jblacker May 7, 2025
cedd7da
doc: Add information about using exported mocks
jblacker May 7, 2025
8904308
build: Update ci to include testtool tag for running tests
jblacker May 7, 2025
26eff7b
build: Update makefile to include build-tags for linting
jblacker May 7, 2025
cf0c504
fix: downgrade ubermock to prevent bumping go version
jblacker May 7, 2025
c1e6b5c
fix: Add codecov.yml to prevent mocks from being included in coverage…
jblacker May 7, 2025
1add33d
feat: Add error types
jblacker May 10, 2025
af389cd
feat: add HookIsolator helper types
jblacker May 10, 2025
8319018
feat: add common components for strategies
jblacker May 10, 2025
b1bfc53
chore: go mod tidy
jblacker May 10, 2025
8ec1e9e
feat: Implement strategies
jblacker May 10, 2025
a952ad5
feat: generate Strategy mock
jblacker May 10, 2025
4560a8f
feat: Implement MultiProvider API
jblacker May 15, 2025
b638653
Merge branch 'main' into implement-multiprovider
jblacker May 19, 2025
d4463cc
Merge branch 'main' into implement-multiprovider
jblacker Jun 4, 2025
ea29d0f
chore: go mod tidy
jblacker Jun 4, 2025
f0dfc05
fix: Implement DiscardHandler for slog
jblacker Jun 4, 2025
e401753
chore: lint everything
jblacker Jun 4, 2025
9b230c0
refactor: Made discard handler private
jblacker Jun 4, 2025
f25b8ca
fix: eliminate potential race conditions per go test -race flag
jblacker Jun 4, 2025
b176b03
Revert "chore: go mod tidy"
jblacker Jun 4, 2025
926c238
fix: re-commit go mod tidy changes without submodule update
jblacker Jun 4, 2025
968ae59
fix: Add additional tests for coverage purposes
jblacker Jun 5, 2025
89b639e
build: Update ignore list for code coverage
jblacker Jun 5, 2025
af6c660
refactor: remove stdout from test
jblacker Jun 5, 2025
a2daf60
build: Update codecov ignore
jblacker Jun 5, 2025
ea18a60
refactor: Use any instead of interface{}
jblacker Jun 5, 2025
d3c8873
refactor: embed UnimplementedHook in HookIsolator
jblacker Jun 5, 2025
4d396d3
Merge branch 'main' into implement-multiprovider
jblacker Jun 6, 2025
bfabc8f
refactor: code cleanup
jblacker Jun 6, 2025
95debf7
cleanup: Keep all strategy enum value consts in strategy.go
jblacker Jun 6, 2025
091f53b
refactor: Prevent invalid state from being possible in ComparisonStra…
jblacker Jun 6, 2025
e94d9d0
docs: Improve documentation
jblacker Jun 6, 2025
efd16e7
docs: Extend strategy information on factory methods
jblacker Jun 6, 2025
e129afd
Merge branch 'implement-multiprovider' of github.com:jblacker/go-sdk …
jblacker Jun 6, 2025
e321603
fix: Remove accidentally committed code
jblacker Jun 6, 2025
ae001f0
refactor: explore another multiprovider implementation
erka Jun 8, 2025
b97baca
Merge branch 'main' into rd/implement-multiprovider
erka Jun 12, 2025
ee6b916
cleanup
erka Jun 12, 2025
5fe2e61
rework first success strategy
erka Jun 15, 2025
01d9ba8
Merge pull request #1 from erka/rd/implement-multiprovider
jblacker Jun 17, 2025
6616e96
refactor: convert multiprovider tests to table-driven style
erka Jun 17, 2025
925348a
Merge branch 'main' into rd/refactor-multiprovider
erka Jul 8, 2025
6f489b7
keep up with the changes on main
erka Jul 8, 2025
4bd4f61
Merge pull request #2 from erka/rd/refactor-multiprovider
jblacker Jul 16, 2025
10ac30a
Merge branch 'main' into implement-multiprovider
jblacker Jul 16, 2025
0298c69
Merge branch 'main' into implement-multiprovider
jblacker Aug 20, 2025
65aca10
fix: Add buffer to initialization channel
jblacker Aug 20, 2025
6773d1b
chore: require go 1.24
jblacker Aug 20, 2025
8a95d7b
chore: remove unused import alias
jblacker Aug 20, 2025
aea7d72
fix: Remove deprecated events during init
jblacker Aug 20, 2025
48c93f5
fix: Correct state evaluation mappings
jblacker Aug 20, 2025
da20339
fix: Remove unnecessary goroutine triggered within an errgroup
jblacker Aug 20, 2025
ba085a3
fix: avoid potential panic if missing targetingKey
jblacker Aug 21, 2025
8bfe632
fix: Use index to avoid duplicate pointers from slice iteration
jblacker Aug 21, 2025
9af28e0
fix: avoid potential panic if missing targetingKey
jblacker Aug 21, 2025
8ff1856
fix: Guarantee map exists before writing flag metadata
jblacker Aug 21, 2025
2bf62dc
Merge branch 'implement-multiprovider' of github.com:jblacker/go-sdk …
jblacker Aug 21, 2025
65a41ac
fix: use labeled break instaed of goto
jblacker Aug 21, 2025
447502f
refactor: Remove discardHandler and use built-in
jblacker Aug 21, 2025
a13b816
refactor: Leverage targeting key constant
jblacker Aug 21, 2025
747d429
fix: Correct docs & typos
jblacker Aug 21, 2025
a741f6a
refactor: simplify aggregate error string generation
jblacker Aug 21, 2025
499393a
refactor: Add additional constant to avoid concatenation everywhere
jblacker Aug 21, 2025
c49c65f
Merge branch 'main' into implement-multiprovider
jblacker Sep 4, 2025
76cf8b7
chore: go mod tidy
jblacker Sep 4, 2025
7ffa4b0
Merge branch 'main' into implement-multiprovider
jblacker Sep 7, 2025
2d38d30
doc: Cleanup NewComparisonStrategy docs
jblacker Sep 7, 2025
f20f8bd
doc: clean up docs in multiprovider top level
jblacker Sep 7, 2025
0a3d47f
refactor: Reorganize constants & update more docs
jblacker Sep 7, 2025
ba1a6ce
doc: Update strategy docs
jblacker Sep 7, 2025
dc1a7f0
fix: Add missing WithCustomComparator Option
jblacker Sep 7, 2025
ed6f308
docs: Update documentation formatting throughout
jblacker Sep 7, 2025
e614107
refactor: Don't export configuration object
jblacker Sep 7, 2025
f722ff3
Update openfeature/multiprovider/strategies.go
jblacker Sep 7, 2025
92458fa
chore: Cleanup leftover discard_handler.go from code coverage
jblacker Sep 27, 2025
4c20284
doc: Comment purpose of interface compliance checks
jblacker Sep 27, 2025
b712e05
fix: Prevent potential panic in defaultComparator
jblacker Sep 27, 2025
63666b3
Merge branch 'main' into implement-multiprovider
jblacker Sep 27, 2025
d089952
chore: go mod tidy
jblacker Sep 27, 2025
c79ed11
dep: bump testify
jblacker Sep 27, 2025
1c23c56
refactor: Add length to provider status map creation
jblacker Sep 29, 2025
59de558
chore: Remove unused constant: MetadataInternalError
jblacker Sep 29, 2025
0e25528
refactor: Update all log messages to be static
jblacker Sep 29, 2025
2bbbbe7
refactor: Remove unused WithTimeout option
jblacker Sep 29, 2025
966b71b
refactor: Rename package multi and type Provider
jblacker Sep 29, 2025
08674d5
doc: Update package level documentation
jblacker Sep 29, 2025
5e5d690
doc: Update documentation for BuildDefaultResult method
jblacker Sep 29, 2025
017e0e4
refactor: Unexport hookIsolator & eventHandlingHookIsolator
jblacker Sep 29, 2025
adb9ccd
refactor: Unexport strategy constructors
jblacker Sep 29, 2025
b000891
doc: Add definitions to multiprovider Reasons
jblacker Sep 29, 2025
623d1d9
fix: remove invalid format strings from log message
jblacker Oct 1, 2025
3af6283
fix: return global hooks for exported Hooks function
jblacker Oct 1, 2025
72c5c6f
fix: export error rather than text of the error as a "sentinel" var
jblacker Oct 1, 2025
e7f3594
refactor: Export Evaluate for implementers of custom StrategyFn
jblacker Oct 1, 2025
9c4a15f
doc: Improve documentation throughout
jblacker Oct 1, 2025
4bb3a7c
refactor: Remove unused timeout field of configuration
jblacker Oct 6, 2025
9b7758f
refactor: Update provider reference on methods
jblacker Oct 6, 2025
fc42591
doc: clean up typo
jblacker Oct 6, 2025
b1a376e
doc: improve doc in comparison strategy
jblacker Oct 6, 2025
1d8374d
refactor: Rename internal provider fields
jblacker Oct 6, 2025
1ff145f
doc: Update documentation of Comparator
jblacker Oct 6, 2025
d1d7df2
chore: Add missing link to ErrAggregationNotAllowed doc
jblacker Oct 6, 2025
ced1a90
fix: Correct custom strategy implementation
jblacker Oct 7, 2025
01e1192
doc: fix incorrect comment
jblacker Oct 7, 2025
1c30cf6
doc: Add multiprovider README & update main README
jblacker Oct 7, 2025
ecffe10
doc: Update multi package comment to note it is experimental
jblacker Oct 7, 2025
500bcb1
doc: Fix formatting of multiprovider README
jblacker Oct 7, 2025
6dab42f
Merge branch 'main' into implement-multiprovider
jblacker Oct 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ openfeature.SetProviderAndWait(MyProvider{})
```

In some situations, it may be beneficial to register multiple providers in the same application.
This is possible using [domains](#domains), which is covered in more details below.
This is possible using [domains](#domains), which is covered in more details below, or the included [multiprovider](#multi-provider-implementation)
implementation.

### Targeting

Expand Down Expand Up @@ -331,6 +332,11 @@ tCtx := openfeature.MergeTransactionContext(ctx, openfeature.EvaluationContext{}
client.BooleanValue(tCtx, ....)
```

### Multi-Provider Implementation

Included with this SDK is an _experimental_ multi-provider that can be used to query multiple feature flag providers simultaneously.
More information can be found in the [multi package's README](openfeature/multi/README.md).

## Extending

### Develop a provider
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ignore:
- "**/*_mock.go"
- "**/*_mock.go"
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@ go 1.24.0
require (
github.com/cucumber/godog v0.15.1
github.com/go-logr/logr v1.4.3
github.com/stretchr/testify v1.11.1
go.uber.org/mock v0.6.0
golang.org/x/sync v0.17.0
golang.org/x/text v0.30.0
)

require (
github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.4 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
18 changes: 7 additions & 11 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI=
github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0=
github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo=
github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces=
github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI=
github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8=
github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI=
Expand Down Expand Up @@ -30,14 +28,15 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
Expand All @@ -48,21 +47,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
165 changes: 165 additions & 0 deletions openfeature/multi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
OpenFeature Multi-Provider
------------

> [!WARNING]
> The multi package for the go-sdk is experimental.


The multi-provider allows you to use multiple underlying providers as sources of flag data for the OpenFeature server SDK.
The multi-provider acts as a wrapper providing a unified interface to interact with all of those providers at once.
When a flag is being evaluated, the Multi-Provider will consult each underlying provider it is managing in order to
determine the final result. Different evaluation strategies can be defined to control which providers get evaluated and
which result is used.

The multi-provider is defined within [Appendix A: Included Utilities](https://openfeature.dev/specification/appendix-a#multi-provider)
of the openfeature spec.

The multi-provider is a powerful tool for performing migrations between flag providers, or combining multiple providers
into a single feature flagging interface. For example:

- **Migration**: When migrating between two providers, you can run both in parallel under a unified flagging interface.
As flags are added to the new provider, the multi-provider will automatically find and return them, falling back to the old provider
if the new provider does not have
- **Multiple Data Sources**: The multi-provider allows you to seamlessly combine many sources of flagging data, such as
environment variables, local files, database values and SaaS hosted feature management systems.

# Usage

```go
import (
"github.com/open-feature/go-sdk/openfeature"
"github.com/open-feature/go-sdk/openfeature/multi"
"github.com/open-feature/go-sdk/openfeature/memprovider"
)

providers := make(multi.ProviderMap)
providers["providerA"] = memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{})
providers["providerB"] = myCustomProvider
mprovider, err := multi.NewProvider(providers, multi.StrategyFirstMatch)
if err != nil {
return err
}

openfeature.SetNamedProviderAndWait("multiprovider", mprovider)
```

# Strategies

There are three strategies that are defined by the spec and are available within this multi-provider implementation. In
addition to the three provided strategies a custom strategy can be defined as well.

The three provided strategies are:

- _First Match_
- _First Success_
- _Comparison_

## First Match Strategy

The first match strategy works by **sequentially** calling each provider until a valid result is returned.
The first provider that returns a result will be used. It will try calling the next provider whenever it encounters a `FLAG_NOT_FOUND`
error. However, if a provider returns an error other than `FLAG_NOT_FOUND` the provider will stop and return the default
value along with setting the error details if a detailed request is issued.

## First Success Strategy

The first success strategy also works by calling each provider **sequentially**. The first provider that returns a response
with no errors is used. This differs from the first match strategy in that any provider raising an error will not halt
calling the next provider if a successful result has not yet been encountered. If no provider provides a successful result
the default value will be returned to the caller.

## Comparison Strategy

The comparison strategy works by calling each provider in **parallel**. All results are collected from each provider and
then the resolved results are compared to each other. If they all agree then that value is returned. If not a fallback
provider can be specified to be executed instead or the default value will be returned. If a provider returns
`FLAG_NOT_FOUND` that result will not be included in the comparison. If all providers return not found then the default
value is returned. Finally, if any provider returns an error other than `FLAG_NOT_FOUND` the evaluation immediately stops
and that error result is returned with the default value.

The fallback provider can be set using the `WithFallbackProvider` [`Option`](#options).

Special care must be taken when this strategy is used with `ObjectEvaluation`. If the resulting value is not a
[`comparable`](https://go.dev/blog/comparable) type then the default result or fallback provider will always be used. In
order to evaluate non `comparable` types a `Comparator` function must be provided as an `Option` to the constructor.

## Custom Strategies

A custom strategy can be defined using the `WithCustomStrategy` `Option` along with the `StrategyCustom` constant.
A custom strategy is defined by the following generic function signature:

```go
StrategyFn[T FlagTypes] func(ctx context.Context, flag string, defaultValue T, flatCtx openfeature.FlattenedContext) openfeature.GenericResolutionDetail[T]
```

However, this doesn't provide any way to retrieve the providers! Therefore, there's the type `StrategyConstructor` that
is called for you to close over the providers inside your `StratetegyFn` implementation.

```go
type StrategyConstructor func(providers []*NamedProvider) StrategyFn[FlagTypes]
```

Build your strategy to wrap around the slice of providers
```go
option := multi.WithCustomStrategy(func(providers []*NamedProvider) StrategyFn[FlagTypes] {
return func[T FlagTypes](ctx context.Context, flag string, defaultValue T, flatCtx openfeature.FlattenedContext) openfeature.GenericResolutionDetail[T] {
// implementation
// ...
}
})
```

It is highly recommended to use the provided exposed functions to build your custom strategy. Specifically, the functions
`BuildDefaultResult` & `Evaluate` are exposed for those implementing their own custom strategies.

The `Evaluate` method should be used for evaluating the result of a single `NamedProvider`. It determines the evaluation
type via the type of the generic `defaultVal` parameter.

The `BuildDefaultResult` method should be called when an error is encountered or the strategy "fails" and needs to return
the default result passed to one of the Evaluation methods of `openfeature.FeatureProvider`.

# Options

The `multi.NewProvider` constructor implements the optional pattern for setting additional configuration.

## General Options

### `WithLogger`

Allows for providing a `slog.Logger` instance for internal logging of the multi-provider's evaluation processing for debugging
purposes. By default, are logs are discarded unless this option is used.

### `WithCustomStrategy`

Allows for setting a custom strategy function for the evaluation of providers. This must be used in conjunction with the
`StrategyCustom` `EvaluationStrategy` parameter. The option itself takes a `StrategyConstructor` function, which is
essentially a factory that allows the `StrategyFn` to wrap around a slice of `NamedProvider` instances.

### `WithGlobalHooks`

Allows for setting global hooks for the multi-provider. These are `openfeature.Hook` implementations that affect
**all** internal `FeatureProvider` instances.

### `WithProviderHooks`

Allows for setting `openfeature.Hook` implementations on a specific named `FeatureProvider` within the multi-provider.
This should only be used when hooks need to be attached to a `FeatureProvider` instance that does not implement that functionality.
Using a provider name that is not known will cause an error to be returned during the creation time. This option can be
used multiple times using unique provider names.

## `StrategyComparision` specific options

There are two options specifically for usage with the `StrategyComparision` `EvaluationStrategy`. If these options are
used with a different `EvaluationStrategy` they are ignored.

### `WithFallbackProvider`

When the results are not in agreement with each other the fallback provider will be called. The result of this provider
is what will be returned to the caller. If no fallback provider is set then the default value will be returned instead.

### `WithCustomComparator`

When using `ObjectEvaluation` there are cases where the results are not able to be compared to each other by default.
This happens if the returned type is not `comparable`. In that situation all the results are passed to the custom `Comparator`
to evaluate if they are in agreement or not. If not provided and the return type is not `comparable` then either the fallback
provider is used or the default value.
Loading