@openfeature/core
Advanced tools
Comparing version 0.0.26 to 0.0.27
@@ -24,3 +24,5 @@ "use strict"; | ||
AllProviderEvents: () => ClientProviderEvents, | ||
AllProviderStatus: () => ClientProviderStatus, | ||
ClientProviderEvents: () => ClientProviderEvents, | ||
ClientProviderStatus: () => ClientProviderStatus, | ||
DefaultLogger: () => DefaultLogger, | ||
@@ -36,6 +38,8 @@ ErrorCode: () => ErrorCode, | ||
ParseError: () => ParseError, | ||
ProviderFatalError: () => ProviderFatalError, | ||
ProviderNotReadyError: () => ProviderNotReadyError, | ||
ProviderStatus: () => ProviderStatus, | ||
ProviderWrapper: () => ProviderWrapper, | ||
SafeLogger: () => SafeLogger, | ||
ServerProviderEvents: () => ServerProviderEvents, | ||
ServerProviderStatus: () => ServerProviderStatus, | ||
StandardResolutionReasons: () => StandardResolutionReasons, | ||
@@ -100,2 +104,3 @@ TargetingKeyMissingError: () => TargetingKeyMissingError, | ||
ErrorCode2["PROVIDER_NOT_READY"] = "PROVIDER_NOT_READY"; | ||
ErrorCode2["PROVIDER_FATAL"] = "PROVIDER_FATAL"; | ||
ErrorCode2["FLAG_NOT_FOUND"] = "FLAG_NOT_FOUND"; | ||
@@ -187,10 +192,31 @@ ErrorCode2["PARSE_ERROR"] = "PARSE_ERROR"; | ||
// src/errors/provider-fatal-error.ts | ||
var ProviderFatalError = class _ProviderFatalError extends OpenFeatureError { | ||
code; | ||
constructor(message, options) { | ||
super(message, options); | ||
Object.setPrototypeOf(this, _ProviderFatalError.prototype); | ||
this.name = "ProviderFatalError"; | ||
this.code = "PROVIDER_FATAL" /* PROVIDER_FATAL */; | ||
} | ||
}; | ||
// src/provider/provider.ts | ||
var ProviderStatus = /* @__PURE__ */ ((ProviderStatus2) => { | ||
ProviderStatus2["NOT_READY"] = "NOT_READY"; | ||
ProviderStatus2["READY"] = "READY"; | ||
ProviderStatus2["ERROR"] = "ERROR"; | ||
ProviderStatus2["STALE"] = "STALE"; | ||
return ProviderStatus2; | ||
})(ProviderStatus || {}); | ||
var ServerProviderStatus = /* @__PURE__ */ ((ServerProviderStatus3) => { | ||
ServerProviderStatus3["NOT_READY"] = "NOT_READY"; | ||
ServerProviderStatus3["READY"] = "READY"; | ||
ServerProviderStatus3["ERROR"] = "ERROR"; | ||
ServerProviderStatus3["STALE"] = "STALE"; | ||
ServerProviderStatus3["FATAL"] = "FATAL"; | ||
return ServerProviderStatus3; | ||
})(ServerProviderStatus || {}); | ||
var ClientProviderStatus = /* @__PURE__ */ ((ClientProviderStatus3) => { | ||
ClientProviderStatus3["NOT_READY"] = "NOT_READY"; | ||
ClientProviderStatus3["READY"] = "READY"; | ||
ClientProviderStatus3["ERROR"] = "ERROR"; | ||
ClientProviderStatus3["STALE"] = "STALE"; | ||
ClientProviderStatus3["FATAL"] = "FATAL"; | ||
ClientProviderStatus3["RECONCILING"] = "RECONCILING"; | ||
return ClientProviderStatus3; | ||
})(ClientProviderStatus || {}); | ||
@@ -210,2 +236,3 @@ // src/events/events.ts | ||
ClientProviderEvents2["ContextChanged"] = "PROVIDER_CONTEXT_CHANGED"; | ||
ClientProviderEvents2["Reconciling"] = "PROVIDER_RECONCILING"; | ||
ClientProviderEvents2["Stale"] = "PROVIDER_STALE"; | ||
@@ -219,3 +246,5 @@ return ClientProviderEvents2; | ||
["ERROR" /* ERROR */]: "PROVIDER_ERROR" /* Error */, | ||
["FATAL" /* FATAL */]: "PROVIDER_ERROR" /* Error */, | ||
["STALE" /* STALE */]: "PROVIDER_STALE" /* Stale */, | ||
["RECONCILING" /* RECONCILING */]: "PROVIDER_RECONCILING" /* Reconciling */, | ||
["NOT_READY" /* NOT_READY */]: void 0 | ||
@@ -291,3 +320,4 @@ }; | ||
["PROVIDER_ERROR" /* Error */]: /* @__PURE__ */ new WeakMap(), | ||
["PROVIDER_STALE" /* Stale */]: /* @__PURE__ */ new WeakMap() | ||
["PROVIDER_STALE" /* Stale */]: /* @__PURE__ */ new WeakMap(), | ||
["PROVIDER_RECONCILING" /* Reconciling */]: /* @__PURE__ */ new WeakMap() | ||
}; | ||
@@ -355,2 +385,43 @@ _eventLogger; | ||
// src/open-feature.ts | ||
var ProviderWrapper = class { | ||
constructor(_provider, _status, _statusEnumType) { | ||
this._provider = _provider; | ||
this._status = _status; | ||
_provider.events?.addHandler("PROVIDER_READY" /* Ready */, () => { | ||
this._status = _statusEnumType.READY; | ||
}); | ||
_provider.events?.addHandler("PROVIDER_STALE" /* Stale */, () => { | ||
this._status = _statusEnumType.STALE; | ||
}); | ||
_provider.events?.addHandler("PROVIDER_ERROR" /* Error */, (details) => { | ||
if (details?.errorCode === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { | ||
this._status = _statusEnumType.FATAL; | ||
} else { | ||
this._status = _statusEnumType.ERROR; | ||
} | ||
}); | ||
} | ||
_pendingContextChanges = 0; | ||
get provider() { | ||
return this._provider; | ||
} | ||
set provider(provider) { | ||
this._provider = provider; | ||
} | ||
get status() { | ||
return this._status; | ||
} | ||
set status(status) { | ||
this._status = status; | ||
} | ||
get allContextChangesSettled() { | ||
return this._pendingContextChanges === 0; | ||
} | ||
incrementPendingContextChanges() { | ||
this._pendingContextChanges++; | ||
} | ||
decrementPendingContextChanges() { | ||
this._pendingContextChanges--; | ||
} | ||
}; | ||
var OpenFeatureCommonAPI = class { | ||
@@ -361,3 +432,2 @@ _hooks = []; | ||
_clientEventHandlers = /* @__PURE__ */ new Map(); | ||
_domainScopedProviders = /* @__PURE__ */ new Map(); | ||
_domainScopedContext = /* @__PURE__ */ new Map(); | ||
@@ -410,4 +480,5 @@ _clientEvents = /* @__PURE__ */ new Map(); | ||
const domain = keyProviderTuple[0]; | ||
const provider = keyProviderTuple[1]; | ||
const shouldRunNow = statusMatchesEvent(eventType, keyProviderTuple[1].status); | ||
const provider = keyProviderTuple[1].provider; | ||
const status = keyProviderTuple[1].status; | ||
const shouldRunNow = statusMatchesEvent(eventType, status); | ||
if (shouldRunNow) { | ||
@@ -421,3 +492,3 @@ try { | ||
}); | ||
this._events.addHandler(eventType, handler); | ||
this._apiEmitter.addHandler(eventType, handler); | ||
} | ||
@@ -430,3 +501,3 @@ /** | ||
removeHandler(eventType, handler) { | ||
this._events.removeHandler(eventType, handler); | ||
this._apiEmitter.removeHandler(eventType, handler); | ||
} | ||
@@ -437,3 +508,3 @@ /** | ||
clearHandlers() { | ||
this._events.removeAllHandlers(); | ||
this._apiEmitter.removeAllHandlers(); | ||
} | ||
@@ -446,3 +517,3 @@ /** | ||
getHandlers(eventType) { | ||
return this._events.getHandlers(eventType); | ||
return this._apiEmitter.getHandlers(eventType); | ||
} | ||
@@ -479,16 +550,17 @@ async setProviderAndWait(domainOrProvider, providerOrUndefined) { | ||
const emitters = this.getAssociatedEventEmitters(domain); | ||
if (typeof provider.initialize === "function" && provider.status === void 0) { | ||
const activeLogger = this._logger || console; | ||
activeLogger.warn( | ||
`Provider ${providerName} implements 'initialize' but not 'status'. Please implement 'status'.` | ||
); | ||
} | ||
let initializationPromise = void 0; | ||
if (provider?.status === "NOT_READY" /* NOT_READY */ && typeof provider.initialize === "function") { | ||
const wrappedProvider = new ProviderWrapper(provider, this._statusEnumType.NOT_READY, this._statusEnumType); | ||
if (typeof provider.initialize === "function" && !this.allProviders.includes(provider)) { | ||
initializationPromise = provider.initialize?.(domain ? this._domainScopedContext.get(domain) ?? this._context : this._context)?.then(() => { | ||
wrappedProvider.status = this._statusEnumType.READY; | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
this._apiEmitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
})?.catch((error) => { | ||
if (error?.code === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { | ||
wrappedProvider.status = this._statusEnumType.FATAL; | ||
} else { | ||
wrappedProvider.status = this._statusEnumType.ERROR; | ||
} | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
@@ -502,3 +574,3 @@ emitter?.emit("PROVIDER_ERROR" /* Error */, { | ||
}); | ||
this._events?.emit("PROVIDER_ERROR" /* Error */, { | ||
this._apiEmitter?.emit("PROVIDER_ERROR" /* Error */, { | ||
clientName: domain, | ||
@@ -512,15 +584,18 @@ domain, | ||
} else { | ||
wrappedProvider.status = this._statusEnumType.READY; | ||
emitters.forEach((emitter) => { | ||
emitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
this._apiEmitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
} | ||
if (domain) { | ||
this._domainScopedProviders.set(domain, provider); | ||
this._domainScopedProviders.set(domain, wrappedProvider); | ||
} else { | ||
this._defaultProvider = provider; | ||
this._defaultProvider = wrappedProvider; | ||
} | ||
this.transferListeners(oldProvider, provider, domain, emitters); | ||
if (![...this._domainScopedProviders.values(), this._defaultProvider].includes(oldProvider)) { | ||
oldProvider?.onClose?.(); | ||
if (!this.allProviders.includes(oldProvider)) { | ||
oldProvider?.onClose?.()?.catch((err) => { | ||
this._logger.error(`error closing provider: ${err?.message}, ${err?.stack}`); | ||
}); | ||
} | ||
@@ -531,5 +606,5 @@ return initializationPromise; | ||
if (!domain) { | ||
return this._defaultProvider; | ||
return this._defaultProvider.provider; | ||
} | ||
return this._domainScopedProviders.get(domain) ?? this._defaultProvider; | ||
return this._domainScopedProviders.get(domain)?.provider ?? this._defaultProvider.provider; | ||
} | ||
@@ -577,3 +652,3 @@ buildAndCacheEventEmitterForClient(domain) { | ||
}); | ||
this._events.emit(eventType, { | ||
this._apiEmitter.emit(eventType, { | ||
...details, | ||
@@ -592,13 +667,13 @@ clientName: domain, | ||
try { | ||
await this?._defaultProvider?.onClose?.(); | ||
await this?._defaultProvider.provider?.onClose?.(); | ||
} catch (err) { | ||
this.handleShutdownError(this._defaultProvider, err); | ||
this.handleShutdownError(this._defaultProvider.provider, err); | ||
} | ||
const providers = Array.from(this._domainScopedProviders); | ||
const wrappers = Array.from(this._domainScopedProviders); | ||
await Promise.all( | ||
providers.map(async ([, provider]) => { | ||
wrappers.map(async ([, wrapper]) => { | ||
try { | ||
await provider.onClose?.(); | ||
await wrapper?.provider.onClose?.(); | ||
} catch (err) { | ||
this.handleShutdownError(provider, err); | ||
this.handleShutdownError(wrapper?.provider, err); | ||
} | ||
@@ -615,5 +690,11 @@ }) | ||
this._domainScopedProviders.clear(); | ||
this._defaultProvider = defaultProvider; | ||
this._defaultProvider = new ProviderWrapper(defaultProvider, this._statusEnumType.NOT_READY, this._statusEnumType); | ||
} | ||
} | ||
get allProviders() { | ||
return [ | ||
...[...this._domainScopedProviders.values()].map((wrappers) => wrappers.provider), | ||
this._defaultProvider.provider | ||
]; | ||
} | ||
handleShutdownError(provider, err) { | ||
@@ -620,0 +701,0 @@ this._logger.error(`Error during shutdown of provider ${provider.metadata.name}: ${err}`); |
@@ -49,2 +49,3 @@ // src/errors/open-feature-error-abstract.ts | ||
ErrorCode2["PROVIDER_NOT_READY"] = "PROVIDER_NOT_READY"; | ||
ErrorCode2["PROVIDER_FATAL"] = "PROVIDER_FATAL"; | ||
ErrorCode2["FLAG_NOT_FOUND"] = "FLAG_NOT_FOUND"; | ||
@@ -136,10 +137,31 @@ ErrorCode2["PARSE_ERROR"] = "PARSE_ERROR"; | ||
// src/errors/provider-fatal-error.ts | ||
var ProviderFatalError = class _ProviderFatalError extends OpenFeatureError { | ||
code; | ||
constructor(message, options) { | ||
super(message, options); | ||
Object.setPrototypeOf(this, _ProviderFatalError.prototype); | ||
this.name = "ProviderFatalError"; | ||
this.code = "PROVIDER_FATAL" /* PROVIDER_FATAL */; | ||
} | ||
}; | ||
// src/provider/provider.ts | ||
var ProviderStatus = /* @__PURE__ */ ((ProviderStatus2) => { | ||
ProviderStatus2["NOT_READY"] = "NOT_READY"; | ||
ProviderStatus2["READY"] = "READY"; | ||
ProviderStatus2["ERROR"] = "ERROR"; | ||
ProviderStatus2["STALE"] = "STALE"; | ||
return ProviderStatus2; | ||
})(ProviderStatus || {}); | ||
var ServerProviderStatus = /* @__PURE__ */ ((ServerProviderStatus3) => { | ||
ServerProviderStatus3["NOT_READY"] = "NOT_READY"; | ||
ServerProviderStatus3["READY"] = "READY"; | ||
ServerProviderStatus3["ERROR"] = "ERROR"; | ||
ServerProviderStatus3["STALE"] = "STALE"; | ||
ServerProviderStatus3["FATAL"] = "FATAL"; | ||
return ServerProviderStatus3; | ||
})(ServerProviderStatus || {}); | ||
var ClientProviderStatus = /* @__PURE__ */ ((ClientProviderStatus3) => { | ||
ClientProviderStatus3["NOT_READY"] = "NOT_READY"; | ||
ClientProviderStatus3["READY"] = "READY"; | ||
ClientProviderStatus3["ERROR"] = "ERROR"; | ||
ClientProviderStatus3["STALE"] = "STALE"; | ||
ClientProviderStatus3["FATAL"] = "FATAL"; | ||
ClientProviderStatus3["RECONCILING"] = "RECONCILING"; | ||
return ClientProviderStatus3; | ||
})(ClientProviderStatus || {}); | ||
@@ -159,2 +181,3 @@ // src/events/events.ts | ||
ClientProviderEvents2["ContextChanged"] = "PROVIDER_CONTEXT_CHANGED"; | ||
ClientProviderEvents2["Reconciling"] = "PROVIDER_RECONCILING"; | ||
ClientProviderEvents2["Stale"] = "PROVIDER_STALE"; | ||
@@ -168,3 +191,5 @@ return ClientProviderEvents2; | ||
["ERROR" /* ERROR */]: "PROVIDER_ERROR" /* Error */, | ||
["FATAL" /* FATAL */]: "PROVIDER_ERROR" /* Error */, | ||
["STALE" /* STALE */]: "PROVIDER_STALE" /* Stale */, | ||
["RECONCILING" /* RECONCILING */]: "PROVIDER_RECONCILING" /* Reconciling */, | ||
["NOT_READY" /* NOT_READY */]: void 0 | ||
@@ -240,3 +265,4 @@ }; | ||
["PROVIDER_ERROR" /* Error */]: /* @__PURE__ */ new WeakMap(), | ||
["PROVIDER_STALE" /* Stale */]: /* @__PURE__ */ new WeakMap() | ||
["PROVIDER_STALE" /* Stale */]: /* @__PURE__ */ new WeakMap(), | ||
["PROVIDER_RECONCILING" /* Reconciling */]: /* @__PURE__ */ new WeakMap() | ||
}; | ||
@@ -304,2 +330,43 @@ _eventLogger; | ||
// src/open-feature.ts | ||
var ProviderWrapper = class { | ||
constructor(_provider, _status, _statusEnumType) { | ||
this._provider = _provider; | ||
this._status = _status; | ||
_provider.events?.addHandler("PROVIDER_READY" /* Ready */, () => { | ||
this._status = _statusEnumType.READY; | ||
}); | ||
_provider.events?.addHandler("PROVIDER_STALE" /* Stale */, () => { | ||
this._status = _statusEnumType.STALE; | ||
}); | ||
_provider.events?.addHandler("PROVIDER_ERROR" /* Error */, (details) => { | ||
if (details?.errorCode === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { | ||
this._status = _statusEnumType.FATAL; | ||
} else { | ||
this._status = _statusEnumType.ERROR; | ||
} | ||
}); | ||
} | ||
_pendingContextChanges = 0; | ||
get provider() { | ||
return this._provider; | ||
} | ||
set provider(provider) { | ||
this._provider = provider; | ||
} | ||
get status() { | ||
return this._status; | ||
} | ||
set status(status) { | ||
this._status = status; | ||
} | ||
get allContextChangesSettled() { | ||
return this._pendingContextChanges === 0; | ||
} | ||
incrementPendingContextChanges() { | ||
this._pendingContextChanges++; | ||
} | ||
decrementPendingContextChanges() { | ||
this._pendingContextChanges--; | ||
} | ||
}; | ||
var OpenFeatureCommonAPI = class { | ||
@@ -310,3 +377,2 @@ _hooks = []; | ||
_clientEventHandlers = /* @__PURE__ */ new Map(); | ||
_domainScopedProviders = /* @__PURE__ */ new Map(); | ||
_domainScopedContext = /* @__PURE__ */ new Map(); | ||
@@ -359,4 +425,5 @@ _clientEvents = /* @__PURE__ */ new Map(); | ||
const domain = keyProviderTuple[0]; | ||
const provider = keyProviderTuple[1]; | ||
const shouldRunNow = statusMatchesEvent(eventType, keyProviderTuple[1].status); | ||
const provider = keyProviderTuple[1].provider; | ||
const status = keyProviderTuple[1].status; | ||
const shouldRunNow = statusMatchesEvent(eventType, status); | ||
if (shouldRunNow) { | ||
@@ -370,3 +437,3 @@ try { | ||
}); | ||
this._events.addHandler(eventType, handler); | ||
this._apiEmitter.addHandler(eventType, handler); | ||
} | ||
@@ -379,3 +446,3 @@ /** | ||
removeHandler(eventType, handler) { | ||
this._events.removeHandler(eventType, handler); | ||
this._apiEmitter.removeHandler(eventType, handler); | ||
} | ||
@@ -386,3 +453,3 @@ /** | ||
clearHandlers() { | ||
this._events.removeAllHandlers(); | ||
this._apiEmitter.removeAllHandlers(); | ||
} | ||
@@ -395,3 +462,3 @@ /** | ||
getHandlers(eventType) { | ||
return this._events.getHandlers(eventType); | ||
return this._apiEmitter.getHandlers(eventType); | ||
} | ||
@@ -428,16 +495,17 @@ async setProviderAndWait(domainOrProvider, providerOrUndefined) { | ||
const emitters = this.getAssociatedEventEmitters(domain); | ||
if (typeof provider.initialize === "function" && provider.status === void 0) { | ||
const activeLogger = this._logger || console; | ||
activeLogger.warn( | ||
`Provider ${providerName} implements 'initialize' but not 'status'. Please implement 'status'.` | ||
); | ||
} | ||
let initializationPromise = void 0; | ||
if (provider?.status === "NOT_READY" /* NOT_READY */ && typeof provider.initialize === "function") { | ||
const wrappedProvider = new ProviderWrapper(provider, this._statusEnumType.NOT_READY, this._statusEnumType); | ||
if (typeof provider.initialize === "function" && !this.allProviders.includes(provider)) { | ||
initializationPromise = provider.initialize?.(domain ? this._domainScopedContext.get(domain) ?? this._context : this._context)?.then(() => { | ||
wrappedProvider.status = this._statusEnumType.READY; | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
emitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
this._apiEmitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
})?.catch((error) => { | ||
if (error?.code === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { | ||
wrappedProvider.status = this._statusEnumType.FATAL; | ||
} else { | ||
wrappedProvider.status = this._statusEnumType.ERROR; | ||
} | ||
this.getAssociatedEventEmitters(domain).forEach((emitter) => { | ||
@@ -451,3 +519,3 @@ emitter?.emit("PROVIDER_ERROR" /* Error */, { | ||
}); | ||
this._events?.emit("PROVIDER_ERROR" /* Error */, { | ||
this._apiEmitter?.emit("PROVIDER_ERROR" /* Error */, { | ||
clientName: domain, | ||
@@ -461,15 +529,18 @@ domain, | ||
} else { | ||
wrappedProvider.status = this._statusEnumType.READY; | ||
emitters.forEach((emitter) => { | ||
emitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
}); | ||
this._events?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
this._apiEmitter?.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); | ||
} | ||
if (domain) { | ||
this._domainScopedProviders.set(domain, provider); | ||
this._domainScopedProviders.set(domain, wrappedProvider); | ||
} else { | ||
this._defaultProvider = provider; | ||
this._defaultProvider = wrappedProvider; | ||
} | ||
this.transferListeners(oldProvider, provider, domain, emitters); | ||
if (![...this._domainScopedProviders.values(), this._defaultProvider].includes(oldProvider)) { | ||
oldProvider?.onClose?.(); | ||
if (!this.allProviders.includes(oldProvider)) { | ||
oldProvider?.onClose?.()?.catch((err) => { | ||
this._logger.error(`error closing provider: ${err?.message}, ${err?.stack}`); | ||
}); | ||
} | ||
@@ -480,5 +551,5 @@ return initializationPromise; | ||
if (!domain) { | ||
return this._defaultProvider; | ||
return this._defaultProvider.provider; | ||
} | ||
return this._domainScopedProviders.get(domain) ?? this._defaultProvider; | ||
return this._domainScopedProviders.get(domain)?.provider ?? this._defaultProvider.provider; | ||
} | ||
@@ -526,3 +597,3 @@ buildAndCacheEventEmitterForClient(domain) { | ||
}); | ||
this._events.emit(eventType, { | ||
this._apiEmitter.emit(eventType, { | ||
...details, | ||
@@ -541,13 +612,13 @@ clientName: domain, | ||
try { | ||
await this?._defaultProvider?.onClose?.(); | ||
await this?._defaultProvider.provider?.onClose?.(); | ||
} catch (err) { | ||
this.handleShutdownError(this._defaultProvider, err); | ||
this.handleShutdownError(this._defaultProvider.provider, err); | ||
} | ||
const providers = Array.from(this._domainScopedProviders); | ||
const wrappers = Array.from(this._domainScopedProviders); | ||
await Promise.all( | ||
providers.map(async ([, provider]) => { | ||
wrappers.map(async ([, wrapper]) => { | ||
try { | ||
await provider.onClose?.(); | ||
await wrapper?.provider.onClose?.(); | ||
} catch (err) { | ||
this.handleShutdownError(provider, err); | ||
this.handleShutdownError(wrapper?.provider, err); | ||
} | ||
@@ -564,5 +635,11 @@ }) | ||
this._domainScopedProviders.clear(); | ||
this._defaultProvider = defaultProvider; | ||
this._defaultProvider = new ProviderWrapper(defaultProvider, this._statusEnumType.NOT_READY, this._statusEnumType); | ||
} | ||
} | ||
get allProviders() { | ||
return [ | ||
...[...this._domainScopedProviders.values()].map((wrappers) => wrappers.provider), | ||
this._defaultProvider.provider | ||
]; | ||
} | ||
handleShutdownError(provider, err) { | ||
@@ -575,3 +652,5 @@ this._logger.error(`Error during shutdown of provider ${provider.metadata.name}: ${err}`); | ||
ClientProviderEvents as AllProviderEvents, | ||
ClientProviderStatus as AllProviderStatus, | ||
ClientProviderEvents, | ||
ClientProviderStatus, | ||
DefaultLogger, | ||
@@ -587,6 +666,8 @@ ErrorCode, | ||
ParseError, | ||
ProviderFatalError, | ||
ProviderNotReadyError, | ||
ProviderStatus, | ||
ProviderWrapper, | ||
SafeLogger, | ||
ServerProviderEvents, | ||
ServerProviderStatus, | ||
StandardResolutionReasons, | ||
@@ -593,0 +674,0 @@ TargetingKeyMissingError, |
@@ -76,2 +76,6 @@ type FlagValueType = 'boolean' | 'string' | 'number' | 'object'; | ||
/** | ||
* The provider has entered an irrecoverable error state. | ||
*/ | ||
PROVIDER_FATAL = "PROVIDER_FATAL", | ||
/** | ||
* The flag could not be found. | ||
@@ -212,2 +216,6 @@ */ | ||
/** | ||
* The context associated with the provider has changed, and the provider has not yet reconciled its associated state. | ||
*/ | ||
Reconciling = "PROVIDER_RECONCILING", | ||
/** | ||
* The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | ||
@@ -220,3 +228,3 @@ */ | ||
* A type representing any possible ProviderEvent (server or client side). | ||
* If you are implementing a hook or provider, you probably want to import `ProviderEvents` from the respective SDK. | ||
* In most cases, you probably want to import `ProviderEvents` from the respective SDK. | ||
*/ | ||
@@ -243,23 +251,38 @@ type AnyProviderEvent = ServerProviderEvents | ClientProviderEvents; | ||
type StaleEvent = CommonEventProps; | ||
type ReconcilingEvent = CommonEventProps & { | ||
errorCode: ErrorCode; | ||
}; | ||
type ConfigChangeEvent = CommonEventProps & { | ||
flagsChanged?: string[]; | ||
}; | ||
type EventMap = { | ||
type ServerEventMap = { | ||
[ServerProviderEvents.Ready]: ReadyEvent; | ||
[ServerProviderEvents.Error]: ErrorEvent; | ||
[ServerProviderEvents.Stale]: StaleEvent; | ||
[ServerProviderEvents.ConfigurationChanged]: ConfigChangeEvent; | ||
}; | ||
type ClientEventMap = { | ||
[ClientProviderEvents.Ready]: ReadyEvent; | ||
[ClientProviderEvents.Error]: ErrorEvent; | ||
[ClientProviderEvents.Stale]: StaleEvent; | ||
[ClientProviderEvents.ConfigurationChanged]: ConfigChangeEvent; | ||
[ClientProviderEvents.Reconciling]: CommonEventProps; | ||
[ClientProviderEvents.ContextChanged]: CommonEventProps; | ||
[ClientProviderEvents.ConfigurationChanged]: ConfigChangeEvent; | ||
}; | ||
type EventContext<U extends Record<string, unknown> = Record<string, unknown>> = EventMap[ClientProviderEvents] & U; | ||
type EventDetails = EventContext & CommonEventDetails; | ||
type EventHandler = (eventDetails?: EventDetails) => Promise<unknown> | unknown; | ||
interface Eventing { | ||
type ServerNotChangeEvents = ServerProviderEvents.Ready | ServerProviderEvents.Error | ServerProviderEvents.Stale; | ||
type ClientNotChangeEvents = ClientProviderEvents.Ready | ClientProviderEvents.Error | ClientProviderEvents.Stale | ClientProviderEvents.ContextChanged | ClientProviderEvents.Reconciling; | ||
type NotChangeEvents = ServerNotChangeEvents | ClientNotChangeEvents; | ||
type EventContext<U extends Record<string, unknown> = Record<string, unknown>, T extends ServerProviderEvents | ClientProviderEvents = ServerProviderEvents | ClientProviderEvents> = (T extends ClientProviderEvents ? ClientEventMap[T] : T extends ServerProviderEvents ? ServerEventMap[T] : never) & U; | ||
type EventDetails<T extends ServerProviderEvents | ClientProviderEvents = ServerProviderEvents | ClientProviderEvents> = EventContext<Record<string, unknown>, T> & CommonEventDetails; | ||
type EventHandler<T extends ServerProviderEvents | ClientProviderEvents = ServerProviderEvents | ClientProviderEvents> = (eventDetails?: EventDetails<T>) => Promise<unknown> | unknown; | ||
interface Eventing<T extends ServerProviderEvents | ClientProviderEvents> { | ||
/** | ||
* Adds a handler for the given provider event type. | ||
* The handlers are called in the order they have been added. | ||
* @param {AnyProviderEvent} eventType The provider event type to listen to | ||
* @param eventType The provider event type to listen to | ||
* @param {EventHandler} handler The handler to run on occurrence of the event type | ||
*/ | ||
addHandler(eventType: AnyProviderEvent, handler: EventHandler): void; | ||
addHandler(eventType: T extends ClientProviderEvents ? ClientProviderEvents.ConfigurationChanged : ServerProviderEvents.ConfigurationChanged, handler: EventHandler<T extends ClientProviderEvents ? ClientProviderEvents.ConfigurationChanged : ServerProviderEvents.ConfigurationChanged>): void; | ||
addHandler(eventType: T extends ClientProviderEvents ? ClientNotChangeEvents : ServerNotChangeEvents, handler: EventHandler<T extends ClientProviderEvents ? ClientNotChangeEvents : ServerNotChangeEvents>): void; | ||
addHandler(eventType: T extends ClientProviderEvents ? ClientProviderEvents : ServerProviderEvents, handler: EventHandler<T extends ClientProviderEvents ? ClientProviderEvents : ServerProviderEvents>): void; | ||
/** | ||
@@ -270,3 +293,3 @@ * Removes a handler for the given provider event type. | ||
*/ | ||
removeHandler(eventType: AnyProviderEvent, handler: EventHandler): void; | ||
removeHandler(eventType: T, handler: EventHandler<T>): void; | ||
/** | ||
@@ -277,3 +300,3 @@ * Gets the current handlers for the given provider event type. | ||
*/ | ||
getHandlers(eventType: AnyProviderEvent): EventHandler[]; | ||
getHandlers(eventType: T): EventHandler<T>[]; | ||
} | ||
@@ -297,6 +320,6 @@ | ||
* @param {AnyProviderEvent} event event to match | ||
* @param {ProviderStatus} status status of provider | ||
* @param {ClientProviderStatus | ServerProviderStatus} status status of provider | ||
* @returns {boolean} boolean indicating if the provider status corresponds to the event. | ||
*/ | ||
declare const statusMatchesEvent: <T extends AnyProviderEvent>(event: T, status?: ProviderStatus) => boolean; | ||
declare const statusMatchesEvent: <T extends AnyProviderEvent>(event: T, status?: ClientProviderStatus | ServerProviderStatus) => boolean; | ||
@@ -355,4 +378,5 @@ /** | ||
* The state of the provider. | ||
* Note that the provider's state is handled by the SDK. | ||
*/ | ||
declare enum ProviderStatus { | ||
declare enum ServerProviderStatus { | ||
/** | ||
@@ -373,5 +397,40 @@ * The provider has not been initialized and cannot yet evaluate flags. | ||
*/ | ||
STALE = "STALE" | ||
STALE = "STALE", | ||
/** | ||
* The provider has entered an irrecoverable error state. | ||
*/ | ||
FATAL = "FATAL" | ||
} | ||
/** | ||
* The state of the provider. | ||
* Note that the provider's state is handled by the SDK. | ||
*/ | ||
declare enum ClientProviderStatus { | ||
/** | ||
* The provider has not been initialized and cannot yet evaluate flags. | ||
*/ | ||
NOT_READY = "NOT_READY", | ||
/** | ||
* The provider is ready to resolve flags. | ||
*/ | ||
READY = "READY", | ||
/** | ||
* The provider is in an error state and unable to evaluate flags. | ||
*/ | ||
ERROR = "ERROR", | ||
/** | ||
* The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | ||
*/ | ||
STALE = "STALE", | ||
/** | ||
* The provider has entered an irrecoverable error state. | ||
*/ | ||
FATAL = "FATAL", | ||
/** | ||
* The provider is reconciling its state with a context change. | ||
*/ | ||
RECONCILING = "RECONCILING" | ||
} | ||
/** | ||
* Static data about the provider. | ||
@@ -382,3 +441,3 @@ */ | ||
} | ||
interface CommonProvider { | ||
interface CommonProvider<S extends ClientProviderStatus | ServerProviderStatus> { | ||
readonly metadata: ProviderMetadata; | ||
@@ -391,10 +450,8 @@ /** | ||
/** | ||
* @deprecated the SDK now maintains the provider's state; there's no need for providers to implement this field. | ||
* Returns a representation of the current readiness of the provider. | ||
* If the provider needs to be initialized, it should return {@link ProviderStatus.READY}. | ||
* If the provider is in an error state, it should return {@link ProviderStatus.ERROR}. | ||
* If the provider is functioning normally, it should return {@link ProviderStatus.NOT_READY}. | ||
* | ||
* _Providers which do not implement this method are assumed to be ready immediately._ | ||
*/ | ||
readonly status?: ProviderStatus; | ||
readonly status?: S; | ||
/** | ||
@@ -412,3 +469,3 @@ * An event emitter for ProviderEvents. | ||
* A function used to setup the provider. | ||
* Called by the SDK after the provider is set if the provider's status is {@link ProviderStatus.NOT_READY}. | ||
* Called by the SDK after the provider is set if the provider's status is NOT_READY. | ||
* When the returned promise resolves, the SDK fires the ProviderEvents.Ready event. | ||
@@ -542,2 +599,7 @@ * If the returned promise rejects, the SDK fires the ProviderEvents.Error event. | ||
declare class ProviderFatalError extends OpenFeatureError { | ||
code: ErrorCode; | ||
constructor(message?: string, options?: ErrorOptions); | ||
} | ||
/** | ||
@@ -568,6 +630,26 @@ * Checks whether the parameter is a string. | ||
declare abstract class OpenFeatureCommonAPI<P extends CommonProvider = CommonProvider, H extends BaseHook = BaseHook> implements Eventing, EvaluationLifeCycle<OpenFeatureCommonAPI<P>>, ManageLogger<OpenFeatureCommonAPI<P>> { | ||
type AnyProviderStatus = ClientProviderStatus | ServerProviderStatus; | ||
/** | ||
* A provider and its current status. | ||
* For internal use only. | ||
*/ | ||
declare class ProviderWrapper<P extends CommonProvider<AnyProviderStatus>, S extends AnyProviderStatus> { | ||
private _provider; | ||
private _status; | ||
private _pendingContextChanges; | ||
constructor(_provider: P, _status: S, _statusEnumType: typeof ClientProviderStatus | typeof ServerProviderStatus); | ||
get provider(): P; | ||
set provider(provider: P); | ||
get status(): S; | ||
set status(status: S); | ||
get allContextChangesSettled(): boolean; | ||
incrementPendingContextChanges(): void; | ||
decrementPendingContextChanges(): void; | ||
} | ||
declare abstract class OpenFeatureCommonAPI<S extends AnyProviderStatus, P extends CommonProvider<S> = CommonProvider<S>, H extends BaseHook = BaseHook> implements Eventing<AnyProviderEvent>, EvaluationLifeCycle<OpenFeatureCommonAPI<S, P>>, ManageLogger<OpenFeatureCommonAPI<S, P>> { | ||
protected abstract readonly _statusEnumType: typeof ClientProviderStatus | typeof ServerProviderStatus; | ||
protected abstract _createEventEmitter(): GenericEventEmitter<AnyProviderEvent>; | ||
protected abstract _defaultProvider: P; | ||
protected abstract readonly _events: GenericEventEmitter<AnyProviderEvent>; | ||
protected abstract _defaultProvider: ProviderWrapper<P, AnyProviderStatus>; | ||
protected abstract readonly _domainScopedProviders: Map<string, ProviderWrapper<P, AnyProviderStatus>>; | ||
protected abstract readonly _apiEmitter: GenericEventEmitter<AnyProviderEvent>; | ||
protected _hooks: H[]; | ||
@@ -577,3 +659,2 @@ protected _context: EvaluationContext; | ||
private readonly _clientEventHandlers; | ||
protected _domainScopedProviders: Map<string, P>; | ||
protected _domainScopedContext: Map<string, EvaluationContext>; | ||
@@ -670,5 +751,6 @@ protected _clientEvents: Map<string | undefined, GenericEventEmitter<AnyProviderEvent>>; | ||
protected clearProvidersAndSetDefault(defaultProvider: P): Promise<void>; | ||
private get allProviders(); | ||
private handleShutdownError; | ||
} | ||
export { ClientProviderEvents as AllProviderEvents, AnyProviderEvent, BaseHook, BeforeHookContext, ClientMetadata, ClientProviderEvents, CommonEventDetails, CommonProvider, ConfigChangeEvent, DefaultLogger, ErrorCode, ErrorEvent, EvaluationContext, EvaluationContextValue, EvaluationDetails, EvaluationLifeCycle, EventContext, EventDetails, EventHandler, EventMetadata, Eventing, FlagMetadata, FlagNotFoundError, FlagValue, FlagValueType, GeneralError, GenericEventEmitter, HookContext, HookHints, InvalidContextError, JsonArray, JsonObject, JsonValue, LOG_LEVELS, Logger, ManageContext, ManageLogger, Metadata, OpenFeatureCommonAPI, OpenFeatureError, Paradigm, ParseError, PrimitiveValue, ProviderEventEmitter, ProviderMetadata, ProviderNotReadyError, ProviderStatus, ReadyEvent, ResolutionDetails, ResolutionReason, SafeLogger, ServerProviderEvents, StaleEvent, StandardResolutionReasons, TargetingKeyMissingError, TypeMismatchError, isObject, isString, objectOrUndefined, statusMatchesEvent, stringOrUndefined }; | ||
export { ClientProviderEvents as AllProviderEvents, ClientProviderStatus as AllProviderStatus, AnyProviderEvent, BaseHook, BeforeHookContext, ClientMetadata, ClientProviderEvents, ClientProviderStatus, CommonEventDetails, CommonProvider, ConfigChangeEvent, DefaultLogger, ErrorCode, ErrorEvent, EvaluationContext, EvaluationContextValue, EvaluationDetails, EvaluationLifeCycle, EventContext, EventDetails, EventHandler, EventMetadata, Eventing, FlagMetadata, FlagNotFoundError, FlagValue, FlagValueType, GeneralError, GenericEventEmitter, HookContext, HookHints, InvalidContextError, JsonArray, JsonObject, JsonValue, LOG_LEVELS, Logger, ManageContext, ManageLogger, Metadata, NotChangeEvents, OpenFeatureCommonAPI, OpenFeatureError, Paradigm, ParseError, PrimitiveValue, ProviderEventEmitter, ProviderFatalError, ProviderMetadata, ProviderNotReadyError, ProviderWrapper, ReadyEvent, ReconcilingEvent, ResolutionDetails, ResolutionReason, SafeLogger, ServerProviderEvents, ServerProviderStatus, StaleEvent, StandardResolutionReasons, TargetingKeyMissingError, TypeMismatchError, isObject, isString, objectOrUndefined, statusMatchesEvent, stringOrUndefined }; |
{ | ||
"name": "@openfeature/core", | ||
"version": "0.0.26", | ||
"version": "0.0.27", | ||
"description": "Shared OpenFeature JS components (server and web)", | ||
@@ -5,0 +5,0 @@ "main": "./dist/cjs/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
200164
2024