@openfeature/web-sdk 1.0.3 → 1.1.0

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.
package/README.md CHANGED
@@ -16,8 +16,8 @@
16
16
  <img alt="Specification" src="https://img.shields.io/static/v1?label=specification&message=v0.8.0&color=yellow&style=for-the-badge" />
17
17
  </a>
18
18
  <!-- x-release-please-start-version -->
19
- <a href="https://github.com/open-feature/js-sdk/releases/tag/web-sdk-v1.0.3">
20
- <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.0.3&color=blue&style=for-the-badge" />
19
+ <a href="https://github.com/open-feature/js-sdk/releases/tag/web-sdk-v1.1.0">
20
+ <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.1.0&color=blue&style=for-the-badge" />
21
21
  </a>
22
22
  <!-- x-release-please-end -->
23
23
  <br/>
@@ -54,6 +54,9 @@
54
54
  npm install --save @openfeature/web-sdk
55
55
  ```
56
56
 
57
+ > [!TIP]
58
+ > This SDK is designed to run in the browser. If you're interested in server support, check out the [Node.js SDK](https://openfeature.dev/docs/reference/technologies/server/javascript/).
59
+
57
60
  #### yarn
58
61
 
59
62
  ```sh
@@ -118,7 +121,7 @@ To register a provider and ensure it is ready before further actions are taken,
118
121
 
119
122
  ```ts
120
123
  await OpenFeature.setProviderAndWait(new MyProvider());
121
- ```
124
+ ```
122
125
 
123
126
  #### Synchronous
124
127
 
@@ -155,9 +158,16 @@ Sometimes, the value of a flag must consider some dynamic criteria about the app
155
158
  In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting).
156
159
  If the flag management system you're using supports targeting, you can provide the input data using the [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context).
157
160
 
161
+ ```ts
162
+ // Sets global context during provider registration
163
+ await OpenFeature.setProvider(new MyProvider(), { origin: document.location.host });
164
+ ```
165
+
166
+ Change context after the provider has been registered using `setContext`.
167
+
158
168
  ```ts
159
169
  // Set a value to the global context
