@featurevisor/core 1.22.0 → 1.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.
Files changed (34) hide show
  1. package/.eslintcache +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/coverage/clover.xml +146 -142
  4. package/coverage/coverage-final.json +4 -4
  5. package/coverage/lcov-report/index.html +21 -21
  6. package/coverage/lcov-report/lib/builder/allocator.js.html +37 -28
  7. package/coverage/lcov-report/lib/builder/index.html +10 -10
  8. package/coverage/lcov-report/lib/builder/revision.js.html +1 -1
  9. package/coverage/lcov-report/lib/builder/traffic.js.html +46 -46
  10. package/coverage/lcov-report/lib/tester/checkIfObjectsAreEqual.js.html +1 -1
  11. package/coverage/lcov-report/lib/tester/index.html +1 -1
  12. package/coverage/lcov-report/lib/tester/matrix.js.html +1 -1
  13. package/coverage/lcov-report/src/builder/allocator.ts.html +40 -28
  14. package/coverage/lcov-report/src/builder/index.html +10 -10
  15. package/coverage/lcov-report/src/builder/revision.ts.html +1 -1
  16. package/coverage/lcov-report/src/builder/traffic.ts.html +44 -44
  17. package/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +1 -1
  18. package/coverage/lcov-report/src/tester/index.html +1 -1
  19. package/coverage/lcov-report/src/tester/matrix.ts.html +1 -1
  20. package/coverage/lcov.info +242 -235
  21. package/lib/builder/allocator.js +3 -0
  22. package/lib/builder/allocator.js.map +1 -1
  23. package/lib/builder/traffic.spec.js +236 -0
  24. package/lib/builder/traffic.spec.js.map +1 -1
  25. package/lib/config/projectConfig.d.ts +1 -0
  26. package/lib/config/projectConfig.js +1 -0
  27. package/lib/config/projectConfig.js.map +1 -1
  28. package/lib/linter/featureSchema.js +15 -0
  29. package/lib/linter/featureSchema.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/builder/allocator.ts +4 -0
  32. package/src/builder/traffic.spec.ts +259 -0
  33. package/src/config/projectConfig.ts +3 -0
  34. package/src/linter/featureSchema.ts +25 -0
@@ -47,6 +47,265 @@ describe("core: Traffic", function () {
47
47
  ]);
48
48
  });
49
49
 
