@@ -8,6 +8,7 @@ import type {
88 HookContext ,
99 JsonValue ,
1010 Logger ,
11+ TrackingEventDetails ,
1112 OpenFeatureError ,
1213 ResolutionDetails } from '@openfeature/core' ;
1314import {
@@ -223,6 +224,19 @@ export class OpenFeatureClient implements Client {
223224 return this . evaluate < T > ( flagKey , this . _provider . resolveObjectEvaluation , defaultValue , 'object' , context , options ) ;
224225 }
225226
227+ track ( occurrenceKey : string , context : EvaluationContext , occurrenceDetails : TrackingEventDetails ) : void {
228+
229+ this . shortCircuitIfNotReady ( ) ;
230+
231+ const mergedContext = this . mergeContexts ( context ) ;
232+
233+ if ( typeof this . _provider . track === 'function' ) {
234+ return this . _provider . track ?.( occurrenceKey , mergedContext , occurrenceDetails ) ;
235+ } else {
236+ this . _logger . debug ( 'Provider does not implement track function: will no-op.' ) ;
237+ }
238+ }
239+
226240 private async evaluate < T extends FlagValue > (
227241 flagKey : string ,
228242 resolver : (
@@ -246,13 +260,7 @@ export class OpenFeatureClient implements Client {
246260 ] ;
247261 const allHooksReversed = [ ...allHooks ] . reverse ( ) ;
248262
249- // merge global and client contexts
250- const mergedContext = {
251- ...OpenFeature . getContext ( ) ,
252- ...OpenFeature . getTransactionContext ( ) ,
253- ...this . _context ,
254- ...invocationContext ,
255- } ;
263+ const mergedContext = this . mergeContexts ( invocationContext ) ;
256264
257265 // this reference cannot change during the course of evaluation
258266 // it may be used as a key in WeakMaps
@@ -269,12 +277,7 @@ export class OpenFeatureClient implements Client {
269277 try {
270278 const frozenContext = await this . beforeHooks ( allHooks , hookContext , options ) ;
271279
272- // short circuit evaluation entirely if provider is in a bad state
273- if ( this . providerStatus === ProviderStatus . NOT_READY ) {
274- throw new ProviderNotReadyError ( 'provider has not yet initialized' ) ;
275- } else if ( this . providerStatus === ProviderStatus . FATAL ) {
276- throw new ProviderFatalError ( 'provider is in an irrecoverable error state' ) ;
277- }
280+ this . shortCircuitIfNotReady ( ) ;
278281
279282 // run the referenced resolver, binding the provider.
280283 const resolution = await resolver . call ( this . _provider , flagKey , defaultValue , frozenContext , this . _logger ) ;
@@ -380,4 +383,23 @@ export class OpenFeatureClient implements Client {
380383 private get _logger ( ) {
381384 return this . _clientLogger || this . globalLogger ( ) ;
382385 }
386+
387+ private mergeContexts ( invocationContext : EvaluationContext ) {
388+ // merge global and client contexts
389+ return {
390+ ...OpenFeature . getContext ( ) ,
391+ ...OpenFeature . getTransactionContext ( ) ,
392+ ...this . _context ,
393+ ...invocationContext ,
394+ } ;
395+ }
396+
397+ private shortCircuitIfNotReady ( ) {
398+ // short circuit evaluation entirely if provider is in a bad state
399+ if ( this . providerStatus === ProviderStatus . NOT_READY ) {
400+ throw new ProviderNotReadyError ( 'provider has not yet initialized' ) ;
401+ } else if ( this . providerStatus === ProviderStatus . FATAL ) {
402+ throw new ProviderFatalError ( 'provider is in an irrecoverable error state' ) ;
403+ }
404+ }
383405}
0 commit comments