160
- await OpenFeature.setContext({ origin: document.location.host });
170
+ await OpenFeature.setContext({ targetingKey: localStorage.getItem("targetingKey") });
161
171
  ```
162
172
 
163
173
  Context is global and setting it is `async`.
@@ -230,6 +240,24 @@ const domainScopedClient = OpenFeature.getClient("my-domain");
230
240
  Domains can be defined on a provider during registration.
231
241
  For more details, please refer to the [providers](#providers) section.
232
242
 
243
+ #### Manage evaluation context for domains
244
+
245
+ By default, domain-scoped clients use the global context.
246
+ This can be overridden by explicitly setting context when registering the provider or by references the domain when updating context:
247
+
248
+ ```ts
249
+ OpenFeature.setProvider("my-domain", new NewCachedProvider(), { targetingKey: localStorage.getItem("targetingKey") });
250
+ ```
251
+
252
+ To change context after the provider has been registered, use `setContext` with a name:
253
+
254
+ ```ts
255
+ await OpenFeature.setContext("my-domain", { targetingKey: localStorage.getItem("targetingKey") })
256
+ ```
257
+
258
+ Once context has been defined for a named client, it will override the global context for all clients using the associated provider.
259
+ Context can be cleared using for a named provider using `OpenFeature.clearContext("my-domain")` or call `OpenFeature.clearContexts()` to reset all context.
260
+
233
261
  ### Eventing
234
262
 
235
263
  Events allow you to react to state changes in the provider or underlying flag management system, such as flag definition changes, provider readiness, or error conditions.
package/dist/cjs/index.js CHANGED
@@ -220,38 +220,14 @@ __export(src_exports, {
220
220
  NOOP_PROVIDER: () => NOOP_PROVIDER,
221
221
  OpenFeature: () => OpenFeature,
222
222
  OpenFeatureAPI: () => OpenFeatureAPI,
223
- OpenFeatureClient: () => OpenFeatureClient,
224
223
  OpenFeatureEventEmitter: () => OpenFeatureEventEmitter,
225
- ProviderEvents: () => import_core2.ClientProviderEvents,
226
- ProviderStatus: () => import_core3.ClientProviderStatus
224
+ ProviderEvents: () => import_core3.ClientProviderEvents,
225
+ ProviderStatus: () => import_core.ClientProviderStatus
227
226
  });
228
227
  module.exports = __toCommonJS(src_exports);
229
228
 
230
- // src/client/open-feature-client.ts
231
- var import_core7 = require("@openfeature/core");
232
-
233
- // src/open-feature.ts
234
- var import_core6 = require("@openfeature/core");
235
-
236
- // src/events/open-feature-event-emitter.ts
237
- var import_core = require("@openfeature/core");
238
-
239
- // ../../node_modules/eventemitter3/index.mjs
240
- var import_index = __toESM(require_eventemitter3(), 1);
241
-
242
- // src/events/open-feature-event-emitter.ts
243
- var OpenFeatureEventEmitter = class extends import_core.GenericEventEmitter {
244
- eventEmitter = new import_index.default();
245
- constructor() {
246
- super();
247
- }
248
- };
249
-
250
- // src/events/events.ts
251
- var import_core2 = require("@openfeature/core");
252
-
253
229
  // src/provider/provider.ts
254
- var import_core3 = require("@openfeature/core");
230
+ var import_core = require("@openfeature/core");
255
231
 
256
232
  // src/provider/no-op-provider.ts
257
233
  var REASON_NO_OP = "No-op";
@@ -283,6 +259,23 @@ var NOOP_PROVIDER = new NoopFeatureProvider();
283
259
  // src/provider/in-memory-provider/in-memory-provider.ts
284
260
  var import_core5 = require("@openfeature/core");
285
261
 
262
+ // src/events/open-feature-event-emitter.ts
263
+ var import_core2 = require("@openfeature/core");
264
+
265
+ // ../../node_modules/eventemitter3/index.mjs
266
+ var import_index = __toESM(require_eventemitter3(), 1);
267
+
268
+ // src/events/open-feature-event-emitter.ts
269
+ var OpenFeatureEventEmitter = class extends import_core2.GenericEventEmitter {
270
+ eventEmitter = new import_index.default();
271
+ constructor() {
272
+ super();
273
+ }
274
+ };
275
+
276
+ // src/events/events.ts
277
+ var import_core3 = require("@openfeature/core");
278
+
286
279
  // src/provider/in-memory-provider/variant-not-found-error.ts
287
280
  var import_core4 = require("@openfeature/core");
288
281
  var VariantNotFoundError = class _VariantNotFoundError extends import_core4.OpenFeatureError {
@@ -326,9 +319,9 @@ var InMemoryProvider = class {
326
319
  this._flagConfiguration = { ...flagConfiguration };
327
320
  try {
328
321
  await this.initialize(this._context);
329
- this.events.emit(import_core2.ClientProviderEvents.ConfigurationChanged, { flagsChanged });
322
+ this.events.emit(import_core3.ClientProviderEvents.ConfigurationChanged, { flagsChanged });
330
323
  } catch (err) {
331
- this.events.emit(import_core2.ClientProviderEvents.Error);
324
+ this.events.emit(import_core3.ClientProviderEvents.Error);
332
325
  throw err;
333
326
  }
334
327
  }
@@ -390,172 +383,10 @@ var InMemoryProvider = class {
390
383
  };
391
384
 
392
385
  // src/open-feature.ts
393
- var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/web-sdk/api");
394
- var _globalThis = globalThis;
395
- var OpenFeatureAPI = class _OpenFeatureAPI extends import_core6.OpenFeatureCommonAPI {
396
- _statusEnumType = import_core3.ClientProviderStatus;
397
- _apiEmitter = new OpenFeatureEventEmitter();
398
- _defaultProvider = new import_core6.ProviderWrapper(NOOP_PROVIDER, import_core3.ClientProviderStatus.NOT_READY, this._statusEnumType);
399
- _domainScopedProviders = /* @__PURE__ */ new Map();
400
- _createEventEmitter = () => new OpenFeatureEventEmitter();
401
- constructor() {
402
- super("client");
403
- }
404
- /**
405
- * Gets a singleton instance of the OpenFeature API.
406
- * @ignore
407
- * @returns {OpenFeatureAPI} OpenFeature API
408
- */
409
- static getInstance() {
410
- const globalApi = _globalThis[GLOBAL_OPENFEATURE_API_KEY];
411
- if (globalApi) {
412
- return globalApi;
413
- }
414
- const instance = new _OpenFeatureAPI();
415
- _globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
416
- return instance;
417
- }
418
- getProviderStatus(domain) {
419
- if (!domain) {
420
- return this._defaultProvider.status;
421
- }
422
- return this._domainScopedProviders.get(domain)?.status ?? this._defaultProvider.status;
423
- }
424
- async setContext(domainOrContext, contextOrUndefined) {
425
- const domain = (0, import_core6.stringOrUndefined)(domainOrContext);
426
- const context = (0, import_core6.objectOrUndefined)(domainOrContext) ?? (0, import_core6.objectOrUndefined)(contextOrUndefined) ?? {};
427
- if (domain) {
428
- const wrapper = this._domainScopedProviders.get(domain);
429
- if (wrapper) {
430
- const oldContext = this.getContext(domain);
431
- this._domainScopedContext.set(domain, context);
432
- await this.runProviderContextChangeHandler(domain, wrapper, oldContext, context);
433
- } else {
434
- this._domainScopedContext.set(domain, context);
435
- }
436
- } else {
437
- const oldContext = this._context;
438
- this._context = context;
439
- const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, wrapper]) => {
440
- acc.push({ domain: domain2, wrapper });
441
- return acc;
442
- }, []);
443
- const allDomainRecords = [
444
- // add in the default (no domain)
445
- { domain: void 0, wrapper: this._defaultProvider },
446
- ...unboundProviders
447
- ];
448
- await Promise.all(
449
- allDomainRecords.map(
450
- (dm) => this.runProviderContextChangeHandler(dm.domain, dm.wrapper, oldContext, context)
451
- )
452
- );
453
- }
454
- }
455
- getContext(domainOrUndefined) {
456
- const domain = (0, import_core6.stringOrUndefined)(domainOrUndefined);
457
- if (domain) {
458
- const context = this._domainScopedContext.get(domain);
459
- if (context) {
460
- return context;
461
- } else {
462
- this._logger.debug(`Unable to find context for '${domain}'.`);
463
- }
464
- }
465
- return this._context;
466
- }
467
- async clearContext(domainOrUndefined) {
468
- const domain = (0, import_core6.stringOrUndefined)(domainOrUndefined);
469
- if (domain) {
470
- const wrapper = this._domainScopedProviders.get(domain);
471
- if (wrapper) {
472
- const oldContext = this.getContext(domain);
473
- this._domainScopedContext.delete(domain);
474
- const newContext = this.getContext();
475
- await this.runProviderContextChangeHandler(domain, wrapper, oldContext, newContext);
476
- } else {
477
- this._domainScopedContext.delete(domain);
478
- }
479
- } else {
480
- return this.setContext({});
481
- }
482
- }
483
- /**
484
- * Resets the global evaluation context and removes the evaluation context for
485
- * all domains.
486
- */
487
- async clearContexts() {
488
- await this.clearContext();
489
- await Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain)));
490
- }
491
- /**
492
- * A factory function for creating new named OpenFeature clients. Clients can contain
493
- * their own state (e.g. logger, hook, context). Multiple clients can be used
494
- * to segment feature flag configuration.
495
- *
496
- * If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
497
- * Otherwise, the default provider is used until a provider is assigned to that name.
498
- * @param {string} domain An identifier which logically binds clients with providers
499
- * @param {string} version The version of the client (only used for metadata)
500
- * @returns {Client} OpenFeature Client
501
- */
502
- getClient(domain, version) {
503
- return new OpenFeatureClient(
504
- // functions are passed here to make sure that these values are always up to date,
505
- // and so we don't have to make these public properties on the API class.
506
- () => this.getProviderForClient(domain),
507
- () => this.getProviderStatus(domain),
508
- () => this.buildAndCacheEventEmitterForClient(domain),
509
- () => this._logger,
510
- { domain, version }
511
- );
512
- }
513
- /**
514
- * Clears all registered providers and resets the default provider.
515
- * @returns {Promise<void>}
516
- */
517
- async clearProviders() {
518
- await super.clearProvidersAndSetDefault(NOOP_PROVIDER);
519
- this._domainScopedContext.clear();
520
- }
521
- async runProviderContextChangeHandler(domain, wrapper, oldContext, newContext) {
522
- const providerName = wrapper.provider?.metadata?.name || "unnamed-provider";
523
- try {
524
- if (typeof wrapper.provider.onContextChange === "function") {
525
- wrapper.incrementPendingContextChanges();
526
- wrapper.status = this._statusEnumType.RECONCILING;
527
- this.getAssociatedEventEmitters(domain).forEach((emitter) => {
528
- emitter?.emit(import_core2.ClientProviderEvents.Reconciling, { domain, providerName });
529
- });
530
- this._apiEmitter?.emit(import_core2.ClientProviderEvents.Reconciling, { domain, providerName });
531
- await wrapper.provider.onContextChange(oldContext, newContext);
532
- wrapper.decrementPendingContextChanges();
533
- }
534
- wrapper.status = this._statusEnumType.READY;
535
- if (wrapper.allContextChangesSettled) {
536
- this.getAssociatedEventEmitters(domain).forEach((emitter) => {
537
- emitter?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
538
- });
539
- this._apiEmitter?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
540
- }
541
- } catch (err) {
542
- wrapper.decrementPendingContextChanges();
543
- wrapper.status = this._statusEnumType.ERROR;
544
- if (wrapper.allContextChangesSettled) {
545
- const error = err;
546
- const message = `Error running ${providerName}'s context change handler: ${error?.message}`;
547
- this._logger?.error(`${message}`, err);
548
- this.getAssociatedEventEmitters(domain).forEach((emitter) => {
549
- emitter?.emit(import_core2.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
550
- });
551
- this._apiEmitter?.emit(import_core2.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
552
- }
553
- }
554
- }
555
- };
556
- var OpenFeature = OpenFeatureAPI.getInstance();
386
+ var import_core7 = require("@openfeature/core");
557
387
 
