@terreno/feature-flags 0.21.0 → 0.23.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.
@@ -1,428 +0,0 @@
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
- Object.defineProperty(exports, "__esModule", { value: true });
39
- var bun_test_1 = require("bun:test");
40
- var featureFlagModel_1 = require("../featureFlagModel");
41
- (0, bun_test_1.describe)("FeatureFlag model", function () {
42
- (0, bun_test_1.afterEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
43
- return __generator(this, function (_a) {
44
- switch (_a.label) {
45
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.deleteMany({})];
46
- case 1:
47
- _a.sent();
48
- return [2 /*return*/];
49
- }
50
- });
51
- }); });
52
- (0, bun_test_1.it)("creates a boolean flag with defaults", function () { return __awaiter(void 0, void 0, void 0, function () {
53
- var flag;
54
- return __generator(this, function (_a) {
55
- switch (_a.label) {
56
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
57
- key: "new-checkout-flow",
58
- name: "New Checkout Flow",
59
- })];
60
- case 1:
61
- flag = _a.sent();
62
- (0, bun_test_1.expect)(flag.key).toBe("new-checkout-flow");
63
- (0, bun_test_1.expect)(flag.name).toBe("New Checkout Flow");
64
- (0, bun_test_1.expect)(flag.enabled).toBe(false);
65
- (0, bun_test_1.expect)(flag.archived).toBe(false);
66
- (0, bun_test_1.expect)(flag.type).toBe("boolean");
67
- (0, bun_test_1.expect)(flag.rolloutPercentage).toBe(100);
68
- (0, bun_test_1.expect)(flag.description).toBe("");
69
- (0, bun_test_1.expect)(flag.rules).toEqual([]);
70
- (0, bun_test_1.expect)(flag.variants).toEqual([]);
71
- return [2 /*return*/];
72
- }
73
- });
74
- }); });
75
- (0, bun_test_1.it)("rejects unknown fields (strict: 'throw')", function () { return __awaiter(void 0, void 0, void 0, function () {
76
- return __generator(this, function (_a) {
77
- switch (_a.label) {
78
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
79
- key: "strict-test",
80
- name: "Strict Test",
81
- unknownField: true,
82
- })).rejects.toBeDefined()];
83
- case 1:
84
- _a.sent();
85
- return [2 /*return*/];
86
- }
87
- });
88
- }); });
89
- (0, bun_test_1.it)("requires a key and name", function () { return __awaiter(void 0, void 0, void 0, function () {
90
- return __generator(this, function (_a) {
91
- switch (_a.label) {
92
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
93
- name: "Missing key",
94
- })).rejects.toBeDefined()];
95
- case 1:
96
- _a.sent();
97
- return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
98
- key: "missing-name",
99
- })).rejects.toBeDefined()];
100
- case 2:
101
- _a.sent();
102
- return [2 /*return*/];
103
- }
104
- });
105
- }); });
106
- (0, bun_test_1.it)("enforces unique keys", function () { return __awaiter(void 0, void 0, void 0, function () {
107
- return __generator(this, function (_a) {
108
- switch (_a.label) {
109
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.syncIndexes()];
110
- case 1:
111
- _a.sent();
112
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({ key: "dup-key", name: "First" })];
113
- case 2:
114
- _a.sent();
115
- return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({ key: "dup-key", name: "Second" })).rejects.toBeDefined()];
116
- case 3:
117
- _a.sent();
118
- return [2 /*return*/];
119
- }
120
- });
121
- }); });
122
- (0, bun_test_1.it)("clamps rolloutPercentage to the 0-100 range", function () { return __awaiter(void 0, void 0, void 0, function () {
123
- return __generator(this, function (_a) {
124
- switch (_a.label) {
125
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
126
- key: "over-rollout",
127
- name: "Over",
128
- rolloutPercentage: 150,
129
- })).rejects.toBeDefined()];
130
- case 1:
131
- _a.sent();
132
- return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
133
- key: "under-rollout",
134
- name: "Under",
135
- rolloutPercentage: -5,
136
- })).rejects.toBeDefined()];
137
- case 2:
138
- _a.sent();
139
- return [2 /*return*/];
140
- }
141
- });
142
- }); });
143
- (0, bun_test_1.it)("accepts variant flags with variants that sum to 100", function () { return __awaiter(void 0, void 0, void 0, function () {
144
- var flag;
145
- return __generator(this, function (_a) {
146
- switch (_a.label) {
147
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
148
- key: "variant-flag",
149
- name: "Variant Flag",
150
- type: "variant",
151
- variants: [
152
- { key: "control", weight: 40 },
153
- { key: "variant-a", weight: 60 },
154
- ],
155
- })];
156
- case 1:
157
- flag = _a.sent();
158
- (0, bun_test_1.expect)(flag.type).toBe("variant");
159
- (0, bun_test_1.expect)(flag.variants.length).toBe(2);
160
- return [2 /*return*/];
161
- }
162
- });
163
- }); });
164
- (0, bun_test_1.it)("rejects variant flags with no variants", function () { return __awaiter(void 0, void 0, void 0, function () {
165
- return __generator(this, function (_a) {
166
- switch (_a.label) {
167
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
168
- key: "variant-no-variants",
169
- name: "No Variants",
170
- type: "variant",
171
- variants: [],
172
- })).rejects.toMatchObject({ title: "Variant flags must have at least one variant" })];
173
- case 1:
174
- _a.sent();
175
- return [2 /*return*/];
176
- }
177
- });
178
- }); });
179
- (0, bun_test_1.it)("rejects variant flags whose variant weights do not sum to 100", function () { return __awaiter(void 0, void 0, void 0, function () {
180
- return __generator(this, function (_a) {
181
- switch (_a.label) {
182
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
183
- key: "variant-bad-weights",
184
- name: "Bad Weights",
185
- type: "variant",
186
- variants: [
187
- { key: "control", weight: 10 },
188
- { key: "variant-a", weight: 10 },
189
- ],
190
- })).rejects.toMatchObject({ title: "Variant weights must sum to 100" })];
191
- case 1:
192
- _a.sent();
193
- return [2 /*return*/];
194
- }
195
- });
196
- }); });
197
- (0, bun_test_1.it)("allows boolean flags without variants", function () { return __awaiter(void 0, void 0, void 0, function () {
198
- var flag;
199
- return __generator(this, function (_a) {
200
- switch (_a.label) {
201
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
202
- key: "boolean-no-variants",
203
- name: "Boolean",
204
- type: "boolean",
205
- })];
206
- case 1:
207
- flag = _a.sent();
208
- (0, bun_test_1.expect)(flag.variants).toEqual([]);
209
- return [2 /*return*/];
210
- }
211
- });
212
- }); });
213
- (0, bun_test_1.it)("trims whitespace on keys", function () { return __awaiter(void 0, void 0, void 0, function () {
214
- var flag;
215
- return __generator(this, function (_a) {
216
- switch (_a.label) {
217
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
218
- key: " padded-key ",
219
- name: "Padded",
220
- })];
221
- case 1:
222
- flag = _a.sent();
223
- (0, bun_test_1.expect)(flag.key).toBe("padded-key");
224
- return [2 /*return*/];
225
- }
226
- });
227
- }); });
228
- (0, bun_test_1.it)("stores complex rules including segments and field rules", function () { return __awaiter(void 0, void 0, void 0, function () {
229
- var flag;
230
- return __generator(this, function (_a) {
231
- switch (_a.label) {
232
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
233
- key: "complex-rules",
234
- name: "Complex",
235
- rules: [
236
- { enabled: true, segment: "beta" },
237
- { enabled: true, field: "plan", operator: "in", value: ["pro", "enterprise"] },
238
- { enabled: false, field: "email", operator: "contains", value: "@example.com" },
239
- ],
240
- })];
241
- case 1:
242
- flag = _a.sent();
243
- (0, bun_test_1.expect)(flag.rules.length).toBe(3);
244
- (0, bun_test_1.expect)(flag.rules[0].segment).toBe("beta");
245
- (0, bun_test_1.expect)(flag.rules[1].operator).toBe("in");
246
- (0, bun_test_1.expect)(flag.rules[1].value).toEqual(["pro", "enterprise"]);
247
- (0, bun_test_1.expect)(flag.rules[2].enabled).toBe(false);
248
- return [2 /*return*/];
249
- }
250
- });
251
- }); });
252
- (0, bun_test_1.it)("rejects invalid rule operators via the enum constraint", function () { return __awaiter(void 0, void 0, void 0, function () {
253
- return __generator(this, function (_a) {
254
- switch (_a.label) {
255
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
256
- key: "bad-op",
257
- name: "Bad Operator",
258
- rules: [{ enabled: true, field: "plan", operator: "bogus", value: "pro" }],
259
- })).rejects.toBeDefined()];
260
- case 1:
261
- _a.sent();
262
- return [2 /*return*/];
263
- }
264
- });
265
- }); });
266
- (0, bun_test_1.it)("rejects invalid type values via the enum constraint", function () { return __awaiter(void 0, void 0, void 0, function () {
267
- return __generator(this, function (_a) {
268
- switch (_a.label) {
269
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
270
- key: "bad-type",
271
- name: "Bad Type",
272
- type: "nonsense",
273
- })).rejects.toBeDefined()];
274
- case 1:
275
- _a.sent();
276
- return [2 /*return*/];
277
- }
278
- });
279
- }); });
280
- (0, bun_test_1.it)("supports the isDeleted soft-delete plugin", function () { return __awaiter(void 0, void 0, void 0, function () {
281
- var flag, found, foundWithDeleted;
282
- return __generator(this, function (_a) {
283
- switch (_a.label) {
284
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
285
- key: "soft-delete",
286
- name: "Soft",
287
- })];
288
- case 1:
289
- flag = _a.sent();
290
- (0, bun_test_1.expect)(flag.deleted).toBe(false);
291
- flag.deleted = true;
292
- return [4 /*yield*/, flag.save()];
293
- case 2:
294
- _a.sent();
295
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.findOne({ key: "soft-delete" })];
296
- case 3:
297
- found = _a.sent();
298
- (0, bun_test_1.expect)(found).toBeNull();
299
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.findOne({ deleted: true, key: "soft-delete" })];
300
- case 4:
301
- foundWithDeleted = _a.sent();
302
- (0, bun_test_1.expect)(foundWithDeleted === null || foundWithDeleted === void 0 ? void 0 : foundWithDeleted._id.toString()).toBe(flag._id.toString());
303
- return [2 /*return*/];
304
- }
305
- });
306
- }); });
307
- (0, bun_test_1.it)("sets created/updated timestamps on save", function () { return __awaiter(void 0, void 0, void 0, function () {
308
- var flag;
309
- return __generator(this, function (_a) {
310
- switch (_a.label) {
311
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
312
- key: "timestamps",
313
- name: "Timestamps",
314
- })];
315
- case 1:
316
- flag = _a.sent();
317
- (0, bun_test_1.expect)(flag.created).toBeInstanceOf(Date);
318
- (0, bun_test_1.expect)(flag.updated).toBeInstanceOf(Date);
319
- return [2 /*return*/];
320
- }
321
- });
322
- }); });
323
- (0, bun_test_1.it)("supports findExactlyOne and findOneOrNone plugins", function () { return __awaiter(void 0, void 0, void 0, function () {
324
- var exact, maybe;
325
- return __generator(this, function (_a) {
326
- switch (_a.label) {
327
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({ key: "existing-flag", name: "Exists" })];
328
- case 1:
329
- _a.sent();
330
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.findExactlyOne({ key: "existing-flag" })];
331
- case 2:
332
- exact = _a.sent();
333
- (0, bun_test_1.expect)(exact.name).toBe("Exists");
334
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.findOneOrNone({ key: "missing-flag" })];
335
- case 3:
336
- maybe = _a.sent();
337
- (0, bun_test_1.expect)(maybe).toBeNull();
338
- return [2 /*return*/];
339
- }
340
- });
341
- }); });
342
- (0, bun_test_1.it)("auto-fills defaultVariant to off for boolean flags when omitted", function () { return __awaiter(void 0, void 0, void 0, function () {
343
- var flag;
344
- return __generator(this, function (_a) {
345
- switch (_a.label) {
346
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
347
- key: "default-variant-bool",
348
- name: "Default variant bool",
349
- })];
350
- case 1:
351
- flag = _a.sent();
352
- (0, bun_test_1.expect)(flag.defaultVariant).toBe("off");
353
- return [2 /*return*/];
354
- }
355
- });
356
- }); });
357
- (0, bun_test_1.it)("auto-fills defaultVariant to the first variant key when omitted on variant flags", function () { return __awaiter(void 0, void 0, void 0, function () {
358
- var flag;
359
- return __generator(this, function (_a) {
360
- switch (_a.label) {
361
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
362
- key: "default-variant-var",
363
- name: "Default variant var",
364
- type: "variant",
365
- variants: [
366
- { key: "control", weight: 40 },
367
- { key: "treatment", weight: 60 },
368
- ],
369
- })];
370
- case 1:
371
- flag = _a.sent();
372
- (0, bun_test_1.expect)(flag.defaultVariant).toBe("control");
373
- return [2 /*return*/];
374
- }
375
- });
376
- }); });
377
- (0, bun_test_1.it)("accepts explicit defaultVariant on for boolean flags", function () { return __awaiter(void 0, void 0, void 0, function () {
378
- var flag;
379
- return __generator(this, function (_a) {
380
- switch (_a.label) {
381
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.create({
382
- defaultVariant: "on",
383
- enabled: true,
384
- key: "explicit-on-bool",
385
- name: "Explicit on",
386
- rolloutPercentage: 100,
387
- type: "boolean",
388
- })];
389
- case 1:
390
- flag = _a.sent();
391
- (0, bun_test_1.expect)(flag.defaultVariant).toBe("on");
392
- return [2 /*return*/];
393
- }
394
- });
395
- }); });
396
- (0, bun_test_1.it)("rejects boolean defaultVariant values other than on or off", function () { return __awaiter(void 0, void 0, void 0, function () {
397
- return __generator(this, function (_a) {
398
- switch (_a.label) {
399
- case 0: return [4 /*yield*/, (0, bun_test_1.expect)(featureFlagModel_1.FeatureFlag.create({
400
- defaultVariant: "maybe",
401
- enabled: true,
402
- key: "bad-default-variant",
403
- name: "Bad default",
404
- type: "boolean",
405
- })).rejects.toMatchObject({ title: "Boolean flags must use defaultVariant 'on' or 'off'" })];
406
- case 1:
407
- _a.sent();
408
- return [2 /*return*/];
409
- }
410
- });
411
- }); });
412
- });
413
- (0, bun_test_1.describe)("featureFlagAdminConfig", function () {
414
- (0, bun_test_1.it)("has the expected shape for AdminApp registration", function () {
415
- (0, bun_test_1.expect)(featureFlagModel_1.featureFlagAdminConfig.model).toBe(featureFlagModel_1.FeatureFlag);
416
- (0, bun_test_1.expect)(featureFlagModel_1.featureFlagAdminConfig.routePath).toBe("/feature-flags");
417
- (0, bun_test_1.expect)(featureFlagModel_1.featureFlagAdminConfig.displayName).toBe("Feature Flags");
418
- (0, bun_test_1.expect)(featureFlagModel_1.featureFlagAdminConfig.listFields).toEqual([
419
- "key",
420
- "name",
421
- "type",
422
- "enabled",
423
- "archived",
424
- "defaultVariant",
425
- "created",
426
- ]);
427
- });
428
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,203 +0,0 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
- return new (P || (P = Promise))(function (resolve, reject) {
20
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
- step((generator = generator.apply(thisArg, _arguments || [])).next());
24
- });
25
- };
26
- var __generator = (this && this.__generator) || function (thisArg, body) {
27
- 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);
28
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
29
- function verb(n) { return function (v) { return step([n, v]); }; }
30
- function step(op) {
31
- if (f) throw new TypeError("Generator is already executing.");
32
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
33
- 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;
34
- if (y = 0, t) op = [op[0] & 2, t.value];
35
- switch (op[0]) {
36
- case 0: case 1: t = op; break;
37
- case 4: _.label++; return { value: op[1], done: false };
38
- case 5: _.label++; y = op[1]; op = [0]; continue;
39
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
40
- default:
41
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
42
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
43
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
44
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
45
- if (t[2]) _.ops.pop();
46
- _.trys.pop(); continue;
47
- }
48
- op = body.call(thisArg, _);
49
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
50
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
51
- }
52
- };
53
- var __read = (this && this.__read) || function (o, n) {
54
- var m = typeof Symbol === "function" && o[Symbol.iterator];
55
- if (!m) return o;
56
- var i = m.call(o), r, ar = [], e;
57
- try {
58
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
59
- }
60
- catch (error) { e = { error: error }; }
61
- finally {
62
- try {
63
- if (r && !r.done && (m = i["return"])) m.call(i);
64
- }
65
- finally { if (e) throw e.error; }
66
- }
67
- return ar;
68
- };
69
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
70
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
71
- if (ar || !(i in from)) {
72
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
73
- ar[i] = from[i];
74
- }
75
- }
76
- return to.concat(ar || Array.prototype.slice.call(from));
77
- };
78
- var __importDefault = (this && this.__importDefault) || function (mod) {
79
- return (mod && mod.__esModule) ? mod : { "default": mod };
80
- };
81
- Object.defineProperty(exports, "__esModule", { value: true });
82
- var bun_test_1 = require("bun:test");
83
- var node_events_1 = __importDefault(require("node:events"));
84
- var server_sdk_1 = require("@openfeature/server-sdk");
85
- var api_1 = require("@terreno/api");
86
- var tests_1 = require("@terreno/api/src/tests");
87
- var featureFlagModel_1 = require("../featureFlagModel");
88
- var featureFlagsApp_1 = require("../featureFlagsApp");
89
- var FakeChangeStream = /** @class */ (function (_super) {
90
- __extends(FakeChangeStream, _super);
91
- function FakeChangeStream() {
92
- var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
93
- _this.close = (0, bun_test_1.mock)(function () { return Promise.resolve(); });
94
- return _this;
95
- }
96
- return FakeChangeStream;
97
- }(node_events_1.default));
98
- var buildAppWithLive = function (options) {
99
- var app = (0, tests_1.getBaseServer)();
100
- (0, api_1.setupAuth)(app, tests_1.UserModel);
101
- (0, api_1.addAuthRoutes)(app, tests_1.UserModel);
102
- var plugin = new featureFlagsApp_1.FeatureFlagsApp({
103
- liveUpdates: {
104
- eventName: options.eventName,
105
- socketIoServer: function () { return options.io; },
106
- },
107
- });
108
- plugin.register(app);
109
- app.use(api_1.apiUnauthorizedMiddleware);
110
- app.use(api_1.apiErrorMiddleware);
111
- return app;
112
- };
113
- (0, bun_test_1.describe)("FeatureFlagsApp live updates", function () {
114
- var watchSpy;
115
- (0, bun_test_1.beforeEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
116
- return __generator(this, function (_a) {
117
- switch (_a.label) {
118
- case 0: return [4 /*yield*/, (0, tests_1.setupDb)()];
119
- case 1:
120
- _a.sent();
121
- return [4 /*yield*/, featureFlagModel_1.FeatureFlag.deleteMany({})];
122
- case 2:
123
- _a.sent();
124
- return [2 /*return*/];
125
- }
126
- });
127
- }); });
128
- (0, bun_test_1.afterEach)(function () { return __awaiter(void 0, void 0, void 0, function () {
129
- return __generator(this, function (_a) {
130
- switch (_a.label) {
131
- case 0: return [4 /*yield*/, featureFlagModel_1.FeatureFlag.deleteMany({})];
132
- case 1:
133
- _a.sent();
134
- watchSpy === null || watchSpy === void 0 ? void 0 : watchSpy.mockRestore();
135
- return [4 /*yield*/, server_sdk_1.OpenFeature.clearProviders()];
136
- case 2:
137
- _a.sent();
138
- return [2 /*return*/];
139
- }
140
- });
141
- }); });
142
- (0, bun_test_1.it)("emits the default socket event and payload on change stream events", function () { return __awaiter(void 0, void 0, void 0, function () {
143
- var stream, io;
144
- return __generator(this, function (_a) {
145
- stream = new FakeChangeStream();
146
- watchSpy = (0, bun_test_1.spyOn)(featureFlagModel_1.FeatureFlag, "watch").mockReturnValue(stream);
147
- io = { emit: (0, bun_test_1.mock)(function () { }) };
148
- buildAppWithLive({ io: io });
149
- stream.emit("change", { fullDocument: { key: "todo-summary-card" } });
150
- (0, bun_test_1.expect)(io.emit).toHaveBeenCalledWith("featureFlagsChanged", { key: "todo-summary-card" });
151
- return [2 /*return*/];
152
- });
153
- }); });
154
- (0, bun_test_1.it)("respects a custom liveUpdates.eventName", function () { return __awaiter(void 0, void 0, void 0, function () {
155
- var stream, io;
156
- return __generator(this, function (_a) {
157
- stream = new FakeChangeStream();
158
- watchSpy = (0, bun_test_1.spyOn)(featureFlagModel_1.FeatureFlag, "watch").mockReturnValue(stream);
159
- io = { emit: (0, bun_test_1.mock)(function () { }) };
160
- buildAppWithLive({ eventName: "flagsUpdated", io: io });
161
- stream.emit("change", { fullDocument: { key: "k" } });
162
- (0, bun_test_1.expect)(io.emit).toHaveBeenCalledWith("flagsUpdated", { key: "k" });
163
- return [2 /*return*/];
164
- });
165
- }); });
166
- (0, bun_test_1.it)("logs a warn on stream error, retries once, then disables after a second error", function () { return __awaiter(void 0, void 0, void 0, function () {
167
- var warnSpy, stream1, stream2, call, io, disabledMsg;
168
- return __generator(this, function (_a) {
169
- switch (_a.label) {
170
- case 0:
171
- warnSpy = (0, bun_test_1.spyOn)(api_1.logger, "warn").mockImplementation(function () { });
172
- stream1 = new FakeChangeStream();
173
- stream2 = new FakeChangeStream();
174
- call = 0;
175
- watchSpy = (0, bun_test_1.spyOn)(featureFlagModel_1.FeatureFlag, "watch").mockImplementation(function () {
176
- call += 1;
177
- if (call === 1) {
178
- return stream1;
179
- }
180
- return stream2;
181
- });
182
- io = { emit: (0, bun_test_1.mock)(function () { }) };
183
- buildAppWithLive({ io: io });
184
- stream1.emit("error", new Error("first"));
185
- return [4 /*yield*/, new Promise(function (resolve) {
186
- setTimeout(resolve, 0);
187
- })];
188
- case 1:
189
- _a.sent();
190
- stream2.emit("error", new Error("second"));
191
- return [4 /*yield*/, new Promise(function (resolve) {
192
- setTimeout(resolve, 0);
193
- })];
194
- case 2:
195
- _a.sent();
196
- disabledMsg = warnSpy.mock.calls.find(function (c) { return typeof c[0] === "string" && c[0].includes("change stream disabled"); });
197
- (0, bun_test_1.expect)(disabledMsg).toBeDefined();
198
- warnSpy.mockRestore();
199
- return [2 /*return*/];
200
- }
201
- });
202
- }); });
203
- });
@@ -1 +0,0 @@
1
- export {};