@@ -28,6 +28,7 @@ type DomainRecord = {
2828} ;
2929
3030const _globalThis = globalThis as OpenFeatureGlobal ;
31+ const _localThis = { } as OpenFeatureGlobal ;
3132
3233export class OpenFeatureAPI
3334 extends OpenFeatureCommonAPI < ClientProviderStatus , Provider , Hook >
@@ -50,16 +51,19 @@ export class OpenFeatureAPI
5051 /**
5152 * Gets a singleton instance of the OpenFeature API.
5253 * @ignore
54+ * @param {boolean } global Whether to get the global (window) singleton instance or a package-local singleton instance.
5355 * @returns {OpenFeatureAPI } OpenFeature API
5456 */
55- static getInstance ( ) : OpenFeatureAPI {
56- const globalApi = _globalThis [ GLOBAL_OPENFEATURE_API_KEY ] ;
57+ static getInstance ( global = true ) : OpenFeatureAPI {
58+ const store = global ? _globalThis : _localThis ;
59+
60+ const globalApi = store [ GLOBAL_OPENFEATURE_API_KEY ] ;
5761 if ( globalApi ) {
5862 return globalApi ;
5963 }
6064
6165 const instance = new OpenFeatureAPI ( ) ;
62- _globalThis [ GLOBAL_OPENFEATURE_API_KEY ] = instance ;
66+ store [ GLOBAL_OPENFEATURE_API_KEY ] = instance ;
6367 return instance ;
6468 }
6569
@@ -430,8 +434,60 @@ export class OpenFeatureAPI
430434 }
431435}
432436
437+ interface OpenFeatureAPIWithIsolated extends OpenFeatureAPI {
438+ /**
439+ * An package-local singleton instance of the OpenFeature API.
440+ *
441+ * By default, the OpenFeature API is exposed as a global singleton (stored on `window` in browsers) instance.
442+ * While this can be very convenient as domains, providers, etc., are shared across an entire application,
443+ * this can mean that in multi-frontend architectures (e.g. micro-frontends) different parts of an application
444+ * can think they're loading different versions of OpenFeature, when they're actually all sharing the same instance.
445+ *
446+ * The `isolated` property provides access to a package-local singleton instance of the OpenFeature API,
447+ * which is not shared globally, isolated from the global singleton.
448+ * This allows different parts of an application to have their own isolated OpenFeature API instances,
449+ * avoiding potential conflicts and ensuring they're using the expected version of the SDK.
450+ *
451+ * HOWEVER, the `isolated` instance is *isolated* to the package, and so will not share domains, providers, etc., with
452+ * the global singleton instance. As it is still a singleton within the package though, it will share state with other
453+ * uses of the `isolated` instance imported from the same package within the same micro-frontend.
454+ * @example
455+ * import { OpenFeature } from '@openfeature/web-sdk';
456+ *
457+ * OpenFeature.setProvider(new MyGlobalProvider()); // Sets the provider for the default domain on the global instance
458+ * OpenFeature.isolated.setProvider(new MyIsolatedProvider()); // Sets the provider for the default domain on the isolated instance
459+ *
460+ * const globalClient = OpenFeature.getClient(); // Uses MyGlobalProvider, the provider for the default domain on the global instance
461+ * const isolatedClient = OpenFeature.isolated.getClient(); // Uses MyIsolatedProvider, the provider for the default domain on the isolated instance
462+ *
463+ * // In the same micro-frontend, in a different file ...
464+ * import { OpenFeature } from '@openfeature/web-sdk';
465+ *
466+ * const globalClient = OpenFeature.getClient(); // Uses MyGlobalProvider, the provider for the default domain on the global instance
467+ * const isolatedClient = OpenFeature.isolated.getClient(); // Uses MyIsolatedProvider, the provider for the default domain on the isolated instance
468+ *
469+ * // In another micro-frontend, after the above has executed ...
470+ * import { OpenFeature } from '@openfeature/web-sdk';
471+ *
472+ * const globalClient = OpenFeature.getClient(); // Uses MyGlobalProvider, the provider for the default domain on the global instance
473+ * const isolatedClient = OpenFeature.isolated.getClient(); // Returns the NOOP provider, as this is a different isolated instance
474+ */
475+ readonly isolated : OpenFeatureAPI ;
476+ }
477+
478+ const createOpenFeatureAPI = ( ) : OpenFeatureAPIWithIsolated => {
479+ const globalInstance = OpenFeatureAPI . getInstance ( ) ;
480+ const localInstance = OpenFeatureAPI . getInstance ( false ) ;
481+
482+ return Object . assign ( globalInstance , {
483+ get isolated ( ) {
484+ return localInstance ;
485+ } ,
486+ } ) ;
487+ } ;
488+
433489/**
434490 * A singleton instance of the OpenFeature API.
435- * @returns {OpenFeatureAPI } OpenFeature API
491+ * @returns {OpenFeatureAPIWithIsolated } OpenFeature API
436492 */
437- export const OpenFeature = OpenFeatureAPI . getInstance ( ) ;
493+ export const OpenFeature = createOpenFeatureAPI ( ) ;
0 commit comments