558
- // src/client/open-feature-client.ts
388
+ // src/client/internal/open-feature-client.ts
389
+ var import_core6 = require("@openfeature/core");
559
390
  var OpenFeatureClient = class {
560
391
  constructor(providerAccessor, providerStatusAccessor, emitterAccessor, globalLogger, options) {
561
392
  this.providerAccessor = providerAccessor;
@@ -580,7 +411,7 @@ var OpenFeatureClient = class {
580
411
  }
581
412
  addHandler(eventType, handler) {
582
413
  this.emitterAccessor().addHandler(eventType, handler);
583
- const shouldRunNow = (0, import_core7.statusMatchesEvent)(eventType, this.providerStatus);
414
+ const shouldRunNow = (0, import_core6.statusMatchesEvent)(eventType, this.providerStatus);
584
415
  if (shouldRunNow) {
585
416
  try {
586
417
  handler({
@@ -600,7 +431,7 @@ var OpenFeatureClient = class {
600
431
  return this.emitterAccessor().getHandlers(eventType);
601
432
  }
602
433
  setLogger(logger) {
603
- this._clientLogger = new import_core7.SafeLogger(logger);
434
+ this._clientLogger = new import_core6.SafeLogger(logger);
604
435
  return this;
605
436
  }
606
437
  addHooks(...hooks) {
@@ -674,10 +505,10 @@ var OpenFeatureClient = class {
674
505
  };
675
506
  try {
676
507
  this.beforeHooks(allHooks, hookContext, options);
677
- if (this.providerStatus === import_core3.ClientProviderStatus.NOT_READY) {
678
- throw new import_core7.ProviderNotReadyError("provider has not yet initialized");
679
- } else if (this.providerStatus === import_core3.ClientProviderStatus.FATAL) {
680
- throw new import_core7.ProviderFatalError("provider is in an irrecoverable error state");
508
+ if (this.providerStatus === import_core.ClientProviderStatus.NOT_READY) {
509
+ throw new import_core6.ProviderNotReadyError("provider has not yet initialized");
510
+ } else if (this.providerStatus === import_core.ClientProviderStatus.FATAL) {
511
+ throw new import_core6.ProviderFatalError("provider is in an irrecoverable error state");
681
512
  }
682
513
  const resolution = resolver.call(this._provider, flagKey, defaultValue, context, this._logger);
683
514
  const evaluationDetails = {
@@ -685,17 +516,20 @@ var OpenFeatureClient = class {
685
516
  flagMetadata: Object.freeze(resolution.flagMetadata ?? {}),
686
517
  flagKey
687
518
  };
519
+ if (evaluationDetails.errorCode) {
520
+ throw (0, import_core6.instantiateErrorByErrorCode)(evaluationDetails.errorCode);
521
+ }
688
522
  this.afterHooks(allHooksReversed, hookContext, evaluationDetails, options);
689
523
  return evaluationDetails;
690
524
  } catch (err) {
691
525
  const errorMessage = err?.message;
692
- const errorCode = err?.code || import_core7.ErrorCode.GENERAL;
526
+ const errorCode = err?.code || import_core6.ErrorCode.GENERAL;
693
527
  this.errorHooks(allHooksReversed, hookContext, err, options);
694
528
  return {
695
529
  errorCode,
696
530
  errorMessage,
697
531
  value: defaultValue,
698
- reason: import_core7.StandardResolutionReasons.ERROR,
532
+ reason: import_core6.StandardResolutionReasons.ERROR,
699
533
  flagMetadata: Object.freeze({}),
700
534
  flagKey
701
535
  };
@@ -749,6 +583,207 @@ var OpenFeatureClient = class {
749
583
  }
750
584
  };
751
585
 
586
+ // src/open-feature.ts
587
+ var GLOBAL_OPENFEATURE_API_KEY = Symbol.for("@openfeature/web-sdk/api");
588
+ var _globalThis = globalThis;
589
+ var OpenFeatureAPI = class _OpenFeatureAPI extends import_core7.OpenFeatureCommonAPI {
590
+ _statusEnumType = import_core.ClientProviderStatus;
591
+ _apiEmitter = new OpenFeatureEventEmitter();
592
+ _defaultProvider = new import_core7.ProviderWrapper(
593
+ NOOP_PROVIDER,
594
+ import_core.ClientProviderStatus.NOT_READY,
595
+ this._statusEnumType
596
+ );
597
+ _domainScopedProviders = /* @__PURE__ */ new Map();
598
+ _createEventEmitter = () => new OpenFeatureEventEmitter();
599
+ constructor() {
600
+ super("client");
601
+ }
602
+ /**
603
+ * Gets a singleton instance of the OpenFeature API.
604
+ * @ignore
605
+ * @returns {OpenFeatureAPI} OpenFeature API
606
+ */
607
+ static getInstance() {
608
+ const globalApi = _globalThis[GLOBAL_OPENFEATURE_API_KEY];
609
+ if (globalApi) {
610
+ return globalApi;
611
+ }
612
+ const instance = new _OpenFeatureAPI();
613
+ _globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
614
+ return instance;
615
+ }
616
+ getProviderStatus(domain) {
617
+ if (!domain) {
618
+ return this._defaultProvider.status;
619
+ }
620
+ return this._domainScopedProviders.get(domain)?.status ?? this._defaultProvider.status;
621
+ }
622
+ async setProviderAndWait(clientOrProvider, providerContextOrUndefined, contextOrUndefined) {
623
+ const domain = (0, import_core7.stringOrUndefined)(clientOrProvider);
624
+ const provider = domain ? (0, import_core7.objectOrUndefined)(providerContextOrUndefined) : (0, import_core7.objectOrUndefined)(clientOrProvider);
625
+ const context = domain ? (0, import_core7.objectOrUndefined)(contextOrUndefined) : (0, import_core7.objectOrUndefined)(providerContextOrUndefined);
626
+ if (context) {
627
+ if (domain) {
628
+ this._domainScopedContext.set(domain, context);
629
+ } else {
630
+ this._context = context;
631
+ }
632
+ }
633
+ await this.setAwaitableProvider(domain, provider);
634
+ }
635
+ setProvider(domainOrProvider, providerContextOrUndefined, contextOrUndefined) {
636
+ const domain = (0, import_core7.stringOrUndefined)(domainOrProvider);
637
+ const provider = domain ? (0, import_core7.objectOrUndefined)(providerContextOrUndefined) : (0, import_core7.objectOrUndefined)(domainOrProvider);
638
+ const context = domain ? (0, import_core7.objectOrUndefined)(contextOrUndefined) : (0, import_core7.objectOrUndefined)(providerContextOrUndefined);
639
+ if (context) {
640
+ if (domain) {
641
+ this._domainScopedContext.set(domain, context);
642
+ } else {
643
+ this._context = context;
644
+ }
645
+ }
646
+ const maybePromise = this.setAwaitableProvider(domain, provider);
647
+ Promise.resolve(maybePromise).catch((err) => {
648
+ this._logger.error("Error during provider initialization:", err);
649
+ });
650
+ return this;
651
+ }
652
+ async setContext(domainOrContext, contextOrUndefined) {
653
+ const domain = (0, import_core7.stringOrUndefined)(domainOrContext);
654
+ const context = (0, import_core7.objectOrUndefined)(domainOrContext) ?? (0, import_core7.objectOrUndefined)(contextOrUndefined) ?? {};
655
+ if (domain) {
656
+ const wrapper = this._domainScopedProviders.get(domain);
657
+ if (wrapper) {
658
+ const oldContext = this.getContext(domain);
659
+ this._domainScopedContext.set(domain, context);
660
+ await this.runProviderContextChangeHandler(domain, wrapper, oldContext, context);
661
+ } else {
662
+ this._domainScopedContext.set(domain, context);
663
+ }
664
+ } else {
665
+ const oldContext = this._context;
666
+ this._context = context;
667
+ const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, wrapper]) => {
668
+ acc.push({ domain: domain2, wrapper });
669
+ return acc;
670
+ }, []);
671
+ const allDomainRecords = [
672
+ // add in the default (no domain)
673
+ { domain: void 0, wrapper: this._defaultProvider },
674
+ ...unboundProviders
675
+ ];
676
+ await Promise.all(
677
+ allDomainRecords.map((dm) => this.runProviderContextChangeHandler(dm.domain, dm.wrapper, oldContext, context))
678
+ );
679
+ }
680
+ }
681
+ getContext(domainOrUndefined) {
682
+ const domain = (0, import_core7.stringOrUndefined)(domainOrUndefined);
683
+ if (domain) {
684
+ const context = this._domainScopedContext.get(domain);
685
+ if (context) {
686
+ return context;
687
+ } else {
688
+ this._logger.debug(`Unable to find context for '${domain}'.`);
689
+ }
690
+ }
691
+ return this._context;
692
+ }
693
+ async clearContext(domainOrUndefined) {
694
+ const domain = (0, import_core7.stringOrUndefined)(domainOrUndefined);
695
+ if (domain) {
696
+ const wrapper = this._domainScopedProviders.get(domain);
697
+ if (wrapper) {
698
+ const oldContext = this.getContext(domain);
699
+ this._domainScopedContext.delete(domain);
700
+ const newContext = this.getContext();
701
+ await this.runProviderContextChangeHandler(domain, wrapper, oldContext, newContext);
702
+ } else {
703
+ this._domainScopedContext.delete(domain);
704
+ }
705
+ } else {
706
+ return this.setContext({});
707
+ }
708
+ }
709
+ /**
710
+ * Resets the global evaluation context and removes the evaluation context for
711
+ * all domains.
712
+ */
713
+ async clearContexts() {
714
+ await this.clearContext();
715
+ await Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain)));
716
+ }
717
+ /**
718
+ * A factory function for creating new named OpenFeature clients. Clients can contain
719
+ * their own state (e.g. logger, hook, context). Multiple clients can be used
720
+ * to segment feature flag configuration.
721
+ *
722
+ * If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
723
+ * Otherwise, the default provider is used until a provider is assigned to that name.
724
+ * @param {string} domain An identifier which logically binds clients with providers
725
+ * @param {string} version The version of the client (only used for metadata)
726
+ * @returns {Client} OpenFeature Client
727
+ */
728
+ getClient(domain, version) {
729
+ return new OpenFeatureClient(
730
+ // functions are passed here to make sure that these values are always up to date,
731
+ // and so we don't have to make these public properties on the API class.
732
+ () => this.getProviderForClient(domain),
733
+ () => this.getProviderStatus(domain),
734
+ () => this.buildAndCacheEventEmitterForClient(domain),
735
+ () => this._logger,
736
+ { domain, version }
737
+ );
738
+ }
739
+ /**
740
+ * Clears all registered providers and resets the default provider.
741
+ * @returns {Promise<void>}
742
+ */
743
+ async clearProviders() {
744
+ await super.clearProvidersAndSetDefault(NOOP_PROVIDER);
745
+ this._domainScopedContext.clear();
746
+ }
747
+ async runProviderContextChangeHandler(domain, wrapper, oldContext, newContext) {
748
+ const providerName = wrapper.provider?.metadata?.name || "unnamed-provider";
749
+ try {
750
+ if (typeof wrapper.provider.onContextChange === "function") {
751
+ const maybePromise = wrapper.provider.onContextChange(oldContext, newContext);
752
+ if (typeof maybePromise?.then === "function") {
753
+ wrapper.incrementPendingContextChanges();
754
+ wrapper.status = this._statusEnumType.RECONCILING;
755
+ this.getAssociatedEventEmitters(domain).forEach((emitter) => {
756
+ emitter?.emit(import_core3.ClientProviderEvents.Reconciling, { domain, providerName });
757
+ });
758
+ this._apiEmitter?.emit(import_core3.ClientProviderEvents.Reconciling, { domain, providerName });
759
+ await maybePromise;
760
+ wrapper.decrementPendingContextChanges();
761
+ }
762
+ }
763
+ wrapper.status = this._statusEnumType.READY;
764
+ if (wrapper.allContextChangesSettled) {
765
+ this.getAssociatedEventEmitters(domain).forEach((emitter) => {
766
+ emitter?.emit(import_core3.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
767
+ });
768
+ this._apiEmitter?.emit(import_core3.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName });
769
+ }
770
+ } catch (err) {
771
+ wrapper.decrementPendingContextChanges();
772
+ wrapper.status = this._statusEnumType.ERROR;
773
+ if (wrapper.allContextChangesSettled) {
774
+ const error = err;
775
+ const message = `Error running ${providerName}'s context change handler: ${error?.message}`;
776
+ this._logger?.error(`${message}`, err);
777
+ this.getAssociatedEventEmitters(domain).forEach((emitter) => {
778
+ emitter?.emit(import_core3.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
779
+ });
780
+ this._apiEmitter?.emit(import_core3.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message });
781
+ }
782
+ }
783
+ }
784
+ };
785
+ var OpenFeature = OpenFeatureAPI.getInstance();
786
+
752
787
  // src/index.ts
753
788
  __reExport(src_exports, require("@openfeature/core"), module.exports);
754
789
  //# sourceMappingURL=index.js.map