@@ -8,6 +8,7 @@ import type {
8
8
HookContext ,
9
9
JsonValue ,
10
10
Logger ,
11
+ TrackingEventDetails ,
11
12
OpenFeatureError ,
12
13
ResolutionDetails } from '@openfeature/core' ;
13
14
import {
@@ -223,6 +224,19 @@ export class OpenFeatureClient implements Client {
223
224
return this . evaluate < T > ( flagKey , this . _provider . resolveObjectEvaluation , defaultValue , 'object' , context , options ) ;
224
225
}
225
226
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
+
226
240
private async evaluate < T extends FlagValue > (
227
241
flagKey : string ,
228
242
resolver : (
@@ -246,13 +260,7 @@ export class OpenFeatureClient implements Client {
246
260
] ;
247
261
const allHooksReversed = [ ...allHooks ] . reverse ( ) ;
248
262
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 ) ;
256
264
257
265
// this reference cannot change during the course of evaluation
258
266
// it may be used as a key in WeakMaps
@@ -269,12 +277,7 @@ export class OpenFeatureClient implements Client {
269
277
try {
270
278
const frozenContext = await this . beforeHooks ( allHooks , hookContext , options ) ;
271
279
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 ( ) ;
278
281
279
282
// run the referenced resolver, binding the provider.
280
283
const resolution = await resolver . call ( this . _provider , flagKey , defaultValue , frozenContext , this . _logger ) ;
@@ -380,4 +383,23 @@ export class OpenFeatureClient implements Client {
380
383
private get _logger ( ) {
381
384
return this . _clientLogger || this . globalLogger ( ) ;
382
385
}
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
+ }
383
405
}
0 commit comments