@kenji71089/evaluation 0.0.1 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +36 -6
- package/lib/bucketeer.d.ts +6 -0
- package/lib/bucketeer.js +65 -0
- package/lib/bucketeer.mjs +38 -0
- package/lib/clauseEvaluator.js +49 -7
- package/lib/clauseEvaluator.mjs +41 -7
- package/lib/evaluation.d.ts +10 -1
- package/lib/evaluation.js +184 -27
- package/lib/evaluation.mjs +141 -10
- package/lib/index.d.ts +12 -10
- package/lib/index.js +61 -40
- package/lib/index.mjs +16 -10
- package/lib/modelFactory.d.ts +1 -0
- package/lib/modelFactory.js +2 -0
- package/lib/modelFactory.mjs +2 -0
- package/lib/proto/event/client/event_pb.d.ts +19 -9
- package/lib/proto/event/client/event_pb.js +22 -12
- package/lib/proto/event/client/event_pb.mjs +100 -90
- package/lib/proto/feature/clause_pb.d.ts +2 -1
- package/lib/proto/feature/clause_pb.js +3 -2
- package/lib/proto/feature/clause_pb.mjs +7 -6
- package/lib/proto/feature/evaluation_pb.d.ts +3 -3
- package/lib/proto/feature/evaluation_pb.js +1 -1
- package/lib/proto/feature/evaluation_pb.mjs +15 -15
- package/lib/proto/feature/feature_last_used_info_pb.d.ts +10 -1
- package/lib/proto/feature/feature_last_used_info_pb.js +12 -1
- package/lib/proto/feature/feature_last_used_info_pb.mjs +18 -7
- package/lib/proto/feature/feature_pb.d.ts +72 -7
- package/lib/proto/feature/feature_pb.js +446 -4
- package/lib/proto/feature/feature_pb.mjs +535 -29
- package/lib/proto/feature/prerequisite_pb.d.ts +1 -1
- package/lib/proto/feature/prerequisite_pb.js +1 -1
- package/lib/proto/feature/prerequisite_pb.mjs +3 -3
- package/lib/proto/feature/reason_pb.d.ts +8 -1
- package/lib/proto/feature/reason_pb.js +9 -2
- package/lib/proto/feature/reason_pb.mjs +11 -4
- package/lib/proto/feature/rule_pb.d.ts +3 -3
- package/lib/proto/feature/rule_pb.js +1 -1
- package/lib/proto/feature/rule_pb.mjs +4 -4
- package/lib/proto/feature/segment_pb.d.ts +3 -3
- package/lib/proto/feature/segment_pb.js +1 -1
- package/lib/proto/feature/segment_pb.mjs +22 -22
- package/lib/proto/feature/strategy_pb.d.ts +31 -1
- package/lib/proto/feature/strategy_pb.js +206 -2
- package/lib/proto/feature/strategy_pb.mjs +242 -9
- package/lib/proto/feature/target_pb.d.ts +1 -1
- package/lib/proto/feature/target_pb.js +1 -1
- package/lib/proto/feature/target_pb.mjs +3 -3
- package/lib/proto/feature/variation_pb.d.ts +1 -1
- package/lib/proto/feature/variation_pb.js +1 -1
- package/lib/proto/feature/variation_pb.mjs +5 -5
- package/lib/proto/user/user_pb.d.ts +53 -1
- package/lib/proto/user/user_pb.js +374 -1
- package/lib/proto/user/user_pb.mjs +427 -7
- package/lib/strategyEvaluator.d.ts +0 -2
- package/lib/strategyEvaluator.js +23 -57
- package/lib/strategyEvaluator.mjs +23 -46
- package/lib/userEvaluation.js +13 -3
- package/lib/userEvaluation.mjs +13 -3
- package/package.json +15 -8
- package/lib/google/api/annotations_pb.d.ts +0 -8
- package/lib/google/api/annotations_pb.js +0 -40
- package/lib/google/api/annotations_pb.mjs +0 -54
- package/lib/google/api/annotations_pb_service.d.ts +0 -3
- package/lib/google/api/annotations_pb_service.js +0 -3
- package/lib/google/api/annotations_pb_service.mjs +0 -3
- package/lib/google/api/http_pb.d.ts +0 -132
- package/lib/google/api/http_pb.js +0 -860
- package/lib/google/api/http_pb.mjs +0 -982
- package/lib/google/api/http_pb_service.d.ts +0 -3
- package/lib/google/api/http_pb_service.js +0 -3
- package/lib/google/api/http_pb_service.mjs +0 -3
- package/lib/google/rpc/code_pb.d.ts +0 -26
- package/lib/google/rpc/code_pb.js +0 -44
- package/lib/google/rpc/code_pb.mjs +0 -48
- package/lib/google/rpc/code_pb_service.d.ts +0 -3
- package/lib/google/rpc/code_pb_service.js +0 -3
- package/lib/google/rpc/code_pb_service.mjs +0 -3
- package/lib/google/rpc/error_details_pb.d.ts +0 -322
- package/lib/google/rpc/error_details_pb.js +0 -2220
- package/lib/google/rpc/error_details_pb.mjs +0 -2499
- package/lib/google/rpc/error_details_pb_service.d.ts +0 -3
- package/lib/google/rpc/error_details_pb_service.js +0 -3
- package/lib/google/rpc/error_details_pb_service.mjs +0 -3
- package/lib/google/rpc/status_pb.d.ts +0 -36
- package/lib/google/rpc/status_pb.js +0 -235
- package/lib/google/rpc/status_pb.mjs +0 -268
- package/lib/google/rpc/status_pb_service.d.ts +0 -3
- package/lib/google/rpc/status_pb_service.js +0 -3
- package/lib/google/rpc/status_pb_service.mjs +0 -3
- package/lib/proto/event/client/event_pb_service.d.ts +0 -3
- package/lib/proto/event/client/event_pb_service.js +0 -3
- package/lib/proto/event/client/event_pb_service.mjs +0 -3
- package/lib/proto/event/domain/event_pb.d.ts +0 -4518
- package/lib/proto/event/domain/event_pb.js +0 -10834
- package/lib/proto/event/domain/event_pb.mjs +0 -33315
- package/lib/proto/event/domain/event_pb_service.d.ts +0 -3
- package/lib/proto/event/domain/event_pb_service.js +0 -3
- package/lib/proto/event/domain/event_pb_service.mjs +0 -3
- package/lib/proto/event/domain/localized_message_pb.d.ts +0 -29
- package/lib/proto/event/domain/localized_message_pb.js +0 -183
- package/lib/proto/event/domain/localized_message_pb.mjs +0 -206
- package/lib/proto/event/domain/localized_message_pb_service.d.ts +0 -3
- package/lib/proto/event/domain/localized_message_pb_service.js +0 -3
- package/lib/proto/event/domain/localized_message_pb_service.mjs +0 -3
- package/lib/proto/event/service/feature_pb.d.ts +0 -44
- package/lib/proto/event/service/feature_pb.js +0 -277
- package/lib/proto/event/service/feature_pb.mjs +0 -319
- package/lib/proto/event/service/feature_pb_service.d.ts +0 -3
- package/lib/proto/event/service/feature_pb_service.js +0 -3
- package/lib/proto/event/service/feature_pb_service.mjs +0 -3
- package/lib/proto/event/service/segment_pb.d.ts +0 -51
- package/lib/proto/event/service/segment_pb.js +0 -324
- package/lib/proto/event/service/segment_pb.mjs +0 -375
- package/lib/proto/event/service/segment_pb_service.d.ts +0 -3
- package/lib/proto/event/service/segment_pb_service.js +0 -3
- package/lib/proto/event/service/segment_pb_service.mjs +0 -3
- package/lib/proto/event/service/user_pb.d.ts +0 -49
- package/lib/proto/event/service/user_pb.js +0 -315
- package/lib/proto/event/service/user_pb.mjs +0 -362
- package/lib/proto/event/service/user_pb_service.d.ts +0 -3
- package/lib/proto/event/service/user_pb_service.js +0 -3
- package/lib/proto/event/service/user_pb_service.mjs +0 -3
- package/lib/proto/feature/clause_pb_service.d.ts +0 -3
- package/lib/proto/feature/clause_pb_service.js +0 -3
- package/lib/proto/feature/clause_pb_service.mjs +0 -3
- package/lib/proto/feature/command_pb.d.ts +0 -1213
- package/lib/proto/feature/command_pb.js +0 -8260
- package/lib/proto/feature/command_pb.mjs +0 -9275
- package/lib/proto/feature/command_pb_service.d.ts +0 -3
- package/lib/proto/feature/command_pb_service.js +0 -3
- package/lib/proto/feature/command_pb_service.mjs +0 -3
- package/lib/proto/feature/evaluation_pb_service.d.ts +0 -3
- package/lib/proto/feature/evaluation_pb_service.js +0 -3
- package/lib/proto/feature/evaluation_pb_service.mjs +0 -3
- package/lib/proto/feature/feature_last_used_info_pb_service.d.ts +0 -3
- package/lib/proto/feature/feature_last_used_info_pb_service.js +0 -3
- package/lib/proto/feature/feature_last_used_info_pb_service.mjs +0 -3
- package/lib/proto/feature/feature_pb_service.d.ts +0 -3
- package/lib/proto/feature/feature_pb_service.js +0 -3
- package/lib/proto/feature/feature_pb_service.mjs +0 -3
- package/lib/proto/feature/flag_trigger_pb.d.ts +0 -84
- package/lib/proto/feature/flag_trigger_pb.js +0 -452
- package/lib/proto/feature/flag_trigger_pb.mjs +0 -525
- package/lib/proto/feature/flag_trigger_pb_service.d.ts +0 -3
- package/lib/proto/feature/flag_trigger_pb_service.js +0 -3
- package/lib/proto/feature/flag_trigger_pb_service.mjs +0 -3
- package/lib/proto/feature/prerequisite_pb_service.d.ts +0 -3
- package/lib/proto/feature/prerequisite_pb_service.js +0 -3
- package/lib/proto/feature/prerequisite_pb_service.mjs +0 -3
- package/lib/proto/feature/reason_pb_service.d.ts +0 -3
- package/lib/proto/feature/reason_pb_service.js +0 -3
- package/lib/proto/feature/reason_pb_service.mjs +0 -3
- package/lib/proto/feature/rule_pb_service.d.ts +0 -3
- package/lib/proto/feature/rule_pb_service.js +0 -3
- package/lib/proto/feature/rule_pb_service.mjs +0 -3
- package/lib/proto/feature/segment_pb_service.d.ts +0 -3
- package/lib/proto/feature/segment_pb_service.js +0 -3
- package/lib/proto/feature/segment_pb_service.mjs +0 -3
- package/lib/proto/feature/service_pb.d.ts +0 -2158
- package/lib/proto/feature/service_pb.js +0 -5363
- package/lib/proto/feature/service_pb.mjs +0 -16348
- package/lib/proto/feature/service_pb_service.d.ts +0 -747
- package/lib/proto/feature/service_pb_service.js +0 -1424
- package/lib/proto/feature/service_pb_service.mjs +0 -1501
- package/lib/proto/feature/strategy_pb_service.d.ts +0 -3
- package/lib/proto/feature/strategy_pb_service.js +0 -3
- package/lib/proto/feature/strategy_pb_service.mjs +0 -3
- package/lib/proto/feature/target_pb_service.d.ts +0 -3
- package/lib/proto/feature/target_pb_service.js +0 -3
- package/lib/proto/feature/target_pb_service.mjs +0 -3
- package/lib/proto/feature/variation_pb_service.d.ts +0 -3
- package/lib/proto/feature/variation_pb_service.js +0 -3
- package/lib/proto/feature/variation_pb_service.mjs +0 -3
- package/lib/proto/gateway/service_pb.d.ts +0 -772
- package/lib/proto/gateway/service_pb.js +0 -5249
- package/lib/proto/gateway/service_pb.mjs +0 -6001
- package/lib/proto/gateway/service_pb_service.d.ts +0 -253
- package/lib/proto/gateway/service_pb_service.js +0 -436
- package/lib/proto/gateway/service_pb_service.mjs +0 -461
- package/lib/proto/user/user_pb_service.d.ts +0 -3
- package/lib/proto/user/user_pb_service.js +0 -3
- package/lib/proto/user/user_pb_service.mjs +0 -3
- package/lib/protoc-gen-openapiv2/options/annotations_pb.d.ts +0 -16
- package/lib/protoc-gen-openapiv2/options/annotations_pb.js +0 -100
- package/lib/protoc-gen-openapiv2/options/annotations_pb.mjs +0 -158
- package/lib/protoc-gen-openapiv2/options/annotations_pb_service.d.ts +0 -3
- package/lib/protoc-gen-openapiv2/options/annotations_pb_service.js +0 -3
- package/lib/protoc-gen-openapiv2/options/annotations_pb_service.mjs +0 -3
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb.d.ts +0 -834
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb.js +0 -5456
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb.mjs +0 -6256
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb_service.d.ts +0 -3
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb_service.js +0 -3
- package/lib/protoc-gen-openapiv2/options/openapiv2_pb_service.mjs +0 -3
package/lib/evaluation.js
CHANGED
|
@@ -20,7 +20,8 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
|
20
20
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
21
21
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
22
22
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
23
|
-
function
|
|
23
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
24
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
24
25
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
25
26
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
26
27
|
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
@@ -29,20 +30,64 @@ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r),
|
|
|
29
30
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
30
31
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
31
32
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
33
|
+
var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
36
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
37
|
+
desc = {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
get: function get() {
|
|
40
|
+
return m[k];
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
Object.defineProperty(o, k2, desc);
|
|
45
|
+
} : function (o, m, k, k2) {
|
|
46
|
+
if (k2 === undefined) k2 = k;
|
|
47
|
+
o[k2] = m[k];
|
|
48
|
+
});
|
|
49
|
+
var __setModuleDefault = void 0 && (void 0).__setModuleDefault || (Object.create ? function (o, v) {
|
|
50
|
+
Object.defineProperty(o, "default", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
value: v
|
|
53
|
+
});
|
|
54
|
+
} : function (o, v) {
|
|
55
|
+
o["default"] = v;
|
|
56
|
+
});
|
|
57
|
+
var __importStar = void 0 && (void 0).__importStar || function () {
|
|
58
|
+
var _ownKeys = function ownKeys(o) {
|
|
59
|
+
_ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
60
|
+
var ar = [];
|
|
61
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
62
|
+
return ar;
|
|
63
|
+
};
|
|
64
|
+
return _ownKeys(o);
|
|
65
|
+
};
|
|
66
|
+
return function (mod) {
|
|
67
|
+
if (mod && mod.__esModule) return mod;
|
|
68
|
+
var result = {};
|
|
69
|
+
if (mod != null) for (var k = _ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
70
|
+
__setModuleDefault(result, mod);
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
}();
|
|
32
74
|
Object.defineProperty(exports, "__esModule", {
|
|
33
75
|
value: true
|
|
34
76
|
});
|
|
35
77
|
exports.Evaluator = void 0;
|
|
36
78
|
exports.EvaluationID = EvaluationID;
|
|
79
|
+
exports.getFeatureIDsDependsOn = getFeatureIDsDependsOn;
|
|
37
80
|
var errors_1 = require("./errors");
|
|
38
81
|
var clause_pb_1 = require("./proto/feature/clause_pb");
|
|
39
82
|
var evaluation_pb_1 = require("./proto/feature/evaluation_pb");
|
|
83
|
+
var feature_pb_1 = require("./proto/feature/feature_pb");
|
|
40
84
|
var reason_pb_1 = require("./proto/feature/reason_pb");
|
|
41
85
|
var variation_pb_1 = require("./proto/feature/variation_pb");
|
|
42
86
|
var ruleEvaluator_1 = require("./ruleEvaluator");
|
|
43
87
|
var strategyEvaluator_1 = require("./strategyEvaluator");
|
|
44
88
|
var userEvaluation_1 = require("./userEvaluation");
|
|
45
89
|
var modelFactory_1 = require("./modelFactory");
|
|
90
|
+
var yaml = __importStar(require("js-yaml"));
|
|
46
91
|
var SECONDS_TO_RE_EVALUATE_ALL = 30 * 24 * 60 * 60; // 30 days
|
|
47
92
|
var SECONDS_FOR_ADJUSTMENT = 10; // 10 seconds
|
|
48
93
|
function EvaluationID(featureID, featureVersion, userID) {
|
|
@@ -53,20 +98,21 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
53
98
|
_classCallCheck(this, Evaluator);
|
|
54
99
|
_defineProperty(this, "ruleEvaluator", void 0);
|
|
55
100
|
_defineProperty(this, "strategyEvaluator", void 0);
|
|
101
|
+
// variationCache caches YAML to JSON conversions using variation ID as the key.
|
|
102
|
+
// Since variation IDs are UUIDs, they are globally unique and safe to use as cache keys.
|
|
103
|
+
_defineProperty(this, "variationCache", void 0);
|
|
56
104
|
this.ruleEvaluator = new ruleEvaluator_1.RuleEvaluator();
|
|
57
105
|
this.strategyEvaluator = new strategyEvaluator_1.StrategyEvaluator();
|
|
106
|
+
this.variationCache = new Map();
|
|
58
107
|
}
|
|
59
108
|
return _createClass(Evaluator, [{
|
|
60
109
|
key: "evaluateFeatures",
|
|
61
110
|
value: function () {
|
|
62
|
-
var _evaluateFeatures = _asyncToGenerator(/*#__PURE__*/
|
|
63
|
-
return
|
|
64
|
-
while (1) switch (_context.
|
|
111
|
+
var _evaluateFeatures = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(features, user, mapSegmentUsers, targetTag) {
|
|
112
|
+
return _regenerator().w(function (_context) {
|
|
113
|
+
while (1) switch (_context.n) {
|
|
65
114
|
case 0:
|
|
66
|
-
return _context.
|
|
67
|
-
case 1:
|
|
68
|
-
case "end":
|
|
69
|
-
return _context.stop();
|
|
115
|
+
return _context.a(2, this.evaluate(features, user, mapSegmentUsers, false, targetTag));
|
|
70
116
|
}
|
|
71
117
|
}, _callee, this);
|
|
72
118
|
}));
|
|
@@ -129,13 +175,6 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
129
175
|
try {
|
|
130
176
|
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
131
177
|
var feature = _step2.value;
|
|
132
|
-
if (feature.getArchived()) {
|
|
133
|
-
// To keep response size small, the feature flags archived long time ago are excluded.
|
|
134
|
-
if (!this.isArchivedBeforeLastThirtyDays(feature)) {
|
|
135
|
-
archivedIDs.push(feature.getId());
|
|
136
|
-
}
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
178
|
var segmentUsers = this.listSegmentIDs(feature).flatMap(function (id) {
|
|
140
179
|
return mapSegmentUsers.get(id) || [];
|
|
141
180
|
});
|
|
@@ -144,13 +183,23 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
144
183
|
reason = _this$assignUser2[0],
|
|
145
184
|
variation = _this$assignUser2[1];
|
|
146
185
|
// VariationId is used to check if prerequisite flag's result is what user expects it to be.
|
|
186
|
+
// This must be set for ALL features (including archived) for dependency resolution to work
|
|
147
187
|
flagVariations[feature.getId()] = variation.getId();
|
|
188
|
+
if (feature.getArchived()) {
|
|
189
|
+
// To keep response size small, the feature flags archived long time ago are excluded.
|
|
190
|
+
if (!this.isArchivedBeforeLastThirtyDays(feature)) {
|
|
191
|
+
archivedIDs.push(feature.getId());
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
148
195
|
// When the tag is set in the request,
|
|
149
196
|
// it will return only the evaluations of flags that match the tag configured on the dashboard.
|
|
150
197
|
// When empty, it will return all the evaluations of the flags in the environment.
|
|
151
198
|
if (targetTag !== '' && !this.tagExist(feature.getTagsList(), targetTag)) {
|
|
152
199
|
continue;
|
|
153
200
|
}
|
|
201
|
+
// Convert YAML to JSON for client SDKs if needed
|
|
202
|
+
var convertedValue = this.convertVariationValue(feature, variation);
|
|
154
203
|
var evaluationID = EvaluationID(feature.getId(), feature.getVersion(), user.getId());
|
|
155
204
|
var evaluation = new evaluation_pb_1.Evaluation();
|
|
156
205
|
evaluation.setId(evaluationID);
|
|
@@ -159,7 +208,7 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
159
208
|
evaluation.setUserId(user.getId());
|
|
160
209
|
evaluation.setVariationId(variation.getId());
|
|
161
210
|
evaluation.setVariationName(variation.getName());
|
|
162
|
-
evaluation.setVariationValue(
|
|
211
|
+
evaluation.setVariationValue(convertedValue);
|
|
163
212
|
// Deprecated
|
|
164
213
|
// FIXME: Remove the Variation when is no longer being used.
|
|
165
214
|
// For security reasons, we should remove the variation description.
|
|
@@ -168,7 +217,7 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
168
217
|
var copyVariation = new variation_pb_1.Variation();
|
|
169
218
|
copyVariation.setId(variation.getId());
|
|
170
219
|
copyVariation.setName(variation.getName());
|
|
171
|
-
copyVariation.setValue(
|
|
220
|
+
copyVariation.setValue(convertedValue);
|
|
172
221
|
evaluation.setVariation(copyVariation);
|
|
173
222
|
evaluation.setReason(reason);
|
|
174
223
|
evaluations.push(evaluation);
|
|
@@ -334,6 +383,39 @@ var Evaluator = /*#__PURE__*/function () {
|
|
|
334
383
|
}
|
|
335
384
|
return variation;
|
|
336
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Converts YAML to JSON if needed, with in-memory caching.
|
|
388
|
+
* This ensures client SDKs can retrieve variation values using the object variation interface.
|
|
389
|
+
* Only performs conversion when the variation type is YAML.
|
|
390
|
+
* Cache key uses feature.updatedAt + variation.id to ensure cache invalidation when variations are updated.
|
|
391
|
+
*/
|
|
392
|
+
}, {
|
|
393
|
+
key: "convertVariationValue",
|
|
394
|
+
value: function convertVariationValue(feature, variation) {
|
|
395
|
+
// Only convert if type is YAML
|
|
396
|
+
if (feature.getVariationType() !== feature_pb_1.Feature.VariationType.YAML) {
|
|
397
|
+
return variation.getValue();
|
|
398
|
+
}
|
|
399
|
+
// Cache key: {featureUpdatedAt}:{variationId}
|
|
400
|
+
// This ensures cache is invalidated when the feature (and its variations) are updated,
|
|
401
|
+
// including changes from auto operations that don't increment feature.version
|
|
402
|
+
var cacheKey = "".concat(feature.getUpdatedAt(), ":").concat(variation.getId());
|
|
403
|
+
// Check cache first
|
|
404
|
+
var cached = this.variationCache.get(cacheKey);
|
|
405
|
+
if (cached !== undefined) {
|
|
406
|
+
return cached;
|
|
407
|
+
}
|
|
408
|
+
// Convert YAML to JSON
|
|
409
|
+
try {
|
|
410
|
+
var jsonValue = yamlToJSON(variation.getValue());
|
|
411
|
+
// Cache the result for future requests
|
|
412
|
+
this.variationCache.set(cacheKey, jsonValue);
|
|
413
|
+
return jsonValue;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
// Return original value as fallback on error
|
|
416
|
+
return variation.getValue();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
337
419
|
}]);
|
|
338
420
|
}();
|
|
339
421
|
exports.Evaluator = Evaluator;
|
|
@@ -344,7 +426,7 @@ var Mark;
|
|
|
344
426
|
Mark[Mark["Permanently"] = 2] = "Permanently";
|
|
345
427
|
})(Mark || (Mark = {}));
|
|
346
428
|
// FeatureIDsDependsOn returns the ids of the features that this feature depends on.
|
|
347
|
-
function
|
|
429
|
+
function getFeatureIDsDependsOn(feature) {
|
|
348
430
|
var ids = [];
|
|
349
431
|
// Iterate over prerequisites and add their FeatureId
|
|
350
432
|
feature.getPrerequisitesList().forEach(function (p) {
|
|
@@ -392,7 +474,7 @@ function topologicalSort(features) {
|
|
|
392
474
|
throw new ErrCycleExists();
|
|
393
475
|
}
|
|
394
476
|
marks[fId] = Mark.Temporary;
|
|
395
|
-
var dependentFeatureIds =
|
|
477
|
+
var dependentFeatureIds = getFeatureIDsDependsOn(f);
|
|
396
478
|
var _iterator8 = _createForOfIteratorHelper(dependentFeatureIds),
|
|
397
479
|
_step8;
|
|
398
480
|
try {
|
|
@@ -439,7 +521,7 @@ function getFeaturesDependedOnTargets(targets, all) {
|
|
|
439
521
|
// Add feature to evals
|
|
440
522
|
evals[f.getId()] = f;
|
|
441
523
|
// Get dependent features recursively
|
|
442
|
-
var featureDependencies =
|
|
524
|
+
var featureDependencies = getFeatureIDsDependsOn(f);
|
|
443
525
|
featureDependencies.forEach(function (fid) {
|
|
444
526
|
var target = all.get(fid);
|
|
445
527
|
if (target !== undefined) {
|
|
@@ -455,6 +537,24 @@ function getFeaturesDependedOnTargets(targets, all) {
|
|
|
455
537
|
}
|
|
456
538
|
// getFeaturesDependsOnTargets returns the features that depend on the target features.
|
|
457
539
|
// targetFeatures are included in the result.
|
|
540
|
+
//
|
|
541
|
+
// This function ensures complete transitive closure for incremental evaluation.
|
|
542
|
+
//
|
|
543
|
+
// Example scenario:
|
|
544
|
+
// Feature Dependencies: A ← B ← C, A ← D, E ← D
|
|
545
|
+
// Target: [C] (recently updated)
|
|
546
|
+
//
|
|
547
|
+
// Step 1 - DFS finds direct/transitive dependents of targets:
|
|
548
|
+
// - A depends on B, B depends on C (target) → Add A, B
|
|
549
|
+
// - E depends on D, D doesn't depend on C → Skip E
|
|
550
|
+
// Result: {C, A, B}
|
|
551
|
+
//
|
|
552
|
+
// Step 2 - Find dependencies of discovered dependents:
|
|
553
|
+
// - A depends on D (new!) → Add D
|
|
554
|
+
// Result: {C, A, B, D}
|
|
555
|
+
//
|
|
556
|
+
// Without Step 2: Evaluating A would fail with "feature D not found"
|
|
557
|
+
// With Step 2: Complete closure ensures all dependencies are available
|
|
458
558
|
function getFeaturesDependsOnTargets(targets, all) {
|
|
459
559
|
var evals = {};
|
|
460
560
|
// Mark target features in the evals map initially
|
|
@@ -466,12 +566,12 @@ function getFeaturesDependsOnTargets(targets, all) {
|
|
|
466
566
|
if (evals[f.getId()]) {
|
|
467
567
|
return true;
|
|
468
568
|
}
|
|
469
|
-
var featureDependencies =
|
|
470
|
-
var
|
|
471
|
-
|
|
569
|
+
var featureDependencies = getFeatureIDsDependsOn(f);
|
|
570
|
+
var _iterator0 = _createForOfIteratorHelper(featureDependencies),
|
|
571
|
+
_step0;
|
|
472
572
|
try {
|
|
473
|
-
for (
|
|
474
|
-
var fid =
|
|
573
|
+
for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
|
|
574
|
+
var fid = _step0.value;
|
|
475
575
|
var dependentFeature = all.get(fid);
|
|
476
576
|
if (dependentFeature && _dfs2(dependentFeature)) {
|
|
477
577
|
// If the feature depends on one of the target features, add it to evals
|
|
@@ -480,9 +580,9 @@ function getFeaturesDependsOnTargets(targets, all) {
|
|
|
480
580
|
}
|
|
481
581
|
}
|
|
482
582
|
} catch (err) {
|
|
483
|
-
|
|
583
|
+
_iterator0.e(err);
|
|
484
584
|
} finally {
|
|
485
|
-
|
|
585
|
+
_iterator0.f();
|
|
486
586
|
}
|
|
487
587
|
return false;
|
|
488
588
|
};
|
|
@@ -490,6 +590,56 @@ function getFeaturesDependsOnTargets(targets, all) {
|
|
|
490
590
|
all.forEach(function (f) {
|
|
491
591
|
_dfs2(f);
|
|
492
592
|
});
|
|
593
|
+
// Step 2: Ensure complete transitive closure
|
|
594
|
+
// The DFS above finds dependents (who depends on targets), but misses dependencies of those dependents.
|
|
595
|
+
// Example: If target C → dependent A → dependency D, we found A but missed D.
|
|
596
|
+
// Efficiently process only newly discovered features in each iteration.
|
|
597
|
+
var processed = new Set();
|
|
598
|
+
var queue = [];
|
|
599
|
+
for (var _i = 0, _Object$values = Object.values(evals); _i < _Object$values.length; _i++) {
|
|
600
|
+
var f = _Object$values[_i];
|
|
601
|
+
queue.push(f);
|
|
602
|
+
}
|
|
603
|
+
var maxIterations = 100; // Prevent infinite loops in case of circular dependencies
|
|
604
|
+
var iteration = 0;
|
|
605
|
+
while (queue.length > 0 && iteration < maxIterations) {
|
|
606
|
+
iteration++;
|
|
607
|
+
var nextQueue = [];
|
|
608
|
+
var _iterator1 = _createForOfIteratorHelper(queue),
|
|
609
|
+
_step1;
|
|
610
|
+
try {
|
|
611
|
+
for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
|
|
612
|
+
var _f = _step1.value;
|
|
613
|
+
if (processed.has(_f.getId())) {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
processed.add(_f.getId());
|
|
617
|
+
// Find dependencies of f
|
|
618
|
+
var featureDependencies = getFeatureIDsDependsOn(_f);
|
|
619
|
+
var _iterator10 = _createForOfIteratorHelper(featureDependencies),
|
|
620
|
+
_step10;
|
|
621
|
+
try {
|
|
622
|
+
for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
|
|
623
|
+
var depID = _step10.value;
|
|
624
|
+
var dep = all.get(depID);
|
|
625
|
+
if (dep && !evals[depID]) {
|
|
626
|
+
evals[depID] = dep;
|
|
627
|
+
nextQueue.push(dep);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
} catch (err) {
|
|
631
|
+
_iterator10.e(err);
|
|
632
|
+
} finally {
|
|
633
|
+
_iterator10.f();
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
} catch (err) {
|
|
637
|
+
_iterator1.e(err);
|
|
638
|
+
} finally {
|
|
639
|
+
_iterator1.f();
|
|
640
|
+
}
|
|
641
|
+
queue = nextQueue;
|
|
642
|
+
}
|
|
493
643
|
return evals;
|
|
494
644
|
}
|
|
495
645
|
function arrayToRecord(arr) {
|
|
@@ -500,4 +650,11 @@ function arrayToRecord(arr) {
|
|
|
500
650
|
acc[key] = value;
|
|
501
651
|
return acc;
|
|
502
652
|
}, {});
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Converts a YAML string to a JSON string.
|
|
656
|
+
*/
|
|
657
|
+
function yamlToJSON(yamlStr) {
|
|
658
|
+
var data = yaml.load(yamlStr);
|
|
659
|
+
return JSON.stringify(data);
|
|
503
660
|
}
|
package/lib/evaluation.mjs
CHANGED
|
@@ -1,16 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.Evaluator = void 0;
|
|
4
37
|
exports.EvaluationID = EvaluationID;
|
|
38
|
+
exports.getFeatureIDsDependsOn = getFeatureIDsDependsOn;
|
|
5
39
|
const errors_1 = require("./errors");
|
|
6
40
|
const clause_pb_1 = require("./proto/feature/clause_pb");
|
|
7
41
|
const evaluation_pb_1 = require("./proto/feature/evaluation_pb");
|
|
42
|
+
const feature_pb_1 = require("./proto/feature/feature_pb");
|
|
8
43
|
const reason_pb_1 = require("./proto/feature/reason_pb");
|
|
9
44
|
const variation_pb_1 = require("./proto/feature/variation_pb");
|
|
10
45
|
const ruleEvaluator_1 = require("./ruleEvaluator");
|
|
11
46
|
const strategyEvaluator_1 = require("./strategyEvaluator");
|
|
12
47
|
const userEvaluation_1 = require("./userEvaluation");
|
|
13
48
|
const modelFactory_1 = require("./modelFactory");
|
|
49
|
+
const yaml = __importStar(require("js-yaml"));
|
|
14
50
|
const SECONDS_TO_RE_EVALUATE_ALL = 30 * 24 * 60 * 60; // 30 days
|
|
15
51
|
const SECONDS_FOR_ADJUSTMENT = 10; // 10 seconds
|
|
16
52
|
function EvaluationID(featureID, featureVersion, userID) {
|
|
@@ -19,9 +55,13 @@ function EvaluationID(featureID, featureVersion, userID) {
|
|
|
19
55
|
class Evaluator {
|
|
20
56
|
ruleEvaluator;
|
|
21
57
|
strategyEvaluator;
|
|
58
|
+
// variationCache caches YAML to JSON conversions using variation ID as the key.
|
|
59
|
+
// Since variation IDs are UUIDs, they are globally unique and safe to use as cache keys.
|
|
60
|
+
variationCache;
|
|
22
61
|
constructor() {
|
|
23
62
|
this.ruleEvaluator = new ruleEvaluator_1.RuleEvaluator();
|
|
24
63
|
this.strategyEvaluator = new strategyEvaluator_1.StrategyEvaluator();
|
|
64
|
+
this.variationCache = new Map();
|
|
25
65
|
}
|
|
26
66
|
async evaluateFeatures(features, user, mapSegmentUsers, targetTag) {
|
|
27
67
|
return this.evaluate(features, user, mapSegmentUsers, false, targetTag);
|
|
@@ -61,6 +101,11 @@ class Evaluator {
|
|
|
61
101
|
const evaluations = [];
|
|
62
102
|
const archivedIDs = [];
|
|
63
103
|
for (const feature of sortedFeatures) {
|
|
104
|
+
const segmentUsers = this.listSegmentIDs(feature).flatMap((id) => mapSegmentUsers.get(id) || []);
|
|
105
|
+
const [reason, variation] = this.assignUser(feature, user, segmentUsers, flagVariations);
|
|
106
|
+
// VariationId is used to check if prerequisite flag's result is what user expects it to be.
|
|
107
|
+
// This must be set for ALL features (including archived) for dependency resolution to work
|
|
108
|
+
flagVariations[feature.getId()] = variation.getId();
|
|
64
109
|
if (feature.getArchived()) {
|
|
65
110
|
// To keep response size small, the feature flags archived long time ago are excluded.
|
|
66
111
|
if (!this.isArchivedBeforeLastThirtyDays(feature)) {
|
|
@@ -68,16 +113,14 @@ class Evaluator {
|
|
|
68
113
|
}
|
|
69
114
|
continue;
|
|
70
115
|
}
|
|
71
|
-
const segmentUsers = this.listSegmentIDs(feature).flatMap((id) => mapSegmentUsers.get(id) || []);
|
|
72
|
-
const [reason, variation] = this.assignUser(feature, user, segmentUsers, flagVariations);
|
|
73
|
-
// VariationId is used to check if prerequisite flag's result is what user expects it to be.
|
|
74
|
-
flagVariations[feature.getId()] = variation.getId();
|
|
75
116
|
// When the tag is set in the request,
|
|
76
117
|
// it will return only the evaluations of flags that match the tag configured on the dashboard.
|
|
77
118
|
// When empty, it will return all the evaluations of the flags in the environment.
|
|
78
119
|
if (targetTag !== '' && !this.tagExist(feature.getTagsList(), targetTag)) {
|
|
79
120
|
continue;
|
|
80
121
|
}
|
|
122
|
+
// Convert YAML to JSON for client SDKs if needed
|
|
123
|
+
const convertedValue = this.convertVariationValue(feature, variation);
|
|
81
124
|
const evaluationID = EvaluationID(feature.getId(), feature.getVersion(), user.getId());
|
|
82
125
|
const evaluation = new evaluation_pb_1.Evaluation();
|
|
83
126
|
evaluation.setId(evaluationID);
|
|
@@ -86,7 +129,7 @@ class Evaluator {
|
|
|
86
129
|
evaluation.setUserId(user.getId());
|
|
87
130
|
evaluation.setVariationId(variation.getId());
|
|
88
131
|
evaluation.setVariationName(variation.getName());
|
|
89
|
-
evaluation.setVariationValue(
|
|
132
|
+
evaluation.setVariationValue(convertedValue);
|
|
90
133
|
// Deprecated
|
|
91
134
|
// FIXME: Remove the Variation when is no longer being used.
|
|
92
135
|
// For security reasons, we should remove the variation description.
|
|
@@ -95,7 +138,7 @@ class Evaluator {
|
|
|
95
138
|
const copyVariation = new variation_pb_1.Variation();
|
|
96
139
|
copyVariation.setId(variation.getId());
|
|
97
140
|
copyVariation.setName(variation.getName());
|
|
98
|
-
copyVariation.setValue(
|
|
141
|
+
copyVariation.setValue(convertedValue);
|
|
99
142
|
evaluation.setVariation(copyVariation);
|
|
100
143
|
evaluation.setReason(reason);
|
|
101
144
|
evaluations.push(evaluation);
|
|
@@ -195,6 +238,38 @@ class Evaluator {
|
|
|
195
238
|
}
|
|
196
239
|
return variation;
|
|
197
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Converts YAML to JSON if needed, with in-memory caching.
|
|
243
|
+
* This ensures client SDKs can retrieve variation values using the object variation interface.
|
|
244
|
+
* Only performs conversion when the variation type is YAML.
|
|
245
|
+
* Cache key uses feature.updatedAt + variation.id to ensure cache invalidation when variations are updated.
|
|
246
|
+
*/
|
|
247
|
+
convertVariationValue(feature, variation) {
|
|
248
|
+
// Only convert if type is YAML
|
|
249
|
+
if (feature.getVariationType() !== feature_pb_1.Feature.VariationType.YAML) {
|
|
250
|
+
return variation.getValue();
|
|
251
|
+
}
|
|
252
|
+
// Cache key: {featureUpdatedAt}:{variationId}
|
|
253
|
+
// This ensures cache is invalidated when the feature (and its variations) are updated,
|
|
254
|
+
// including changes from auto operations that don't increment feature.version
|
|
255
|
+
const cacheKey = `${feature.getUpdatedAt()}:${variation.getId()}`;
|
|
256
|
+
// Check cache first
|
|
257
|
+
const cached = this.variationCache.get(cacheKey);
|
|
258
|
+
if (cached !== undefined) {
|
|
259
|
+
return cached;
|
|
260
|
+
}
|
|
261
|
+
// Convert YAML to JSON
|
|
262
|
+
try {
|
|
263
|
+
const jsonValue = yamlToJSON(variation.getValue());
|
|
264
|
+
// Cache the result for future requests
|
|
265
|
+
this.variationCache.set(cacheKey, jsonValue);
|
|
266
|
+
return jsonValue;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
// Return original value as fallback on error
|
|
270
|
+
return variation.getValue();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
198
273
|
}
|
|
199
274
|
exports.Evaluator = Evaluator;
|
|
200
275
|
var Mark;
|
|
@@ -204,7 +279,7 @@ var Mark;
|
|
|
204
279
|
Mark[Mark["Permanently"] = 2] = "Permanently";
|
|
205
280
|
})(Mark || (Mark = {}));
|
|
206
281
|
// FeatureIDsDependsOn returns the ids of the features that this feature depends on.
|
|
207
|
-
function
|
|
282
|
+
function getFeatureIDsDependsOn(feature) {
|
|
208
283
|
const ids = [];
|
|
209
284
|
// Iterate over prerequisites and add their FeatureId
|
|
210
285
|
feature.getPrerequisitesList().forEach((p) => {
|
|
@@ -248,7 +323,7 @@ function topologicalSort(features) {
|
|
|
248
323
|
throw new ErrCycleExists();
|
|
249
324
|
}
|
|
250
325
|
marks[fId] = Mark.Temporary;
|
|
251
|
-
const dependentFeatureIds =
|
|
326
|
+
const dependentFeatureIds = getFeatureIDsDependsOn(f);
|
|
252
327
|
for (const fid of dependentFeatureIds) {
|
|
253
328
|
const pf = mapFeatures[fid];
|
|
254
329
|
if (!pf) {
|
|
@@ -278,7 +353,7 @@ function getFeaturesDependedOnTargets(targets, all) {
|
|
|
278
353
|
// Add feature to evals
|
|
279
354
|
evals[f.getId()] = f;
|
|
280
355
|
// Get dependent features recursively
|
|
281
|
-
const featureDependencies =
|
|
356
|
+
const featureDependencies = getFeatureIDsDependsOn(f);
|
|
282
357
|
featureDependencies.forEach((fid) => {
|
|
283
358
|
const target = all.get(fid);
|
|
284
359
|
if (target !== undefined) {
|
|
@@ -292,6 +367,24 @@ function getFeaturesDependedOnTargets(targets, all) {
|
|
|
292
367
|
}
|
|
293
368
|
// getFeaturesDependsOnTargets returns the features that depend on the target features.
|
|
294
369
|
// targetFeatures are included in the result.
|
|
370
|
+
//
|
|
371
|
+
// This function ensures complete transitive closure for incremental evaluation.
|
|
372
|
+
//
|
|
373
|
+
// Example scenario:
|
|
374
|
+
// Feature Dependencies: A ← B ← C, A ← D, E ← D
|
|
375
|
+
// Target: [C] (recently updated)
|
|
376
|
+
//
|
|
377
|
+
// Step 1 - DFS finds direct/transitive dependents of targets:
|
|
378
|
+
// - A depends on B, B depends on C (target) → Add A, B
|
|
379
|
+
// - E depends on D, D doesn't depend on C → Skip E
|
|
380
|
+
// Result: {C, A, B}
|
|
381
|
+
//
|
|
382
|
+
// Step 2 - Find dependencies of discovered dependents:
|
|
383
|
+
// - A depends on D (new!) → Add D
|
|
384
|
+
// Result: {C, A, B, D}
|
|
385
|
+
//
|
|
386
|
+
// Without Step 2: Evaluating A would fail with "feature D not found"
|
|
387
|
+
// With Step 2: Complete closure ensures all dependencies are available
|
|
295
388
|
function getFeaturesDependsOnTargets(targets, all) {
|
|
296
389
|
const evals = {};
|
|
297
390
|
// Mark target features in the evals map initially
|
|
@@ -303,7 +396,7 @@ function getFeaturesDependsOnTargets(targets, all) {
|
|
|
303
396
|
if (evals[f.getId()]) {
|
|
304
397
|
return true;
|
|
305
398
|
}
|
|
306
|
-
const featureDependencies =
|
|
399
|
+
const featureDependencies = getFeatureIDsDependsOn(f);
|
|
307
400
|
for (const fid of featureDependencies) {
|
|
308
401
|
const dependentFeature = all.get(fid);
|
|
309
402
|
if (dependentFeature && dfs(dependentFeature)) {
|
|
@@ -318,6 +411,37 @@ function getFeaturesDependsOnTargets(targets, all) {
|
|
|
318
411
|
all.forEach((f) => {
|
|
319
412
|
dfs(f);
|
|
320
413
|
});
|
|
414
|
+
// Step 2: Ensure complete transitive closure
|
|
415
|
+
// The DFS above finds dependents (who depends on targets), but misses dependencies of those dependents.
|
|
416
|
+
// Example: If target C → dependent A → dependency D, we found A but missed D.
|
|
417
|
+
// Efficiently process only newly discovered features in each iteration.
|
|
418
|
+
const processed = new Set();
|
|
419
|
+
let queue = [];
|
|
420
|
+
for (const f of Object.values(evals)) {
|
|
421
|
+
queue.push(f);
|
|
422
|
+
}
|
|
423
|
+
const maxIterations = 100; // Prevent infinite loops in case of circular dependencies
|
|
424
|
+
let iteration = 0;
|
|
425
|
+
while (queue.length > 0 && iteration < maxIterations) {
|
|
426
|
+
iteration++;
|
|
427
|
+
const nextQueue = [];
|
|
428
|
+
for (const f of queue) {
|
|
429
|
+
if (processed.has(f.getId())) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
processed.add(f.getId());
|
|
433
|
+
// Find dependencies of f
|
|
434
|
+
const featureDependencies = getFeatureIDsDependsOn(f);
|
|
435
|
+
for (const depID of featureDependencies) {
|
|
436
|
+
const dep = all.get(depID);
|
|
437
|
+
if (dep && !evals[depID]) {
|
|
438
|
+
evals[depID] = dep;
|
|
439
|
+
nextQueue.push(dep);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
queue = nextQueue;
|
|
444
|
+
}
|
|
321
445
|
return evals;
|
|
322
446
|
}
|
|
323
447
|
function arrayToRecord(arr) {
|
|
@@ -326,3 +450,10 @@ function arrayToRecord(arr) {
|
|
|
326
450
|
return acc;
|
|
327
451
|
}, {});
|
|
328
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Converts a YAML string to a JSON string.
|
|
455
|
+
*/
|
|
456
|
+
function yamlToJSON(yamlStr) {
|
|
457
|
+
const data = yaml.load(yamlStr);
|
|
458
|
+
return JSON.stringify(data);
|
|
459
|
+
}
|