@@ -25,7 +25,8 @@ import {
2525 MapHookData ,
2626} from '@openfeature/core' ;
2727import type { FlagEvaluationOptions } from '../../evaluation' ;
28- import type { ProviderEvents } from '../../events' ;
28+ import { EvaluationDetailsWithSubscription } from '../../evaluation' ;
29+ import { ProviderEvents } from '../../events' ;
2930import type { InternalEventEmitter } from '../../events/internal/internal-event-emitter' ;
3031import type { Hook } from '../../hooks' ;
3132import type { Provider } from '../../provider' ;
@@ -136,7 +137,13 @@ export class OpenFeatureClient implements Client {
136137 defaultValue : boolean ,
137138 options ?: FlagEvaluationOptions ,
138139 ) : EvaluationDetails < boolean > {
139- return this . evaluate < boolean > ( flagKey , this . _provider . resolveBooleanEvaluation , defaultValue , 'boolean' , options ) ;
140+ return this . evaluateWithSubscription < boolean > (
141+ flagKey ,
142+ this . _provider . resolveBooleanEvaluation ,
143+ defaultValue ,
144+ 'boolean' ,
145+ options ,
146+ ) ;
140147 }
141148
142149 getStringValue < T extends string = string > ( flagKey : string , defaultValue : T , options ?: FlagEvaluationOptions ) : T {
@@ -148,7 +155,7 @@ export class OpenFeatureClient implements Client {
148155 defaultValue : T ,
149156 options ?: FlagEvaluationOptions ,
150157 ) : EvaluationDetails < T > {
151- return this . evaluate < T > (
158+ return this . evaluateWithSubscription < T > (
152159 flagKey ,
153160 // this isolates providers from our restricted string generic argument.
154161 this . _provider . resolveStringEvaluation as ( ) => EvaluationDetails < T > ,
@@ -167,7 +174,7 @@ export class OpenFeatureClient implements Client {
167174 defaultValue : T ,
168175 options ?: FlagEvaluationOptions ,
169176 ) : EvaluationDetails < T > {
170- return this . evaluate < T > (
177+ return this . evaluateWithSubscription < T > (
171178 flagKey ,
172179 // this isolates providers from our restricted number generic argument.
173180 this . _provider . resolveNumberEvaluation as ( ) => EvaluationDetails < T > ,
@@ -190,7 +197,107 @@ export class OpenFeatureClient implements Client {
190197 defaultValue : T ,
191198 options ?: FlagEvaluationOptions ,
192199 ) : EvaluationDetails < T > {
193- return this . evaluate < T > ( flagKey , this . _provider . resolveObjectEvaluation , defaultValue , 'object' , options ) ;
200+ return this . evaluateWithSubscription < T > (
201+ flagKey ,
202+ this . _provider . resolveObjectEvaluation ,
203+ defaultValue ,
204+ 'object' ,
205+ options ,
206+ ) ;
207+ }
208+
209+ onBooleanContextChanged (
210+ flagKey : string ,
211+ defaultValue : boolean ,
212+ callback : ( newDetails : EvaluationDetails < boolean > , oldDetails : EvaluationDetails < boolean > ) => void ,
213+ options ?: FlagEvaluationOptions ,
214+ ) : ( ) => void {
215+ return this . subscribeToContextChanges ( flagKey , defaultValue , 'boolean' , callback , options ) ;
216+ }
217+
218+ onStringContextChanged (
219+ flagKey : string ,
220+ defaultValue : string ,
221+ callback : ( newDetails : EvaluationDetails < string > , oldDetails : EvaluationDetails < string > ) => void ,
222+ options ?: FlagEvaluationOptions ,
223+ ) : ( ) => void {
224+ return this . subscribeToContextChanges ( flagKey , defaultValue , 'string' , callback , options ) ;
225+ }
226+
227+ onNumberContextChanged (
228+ flagKey : string ,
229+ defaultValue : number ,
230+ callback : ( newDetails : EvaluationDetails < number > , oldDetails : EvaluationDetails < number > ) => void ,
231+ options ?: FlagEvaluationOptions ,
232+ ) : ( ) => void {
233+ return this . subscribeToContextChanges ( flagKey , defaultValue , 'number' , callback , options ) ;
234+ }
235+
236+ onObjectContextChanged < T extends JsonValue = JsonValue > (
237+ flagKey : string ,
238+ defaultValue : T ,
239+ callback : ( newDetails : EvaluationDetails < T > , oldDetails : EvaluationDetails < T > ) => void ,
240+ options ?: FlagEvaluationOptions ,
241+ ) : ( ) => void {
242+ return this . subscribeToContextChanges ( flagKey , defaultValue , 'object' , callback , options ) ;
243+ }
244+
245+ private subscribeToContextChanges < T extends FlagValue > (
246+ flagKey : string ,
247+ defaultValue : T ,
248+ flagType : FlagValueType ,
249+ callback : ( newDetails : EvaluationDetails < T > , oldDetails : EvaluationDetails < T > ) => void ,
250+ options ?: FlagEvaluationOptions ,
251+ ) : ( ) => void {
252+ let currentDetails : EvaluationDetails < T > ;
253+
254+ switch ( flagType ) {
255+ case 'boolean' :
256+ currentDetails = this . getBooleanDetails ( flagKey , defaultValue as boolean , options ) as EvaluationDetails < T > ;
257+ break ;
258+ case 'string' :
259+ currentDetails = this . getStringDetails ( flagKey , defaultValue as string , options ) as EvaluationDetails < T > ;
260+ break ;
261+ case 'number' :
262+ currentDetails = this . getNumberDetails ( flagKey , defaultValue as number , options ) as EvaluationDetails < T > ;
263+ break ;
264+ case 'object' :
265+ currentDetails = this . getObjectDetails ( flagKey , defaultValue as JsonValue , options ) as EvaluationDetails < T > ;
266+ break ;
267+ default :
268+ throw new Error ( `Unsupported flag type: ${ flagType } ` ) ;
269+ }
270+
271+ const handler = ( ) => {
272+ const oldDetails = { ...currentDetails } ;
273+ let newDetails : EvaluationDetails < T > ;
274+
275+ switch ( flagType ) {
276+ case 'boolean' :
277+ newDetails = this . getBooleanDetails ( flagKey , defaultValue as boolean , options ) as EvaluationDetails < T > ;
278+ break ;
279+ case 'string' :
280+ newDetails = this . getStringDetails ( flagKey , defaultValue as string , options ) as EvaluationDetails < T > ;
281+ break ;
282+ case 'number' :
283+ newDetails = this . getNumberDetails ( flagKey , defaultValue as number , options ) as EvaluationDetails < T > ;
284+ break ;
285+ case 'object' :
286+ newDetails = this . getObjectDetails ( flagKey , defaultValue as JsonValue , options ) as EvaluationDetails < T > ;
287+ break ;
288+ default :
289+ return ;
290+ }
291+
292+ currentDetails = newDetails ;
293+ callback ( newDetails , oldDetails ) ;
294+ } ;
295+
296+ this . addHandler ( ProviderEvents . ContextChanged , handler , { } ) ;
297+
298+ return ( ) => {
299+ this . removeHandler ( ProviderEvents . ContextChanged , handler ) ;
300+ } ;
194301 }
195302
196303 track ( occurrenceKey : string , occurrenceDetails : TrackingEventDetails = { } ) : void {
@@ -211,6 +318,17 @@ export class OpenFeatureClient implements Client {
211318 }
212319 }
213320
321+ private evaluateWithSubscription < T extends FlagValue > (
322+ flagKey : string ,
323+ resolver : ( flagKey : string , defaultValue : T , context : EvaluationContext , logger : Logger ) => ResolutionDetails < T > ,
324+ defaultValue : T ,
325+ flagType : FlagValueType ,
326+ options : FlagEvaluationOptions = { } ,
327+ ) : EvaluationDetails < T > {
328+ const details = this . evaluate < T > ( flagKey , resolver , defaultValue , flagType , options ) ;
329+ return new EvaluationDetailsWithSubscription ( this , flagKey , defaultValue , flagType , details , options ) ;
330+ }
331+
214332 private evaluate < T extends FlagValue > (
215333 flagKey : string ,
216334 resolver : ( flagKey : string , defaultValue : T , context : EvaluationContext , logger : Logger ) => ResolutionDetails < T > ,
0 commit comments