50
+ it("should allocate traffic for 0-100 weight on two variations", function () {
51
+ const result = getTraffic(
52
+ // parsed variations from YAML
53
+ [
54
+ {
55
+ value: "control",
56
+ weight: 0,
57
+ },
58
+ {
59
+ value: "treatment",
60
+ weight: 100,
61
+ },
62
+ ],
63
+
64
+ // parsed rollouts from YAML
65
+ [
66
+ {
67
+ key: "1",
68
+ segments: "*",
69
+ percentage: 80,
70
+ },
71
+ ],
72
+
73
+ // existing feature from previous release
74
+ undefined,
75
+ );
76
+
77
+ expect(result).toEqual([
78
+ {
79
+ key: "1",
80
+ segments: "*",
81
+ percentage: 80000,
82
+ allocation: [
83
+ // should be filtered out automatically
84
+ // {
85
+ // variation: "control",
86
+ // range: [0, 0],
87
+ // },
88
+
89
+ {
90
+ variation: "treatment",
91
+ range: [0, 80000],
92
+ },
93
+ ],
94
+ },
95
+ ]);
96
+ });
97
+
98
+ it("should allocate traffic for 0-100 weight on two variations, with rule percentage going from 80% to 100%", function () {
99
+ const result = getTraffic(
100
+ // parsed variations from YAML
101
+ [
102
+ {
103
+ value: "control",
104
+ weight: 0,
105
+ },
106
+ {
107
+ value: "treatment",
108
+ weight: 100,
109
+ },
110
+ ],
111
+
112
+ // parsed rollouts from YAML
113
+ [
114
+ {
115
+ key: "1",
116
+ segments: "*",
117
+ percentage: 100,
118
+ },
119
+ ],
120
+
121
+ // existing feature from previous release
122
+ {
123
+ variations: [
124
+ {
125
+ value: "control",
126
+ weight: 0,
127
+ },
128
+ {
129
+ value: "treatment",
130
+ weight: 80,
131
+ },
132
+ ],
133
+ traffic: [
134
+ {
135
+ key: "1",
136
+ percentage: 80000,
137
+ allocation: [
138
+ {
139
+ variation: "treatment",
140
+ range: [0, 80000],
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ },
146
+ );
147
+
148
+ expect(result).toEqual([
149
+ {
150
+ key: "1",
151
+ segments: "*",
152
+ percentage: 100000,
153
+ allocation: [
154
+ // should be filtered out automatically
155
+ // {
156
+ // variation: "control",
157
+ // range: [0, 0],
158
+ // },
159
+
160
+ {
161
+ variation: "treatment",
162
+ range: [0, 100000],
163
+ },
164
+ ],
165
+ },
166
+ ]);
167
+ });
168
+
169
+ it("should allocate traffic for 0-100 weight on two variations, with rule percentage going from 100% to 80%", function () {
170
+ const result = getTraffic(
171
+ // parsed variations from YAML
172
+ [
173
+ {
174
+ value: "control",
175
+ weight: 0,
176
+ },
177
+ {
178
+ value: "treatment",
179
+ weight: 100,
180
+ },
181
+ ],
182
+
183
+ // parsed rollouts from YAML
184
+ [
185
+ {
186
+ key: "1",
187
+ segments: "*",
188
+ percentage: 80,
189
+ },
190
+ ],
191
+
192
+ // existing feature from previous release
193
+ {
194
+ variations: [
195
+ {
196
+ value: "control",
197
+ weight: 0,
198
+ },
199
+ {
200
+ value: "treatment",
201
+ weight: 80,
202
+ },
203
+ ],
204
+ traffic: [
205
+ {
206
+ key: "1",
207
+ percentage: 100000,
208
+ allocation: [
209
+ {
210
+ variation: "treatment",
211
+ range: [0, 100000],
212
+ },
213
+ ],
214
+ },
215
+ ],
216
+ },
217
+ );
218
+
219
+ expect(result).toEqual([
220
+ {
221
+ key: "1",
222
+ segments: "*",
223
+ percentage: 80000,
224
+ allocation: [
225
+ // should be filtered out automatically
226
+ // {
227
+ // variation: "control",
228
+ // range: [0, 0],
229
+ // },
230
+
231
+ {
232
+ variation: "treatment",
233
+ range: [0, 80000],
234
+ },
235
+ ],
236
+ },
237
+ ]);
238
+ });
239
+
240
+ it("should allocate traffic for existing variations state of 0-100 weights", function () {
241
+ const result = getTraffic(
242
+ // parsed variations from YAML
243
+ [
244
+ {
245
+ value: "control",
246
+ weight: 50,
247
+ },
248
+ {
249
+ value: "treatment",
250
+ weight: 50,
251
+ },
252
+ ],
253
+
254
+ // parsed rollouts from YAML
255
+ [
256
+ {
257
+ key: "1",
258
+ segments: "*",
259
+ percentage: 80,
260
+ },
261
+ ],
262
+
263
+ // existing feature from previous release
264
+ {
265
+ variations: [
266
+ {
267
+ value: "control",
268
+ weight: 0,
269
+ },
270
+ {
271
+ value: "treatment",
272
+ weight: 80,
273
+ },
274
+ ],
275
+ traffic: [
276
+ {
277
+ key: "1",
278
+ percentage: 80000,
279
+ allocation: [
280
+ {
281
+ variation: "treatment",
282
+ range: [0, 80000],
283
+ },
284
+ ],
285
+ },
286
+ ],
287
+ },
288
+ );
289
+
290
+ expect(result).toEqual([
291
+ {
292
+ key: "1",
293
+ segments: "*",
294
+ percentage: 80000,
295
+ allocation: [
296
+ {
297
+ variation: "control",
298
+ range: [0, 40000],
299
+ },
300
+ {
301
+ variation: "treatment",
302
+ range: [40000, 80000],
303
+ },
304
+ ],
305
+ },
306
+ ]);
307
+ });
308
+
50
309
  it("should allocate traffic for 50-50 weight on two variations", function () {
51
310
  const result = getTraffic(
52
311
  // parsed variations from YAML
@@ -44,6 +44,7 @@ export interface ProjectConfig {
44
44
  prettyDatafile: boolean;
45
45
  stringify: boolean;
46
46
  siteExportDirectoryPath: string;
47
+ enforceCatchAllRule?: boolean;
47
48
  adapter: any; // @TODO: type this properly later
48
49
  }
49
50
 
@@ -70,6 +71,8 @@ export function getProjectConfig(rootDirectoryPath: string): ProjectConfig {
70
71
  stateDirectoryPath: path.join(rootDirectoryPath, STATE_DIRECTORY_NAME),
71
72
  outputDirectoryPath: path.join(rootDirectoryPath, OUTPUT_DIRECTORY_NAME),
72
73
  siteExportDirectoryPath: path.join(rootDirectoryPath, SITE_EXPORT_DIRECTORY_NAME),
74
+
75
+ enforceCatchAllRule: false,
73
76
  };
74
77
 
75
78
  const configModulePath = path.join(rootDirectoryPath, CONFIG_MODULE_NAME);
@@ -87,6 +87,16 @@ export function getFeatureZodSchema(
87
87
  })
88
88
  .strict(),
89
89
  )
90
+
91
+ // must have at least one rule
92
+ .refine(
93
+ (value) => value.length > 0,
94
+ () => ({
95
+ message: "Must have at least one rule",
96
+ }),
97
+ )
98
+
99
+ // duplicate rules
90
100
  .refine(
91
101
  (value) => {
92
102
  const keys = value.map((v) => v.key);
@@ -95,6 +105,21 @@ export function getFeatureZodSchema(
95
105
  (value) => ({
96
106
  message: "Duplicate rule keys found: " + value.map((v) => v.key).join(", "),
97
107
  }),
108
+ )
109
+
110
+ // enforce catch-all rule
111
+ .refine(
112
+ (value) => {
113
+ if (!projectConfig.enforceCatchAllRule) {
114
+ return true;
115
+ }
116
+
117
+ const hasCatchAllAsLastRule = value[value.length - 1].segments === "*";
118
+ return hasCatchAllAsLastRule;
119
+ },
120
+ () => ({
121
+ message: `Missing catch-all rule with \`segments: "*"\` at the end`,
122
+ }),
98
123
  ),
99
124
  force: z
100
125
  .array(