UNPKG

@openfeature/web-sdk

Version:
1,290 lines (1,258 loc) 91.7 kB
"use strict"; var OpenFeature = (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __reflectGet = Reflect.get; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __superGet = (cls, obj, key) => __reflectGet(__getProtoOf(cls), key, obj); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // ../../node_modules/eventemitter3/index.js var require_eventemitter3 = __commonJS({ "../../node_modules/eventemitter3/index.js"(exports, module) { "use strict"; var has = Object.prototype.hasOwnProperty; var prefix = "~"; function Events() { } if (Object.create) { Events.prototype = /* @__PURE__ */ Object.create(null); if (!new Events().__proto__) prefix = false; } function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } function addListener(emitter, event, fn, context, once) { if (typeof fn !== "function") { throw new TypeError("The listener must be a function"); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } function EventEmitter2() { this._events = new Events(); this._eventsCount = 0; } EventEmitter2.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; EventEmitter2.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; EventEmitter2.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, void 0, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; EventEmitter2.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; EventEmitter2.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) { events.push(listeners[i]); } } if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener; EventEmitter2.prototype.addListener = EventEmitter2.prototype.on; EventEmitter2.prefixed = prefix; EventEmitter2.EventEmitter = EventEmitter2; if ("undefined" !== typeof module) { module.exports = EventEmitter2; } } }); // src/index.ts var index_exports = {}; __export(index_exports, { AggregateError: () => AggregateError, AllProviderEvents: () => ClientProviderEvents, AllProviderStatus: () => ClientProviderStatus, BaseComparisonStrategy: () => ComparisonStrategy, BaseEvaluationStrategy: () => BaseEvaluationStrategy, BaseFirstMatchStrategy: () => FirstMatchStrategy, BaseFirstSuccessfulStrategy: () => FirstSuccessfulStrategy, ClientProviderEvents: () => ClientProviderEvents, ClientProviderStatus: () => ClientProviderStatus, ComparisonStrategy: () => ComparisonStrategy2, DefaultLogger: () => DefaultLogger, ErrorCode: () => ErrorCode, ErrorWithCode: () => ErrorWithCode, FirstMatchStrategy: () => FirstMatchStrategy2, FirstSuccessfulStrategy: () => FirstSuccessfulStrategy2, FlagNotFoundError: () => FlagNotFoundError, GeneralError: () => GeneralError, GenericEventEmitter: () => GenericEventEmitter, InMemoryProvider: () => InMemoryProvider, InvalidContextError: () => InvalidContextError, LOG_LEVELS: () => LOG_LEVELS, MapHookData: () => MapHookData, MultiProvider: () => MultiProvider, NOOP_PROVIDER: () => NOOP_PROVIDER, OpenFeature: () => OpenFeature, OpenFeatureAPI: () => OpenFeatureAPI, OpenFeatureCommonAPI: () => OpenFeatureCommonAPI, OpenFeatureError: () => OpenFeatureError, OpenFeatureEventEmitter: () => OpenFeatureEventEmitter, ParseError: () => ParseError, ProviderEvents: () => ClientProviderEvents, ProviderFatalError: () => ProviderFatalError, ProviderNotReadyError: () => ProviderNotReadyError, ProviderStatus: () => ClientProviderStatus, ProviderWrapper: () => ProviderWrapper, SafeLogger: () => SafeLogger, ServerProviderEvents: () => ServerProviderEvents, ServerProviderStatus: () => ServerProviderStatus, StandardResolutionReasons: () => StandardResolutionReasons, StatusTracker: () => StatusTracker, TargetingKeyMissingError: () => TargetingKeyMissingError, TelemetryAttribute: () => TelemetryAttribute, TelemetryFlagMetadata: () => TelemetryFlagMetadata, TypeMismatchError: () => TypeMismatchError, TypedInMemoryProvider: () => TypedInMemoryProvider, constructAggregateError: () => constructAggregateError, createEvaluationEvent: () => createEvaluationEvent, instantiateErrorByErrorCode: () => instantiateErrorByErrorCode, isObject: () => isObject, isString: () => isString, objectOrUndefined: () => objectOrUndefined, statusMatchesEvent: () => statusMatchesEvent, stringOrUndefined: () => stringOrUndefined, throwAggregateErrorFromPromiseResults: () => throwAggregateErrorFromPromiseResults }); // ../shared/src/hooks/hook-data.ts var MapHookData = class { constructor() { this.data = /* @__PURE__ */ new Map(); } set(key, value) { this.data.set(key, value); } get(key) { return this.data.get(key); } has(key) { return this.data.has(key); } delete(key) { return this.data.delete(key); } clear() { this.data.clear(); } }; // ../shared/src/evaluation/evaluation.ts var StandardResolutionReasons = { /** * The resolved value is static (no dynamic evaluation). */ STATIC: "STATIC", /** * The resolved value was configured statically, or otherwise fell back to a pre-configured value. */ DEFAULT: "DEFAULT", /** * The resolved value was the result of a dynamic evaluation, such as a rule or specific user-targeting. */ TARGETING_MATCH: "TARGETING_MATCH", /** * The resolved value was the result of pseudorandom assignment. */ SPLIT: "SPLIT", /** * The resolved value was retrieved from cache. */ CACHED: "CACHED", /** * The resolved value was the result of the flag being disabled in the management system. */ DISABLED: "DISABLED", /** * The reason for the resolved value could not be determined. */ UNKNOWN: "UNKNOWN", /** * The resolved value is non-authoritative or possibly out of date. */ STALE: "STALE", /** * The resolved value was the result of an error. * * Note: The `errorCode` and `errorMessage` fields may contain additional details of this error. */ ERROR: "ERROR" }; var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => { ErrorCode2["PROVIDER_NOT_READY"] = "PROVIDER_NOT_READY"; ErrorCode2["PROVIDER_FATAL"] = "PROVIDER_FATAL"; ErrorCode2["FLAG_NOT_FOUND"] = "FLAG_NOT_FOUND"; ErrorCode2["PARSE_ERROR"] = "PARSE_ERROR"; ErrorCode2["TYPE_MISMATCH"] = "TYPE_MISMATCH"; ErrorCode2["TARGETING_KEY_MISSING"] = "TARGETING_KEY_MISSING"; ErrorCode2["INVALID_CONTEXT"] = "INVALID_CONTEXT"; ErrorCode2["GENERAL"] = "GENERAL"; return ErrorCode2; })(ErrorCode || {}); // ../shared/src/errors/open-feature-error-abstract.ts var OpenFeatureError = class _OpenFeatureError extends Error { constructor(message, options) { super(message); Object.setPrototypeOf(this, _OpenFeatureError.prototype); this.name = "OpenFeatureError"; this.cause = options == null ? void 0 : options.cause; } }; // ../shared/src/errors/flag-not-found-error.ts var FlagNotFoundError = class _FlagNotFoundError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _FlagNotFoundError.prototype); this.name = "FlagNotFoundError"; this.code = "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */; } }; // ../shared/src/errors/general-error.ts var GeneralError = class _GeneralError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _GeneralError.prototype); this.name = "GeneralError"; this.code = "GENERAL" /* GENERAL */; } }; // ../shared/src/errors/invalid-context-error.ts var InvalidContextError = class _InvalidContextError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _InvalidContextError.prototype); this.name = "InvalidContextError"; this.code = "INVALID_CONTEXT" /* INVALID_CONTEXT */; } }; // ../shared/src/errors/parse-error.ts var ParseError = class _ParseError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _ParseError.prototype); this.name = "ParseError"; this.code = "PARSE_ERROR" /* PARSE_ERROR */; } }; // ../shared/src/errors/provider-fatal-error.ts var ProviderFatalError = class _ProviderFatalError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _ProviderFatalError.prototype); this.name = "ProviderFatalError"; this.code = "PROVIDER_FATAL" /* PROVIDER_FATAL */; } }; // ../shared/src/errors/provider-not-ready-error.ts var ProviderNotReadyError = class _ProviderNotReadyError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _ProviderNotReadyError.prototype); this.name = "ProviderNotReadyError"; this.code = "PROVIDER_NOT_READY" /* PROVIDER_NOT_READY */; } }; // ../shared/src/errors/targeting-key-missing-error.ts var TargetingKeyMissingError = class _TargetingKeyMissingError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _TargetingKeyMissingError.prototype); this.name = "TargetingKeyMissingError"; this.code = "TARGETING_KEY_MISSING" /* TARGETING_KEY_MISSING */; } }; // ../shared/src/errors/type-mismatch-error.ts var TypeMismatchError = class _TypeMismatchError extends OpenFeatureError { constructor(message, options) { super(message, options); Object.setPrototypeOf(this, _TypeMismatchError.prototype); this.name = "TypeMismatchError"; this.code = "TYPE_MISMATCH" /* TYPE_MISMATCH */; } }; // ../shared/src/errors/index.ts var instantiateErrorByErrorCode = (errorCode, message) => { switch (errorCode) { case "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */: return new FlagNotFoundError(message); case "PARSE_ERROR" /* PARSE_ERROR */: return new ParseError(message); case "TYPE_MISMATCH" /* TYPE_MISMATCH */: return new TypeMismatchError(message); case "TARGETING_KEY_MISSING" /* TARGETING_KEY_MISSING */: return new TargetingKeyMissingError(message); case "INVALID_CONTEXT" /* INVALID_CONTEXT */: return new InvalidContextError(message); case "PROVIDER_NOT_READY" /* PROVIDER_NOT_READY */: return new ProviderNotReadyError(message); case "PROVIDER_FATAL" /* PROVIDER_FATAL */: return new ProviderFatalError(message); default: return new GeneralError(message); } }; // ../shared/src/provider/provider.ts var ServerProviderStatus = /* @__PURE__ */ ((ServerProviderStatus2) => { ServerProviderStatus2["NOT_READY"] = "NOT_READY"; ServerProviderStatus2["READY"] = "READY"; ServerProviderStatus2["ERROR"] = "ERROR"; ServerProviderStatus2["STALE"] = "STALE"; ServerProviderStatus2["FATAL"] = "FATAL"; return ServerProviderStatus2; })(ServerProviderStatus || {}); var ClientProviderStatus = /* @__PURE__ */ ((ClientProviderStatus2) => { ClientProviderStatus2["NOT_READY"] = "NOT_READY"; ClientProviderStatus2["READY"] = "READY"; ClientProviderStatus2["ERROR"] = "ERROR"; ClientProviderStatus2["STALE"] = "STALE"; ClientProviderStatus2["FATAL"] = "FATAL"; ClientProviderStatus2["RECONCILING"] = "RECONCILING"; return ClientProviderStatus2; })(ClientProviderStatus || {}); // ../shared/src/provider/multi-provider/errors.ts var ErrorWithCode = class extends OpenFeatureError { constructor(code, message) { super(message); this.code = code; } }; var AggregateError = class _AggregateError extends GeneralError { constructor(message, originalErrors) { super(message); this.originalErrors = originalErrors; Object.setPrototypeOf(this, _AggregateError.prototype); this.name = "AggregateError"; } }; var constructAggregateError = (providerErrors) => { const errorsWithSource = providerErrors.map(({ providerName, error }) => { return { source: providerName, error }; }).flat(); const firstError = errorsWithSource[0]; const message = firstError ? `Provider errors occurred: ${firstError.source}: ${firstError.error}` : "Provider errors occurred"; return new AggregateError(message, 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); } }; // ../shared/src/provider/multi-provider/status-tracker.ts var StatusTracker = class { constructor(events, statusEnum, eventEnum) { this.events = events; this.statusEnum = statusEnum; this.eventEnum = eventEnum; this.providerStatuses = {}; } wrapEventHandler(providerEntry) { var _a, _b, _c, _d, _e; const provider = providerEntry.provider; (_a = provider.events) == null ? void 0 : _a.addHandler(this.eventEnum.Error, (details) => { this.changeProviderStatus(providerEntry.name, this.statusEnum.ERROR, details); }); (_b = provider.events) == null ? void 0 : _b.addHandler(this.eventEnum.Stale, (details) => { this.changeProviderStatus(providerEntry.name, this.statusEnum.STALE, details); }); (_c = provider.events) == null ? void 0 : _c.addHandler(this.eventEnum.ConfigurationChanged, (details) => { this.events.emit(this.eventEnum.ConfigurationChanged, details); }); (_d = provider.events) == null ? void 0 : _d.addHandler(this.eventEnum.Ready, (details) => { this.changeProviderStatus(providerEntry.name, this.statusEnum.READY, details); }); const reconcilingEvent = this.eventEnum.Reconciling; const reconcilingStatus = this.statusEnum.RECONCILING; if (reconcilingEvent && reconcilingStatus) { (_e = provider.events) == null ? void 0 : _e.addHandler(reconcilingEvent, (details) => { this.changeProviderStatus(providerEntry.name, reconcilingStatus, details); }); } } providerStatus(name) { return this.providerStatuses[name]; } getStatusFromProviderStatuses() { const statuses = Object.values(this.providerStatuses); if (statuses.includes(this.statusEnum.FATAL)) { return this.statusEnum.FATAL; } else if (statuses.includes(this.statusEnum.NOT_READY)) { return this.statusEnum.NOT_READY; } else if (statuses.includes(this.statusEnum.ERROR)) { return this.statusEnum.ERROR; } else if (statuses.includes(this.statusEnum.STALE)) { return this.statusEnum.STALE; } else if (this.statusEnum.RECONCILING && statuses.includes(this.statusEnum.RECONCILING)) { return this.statusEnum.RECONCILING; } return this.statusEnum.READY; } changeProviderStatus(name, status, details) { const currentStatus = this.getStatusFromProviderStatuses(); this.providerStatuses[name] = status; const newStatus = this.getStatusFromProviderStatuses(); if (currentStatus !== newStatus) { if (newStatus === this.statusEnum.FATAL || newStatus === this.statusEnum.ERROR) { this.events.emit(this.eventEnum.Error, details); } else if (newStatus === this.statusEnum.STALE) { this.events.emit(this.eventEnum.Stale, details); } else if (newStatus === this.statusEnum.READY) { this.events.emit(this.eventEnum.Ready, details); } else { const reconcilingEvent = this.eventEnum.Reconciling; if (reconcilingEvent && this.statusEnum.RECONCILING && newStatus === this.statusEnum.RECONCILING) { this.events.emit(reconcilingEvent, details); } } } } }; // ../shared/src/provider/multi-provider/strategies/base-evaluation-strategy.ts var BaseEvaluationStrategy = class { constructor(statusEnum) { this.statusEnum = statusEnum; this.runMode = "sequential"; } shouldEvaluateThisProvider(strategyContext, _evalContext) { if (strategyContext.providerStatus === this.statusEnum.NOT_READY || strategyContext.providerStatus === this.statusEnum.FATAL) { return false; } return true; } shouldEvaluateNextProvider(_strategyContext, _context, _result) { return true; } shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) { if (strategyContext.providerStatus === this.statusEnum.NOT_READY || strategyContext.providerStatus === this.statusEnum.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 }; } }; // ../shared/src/provider/multi-provider/strategies/first-match-strategy.ts var FirstMatchStrategy = class extends BaseEvaluationStrategy { shouldEvaluateNextProvider(strategyContext, context, result) { if (this.hasErrorWithCode(result, "FLAG_NOT_FOUND" /* 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); } }; // ../shared/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); } }; // ../shared/src/provider/multi-provider/strategies/comparison-strategy.ts var ComparisonStrategy = class extends BaseEvaluationStrategy { constructor(statusEnum, fallbackProvider, onMismatch) { super(statusEnum); 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 GeneralError("Fallback provider not found in resolution results"); } if (!finalResolution) { throw new 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); } }; // ../shared/src/events/events.ts var ServerProviderEvents = /* @__PURE__ */ ((ServerProviderEvents2) => { ServerProviderEvents2["Ready"] = "PROVIDER_READY"; ServerProviderEvents2["Error"] = "PROVIDER_ERROR"; ServerProviderEvents2["ConfigurationChanged"] = "PROVIDER_CONFIGURATION_CHANGED"; ServerProviderEvents2["Stale"] = "PROVIDER_STALE"; return ServerProviderEvents2; })(ServerProviderEvents || {}); var ClientProviderEvents = /* @__PURE__ */ ((ClientProviderEvents2) => { ClientProviderEvents2["Ready"] = "PROVIDER_READY"; ClientProviderEvents2["Error"] = "PROVIDER_ERROR"; ClientProviderEvents2["ConfigurationChanged"] = "PROVIDER_CONFIGURATION_CHANGED"; ClientProviderEvents2["ContextChanged"] = "PROVIDER_CONTEXT_CHANGED"; ClientProviderEvents2["Reconciling"] = "PROVIDER_RECONCILING"; ClientProviderEvents2["Stale"] = "PROVIDER_STALE"; return ClientProviderEvents2; })(ClientProviderEvents || {}); // ../shared/src/events/event-utils.ts var eventStatusMap = { ["READY" /* READY */]: "PROVIDER_READY" /* Ready */, ["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 }; var statusMatchesEvent = (event, status) => { return !status && event === "PROVIDER_READY" /* Ready */ || eventStatusMap[status] === event; }; // ../shared/src/logger/default-logger.ts var DefaultLogger = class { error(...args) { console.error(...args); } warn(...args) { console.warn(...args); } info() { } debug() { } }; // ../shared/src/logger/safe-logger.ts var LOG_LEVELS = ["error", "warn", "info", "debug"]; var SafeLogger = class { constructor(logger) { this.fallbackLogger = new DefaultLogger(); try { for (const level of LOG_LEVELS) { if (!logger[level] || typeof logger[level] !== "function") { throw new Error(`The provided logger is missing the ${level} method.`); } } this.logger = logger; } catch (err) { console.error(err); console.error("Falling back to the default logger."); this.logger = this.fallbackLogger; } } error(...args) { this.log("error", ...args); } warn(...args) { this.log("warn", ...args); } info(...args) { this.log("info", ...args); } debug(...args) { this.log("debug", ...args); } log(level, ...args) { try { this.logger[level](...args); } catch (error) { this.fallbackLogger[level](...args); } } }; // ../shared/src/events/generic-event-emitter.ts var GenericEventEmitter = class { constructor(globalLogger) { this.globalLogger = globalLogger; this._handlers = { ["PROVIDER_CONFIGURATION_CHANGED" /* ConfigurationChanged */]: /* @__PURE__ */ new WeakMap(), ["PROVIDER_CONTEXT_CHANGED" /* ContextChanged */]: /* @__PURE__ */ new WeakMap(), ["PROVIDER_READY" /* Ready */]: /* @__PURE__ */ new WeakMap(), ["PROVIDER_ERROR" /* Error */]: /* @__PURE__ */ new WeakMap(), ["PROVIDER_STALE" /* Stale */]: /* @__PURE__ */ new WeakMap(), ["PROVIDER_RECONCILING" /* Reconciling */]: /* @__PURE__ */ new WeakMap() }; } // here we use E, to restrict the events a provider can manually emit (PROVIDER_CONTEXT_CHANGED is emitted by the SDK) emit(eventType, context) { this.eventEmitter.emit(eventType, context); } addHandler(eventType, handler) { const asyncHandler = (details) => __async(this, null, function* () { var _a; try { yield handler(details); } catch (err) { (_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err); } }); const existingAsyncHandlers = this._handlers[eventType].get(handler); this._handlers[eventType].set(handler, [...existingAsyncHandlers || [], asyncHandler]); this.eventEmitter.on(eventType, asyncHandler); } removeHandler(eventType, handler) { const existingAsyncHandlers = this._handlers[eventType].get(handler); if (existingAsyncHandlers) { const removedAsyncHandler = existingAsyncHandlers.pop(); if (removedAsyncHandler) { this.eventEmitter.removeListener(eventType, removedAsyncHandler); } } } removeAllHandlers(eventType) { if (eventType) { this.eventEmitter.removeAllListeners(eventType); } else { this.eventEmitter.removeAllListeners(); } } getHandlers(eventType) { return this.eventEmitter.listeners(eventType); } setLogger(logger) { this._eventLogger = new SafeLogger(logger); return this; } get _logger() { var _a, _b; return (_b = this._eventLogger) != null ? _b : (_a = this.globalLogger) == null ? void 0 : _a.call(this); } }; // ../shared/src/telemetry/attributes.ts var TelemetryAttribute = { /** * The lookup key of the feature flag. * * - type: `string` * - requirement level: `required` * - example: `logo-color` */ KEY: "feature_flag.key", /** * Describes a class of error the operation ended with. * * - type: `string` * - requirement level: `conditionally required` * - condition: `reason` is `error` * - example: `flag_not_found` */ ERROR_CODE: "error.type", /** * A message explaining the nature of an error occurring during flag evaluation. * * - type: `string` * - requirement level: `recommended` * - example: `Flag not found` */ ERROR_MESSAGE: "error.message", /** * A semantic identifier for an evaluated flag value. * * - type: `string` * - requirement level: `conditionally required` * - condition: variant is defined on the evaluation details * - example: `blue`; `on`; `true` */ VARIANT: "feature_flag.result.variant", /** * The evaluated value of the feature flag. * * - type: `undefined` * - requirement level: `conditionally required` * - example: `#ff0000`; `1`; `true` */ VALUE: "feature_flag.result.value", /** * The unique identifier for the flag evaluation context. For example, the targeting key. * * - type: `string` * - requirement level: `recommended` * - example: `5157782b-2203-4c80-a857-dbbd5e7761db` */ CONTEXT_ID: "feature_flag.context.id", /** * The reason code which shows how a feature flag value was determined. * * - type: `string` * - requirement level: `recommended` * - example: `targeting_match` */ REASON: "feature_flag.result.reason", /** * Describes a class of error the operation ended with. * * - type: `string` * - requirement level: `recommended` * - example: `flag_not_found` */ PROVIDER: "feature_flag.provider.name", /** * The identifier of the flag set to which the feature flag belongs. * * - type: `string` * - requirement level: `recommended` * - example: `proj-1`; `ab98sgs`; `service1/dev` */ FLAG_SET_ID: "feature_flag.set.id", /** * The version of the ruleset used during the evaluation. This may be any stable value which uniquely identifies the ruleset. * * - type: `string` * - requirement level: `recommended` * - example: `1.0.0`; `2021-01-01` */ VERSION: "feature_flag.version" }; // ../shared/src/telemetry/flag-metadata.ts var TelemetryFlagMetadata = { /** * The context identifier returned in the flag metadata uniquely identifies * the subject of the flag evaluation. If not available, the targeting key * should be used. */ CONTEXT_ID: "contextId", /** * A logical identifier for the flag set. */ FLAG_SET_ID: "flagSetId", /** * A version string (format unspecified) for the flag or flag set. */ VERSION: "version" }; // ../shared/src/telemetry/evaluation-event.ts var FLAG_EVALUATION_EVENT_NAME = "feature_flag.evaluation"; function createEvaluationEvent(hookContext, evaluationDetails) { var _a, _b, _c; const attributes = { [TelemetryAttribute.KEY]: hookContext.flagKey, [TelemetryAttribute.PROVIDER]: hookContext.providerMetadata.name, [TelemetryAttribute.REASON]: ((_a = evaluationDetails.reason) != null ? _a : StandardResolutionReasons.UNKNOWN).toLowerCase() }; if (evaluationDetails.variant) { attributes[TelemetryAttribute.VARIANT] = evaluationDetails.variant; } if (evaluationDetails.value !== null) { if (typeof evaluationDetails.value !== "object") { attributes[TelemetryAttribute.VALUE] = evaluationDetails.value; } else { try { attributes[TelemetryAttribute.VALUE] = JSON.stringify(evaluationDetails.value); } catch (e) { } } } const contextId = (_b = evaluationDetails.flagMetadata[TelemetryFlagMetadata.CONTEXT_ID]) != null ? _b : hookContext.context.targetingKey; if (contextId) { attributes[TelemetryAttribute.CONTEXT_ID] = contextId; } const setId = evaluationDetails.flagMetadata[TelemetryFlagMetadata.FLAG_SET_ID]; if (setId) { attributes[TelemetryAttribute.FLAG_SET_ID] = setId; } const version = evaluationDetails.flagMetadata[TelemetryFlagMetadata.VERSION]; if (version) { attributes[TelemetryAttribute.VERSION] = version; } if (evaluationDetails.reason === StandardResolutionReasons.ERROR) { attributes[TelemetryAttribute.ERROR_CODE] = ((_c = evaluationDetails.errorCode) != null ? _c : "GENERAL" /* GENERAL */).toLowerCase(); if (evaluationDetails.errorMessage) { attributes[TelemetryAttribute.ERROR_MESSAGE] = evaluationDetails.errorMessage; } } return { name: FLAG_EVALUATION_EVENT_NAME, attributes }; } // ../shared/src/type-guards.ts function isString(value) { return typeof value === "string"; } function stringOrUndefined(value) { return isString(value) ? value : void 0; } function isObject(value) { return typeof value === "object"; } function objectOrUndefined(value) { return isObject(value) ? value : void 0; } // ../shared/src/filter.ts function isDefined(input) { return typeof input !== "undefined" && input !== null; } // ../shared/src/open-feature.ts var ProviderWrapper = class { constructor(_provider, _status, _statusEnumType) { this._provider = _provider; this._status = _status; this._pendingContextChanges = 0; var _a, _b, _c; (_a = _provider.events) == null ? void 0 : _a.addHandler("PROVIDER_READY" /* Ready */, () => { this._status = _statusEnumType.READY; }); (_b = _provider.events) == null ? void 0 : _b.addHandler("PROVIDER_STALE" /* Stale */, () => { this._status = _statusEnumType.STALE; }); (_c = _provider.events) == null ? void 0 : _c.addHandler("PROVIDER_ERROR" /* Error */, (details) => { if ((details == null ? void 0 : details.errorCode) === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { this._status = _statusEnumType.FATAL; } else { this._status = _statusEnumType.ERROR; } }); } 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 { constructor(category) { this._hooks = []; this._context = {}; this._logger = new DefaultLogger(); this._clientEventHandlers = /* @__PURE__ */ new Map(); this._domainScopedContext = /* @__PURE__ */ new Map(); this._clientEvents = /* @__PURE__ */ new Map(); this._runsOn = category; } addHooks(...hooks) { this._hooks = [...this._hooks, ...hooks]; return this; } getHooks() { return this._hooks; } clearHooks() { this._hooks = []; return this; } setLogger(logger) { this._logger = new SafeLogger(logger); return this; } /** * Get metadata about the default provider. * @returns {ProviderMetadata} Provider Metadata */ get providerMetadata() { return this.getProviderMetadata(); } /** * Get metadata about a registered provider using the client name. * An unbound or empty client name will return metadata from the default provider. * @param {string} domain An identifier which logically binds clients with providers * @returns {ProviderMetadata} Provider Metadata */ getProviderMetadata(domain) { return this.getProviderForClient(domain).metadata; } /** * Adds a handler for the given provider event type. * The handlers are called in the order they have been added. * API (global) events run for all providers. * @param {AnyProviderEvent} eventType The provider event type to listen to * @param {EventHandler} handler The handler to run on occurrence of the event type * @param {EventOptions} options Optional options such as signal for aborting */ addHandler(eventType, handler, options) { [.../* @__PURE__ */ new Map([[void 0, this._defaultProvider]]), ...this._domainScopedProviders].forEach((keyProviderTuple) => { var _a; const domain = keyProviderTuple[0]; const provider = keyProviderTuple[1].provider; const status = keyProviderTuple[1].status; const shouldRunNow = statusMatchesEvent(eventType, status); if (shouldRunNow) { try { handler({ domain, providerName: provider.metadata.name }); } catch (err) { (_a = this._logger) == null ? void 0 : _a.error("Error running event handler:", err); } } }); this._apiEmitter.addHandler(eventType, handler); if ((options == null ? void 0 : options.signal) && typeof options.signal.addEventListener === "function") { options.signal.addEventListener("abort", () => { this.removeHandler(eventType, handler); }); } } /** * Removes a handler for the given provider event type. * @param {AnyProviderEvent} eventType The provider event type to remove the listener for * @param {EventHandler} handler The handler to remove for the provider event type */ removeHandler(eventType, handler) { this._apiEmitter.removeHandler(eventType, handler); } /** * Removes all event handlers. */ clearHandlers() { this._apiEmitter.removeAllHandlers(); } /** * Gets the current handlers for the given provider event type. * @param {AnyProviderEvent} eventType The provider event type to get the current handlers for * @returns {EventHandler[]} The handlers currently attached to the given provider event type */ getHandlers(eventType) { return this._apiEmitter.getHandlers(eventType); } setAwaitableProvider(domainOrProvider, providerOrUndefined) { var _a, _b, _c, _d, _e, _f, _g, _h; const domain = stringOrUndefined(domainOrProvider); const provider = (_a = objectOrUndefined(domainOrProvider)) != null ? _a : objectOrUndefined(providerOrUndefined); if (!provider) { this._logger.debug("No provider defined, ignoring setProvider call"); return; } const oldProvider = this.getProviderForClient(domain); const providerName = provider.metadata.name; if (oldProvider === provider) { this._logger.debug("Provider is already set, ignoring setProvider call"); return; } if (!provider.runsOn) { this._logger.debug(`Provider '${provider.metadata.name}' has not defined its intended use.`); } else if (provider.runsOn !== this._runsOn) { throw new GeneralError(`Provider '${provider.metadata.name}' is intended for use on the ${provider.runsOn}.`); } const emitters = this.getAssociatedEventEmitters(domain); let initializationPromise = void 0; const wrappedProvider = new ProviderWrapper( provider, this._statusEnumType.NOT_READY, this._statusEnumType ); if (typeof provider.initialize === "function" && !this.allProviders.includes(provider)) { initializationPromise = (_e = (_d = (_c = provider.initialize) == null ? void 0 : _c.call(provider, domain ? (_b = this._domainScopedContext.get(domain)) != null ? _b : this._context : this._context)) == null ? void 0 : _d.then(() => { var _a2; wrappedProvider.status = this._statusEnumType.READY; this.getAssociatedEventEmitters(domain).forEach((emitter) => { emitter == null ? void 0 : emitter.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); }); (_a2 = this._apiEmitter) == null ? void 0 : _a2.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); })) == null ? void 0 : _e.catch((error) => { var _a2; if ((error == null ? void 0 : error.code) === "PROVIDER_FATAL" /* PROVIDER_FATAL */) { wrappedProvider.status = this._statusEnumType.FATAL; } else { wrappedProvider.status = this._statusEnumType.ERROR; } this.getAssociatedEventEmitters(domain).forEach((emitter) => { emitter == null ? void 0 : emitter.emit("PROVIDER_ERROR" /* Error */, { clientName: domain, domain, providerName, message: error == null ? void 0 : error.message }); }); (_a2 = this._apiEmitter) == null ? void 0 : _a2.emit("PROVIDER_ERROR" /* Error */, { clientName: domain, domain, providerName, message: error == null ? void 0 : error.message }); throw error; }); } else { wrappedProvider.status = this._statusEnumType.READY; emitters.forEach((emitter) => { emitter == null ? void 0 : emitter.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); }); (_f = this._apiEmitter) == null ? void 0 : _f.emit("PROVIDER_READY" /* Ready */, { clientName: domain, domain, providerName }); } if (domain) { this._domainScopedProviders.set(domain, wrappedProvider); } else { this._defaultProvider = wrappedProvider; } this.transferListeners(oldProvider, provider, domain, emitters); if (!this.allProviders.includes(oldProvider)) { (_h = (_g = oldProvider == null ? void 0 : oldProvider.onClose) == null ? void 0 : _g.call(oldProvider)) == null ? void 0 : _h.catch((err) => { this._logger.error(`error closing provider: ${err == null ? void 0 : err.message}, ${err == null ? void 0 : err.stack}`); }); } return initializationPromise; } getProviderForClient(domain) { var _a, _b; if (!domain) { return this._defaultProvider.provider; } return (_b = (_a = this._domainScopedProviders.get(domain)) == null ? void 0 : _a.provider) != null ? _b : this._defaultProvider.provider; } buildAndCacheEventEmitterForClient(domain) { const emitter = this._clientEvents.get(domain); if (emitter) { return emitter; } const newEmitter = this._createEventEmitter(); this._clientEvents.set(domain, newEmitter); const clientProvider = this.getProviderForClient(domain); Object.values(ClientProviderEvents).forEach( (eventType) => { var _a; return (_a = clientProvider.events) == null ? void 0 : _a.addHandler(eventType, (details) => __async(this, null, function* () { newEmitter.emit(eventType, __spreadProps(__spreadValues({}, details), { clientName: domain, domain, providerName: clientProvider.metadata.name })); })); } ); return newEmitter; } getUnboundEmitters() { const domainScopedProviders = [...this._domainScopedProviders.keys()]; const eventEmitte