openfeature-sdk 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +25 -2
- data/lib/open_feature/sdk/api.rb +6 -4
- data/lib/open_feature/sdk/client.rb +12 -11
- data/lib/open_feature/sdk/client_metadata.rb +5 -0
- data/lib/open_feature/sdk/configuration.rb +3 -2
- data/lib/open_feature/sdk/isolated.rb +15 -0
- data/lib/open_feature/sdk/provider/event_emitter.rb +9 -4
- data/lib/open_feature/sdk/provider/no_op_provider.rb +1 -1
- data/lib/open_feature/sdk/provider_initialization_error.rb +2 -2
- data/lib/open_feature/sdk/provider_state_registry.rb +7 -7
- data/lib/open_feature/sdk/thread_local_transaction_context_propagator.rb +7 -2
- data/lib/open_feature/sdk/version.rb +1 -1
- data/sig/open_feature/sdk/api.rbs +5 -2
- data/sig/open_feature/sdk/client.rbs +1 -1
- data/sig/open_feature/sdk/isolated.rbs +7 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf1041c34e962f832b25f1be3e9eeab7fee3fd4272dc9e1e0e8dabe85b6d21e8
|
|
4
|
+
data.tar.gz: f45667a6a898d3521ea87c31aea62491ace0682c72ef2cfa2a16d5f8a0f3f0b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 393adb8b637763e5b397f6ee702017705bf355c8785ceb1280aa4f95b585a061f8da6e4a6d67ed9e8ab20de1f341b36d4dc3febb38dbbcc7a41a68cfb8002b8c
|
|
7
|
+
data.tar.gz: d145cc98fb609aa75ad06485d4486cc6aec19ea953cd627f9f71b33e991fafd3a57fa139fbe07f878e5bc105885ce35a74c0ab12485e50649d63b79a100eec98
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1](https://github.com/open-feature/ruby-sdk/compare/v0.7.0...v0.7.1) (2026-10-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* isolated API instances ([#354](https://github.com/open-feature/ruby-sdk/issues/354)) ([1c92b4a](https://github.com/open-feature/ruby-sdk/commit/1c92b4ad7550a6b345e7471f84cd5a7ff445591e))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* key provider state by identity, default the NoOp reason, freeze client metadata ([#355](https://github.com/open-feature/ruby-sdk/issues/355)) ([d15bd84](https://github.com/open-feature/ruby-sdk/commit/d15bd84e1456b6200e29c12c3463c4f8ddfe693f))
|
|
14
|
+
|
|
3
15
|
## [0.7.0](https://github.com/open-feature/ruby-sdk/compare/v0.6.7...v0.7.0) (2026-10-23)
|
|
4
16
|
|
|
5
17
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
</a>
|
|
18
18
|
<!-- x-release-please-start-version -->
|
|
19
19
|
|
|
20
|
-
<a href="https://github.com/open-feature/ruby-sdk/releases/tag/v0.7.
|
|
21
|
-
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.7.
|
|
20
|
+
<a href="https://github.com/open-feature/ruby-sdk/releases/tag/v0.7.1">
|
|
21
|
+
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.7.1&color=blue&style=for-the-badge" />
|
|
22
22
|
</a>
|
|
23
23
|
|
|
24
24
|
<!-- x-release-please-end -->
|
|
@@ -443,6 +443,29 @@ class MyRequestScopedPropagator
|
|
|
443
443
|
end
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
+
### Isolated API instances
|
|
447
|
+
|
|
448
|
+
`OpenFeature::SDK` is a global singleton. Some applications need more than one independent API in a process, for example separately developed modules that each need their own provider, or tests that run in parallel. Require the factory explicitly to create one:
|
|
449
|
+
|
|
450
|
+
```ruby
|
|
451
|
+
require "open_feature/sdk/isolated"
|
|
452
|
+
|
|
453
|
+
api = OpenFeature::SDK::Isolated.create_api
|
|
454
|
+
api.set_provider_and_wait(MyProvider.new)
|
|
455
|
+
client = api.build_client
|
|
456
|
+
client.fetch_boolean_value(flag_key: "my-flag", default_value: false)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Each instance has its own providers, hooks, evaluation context, event handlers, transaction context propagator and `shutdown`, and shares none of them with `OpenFeature::SDK` or other instances.
|
|
460
|
+
Build its clients with `api.build_client`; `OpenFeature::SDK::Client.new` binds to the global API.
|
|
461
|
+
Prefer the global API unless you need this.
|
|
462
|
+
|
|
463
|
+
A provider instance should be registered with only one API at a time.
|
|
464
|
+
Registering a provider that includes `OpenFeature::SDK::Provider::EventEmitter` with a second API raises `ArgumentError`; other providers can't be detected.
|
|
465
|
+
Once the first API replaces the provider or shuts down, it can be registered with another.
|
|
466
|
+
|
|
467
|
+
A `LoggingHook` created without `logger:` logs to the global API's logger, even when added to an isolated API. Pass `logger:` to use another.
|
|
468
|
+
|
|
446
469
|
## Extending
|
|
447
470
|
|
|
448
471
|
### Develop a provider
|
data/lib/open_feature/sdk/api.rb
CHANGED
|
@@ -32,13 +32,15 @@ module OpenFeature
|
|
|
32
32
|
#
|
|
33
33
|
# client = OpenFeature::SDK::API.instance.build_client(name: 'my-open-feature-client')
|
|
34
34
|
class API
|
|
35
|
-
include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1
|
|
35
|
+
include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1; Isolated.create_api builds others (1.8.1)
|
|
36
36
|
extend Forwardable
|
|
37
37
|
|
|
38
38
|
def_delegators :configuration, :provider, :set_provider, :set_provider_and_wait, :hooks, :add_hooks, :evaluation_context
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
attr_reader :configuration
|
|
41
|
+
|
|
42
|
+
def initialize
|
|
43
|
+
@configuration = Configuration.new
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def configure(&block)
|
|
@@ -48,7 +50,7 @@ module OpenFeature
|
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def build_client(domain: nil, evaluation_context: nil)
|
|
51
|
-
Client.new(domain:, evaluation_context:)
|
|
53
|
+
Client.new(domain:, evaluation_context:, configuration:)
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def provider_metadata(domain: nil)
|
|
@@ -21,8 +21,9 @@ module OpenFeature
|
|
|
21
21
|
|
|
22
22
|
attr_accessor :hooks, :evaluation_context
|
|
23
23
|
|
|
24
|
-
def initialize(provider: nil, domain: nil, evaluation_context: nil)
|
|
24
|
+
def initialize(provider: nil, domain: nil, evaluation_context: nil, configuration: API.instance.configuration)
|
|
25
25
|
@provider = provider
|
|
26
|
+
@configuration = configuration
|
|
26
27
|
@metadata = ClientMetadata.new(domain:)
|
|
27
28
|
@evaluation_context = evaluation_context
|
|
28
29
|
@hooks = []
|
|
@@ -33,17 +34,17 @@ module OpenFeature
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def provider_status
|
|
36
|
-
|
|
37
|
+
@configuration.provider_state(resolve_provider)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def add_handler(event_type, handler = nil, &block)
|
|
40
41
|
actual_handler = handler || block
|
|
41
|
-
|
|
42
|
+
@configuration.add_client_handler(self, event_type, actual_handler)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def remove_handler(event_type, handler = nil, &block)
|
|
45
46
|
actual_handler = handler || block
|
|
46
|
-
|
|
47
|
+
@configuration.remove_client_handler(self, event_type, actual_handler)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
# Tracking API (spec 6.1.1.1) — dynamic-context paradigm
|
|
@@ -81,13 +82,13 @@ module OpenFeature
|
|
|
81
82
|
|
|
82
83
|
# Assemble ordered hooks: API → Client → Invocation → Provider (spec 4.4.2)
|
|
83
84
|
provider_hooks = provider.respond_to?(:hooks) ? Array(provider.hooks) : []
|
|
84
|
-
ordered_hooks = [
|
|
85
|
+
ordered_hooks = [*@configuration.hooks, *@hooks, *invocation_hooks, *provider_hooks]
|
|
85
86
|
|
|
86
87
|
# Short-circuit NOT_READY/FATAL providers (required by evaluation_v2.feature). A provider resolved from the
|
|
87
88
|
# configuration is tracked while bound, so one that isn't was replaced mid-call and reads NOT_READY.
|
|
88
89
|
short_circuit_code = nil
|
|
89
|
-
if @provider ?
|
|
90
|
-
short_circuit_code = short_circuit_error_code(
|
|
90
|
+
if @provider ? @configuration.provider_tracked?(provider) : !provider.equal?(@fallback_provider)
|
|
91
|
+
short_circuit_code = short_circuit_error_code(@configuration.provider_state(provider))
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
# Fast path: skip hook ceremony when no hooks are registered
|
|
@@ -121,7 +122,7 @@ module OpenFeature
|
|
|
121
122
|
EMPTY_HINTS
|
|
122
123
|
end
|
|
123
124
|
|
|
124
|
-
executor = Hooks::HookExecutor.new(logger:
|
|
125
|
+
executor = Hooks::HookExecutor.new(logger: @configuration.logger)
|
|
125
126
|
|
|
126
127
|
if short_circuit_code
|
|
127
128
|
resolution = Provider::ResolutionDetails.new(
|
|
@@ -169,7 +170,7 @@ module OpenFeature
|
|
|
169
170
|
# Callers resolve once per operation, so a rebinding mid-evaluation can't mix two providers.
|
|
170
171
|
# The fallback NoOp is never registered: untracked, so not short-circuited, and its status reads NOT_READY.
|
|
171
172
|
def resolve_provider
|
|
172
|
-
@provider ||
|
|
173
|
+
@provider || @configuration.provider(domain: @metadata.domain) || (@fallback_provider ||= Provider::NoOpProvider.new)
|
|
173
174
|
end
|
|
174
175
|
|
|
175
176
|
def short_circuit_error_code(state)
|
|
@@ -180,11 +181,11 @@ module OpenFeature
|
|
|
180
181
|
end
|
|
181
182
|
|
|
182
183
|
def build_evaluation_context(invocation_context)
|
|
183
|
-
propagator =
|
|
184
|
+
propagator = @configuration.transaction_context_propagator
|
|
184
185
|
transaction_context = propagator&.get_transaction_context
|
|
185
186
|
|
|
186
187
|
EvaluationContextBuilder.new.call(
|
|
187
|
-
api_context:
|
|
188
|
+
api_context: @configuration.evaluation_context,
|
|
188
189
|
transaction_context: transaction_context,
|
|
189
190
|
client_context: evaluation_context,
|
|
190
191
|
invocation_context: invocation_context
|
|
@@ -162,9 +162,10 @@ module OpenFeature
|
|
|
162
162
|
needs_init = attempt.nil? || attempt.state == :failed
|
|
163
163
|
|
|
164
164
|
if needs_init
|
|
165
|
+
# First, so a provider registered with another API (1.8.4) leaves no state behind
|
|
166
|
+
provider.send(:attach, self) if provider.is_a?(Provider::EventEmitter)
|
|
165
167
|
attempt = @init_attempts[provider] = InitAttempt.new(:running, nil, Thread::Queue.new)
|
|
166
168
|
@provider_state_registry.set_initial_state(provider)
|
|
167
|
-
provider.send(:attach, self) if provider.is_a?(Provider::EventEmitter)
|
|
168
169
|
end
|
|
169
170
|
|
|
170
171
|
# Publish last: clients read @providers without the lock, and must not see an untracked provider
|
|
@@ -219,7 +220,7 @@ module OpenFeature
|
|
|
219
220
|
end
|
|
220
221
|
ensure
|
|
221
222
|
# Fail an attempt no init took over (e.g. a Timeout hit first), or it would stay running forever
|
|
222
|
-
finish_init_attempt(attempt, :failed) if needs_init && !init_thread
|
|
223
|
+
finish_init_attempt(attempt, :failed) if needs_init && attempt && !init_thread
|
|
223
224
|
end
|
|
224
225
|
|
|
225
226
|
def init_provider(provider, context, domain:, attempt:, raise_on_error: false)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../sdk"
|
|
4
|
+
|
|
5
|
+
module OpenFeature
|
|
6
|
+
module SDK
|
|
7
|
+
# Spec 1.8.3: a separate require, so isolated instances are an explicit opt-in
|
|
8
|
+
module Isolated
|
|
9
|
+
# Spec 1.8.1: a new API that shares no state with the global one or other isolated APIs
|
|
10
|
+
def self.create_api
|
|
11
|
+
API.send(:new)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -8,7 +8,7 @@ module OpenFeature
|
|
|
8
8
|
# Mixin for providers that emit lifecycle events
|
|
9
9
|
module EventEmitter
|
|
10
10
|
def emit_event(event_type, details = {})
|
|
11
|
-
config = @
|
|
11
|
+
config = @openfeature_configuration
|
|
12
12
|
return unless config
|
|
13
13
|
|
|
14
14
|
unless ::OpenFeature::SDK::ProviderEvent::ALL_EVENTS.include?(event_type)
|
|
@@ -19,17 +19,22 @@ module OpenFeature
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def configuration_attached?
|
|
22
|
-
!@
|
|
22
|
+
!@openfeature_configuration.nil?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
27
|
def attach(configuration)
|
|
28
|
-
|
|
28
|
+
# Spec 1.8.4: only providers using this mixin know which API they're registered with
|
|
29
|
+
if @openfeature_configuration && !@openfeature_configuration.equal?(configuration)
|
|
30
|
+
raise ArgumentError, "Provider is already registered with another OpenFeature API instance"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@openfeature_configuration = configuration
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
def detach
|
|
32
|
-
@
|
|
37
|
+
@openfeature_configuration = nil
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
@@ -21,8 +21,8 @@ module OpenFeature
|
|
|
21
21
|
# @param message [String] the error message
|
|
22
22
|
# @param provider [Object] the provider that failed to initialize
|
|
23
23
|
# @param original_error [Exception] the original error that caused the failure
|
|
24
|
-
# @param error_code [String] the OpenFeature error code (defaults to
|
|
25
|
-
def initialize(message, provider: nil, original_error: nil, error_code: Provider::ErrorCode::
|
|
24
|
+
# @param error_code [String] the OpenFeature error code (defaults to GENERAL)
|
|
25
|
+
def initialize(message, provider: nil, original_error: nil, error_code: Provider::ErrorCode::GENERAL)
|
|
26
26
|
super(message)
|
|
27
27
|
@provider = provider
|
|
28
28
|
@original_error = original_error
|
|
@@ -9,7 +9,7 @@ module OpenFeature
|
|
|
9
9
|
# Tracks provider states
|
|
10
10
|
class ProviderStateRegistry
|
|
11
11
|
def initialize
|
|
12
|
-
@states = {}
|
|
12
|
+
@states = {}.compare_by_identity
|
|
13
13
|
@mutex = Mutex.new
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ module OpenFeature
|
|
|
17
17
|
return unless provider
|
|
18
18
|
|
|
19
19
|
@mutex.synchronize do
|
|
20
|
-
@states[provider
|
|
20
|
+
@states[provider] = {state: state, details: {}}
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ module OpenFeature
|
|
|
29
29
|
# Only update state if the event should cause a state change
|
|
30
30
|
if new_state
|
|
31
31
|
@mutex.synchronize do
|
|
32
|
-
@states[provider
|
|
32
|
+
@states[provider] = {state: new_state, details: event_details || {}}
|
|
33
33
|
end
|
|
34
34
|
new_state
|
|
35
35
|
else
|
|
@@ -42,7 +42,7 @@ module OpenFeature
|
|
|
42
42
|
return ProviderState::NOT_READY unless provider
|
|
43
43
|
|
|
44
44
|
@mutex.synchronize do
|
|
45
|
-
entry = @states[provider
|
|
45
|
+
entry = @states[provider]
|
|
46
46
|
entry ? entry[:state] : ProviderState::NOT_READY
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -51,7 +51,7 @@ module OpenFeature
|
|
|
51
51
|
return {} unless provider
|
|
52
52
|
|
|
53
53
|
@mutex.synchronize do
|
|
54
|
-
entry = @states[provider
|
|
54
|
+
entry = @states[provider]
|
|
55
55
|
entry ? entry[:details] : {}
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -60,7 +60,7 @@ module OpenFeature
|
|
|
60
60
|
return unless provider
|
|
61
61
|
|
|
62
62
|
@mutex.synchronize do
|
|
63
|
-
@states.delete(provider
|
|
63
|
+
@states.delete(provider)
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -68,7 +68,7 @@ module OpenFeature
|
|
|
68
68
|
return false unless provider
|
|
69
69
|
|
|
70
70
|
@mutex.synchronize do
|
|
71
|
-
@states.key?(provider
|
|
71
|
+
@states.key?(provider)
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -5,14 +5,19 @@ module OpenFeature
|
|
|
5
5
|
class ThreadLocalTransactionContextPropagator
|
|
6
6
|
include TransactionContextPropagator
|
|
7
7
|
|
|
8
|
+
# One context per propagator under this key, so separate APIs never share one (spec 1.8.2)
|
|
9
|
+
# while resetting the key still clears them all. Weak keys let discarded propagators be collected.
|
|
8
10
|
THREAD_KEY = :openfeature_transaction_context
|
|
9
11
|
|
|
10
12
|
def set_transaction_context(evaluation_context)
|
|
11
|
-
Thread.current[THREAD_KEY]
|
|
13
|
+
contexts = Thread.current[THREAD_KEY]
|
|
14
|
+
contexts = Thread.current[THREAD_KEY] = ObjectSpace::WeakKeyMap.new unless contexts.is_a?(ObjectSpace::WeakKeyMap)
|
|
15
|
+
evaluation_context.nil? ? contexts.delete(self) : contexts[self] = evaluation_context
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
def get_transaction_context
|
|
15
|
-
Thread.current[THREAD_KEY]
|
|
19
|
+
contexts = Thread.current[THREAD_KEY]
|
|
20
|
+
contexts[self] if contexts.is_a?(ObjectSpace::WeakKeyMap)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
18
23
|
end
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
module OpenFeature
|
|
2
2
|
module SDK
|
|
3
3
|
class API
|
|
4
|
-
include Singleton
|
|
5
4
|
extend Forwardable
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
attr_reader configuration: Configuration
|
|
9
|
+
|
|
10
|
+
def initialize: () -> void
|
|
8
11
|
|
|
9
12
|
def configure: () { (Configuration) -> void } -> void
|
|
10
13
|
|
|
@@ -10,7 +10,7 @@ module OpenFeature
|
|
|
10
10
|
attr_accessor evaluation_context: EvaluationContext?
|
|
11
11
|
attr_accessor hooks: Array[untyped]
|
|
12
12
|
|
|
13
|
-
def initialize: (?provider: untyped, ?domain: String?, ?evaluation_context: EvaluationContext?) -> void
|
|
13
|
+
def initialize: (?provider: untyped, ?domain: String?, ?evaluation_context: EvaluationContext?, ?configuration: Configuration) -> void
|
|
14
14
|
|
|
15
15
|
def add_hooks: (*Array[untyped] new_hooks) -> void
|
|
16
16
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openfeature-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenFeature Authors
|
|
@@ -55,6 +55,7 @@ files:
|
|
|
55
55
|
- lib/open_feature/sdk/hooks/hook_context.rb
|
|
56
56
|
- lib/open_feature/sdk/hooks/hook_executor.rb
|
|
57
57
|
- lib/open_feature/sdk/hooks/logging_hook.rb
|
|
58
|
+
- lib/open_feature/sdk/isolated.rb
|
|
58
59
|
- lib/open_feature/sdk/provider.rb
|
|
59
60
|
- lib/open_feature/sdk/provider/error_code.rb
|
|
60
61
|
- lib/open_feature/sdk/provider/event_emitter.rb
|
|
@@ -92,6 +93,7 @@ files:
|
|
|
92
93
|
- sig/open_feature/sdk/hooks/hook_context.rbs
|
|
93
94
|
- sig/open_feature/sdk/hooks/hook_executor.rbs
|
|
94
95
|
- sig/open_feature/sdk/hooks/logging_hook.rbs
|
|
96
|
+
- sig/open_feature/sdk/isolated.rbs
|
|
95
97
|
- sig/open_feature/sdk/provider.rbs
|
|
96
98
|
- sig/open_feature/sdk/provider/error_code.rbs
|
|
97
99
|
- sig/open_feature/sdk/provider/event_emitter.rbs
|