@openfeature/server-sdk
Advanced tools
+54
-267
@@ -61,7 +61,4 @@ "use strict"; | ||
| __export(index_exports, { | ||
| AggregateError: () => AggregateError, | ||
| AsyncLocalStorageTransactionContextPropagator: () => AsyncLocalStorageTransactionContextPropagator, | ||
| BaseEvaluationStrategy: () => BaseEvaluationStrategy, | ||
| ComparisonStrategy: () => ComparisonStrategy, | ||
| ErrorWithCode: () => ErrorWithCode, | ||
| FirstMatchStrategy: () => FirstMatchStrategy, | ||
@@ -77,5 +74,3 @@ FirstSuccessfulStrategy: () => FirstSuccessfulStrategy, | ||
| ProviderEvents: () => import_core6.ServerProviderEvents, | ||
| ProviderStatus: () => import_core.ServerProviderStatus, | ||
| constructAggregateError: () => constructAggregateError, | ||
| throwAggregateErrorFromPromiseResults: () => throwAggregateErrorFromPromiseResults | ||
| ProviderStatus: () => import_core.ServerProviderStatus | ||
| }); | ||
@@ -209,8 +204,26 @@ module.exports = __toCommonJS(index_exports); | ||
| // src/provider/multi-provider/multi-provider.ts | ||
| var import_core9 = require("@openfeature/core"); | ||
| var import_core7 = require("@openfeature/core"); | ||
| // src/provider/multi-provider/strategies.ts | ||
| var import_core4 = require("@openfeature/core"); | ||
| var FirstMatchStrategy = class extends import_core4.BaseFirstMatchStrategy { | ||
| constructor() { | ||
| super(import_core.ServerProviderStatus); | ||
| } | ||
| }; | ||
| var FirstSuccessfulStrategy = class extends import_core4.BaseFirstSuccessfulStrategy { | ||
| constructor() { | ||
| super(import_core.ServerProviderStatus); | ||
| } | ||
| }; | ||
| var ComparisonStrategy = class extends import_core4.BaseComparisonStrategy { | ||
| constructor(fallbackProvider, onMismatch) { | ||
| super(import_core.ServerProviderStatus, fallbackProvider, onMismatch); | ||
| } | ||
| }; | ||
| // src/events/open-feature-event-emitter.ts | ||
| var import_core4 = require("@openfeature/core"); | ||
| var import_core5 = require("@openfeature/core"); | ||
| var import_node_events = require("node:events"); | ||
| var OpenFeatureEventEmitter = class extends import_core4.GenericEventEmitter { | ||
| var OpenFeatureEventEmitter = class extends import_core5.GenericEventEmitter { | ||
| constructor() { | ||
@@ -226,36 +239,4 @@ super(); | ||
| // src/provider/multi-provider/errors.ts | ||
| var import_core5 = require("@openfeature/core"); | ||
| var ErrorWithCode = class extends import_core5.OpenFeatureError { | ||
| constructor(code, message) { | ||
| super(message); | ||
| this.code = code; | ||
| } | ||
| }; | ||
| var AggregateError = class extends import_core5.GeneralError { | ||
| constructor(message, originalErrors) { | ||
| super(message); | ||
| this.originalErrors = originalErrors; | ||
| } | ||
| }; | ||
| var constructAggregateError = (providerErrors) => { | ||
| const errorsWithSource = providerErrors.map(({ providerName, error }) => { | ||
| return { source: providerName, error }; | ||
| }).flat(); | ||
| return new AggregateError( | ||
| `Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`, | ||
| errorsWithSource | ||
| ); | ||
| }; | ||
| var throwAggregateErrorFromPromiseResults = (result, providerEntries) => { | ||
| const errors = result.map((r, i) => { | ||
| if (r.status === "rejected") { | ||
| return { error: r.reason, providerName: providerEntries[i].name }; | ||
| } | ||
| return null; | ||
| }).filter((val) => Boolean(val)); | ||
| if (errors.length) { | ||
| throw constructAggregateError(errors); | ||
| } | ||
| }; | ||
| // src/events/events.ts | ||
| var import_core6 = require("@openfeature/core"); | ||
@@ -319,192 +300,5 @@ // src/provider/multi-provider/hook-executor.ts | ||
| // src/events/events.ts | ||
| var import_core6 = require("@openfeature/core"); | ||
| // src/provider/multi-provider/status-tracker.ts | ||
| var StatusTracker = class { | ||
| constructor(events) { | ||
| this.events = events; | ||
| this.providerStatuses = {}; | ||
| } | ||
| wrapEventHandler(providerEntry) { | ||
| var _a, _b, _c, _d; | ||
| const provider = providerEntry.provider; | ||
| (_a = provider.events) == null ? void 0 : _a.addHandler(import_core6.ServerProviderEvents.Error, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.ERROR, details); | ||
| }); | ||
| (_b = provider.events) == null ? void 0 : _b.addHandler(import_core6.ServerProviderEvents.Stale, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.STALE, details); | ||
| }); | ||
| (_c = provider.events) == null ? void 0 : _c.addHandler(import_core6.ServerProviderEvents.ConfigurationChanged, (details) => { | ||
| this.events.emit(import_core6.ServerProviderEvents.ConfigurationChanged, details); | ||
| }); | ||
| (_d = provider.events) == null ? void 0 : _d.addHandler(import_core6.ServerProviderEvents.Ready, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, import_core.ServerProviderStatus.READY, details); | ||
| }); | ||
| } | ||
| providerStatus(name) { | ||
| return this.providerStatuses[name]; | ||
| } | ||
| getStatusFromProviderStatuses() { | ||
| const statuses = Object.values(this.providerStatuses); | ||
| if (statuses.includes(import_core.ServerProviderStatus.FATAL)) { | ||
| return import_core.ServerProviderStatus.FATAL; | ||
| } else if (statuses.includes(import_core.ServerProviderStatus.NOT_READY)) { | ||
| return import_core.ServerProviderStatus.NOT_READY; | ||
| } else if (statuses.includes(import_core.ServerProviderStatus.ERROR)) { | ||
| return import_core.ServerProviderStatus.ERROR; | ||
| } else if (statuses.includes(import_core.ServerProviderStatus.STALE)) { | ||
| return import_core.ServerProviderStatus.STALE; | ||
| } | ||
| return import_core.ServerProviderStatus.READY; | ||
| } | ||
| changeProviderStatus(name, status, details) { | ||
| const currentStatus = this.getStatusFromProviderStatuses(); | ||
| this.providerStatuses[name] = status; | ||
| const newStatus = this.getStatusFromProviderStatuses(); | ||
| if (currentStatus !== newStatus) { | ||
| if (newStatus === import_core.ServerProviderStatus.FATAL || newStatus === import_core.ServerProviderStatus.ERROR) { | ||
| this.events.emit(import_core6.ServerProviderEvents.Error, details); | ||
| } else if (newStatus === import_core.ServerProviderStatus.STALE) { | ||
| this.events.emit(import_core6.ServerProviderEvents.Stale, details); | ||
| } else if (newStatus === import_core.ServerProviderStatus.READY) { | ||
| this.events.emit(import_core6.ServerProviderEvents.Ready, details); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/base-evaluation-strategy.ts | ||
| var BaseEvaluationStrategy = class { | ||
| constructor() { | ||
| this.runMode = "sequential"; | ||
| } | ||
| shouldEvaluateThisProvider(strategyContext, _evalContext) { | ||
| if (strategyContext.providerStatus === import_core.ServerProviderStatus.NOT_READY || strategyContext.providerStatus === import_core.ServerProviderStatus.FATAL) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| shouldEvaluateNextProvider(_strategyContext, _context, _result) { | ||
| return true; | ||
| } | ||
| shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) { | ||
| if (strategyContext.providerStatus === import_core.ServerProviderStatus.NOT_READY || strategyContext.providerStatus === import_core.ServerProviderStatus.FATAL) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| hasError(resolution) { | ||
| return "thrownError" in resolution || !!resolution.details.errorCode; | ||
| } | ||
| hasErrorWithCode(resolution, code) { | ||
| var _a; | ||
| return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code; | ||
| } | ||
| collectProviderErrors(resolutions) { | ||
| var _a; | ||
| const errors = []; | ||
| for (const resolution of resolutions) { | ||
| if ("thrownError" in resolution) { | ||
| errors.push({ providerName: resolution.providerName, error: resolution.thrownError }); | ||
| } else if (resolution.details.errorCode) { | ||
| errors.push({ | ||
| providerName: resolution.providerName, | ||
| error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error") | ||
| }); | ||
| } | ||
| } | ||
| return { errors }; | ||
| } | ||
| resolutionToFinalResult(resolution) { | ||
| return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName }; | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/first-match-strategy.ts | ||
| var import_core7 = require("@openfeature/core"); | ||
| var FirstMatchStrategy = class extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider(strategyContext, context, result) { | ||
| if (this.hasErrorWithCode(result, import_core7.ErrorCode.FLAG_NOT_FOUND)) { | ||
| return true; | ||
| } | ||
| if (this.hasError(result)) { | ||
| return false; | ||
| } | ||
| return false; | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| const finalResolution = resolutions[resolutions.length - 1]; | ||
| if (this.hasError(finalResolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/first-successful-strategy.ts | ||
| var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider(strategyContext, context, result) { | ||
| return this.hasError(result); | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| const finalResolution = resolutions[resolutions.length - 1]; | ||
| if (this.hasError(finalResolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/comparison-strategy.ts | ||
| var import_core8 = require("@openfeature/core"); | ||
| var ComparisonStrategy = class extends BaseEvaluationStrategy { | ||
| constructor(fallbackProvider, onMismatch) { | ||
| super(); | ||
| this.fallbackProvider = fallbackProvider; | ||
| this.onMismatch = onMismatch; | ||
| this.runMode = "parallel"; | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| var _a; | ||
| let value; | ||
| let fallbackResolution; | ||
| let finalResolution; | ||
| let mismatch = false; | ||
| for (const [i, resolution] of resolutions.entries()) { | ||
| if (this.hasError(resolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| if (resolution.provider === this.fallbackProvider) { | ||
| fallbackResolution = resolution; | ||
| } | ||
| if (i === 0) { | ||
| finalResolution = resolution; | ||
| } | ||
| if (typeof value !== "undefined" && value !== resolution.details.value) { | ||
| mismatch = true; | ||
| } else { | ||
| value = resolution.details.value; | ||
| } | ||
| } | ||
| if (!fallbackResolution) { | ||
| throw new import_core8.GeneralError("Fallback provider not found in resolution results"); | ||
| } | ||
| if (!finalResolution) { | ||
| throw new import_core8.GeneralError("Final resolution not found in resolution results"); | ||
| } | ||
| if (mismatch) { | ||
| (_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions); | ||
| return { | ||
| details: fallbackResolution.details, | ||
| provider: fallbackResolution.provider | ||
| }; | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/multi-provider.ts | ||
| var MultiProvider = class _MultiProvider { | ||
| constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new import_core9.DefaultLogger()) { | ||
| constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new import_core7.DefaultLogger()) { | ||
| this.constructorProviders = constructorProviders; | ||
@@ -519,3 +313,3 @@ this.evaluationStrategy = evaluationStrategy; | ||
| this.providerEntriesByName = {}; | ||
| this.statusTracker = new StatusTracker(this.events); | ||
| this.statusTracker = new import_core7.StatusTracker(this.events, import_core.ServerProviderStatus, import_core6.ServerProviderEvents); | ||
| this.hookExecutor = new HookExecutor(this.logger); | ||
@@ -562,3 +356,3 @@ this.registerProviders(constructorProviders); | ||
| ); | ||
| throwAggregateErrorFromPromiseResults(result, this.providerEntries); | ||
| (0, import_core7.throwAggregateErrorFromPromiseResults)(result, this.providerEntries); | ||
| }); | ||
@@ -572,3 +366,3 @@ } | ||
| })); | ||
| throwAggregateErrorFromPromiseResults(result, this.providerEntries); | ||
| (0, import_core7.throwAggregateErrorFromPromiseResults)(result, this.providerEntries); | ||
| }); | ||
@@ -594,3 +388,3 @@ } | ||
| if (!hookContext || !hookHints) { | ||
| throw new import_core9.GeneralError("Hook context not available for evaluation"); | ||
| throw new import_core7.GeneralError("Hook context not available for evaluation"); | ||
| } | ||
@@ -620,6 +414,6 @@ const tasks = []; | ||
| if ((_a = finalResult.errors) == null ? void 0 : _a.length) { | ||
| throw constructAggregateError(finalResult.errors); | ||
| throw (0, import_core7.constructAggregateError)(finalResult.errors); | ||
| } | ||
| if (!finalResult.details) { | ||
| throw new import_core9.GeneralError("No result was returned from any provider"); | ||
| throw new import_core7.GeneralError("No result was returned from any provider"); | ||
| } | ||
@@ -703,3 +497,3 @@ return finalResult.details; | ||
| default: | ||
| throw new import_core9.GeneralError("Invalid flag evaluation type"); | ||
| throw new import_core7.GeneralError("Invalid flag evaluation type"); | ||
| } | ||
@@ -749,3 +543,3 @@ }); | ||
| const errorMessage = err == null ? void 0 : err.message; | ||
| const errorCode = (err == null ? void 0 : err.code) || import_core9.ErrorCode.GENERAL; | ||
| const errorCode = (err == null ? void 0 : err.code) || import_core7.ErrorCode.GENERAL; | ||
| return { | ||
@@ -755,3 +549,3 @@ errorCode, | ||
| value: defaultValue, | ||
| reason: import_core9.StandardResolutionReasons.ERROR, | ||
| reason: import_core7.StandardResolutionReasons.ERROR, | ||
| flagMetadata: Object.freeze(flagMetadata), | ||
@@ -764,6 +558,6 @@ flagKey | ||
| // src/open-feature.ts | ||
| var import_core11 = require("@openfeature/core"); | ||
| var import_core9 = require("@openfeature/core"); | ||
| // src/client/internal/open-feature-client.ts | ||
| var import_core10 = require("@openfeature/core"); | ||
| var import_core8 = require("@openfeature/core"); | ||
| var OpenFeatureClient = class { | ||
@@ -798,3 +592,3 @@ constructor(providerAccessor, providerStatusAccessor, emitterAccessor, apiContextAccessor, apiHooksAccessor, transactionContextAccessor, globalLogger, options, context = {}) { | ||
| this.emitterAccessor().addHandler(eventType, handler); | ||
| const shouldRunNow = (0, import_core10.statusMatchesEvent)(eventType, this._providerStatus); | ||
| const shouldRunNow = (0, import_core8.statusMatchesEvent)(eventType, this._providerStatus); | ||
| if (shouldRunNow) { | ||
@@ -824,3 +618,3 @@ try { | ||
| setLogger(logger) { | ||
| this._clientLogger = new import_core10.SafeLogger(logger); | ||
| this._clientLogger = new import_core8.SafeLogger(logger); | ||
| return this; | ||
@@ -935,3 +729,3 @@ } | ||
| logger: this._logger, | ||
| hookData: new import_core10.MapHookData() | ||
| hookData: new import_core8.MapHookData() | ||
| }) | ||
@@ -949,3 +743,3 @@ ); | ||
| if (resolutionDetails.errorCode) { | ||
| const err = (0, import_core10.instantiateErrorByErrorCode)(resolutionDetails.errorCode, resolutionDetails.errorMessage); | ||
| const err = (0, import_core8.instantiateErrorByErrorCode)(resolutionDetails.errorCode, resolutionDetails.errorMessage); | ||
| yield this.errorHooks(allHooksReversed, hookContexts, err, options); | ||
@@ -976,5 +770,3 @@ evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, err, resolutionDetails.flagMetadata); | ||
| Object.assign(accumulatedContext, hookResult); | ||
| for (let i = 0; i < hooks.length; i++) { | ||
| Object.assign(hookContexts[hookContextIndex].context, accumulatedContext); | ||
| } | ||
| Object.assign(hookContext.context, accumulatedContext); | ||
| } | ||
@@ -1042,5 +834,5 @@ } | ||
| if (this.providerStatus === import_core.ServerProviderStatus.NOT_READY) { | ||
| throw new import_core10.ProviderNotReadyError("provider has not yet initialized"); | ||
| throw new import_core8.ProviderNotReadyError("provider has not yet initialized"); | ||
| } else if (this.providerStatus === import_core.ServerProviderStatus.FATAL) { | ||
| throw new import_core10.ProviderFatalError("provider is in an irrecoverable error state"); | ||
| throw new import_core8.ProviderFatalError("provider is in an irrecoverable error state"); | ||
| } | ||
@@ -1050,3 +842,3 @@ } | ||
| const errorMessage = err == null ? void 0 : err.message; | ||
| const errorCode = (err == null ? void 0 : err.code) || import_core10.ErrorCode.GENERAL; | ||
| const errorCode = (err == null ? void 0 : err.code) || import_core8.ErrorCode.GENERAL; | ||
| return { | ||
@@ -1056,3 +848,3 @@ errorCode, | ||
| value: defaultValue, | ||
| reason: import_core10.StandardResolutionReasons.ERROR, | ||
| reason: import_core8.StandardResolutionReasons.ERROR, | ||
| flagMetadata: Object.freeze(flagMetadata), | ||
@@ -1093,3 +885,3 @@ flagKey | ||
| var _globalThis = globalThis; | ||
| var OpenFeatureAPI = class _OpenFeatureAPI extends import_core11.OpenFeatureCommonAPI { | ||
| var OpenFeatureAPI = class _OpenFeatureAPI extends import_core9.OpenFeatureCommonAPI { | ||
| constructor() { | ||
@@ -1099,3 +891,3 @@ super("server"); | ||
| this._apiEmitter = new OpenFeatureEventEmitter(); | ||
| this._defaultProvider = new import_core11.ProviderWrapper( | ||
| this._defaultProvider = new import_core9.ProviderWrapper( | ||
| NOOP_PROVIDER, | ||
@@ -1132,4 +924,4 @@ import_core.ServerProviderStatus.NOT_READY, | ||
| return __async(this, null, function* () { | ||
| const domain = (0, import_core11.stringOrUndefined)(domainOrProvider); | ||
| const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(domainOrProvider); | ||
| const domain = (0, import_core9.stringOrUndefined)(domainOrProvider); | ||
| const provider = domain ? (0, import_core9.objectOrUndefined)(providerOrUndefined) : (0, import_core9.objectOrUndefined)(domainOrProvider); | ||
| yield this.setAwaitableProvider(domain, provider); | ||
@@ -1139,4 +931,4 @@ }); | ||
| setProvider(clientOrProvider, providerOrUndefined) { | ||
| const domain = (0, import_core11.stringOrUndefined)(clientOrProvider); | ||
| const provider = domain ? (0, import_core11.objectOrUndefined)(providerOrUndefined) : (0, import_core11.objectOrUndefined)(clientOrProvider); | ||
| const domain = (0, import_core9.stringOrUndefined)(clientOrProvider); | ||
| const provider = domain ? (0, import_core9.objectOrUndefined)(providerOrUndefined) : (0, import_core9.objectOrUndefined)(clientOrProvider); | ||
| const maybePromise = this.setAwaitableProvider(domain, provider); | ||
@@ -1160,5 +952,5 @@ Promise.resolve(maybePromise).catch((err) => { | ||
| var _a, _b; | ||
| const domain = (0, import_core11.stringOrUndefined)(domainOrContext); | ||
| const version = (0, import_core11.stringOrUndefined)(versionOrContext); | ||
| const context = (_b = (_a = (0, import_core11.objectOrUndefined)(domainOrContext)) != null ? _a : (0, import_core11.objectOrUndefined)(versionOrContext)) != null ? _b : (0, import_core11.objectOrUndefined)(contextOrUndefined); | ||
| const domain = (0, import_core9.stringOrUndefined)(domainOrContext); | ||
| const version = (0, import_core9.stringOrUndefined)(versionOrContext); | ||
| const context = (_b = (_a = (0, import_core9.objectOrUndefined)(domainOrContext)) != null ? _a : (0, import_core9.objectOrUndefined)(versionOrContext)) != null ? _b : (0, import_core9.objectOrUndefined)(contextOrUndefined); | ||
| return new OpenFeatureClient( | ||
@@ -1214,7 +1006,4 @@ () => this.getProviderForClient(domain), | ||
| 0 && (module.exports = { | ||
| AggregateError, | ||
| AsyncLocalStorageTransactionContextPropagator, | ||
| BaseEvaluationStrategy, | ||
| ComparisonStrategy, | ||
| ErrorWithCode, | ||
| FirstMatchStrategy, | ||
@@ -1231,6 +1020,4 @@ FirstSuccessfulStrategy, | ||
| ProviderStatus, | ||
| constructAggregateError, | ||
| throwAggregateErrorFromPromiseResults, | ||
| ...require("@openfeature/core") | ||
| }); | ||
| //# sourceMappingURL=index.js.map |
+44
-247
@@ -42,5 +42,3 @@ var __defProp = Object.defineProperty; | ||
| // src/provider/provider.ts | ||
| import { | ||
| ServerProviderStatus | ||
| } from "@openfeature/core"; | ||
| import { ServerProviderStatus } from "@openfeature/core"; | ||
@@ -175,4 +173,34 @@ // src/provider/no-op-provider.ts | ||
| // src/provider/multi-provider/multi-provider.ts | ||
| import { DefaultLogger, ErrorCode as ErrorCode3, GeneralError as GeneralError4, StandardResolutionReasons as StandardResolutionReasons2 } from "@openfeature/core"; | ||
| import { | ||
| DefaultLogger, | ||
| ErrorCode as ErrorCode2, | ||
| GeneralError as GeneralError2, | ||
| StandardResolutionReasons as StandardResolutionReasons2, | ||
| constructAggregateError, | ||
| throwAggregateErrorFromPromiseResults, | ||
| StatusTracker | ||
| } from "@openfeature/core"; | ||
| // src/provider/multi-provider/strategies.ts | ||
| import { | ||
| BaseFirstMatchStrategy, | ||
| BaseFirstSuccessfulStrategy, | ||
| BaseComparisonStrategy | ||
| } from "@openfeature/core"; | ||
| var FirstMatchStrategy = class extends BaseFirstMatchStrategy { | ||
| constructor() { | ||
| super(ServerProviderStatus); | ||
| } | ||
| }; | ||
| var FirstSuccessfulStrategy = class extends BaseFirstSuccessfulStrategy { | ||
| constructor() { | ||
| super(ServerProviderStatus); | ||
| } | ||
| }; | ||
| var ComparisonStrategy = class extends BaseComparisonStrategy { | ||
| constructor(fallbackProvider, onMismatch) { | ||
| super(ServerProviderStatus, fallbackProvider, onMismatch); | ||
| } | ||
| }; | ||
| // src/events/open-feature-event-emitter.ts | ||
@@ -192,36 +220,4 @@ import { GenericEventEmitter } from "@openfeature/core"; | ||
| // src/provider/multi-provider/errors.ts | ||
| import { GeneralError as GeneralError2, OpenFeatureError as OpenFeatureError3 } from "@openfeature/core"; | ||
| var ErrorWithCode = class extends OpenFeatureError3 { | ||
| constructor(code, message) { | ||
| super(message); | ||
| this.code = code; | ||
| } | ||
| }; | ||
| var AggregateError = class extends GeneralError2 { | ||
| constructor(message, originalErrors) { | ||
| super(message); | ||
| this.originalErrors = originalErrors; | ||
| } | ||
| }; | ||
| var constructAggregateError = (providerErrors) => { | ||
| const errorsWithSource = providerErrors.map(({ providerName, error }) => { | ||
| return { source: providerName, error }; | ||
| }).flat(); | ||
| return new AggregateError( | ||
| `Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`, | ||
| errorsWithSource | ||
| ); | ||
| }; | ||
| var throwAggregateErrorFromPromiseResults = (result, providerEntries) => { | ||
| const errors = result.map((r, i) => { | ||
| if (r.status === "rejected") { | ||
| return { error: r.reason, providerName: providerEntries[i].name }; | ||
| } | ||
| return null; | ||
| }).filter((val) => Boolean(val)); | ||
| if (errors.length) { | ||
| throw constructAggregateError(errors); | ||
| } | ||
| }; | ||
| // src/events/events.ts | ||
| import { ServerProviderEvents } from "@openfeature/core"; | ||
@@ -285,189 +281,2 @@ // src/provider/multi-provider/hook-executor.ts | ||
| // src/events/events.ts | ||
| import { ServerProviderEvents } from "@openfeature/core"; | ||
| // src/provider/multi-provider/status-tracker.ts | ||
| var StatusTracker = class { | ||
| constructor(events) { | ||
| this.events = events; | ||
| this.providerStatuses = {}; | ||
| } | ||
| wrapEventHandler(providerEntry) { | ||
| var _a, _b, _c, _d; | ||
| const provider = providerEntry.provider; | ||
| (_a = provider.events) == null ? void 0 : _a.addHandler(ServerProviderEvents.Error, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, ServerProviderStatus.ERROR, details); | ||
| }); | ||
| (_b = provider.events) == null ? void 0 : _b.addHandler(ServerProviderEvents.Stale, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, ServerProviderStatus.STALE, details); | ||
| }); | ||
| (_c = provider.events) == null ? void 0 : _c.addHandler(ServerProviderEvents.ConfigurationChanged, (details) => { | ||
| this.events.emit(ServerProviderEvents.ConfigurationChanged, details); | ||
| }); | ||
| (_d = provider.events) == null ? void 0 : _d.addHandler(ServerProviderEvents.Ready, (details) => { | ||
| this.changeProviderStatus(providerEntry.name, ServerProviderStatus.READY, details); | ||
| }); | ||
| } | ||
| providerStatus(name) { | ||
| return this.providerStatuses[name]; | ||
| } | ||
| getStatusFromProviderStatuses() { | ||
| const statuses = Object.values(this.providerStatuses); | ||
| if (statuses.includes(ServerProviderStatus.FATAL)) { | ||
| return ServerProviderStatus.FATAL; | ||
| } else if (statuses.includes(ServerProviderStatus.NOT_READY)) { | ||
| return ServerProviderStatus.NOT_READY; | ||
| } else if (statuses.includes(ServerProviderStatus.ERROR)) { | ||
| return ServerProviderStatus.ERROR; | ||
| } else if (statuses.includes(ServerProviderStatus.STALE)) { | ||
| return ServerProviderStatus.STALE; | ||
| } | ||
| return ServerProviderStatus.READY; | ||
| } | ||
| changeProviderStatus(name, status, details) { | ||
| const currentStatus = this.getStatusFromProviderStatuses(); | ||
| this.providerStatuses[name] = status; | ||
| const newStatus = this.getStatusFromProviderStatuses(); | ||
| if (currentStatus !== newStatus) { | ||
| if (newStatus === ServerProviderStatus.FATAL || newStatus === ServerProviderStatus.ERROR) { | ||
| this.events.emit(ServerProviderEvents.Error, details); | ||
| } else if (newStatus === ServerProviderStatus.STALE) { | ||
| this.events.emit(ServerProviderEvents.Stale, details); | ||
| } else if (newStatus === ServerProviderStatus.READY) { | ||
| this.events.emit(ServerProviderEvents.Ready, details); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/base-evaluation-strategy.ts | ||
| var BaseEvaluationStrategy = class { | ||
| constructor() { | ||
| this.runMode = "sequential"; | ||
| } | ||
| shouldEvaluateThisProvider(strategyContext, _evalContext) { | ||
| if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| shouldEvaluateNextProvider(_strategyContext, _context, _result) { | ||
| return true; | ||
| } | ||
| shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) { | ||
| if (strategyContext.providerStatus === ServerProviderStatus.NOT_READY || strategyContext.providerStatus === ServerProviderStatus.FATAL) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| hasError(resolution) { | ||
| return "thrownError" in resolution || !!resolution.details.errorCode; | ||
| } | ||
| hasErrorWithCode(resolution, code) { | ||
| var _a; | ||
| return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code; | ||
| } | ||
| collectProviderErrors(resolutions) { | ||
| var _a; | ||
| const errors = []; | ||
| for (const resolution of resolutions) { | ||
| if ("thrownError" in resolution) { | ||
| errors.push({ providerName: resolution.providerName, error: resolution.thrownError }); | ||
| } else if (resolution.details.errorCode) { | ||
| errors.push({ | ||
| providerName: resolution.providerName, | ||
| error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error") | ||
| }); | ||
| } | ||
| } | ||
| return { errors }; | ||
| } | ||
| resolutionToFinalResult(resolution) { | ||
| return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName }; | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/first-match-strategy.ts | ||
| import { ErrorCode as ErrorCode2 } from "@openfeature/core"; | ||
| var FirstMatchStrategy = class extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider(strategyContext, context, result) { | ||
| if (this.hasErrorWithCode(result, ErrorCode2.FLAG_NOT_FOUND)) { | ||
| return true; | ||
| } | ||
| if (this.hasError(result)) { | ||
| return false; | ||
| } | ||
| return false; | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| const finalResolution = resolutions[resolutions.length - 1]; | ||
| if (this.hasError(finalResolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/first-successful-strategy.ts | ||
| var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider(strategyContext, context, result) { | ||
| return this.hasError(result); | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| const finalResolution = resolutions[resolutions.length - 1]; | ||
| if (this.hasError(finalResolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/strategies/comparison-strategy.ts | ||
| import { GeneralError as GeneralError3 } from "@openfeature/core"; | ||
| var ComparisonStrategy = class extends BaseEvaluationStrategy { | ||
| constructor(fallbackProvider, onMismatch) { | ||
| super(); | ||
| this.fallbackProvider = fallbackProvider; | ||
| this.onMismatch = onMismatch; | ||
| this.runMode = "parallel"; | ||
| } | ||
| determineFinalResult(strategyContext, context, resolutions) { | ||
| var _a; | ||
| let value; | ||
| let fallbackResolution; | ||
| let finalResolution; | ||
| let mismatch = false; | ||
| for (const [i, resolution] of resolutions.entries()) { | ||
| if (this.hasError(resolution)) { | ||
| return this.collectProviderErrors(resolutions); | ||
| } | ||
| if (resolution.provider === this.fallbackProvider) { | ||
| fallbackResolution = resolution; | ||
| } | ||
| if (i === 0) { | ||
| finalResolution = resolution; | ||
| } | ||
| if (typeof value !== "undefined" && value !== resolution.details.value) { | ||
| mismatch = true; | ||
| } else { | ||
| value = resolution.details.value; | ||
| } | ||
| } | ||
| if (!fallbackResolution) { | ||
| throw new GeneralError3("Fallback provider not found in resolution results"); | ||
| } | ||
| if (!finalResolution) { | ||
| throw new GeneralError3("Final resolution not found in resolution results"); | ||
| } | ||
| if (mismatch) { | ||
| (_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions); | ||
| return { | ||
| details: fallbackResolution.details, | ||
| provider: fallbackResolution.provider | ||
| }; | ||
| } | ||
| return this.resolutionToFinalResult(finalResolution); | ||
| } | ||
| }; | ||
| // src/provider/multi-provider/multi-provider.ts | ||
@@ -485,3 +294,3 @@ var MultiProvider = class _MultiProvider { | ||
| this.providerEntriesByName = {}; | ||
| this.statusTracker = new StatusTracker(this.events); | ||
| this.statusTracker = new StatusTracker(this.events, ServerProviderStatus, ServerProviderEvents); | ||
| this.hookExecutor = new HookExecutor(this.logger); | ||
@@ -558,3 +367,3 @@ this.registerProviders(constructorProviders); | ||
| if (!hookContext || !hookHints) { | ||
| throw new GeneralError4("Hook context not available for evaluation"); | ||
| throw new GeneralError2("Hook context not available for evaluation"); | ||
| } | ||
@@ -587,3 +396,3 @@ const tasks = []; | ||
| if (!finalResult.details) { | ||
| throw new GeneralError4("No result was returned from any provider"); | ||
| throw new GeneralError2("No result was returned from any provider"); | ||
| } | ||
@@ -667,3 +476,3 @@ return finalResult.details; | ||
| default: | ||
| throw new GeneralError4("Invalid flag evaluation type"); | ||
| throw new GeneralError2("Invalid flag evaluation type"); | ||
| } | ||
@@ -713,3 +522,3 @@ }); | ||
| const errorMessage = err == null ? void 0 : err.message; | ||
| const errorCode = (err == null ? void 0 : err.code) || ErrorCode3.GENERAL; | ||
| const errorCode = (err == null ? void 0 : err.code) || ErrorCode2.GENERAL; | ||
| return { | ||
@@ -727,12 +536,7 @@ errorCode, | ||
| // src/open-feature.ts | ||
| import { | ||
| OpenFeatureCommonAPI, | ||
| ProviderWrapper, | ||
| objectOrUndefined, | ||
| stringOrUndefined | ||
| } from "@openfeature/core"; | ||
| import { OpenFeatureCommonAPI, ProviderWrapper, objectOrUndefined, stringOrUndefined } from "@openfeature/core"; | ||
| // src/client/internal/open-feature-client.ts | ||
| import { | ||
| ErrorCode as ErrorCode4, | ||
| ErrorCode as ErrorCode3, | ||
| ProviderFatalError, | ||
@@ -949,5 +753,3 @@ ProviderNotReadyError, | ||
| Object.assign(accumulatedContext, hookResult); | ||
| for (let i = 0; i < hooks.length; i++) { | ||
| Object.assign(hookContexts[hookContextIndex].context, accumulatedContext); | ||
| } | ||
| Object.assign(hookContext.context, accumulatedContext); | ||
| } | ||
@@ -1022,3 +824,3 @@ } | ||
| const errorMessage = err == null ? void 0 : err.message; | ||
| const errorCode = (err == null ? void 0 : err.code) || ErrorCode4.GENERAL; | ||
| const errorCode = (err == null ? void 0 : err.code) || ErrorCode3.GENERAL; | ||
| return { | ||
@@ -1179,7 +981,4 @@ errorCode, | ||
| export { | ||
| AggregateError, | ||
| AsyncLocalStorageTransactionContextPropagator, | ||
| BaseEvaluationStrategy, | ||
| ComparisonStrategy, | ||
| ErrorWithCode, | ||
| FirstMatchStrategy, | ||
@@ -1195,6 +994,4 @@ FirstSuccessfulStrategy, | ||
| ServerProviderEvents as ProviderEvents, | ||
| ServerProviderStatus as ProviderStatus, | ||
| constructAggregateError, | ||
| throwAggregateErrorFromPromiseResults | ||
| ServerProviderStatus as ProviderStatus | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
+44
-117
@@ -1,4 +0,4 @@ | ||
| import { BaseHook, FlagValue, EvaluationContext, HookHints, EvaluationDetails, JsonValue, CommonProvider, ServerProviderStatus, Logger, ResolutionDetails, GenericEventEmitter, ServerProviderEvents, FlagValueType, TrackingEventDetails, ErrorCode, ProviderMetadata, OpenFeatureError, GeneralError, EvaluationLifeCycle, ManageContext, ManageLogger, Eventing, ClientMetadata, OpenFeatureCommonAPI, ProviderWrapper } from '@openfeature/core'; | ||
| import { BaseHook, FlagValue, EvaluationContext, HookHints, EvaluationDetails, JsonValue, CommonProvider, ServerProviderStatus, Logger, ResolutionDetails, GenericEventEmitter, ServerProviderEvents, ProviderEntryInput, ProviderMetadata, RegisteredProvider, BaseEvaluationStrategy, TrackingEventDetails, BaseStrategyProviderContext, BaseStrategyPerProviderContext, BaseProviderResolutionResult, BaseProviderResolutionSuccessResult, BaseProviderResolutionErrorResult, BaseFinalResult, BaseFirstMatchStrategy, BaseFirstSuccessfulStrategy, BaseComparisonStrategy, EvaluationLifeCycle, ManageContext, ManageLogger, Eventing, ClientMetadata, OpenFeatureCommonAPI, ProviderWrapper } from '@openfeature/core'; | ||
| export * from '@openfeature/core'; | ||
| export { ServerProviderEvents as ProviderEvents, ServerProviderStatus as ProviderStatus } from '@openfeature/core'; | ||
| export { ServerProviderEvents as ProviderEvents, ServerProviderStatus as ProviderStatus, StrategyEvaluationContext } from '@openfeature/core'; | ||
| import { EventEmitter } from 'node:events'; | ||
@@ -216,97 +216,4 @@ | ||
| type StrategyEvaluationContext = { | ||
| flagKey: string; | ||
| flagType: FlagValueType; | ||
| }; | ||
| type StrategyProviderContext = { | ||
| provider: Provider; | ||
| providerName: string; | ||
| providerStatus: ServerProviderStatus; | ||
| }; | ||
| type StrategyPerProviderContext = StrategyEvaluationContext & StrategyProviderContext; | ||
| type ProviderResolutionResultBase = { | ||
| provider: Provider; | ||
| providerName: string; | ||
| }; | ||
| type ProviderResolutionSuccessResult<T extends FlagValue> = ProviderResolutionResultBase & { | ||
| details: ResolutionDetails<T>; | ||
| }; | ||
| type ProviderResolutionErrorResult = ProviderResolutionResultBase & { | ||
| thrownError: unknown; | ||
| }; | ||
| type ProviderResolutionResult<T extends FlagValue> = ProviderResolutionSuccessResult<T> | ProviderResolutionErrorResult; | ||
| type FinalResult<T extends FlagValue> = { | ||
| details?: ResolutionDetails<T>; | ||
| provider?: Provider; | ||
| providerName?: string; | ||
| errors?: { | ||
| providerName: string; | ||
| error: unknown; | ||
| }[]; | ||
| }; | ||
| /** | ||
| * Base strategy to inherit from. Not directly usable, as strategies must implement the "determineResult" method | ||
| * Contains default implementations for `shouldEvaluateThisProvider` and `shouldEvaluateNextProvider` | ||
| */ | ||
| declare abstract class BaseEvaluationStrategy { | ||
| runMode: 'parallel' | 'sequential'; | ||
| shouldEvaluateThisProvider(strategyContext: StrategyPerProviderContext, _evalContext?: EvaluationContext): boolean; | ||
| shouldEvaluateNextProvider<T extends FlagValue>(_strategyContext?: StrategyPerProviderContext, _context?: EvaluationContext, _result?: ProviderResolutionResult<T>): boolean; | ||
| shouldTrackWithThisProvider(strategyContext: StrategyProviderContext, _context?: EvaluationContext, _trackingEventName?: string, _trackingEventDetails?: TrackingEventDetails): boolean; | ||
| abstract determineFinalResult<T extends FlagValue>(strategyContext: StrategyEvaluationContext, context: EvaluationContext, resolutions: ProviderResolutionResult<T>[]): FinalResult<T>; | ||
| protected hasError(resolution: ProviderResolutionResult<FlagValue>): resolution is ProviderResolutionErrorResult | (ProviderResolutionSuccessResult<FlagValue> & { | ||
| details: ResolutionDetails<FlagValue> & { | ||
| errorCode: ErrorCode; | ||
| }; | ||
| }); | ||
| protected hasErrorWithCode(resolution: ProviderResolutionResult<FlagValue>, code: ErrorCode): boolean; | ||
| protected collectProviderErrors<T extends FlagValue>(resolutions: ProviderResolutionResult<T>[]): FinalResult<T>; | ||
| protected resolutionToFinalResult<T extends FlagValue>(resolution: ProviderResolutionSuccessResult<T>): { | ||
| details: ResolutionDetails<T>; | ||
| provider: Provider; | ||
| providerName: string; | ||
| }; | ||
| } | ||
| /** | ||
| * Return the first result that did not indicate "flag not found". | ||
| * If any provider in the course of evaluation returns or throws an error, throw that error | ||
| */ | ||
| declare class FirstMatchStrategy extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider<T extends FlagValue>(strategyContext: StrategyPerProviderContext, context: EvaluationContext, result: ProviderResolutionResult<T>): boolean; | ||
| determineFinalResult<T extends FlagValue>(strategyContext: StrategyPerProviderContext, context: EvaluationContext, resolutions: ProviderResolutionResult<T>[]): FinalResult<T>; | ||
| } | ||
| /** | ||
| * Return the first result that did NOT result in an error | ||
| * If any provider in the course of evaluation returns or throws an error, ignore it as long as there is a successful result | ||
| * If there is no successful result, throw all errors | ||
| */ | ||
| declare class FirstSuccessfulStrategy extends BaseEvaluationStrategy { | ||
| shouldEvaluateNextProvider<T extends FlagValue>(strategyContext: StrategyPerProviderContext, context: EvaluationContext, result: ProviderResolutionResult<T>): boolean; | ||
| determineFinalResult<T extends FlagValue>(strategyContext: StrategyPerProviderContext, context: EvaluationContext, resolutions: ProviderResolutionResult<T>[]): FinalResult<T>; | ||
| } | ||
| /** | ||
| * Evaluate all providers in parallel and compare the results. | ||
| * If the values agree, return the value | ||
| * If the values disagree, return the value from the configured "fallback provider" and execute the "onMismatch" | ||
| * callback if defined | ||
| */ | ||
| declare class ComparisonStrategy extends BaseEvaluationStrategy { | ||
| private fallbackProvider; | ||
| private onMismatch?; | ||
| runMode: "parallel"; | ||
| constructor(fallbackProvider: Provider, onMismatch?: ((resolutions: ProviderResolutionResult<FlagValue>[]) => void) | undefined); | ||
| determineFinalResult<T extends FlagValue>(strategyContext: StrategyPerProviderContext, context: EvaluationContext, resolutions: ProviderResolutionResult<T>[]): FinalResult<T>; | ||
| } | ||
| type ProviderEntryInput = { | ||
| provider: Provider; | ||
| name?: string; | ||
| }; | ||
| type RegisteredProvider = Required<ProviderEntryInput>; | ||
| declare class MultiProvider implements Provider { | ||
| readonly constructorProviders: ProviderEntryInput[]; | ||
| readonly constructorProviders: ProviderEntryInput<Provider>[]; | ||
| private readonly evaluationStrategy; | ||
@@ -319,7 +226,7 @@ private readonly logger; | ||
| metadata: ProviderMetadata; | ||
| providerEntries: RegisteredProvider[]; | ||
| providerEntries: RegisteredProvider<Provider>[]; | ||
| private providerEntriesByName; | ||
| private hookExecutor; | ||
| private statusTracker; | ||
| constructor(constructorProviders: ProviderEntryInput[], evaluationStrategy?: BaseEvaluationStrategy, logger?: Logger); | ||
| constructor(constructorProviders: ProviderEntryInput<Provider>[], evaluationStrategy?: BaseEvaluationStrategy<ServerProviderStatus, Provider>, logger?: Logger); | ||
| private registerProviders; | ||
@@ -341,21 +248,41 @@ initialize(context?: EvaluationContext): Promise<void>; | ||
| declare class ErrorWithCode extends OpenFeatureError { | ||
| code: ErrorCode; | ||
| constructor(code: ErrorCode, message: string); | ||
| /** | ||
| * Pre-configured strategy exports for the server SDK. | ||
| * These classes extend the base strategies from @openfeature/core with the | ||
| * server-specific ProviderStatus enum already bound. | ||
| */ | ||
| /** | ||
| * Pre-bound type aliases for server SDK. | ||
| * These types have the server-specific ProviderStatus and Provider types already applied, | ||
| * providing backward compatibility for existing consumers. | ||
| */ | ||
| type StrategyProviderContext = BaseStrategyProviderContext<ServerProviderStatus, Provider>; | ||
| type StrategyPerProviderContext = BaseStrategyPerProviderContext<ServerProviderStatus, Provider>; | ||
| type ProviderResolutionResult<T extends FlagValue> = BaseProviderResolutionResult<T, ServerProviderStatus, Provider>; | ||
| type ProviderResolutionSuccessResult<T extends FlagValue> = BaseProviderResolutionSuccessResult<T, ServerProviderStatus, Provider>; | ||
| type ProviderResolutionErrorResult = BaseProviderResolutionErrorResult<ServerProviderStatus, Provider>; | ||
| type FinalResult<T extends FlagValue> = BaseFinalResult<T, ServerProviderStatus, Provider>; | ||
| /** | ||
| * Evaluates providers in order and returns the first successful result. | ||
| * Providers that return FLAG_NOT_FOUND are skipped. Any other error stops evaluation. | ||
| */ | ||
| declare class FirstMatchStrategy extends BaseFirstMatchStrategy<ServerProviderStatus, Provider> { | ||
| constructor(); | ||
| } | ||
| declare class AggregateError extends GeneralError { | ||
| originalErrors: { | ||
| source: string; | ||
| error: unknown; | ||
| }[]; | ||
| constructor(message: string, originalErrors: { | ||
| source: string; | ||
| error: unknown; | ||
| }[]); | ||
| /** | ||
| * Evaluates providers in order and returns the first successful result. | ||
| * Any error causes that provider to be skipped. | ||
| */ | ||
| declare class FirstSuccessfulStrategy extends BaseFirstSuccessfulStrategy<ServerProviderStatus, Provider> { | ||
| constructor(); | ||
| } | ||
| declare const constructAggregateError: (providerErrors: { | ||
| error: unknown; | ||
| providerName: string; | ||
| }[]) => AggregateError; | ||
| declare const throwAggregateErrorFromPromiseResults: (result: PromiseSettledResult<unknown>[], providerEntries: RegisteredProvider[]) => void; | ||
| /** | ||
| * Evaluates all providers in parallel and compares results. | ||
| * If all providers agree, returns that result. Otherwise, returns the fallback provider's result. | ||
| */ | ||
| declare class ComparisonStrategy extends BaseComparisonStrategy<ServerProviderStatus, Provider> { | ||
| constructor(fallbackProvider: Provider, onMismatch?: (resolutions: ProviderResolutionResult<FlagValue>[]) => void); | ||
| } | ||
@@ -566,3 +493,3 @@ interface Tracking { | ||
| export { AggregateError, AsyncLocalStorageTransactionContextPropagator, BaseEvaluationStrategy, ComparisonStrategy, ErrorWithCode, FirstMatchStrategy, FirstSuccessfulStrategy, InMemoryProvider, MultiProvider, NOOP_PROVIDER, NOOP_TRANSACTION_CONTEXT_PROPAGATOR, OpenFeature, OpenFeatureAPI, OpenFeatureEventEmitter, constructAggregateError, throwAggregateErrorFromPromiseResults }; | ||
| export type { Client, Features, FinalResult, FlagEvaluationOptions, Hook, ManageTransactionContextPropagator, Provider, ProviderResolutionErrorResult, ProviderResolutionResult, ProviderResolutionSuccessResult, StrategyEvaluationContext, StrategyPerProviderContext, StrategyProviderContext, Tracking, TransactionContext, TransactionContextPropagator }; | ||
| export { AsyncLocalStorageTransactionContextPropagator, ComparisonStrategy, FirstMatchStrategy, FirstSuccessfulStrategy, InMemoryProvider, MultiProvider, NOOP_PROVIDER, NOOP_TRANSACTION_CONTEXT_PROPAGATOR, OpenFeature, OpenFeatureAPI, OpenFeatureEventEmitter }; | ||
| export type { Client, Features, FinalResult, FlagEvaluationOptions, Hook, ManageTransactionContextPropagator, Provider, ProviderResolutionErrorResult, ProviderResolutionResult, ProviderResolutionSuccessResult, StrategyPerProviderContext, StrategyProviderContext, Tracking, TransactionContext, TransactionContextPropagator }; |
+3
-3
| { | ||
| "name": "@openfeature/server-sdk", | ||
| "version": "1.20.1", | ||
| "version": "1.20.2", | ||
| "description": "OpenFeature SDK for JavaScript", | ||
@@ -51,7 +51,7 @@ "main": "./dist/cjs/index.js", | ||
| "peerDependencies": { | ||
| "@openfeature/core": "^1.9.0" | ||
| "@openfeature/core": "^1.9.2" | ||
| }, | ||
| "devDependencies": { | ||
| "@openfeature/core": "^1.9.0" | ||
| "@openfeature/core": "^1.9.2" | ||
| } | ||
| } |
+63
-49
@@ -19,4 +19,4 @@ <!-- markdownlint-disable MD033 --> | ||
| <!-- x-release-please-start-version --> | ||
| <a href="https://github.com/open-feature/js-sdk/releases/tag/server-sdk-v1.20.1"> | ||
| <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.20.1&color=blue&style=for-the-badge" /> | ||
| <a href="https://github.com/open-feature/js-sdk/releases/tag/server-sdk-v1.20.2"> | ||
| <img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.20.2&color=blue&style=for-the-badge" /> | ||
| </a> | ||
@@ -92,3 +92,3 @@ <!-- x-release-please-end --> | ||
| if (v2Enabled) { | ||
| console.log("v2 is enabled"); | ||
| console.log('v2 is enabled'); | ||
| } | ||
@@ -103,15 +103,15 @@ ``` | ||
| | Status | Features | Description | | ||
| | ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. | | ||
| | ✅ | [Targeting](#targeting) | 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. | | ||
| | ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) | | ||
| | ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations, particularly for A/B testing. | | ||
| | ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | | ||
| | ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. | | ||
| | ✅ | [Multi-Provider](#multi-provider) | Combine multiple providers with configurable evaluation strategies. | | ||
| | Status | Features | Description | | ||
| | ------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| | ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. | | ||
| | ✅ | [Targeting](#targeting) | 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. | | ||
| | ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread) | | ||
| | ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations, particularly for A/B testing. | | ||
| | ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. | | ||
| | ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. | | ||
| | ✅ | [Multi-Provider](#multi-provider) | Combine multiple providers with configurable evaluation strategies. | | ||
@@ -168,6 +168,3 @@ <sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub> | ||
| // Create multi-provider with a strategy | ||
| const multiProvider = new MultiProvider( | ||
| [primaryProvider, backupProvider], | ||
| new FirstMatchStrategy() | ||
| ); | ||
| const multiProvider = new MultiProvider([primaryProvider, backupProvider], new FirstMatchStrategy()); | ||
@@ -198,3 +195,3 @@ // Register the multi-provider | ||
| [newProvider, oldProvider], // New provider is consulted first | ||
| new FirstMatchStrategy() | ||
| new FirstMatchStrategy(), | ||
| ); | ||
@@ -215,9 +212,6 @@ | ||
| const multiProvider = new MultiProvider( | ||
| [ | ||
| { provider: providerA }, | ||
| { provider: providerB } | ||
| ], | ||
| [{ provider: providerA }, { provider: providerB }], | ||
| new ComparisonStrategy(providerA, (resolutions) => { | ||
| console.warn('Mismatch detected', resolutions); | ||
| }) | ||
| }), | ||
| ); | ||
@@ -236,3 +230,3 @@ | ||
| // set a value to the global context | ||
| OpenFeature.setContext({ region: "us-east-1" }); | ||
| OpenFeature.setContext({ region: 'us-east-1' }); | ||
@@ -247,3 +241,3 @@ // set a value to the client context | ||
| email: req.session.email, | ||
| product: req.productId | ||
| product: req.productId, | ||
| }; | ||
@@ -266,3 +260,3 @@ | ||
| ```ts | ||
| import { OpenFeature } from "@openfeature/server-sdk"; | ||
| import { OpenFeature } from '@openfeature/server-sdk'; | ||
@@ -277,3 +271,3 @@ // add a hook globally, to run on all evaluations | ||
| // add a hook for this evaluation only | ||
| const boolValue = await client.getBooleanValue("bool-flag", false, { hooks: [new ExampleHook()]}); | ||
| const boolValue = await client.getBooleanValue('bool-flag', false, { hooks: [new ExampleHook()] }); | ||
| ``` | ||
@@ -288,3 +282,3 @@ | ||
| ```ts | ||
| import type { Logger } from "@openfeature/server-sdk"; | ||
| import type { Logger } from '@openfeature/server-sdk'; | ||
@@ -309,13 +303,13 @@ // The logger can be anything that conforms with the Logger interface | ||
| ```ts | ||
| import { OpenFeature, InMemoryProvider } from "@openfeature/server-sdk"; | ||
| import { OpenFeature, InMemoryProvider } from '@openfeature/server-sdk'; | ||
| const myFlags = { | ||
| 'v2_enabled': { | ||
| v2_enabled: { | ||
| variants: { | ||
| on: true, | ||
| off: false | ||
| off: false, | ||
| }, | ||
| disabled: false, | ||
| defaultVariant: "on" | ||
| } | ||
| defaultVariant: 'on', | ||
| }, | ||
| }; | ||
@@ -326,3 +320,3 @@ | ||
| // Registering a provider to a domain | ||
| OpenFeature.setProvider("my-domain", new InMemoryProvider(someOtherFlags)); | ||
| OpenFeature.setProvider('my-domain', new InMemoryProvider(someOtherFlags)); | ||
@@ -332,3 +326,3 @@ // A Client bound to the default provider | ||
| // A Client bound to the InMemoryProvider provider | ||
| const domainScopedClient = OpenFeature.getClient("my-domain"); | ||
| const domainScopedClient = OpenFeature.getClient('my-domain'); | ||
| ``` | ||
@@ -370,6 +364,6 @@ | ||
| ```ts | ||
| import express, { Request, Response, NextFunction } from "express"; | ||
| import express, { Request, Response, NextFunction } from 'express'; | ||
| import { OpenFeature, AsyncLocalStorageTransactionContextPropagator } from '@openfeature/server-sdk'; | ||
| OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContextPropagator()) | ||
| OpenFeature.setTransactionContextPropagator(new AsyncLocalStorageTransactionContextPropagator()); | ||
@@ -381,3 +375,3 @@ /** | ||
| app.use((req: Request, res: Response, next: NextFunction) => { | ||
| const ip = res.headers.get("X-Forwarded-For") | ||
| const ip = res.headers.get('X-Forwarded-For'); | ||
| OpenFeature.setTransactionContext({ targetingKey: req.user.id, ipAddress: ip }, () => { | ||
@@ -387,3 +381,3 @@ // The transaction context is used in any flag evaluation throughout the whole call chain of next | ||
| }); | ||
| }) | ||
| }); | ||
| ``` | ||
@@ -414,3 +408,3 @@ | ||
| await OpenFeature.close() | ||
| await OpenFeature.close(); | ||
| ``` | ||
@@ -435,3 +429,3 @@ | ||
| ProviderEventEmitter, | ||
| ResolutionDetails | ||
| ResolutionDetails, | ||
| } from '@openfeature/server-sdk'; | ||
@@ -448,12 +442,32 @@ | ||
| hooks?: Hook[]; | ||
| resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>> { | ||
| resolveBooleanEvaluation( | ||
| flagKey: string, | ||
| defaultValue: boolean, | ||
| context: EvaluationContext, | ||
| logger: Logger, | ||
| ): Promise<ResolutionDetails<boolean>> { | ||
| // code to evaluate a boolean | ||
| } | ||
| resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>> { | ||
| resolveStringEvaluation( | ||
| flagKey: string, | ||
| defaultValue: string, | ||
| context: EvaluationContext, | ||
| logger: Logger, | ||
| ): Promise<ResolutionDetails<string>> { | ||
| // code to evaluate a string | ||
| } | ||
| resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>> { | ||
| resolveNumberEvaluation( | ||
| flagKey: string, | ||
| defaultValue: number, | ||
| context: EvaluationContext, | ||
| logger: Logger, | ||
| ): Promise<ResolutionDetails<number>> { | ||
| // code to evaluate a number | ||
| } | ||
| resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>> { | ||
| resolveObjectEvaluation<T extends JsonValue>( | ||
| flagKey: string, | ||
| defaultValue: T, | ||
| context: EvaluationContext, | ||
| logger: Logger, | ||
| ): Promise<ResolutionDetails<T>> { | ||
| // code to evaluate an object | ||
@@ -483,3 +497,3 @@ } | ||
| ```ts | ||
| import type { Hook, HookContext, EvaluationDetails, FlagValue } from "@openfeature/server-sdk"; | ||
| import type { Hook, HookContext, EvaluationDetails, FlagValue } from '@openfeature/server-sdk'; | ||
@@ -486,0 +500,0 @@ export class MyHook implements Hook { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
495
2.91%288629
-14.9%2417
-16.45%