UNPKG

@launchdarkly/js-server-sdk-common

Version:
88 lines 4.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const BEFORE_EVALUATION_STAGE_NAME = 'beforeEvaluation'; const AFTER_EVALUATION_STAGE_NAME = 'afterEvaluation'; const UNKNOWN_HOOK_NAME = 'unknown hook'; class HookRunner { constructor(_logger, hooks) { this._logger = _logger; this._hooks = []; this._hooks.push(...hooks); } async withEvaluationSeries(key, context, defaultValue, methodName, method) { // This early return is here to avoid the extra async/await associated with // using withHooksDataWithDetail. if (this._hooks.length === 0) { return method(); } return this.withEvaluationSeriesExtraDetail(key, context, defaultValue, methodName, async () => { const detail = await method(); return { detail }; }).then(({ detail }) => detail); } /** * This function allows extra information to be returned with the detail for situations like * migrations where a tracker is returned with the detail. */ async withEvaluationSeriesExtraDetail(key, context, defaultValue, methodName, method) { if (this._hooks.length === 0) { return method(); } const { hooks, hookContext } = this._prepareHooks(key, context, defaultValue, methodName); const hookData = this._executeBeforeEvaluation(hooks, hookContext); const result = await method(); this._executeAfterEvaluation(hooks, hookContext, hookData, result.detail); return result; } _tryExecuteStage(method, hookName, stage) { var _a; try { return stage(); } catch (err) { (_a = this._logger) === null || _a === void 0 ? void 0 : _a.error(`An error was encountered in "${method}" of the "${hookName}" hook: ${err}`); return {}; } } _hookName(hook) { var _a, _b; try { return (_a = hook === null || hook === void 0 ? void 0 : hook.getMetadata().name) !== null && _a !== void 0 ? _a : UNKNOWN_HOOK_NAME; } catch (_c) { (_b = this._logger) === null || _b === void 0 ? void 0 : _b.error(`Exception thrown getting metadata for hook. Unable to get hook name.`); return UNKNOWN_HOOK_NAME; } } _executeAfterEvaluation(hooks, hookContext, updatedData, result) { var _a; // This iterates in reverse, versus reversing a shallow copy of the hooks, // for efficiency. for (let hookIndex = hooks.length - 1; hookIndex >= 0; hookIndex -= 1) { const hook = hooks[hookIndex]; const data = (_a = updatedData[hookIndex]) !== null && _a !== void 0 ? _a : {}; this._tryExecuteStage(AFTER_EVALUATION_STAGE_NAME, this._hookName(hook), () => { var _a, _b; return (_b = (_a = hook === null || hook === void 0 ? void 0 : hook.afterEvaluation) === null || _a === void 0 ? void 0 : _a.call(hook, hookContext, data, result)) !== null && _b !== void 0 ? _b : {}; }); } } _executeBeforeEvaluation(hooks, hookContext) { return hooks.map((hook) => this._tryExecuteStage(BEFORE_EVALUATION_STAGE_NAME, this._hookName(hook), () => { var _a, _b; return (_b = (_a = hook === null || hook === void 0 ? void 0 : hook.beforeEvaluation) === null || _a === void 0 ? void 0 : _a.call(hook, hookContext, {})) !== null && _b !== void 0 ? _b : {}; })); } _prepareHooks(key, context, defaultValue, methodName) { // Copy the hooks to use a consistent set during evaluation. Hooks could be added and we want // to ensure all correct stages for any give hook execute. Not for instance the afterEvaluation // stage without beforeEvaluation having been called on that hook. const hooks = [...this._hooks]; const hookContext = { flagKey: key, context, defaultValue, method: methodName, }; return { hooks, hookContext }; } addHook(hook) { this._hooks.push(hook); } } exports.default = HookRunner; //# sourceMappingURL=HookRunner.js.map