@openfeature/web-sdk
Advanced tools
Comparing version 0.4.12 to 0.4.13
@@ -477,7 +477,7 @@ "use strict"; | ||
var import_core4 = require("@openfeature/core"); | ||
var VariantNotFoundError = class extends import_core4.OpenFeatureError { | ||
var VariantNotFoundError = class _VariantNotFoundError extends import_core4.OpenFeatureError { | ||
code; | ||
constructor(message) { | ||
super(message); | ||
Object.setPrototypeOf(this, VariantNotFoundError.prototype); | ||
Object.setPrototypeOf(this, _VariantNotFoundError.prototype); | ||
this.name = "VariantNotFoundError"; | ||
@@ -592,7 +592,6 @@ this.code = import_core4.ErrorCode.GENERAL; | ||
var _globalThis = globalThis; | ||
var OpenFeatureAPI = class extends import_core6.OpenFeatureCommonAPI { | ||
var OpenFeatureAPI = class _OpenFeatureAPI extends import_core6.OpenFeatureCommonAPI { | ||
_events = new OpenFeatureEventEmitter(); | ||
_defaultProvider = NOOP_PROVIDER; | ||
_createEventEmitter = () => new OpenFeatureEventEmitter(); | ||
_namedProviderContext = /* @__PURE__ */ new Map(); | ||
constructor() { | ||
@@ -611,17 +610,17 @@ super("client"); | ||
} | ||
const instance = new OpenFeatureAPI(); | ||
const instance = new _OpenFeatureAPI(); | ||
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance; | ||
return instance; | ||
} | ||
async setContext(nameOrContext, contextOrUndefined) { | ||
const clientName = (0, import_core6.stringOrUndefined)(nameOrContext); | ||
const context = (0, import_core6.objectOrUndefined)(nameOrContext) ?? (0, import_core6.objectOrUndefined)(contextOrUndefined) ?? {}; | ||
if (clientName) { | ||
const provider = this._clientProviders.get(clientName); | ||
async setContext(domainOrContext, contextOrUndefined) { | ||
const domain = (0, import_core6.stringOrUndefined)(domainOrContext); | ||
const context = (0, import_core6.objectOrUndefined)(domainOrContext) ?? (0, import_core6.objectOrUndefined)(contextOrUndefined) ?? {}; | ||
if (domain) { | ||
const provider = this._domainScopedProviders.get(domain); | ||
if (provider) { | ||
const oldContext = this.getContext(clientName); | ||
this._namedProviderContext.set(clientName, context); | ||
await this.runProviderContextChangeHandler(clientName, provider, oldContext, context); | ||
const oldContext = this.getContext(domain); | ||
this._domainScopedContext.set(domain, context); | ||
await this.runProviderContextChangeHandler(domain, provider, oldContext, context); | ||
} else { | ||
this._namedProviderContext.set(clientName, context); | ||
this._domainScopedContext.set(domain, context); | ||
} | ||
@@ -631,14 +630,14 @@ } else { | ||
this._context = context; | ||
const defaultContextNameProviders = Array.from(this._clientProviders.entries()).filter(([name]) => !this._namedProviderContext.has(name)).reduce((acc, [name, provider]) => { | ||
acc.push({ name, provider }); | ||
const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, provider]) => { | ||
acc.push({ domain: domain2, provider }); | ||
return acc; | ||
}, []); | ||
const allProviders = [ | ||
// add in the default (no name) | ||
{ name: void 0, provider: this._defaultProvider }, | ||
...defaultContextNameProviders | ||
// add in the default (no domain) | ||
{ domain: void 0, provider: this._defaultProvider }, | ||
...unboundProviders | ||
]; | ||
await Promise.all( | ||
allProviders.map( | ||
(tuple) => this.runProviderContextChangeHandler(tuple.name, tuple.provider, oldContext, context) | ||
(tuple) => this.runProviderContextChangeHandler(tuple.domain, tuple.provider, oldContext, context) | ||
) | ||
@@ -648,10 +647,10 @@ ); | ||
} | ||
getContext(nameOrUndefined) { | ||
const clientName = (0, import_core6.stringOrUndefined)(nameOrUndefined); | ||
if (clientName) { | ||
const context = this._namedProviderContext.get(clientName); | ||
getContext(domainOrUndefined) { | ||
const domain = (0, import_core6.stringOrUndefined)(domainOrUndefined); | ||
if (domain) { | ||
const context = this._domainScopedContext.get(domain); | ||
if (context) { | ||
return context; | ||
} else { | ||
this._logger.debug(`Unable to find context for '${clientName}'.`); | ||
this._logger.debug(`Unable to find context for '${domain}'.`); | ||
} | ||
@@ -661,13 +660,13 @@ } | ||
} | ||
async clearContext(nameOrUndefined) { | ||
const clientName = (0, import_core6.stringOrUndefined)(nameOrUndefined); | ||
if (clientName) { | ||
const provider = this._clientProviders.get(clientName); | ||
async clearContext(domainOrUndefined) { | ||
const domain = (0, import_core6.stringOrUndefined)(domainOrUndefined); | ||
if (domain) { | ||
const provider = this._domainScopedProviders.get(domain); | ||
if (provider) { | ||
const oldContext = this.getContext(clientName); | ||
this._namedProviderContext.delete(clientName); | ||
const oldContext = this.getContext(domain); | ||
this._domainScopedContext.delete(domain); | ||
const newContext = this.getContext(); | ||
await this.runProviderContextChangeHandler(clientName, provider, oldContext, newContext); | ||
await this.runProviderContextChangeHandler(domain, provider, oldContext, newContext); | ||
} else { | ||
this._namedProviderContext.delete(clientName); | ||
this._domainScopedContext.delete(domain); | ||
} | ||
@@ -680,7 +679,7 @@ } else { | ||
* Resets the global evaluation context and removes the evaluation context for | ||
* all named clients. | ||
* all domains. | ||
*/ | ||
async clearContexts() { | ||
await this.clearContext(); | ||
await Promise.allSettled(Array.from(this._clientProviders.keys()).map((name) => this.clearContext(name))); | ||
await Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain))); | ||
} | ||
@@ -694,14 +693,14 @@ /** | ||
* Otherwise, the default provider is used until a provider is assigned to that name. | ||
* @param {string} name The name of the client | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
* @param {string} version The version of the client (only used for metadata) | ||
* @returns {Client} OpenFeature Client | ||
*/ | ||
getClient(name, version) { | ||
getClient(domain, version) { | ||
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.getProviderForClient(domain), | ||
() => this.buildAndCacheEventEmitterForClient(domain), | ||
() => this._logger, | ||
{ name, version } | ||
{ domain, version } | ||
); | ||
@@ -715,12 +714,12 @@ } | ||
await super.clearProvidersAndSetDefault(NOOP_PROVIDER); | ||
this._namedProviderContext.clear(); | ||
this._domainScopedContext.clear(); | ||
} | ||
async runProviderContextChangeHandler(clientName, provider, oldContext, newContext) { | ||
async runProviderContextChangeHandler(domain, provider, oldContext, newContext) { | ||
const providerName = provider.metadata.name; | ||
try { | ||
await provider.onContextChange?.(oldContext, newContext); | ||
this.getAssociatedEventEmitters(clientName).forEach((emitter) => { | ||
emitter?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName, providerName }); | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName, providerName }); | ||
this._events?.emit(import_core2.ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName }); | ||
} catch (err) { | ||
@@ -730,6 +729,6 @@ const error = err; | ||
this._logger?.error(`${message}`, err); | ||
this.getAssociatedEventEmitters(clientName).forEach((emitter) => { | ||
emitter?.emit(import_core2.ClientProviderEvents.Error, { clientName, providerName, message }); | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit(import_core2.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message }); | ||
}); | ||
this._events?.emit(import_core2.ClientProviderEvents.Error, { clientName, providerName, message }); | ||
this._events?.emit(import_core2.ClientProviderEvents.Error, { clientName: domain, domain, providerName, message }); | ||
} | ||
@@ -752,3 +751,5 @@ } | ||
return { | ||
name: this.options.name, | ||
// Use domain if name is not provided | ||
name: this.options.domain ?? this.options.name, | ||
domain: this.options.domain ?? this.options.name, | ||
version: this.options.version, | ||
@@ -766,3 +767,7 @@ providerMetadata: this.providerAccessor().metadata | ||
try { | ||
handler({ clientName: this.metadata.name, providerName: this._provider.metadata.name }); | ||
handler({ | ||
clientName: this.metadata.name, | ||
domain: this.metadata.domain, | ||
providerName: this._provider.metadata.name | ||
}); | ||
} catch (err) { | ||
@@ -841,3 +846,3 @@ this._logger?.error("Error running event handler:", err); | ||
const context = { | ||
...OpenFeature.getContext(this?.options?.name) | ||
...OpenFeature.getContext(this?.options?.domain) | ||
}; | ||
@@ -844,0 +849,0 @@ const hookContext = { |
@@ -474,7 +474,7 @@ var __create = Object.create; | ||
import { ErrorCode, OpenFeatureError } from "@openfeature/core"; | ||
var VariantNotFoundError = class extends OpenFeatureError { | ||
var VariantNotFoundError = class _VariantNotFoundError extends OpenFeatureError { | ||
code; | ||
constructor(message) { | ||
super(message); | ||
Object.setPrototypeOf(this, VariantNotFoundError.prototype); | ||
Object.setPrototypeOf(this, _VariantNotFoundError.prototype); | ||
this.name = "VariantNotFoundError"; | ||
@@ -589,7 +589,6 @@ this.code = ErrorCode.GENERAL; | ||
var _globalThis = globalThis; | ||
var OpenFeatureAPI = class extends OpenFeatureCommonAPI { | ||
var OpenFeatureAPI = class _OpenFeatureAPI extends OpenFeatureCommonAPI { | ||
_events = new OpenFeatureEventEmitter(); | ||
_defaultProvider = NOOP_PROVIDER; | ||
_createEventEmitter = () => new OpenFeatureEventEmitter(); | ||
_namedProviderContext = /* @__PURE__ */ new Map(); | ||
constructor() { | ||
@@ -608,17 +607,17 @@ super("client"); | ||
} | ||
const instance = new OpenFeatureAPI(); | ||
const instance = new _OpenFeatureAPI(); | ||
_globalThis[GLOBAL_OPENFEATURE_API_KEY] = instance; | ||
return instance; | ||
} | ||
async setContext(nameOrContext, contextOrUndefined) { | ||
const clientName = stringOrUndefined(nameOrContext); | ||
const context = objectOrUndefined(nameOrContext) ?? objectOrUndefined(contextOrUndefined) ?? {}; | ||
if (clientName) { | ||
const provider = this._clientProviders.get(clientName); | ||
async setContext(domainOrContext, contextOrUndefined) { | ||
const domain = stringOrUndefined(domainOrContext); | ||
const context = objectOrUndefined(domainOrContext) ?? objectOrUndefined(contextOrUndefined) ?? {}; | ||
if (domain) { | ||
const provider = this._domainScopedProviders.get(domain); | ||
if (provider) { | ||
const oldContext = this.getContext(clientName); | ||
this._namedProviderContext.set(clientName, context); | ||
await this.runProviderContextChangeHandler(clientName, provider, oldContext, context); | ||
const oldContext = this.getContext(domain); | ||
this._domainScopedContext.set(domain, context); | ||
await this.runProviderContextChangeHandler(domain, provider, oldContext, context); | ||
} else { | ||
this._namedProviderContext.set(clientName, context); | ||
this._domainScopedContext.set(domain, context); | ||
} | ||
@@ -628,14 +627,14 @@ } else { | ||
this._context = context; | ||
const defaultContextNameProviders = Array.from(this._clientProviders.entries()).filter(([name]) => !this._namedProviderContext.has(name)).reduce((acc, [name, provider]) => { | ||
acc.push({ name, provider }); | ||
const unboundProviders = Array.from(this._domainScopedProviders.entries()).filter(([domain2]) => !this._domainScopedContext.has(domain2)).reduce((acc, [domain2, provider]) => { | ||
acc.push({ domain: domain2, provider }); | ||
return acc; | ||
}, []); | ||
const allProviders = [ | ||
// add in the default (no name) | ||
{ name: void 0, provider: this._defaultProvider }, | ||
...defaultContextNameProviders | ||
// add in the default (no domain) | ||
{ domain: void 0, provider: this._defaultProvider }, | ||
...unboundProviders | ||
]; | ||
await Promise.all( | ||
allProviders.map( | ||
(tuple) => this.runProviderContextChangeHandler(tuple.name, tuple.provider, oldContext, context) | ||
(tuple) => this.runProviderContextChangeHandler(tuple.domain, tuple.provider, oldContext, context) | ||
) | ||
@@ -645,10 +644,10 @@ ); | ||
} | ||
getContext(nameOrUndefined) { | ||
const clientName = stringOrUndefined(nameOrUndefined); | ||
if (clientName) { | ||
const context = this._namedProviderContext.get(clientName); | ||
getContext(domainOrUndefined) { | ||
const domain = stringOrUndefined(domainOrUndefined); | ||
if (domain) { | ||
const context = this._domainScopedContext.get(domain); | ||
if (context) { | ||
return context; | ||
} else { | ||
this._logger.debug(`Unable to find context for '${clientName}'.`); | ||
this._logger.debug(`Unable to find context for '${domain}'.`); | ||
} | ||
@@ -658,13 +657,13 @@ } | ||
} | ||
async clearContext(nameOrUndefined) { | ||
const clientName = stringOrUndefined(nameOrUndefined); | ||
if (clientName) { | ||
const provider = this._clientProviders.get(clientName); | ||
async clearContext(domainOrUndefined) { | ||
const domain = stringOrUndefined(domainOrUndefined); | ||
if (domain) { | ||
const provider = this._domainScopedProviders.get(domain); | ||
if (provider) { | ||
const oldContext = this.getContext(clientName); | ||
this._namedProviderContext.delete(clientName); | ||
const oldContext = this.getContext(domain); | ||
this._domainScopedContext.delete(domain); | ||
const newContext = this.getContext(); | ||
await this.runProviderContextChangeHandler(clientName, provider, oldContext, newContext); | ||
await this.runProviderContextChangeHandler(domain, provider, oldContext, newContext); | ||
} else { | ||
this._namedProviderContext.delete(clientName); | ||
this._domainScopedContext.delete(domain); | ||
} | ||
@@ -677,7 +676,7 @@ } else { | ||
* Resets the global evaluation context and removes the evaluation context for | ||
* all named clients. | ||
* all domains. | ||
*/ | ||
async clearContexts() { | ||
await this.clearContext(); | ||
await Promise.allSettled(Array.from(this._clientProviders.keys()).map((name) => this.clearContext(name))); | ||
await Promise.allSettled(Array.from(this._domainScopedProviders.keys()).map((domain) => this.clearContext(domain))); | ||
} | ||
@@ -691,14 +690,14 @@ /** | ||
* Otherwise, the default provider is used until a provider is assigned to that name. | ||
* @param {string} name The name of the client | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
* @param {string} version The version of the client (only used for metadata) | ||
* @returns {Client} OpenFeature Client | ||
*/ | ||
getClient(name, version) { | ||
getClient(domain, version) { | ||
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.getProviderForClient(domain), | ||
() => this.buildAndCacheEventEmitterForClient(domain), | ||
() => this._logger, | ||
{ name, version } | ||
{ domain, version } | ||
); | ||
@@ -712,12 +711,12 @@ } | ||
await super.clearProvidersAndSetDefault(NOOP_PROVIDER); | ||
this._namedProviderContext.clear(); | ||
this._domainScopedContext.clear(); | ||
} | ||
async runProviderContextChangeHandler(clientName, provider, oldContext, newContext) { | ||
async runProviderContextChangeHandler(domain, provider, oldContext, newContext) { | ||
const providerName = provider.metadata.name; | ||
try { | ||
await provider.onContextChange?.(oldContext, newContext); | ||
this.getAssociatedEventEmitters(clientName).forEach((emitter) => { | ||
emitter?.emit(ClientProviderEvents.ContextChanged, { clientName, providerName }); | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit(ClientProviderEvents.ContextChanged, { clientName, providerName }); | ||
this._events?.emit(ClientProviderEvents.ContextChanged, { clientName: domain, domain, providerName }); | ||
} catch (err) { | ||
@@ -727,6 +726,6 @@ const error = err; | ||
this._logger?.error(`${message}`, err); | ||
this.getAssociatedEventEmitters(clientName).forEach((emitter) => { | ||
emitter?.emit(ClientProviderEvents.Error, { clientName, providerName, message }); | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message }); | ||
}); | ||
this._events?.emit(ClientProviderEvents.Error, { clientName, providerName, message }); | ||
this._events?.emit(ClientProviderEvents.Error, { clientName: domain, domain, providerName, message }); | ||
} | ||
@@ -749,3 +748,5 @@ } | ||
return { | ||
name: this.options.name, | ||
// Use domain if name is not provided | ||
name: this.options.domain ?? this.options.name, | ||
domain: this.options.domain ?? this.options.name, | ||
version: this.options.version, | ||
@@ -763,3 +764,7 @@ providerMetadata: this.providerAccessor().metadata | ||
try { | ||
handler({ clientName: this.metadata.name, providerName: this._provider.metadata.name }); | ||
handler({ | ||
clientName: this.metadata.name, | ||
domain: this.metadata.domain, | ||
providerName: this._provider.metadata.name | ||
}); | ||
} catch (err) { | ||
@@ -838,3 +843,3 @@ this._logger?.error("Error running event handler:", err); | ||
const context = { | ||
...OpenFeature.getContext(this?.options?.name) | ||
...OpenFeature.getContext(this?.options?.domain) | ||
}; | ||
@@ -841,0 +846,0 @@ const hookContext = { |
@@ -242,3 +242,7 @@ import { BaseHook, HookHints, EvaluationDetails, JsonValue, EvaluationLifeCycle, ManageLogger, Eventing, ClientMetadata, ProviderStatus, ClientProviderEvents, GenericEventEmitter, CommonEventDetails, FlagValue, CommonProvider, EvaluationContext, Logger, ResolutionDetails, EventHandler, OpenFeatureCommonAPI, ManageContext } from '@openfeature/core'; | ||
type OpenFeatureClientOptions = { | ||
/** | ||
* @deprecated Use `domain` instead. | ||
*/ | ||
name?: string; | ||
domain?: string; | ||
version?: string; | ||
@@ -284,3 +288,2 @@ }; | ||
protected _createEventEmitter: () => OpenFeatureEventEmitter; | ||
protected _namedProviderContext: Map<string, EvaluationContext>; | ||
private constructor(); | ||
@@ -295,3 +298,3 @@ /** | ||
* Sets the evaluation context globally. | ||
* This will be used by all providers that have not been overridden with a named client. | ||
* This will be used by all providers that have not bound to a domain. | ||
* @param {EvaluationContext} context Evaluation context | ||
@@ -304,4 +307,4 @@ * @example | ||
* Sets the evaluation context for a specific provider. | ||
* This will only affect providers with a matching client name. | ||
* @param {string} clientName The name to identify the client | ||
* This will only affect providers bound to a domain. | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
* @param {EvaluationContext} context Evaluation context | ||
@@ -313,3 +316,3 @@ * @example | ||
*/ | ||
setContext(clientName: string, context: EvaluationContext): Promise<void>; | ||
setContext(domain: string, context: EvaluationContext): Promise<void>; | ||
/** | ||
@@ -323,6 +326,6 @@ * Access the global evaluation context. | ||
* The global evaluation context is returned if a matching named client is not found. | ||
* @param {string} clientName The name to identify the client | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
* @returns {EvaluationContext} Evaluation context | ||
*/ | ||
getContext(clientName?: string): EvaluationContext; | ||
getContext(domain?: string): EvaluationContext; | ||
/** | ||
@@ -334,8 +337,8 @@ * Resets the global evaluation context to an empty object. | ||
* Removes the evaluation context for a specific named client. | ||
* @param {string} clientName The name to identify the client | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
*/ | ||
clearContext(clientName: string): Promise<void>; | ||
clearContext(domain: string): Promise<void>; | ||
/** | ||
* Resets the global evaluation context and removes the evaluation context for | ||
* all named clients. | ||
* all domains. | ||
*/ | ||
@@ -350,7 +353,7 @@ clearContexts(): Promise<void>; | ||
* Otherwise, the default provider is used until a provider is assigned to that name. | ||
* @param {string} name The name of the client | ||
* @param {string} domain An identifier which logically binds clients with providers | ||
* @param {string} version The version of the client (only used for metadata) | ||
* @returns {Client} OpenFeature Client | ||
*/ | ||
getClient(name?: string, version?: string): Client; | ||
getClient(domain?: string, version?: string): Client; | ||
/** | ||
@@ -357,0 +360,0 @@ * Clears all registered providers and resets the default provider. |
{ | ||
"name": "@openfeature/web-sdk", | ||
"version": "0.4.12", | ||
"version": "0.4.13", | ||
"description": "OpenFeature SDK for Web", | ||
@@ -49,7 +49,7 @@ "main": "./dist/cjs/index.js", | ||
"peerDependencies": { | ||
"@openfeature/core": "0.0.25" | ||
"@openfeature/core": "0.0.26" | ||
}, | ||
"devDependencies": { | ||
"@openfeature/core": "0.0.25" | ||
"@openfeature/core": "0.0.26" | ||
} | ||
} |
@@ -19,4 +19,4 @@ <!-- markdownlint-disable MD033 --> | ||
<!-- x-release-please-start-version --> | ||
<a href="https://github.com/open-feature/js-sdk/releases/tag/web-sdk-v0.4.12"> | ||
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.4.12&color=blue&style=for-the-badge" /> | ||
<a href="https://github.com/open-feature/js-sdk/releases/tag/web-sdk-v0.4.13"> | ||
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v0.4.13&color=blue&style=for-the-badge" /> | ||
</a> | ||
@@ -93,12 +93,12 @@ <!-- x-release-please-end --> | ||
| Status | Features | Description | | ||
| ------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ||
| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. | | ||
| ✅ | [Targeting](#targeting-and-context) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). | | ||
| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. | | ||
| ✅ | [Logging](#logging) | Integrate with popular logging packages. | | ||
| ✅ | [Named clients](#named-clients) | Utilize multiple providers in a single application. | | ||
| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. | | ||
| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | | ||
| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. | | ||
| Status | Features | Description | | ||
| ------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ||
| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. | | ||
| ✅ | [Targeting](#targeting-and-context) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). | | ||
| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. | | ||
| ✅ | [Logging](#logging) | Integrate with popular logging packages. | | ||
| ✅ | [Domains](#domains) | Logically bind clients with providers. | | ||
| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. | | ||
| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | | ||
| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. | | ||
@@ -134,3 +134,3 @@ <sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub> | ||
In some situations, it may be beneficial to register multiple providers in the same application. | ||
This is possible using [named clients](#named-clients), which is covered in more detail below. | ||
This is possible using [domains](#domains), which is covered in more detail below. | ||
@@ -165,8 +165,9 @@ ### Flag evaluation flow | ||
Context is global and setting it is `async`. | ||
Providers may implement an `onContextChanged` method that receives the old context and the newer one. | ||
This method is used internally by the provider to detect if, given the context change, the flags values cached on client side are invalid. If needed a request will be made to the provider with the new context in order to get the correct flags values. | ||
Providers may implement an `onContextChanged` method that receives the old and newer contexts. | ||
Given a context change, providers can use this method internally to detect if the flag values cached on the client are still valid. | ||
If needed, a request will be made to the provider with the new context in order to get the correct flag values. | ||
### Hooks | ||
[Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle | ||
[Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle. | ||
Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Bcategory%5D%5B0%5D=Client-side&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=JavaScript) for a complete list of available hooks. | ||
@@ -193,3 +194,3 @@ If the hook you're looking for hasn't been created yet, see the [develop a hook](#develop-a-hook) section to learn how to build it yourself. | ||
The JS SDK will log warnings and errors to the console by default. | ||
The Web SDK will log warnings and errors to the console by default. | ||
This behavior can be overridden by passing a custom logger either globally or per client. | ||
@@ -212,22 +213,25 @@ A custom logger must implement the [Logger interface](../shared/src/logger/logger.ts). | ||
### Named clients | ||
### Domains | ||
Clients can be given a name. | ||
A name is a logical identifier that can be used to associate clients with a particular provider. | ||
If a name has no associated provider, the global provider is used. | ||
Clients can be assigned to a domain. | ||
A domain is a logical identifier which can be used to associate clients with a particular provider. | ||
If a domain has no associated provider, the default provider is used. | ||
```ts | ||
import { OpenFeature } from "@openfeature/web-sdk"; | ||
import { OpenFeature, InMemoryProvider } from "@openfeature/web-sdk"; | ||
// Registering the default provider | ||
OpenFeature.setProvider(NewLocalProvider()); | ||
// Registering a named provider | ||
OpenFeature.setProvider("clientForCache", new NewCachedProvider()); | ||
OpenFeature.setProvider(InMemoryProvider(myFlags)); | ||
// Registering a provider to a domain | ||
OpenFeature.setProvider("my-domain", new InMemoryProvider(someOtherFlags)); | ||
// A Client backed by default provider | ||
// A Client bound to the default provider | ||
const clientWithDefault = OpenFeature.getClient(); | ||
// A Client backed by NewCachedProvider | ||
const clientForCache = OpenFeature.getClient("clientForCache"); | ||
// A Client bound to the InMemoryProvider provider | ||
const domainScopedClient = OpenFeature.getClient("my-domain"); | ||
``` | ||
Domains can be defined on a provider during registration. | ||
For more details, please refer to the [providers](#providers) section. | ||
### Eventing | ||
@@ -291,3 +295,3 @@ | ||
// Adds runtime validation that the provider is used with the expected SDK | ||
public readonly runsOn = 'server'; | ||
public readonly runsOn = 'client'; | ||
readonly metadata = { | ||
@@ -294,0 +298,0 @@ name: 'My Provider', |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
237903
2170
345