@featurevisor/core 2.25.0 → 2.26.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 +11 -0
- package/coverage/clover.xml +1499 -1476
- package/coverage/coverage-final.json +18 -17
- package/coverage/lcov-report/builder/allocator.ts.html +1 -1
- package/coverage/lcov-report/builder/buildDatafile.ts.html +1 -1
- package/coverage/lcov-report/builder/buildScopedConditions.ts.html +1 -1
- package/coverage/lcov-report/builder/buildScopedDatafile.ts.html +1 -1
- package/coverage/lcov-report/builder/buildScopedSegments.ts.html +1 -1
- package/coverage/lcov-report/builder/convertToV1.ts.html +1 -1
- package/coverage/lcov-report/builder/getFeatureRanges.ts.html +1 -1
- package/coverage/lcov-report/builder/hashes.ts.html +1 -1
- package/coverage/lcov-report/builder/index.html +1 -1
- package/coverage/lcov-report/builder/mutateVariables.ts.html +1 -1
- package/coverage/lcov-report/builder/mutator.ts.html +52 -52
- package/coverage/lcov-report/builder/revision.ts.html +1 -1
- package/coverage/lcov-report/builder/traffic.ts.html +1 -1
- package/coverage/lcov-report/config/index.html +1 -1
- package/coverage/lcov-report/config/index.ts.html +1 -1
- package/coverage/lcov-report/config/projectConfig.ts.html +15 -15
- package/coverage/lcov-report/datasource/adapter.ts.html +1 -1
- package/coverage/lcov-report/datasource/datasource.ts.html +18 -18
- package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +43 -43
- package/coverage/lcov-report/datasource/index.html +1 -1
- package/coverage/lcov-report/datasource/index.ts.html +1 -1
- package/coverage/lcov-report/generate-code/index.html +1 -1
- package/coverage/lcov-report/generate-code/typescript.ts.html +1 -1
- package/coverage/lcov-report/index.html +18 -18
- package/coverage/lcov-report/linter/attributeSchema.ts.html +130 -130
- package/coverage/lcov-report/linter/checkCircularDependency.ts.html +11 -11
- package/coverage/lcov-report/linter/checkPercentageExceedingSlot.ts.html +15 -15
- package/coverage/lcov-report/linter/conditionSchema.ts.html +150 -150
- package/coverage/lcov-report/linter/featureSchema.ts.html +458 -443
- package/coverage/lcov-report/linter/groupSchema.ts.html +24 -27
- package/coverage/lcov-report/linter/index.html +83 -68
- package/coverage/lcov-report/linter/lintProject.ts.html +158 -158
- package/coverage/lcov-report/linter/mutationNotation.ts.html +104 -104
- package/coverage/lcov-report/linter/printError.ts.html +79 -28
- package/coverage/lcov-report/linter/schema.ts.html +129 -135
- package/coverage/lcov-report/linter/segmentSchema.ts.html +15 -9
- package/coverage/lcov-report/linter/testSchema.ts.html +67 -94
- package/coverage/lcov-report/linter/zodHelpers.ts.html +133 -0
- package/coverage/lcov-report/list/index.html +1 -1
- package/coverage/lcov-report/list/matrix.ts.html +1 -1
- package/coverage/lcov-report/tester/cliFormat.ts.html +6 -6
- package/coverage/lcov-report/tester/helpers.ts.html +1 -1
- package/coverage/lcov-report/tester/index.html +1 -1
- package/coverage/lcov-report/utils/extractKeys.ts.html +1 -1
- package/coverage/lcov-report/utils/git.ts.html +1 -1
- package/coverage/lcov-report/utils/index.html +1 -1
- package/coverage/lcov-report/utils/index.ts.html +1 -1
- package/coverage/lcov-report/utils/pretty.ts.html +1 -1
- package/coverage/lcov.info +3031 -2984
- package/lib/linter/attributeSchema.d.ts +19 -352
- package/lib/linter/attributeSchema.spec.js.map +1 -1
- package/lib/linter/conditionSchema.d.ts +1 -1
- package/lib/linter/conditionSchema.js +2 -3
- package/lib/linter/conditionSchema.js.map +1 -1
- package/lib/linter/conditionSchema.spec.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +75 -964
- package/lib/linter/featureSchema.js +64 -53
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/featureSchema.spec.js +28 -0
- package/lib/linter/featureSchema.spec.js.map +1 -1
- package/lib/linter/groupSchema.d.ts +4 -28
- package/lib/linter/groupSchema.js +3 -5
- package/lib/linter/groupSchema.js.map +1 -1
- package/lib/linter/lintProject.spec.js +101 -13
- package/lib/linter/lintProject.spec.js.map +1 -1
- package/lib/linter/printError.js +21 -7
- package/lib/linter/printError.js.map +1 -1
- package/lib/linter/schema.d.ts +9 -2
- package/lib/linter/schema.js +2 -6
- package/lib/linter/schema.js.map +1 -1
- package/lib/linter/schema.spec.js +3 -2
- package/lib/linter/schema.spec.js.map +1 -1
- package/lib/linter/segmentSchema.d.ts +1 -9
- package/lib/linter/segmentSchema.js +3 -1
- package/lib/linter/segmentSchema.js.map +1 -1
- package/lib/linter/segmentSchema.spec.js.map +1 -1
- package/lib/linter/testSchema.d.ts +16 -222
- package/lib/linter/testSchema.js +23 -38
- package/lib/linter/testSchema.js.map +1 -1
- package/lib/linter/testSchema.spec.d.ts +1 -0
- package/lib/linter/testSchema.spec.js +162 -0
- package/lib/linter/testSchema.spec.js.map +1 -0
- package/lib/linter/zodHelpers.d.ts +2 -0
- package/lib/linter/zodHelpers.js +15 -0
- package/lib/linter/zodHelpers.js.map +1 -0
- package/package.json +3 -3
- package/src/linter/attributeSchema.spec.ts +3 -3
- package/src/linter/conditionSchema.spec.ts +3 -3
- package/src/linter/conditionSchema.ts +4 -4
- package/src/linter/featureSchema.spec.ts +39 -4
- package/src/linter/featureSchema.ts +82 -77
- package/src/linter/groupSchema.ts +11 -12
- package/src/linter/lintProject.spec.ts +121 -13
- package/src/linter/printError.ts +27 -10
- package/src/linter/schema.spec.ts +12 -11
- package/src/linter/schema.ts +7 -9
- package/src/linter/segmentSchema.spec.ts +5 -5
- package/src/linter/segmentSchema.ts +3 -1
- package/src/linter/testSchema.spec.ts +193 -0
- package/src/linter/testSchema.ts +36 -45
- package/src/linter/zodHelpers.ts +16 -0
package/lib/linter/testSchema.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTestsZodSchema = getTestsZodSchema;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const zodHelpers_1 = require("./zodHelpers");
|
|
5
6
|
function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegmentKeys) {
|
|
6
7
|
const scopeNames = projectConfig.scopes ? projectConfig.scopes.map((scope) => scope.name) : [];
|
|
7
|
-
const matrixZodSchema = zod_1.z.record(zod_1.z.array(zod_1.z.union([
|
|
8
|
+
const matrixZodSchema = zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.union([
|
|
8
9
|
// allowed values in arrays
|
|
9
10
|
zod_1.z.string(),
|
|
10
11
|
zod_1.z.number(),
|
|
@@ -13,14 +14,12 @@ function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegment
|
|
|
13
14
|
])));
|
|
14
15
|
const segmentTestZodSchema = zod_1.z
|
|
15
16
|
.object({
|
|
16
|
-
segment: zod_1.z.string()
|
|
17
|
-
message: `Unknown segment "${value}"`,
|
|
18
|
-
})),
|
|
17
|
+
segment: (0, zodHelpers_1.refineWithMessage)(zod_1.z.string(), (value) => availableSegmentKeys.includes(value), (value) => `Unknown segment "${value}"`),
|
|
19
18
|
assertions: zod_1.z.array(zod_1.z
|
|
20
19
|
.object({
|
|
21
20
|
matrix: matrixZodSchema.optional(),
|
|
22
21
|
description: zod_1.z.string().optional(),
|
|
23
|
-
context: zod_1.z.record(zod_1.z.unknown()),
|
|
22
|
+
context: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()),
|
|
24
23
|
expectedToMatch: zod_1.z.boolean(),
|
|
25
24
|
})
|
|
26
25
|
.strict()),
|
|
@@ -28,9 +27,7 @@ function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegment
|
|
|
28
27
|
.strict();
|
|
29
28
|
const featureTestZodSchema = zod_1.z
|
|
30
29
|
.object({
|
|
31
|
-
feature: zod_1.z.string()
|
|
32
|
-
message: `Unknown feature "${value}"`,
|
|
33
|
-
})),
|
|
30
|
+
feature: (0, zodHelpers_1.refineWithMessage)(zod_1.z.string(), (value) => availableFeatureKeys.includes(value), (value) => `Unknown feature "${value}"`),
|
|
34
31
|
assertions: zod_1.z.array(zod_1.z
|
|
35
32
|
.object({
|
|
36
33
|
matrix: matrixZodSchema.optional(),
|
|
@@ -41,7 +38,7 @@ function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegment
|
|
|
41
38
|
zod_1.z.string(),
|
|
42
39
|
]),
|
|
43
40
|
environment: Array.isArray(projectConfig.environments)
|
|
44
|
-
? zod_1.z.string()
|
|
41
|
+
? (0, zodHelpers_1.refineWithMessage)(zod_1.z.string(), (value) => {
|
|
45
42
|
if (value.indexOf("${{") === 0) {
|
|
46
43
|
// allow unknown strings for matrix
|
|
47
44
|
return true;
|
|
@@ -52,52 +49,40 @@ function getTestsZodSchema(projectConfig, availableFeatureKeys, availableSegment
|
|
|
52
49
|
return true;
|
|
53
50
|
}
|
|
54
51
|
return false;
|
|
55
|
-
}, (value) =>
|
|
56
|
-
message: `Unknown environment "${value}"`,
|
|
57
|
-
}))
|
|
52
|
+
}, (value) => `Unknown environment "${value}"`)
|
|
58
53
|
: zod_1.z.never().optional(),
|
|
59
|
-
tag: zod_1.z
|
|
60
|
-
|
|
61
|
-
.refine((value) => projectConfig.tags.includes(value), (value) => ({
|
|
62
|
-
message: `Unknown tag "${value}"`,
|
|
63
|
-
}))
|
|
64
|
-
.optional(),
|
|
65
|
-
scope: zod_1.z
|
|
66
|
-
.string()
|
|
67
|
-
.refine((value) => scopeNames.includes(value), (value) => ({
|
|
68
|
-
message: `Unknown scope "${value}"`,
|
|
69
|
-
}))
|
|
70
|
-
.optional(),
|
|
54
|
+
tag: (0, zodHelpers_1.refineWithMessage)(zod_1.z.string(), (value) => projectConfig.tags.includes(value), (value) => `Unknown tag "${value}"`).optional(),
|
|
55
|
+
scope: (0, zodHelpers_1.refineWithMessage)(zod_1.z.string(), (value) => scopeNames.includes(value), (value) => `Unknown scope "${value}"`).optional(),
|
|
71
56
|
// parent
|
|
72
|
-
sticky: zod_1.z.record(zod_1.z.record(zod_1.z.any())).optional(),
|
|
73
|
-
context: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
57
|
+
sticky: zod_1.z.record(zod_1.z.string(), zod_1.z.record(zod_1.z.string(), zod_1.z.any())).optional(),
|
|
58
|
+
context: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
74
59
|
defaultVariationValue: zod_1.z.string().optional(),
|
|
75
|
-
defaultVariableValues: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
60
|
+
defaultVariableValues: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
76
61
|
expectedToBeEnabled: zod_1.z.boolean().optional(),
|
|
77
62
|
expectedVariation: zod_1.z.string().nullable().optional(),
|
|
78
|
-
expectedVariables: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
63
|
+
expectedVariables: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
79
64
|
expectedEvaluations: zod_1.z
|
|
80
65
|
.object({
|
|
81
|
-
flag: zod_1.z.record(zod_1.z.any()).optional(),
|
|
82
|
-
variation: zod_1.z.record(zod_1.z.any()).optional(),
|
|
83
|
-
variables: zod_1.z.record(zod_1.z.record(zod_1.z.any())).optional(),
|
|
66
|
+
flag: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
67
|
+
variation: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
68
|
+
variables: zod_1.z.record(zod_1.z.string(), zod_1.z.record(zod_1.z.string(), zod_1.z.any())).optional(),
|
|
84
69
|
})
|
|
85
70
|
.optional(),
|
|
86
71
|
children: zod_1.z
|
|
87
72
|
.array(zod_1.z.object({
|
|
88
73
|
// copied from parent
|
|
89
|
-
sticky: zod_1.z.record(zod_1.z.record(zod_1.z.any())).optional(),
|
|
90
|
-
context: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
74
|
+
sticky: zod_1.z.record(zod_1.z.string(), zod_1.z.record(zod_1.z.string(), zod_1.z.any())).optional(),
|
|
75
|
+
context: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
91
76
|
defaultVariationValue: zod_1.z.string().optional(),
|
|
92
|
-
defaultVariableValues: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
77
|
+
defaultVariableValues: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
93
78
|
expectedToBeEnabled: zod_1.z.boolean().optional(),
|
|
94
79
|
expectedVariation: zod_1.z.string().nullable().optional(),
|
|
95
|
-
expectedVariables: zod_1.z.record(zod_1.z.unknown()).optional(),
|
|
80
|
+
expectedVariables: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
96
81
|
expectedEvaluations: zod_1.z
|
|
97
82
|
.object({
|
|
98
|
-
flag: zod_1.z.record(zod_1.z.any()).optional(),
|
|
99
|
-
variation: zod_1.z.record(zod_1.z.any()).optional(),
|
|
100
|
-
variables: zod_1.z.record(zod_1.z.record(zod_1.z.any())).optional(),
|
|
83
|
+
flag: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
84
|
+
variation: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
85
|
+
variables: zod_1.z.record(zod_1.z.string(), zod_1.z.record(zod_1.z.string(), zod_1.z.any())).optional(),
|
|
101
86
|
})
|
|
102
87
|
.optional(),
|
|
103
88
|
}))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testSchema.js","sourceRoot":"","sources":["../../src/linter/testSchema.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"testSchema.js","sourceRoot":"","sources":["../../src/linter/testSchema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const printError_1 = require("./printError");
|
|
4
|
+
const testSchema_1 = require("./testSchema");
|
|
5
|
+
function minimalProjectConfig(overrides = {}) {
|
|
6
|
+
return {
|
|
7
|
+
featuresDirectoryPath: "",
|
|
8
|
+
segmentsDirectoryPath: "",
|
|
9
|
+
attributesDirectoryPath: "",
|
|
10
|
+
groupsDirectoryPath: "",
|
|
11
|
+
schemasDirectoryPath: "",
|
|
12
|
+
testsDirectoryPath: "",
|
|
13
|
+
stateDirectoryPath: "",
|
|
14
|
+
datafilesDirectoryPath: "",
|
|
15
|
+
datafileNamePattern: "",
|
|
16
|
+
revisionFileName: "",
|
|
17
|
+
siteExportDirectoryPath: "",
|
|
18
|
+
environmentsDirectoryPath: "",
|
|
19
|
+
environments: ["staging", "production"],
|
|
20
|
+
splitByEnvironment: false,
|
|
21
|
+
tags: ["all", "beta"],
|
|
22
|
+
adapter: {},
|
|
23
|
+
plugins: [],
|
|
24
|
+
defaultBucketBy: "userId",
|
|
25
|
+
parser: "yml",
|
|
26
|
+
prettyState: true,
|
|
27
|
+
prettyDatafile: false,
|
|
28
|
+
stringify: true,
|
|
29
|
+
...overrides,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function getSchema(projectConfig = minimalProjectConfig()) {
|
|
33
|
+
return (0, testSchema_1.getTestsZodSchema)(projectConfig, ["checkout"], ["desktop"]);
|
|
34
|
+
}
|
|
35
|
+
function parseTest(input) {
|
|
36
|
+
return getSchema().safeParse(input);
|
|
37
|
+
}
|
|
38
|
+
function expectTestSuccess(input) {
|
|
39
|
+
const result = parseTest(input);
|
|
40
|
+
expect(result.success).toBe(true);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
const error = result.error;
|
|
43
|
+
throw new Error(error.issues.map((issue) => issue.message).join("; "));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function expectTestFailure(input, messageSubstring) {
|
|
47
|
+
const result = parseTest(input);
|
|
48
|
+
expect(result.success).toBe(false);
|
|
49
|
+
if (result.success) {
|
|
50
|
+
throw new Error("Expected test schema to fail");
|
|
51
|
+
}
|
|
52
|
+
const error = result.error;
|
|
53
|
+
expect(error.issues.map((issue) => issue.message).join(" ")).toContain(messageSubstring);
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
56
|
+
describe("testSchema.ts :: getTestsZodSchema", () => {
|
|
57
|
+
it("accepts a valid feature test with matrix, context, sticky, and expected evaluations", () => {
|
|
58
|
+
expectTestSuccess({
|
|
59
|
+
feature: "checkout",
|
|
60
|
+
assertions: [
|
|
61
|
+
{
|
|
62
|
+
matrix: {
|
|
63
|
+
country: ["nl", "de"],
|
|
64
|
+
loggedIn: [true, false],
|
|
65
|
+
score: [1, 2],
|
|
66
|
+
empty: [null],
|
|
67
|
+
},
|
|
68
|
+
description: "feature assertion",
|
|
69
|
+
at: "${{ score }}",
|
|
70
|
+
environment: "staging",
|
|
71
|
+
tag: "all",
|
|
72
|
+
sticky: {
|
|
73
|
+
flag: { checkout: "cached" },
|
|
74
|
+
},
|
|
75
|
+
context: {
|
|
76
|
+
userId: "user-1",
|
|
77
|
+
},
|
|
78
|
+
defaultVariableValues: {
|
|
79
|
+
title: "Fallback",
|
|
80
|
+
},
|
|
81
|
+
expectedToBeEnabled: true,
|
|
82
|
+
expectedVariation: "control",
|
|
83
|
+
expectedVariables: {
|
|
84
|
+
title: "Checkout",
|
|
85
|
+
},
|
|
86
|
+
expectedEvaluations: {
|
|
87
|
+
flag: { reason: "rule" },
|
|
88
|
+
variation: { reason: "bucketed" },
|
|
89
|
+
variables: { title: { reason: "default" } },
|
|
90
|
+
},
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
context: {
|
|
94
|
+
userId: "child-1",
|
|
95
|
+
},
|
|
96
|
+
expectedToBeEnabled: false,
|
|
97
|
+
expectedVariation: null,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it("accepts matrix placeholders for environment values", () => {
|
|
105
|
+
expectTestSuccess({
|
|
106
|
+
feature: "checkout",
|
|
107
|
+
assertions: [
|
|
108
|
+
{
|
|
109
|
+
matrix: {
|
|
110
|
+
environment: ["staging", "production"],
|
|
111
|
+
},
|
|
112
|
+
at: 1,
|
|
113
|
+
environment: "${{ environment }}",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
it("rejects unknown environments with a precise path", () => {
|
|
119
|
+
const error = expectTestFailure({
|
|
120
|
+
feature: "checkout",
|
|
121
|
+
assertions: [
|
|
122
|
+
{
|
|
123
|
+
at: 1,
|
|
124
|
+
environment: "qa",
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
}, 'Unknown environment "qa"');
|
|
128
|
+
expect(error.issues.some((issue) => issue.path.join(".") === "assertions.0.environment")).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
it("rejects unknown feature and segment keys", () => {
|
|
131
|
+
expectTestFailure({
|
|
132
|
+
feature: "not-real",
|
|
133
|
+
assertions: [{ at: 1, environment: "staging" }],
|
|
134
|
+
}, 'Unknown feature "not-real"');
|
|
135
|
+
expectTestFailure({
|
|
136
|
+
segment: "not-real",
|
|
137
|
+
assertions: [
|
|
138
|
+
{
|
|
139
|
+
context: {},
|
|
140
|
+
expectedToMatch: true,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
}, 'Unknown segment "not-real"');
|
|
144
|
+
});
|
|
145
|
+
it("rejects invalid matrix values", () => {
|
|
146
|
+
const error = expectTestFailure({
|
|
147
|
+
feature: "checkout",
|
|
148
|
+
assertions: [
|
|
149
|
+
{
|
|
150
|
+
matrix: {
|
|
151
|
+
country: [{ code: "nl" }],
|
|
152
|
+
},
|
|
153
|
+
at: 1,
|
|
154
|
+
environment: "staging",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
}, "Invalid input");
|
|
158
|
+
const lintIssues = (0, printError_1.getLintIssuesFromZodError)(error);
|
|
159
|
+
expect(lintIssues.some((issue) => issue.path.join(".").includes("matrix.country.0"))).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=testSchema.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testSchema.spec.js","sourceRoot":"","sources":["../../src/linter/testSchema.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.refineWithMessage = refineWithMessage;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
function refineWithMessage(schema, predicate, getMessage) {
|
|
6
|
+
return schema.superRefine((value, ctx) => {
|
|
7
|
+
if (!predicate(value)) {
|
|
8
|
+
ctx.addIssue({
|
|
9
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
10
|
+
message: getMessage(value),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=zodHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zodHelpers.js","sourceRoot":"","sources":["../../src/linter/zodHelpers.ts"],"names":[],"mappings":";;AAEA,8CAaC;AAfD,6BAAwB;AAExB,SAAgB,iBAAiB,CAC/B,MAAoB,EACpB,SAAgC,EAChC,UAAgC;IAEhC,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,OAAC,CAAC,YAAY,CAAC,MAAM;gBAC3B,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featurevisor/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Core package of Featurevisor for Node.js usage",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"axios": "^1.3.4",
|
|
48
48
|
"tar": "^7.5.7",
|
|
49
49
|
"yargs": "^17.7.2",
|
|
50
|
-
"zod": "^
|
|
50
|
+
"zod": "^4.0.17"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "27068c0b515c56a11bea79bacd128a64d7543325"
|
|
53
53
|
}
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
import { getAttributeZodSchema } from "./attributeSchema";
|
|
4
4
|
|
|
5
|
-
function parseAttribute(input: unknown): z.
|
|
5
|
+
function parseAttribute(input: unknown): z.ZodSafeParseResult<unknown> {
|
|
6
6
|
return getAttributeZodSchema().safeParse(input);
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ function expectAttributeSuccess(input: unknown): void {
|
|
|
10
10
|
const result = parseAttribute(input);
|
|
11
11
|
expect(result.success).toBe(true);
|
|
12
12
|
if (!result.success) {
|
|
13
|
-
const error = (result as z.
|
|
13
|
+
const error = (result as z.ZodSafeParseError<unknown>).error;
|
|
14
14
|
const msg = error.issues.map((issue) => issue.message).join("; ");
|
|
15
15
|
throw new Error(`Expected attribute to pass: ${msg}`);
|
|
16
16
|
}
|
|
@@ -23,7 +23,7 @@ function expectAttributeFailure(input: unknown, messageSubstring?: string): z.Zo
|
|
|
23
23
|
throw new Error("Expected attribute to fail");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const error = (result as z.
|
|
26
|
+
const error = (result as z.ZodSafeParseError<unknown>).error;
|
|
27
27
|
|
|
28
28
|
if (messageSubstring) {
|
|
29
29
|
const messages = error.issues.map((issue) => issue.message).join(" ");
|
|
@@ -115,7 +115,7 @@ function getConditionsSchema() {
|
|
|
115
115
|
return getConditionsZodSchema(minimalProjectConfig(), TEST_ATTRIBUTES);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function parseConditions(input: unknown): z.
|
|
118
|
+
function parseConditions(input: unknown): z.ZodSafeParseResult<unknown> {
|
|
119
119
|
return getConditionsSchema().safeParse(input);
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -123,7 +123,7 @@ function expectConditionsSuccess(input: unknown): void {
|
|
|
123
123
|
const result = parseConditions(input);
|
|
124
124
|
expect(result.success).toBe(true);
|
|
125
125
|
if (!result.success) {
|
|
126
|
-
const err = (result as z.
|
|
126
|
+
const err = (result as z.ZodSafeParseError<unknown>).error;
|
|
127
127
|
const msg = err.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
128
128
|
throw new Error(`Expected conditions to pass: ${msg}`);
|
|
129
129
|
}
|
|
@@ -133,7 +133,7 @@ function expectConditionsFailure(input: unknown, messageSubstring?: string): z.Z
|
|
|
133
133
|
const result = parseConditions(input);
|
|
134
134
|
expect(result.success).toBe(false);
|
|
135
135
|
if (result.success) throw new Error("Expected conditions to fail");
|
|
136
|
-
const err = (result as z.
|
|
136
|
+
const err = (result as z.ZodSafeParseError<unknown>).error;
|
|
137
137
|
if (messageSubstring) {
|
|
138
138
|
const messages = err.issues
|
|
139
139
|
.map((i) => (typeof i.message === "string" ? i.message : ""))
|
|
@@ -2,6 +2,7 @@ import type { Attribute, AttributeProperty, Schema } from "@featurevisor/types";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import { ProjectConfig } from "../config";
|
|
5
|
+
import { refineWithMessage } from "./zodHelpers";
|
|
5
6
|
|
|
6
7
|
const commonOperators: [string, ...string[]] = ["equals", "notEquals"];
|
|
7
8
|
const numericOperators = ["greaterThan", "greaterThanOrEquals", "lessThan", "lessThanOrEquals"];
|
|
@@ -551,11 +552,10 @@ export function getConditionsZodSchema(
|
|
|
551
552
|
) {
|
|
552
553
|
const plainConditionZodSchema = z
|
|
553
554
|
.object({
|
|
554
|
-
attribute:
|
|
555
|
+
attribute: refineWithMessage(
|
|
556
|
+
z.string(),
|
|
555
557
|
(value) => resolveAttributePath(value, attributesByKey, schemasByKey) !== null,
|
|
556
|
-
(value) =>
|
|
557
|
-
message: `Unknown attribute "${value}"`,
|
|
558
|
-
}),
|
|
558
|
+
(value) => `Unknown attribute "${value}"`,
|
|
559
559
|
),
|
|
560
560
|
operator: z.enum([
|
|
561
561
|
...commonOperators,
|
|
@@ -135,7 +135,7 @@ function baseFeature(overrides: Record<string, unknown> = {}) {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function parseFeature(feature: unknown): z.
|
|
138
|
+
function parseFeature(feature: unknown): z.ZodSafeParseResult<unknown> {
|
|
139
139
|
return getFeatureSchema().safeParse(feature);
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -143,7 +143,7 @@ function expectParseSuccess(feature: unknown): void {
|
|
|
143
143
|
const result = parseFeature(feature);
|
|
144
144
|
expect(result.success).toBe(true);
|
|
145
145
|
if (!result.success) {
|
|
146
|
-
const err = (result as z.
|
|
146
|
+
const err = (result as z.ZodSafeParseError<unknown>).error;
|
|
147
147
|
const msg = err.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
148
148
|
throw new Error(`Expected success but got: ${msg}`);
|
|
149
149
|
}
|
|
@@ -153,7 +153,7 @@ function expectParseFailure(feature: unknown, messageSubstring?: string): z.ZodE
|
|
|
153
153
|
const result = parseFeature(feature);
|
|
154
154
|
expect(result.success).toBe(false);
|
|
155
155
|
if (result.success) throw new Error("Expected parse failure");
|
|
156
|
-
const err = (result as z.
|
|
156
|
+
const err = (result as z.ZodSafeParseError<unknown>).error;
|
|
157
157
|
if (messageSubstring) {
|
|
158
158
|
const messages = err.issues
|
|
159
159
|
.map((i) => (typeof i.message === "string" ? i.message : ""))
|
|
@@ -317,7 +317,7 @@ describe("featureSchema.ts :: getFeatureZodSchema (variablesSchema and variable
|
|
|
317
317
|
);
|
|
318
318
|
expect(result.success).toBe(false);
|
|
319
319
|
if (result.success) return;
|
|
320
|
-
const messages = (result as z.
|
|
320
|
+
const messages = (result as z.ZodSafeParseError<unknown>).error.issues
|
|
321
321
|
.map((i) => (typeof i.message === "string" ? i.message : ""))
|
|
322
322
|
.join(" ");
|
|
323
323
|
expect(messages).toContain("could not be loaded");
|
|
@@ -1099,6 +1099,41 @@ describe("featureSchema.ts :: getFeatureZodSchema (variablesSchema and variable
|
|
|
1099
1099
|
});
|
|
1100
1100
|
});
|
|
1101
1101
|
|
|
1102
|
+
describe("environment maps", () => {
|
|
1103
|
+
it("allows a subset of configured environments in rules, force, and expose", () => {
|
|
1104
|
+
expectParseSuccess(
|
|
1105
|
+
baseFeature({
|
|
1106
|
+
expose: {
|
|
1107
|
+
staging: true,
|
|
1108
|
+
},
|
|
1109
|
+
force: {
|
|
1110
|
+
staging: [{ segments: "*", variation: "control" }],
|
|
1111
|
+
},
|
|
1112
|
+
variations: [
|
|
1113
|
+
{ value: "control", weight: 50 },
|
|
1114
|
+
{ value: "treatment", weight: 50 },
|
|
1115
|
+
],
|
|
1116
|
+
rules: {
|
|
1117
|
+
staging: [{ key: "r1", segments: "*", percentage: 100 }],
|
|
1118
|
+
},
|
|
1119
|
+
}),
|
|
1120
|
+
);
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it("rejects unknown environment keys in rules", () => {
|
|
1124
|
+
const err = expectParseFailure(
|
|
1125
|
+
baseFeature({
|
|
1126
|
+
rules: {
|
|
1127
|
+
qa: [{ key: "r1", segments: "*", percentage: 100 }],
|
|
1128
|
+
},
|
|
1129
|
+
}),
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
expect(err.issues.some((issue) => issue.path.join(".").includes("rules"))).toBe(true);
|
|
1133
|
+
expect(err.issues.some((issue) => issue.code === "invalid_key")).toBe(true);
|
|
1134
|
+
});
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1102
1137
|
describe("oneOf: value must match exactly one branch", () => {
|
|
1103
1138
|
it("rejects defaultValue that matches no oneOf branch", () => {
|
|
1104
1139
|
expectParseFailure(
|