@launchdarkly/js-server-sdk-common
Version:
LaunchDarkly Server SDK for JavaScript - common code
88 lines • 4.16 kB
JavaScript
"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