@featurevisor/core 1.35.2 → 2.0.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/CHANGELOG.md +8 -0
- package/README.md +0 -6
- package/coverage/clover.xml +321 -237
- package/coverage/coverage-final.json +8 -8
- package/coverage/lcov-report/index.html +77 -47
- package/coverage/lcov-report/lib/builder/allocator.js.html +14 -14
- package/coverage/lcov-report/lib/builder/index.html +16 -16
- package/coverage/lcov-report/lib/builder/revision.js.html +3 -3
- package/coverage/lcov-report/lib/builder/traffic.js.html +90 -63
- package/coverage/lcov-report/lib/list/index.html +116 -0
- package/coverage/lcov-report/lib/{tester → list}/matrix.js.html +90 -66
- package/coverage/lcov-report/lib/tester/helpers.js.html +295 -0
- package/coverage/lcov-report/lib/tester/index.html +20 -35
- package/coverage/lcov-report/src/builder/allocator.ts.html +2 -2
- package/coverage/lcov-report/src/builder/index.html +15 -15
- package/coverage/lcov-report/src/builder/revision.ts.html +1 -1
- package/coverage/lcov-report/src/builder/traffic.ts.html +101 -23
- package/coverage/lcov-report/src/list/index.html +116 -0
- package/coverage/lcov-report/src/{tester → list}/matrix.ts.html +87 -21
- package/coverage/lcov-report/src/tester/helpers.ts.html +313 -0
- package/coverage/lcov-report/src/tester/index.html +20 -35
- package/coverage/lcov.info +592 -436
- package/lib/assess-distribution/index.d.ts +1 -1
- package/lib/assess-distribution/index.js +102 -162
- package/lib/assess-distribution/index.js.map +1 -1
- package/lib/benchmark/index.js +87 -143
- package/lib/benchmark/index.js.map +1 -1
- package/lib/builder/allocator.d.ts +1 -1
- package/lib/builder/allocator.js +12 -12
- package/lib/builder/allocator.js.map +1 -1
- package/lib/builder/allocator.spec.js +22 -22
- package/lib/builder/allocator.spec.js.map +1 -1
- package/lib/builder/buildDatafile.d.ts +4 -3
- package/lib/builder/buildDatafile.js +311 -388
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/builder/buildProject.d.ts +2 -1
- package/lib/builder/buildProject.js +96 -183
- package/lib/builder/buildProject.js.map +1 -1
- package/lib/builder/convertToV1.d.ts +10 -0
- package/lib/builder/convertToV1.js +119 -0
- package/lib/builder/convertToV1.js.map +1 -0
- package/lib/builder/getFeatureRanges.d.ts +1 -1
- package/lib/builder/getFeatureRanges.js +32 -105
- package/lib/builder/getFeatureRanges.js.map +1 -1
- package/lib/builder/hashes.d.ts +4 -0
- package/lib/builder/hashes.js +70 -0
- package/lib/builder/hashes.js.map +1 -0
- package/lib/builder/revision.js +2 -2
- package/lib/builder/revision.js.map +1 -1
- package/lib/builder/revision.spec.js +1 -1
- package/lib/builder/revision.spec.js.map +1 -1
- package/lib/builder/traffic.d.ts +1 -1
- package/lib/builder/traffic.js +57 -48
- package/lib/builder/traffic.js.map +1 -1
- package/lib/builder/traffic.spec.js +14 -14
- package/lib/builder/traffic.spec.js.map +1 -1
- package/lib/cli/cli.js +60 -129
- package/lib/cli/cli.js.map +1 -1
- package/lib/cli/plugins.js +14 -16
- package/lib/cli/plugins.js.map +1 -1
- package/lib/config/parsers.js +1 -1
- package/lib/config/parsers.js.map +1 -1
- package/lib/config/projectConfig.d.ts +8 -6
- package/lib/config/projectConfig.js +31 -72
- package/lib/config/projectConfig.js.map +1 -1
- package/lib/datasource/adapter.d.ts +1 -1
- package/lib/datasource/adapter.js +2 -5
- package/lib/datasource/adapter.js.map +1 -1
- package/lib/datasource/datasource.d.ts +2 -1
- package/lib/datasource/datasource.js +107 -148
- package/lib/datasource/datasource.js.map +1 -1
- package/lib/datasource/filesystemAdapter.d.ts +1 -1
- package/lib/datasource/filesystemAdapter.js +224 -360
- package/lib/datasource/filesystemAdapter.js.map +1 -1
- package/lib/evaluate/index.d.ts +1 -1
- package/lib/evaluate/index.js +120 -188
- package/lib/evaluate/index.js.map +1 -1
- package/lib/find-duplicate-segments/findDuplicateSegments.d.ts +1 -1
- package/lib/find-duplicate-segments/findDuplicateSegments.js +40 -128
- package/lib/find-duplicate-segments/findDuplicateSegments.js.map +1 -1
- package/lib/find-duplicate-segments/index.js +27 -82
- package/lib/find-duplicate-segments/index.js.map +1 -1
- package/lib/find-usage/index.d.ts +7 -5
- package/lib/find-usage/index.js +333 -507
- package/lib/find-usage/index.js.map +1 -1
- package/lib/generate-code/index.js +36 -91
- package/lib/generate-code/index.js.map +1 -1
- package/lib/generate-code/typescript.js +117 -157
- package/lib/generate-code/typescript.js.map +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/info/index.js +45 -133
- package/lib/info/index.js.map +1 -1
- package/lib/init/index.d.ts +1 -1
- package/lib/init/index.js +16 -64
- package/lib/init/index.js.map +1 -1
- package/lib/linter/attributeSchema.d.ts +21 -6
- package/lib/linter/attributeSchema.js +18 -4
- package/lib/linter/attributeSchema.js.map +1 -1
- package/lib/linter/checkCircularDependency.d.ts +1 -1
- package/lib/linter/checkCircularDependency.js +22 -80
- package/lib/linter/checkCircularDependency.js.map +1 -1
- package/lib/linter/checkPercentageExceedingSlot.d.ts +1 -1
- package/lib/linter/checkPercentageExceedingSlot.js +36 -76
- package/lib/linter/checkPercentageExceedingSlot.js.map +1 -1
- package/lib/linter/conditionSchema.d.ts +1 -1
- package/lib/linter/conditionSchema.js +89 -41
- package/lib/linter/conditionSchema.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +345 -197
- package/lib/linter/featureSchema.js +313 -172
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/groupSchema.js +6 -6
- package/lib/linter/groupSchema.js.map +1 -1
- package/lib/linter/lintProject.js +306 -480
- package/lib/linter/lintProject.js.map +1 -1
- package/lib/linter/printError.js +7 -7
- package/lib/linter/printError.js.map +1 -1
- package/lib/linter/segmentSchema.js +2 -2
- package/lib/linter/segmentSchema.js.map +1 -1
- package/lib/linter/testSchema.d.ts +155 -3
- package/lib/linter/testSchema.js +47 -17
- package/lib/linter/testSchema.js.map +1 -1
- package/lib/list/index.d.ts +1 -0
- package/lib/list/index.js +349 -517
- package/lib/list/index.js.map +1 -1
- package/lib/{tester → list}/matrix.d.ts +1 -1
- package/lib/{tester → list}/matrix.js +50 -42
- package/lib/list/matrix.js.map +1 -0
- package/lib/{tester → list}/matrix.spec.js +7 -7
- package/lib/list/matrix.spec.js.map +1 -0
- package/lib/site/exportSite.js +25 -71
- package/lib/site/exportSite.js.map +1 -1
- package/lib/site/generateHistory.d.ts +1 -1
- package/lib/site/generateHistory.js +26 -82
- package/lib/site/generateHistory.js.map +1 -1
- package/lib/site/generateSiteSearchIndex.d.ts +1 -1
- package/lib/site/generateSiteSearchIndex.js +182 -259
- package/lib/site/generateSiteSearchIndex.js.map +1 -1
- package/lib/site/getLastModifiedFromHistory.d.ts +1 -1
- package/lib/site/getLastModifiedFromHistory.js +2 -2
- package/lib/site/getLastModifiedFromHistory.js.map +1 -1
- package/lib/site/getOwnerAndRepoFromUrl.js +6 -6
- package/lib/site/getOwnerAndRepoFromUrl.js.map +1 -1
- package/lib/site/getRelativePaths.js +7 -7
- package/lib/site/getRelativePaths.js.map +1 -1
- package/lib/site/getRepoDetails.js +20 -20
- package/lib/site/getRepoDetails.js.map +1 -1
- package/lib/site/index.js +25 -73
- package/lib/site/index.js.map +1 -1
- package/lib/site/serveSite.js +10 -10
- package/lib/site/serveSite.js.map +1 -1
- package/lib/tester/helpers.d.ts +2 -0
- package/lib/tester/helpers.js +71 -0
- package/lib/tester/helpers.js.map +1 -0
- package/lib/tester/helpers.spec.js +115 -0
- package/lib/tester/helpers.spec.js.map +1 -0
- package/lib/tester/index.d.ts +0 -1
- package/lib/tester/index.js +0 -1
- package/lib/tester/index.js.map +1 -1
- package/lib/tester/prettyDuration.js +11 -11
- package/lib/tester/prettyDuration.js.map +1 -1
- package/lib/tester/printTestResult.d.ts +1 -1
- package/lib/tester/printTestResult.js +35 -15
- package/lib/tester/printTestResult.js.map +1 -1
- package/lib/tester/testFeature.d.ts +4 -2
- package/lib/tester/testFeature.js +264 -226
- package/lib/tester/testFeature.js.map +1 -1
- package/lib/tester/testProject.d.ts +3 -7
- package/lib/tester/testProject.js +145 -246
- package/lib/tester/testProject.js.map +1 -1
- package/lib/tester/testSegment.d.ts +5 -2
- package/lib/tester/testSegment.js +65 -102
- package/lib/tester/testSegment.js.map +1 -1
- package/lib/utils/extractKeys.d.ts +2 -1
- package/lib/utils/extractKeys.js +57 -12
- package/lib/utils/extractKeys.js.map +1 -1
- package/lib/utils/git.d.ts +1 -1
- package/lib/utils/git.js +23 -23
- package/lib/utils/git.js.map +1 -1
- package/lib/utils/pretty.js +2 -4
- package/lib/utils/pretty.js.map +1 -1
- package/package.json +5 -6
- package/src/assess-distribution/index.ts +3 -2
- package/src/benchmark/index.ts +3 -3
- package/src/builder/allocator.spec.ts +1 -1
- package/src/builder/allocator.ts +1 -1
- package/src/builder/buildDatafile.ts +161 -124
- package/src/builder/buildProject.ts +6 -3
- package/src/builder/convertToV1.ts +166 -0
- package/src/builder/getFeatureRanges.ts +1 -1
- package/src/builder/hashes.ts +109 -0
- package/src/builder/traffic.ts +41 -15
- package/src/cli/cli.ts +1 -1
- package/src/cli/plugins.ts +0 -2
- package/src/config/projectConfig.ts +13 -10
- package/src/datasource/adapter.ts +1 -1
- package/src/datasource/datasource.ts +23 -2
- package/src/datasource/filesystemAdapter.ts +11 -12
- package/src/evaluate/index.ts +7 -6
- package/src/find-duplicate-segments/findDuplicateSegments.ts +1 -1
- package/src/find-usage/index.ts +111 -44
- package/src/generate-code/index.ts +1 -3
- package/src/generate-code/typescript.ts +7 -29
- package/src/index.ts +0 -1
- package/src/info/index.ts +2 -2
- package/src/init/index.ts +2 -2
- package/src/linter/attributeSchema.ts +18 -2
- package/src/linter/checkCircularDependency.ts +1 -1
- package/src/linter/checkPercentageExceedingSlot.ts +28 -8
- package/src/linter/conditionSchema.ts +66 -10
- package/src/linter/featureSchema.ts +312 -116
- package/src/linter/lintProject.ts +9 -4
- package/src/linter/testSchema.ts +42 -3
- package/src/list/index.ts +18 -30
- package/src/{tester → list}/matrix.ts +33 -11
- package/src/site/exportSite.ts +2 -4
- package/src/site/generateHistory.ts +1 -1
- package/src/site/generateSiteSearchIndex.ts +58 -50
- package/src/site/getLastModifiedFromHistory.ts +1 -1
- package/src/tester/helpers.spec.ts +149 -0
- package/src/tester/helpers.ts +76 -0
- package/src/tester/index.ts +0 -1
- package/src/tester/printTestResult.ts +25 -3
- package/src/tester/testFeature.ts +270 -124
- package/src/tester/testProject.ts +28 -49
- package/src/tester/testSegment.ts +48 -40
- package/src/utils/extractKeys.ts +58 -1
- package/src/utils/git.ts +1 -1
- package/tsconfig.cjs.json +1 -0
- package/coverage/lcov-report/lib/tester/checkIfObjectsAreEqual.js.html +0 -151
- package/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +0 -157
- package/lib/restore/index.d.ts +0 -4
- package/lib/restore/index.js +0 -91
- package/lib/restore/index.js.map +0 -1
- package/lib/tester/checkIfArraysAreEqual.d.ts +0 -1
- package/lib/tester/checkIfArraysAreEqual.js +0 -18
- package/lib/tester/checkIfArraysAreEqual.js.map +0 -1
- package/lib/tester/checkIfObjectsAreEqual.d.ts +0 -1
- package/lib/tester/checkIfObjectsAreEqual.js +0 -23
- package/lib/tester/checkIfObjectsAreEqual.js.map +0 -1
- package/lib/tester/checkIfObjectsAreEqual.spec.js +0 -26
- package/lib/tester/checkIfObjectsAreEqual.spec.js.map +0 -1
- package/lib/tester/matrix.js.map +0 -1
- package/lib/tester/matrix.spec.js.map +0 -1
- package/src/restore/index.ts +0 -42
- package/src/tester/checkIfArraysAreEqual.ts +0 -16
- package/src/tester/checkIfObjectsAreEqual.spec.ts +0 -31
- package/src/tester/checkIfObjectsAreEqual.ts +0 -24
- /package/lib/{tester → list}/matrix.spec.d.ts +0 -0
- /package/lib/tester/{checkIfObjectsAreEqual.spec.d.ts → helpers.spec.d.ts} +0 -0
- /package/src/{tester → list}/matrix.spec.ts +0 -0
|
@@ -163,6 +163,7 @@ function superRefineVariableValue(
|
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
// eslint-disable-next-line
|
|
166
167
|
} catch (e) {
|
|
167
168
|
ctx.addIssue({
|
|
168
169
|
code: z.ZodIssueCode.custom,
|
|
@@ -175,6 +176,131 @@ function superRefineVariableValue(
|
|
|
175
176
|
}
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
function refineForce({
|
|
180
|
+
ctx,
|
|
181
|
+
parsedFeature, // eslint-disable-line
|
|
182
|
+
variableSchemaByKey,
|
|
183
|
+
variationValues,
|
|
184
|
+
force,
|
|
185
|
+
pathPrefix,
|
|
186
|
+
projectConfig,
|
|
187
|
+
}) {
|
|
188
|
+
force.forEach((f, fN) => {
|
|
189
|
+
// force[n].variation
|
|
190
|
+
if (f.variation) {
|
|
191
|
+
if (variationValues.indexOf(f.variation) === -1) {
|
|
192
|
+
ctx.addIssue({
|
|
193
|
+
code: z.ZodIssueCode.custom,
|
|
194
|
+
message: `Unknown variation "${f.variation}" in force`,
|
|
195
|
+
path: [...pathPrefix, fN, "variation"],
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// force[n].variables[key]
|
|
201
|
+
if (f.variables) {
|
|
202
|
+
Object.keys(f.variables).forEach((variableKey) => {
|
|
203
|
+
superRefineVariableValue(
|
|
204
|
+
projectConfig,
|
|
205
|
+
variableSchemaByKey[variableKey],
|
|
206
|
+
f.variables[variableKey],
|
|
207
|
+
pathPrefix.concat([fN, "variables", variableKey]),
|
|
208
|
+
ctx,
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function refineRules({
|
|
216
|
+
ctx,
|
|
217
|
+
parsedFeature,
|
|
218
|
+
variableSchemaByKey,
|
|
219
|
+
variationValues,
|
|
220
|
+
rules,
|
|
221
|
+
pathPrefix,
|
|
222
|
+
projectConfig,
|
|
223
|
+
}) {
|
|
224
|
+
rules.forEach((rule, ruleN) => {
|
|
225
|
+
// rules[n].variables[key]
|
|
226
|
+
if (rule.variables) {
|
|
227
|
+
Object.keys(rule.variables).forEach((variableKey) => {
|
|
228
|
+
superRefineVariableValue(
|
|
229
|
+
projectConfig,
|
|
230
|
+
variableSchemaByKey[variableKey],
|
|
231
|
+
rule.variables[variableKey],
|
|
232
|
+
pathPrefix.concat([ruleN, "variables", variableKey]),
|
|
233
|
+
ctx,
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// rules[n].variationWeights
|
|
239
|
+
if (rule.variationWeights) {
|
|
240
|
+
if (!parsedFeature.variations) {
|
|
241
|
+
ctx.addIssue({
|
|
242
|
+
code: z.ZodIssueCode.custom,
|
|
243
|
+
message:
|
|
244
|
+
"Variation weights are overridden from rule, but no variations are present in feature.",
|
|
245
|
+
path: pathPrefix.concat([ruleN, "variationWeights"]),
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
const overriddenVariationValues = Object.keys(rule.variationWeights);
|
|
249
|
+
const overriddenVariationWeights: number[] = Object.values(rule.variationWeights);
|
|
250
|
+
|
|
251
|
+
// unique keys
|
|
252
|
+
if (overriddenVariationValues.length !== new Set(overriddenVariationValues).size) {
|
|
253
|
+
ctx.addIssue({
|
|
254
|
+
code: z.ZodIssueCode.custom,
|
|
255
|
+
message:
|
|
256
|
+
"Duplicate variation values found in variationWeights: " +
|
|
257
|
+
overriddenVariationValues.join(", "),
|
|
258
|
+
path: pathPrefix.concat([ruleN, "variationWeights"]),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// all original variations must be used
|
|
263
|
+
const missingVariations = variationValues.filter(
|
|
264
|
+
(v) => !overriddenVariationValues.includes(v),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (missingVariations.length > 0) {
|
|
268
|
+
ctx.addIssue({
|
|
269
|
+
code: z.ZodIssueCode.custom,
|
|
270
|
+
message: "Missing variations: " + missingVariations.join(", "),
|
|
271
|
+
path: pathPrefix.concat([ruleN, "variationWeights"]),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// unknown variations
|
|
276
|
+
const unknownVariations = overriddenVariationValues.filter(
|
|
277
|
+
(v) => !variationValues.includes(v),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
if (unknownVariations.length > 0) {
|
|
281
|
+
ctx.addIssue({
|
|
282
|
+
code: z.ZodIssueCode.custom,
|
|
283
|
+
message:
|
|
284
|
+
"Variation weights contain unknown variations: " + unknownVariations.join(", "),
|
|
285
|
+
path: pathPrefix.concat([ruleN, "variationWeights"]),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// weights sum must be 100
|
|
290
|
+
const weightsSum = overriddenVariationWeights.reduce((sum, weight) => sum + weight, 0);
|
|
291
|
+
|
|
292
|
+
if (weightsSum !== 100) {
|
|
293
|
+
ctx.addIssue({
|
|
294
|
+
code: z.ZodIssueCode.custom,
|
|
295
|
+
message: "Variation weights must sum to 100",
|
|
296
|
+
path: pathPrefix.concat([ruleN, "variationWeights"]),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
178
304
|
export function getFeatureZodSchema(
|
|
179
305
|
projectConfig: ProjectConfig,
|
|
180
306
|
conditionsZodSchema,
|
|
@@ -243,6 +369,7 @@ export function getFeatureZodSchema(
|
|
|
243
369
|
enabled: z.boolean().optional(),
|
|
244
370
|
variation: variationValueZodSchema.optional(),
|
|
245
371
|
variables: z.record(variableValueZodSchema).optional(),
|
|
372
|
+
variationWeights: z.record(z.number().min(0).max(100)).optional(),
|
|
246
373
|
})
|
|
247
374
|
.strict(),
|
|
248
375
|
)
|
|
@@ -304,26 +431,6 @@ export function getFeatureZodSchema(
|
|
|
304
431
|
)
|
|
305
432
|
.optional();
|
|
306
433
|
|
|
307
|
-
const environmentZodSchema = z
|
|
308
|
-
.object({
|
|
309
|
-
expose: exposeSchema,
|
|
310
|
-
rules: rulesSchema,
|
|
311
|
-
force: forceSchema,
|
|
312
|
-
})
|
|
313
|
-
.strict();
|
|
314
|
-
|
|
315
|
-
let allEnvironmentsZodSchema: z.ZodTypeAny = z.never();
|
|
316
|
-
|
|
317
|
-
if (Array.isArray(projectConfig.environments)) {
|
|
318
|
-
const allEnvironmentsSchema = {};
|
|
319
|
-
|
|
320
|
-
projectConfig.environments.forEach((environmentKey) => {
|
|
321
|
-
allEnvironmentsSchema[environmentKey] = environmentZodSchema;
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
allEnvironmentsZodSchema = z.object(allEnvironmentsSchema).strict();
|
|
325
|
-
}
|
|
326
|
-
|
|
327
434
|
const attributeKeyZodSchema = z.string().refine(
|
|
328
435
|
(value) => value === "*" || availableAttributeKeys.includes(value),
|
|
329
436
|
(value) => ({
|
|
@@ -338,11 +445,14 @@ export function getFeatureZodSchema(
|
|
|
338
445
|
}),
|
|
339
446
|
);
|
|
340
447
|
|
|
448
|
+
const environmentKeys = projectConfig.environments || [];
|
|
449
|
+
|
|
341
450
|
const featureZodSchema = z
|
|
342
451
|
.object({
|
|
343
452
|
archived: z.boolean().optional(),
|
|
344
453
|
deprecated: z.boolean().optional(),
|
|
345
454
|
description: z.string(),
|
|
455
|
+
|
|
346
456
|
tags: z
|
|
347
457
|
.array(
|
|
348
458
|
z.string().refine(
|
|
@@ -360,6 +470,7 @@ export function getFeatureZodSchema(
|
|
|
360
470
|
message: "Duplicate tags found: " + value.join(", "),
|
|
361
471
|
}),
|
|
362
472
|
),
|
|
473
|
+
|
|
363
474
|
required: z
|
|
364
475
|
.array(
|
|
365
476
|
z.union([
|
|
@@ -373,6 +484,7 @@ export function getFeatureZodSchema(
|
|
|
373
484
|
]),
|
|
374
485
|
)
|
|
375
486
|
.optional(),
|
|
487
|
+
|
|
376
488
|
bucketBy: z.union([
|
|
377
489
|
attributeKeyZodSchema,
|
|
378
490
|
z.array(attributeKeyZodSchema),
|
|
@@ -382,34 +494,24 @@ export function getFeatureZodSchema(
|
|
|
382
494
|
})
|
|
383
495
|
.strict(),
|
|
384
496
|
]),
|
|
385
|
-
|
|
497
|
+
|
|
386
498
|
variablesSchema: z
|
|
387
|
-
.
|
|
499
|
+
.record(
|
|
388
500
|
z
|
|
389
501
|
.object({
|
|
390
502
|
deprecated: z.boolean().optional(),
|
|
391
|
-
key: z
|
|
392
|
-
.string()
|
|
393
|
-
.min(1)
|
|
394
|
-
.refine((value) => value !== "variation", {
|
|
395
|
-
message: `variable key cannot be "variation"`,
|
|
396
|
-
}),
|
|
397
503
|
type: z.enum(["string", "integer", "boolean", "double", "array", "object", "json"]),
|
|
398
504
|
description: z.string().optional(),
|
|
399
505
|
defaultValue: variableValueZodSchema,
|
|
506
|
+
useDefaultWhenDisabled: z.boolean().optional(),
|
|
507
|
+
disabledValue: variableValueZodSchema.optional(),
|
|
400
508
|
})
|
|
401
509
|
.strict(),
|
|
402
510
|
)
|
|
403
|
-
.refine(
|
|
404
|
-
(value) => {
|
|
405
|
-
const keys = value.map((v) => v.key);
|
|
406
|
-
return keys.length === new Set(keys).size;
|
|
407
|
-
},
|
|
408
|
-
(value) => ({
|
|
409
|
-
message: "Duplicate variable keys found: " + value.map((v) => v.key).join(", "),
|
|
410
|
-
}),
|
|
411
|
-
)
|
|
412
511
|
.optional(),
|
|
512
|
+
|
|
513
|
+
disabledVariationValue: variationValueZodSchema.optional(),
|
|
514
|
+
|
|
413
515
|
variations: z
|
|
414
516
|
.array(
|
|
415
517
|
z
|
|
@@ -417,32 +519,25 @@ export function getFeatureZodSchema(
|
|
|
417
519
|
description: z.string().optional(),
|
|
418
520
|
value: variationValueZodSchema,
|
|
419
521
|
weight: z.number().min(0).max(100),
|
|
420
|
-
variables: z
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
})
|
|
440
|
-
.strict(),
|
|
441
|
-
]),
|
|
442
|
-
)
|
|
443
|
-
.optional(),
|
|
444
|
-
})
|
|
445
|
-
.strict(),
|
|
522
|
+
variables: z.record(variableValueZodSchema).optional(),
|
|
523
|
+
variableOverrides: z
|
|
524
|
+
.record(
|
|
525
|
+
z.array(
|
|
526
|
+
z.union([
|
|
527
|
+
z
|
|
528
|
+
.object({
|
|
529
|
+
conditions: conditionsZodSchema,
|
|
530
|
+
value: variableValueZodSchema,
|
|
531
|
+
})
|
|
532
|
+
.strict(),
|
|
533
|
+
z
|
|
534
|
+
.object({
|
|
535
|
+
segments: groupSegmentsZodSchema,
|
|
536
|
+
value: variableValueZodSchema,
|
|
537
|
+
})
|
|
538
|
+
.strict(),
|
|
539
|
+
]),
|
|
540
|
+
),
|
|
446
541
|
)
|
|
447
542
|
.optional(),
|
|
448
543
|
})
|
|
@@ -459,94 +554,195 @@ export function getFeatureZodSchema(
|
|
|
459
554
|
)
|
|
460
555
|
.optional(),
|
|
461
556
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
557
|
+
expose:
|
|
558
|
+
projectConfig.environments === false
|
|
559
|
+
? exposeSchema.optional()
|
|
560
|
+
: z.record(z.enum(environmentKeys as [string, ...string[]]), exposeSchema).optional(),
|
|
466
561
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
562
|
+
force:
|
|
563
|
+
projectConfig.environments === false
|
|
564
|
+
? forceSchema
|
|
565
|
+
: z.record(z.enum(environmentKeys as [string, ...string[]]), forceSchema).optional(),
|
|
566
|
+
|
|
567
|
+
rules:
|
|
568
|
+
projectConfig.environments === false
|
|
569
|
+
? rulesSchema
|
|
570
|
+
: z.record(z.enum(environmentKeys as [string, ...string[]]), rulesSchema),
|
|
471
571
|
})
|
|
472
572
|
.strict()
|
|
473
573
|
.superRefine((value, ctx) => {
|
|
574
|
+
// disabledVariationValue
|
|
575
|
+
if (value.disabledVariationValue) {
|
|
576
|
+
if (!value.variations) {
|
|
577
|
+
ctx.addIssue({
|
|
578
|
+
code: z.ZodIssueCode.custom,
|
|
579
|
+
message: "Disabled variation value is set, but no variations are present in feature.",
|
|
580
|
+
path: ["disabledVariationValue"],
|
|
581
|
+
});
|
|
582
|
+
} else {
|
|
583
|
+
const variationValues = value.variations.map((v) => v.value);
|
|
584
|
+
|
|
585
|
+
if (variationValues.indexOf(value.disabledVariationValue) === -1) {
|
|
586
|
+
ctx.addIssue({
|
|
587
|
+
code: z.ZodIssueCode.custom,
|
|
588
|
+
message: `Disabled variation value "${value.disabledVariationValue}" is not one of the defined variations: ${variationValues.join(", ")}`,
|
|
589
|
+
path: ["disabledVariationValue"],
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
474
595
|
if (!value.variablesSchema) {
|
|
475
596
|
return;
|
|
476
597
|
}
|
|
477
598
|
|
|
478
|
-
const
|
|
479
|
-
const
|
|
599
|
+
const variableSchemaByKey = value.variablesSchema;
|
|
600
|
+
const variationValues: string[] = [];
|
|
601
|
+
|
|
602
|
+
if (value.variations) {
|
|
603
|
+
value.variations.forEach((variation) => {
|
|
604
|
+
variationValues.push(variation.value);
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// variablesSchema[key]
|
|
609
|
+
const variableKeys = Object.keys(variableSchemaByKey);
|
|
610
|
+
variableKeys.forEach((variableKey) => {
|
|
611
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
612
|
+
|
|
613
|
+
if (variableKey === "variation") {
|
|
614
|
+
ctx.addIssue({
|
|
615
|
+
code: z.ZodIssueCode.custom,
|
|
616
|
+
message: `Variable key "${variableKey}" is reserved and cannot be used.`,
|
|
617
|
+
path: ["variablesSchema", variableKey],
|
|
618
|
+
});
|
|
619
|
+
}
|
|
480
620
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
621
|
+
// defaultValue
|
|
622
|
+
superRefineVariableValue(
|
|
623
|
+
projectConfig,
|
|
624
|
+
variableSchema,
|
|
625
|
+
variableSchema.defaultValue,
|
|
626
|
+
["variablesSchema", variableKey, "defaultValue"],
|
|
627
|
+
ctx,
|
|
628
|
+
);
|
|
484
629
|
|
|
630
|
+
// disabledValue
|
|
485
631
|
superRefineVariableValue(
|
|
486
632
|
projectConfig,
|
|
487
633
|
variableSchema,
|
|
488
634
|
variableSchema.defaultValue,
|
|
489
|
-
["variablesSchema",
|
|
635
|
+
["variablesSchema", variableKey, "disabledValue"],
|
|
490
636
|
ctx,
|
|
491
637
|
);
|
|
492
638
|
});
|
|
493
639
|
|
|
494
|
-
// variations
|
|
640
|
+
// variations
|
|
495
641
|
if (value.variations) {
|
|
496
642
|
value.variations.forEach((variation, variationN) => {
|
|
497
643
|
if (!variation.variables) {
|
|
498
644
|
return;
|
|
499
645
|
}
|
|
500
646
|
|
|
501
|
-
|
|
647
|
+
// variations[n].variables[key]
|
|
648
|
+
for (const variableKey of Object.keys(variation.variables)) {
|
|
649
|
+
const variableValue = variation.variables[variableKey];
|
|
650
|
+
|
|
502
651
|
superRefineVariableValue(
|
|
503
652
|
projectConfig,
|
|
504
|
-
variableSchemaByKey[
|
|
505
|
-
|
|
506
|
-
["variations", variationN, "variables",
|
|
653
|
+
variableSchemaByKey[variableKey],
|
|
654
|
+
variableValue,
|
|
655
|
+
["variations", variationN, "variables", variableKey],
|
|
507
656
|
ctx,
|
|
508
657
|
);
|
|
509
658
|
|
|
510
|
-
// variations[n].
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
override
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
659
|
+
// variations[n].variableOverrides[n].value
|
|
660
|
+
if (variation.variableOverrides) {
|
|
661
|
+
for (const variableKey of Object.keys(variation.variableOverrides)) {
|
|
662
|
+
const overrides = variation.variableOverrides[variableKey];
|
|
663
|
+
|
|
664
|
+
if (Array.isArray(overrides)) {
|
|
665
|
+
overrides.forEach((override, overrideN) => {
|
|
666
|
+
superRefineVariableValue(
|
|
667
|
+
projectConfig,
|
|
668
|
+
variableSchemaByKey[variableKey],
|
|
669
|
+
override.value,
|
|
670
|
+
[
|
|
671
|
+
"variations",
|
|
672
|
+
variationN,
|
|
673
|
+
"variableOverrides",
|
|
674
|
+
variableKey,
|
|
675
|
+
overrideN,
|
|
676
|
+
"value",
|
|
677
|
+
],
|
|
678
|
+
ctx,
|
|
679
|
+
);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
529
683
|
}
|
|
530
|
-
}
|
|
684
|
+
}
|
|
531
685
|
});
|
|
532
686
|
}
|
|
533
687
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
688
|
+
if (environmentKeys.length > 0) {
|
|
689
|
+
// with environments
|
|
690
|
+
for (const environmentKey of environmentKeys) {
|
|
691
|
+
// rules
|
|
692
|
+
if (value.rules && value.rules[environmentKey]) {
|
|
693
|
+
refineRules({
|
|
694
|
+
parsedFeature: value,
|
|
695
|
+
variableSchemaByKey,
|
|
696
|
+
variationValues,
|
|
697
|
+
rules: value.rules[environmentKey],
|
|
698
|
+
pathPrefix: ["rules", environmentKey],
|
|
699
|
+
ctx,
|
|
700
|
+
projectConfig,
|
|
546
701
|
});
|
|
547
702
|
}
|
|
548
|
-
|
|
549
|
-
|
|
703
|
+
|
|
704
|
+
// force
|
|
705
|
+
if (value.force && value.force[environmentKey]) {
|
|
706
|
+
refineForce({
|
|
707
|
+
parsedFeature: value,
|
|
708
|
+
variableSchemaByKey,
|
|
709
|
+
variationValues,
|
|
710
|
+
force: value.force[environmentKey],
|
|
711
|
+
pathPrefix: ["force", environmentKey],
|
|
712
|
+
ctx,
|
|
713
|
+
projectConfig,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
// no environments
|
|
719
|
+
|
|
720
|
+
// rules
|
|
721
|
+
if (value.rules) {
|
|
722
|
+
refineRules({
|
|
723
|
+
parsedFeature: value,
|
|
724
|
+
variableSchemaByKey,
|
|
725
|
+
variationValues,
|
|
726
|
+
rules: value.rules,
|
|
727
|
+
pathPrefix: ["rules"],
|
|
728
|
+
ctx,
|
|
729
|
+
projectConfig,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// force
|
|
734
|
+
if (value.force) {
|
|
735
|
+
refineForce({
|
|
736
|
+
parsedFeature: value,
|
|
737
|
+
variableSchemaByKey,
|
|
738
|
+
variationValues,
|
|
739
|
+
force: value.force,
|
|
740
|
+
pathPrefix: ["force"],
|
|
741
|
+
ctx,
|
|
742
|
+
projectConfig,
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
550
746
|
});
|
|
551
747
|
|
|
552
748
|
return featureZodSchema;
|
|
@@ -24,6 +24,9 @@ export interface LintProjectOptions {
|
|
|
24
24
|
const ENTITY_NAME_REGEX = /^[a-zA-Z0-9_\-./]+$/;
|
|
25
25
|
const ENTITY_NAME_REGEX_ERROR = "Names must be alphanumeric and can contain _, -, and .";
|
|
26
26
|
|
|
27
|
+
const ATTRIBUTE_NAME_REGEX = /^[a-zA-Z0-9_\-/]+$/;
|
|
28
|
+
const ATTRIBUTE_NAME_REGEX_ERROR = "Names must be alphanumeric and can contain _, and -";
|
|
29
|
+
|
|
27
30
|
async function getAuthorsOfEntity(datasource, entityType, entityKey): Promise<string[]> {
|
|
28
31
|
const entries = await datasource.listHistoryEntries(entityType, entityKey);
|
|
29
32
|
const authors: string[] = Array.from(new Set(entries.map((entry) => entry.author)));
|
|
@@ -88,7 +91,7 @@ export async function lintProject(
|
|
|
88
91
|
for (const key of filteredKeys) {
|
|
89
92
|
const fullPath = getFullPathFromKey("attribute", key);
|
|
90
93
|
|
|
91
|
-
if (!
|
|
94
|
+
if (!ATTRIBUTE_NAME_REGEX.test(key)) {
|
|
92
95
|
console.log(CLI_FORMAT_BOLD_UNDERLINE, fullPath);
|
|
93
96
|
|
|
94
97
|
if (options.authors) {
|
|
@@ -97,7 +100,7 @@ export async function lintProject(
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
console.log(CLI_FORMAT_RED, ` => Error: Invalid name: "${key}"`);
|
|
100
|
-
console.log(CLI_FORMAT_RED, ` ${
|
|
103
|
+
console.log(CLI_FORMAT_RED, ` ${ATTRIBUTE_NAME_REGEX_ERROR}`);
|
|
101
104
|
console.log("");
|
|
102
105
|
hasError = true;
|
|
103
106
|
}
|
|
@@ -137,11 +140,13 @@ export async function lintProject(
|
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
|
|
143
|
+
const flattenedAttributes = await datasource.listFlattenedAttributes();
|
|
144
|
+
|
|
140
145
|
// lint segments
|
|
141
146
|
const segments = await datasource.listSegments();
|
|
142
147
|
const conditionsZodSchema = getConditionsZodSchema(
|
|
143
148
|
projectConfig,
|
|
144
|
-
|
|
149
|
+
flattenedAttributes as [string, ...string[]],
|
|
145
150
|
);
|
|
146
151
|
const segmentZodSchema = getSegmentZodSchema(projectConfig, conditionsZodSchema);
|
|
147
152
|
|
|
@@ -209,7 +214,7 @@ export async function lintProject(
|
|
|
209
214
|
const featureZodSchema = getFeatureZodSchema(
|
|
210
215
|
projectConfig,
|
|
211
216
|
conditionsZodSchema,
|
|
212
|
-
|
|
217
|
+
flattenedAttributes as [string, ...string[]],
|
|
213
218
|
segments as [string, ...string[]],
|
|
214
219
|
features as [string, ...string[]],
|
|
215
220
|
);
|
package/src/linter/testSchema.ts
CHANGED
|
@@ -82,10 +82,49 @@ export function getTestsZodSchema(
|
|
|
82
82
|
}),
|
|
83
83
|
)
|
|
84
84
|
: z.never().optional(),
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
|
|
86
|
+
// parent
|
|
87
|
+
sticky: z.record(z.record(z.any())).optional(),
|
|
88
|
+
context: z.record(z.unknown()).optional(),
|
|
89
|
+
|
|
90
|
+
defaultVariationValue: z.string().optional(),
|
|
91
|
+
defaultVariableValues: z.record(z.unknown()).optional(),
|
|
92
|
+
|
|
93
|
+
expectedToBeEnabled: z.boolean().optional(),
|
|
94
|
+
expectedVariation: z.string().nullable().optional(),
|
|
88
95
|
expectedVariables: z.record(z.unknown()).optional(),
|
|
96
|
+
expectedEvaluations: z
|
|
97
|
+
.object({
|
|
98
|
+
flag: z.record(z.any()).optional(),
|
|
99
|
+
variation: z.record(z.any()).optional(),
|
|
100
|
+
variables: z.record(z.record(z.any())).optional(),
|
|
101
|
+
})
|
|
102
|
+
.optional(),
|
|
103
|
+
|
|
104
|
+
children: z
|
|
105
|
+
.array(
|
|
106
|
+
z.object({
|
|
107
|
+
// copied from parent
|
|
108
|
+
sticky: z.record(z.record(z.any())).optional(),
|
|
109
|
+
context: z.record(z.unknown()).optional(),
|
|
110
|
+
|
|
111
|
+
defaultVariationValue: z.string().optional(),
|
|
112
|
+
defaultVariableValues: z.record(z.unknown()).optional(),
|
|
113
|
+
|
|
114
|
+
expectedToBeEnabled: z.boolean().optional(),
|
|
115
|
+
expectedVariation: z.string().nullable().optional(),
|
|
116
|
+
expectedVariables: z.record(z.unknown()).optional(),
|
|
117
|
+
|
|
118
|
+
expectedEvaluations: z
|
|
119
|
+
.object({
|
|
120
|
+
flag: z.record(z.any()).optional(),
|
|
121
|
+
variation: z.record(z.any()).optional(),
|
|
122
|
+
variables: z.record(z.record(z.any())).optional(),
|
|
123
|
+
})
|
|
124
|
+
.optional(),
|
|
125
|
+
}),
|
|
126
|
+
)
|
|
127
|
+
.optional(),
|
|
89
128
|
})
|
|
90
129
|
.strict(),
|
|
91
130
|
),
|