UNPKG

@openfeature/core

Version:

Shared OpenFeature JS components (server and web)

914 lines (889 loc) 33 kB
"use strict"; 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 __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; 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 __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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 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()); }); }; // src/index.ts var index_exports = {}; __export(index_exports, { AllProviderEvents: () => ClientProviderEvents, AllProviderStatus: () => ClientProviderStatus, ClientProviderEvents: () => ClientProviderEvents, ClientProviderStatus: () => ClientProviderStatus, DefaultLogger: () => DefaultLogger, ErrorCode: () => ErrorCode, FlagNotFoundError: () => FlagNotFoundError, GeneralError: () => GeneralError, GenericEventEmitter: () => GenericEventEmitter, InvalidContextError: () => InvalidContextError, LOG_LEVELS: () => LOG_LEVELS, OpenFeatureCommonAPI: () => OpenFeatureCommonAPI, OpenFeatureError: () => OpenFeatureError, ParseError: () => ParseError, ProviderFatalError: () => ProviderFatalError, ProviderNotReadyError: () => ProviderNotReadyError, ProviderWrapper: () => ProviderWrapper, SafeLogger: () => SafeLogger, ServerProviderEvents: () => ServerProviderEvents, ServerProviderStatus: () => ServerProviderStatus, StandardResolutionReasons: () => StandardResolutionReasons, TargetingKeyMissingError: () => TargetingKeyMissingError, TelemetryAttribute: () => TelemetryAttribute, TelemetryFlagMetadata: () => TelemetryFlagMetadata, TypeMismatchError: () => TypeMismatchError, createEvaluationEvent: () => createEvaluationEvent, instantiateErrorByErrorCode: () => instantiateErrorByErrorCode, isObject: () => isObject, isString: () => isString, objectOrUndefined: () => objectOrUndefined, statusMatchesEvent: () => statusMatchesEvent, stringOrUndefined: () => stringOrUndefined }); module.exports = __toCommonJS(index_exports); // 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 || {}); // 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; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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 */; } }; // 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); } }; // 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 || {}); // 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 || {}); // 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; }; // src/logger/default-logger.ts var DefaultLogger = class { error(...args) { console.error(...args); } warn(...args) { console.warn(...args); } info() { } debug() { } }; // 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); } } }; // 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); } }; // 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` * - condition: variant is not defined on the evaluation details * - 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" }; // 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" }; // src/telemetry/evaluation-event.ts var FLAG_EVALUATION_EVENT_NAME = "feature_flag.evaluation"; function createEvaluationEvent(hookContext, evaluationDetails) { var _a, _b; 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; } else { attributes[TelemetryAttribute.VALUE] = evaluationDetails.value; } const contextId = evaluationDetails.flagMetadata[TelemetryFlagMetadata.CONTEXT_ID] || 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] = ((_b = evaluationDetails.errorCode) != null ? _b : "GENERAL" /* GENERAL */).toLowerCase(); if (evaluationDetails.errorMessage) { attributes[TelemetryAttribute.ERROR_MESSAGE] = evaluationDetails.errorMessage; } } return { name: FLAG_EVALUATION_EVENT_NAME, attributes }; } // 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; } // src/filter.ts function isDefined(input) { return typeof input !== "undefined" && input !== null; } // 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 eventEmitterNames = [...this._clientEvents.keys()].filter(isDefined); const unboundEmitterNames = eventEmitterNames.filter((name) => !domainScopedProviders.includes(name)); return [ // all unbound, named emitters ...unboundEmitterNames.map((name) => this._clientEvents.get(name)), // the default emitter this._clientEvents.get(void 0) ].filter(isDefined); } getAssociatedEventEmitters(domain) { return domain ? [this.buildAndCacheEventEmitterForClient(domain)] : this.getUnboundEmitters(); } transferListeners(oldProvider, newProvider, domain, emitters) { var _a; (_a = this._clientEventHandlers.get(domain)) == null ? void 0 : _a.forEach((eventHandler) => { var _a2; return (_a2 = oldProvider.events) == null ? void 0 : _a2.removeHandler(...eventHandler); }); const newClientHandlers = Object.values(ClientProviderEvents).map((eventType) => { const handler = (details) => __async(this, null, function* () { emitters.forEach((emitter) => { emitter == null ? void 0 : emitter.emit(eventType, __spreadProps(__spreadValues({}, details), { clientName: domain, domain, providerName: newProvider.metadata.name })); }); this._apiEmitter.emit(eventType, __spreadProps(__spreadValues({}, details), { clientName: domain, domain, providerName: newProvider.metadata.name })); }); return [eventType, handler]; }); this._clientEventHandlers.set(domain, newClientHandlers); newClientHandlers.forEach((eventHandler) => { var _a2; return (_a2 = newProvider.events) == null ? void 0 : _a2.addHandler(...eventHandler); }); } close() { return __async(this, null, function* () { var _a, _b; try { yield (_b = (_a = this == null ? void 0 : this._defaultProvider.provider) == null ? void 0 : _a.onClose) == null ? void 0 : _b.call(_a); } catch (err) { this.handleShutdownError(this._defaultProvider.provider, err); } const wrappers = Array.from(this._domainScopedProviders); yield Promise.all( wrappers.map((_0) => __async(this, [_0], function* ([, wrapper]) { var _a2, _b2; try { yield (_b2 = wrapper == null ? void 0 : (_a2 = wrapper.provider).onClose) == null ? void 0 : _b2.call(_a2); } catch (err) { this.handleShutdownError(wrapper == null ? void 0 : wrapper.provider, err); } })) ); }); } clearProvidersAndSetDefault(defaultProvider) { return __async(this, null, function* () { try { yield this.close(); } catch (err) { this._logger.error("Unable to cleanly close providers. Resetting to the default configuration."); } finally { this._domainScopedProviders.clear(); this._defaultProvider = new ProviderWrapper( defaultProvider, this._statusEnumType.NOT_READY, this._statusEnumType ); } }); } get allProviders() { return [ ...[...this._domainScopedProviders.values()].map((wrappers) => wrappers.provider), this._defaultProvider.provider ]; } handleShutdownError(provider, err) { this._logger.error(`Error during shutdown of provider ${provider.metadata.name}: ${err}`); this._logger.error(err == null ? void 0 : err.stack); } }; //# sourceMappingURL=index.js.map