-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathopen-feature.ts
84 lines (73 loc) · 2.88 KB
/
open-feature.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { EvaluationContext, ManageContext, OpenFeatureCommonAPI } from '@openfeature/shared';
import { Client, OpenFeatureClient } from './client';
import { NOOP_PROVIDER, Provider } from './provider';
// use a symbol as a key for the global singleton
const GLOBAL_OPENFEATURE_API_KEY = Symbol.for('@openfeature/web-sdk/api');
type OpenFeatureGlobal = {
[GLOBAL_OPENFEATURE_API_KEY]?: OpenFeatureAPI;
};
const _globalThis = globalThis as OpenFeatureGlobal;
export class OpenFeatureAPI extends OpenFeatureCommonAPI<Provider> implements ManageContext<Promise<void>> {
protected _defaultProvider: Provider = NOOP_PROVIDER;
// eslint-disable-next-line @typescript-eslint/no-empty-function
private constructor() {
super();
}
/**
* Gets a singleton instance of the OpenFeature API.
* @ignore
* @returns {OpenFeatureAPI} OpenFeature API
*/
static getInstance(): OpenFeatureAPI {
const globalApi = _globalThis[GLOBAL_OPENFEATURE_API_KEY];
if (globalApi) {
return globalApi;
}
const instance = new OpenFeatureAPI();
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance;
return instance;
}
async setContext(context: EvaluationContext): Promise<void> {
const oldContext = this._context;
this._context = context;
const allProviders = [this._defaultProvider, ...this._clientProviders.values()];
await Promise.all(
allProviders.map(async (provider) => {
try {
return await provider.onContextChange?.(oldContext, context);
} catch (err) {
this._logger?.error(`Error running context change handler of provider ${provider.metadata.name}:`, err);
}
})
);
}
getContext(): EvaluationContext {
return this._context;
}
/**
* A factory function for creating new named OpenFeature clients. Clients can contain
* their own state (e.g. logger, hook, context). Multiple clients can be used
* to segment feature flag configuration.
*
* If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
* Otherwise, the default provider is used until a provider is assigned to that name.
* @param {string} name The name of the client
* @param {string} version The version of the client (only used for metadata)
* @returns {Client} OpenFeature Client
*/
getClient(name?: string, version?: string): Client {
return new OpenFeatureClient(
// functions are passed here to make sure that these values are always up to date,
// and so we don't have to make these public properties on the API class.
() => this.getProviderForClient(name),
() => this.buildAndCacheEventEmitterForClient(name),
() => this._logger,
{ name, version }
);
}
}
/**
* A singleton instance of the OpenFeature API.
* @returns {OpenFeatureAPI} OpenFeature API
*/
export const OpenFeature = OpenFeatureAPI.getInstance();