@terreno/feature-flags 57.3.0 → 57.6.0

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/README.md CHANGED
@@ -39,10 +39,10 @@ Pass `liveUpdates: {socketIoServer: io}` to broadcast flag-key changes over Sock
39
39
 
40
40
  ## Documentation
41
41
 
42
- Full API reference: [docs/reference/feature-flags.md](https://github.com/flourishhealth/terreno/blob/master/docs/reference/feature-flags.md)
42
+ Full API reference: [docs/reference/feature-flags.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/reference/feature-flags.md)
43
43
 
44
- How-to: [Add feature flags](https://github.com/flourishhealth/terreno/blob/master/docs/how-to/add-feature-flags.md)
44
+ How-to: [Add feature flags](https://github.com/TerrenoLabs/terreno/blob/master/docs/how-to/add-feature-flags.md)
45
45
 
46
46
  ## License and Contributing
47
47
 
48
- Licensed under the [MIT License](https://github.com/flourishhealth/terreno/blob/master/LICENSE). See [CONTRIBUTING.md](https://github.com/flourishhealth/terreno/blob/master/CONTRIBUTING.md) for contribution guidelines.
48
+ Licensed under the [MIT License](https://github.com/TerrenoLabs/terreno/blob/master/LICENSE). See [CONTRIBUTING.md](https://github.com/TerrenoLabs/terreno/blob/master/CONTRIBUTING.md) for contribution guidelines.
package/dist/evaluate.js CHANGED
@@ -1,79 +1,32 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- var __values = (this && this.__values) || function(o) {
39
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
40
- if (m) return m.call(o);
41
- if (o && typeof o.length === "number") return {
42
- next: function () {
43
- if (o && i >= o.length) o = void 0;
44
- return { value: o && o[i++], done: !o };
45
- }
46
- };
47
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
48
- };
49
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
50
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
4
  };
52
5
  Object.defineProperty(exports, "__esModule", { value: true });
53
6
  exports.evaluateAllFlags = exports.evaluateFlag = exports.deterministicHash = void 0;
54
- var api_1 = require("@terreno/api");
55
- var get_1 = __importDefault(require("lodash/get"));
56
- var isDebugEnabled = function () { return process.env.FEATURE_FLAGS_DEBUG !== "false"; };
7
+ const api_1 = require("@terreno/api");
8
+ const get_1 = __importDefault(require("lodash/get"));
9
+ const isDebugEnabled = () => process.env.FEATURE_FLAGS_DEBUG !== "false";
57
10
  /**
58
11
  * Deterministic hash of a string to a number between 0 and 99.
59
12
  * Uses a simple but effective hash (djb2) to ensure the same user+flag
60
13
  * combination always produces the same result.
61
14
  */
62
- var deterministicHash = function (input) {
63
- var hash = 5381;
64
- for (var i = 0; i < input.length; i++) {
15
+ const deterministicHash = (input) => {
16
+ let hash = 5381;
17
+ for (let i = 0; i < input.length; i++) {
65
18
  hash = (hash * 33) ^ input.charCodeAt(i);
66
19
  }
67
20
  return Math.abs(hash) % 100;
68
21
  };
69
22
  exports.deterministicHash = deterministicHash;
70
- var matchesFieldRule = function (user, rule) {
23
+ const matchesFieldRule = (user, rule) => {
71
24
  if (!rule.field || !rule.operator) {
72
25
  return false;
73
26
  }
74
- var fieldValue = (0, get_1.default)(user, rule.field);
75
- var ruleValue = rule.value;
76
- var result = false;
27
+ const fieldValue = (0, get_1.default)(user, rule.field);
28
+ const ruleValue = rule.value;
29
+ let result = false;
77
30
  switch (rule.operator) {
78
31
  case "eq":
79
32
  result = ruleValue !== undefined && fieldValue === ruleValue;
@@ -105,26 +58,26 @@ var matchesFieldRule = function (user, rule) {
105
58
  break;
106
59
  }
107
60
  if (isDebugEnabled()) {
108
- api_1.logger.info("[feature-flags] field rule: user.".concat(rule.field, "=").concat(JSON.stringify(fieldValue), " ").concat(rule.operator, " ").concat(JSON.stringify(ruleValue), " \u2192 ").concat(result));
61
+ api_1.logger.info(`[feature-flags] field rule: user.${rule.field}=${JSON.stringify(fieldValue)} ${rule.operator} ${JSON.stringify(ruleValue)} → ${result}`);
109
62
  }
110
63
  return result;
111
64
  };
112
- var matchesRule = function (user, rule, segments) {
65
+ const matchesRule = (user, rule, segments) => {
113
66
  if (rule.segment) {
114
- var segmentFn = segments[rule.segment];
67
+ const segmentFn = segments[rule.segment];
115
68
  if (!segmentFn) {
116
- api_1.logger.warn("Segment function not found: ".concat(rule.segment));
69
+ api_1.logger.warn(`Segment function not found: ${rule.segment}`);
117
70
  return false;
118
71
  }
119
72
  try {
120
- var result = segmentFn(user);
73
+ const result = segmentFn(user);
121
74
  if (isDebugEnabled()) {
122
- api_1.logger.info("[feature-flags] segment rule: \"".concat(rule.segment, "\" \u2192 ").concat(result));
75
+ api_1.logger.info(`[feature-flags] segment rule: "${rule.segment}" → ${result}`);
123
76
  }
124
77
  return result;
125
78
  }
126
79
  catch (err) {
127
- api_1.logger.warn("Segment function \"".concat(rule.segment, "\" threw: ").concat(err));
80
+ api_1.logger.warn(`Segment function "${rule.segment}" threw: ${err}`);
128
81
  return false;
129
82
  }
130
83
  }
@@ -134,62 +87,50 @@ var matchesRule = function (user, rule, segments) {
134
87
  * Evaluate a single feature flag for a user.
135
88
  * Returns boolean for boolean flags, string key for variant flags, or null for disabled variant flags.
136
89
  */
137
- var evaluateFlag = function (flag, userId, user, segments) {
138
- var e_1, _a;
139
- var _b, _c;
140
- var debug = isDebugEnabled();
90
+ const evaluateFlag = (flag, userId, user, segments) => {
91
+ const debug = isDebugEnabled();
141
92
  if (!flag.enabled) {
142
93
  if (debug) {
143
- api_1.logger.info("[feature-flags] \"".concat(flag.key, "\" is disabled \u2192 ").concat(flag.type === "variant" ? "null" : "false"));
94
+ api_1.logger.info(`[feature-flags] "${flag.key}" is disabled → ${flag.type === "variant" ? "null" : "false"}`);
144
95
  }
145
96
  return flag.type === "variant" ? null : false;
146
97
  }
147
98
  // Check rules in order — first match wins
148
- for (var i = 0; i < flag.rules.length; i++) {
149
- var rule = flag.rules[i];
150
- var matched = matchesRule(user, rule, segments);
99
+ for (let i = 0; i < flag.rules.length; i++) {
100
+ const rule = flag.rules[i];
101
+ const matched = matchesRule(user, rule, segments);
151
102
  if (matched) {
152
- var result = flag.type === "variant" ? ((_b = rule.variant) !== null && _b !== void 0 ? _b : null) : ((_c = rule.enabled) !== null && _c !== void 0 ? _c : false);
103
+ const result = flag.type === "variant" ? (rule.variant ?? null) : (rule.enabled ?? false);
153
104
  if (debug) {
154
- api_1.logger.info("[feature-flags] \"".concat(flag.key, "\" matched rule ").concat(i, " \u2192 ").concat(JSON.stringify(result)));
105
+ api_1.logger.info(`[feature-flags] "${flag.key}" matched rule ${i} → ${JSON.stringify(result)}`);
155
106
  }
156
107
  return result;
157
108
  }
158
109
  }
159
110
  // No rules matched — use deterministic hashing
160
- var hash = (0, exports.deterministicHash)("".concat(userId, ":").concat(flag.key));
111
+ const hash = (0, exports.deterministicHash)(`${userId}:${flag.key}`);
161
112
  if (flag.type === "boolean") {
162
- var result = hash < flag.rolloutPercentage;
113
+ const result = hash < flag.rolloutPercentage;
163
114
  if (debug) {
164
- api_1.logger.info("[feature-flags] \"".concat(flag.key, "\" no rules matched, hash=").concat(hash, " rollout=").concat(flag.rolloutPercentage, "% \u2192 ").concat(result));
115
+ api_1.logger.info(`[feature-flags] "${flag.key}" no rules matched, hash=${hash} rollout=${flag.rolloutPercentage}% → ${result}`);
165
116
  }
166
117
  return result;
167
118
  }
168
119
  // Variant assignment based on cumulative weights
169
- var cumulativeWeight = 0;
170
- try {
171
- for (var _d = __values(flag.variants), _e = _d.next(); !_e.done; _e = _d.next()) {
172
- var variant = _e.value;
173
- cumulativeWeight += variant.weight;
174
- if (hash < cumulativeWeight) {
175
- if (debug) {
176
- api_1.logger.info("[feature-flags] \"".concat(flag.key, "\" no rules matched, hash=").concat(hash, " \u2192 variant \"").concat(variant.key, "\""));
177
- }
178
- return variant.key;
120
+ let cumulativeWeight = 0;
121
+ for (const variant of flag.variants) {
122
+ cumulativeWeight += variant.weight;
123
+ if (hash < cumulativeWeight) {
124
+ if (debug) {
125
+ api_1.logger.info(`[feature-flags] "${flag.key}" no rules matched, hash=${hash} → variant "${variant.key}"`);
179
126
  }
127
+ return variant.key;
180
128
  }
181
129
  }
182
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
183
- finally {
184
- try {
185
- if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
186
- }
187
- finally { if (e_1) throw e_1.error; }
188
- }
189
130
  // Fallback (shouldn't happen if weights sum to 100)
190
- var fallback = flag.variants.length > 0 ? flag.variants[flag.variants.length - 1].key : null;
131
+ const fallback = flag.variants.length > 0 ? flag.variants[flag.variants.length - 1].key : null;
191
132
  if (debug) {
192
- api_1.logger.info("[feature-flags] \"".concat(flag.key, "\" variant fallback \u2192 ").concat(JSON.stringify(fallback)));
133
+ api_1.logger.info(`[feature-flags] "${flag.key}" variant fallback → ${JSON.stringify(fallback)}`);
193
134
  }
194
135
  return fallback;
195
136
  };
@@ -198,36 +139,18 @@ exports.evaluateFlag = evaluateFlag;
198
139
  * Evaluate all enabled, non-archived feature flags for a user.
199
140
  * Returns a map of flag key to evaluation result.
200
141
  */
201
- var evaluateAllFlags = function (flagModel, userId, user, segments) { return __awaiter(void 0, void 0, void 0, function () {
202
- var flags, results, flags_1, flags_1_1, flag;
203
- var e_2, _a;
204
- return __generator(this, function (_b) {
205
- switch (_b.label) {
206
- case 0: return [4 /*yield*/, flagModel.find({ archived: { $ne: true }, enabled: true })];
207
- case 1:
208
- flags = _b.sent();
209
- results = {};
210
- if (isDebugEnabled()) {
211
- api_1.logger.info("[feature-flags] evaluating ".concat(flags.length, " flags for user ").concat(userId));
212
- }
213
- try {
214
- for (flags_1 = __values(flags), flags_1_1 = flags_1.next(); !flags_1_1.done; flags_1_1 = flags_1.next()) {
215
- flag = flags_1_1.value;
216
- results[flag.key] = (0, exports.evaluateFlag)(flag, userId, user, segments);
217
- }
218
- }
219
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
220
- finally {
221
- try {
222
- if (flags_1_1 && !flags_1_1.done && (_a = flags_1.return)) _a.call(flags_1);
223
- }
224
- finally { if (e_2) throw e_2.error; }
225
- }
226
- if (isDebugEnabled()) {
227
- api_1.logger.info("[feature-flags] results: ".concat(JSON.stringify(results)));
228
- }
229
- return [2 /*return*/, results];
230
- }
231
- });
232
- }); };
142
+ const evaluateAllFlags = async (flagModel, userId, user, segments) => {
143
+ const flags = await flagModel.find({ archived: { $ne: true }, enabled: true });
144
+ const results = {};
145
+ if (isDebugEnabled()) {
146
+ api_1.logger.info(`[feature-flags] evaluating ${flags.length} flags for user ${userId}`);
147
+ }
148
+ for (const flag of flags) {
149
+ results[flag.key] = (0, exports.evaluateFlag)(flag, userId, user, segments);
150
+ }
151
+ if (isDebugEnabled()) {
152
+ api_1.logger.info(`[feature-flags] results: ${JSON.stringify(results)}`);
153
+ }
154
+ return results;
155
+ };
233
156
  exports.evaluateAllFlags = evaluateAllFlags;
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.featureFlagAdminConfig = exports.FeatureFlag = void 0;
7
- var api_1 = require("@terreno/api");
8
- var mongoose_1 = __importDefault(require("mongoose"));
9
- var featureFlagSchema = new mongoose_1.default.Schema({
7
+ const api_1 = require("@terreno/api");
8
+ const mongoose_1 = __importDefault(require("mongoose"));
9
+ const featureFlagSchema = new mongoose_1.default.Schema({
10
10
  archived: {
11
11
  default: false,
12
12
  description: "Archived flags are excluded from evaluation. Use this instead of deleting flags to prevent bloat as new features are added.",
@@ -106,7 +106,6 @@ featureFlagSchema.plugin(api_1.findExactlyOne);
106
106
  featureFlagSchema.plugin(api_1.findOneOrNone);
107
107
  featureFlagSchema.index({ archived: 1, enabled: 1 });
108
108
  featureFlagSchema.pre("save", function () {
109
- var _a;
110
109
  if (this.type === "variant") {
111
110
  if (!this.variants || this.variants.length === 0) {
112
111
  throw new api_1.APIError({
@@ -114,10 +113,10 @@ featureFlagSchema.pre("save", function () {
114
113
  title: "Variant flags must have at least one variant",
115
114
  });
116
115
  }
117
- var totalWeight = this.variants.reduce(function (sum, v) { return sum + v.weight; }, 0);
116
+ const totalWeight = this.variants.reduce((sum, v) => sum + v.weight, 0);
118
117
  if (totalWeight !== 100) {
119
118
  throw new api_1.APIError({
120
- detail: "Total weight is ".concat(totalWeight, ", expected 100"),
119
+ detail: `Total weight is ${totalWeight}, expected 100`,
121
120
  status: 400,
122
121
  title: "Variant weights must sum to 100",
123
122
  });
@@ -129,7 +128,7 @@ featureFlagSchema.pre("save", function () {
129
128
  if (this.type === "boolean") {
130
129
  this.set("defaultVariant", "off");
131
130
  }
132
- else if (this.type === "variant" && ((_a = this.variants) === null || _a === void 0 ? void 0 : _a.length)) {
131
+ else if (this.type === "variant" && this.variants?.length) {
133
132
  this.set("defaultVariant", this.variants[0].key);
134
133
  }
135
134
  }
@@ -143,7 +142,7 @@ featureFlagSchema.pre("save", function () {
143
142
  }
144
143
  }
145
144
  else if (this.type === "variant") {
146
- var keys = new Set(this.variants.map(function (v) { return v.key; }));
145
+ const keys = new Set(this.variants.map((v) => v.key));
147
146
  if (!keys.has(this.defaultVariant)) {
148
147
  throw new api_1.APIError({
149
148
  status: 400,
@@ -171,15 +170,15 @@ exports.FeatureFlag = mongoose_1.default.model("FeatureFlag", featureFlagSchema)
171
170
  * .start();
172
171
  * ```
173
172
  */
174
- var featureFlagAdminConfigValue = {
173
+ const featureFlagAdminConfigValue = {
175
174
  displayName: "Feature Flags",
176
175
  listFields: ["key", "name", "type", "enabled", "archived", "defaultVariant", "created"],
177
176
  model: exports.FeatureFlag,
178
177
  routePath: "/feature-flags",
179
178
  };
180
- var warnedLegacyAdminConfigs = new WeakSet();
179
+ const warnedLegacyAdminConfigs = new WeakSet();
181
180
  exports.featureFlagAdminConfig = new Proxy(featureFlagAdminConfigValue, {
182
- get: function (target, property, receiver) {
181
+ get(target, property, receiver) {
183
182
  if (!warnedLegacyAdminConfigs.has(target)) {
184
183
  warnedLegacyAdminConfigs.add(target);
185
184
  api_1.logger.warn("[feature-flags] featureFlagAdminConfig is deprecated. Use new FeatureFlagsApp() and register it with TerrenoApp — see docs/how-to/admin-import-prebuilt.md");
@@ -1,77 +1,19 @@
1
1
  "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
- return new (P || (P = Promise))(function (resolve, reject) {
16
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
- step((generator = generator.apply(thisArg, _arguments || [])).next());
20
- });
21
- };
22
- var __generator = (this && this.__generator) || function (thisArg, body) {
23
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
- function verb(n) { return function (v) { return step([n, v]); }; }
26
- function step(op) {
27
- if (f) throw new TypeError("Generator is already executing.");
28
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
- if (y = 0, t) op = [op[0] & 2, t.value];
31
- switch (op[0]) {
32
- case 0: case 1: t = op; break;
33
- case 4: _.label++; return { value: op[1], done: false };
34
- case 5: _.label++; y = op[1]; op = [0]; continue;
35
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
- default:
37
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
- if (t[2]) _.ops.pop();
42
- _.trys.pop(); continue;
43
- }
44
- op = body.call(thisArg, _);
45
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
- }
48
- };
49
- var __values = (this && this.__values) || function(o) {
50
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
51
- if (m) return m.call(o);
52
- if (o && typeof o.length === "number") return {
53
- next: function () {
54
- if (o && i >= o.length) o = void 0;
55
- return { value: o && o[i++], done: !o };
56
- }
57
- };
58
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
59
- };
60
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
61
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
62
4
  };
63
5
  Object.defineProperty(exports, "__esModule", { value: true });
64
6
  exports.FeatureFlagsApp = void 0;
65
- var server_sdk_1 = require("@openfeature/server-sdk");
66
- var api_1 = require("@terreno/api");
67
- var mongoose_1 = __importDefault(require("mongoose"));
68
- var evaluate_1 = require("./evaluate");
69
- var featureFlagModel_1 = require("./featureFlagModel");
70
- var flagConfiguration_1 = require("./flagConfiguration");
71
- var openFeatureProvider_1 = require("./openFeatureProvider");
72
- var evaluateDeprecationWarned = false;
73
- var resolveSocketServer = function (live) {
74
- var server = live.socketIoServer;
7
+ const server_sdk_1 = require("@openfeature/server-sdk");
8
+ const api_1 = require("@terreno/api");
9
+ const mongoose_1 = __importDefault(require("mongoose"));
10
+ const evaluate_1 = require("./evaluate");
11
+ const featureFlagModel_1 = require("./featureFlagModel");
12
+ const flagConfiguration_1 = require("./flagConfiguration");
13
+ const openFeatureProvider_1 = require("./openFeatureProvider");
14
+ let evaluateDeprecationWarned = false;
15
+ const resolveSocketServer = (live) => {
16
+ const server = live.socketIoServer;
75
17
  if (typeof server === "function") {
76
18
  return server();
77
19
  }
@@ -95,13 +37,15 @@ var resolveSocketServer = function (live) {
95
37
  * .start();
96
38
  * ```
97
39
  */
98
- var FeatureFlagsApp = /** @class */ (function () {
99
- function FeatureFlagsApp(options) {
100
- var _a;
101
- this.options = options !== null && options !== void 0 ? options : {};
102
- this.segments = (_a = this.options.segments) !== null && _a !== void 0 ? _a : {};
40
+ class FeatureFlagsApp {
41
+ options;
42
+ segments;
43
+ mongoProvider;
44
+ constructor(options) {
45
+ this.options = options ?? {};
46
+ this.segments = this.options.segments ?? {};
103
47
  }
104
- FeatureFlagsApp.prototype.adminContribution = function () {
48
+ adminContribution() {
105
49
  return {
106
50
  homeWidgets: [{ displayName: "Flag Overrides", icon: "flag", id: "feature-flags-overrides" }],
107
51
  models: [
@@ -118,146 +62,114 @@ var FeatureFlagsApp = /** @class */ (function () {
118
62
  },
119
63
  ],
120
64
  };
121
- };
122
- FeatureFlagsApp.prototype.register = function (app, openApi) {
123
- var _this = this;
124
- var _a, _b, _c, _d;
125
- var basePath = (_a = this.options.basePath) !== null && _a !== void 0 ? _a : "/feature-flags";
126
- var openFeatureDomain = (_b = this.options.openFeatureDomain) !== null && _b !== void 0 ? _b : "feature-flags";
127
- var routerOptions = __assign(__assign({}, (openApi ? { openApi: openApi } : {})), { permissions: (_c = this.options.permissions) !== null && _c !== void 0 ? _c : {
65
+ }
66
+ register(app, openApi) {
67
+ const basePath = this.options.basePath ?? "/feature-flags";
68
+ const openFeatureDomain = this.options.openFeatureDomain ?? "feature-flags";
69
+ const routerOptions = {
70
+ ...(openApi ? { openApi: openApi } : {}),
71
+ permissions: this.options.permissions ?? {
128
72
  create: [api_1.Permissions.IsAdmin],
129
73
  delete: [api_1.Permissions.IsAdmin],
130
74
  list: [api_1.Permissions.IsAdmin],
131
75
  read: [api_1.Permissions.IsAdmin],
132
76
  update: [api_1.Permissions.IsAdmin],
133
- }, sort: "-created" });
77
+ },
78
+ sort: "-created",
79
+ };
134
80
  this.mongoProvider = new openFeatureProvider_1.MongoFeatureFlagProvider({
135
81
  flagModel: featureFlagModel_1.FeatureFlag,
136
82
  segments: this.segments,
137
83
  });
138
84
  server_sdk_1.OpenFeature.setProvider(openFeatureDomain, this.mongoProvider);
139
- app.use("".concat(basePath, "/flags"), (0, api_1.modelRouter)(featureFlagModel_1.FeatureFlag, routerOptions));
140
- app.get("".concat(basePath, "/flagConfiguration"), (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
141
- var user, targetingKey, flags, config, flags_1, flags_1_1, flag, body;
142
- var e_1, _a;
143
- var _b;
144
- return __generator(this, function (_c) {
145
- switch (_c.label) {
146
- case 0:
147
- user = req.user;
148
- if (!user) {
149
- throw new api_1.APIError({ status: 401, title: "Authentication required" });
150
- }
151
- targetingKey = String((_b = user._id) !== null && _b !== void 0 ? _b : user.id);
152
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.find({ archived: { $ne: true }, enabled: true })];
153
- case 1:
154
- flags = _c.sent();
155
- config = {};
156
- try {
157
- for (flags_1 = __values(flags), flags_1_1 = flags_1.next(); !flags_1_1.done; flags_1_1 = flags_1.next()) {
158
- flag = flags_1_1.value;
159
- config[flag.key] = (0, flagConfiguration_1.buildFlagDefinition)(flag, targetingKey, user, this.segments);
160
- }
161
- }
162
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
163
- finally {
164
- try {
165
- if (flags_1_1 && !flags_1_1.done && (_a = flags_1.return)) _a.call(flags_1);
166
- }
167
- finally { if (e_1) throw e_1.error; }
168
- }
169
- body = { data: config };
170
- return [2 /*return*/, res.json(body)];
171
- }
172
- });
173
- }); }));
174
- app.get("".concat(basePath, "/evaluate"), (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
175
- var sunset, user, userId, results;
176
- var _a;
177
- return __generator(this, function (_b) {
178
- switch (_b.label) {
179
- case 0:
180
- if (!evaluateDeprecationWarned) {
181
- evaluateDeprecationWarned = true;
182
- api_1.logger.warn("[feature-flags] GET /evaluate is deprecated; migrate clients to GET /flagConfiguration or use @terreno/rtk useFeatureFlags / useTerrenoFeatureFlags.");
183
- }
184
- sunset = new Date(Date.now() + 90 * 86400000).toUTCString();
185
- res.setHeader("Deprecation", "true");
186
- res.setHeader("Sunset", sunset);
187
- user = req.user;
188
- if (!user) {
189
- throw new api_1.APIError({ status: 401, title: "Authentication required" });
190
- }
191
- userId = String((_a = user._id) !== null && _a !== void 0 ? _a : user.id);
192
- return [4 /*yield*/, (0, evaluate_1.evaluateAllFlags)(featureFlagModel_1.FeatureFlag, userId, user, this.segments)];
193
- case 1:
194
- results = _b.sent();
195
- return [2 /*return*/, res.json({ data: results })];
196
- }
197
- });
198
- }); }));
199
- app.get("".concat(basePath, "/segments"), (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
200
- var user, allowed;
201
- return __generator(this, function (_a) {
202
- user = req.user;
203
- allowed = this.options.segmentsPermission
204
- ? this.options.segmentsPermission(user)
205
- : Boolean(user === null || user === void 0 ? void 0 : user.admin);
206
- if (!allowed) {
207
- throw new api_1.APIError({ status: 403, title: "Only admins can view segments" });
208
- }
209
- return [2 /*return*/, res.json({ data: Object.keys(this.segments) })];
210
- });
211
- }); }));
212
- var live = this.options.liveUpdates;
85
+ app.use(`${basePath}/flags`, (0, api_1.modelRouter)(featureFlagModel_1.FeatureFlag, routerOptions));
86
+ app.get(`${basePath}/flagConfiguration`, (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(async (req, res) => {
87
+ const user = req.user;
88
+ if (!user) {
89
+ throw new api_1.APIError({ status: 401, title: "Authentication required" });
90
+ }
91
+ const targetingKey = String(user._id ?? user.id);
92
+ const flags = await featureFlagModel_1.FeatureFlag.find({ archived: { $ne: true }, enabled: true });
93
+ const config = {};
94
+ for (const flag of flags) {
95
+ config[flag.key] = (0, flagConfiguration_1.buildFlagDefinition)(flag, targetingKey, user, this.segments);
96
+ }
97
+ const body = { data: config };
98
+ return res.json(body);
99
+ }));
100
+ app.get(`${basePath}/evaluate`, (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(async (req, res) => {
101
+ if (!evaluateDeprecationWarned) {
102
+ evaluateDeprecationWarned = true;
103
+ api_1.logger.warn("[feature-flags] GET /evaluate is deprecated; migrate clients to GET /flagConfiguration or use @terreno/rtk useFeatureFlags / useTerrenoFeatureFlags.");
104
+ }
105
+ const sunset = new Date(Date.now() + 90 * 86400000).toUTCString();
106
+ res.setHeader("Deprecation", "true");
107
+ res.setHeader("Sunset", sunset);
108
+ const user = req.user;
109
+ if (!user) {
110
+ throw new api_1.APIError({ status: 401, title: "Authentication required" });
111
+ }
112
+ const userId = String(user._id ?? user.id);
113
+ const results = await (0, evaluate_1.evaluateAllFlags)(featureFlagModel_1.FeatureFlag, userId, user, this.segments);
114
+ return res.json({ data: results });
115
+ }));
116
+ app.get(`${basePath}/segments`, (0, api_1.authenticateMiddleware)(), (0, api_1.asyncHandler)(async (req, res) => {
117
+ const user = req.user;
118
+ const allowed = this.options.segmentsPermission
119
+ ? this.options.segmentsPermission(user)
120
+ : Boolean(user?.admin);
121
+ if (!allowed) {
122
+ throw new api_1.APIError({ status: 403, title: "Only admins can view segments" });
123
+ }
124
+ return res.json({ data: Object.keys(this.segments) });
125
+ }));
126
+ const live = this.options.liveUpdates;
213
127
  if (live && this.mongoProvider) {
214
- var eventName_1 = (_d = live.eventName) !== null && _d !== void 0 ? _d : "featureFlagsChanged";
215
- var stream_1 = null;
216
- var didRetry_1 = false;
217
- var cleanupStream_1 = function () {
128
+ const eventName = live.eventName ?? "featureFlagsChanged";
129
+ let stream = null;
130
+ let didRetry = false;
131
+ const cleanupStream = () => {
218
132
  try {
219
- stream_1 === null || stream_1 === void 0 ? void 0 : stream_1.close();
133
+ stream?.close();
220
134
  }
221
- catch (_a) {
135
+ catch {
222
136
  /* ignore close errors */
223
137
  }
224
- stream_1 = null;
138
+ stream = null;
225
139
  };
226
- var bindStream_1 = function () {
140
+ const bindStream = () => {
227
141
  try {
228
- stream_1 = featureFlagModel_1.FeatureFlag.watch([], { fullDocument: "updateLookup" });
142
+ stream = featureFlagModel_1.FeatureFlag.watch([], { fullDocument: "updateLookup" });
229
143
  }
230
144
  catch (err) {
231
- api_1.logger.warn("[feature-flags] FeatureFlag.watch() failed \u2014 live updates require MongoDB as a replica set (even single-node). ".concat(String(err)));
145
+ api_1.logger.warn(`[feature-flags] FeatureFlag.watch() failed — live updates require MongoDB as a replica set (even single-node). ${String(err)}`);
232
146
  return;
233
147
  }
234
- stream_1.on("change", function (change) {
235
- var _a, _b;
236
- var key = (_a = change.fullDocument) === null || _a === void 0 ? void 0 : _a.key;
237
- var ioResolved = resolveSocketServer(live);
148
+ stream.on("change", (change) => {
149
+ const key = change.fullDocument?.key;
150
+ const ioResolved = resolveSocketServer(live);
238
151
  if (ioResolved) {
239
- ioResolved.emit(eventName_1, { key: key });
152
+ ioResolved.emit(eventName, { key });
240
153
  }
241
- (_b = _this.mongoProvider) === null || _b === void 0 ? void 0 : _b.emitConfigurationChanged();
154
+ this.mongoProvider?.emitConfigurationChanged();
242
155
  });
243
- stream_1.on("error", function (err) {
244
- api_1.logger.warn("[feature-flags] change stream error: ".concat(String(err)));
245
- cleanupStream_1();
246
- if (!didRetry_1) {
247
- didRetry_1 = true;
248
- bindStream_1();
156
+ stream.on("error", (err) => {
157
+ api_1.logger.warn(`[feature-flags] change stream error: ${String(err)}`);
158
+ cleanupStream();
159
+ if (!didRetry) {
160
+ didRetry = true;
161
+ bindStream();
249
162
  return;
250
163
  }
251
164
  api_1.logger.warn("[feature-flags] change stream disabled after repeated errors");
252
165
  });
253
166
  };
254
- bindStream_1();
255
- var onConnectionClose = function () {
256
- cleanupStream_1();
167
+ bindStream();
168
+ const onConnectionClose = () => {
169
+ cleanupStream();
257
170
  };
258
171
  mongoose_1.default.connection.on("close", onConnectionClose);
259
172
  }
260
- };
261
- return FeatureFlagsApp;
262
- }());
173
+ }
174
+ }
263
175
  exports.FeatureFlagsApp = FeatureFlagsApp;
@@ -1,74 +1,51 @@
1
1
  "use strict";
2
- var __values = (this && this.__values) || function(o) {
3
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
- if (m) return m.call(o);
5
- if (o && typeof o.length === "number") return {
6
- next: function () {
7
- if (o && i >= o.length) o = void 0;
8
- return { value: o && o[i++], done: !o };
9
- }
10
- };
11
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
- };
13
2
  Object.defineProperty(exports, "__esModule", { value: true });
14
3
  exports.buildFlagDefinition = exports.effectiveDefaultVariantForFlag = void 0;
15
- var evaluate_1 = require("./evaluate");
4
+ const evaluate_1 = require("./evaluate");
16
5
  /**
17
6
  * Resolves the OpenFeature defaultVariant for a flag document, including legacy
18
7
  * documents that omit the field.
19
8
  */
20
- var effectiveDefaultVariantForFlag = function (flag) {
21
- var _a, _b;
9
+ const effectiveDefaultVariantForFlag = (flag) => {
22
10
  if (flag.defaultVariant) {
23
11
  return flag.defaultVariant;
24
12
  }
25
13
  if (flag.type === "boolean") {
26
14
  return "off";
27
15
  }
28
- return (_b = (_a = flag.variants[0]) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : "off";
16
+ return flag.variants[0]?.key ?? "off";
29
17
  };
30
18
  exports.effectiveDefaultVariantForFlag = effectiveDefaultVariantForFlag;
31
19
  /**
32
20
  * Builds a {@link FlagDefinition} for the `/flagConfiguration` wire shape from a
33
21
  * persisted flag and the resolved evaluation for one user.
34
22
  */
35
- var buildFlagDefinition = function (flag, targetingKey, user, segments) {
36
- var e_1, _a;
37
- var evaluated = (0, evaluate_1.evaluateFlag)(flag, targetingKey, user, segments);
23
+ const buildFlagDefinition = (flag, targetingKey, user, segments) => {
24
+ const evaluated = (0, evaluate_1.evaluateFlag)(flag, targetingKey, user, segments);
38
25
  if (flag.type === "boolean") {
39
- var active = evaluated === true;
26
+ const active = evaluated === true;
40
27
  return {
41
28
  defaultVariant: active ? "on" : "off",
42
29
  disabled: false,
43
30
  variants: { off: false, on: true },
44
31
  };
45
32
  }
46
- var variantKeys = flag.variants.map(function (v) { return v.key; });
47
- var resolved;
33
+ const variantKeys = flag.variants.map((v) => v.key);
34
+ let resolved;
48
35
  if (typeof evaluated === "string" && variantKeys.includes(evaluated)) {
49
36
  resolved = evaluated;
50
37
  }
51
38
  else {
52
39
  resolved = (0, exports.effectiveDefaultVariantForFlag)(flag);
53
40
  }
54
- var variants = {};
55
- try {
56
- for (var variantKeys_1 = __values(variantKeys), variantKeys_1_1 = variantKeys_1.next(); !variantKeys_1_1.done; variantKeys_1_1 = variantKeys_1.next()) {
57
- var k = variantKeys_1_1.value;
58
- variants[k] = k;
59
- }
60
- }
61
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
62
- finally {
63
- try {
64
- if (variantKeys_1_1 && !variantKeys_1_1.done && (_a = variantKeys_1.return)) _a.call(variantKeys_1);
65
- }
66
- finally { if (e_1) throw e_1.error; }
41
+ const variants = {};
42
+ for (const k of variantKeys) {
43
+ variants[k] = k;
67
44
  }
68
45
  return {
69
46
  defaultVariant: resolved,
70
47
  disabled: false,
71
- variants: variants,
48
+ variants,
72
49
  };
73
50
  };
74
51
  exports.buildFlagDefinition = buildFlagDefinition;
@@ -1,150 +1,88 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
2
  Object.defineProperty(exports, "__esModule", { value: true });
39
3
  exports.MongoFeatureFlagProvider = void 0;
40
- var server_sdk_1 = require("@openfeature/server-sdk");
41
- var evaluate_1 = require("./evaluate");
42
- var MongoFeatureFlagProvider = /** @class */ (function () {
43
- function MongoFeatureFlagProvider(options) {
44
- var _a;
45
- this.metadata = { name: "MongoFeatureFlagProvider" };
46
- this.runsOn = "server";
47
- this.events = new server_sdk_1.OpenFeatureEventEmitter();
4
+ const server_sdk_1 = require("@openfeature/server-sdk");
5
+ const evaluate_1 = require("./evaluate");
6
+ class MongoFeatureFlagProvider {
7
+ metadata = { name: "MongoFeatureFlagProvider" };
8
+ runsOn = "server";
9
+ events = new server_sdk_1.OpenFeatureEventEmitter();
10
+ flagModel;
11
+ segments;
12
+ constructor(options) {
48
13
  this.flagModel = options.flagModel;
49
- this.segments = (_a = options.segments) !== null && _a !== void 0 ? _a : {};
14
+ this.segments = options.segments ?? {};
15
+ }
16
+ async resolveBooleanEvaluation(flagKey, defaultValue, context, _logger) {
17
+ return this.resolve(flagKey, defaultValue, context, "boolean", _logger);
18
+ }
19
+ async resolveStringEvaluation(flagKey, defaultValue, context, _logger) {
20
+ return this.resolve(flagKey, defaultValue, context, "variant", _logger);
21
+ }
22
+ async resolveNumberEvaluation(_flagKey, defaultValue, _context, _logger) {
23
+ return {
24
+ errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
25
+ reason: server_sdk_1.StandardResolutionReasons.ERROR,
26
+ value: defaultValue,
27
+ };
50
28
  }
51
- MongoFeatureFlagProvider.prototype.resolveBooleanEvaluation = function (flagKey, defaultValue, context, _logger) {
52
- return __awaiter(this, void 0, void 0, function () {
53
- return __generator(this, function (_a) {
54
- return [2 /*return*/, this.resolve(flagKey, defaultValue, context, "boolean", _logger)];
55
- });
56
- });
57
- };
58
- MongoFeatureFlagProvider.prototype.resolveStringEvaluation = function (flagKey, defaultValue, context, _logger) {
59
- return __awaiter(this, void 0, void 0, function () {
60
- return __generator(this, function (_a) {
61
- return [2 /*return*/, this.resolve(flagKey, defaultValue, context, "variant", _logger)];
62
- });
63
- });
64
- };
65
- MongoFeatureFlagProvider.prototype.resolveNumberEvaluation = function (_flagKey, defaultValue, _context, _logger) {
66
- return __awaiter(this, void 0, void 0, function () {
67
- return __generator(this, function (_a) {
68
- return [2 /*return*/, {
69
- errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
70
- reason: server_sdk_1.StandardResolutionReasons.ERROR,
71
- value: defaultValue,
72
- }];
73
- });
74
- });
75
- };
76
- MongoFeatureFlagProvider.prototype.resolveObjectEvaluation = function (_flagKey, defaultValue, _context, _logger) {
77
- return __awaiter(this, void 0, void 0, function () {
78
- return __generator(this, function (_a) {
79
- return [2 /*return*/, {
80
- errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
81
- reason: server_sdk_1.StandardResolutionReasons.ERROR,
82
- value: defaultValue,
83
- }];
84
- });
85
- });
86
- };
87
- MongoFeatureFlagProvider.prototype.emitConfigurationChanged = function () {
29
+ async resolveObjectEvaluation(_flagKey, defaultValue, _context, _logger) {
30
+ return {
31
+ errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
32
+ reason: server_sdk_1.StandardResolutionReasons.ERROR,
33
+ value: defaultValue,
34
+ };
35
+ }
36
+ emitConfigurationChanged() {
88
37
  this.events.emit(server_sdk_1.ServerProviderEvents.ConfigurationChanged, { flagsChanged: [] });
89
- };
90
- MongoFeatureFlagProvider.prototype.resolve = function (flagKey, defaultValue, context, expectedType, _logger) {
91
- return __awaiter(this, void 0, void 0, function () {
92
- var flag, targetingKey, userFromContext, user, result, value;
93
- var _a;
94
- return __generator(this, function (_b) {
95
- switch (_b.label) {
96
- case 0: return [4 /*yield*/, this.flagModel.findOneOrNone({ archived: { $ne: true }, key: flagKey })];
97
- case 1:
98
- flag = _b.sent();
99
- if (!flag) {
100
- return [2 /*return*/, {
101
- errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
102
- reason: server_sdk_1.StandardResolutionReasons.ERROR,
103
- value: defaultValue,
104
- }];
105
- }
106
- if (flag.type !== expectedType) {
107
- return [2 /*return*/, {
108
- errorCode: server_sdk_1.ErrorCode.TYPE_MISMATCH,
109
- reason: server_sdk_1.StandardResolutionReasons.ERROR,
110
- value: defaultValue,
111
- }];
112
- }
113
- if (!flag.enabled) {
114
- return [2 /*return*/, {
115
- reason: server_sdk_1.StandardResolutionReasons.DISABLED,
116
- value: defaultValue,
117
- variant: flag.defaultVariant,
118
- }];
119
- }
120
- targetingKey = (_a = context.targetingKey) !== null && _a !== void 0 ? _a : "";
121
- userFromContext = context.user;
122
- user = userFromContext !== undefined ? userFromContext : context;
123
- result = (0, evaluate_1.evaluateFlag)(flag, targetingKey, user, this.segments);
124
- if (expectedType === "boolean") {
125
- value = result;
126
- return [2 /*return*/, {
127
- reason: server_sdk_1.StandardResolutionReasons.TARGETING_MATCH,
128
- value: value,
129
- variant: value ? "on" : "off",
130
- }];
131
- }
132
- if (result === null) {
133
- return [2 /*return*/, {
134
- reason: server_sdk_1.StandardResolutionReasons.DISABLED,
135
- value: defaultValue,
136
- variant: flag.defaultVariant,
137
- }];
138
- }
139
- return [2 /*return*/, {
140
- reason: server_sdk_1.StandardResolutionReasons.TARGETING_MATCH,
141
- value: result,
142
- variant: result,
143
- }];
144
- }
145
- });
146
- });
147
- };
148
- return MongoFeatureFlagProvider;
149
- }());
38
+ }
39
+ async resolve(flagKey, defaultValue, context, expectedType, _logger) {
40
+ const flag = await this.flagModel.findOneOrNone({ archived: { $ne: true }, key: flagKey });
41
+ if (!flag) {
42
+ return {
43
+ errorCode: server_sdk_1.ErrorCode.FLAG_NOT_FOUND,
44
+ reason: server_sdk_1.StandardResolutionReasons.ERROR,
45
+ value: defaultValue,
46
+ };
47
+ }
48
+ if (flag.type !== expectedType) {
49
+ return {
50
+ errorCode: server_sdk_1.ErrorCode.TYPE_MISMATCH,
51
+ reason: server_sdk_1.StandardResolutionReasons.ERROR,
52
+ value: defaultValue,
53
+ };
54
+ }
55
+ if (!flag.enabled) {
56
+ return {
57
+ reason: server_sdk_1.StandardResolutionReasons.DISABLED,
58
+ value: defaultValue,
59
+ variant: flag.defaultVariant,
60
+ };
61
+ }
62
+ const targetingKey = context.targetingKey ?? "";
63
+ const userFromContext = context.user;
64
+ const user = userFromContext !== undefined ? userFromContext : context;
65
+ const result = (0, evaluate_1.evaluateFlag)(flag, targetingKey, user, this.segments);
66
+ if (expectedType === "boolean") {
67
+ const value = result;
68
+ return {
69
+ reason: server_sdk_1.StandardResolutionReasons.TARGETING_MATCH,
70
+ value,
71
+ variant: value ? "on" : "off",
72
+ };
73
+ }
74
+ if (result === null) {
75
+ return {
76
+ reason: server_sdk_1.StandardResolutionReasons.DISABLED,
77
+ value: defaultValue,
78
+ variant: flag.defaultVariant,
79
+ };
80
+ }
81
+ return {
82
+ reason: server_sdk_1.StandardResolutionReasons.TARGETING_MATCH,
83
+ value: result,
84
+ variant: result,
85
+ };
86
+ }
87
+ }
150
88
  exports.MongoFeatureFlagProvider = MongoFeatureFlagProvider;
package/package.json CHANGED
@@ -1,27 +1,26 @@
1
1
  {
2
2
  "author": "Josh Gachnang",
3
3
  "bugs": {
4
- "url": "https://github.com/FlourishHealth/terreno/issues"
4
+ "url": "https://github.com/TerrenoLabs/terreno/issues"
5
5
  },
6
6
  "dependencies": {
7
7
  "@openfeature/server-sdk": "^1.22.0",
8
- "@terreno/api": "57.3.0",
8
+ "@terreno/api": "57.4.0",
9
9
  "express": "^5.2.1",
10
10
  "lodash": "^4.18.1"
11
11
  },
12
12
  "description": "Feature flags and A/B testing plugin for @terreno/api — part of Terreno, the batteries-included TypeScript framework for universal apps.",
13
13
  "devDependencies": {
14
14
  "@biomejs/biome": "^2.3.6",
15
- "@terreno/test": "57.3.0",
15
+ "@terreno/test": "57.4.0",
16
16
  "@types/bun": "^1.4.0",
17
17
  "@types/express": "^5.0.6",
18
18
  "@types/lodash": "^4.17.17",
19
19
  "@types/supertest": "^7.2.0",
20
- "passport-local-mongoose": "^9.0.1",
21
20
  "supertest": "^7.2.2",
22
21
  "typescript": "~6.0.3"
23
22
  },
24
- "homepage": "https://github.com/FlourishHealth/terreno#readme",
23
+ "homepage": "https://github.com/TerrenoLabs/terreno#readme",
25
24
  "keywords": [
26
25
  "feature-flags",
27
26
  "ab-testing",
@@ -30,17 +29,17 @@
30
29
  "terreno"
31
30
  ],
32
31
  "license": "MIT",
32
+ "main": "dist/index.js",
33
+ "name": "@terreno/feature-flags",
33
34
  "peerDependencies": {
34
35
  "mongoose": "^8.0.0 || ^9.0.0"
35
36
  },
36
- "main": "dist/index.js",
37
- "name": "@terreno/feature-flags",
38
37
  "publishConfig": {
39
38
  "access": "public"
40
39
  },
41
40
  "repository": {
42
41
  "type": "git",
43
- "url": "git+ssh://git@github.com/FlourishHealth/terreno.git"
42
+ "url": "git+ssh://git@github.com/TerrenoLabs/terreno.git"
44
43
  },
45
44
  "scripts": {
46
45
  "compile": "bun tsc",
@@ -54,5 +53,5 @@
54
53
  "test:coverage": "bun run ../scripts/check-coverage.ts"
55
54
  },
56
55
  "types": "dist/index.d.ts",
57
- "version": "57.3.0"
56
+ "version": "57.6.0"
58
57
  }
package/tsconfig.json CHANGED
@@ -1,33 +1,18 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "allowJs": true,
4
- "allowSyntheticDefaultImports": true,
5
- "baseUrl": "./",
6
3
  "declaration": true,
7
- "downlevelIteration": true,
8
- "esModuleInterop": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "ignoreDeprecations": "6.0",
11
- "jsx": "react",
12
- "lib": ["es2017", "dom", "es2017.object"],
13
- "module": "commonjs",
14
- "moduleResolution": "node",
15
- "noEmit": false,
4
+ "isolatedModules": true,
5
+ "lib": ["es2023"],
6
+ "module": "nodenext",
16
7
  "noImplicitAny": false,
17
8
  "noImplicitReturns": true,
18
- "noImplicitThis": true,
19
- "noUnusedLocals": false,
20
- "noUnusedParameters": false,
21
9
  "outDir": "./dist",
22
10
  "rootDir": "./src",
23
- "preserveConstEnums": true,
24
- "removeComments": false,
25
11
  "skipLibCheck": true,
26
12
  "strict": true,
27
- "strictFunctionTypes": true,
28
- "strictNullChecks": true,
29
- "target": "es5"
13
+ "target": "es2023",
14
+ "types": ["node"]
30
15
  },
31
16
  "exclude": ["node_modules", "src/tests", "**/*.test.ts"],
32
- "include": ["src/**/*.ts*", "src/*.ts"]
17
+ "include": ["src"]
33
18
  }