@featurevisor/core 2.10.0 → 2.12.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 +22 -0
- package/coverage/clover.xml +684 -3
- package/coverage/coverage-final.json +4 -0
- package/coverage/lcov-report/builder/allocator.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/index.html +1 -1
- package/coverage/lcov-report/builder/revision.ts.html +1 -1
- package/coverage/lcov-report/builder/traffic.ts.html +1 -1
- package/coverage/lcov-report/index.html +25 -10
- package/coverage/lcov-report/linter/conditionSchema.ts.html +775 -0
- package/coverage/lcov-report/linter/featureSchema.ts.html +4924 -0
- package/coverage/lcov-report/linter/index.html +161 -0
- package/coverage/lcov-report/linter/schema.ts.html +1471 -0
- package/coverage/lcov-report/linter/segmentSchema.ts.html +130 -0
- package/coverage/lcov-report/list/index.html +1 -1
- package/coverage/lcov-report/list/matrix.ts.html +1 -1
- package/coverage/lcov-report/parsers/index.html +1 -1
- package/coverage/lcov-report/parsers/json.ts.html +1 -1
- package/coverage/lcov-report/parsers/yml.ts.html +1 -1
- package/coverage/lcov-report/tester/helpers.ts.html +1 -1
- package/coverage/lcov-report/tester/index.html +1 -1
- package/coverage/lcov.info +1471 -0
- package/lib/builder/buildDatafile.js +15 -1
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/config/projectConfig.d.ts +2 -0
- package/lib/config/projectConfig.js +3 -1
- package/lib/config/projectConfig.js.map +1 -1
- package/lib/datasource/datasource.d.ts +6 -1
- package/lib/datasource/datasource.js +16 -0
- package/lib/datasource/datasource.js.map +1 -1
- package/lib/datasource/filesystemAdapter.js +10 -0
- package/lib/datasource/filesystemAdapter.js.map +1 -1
- package/lib/generate-code/typescript.js +283 -49
- package/lib/generate-code/typescript.js.map +1 -1
- package/lib/linter/conditionSchema.spec.d.ts +1 -0
- package/lib/linter/conditionSchema.spec.js +331 -0
- package/lib/linter/conditionSchema.spec.js.map +1 -0
- package/lib/linter/featureSchema.d.ts +153 -17
- package/lib/linter/featureSchema.js +536 -49
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/featureSchema.spec.d.ts +1 -0
- package/lib/linter/featureSchema.spec.js +978 -0
- package/lib/linter/featureSchema.spec.js.map +1 -0
- package/lib/linter/lintProject.js +67 -1
- package/lib/linter/lintProject.js.map +1 -1
- package/lib/linter/schema.d.ts +42 -0
- package/lib/linter/schema.js +417 -0
- package/lib/linter/schema.js.map +1 -0
- package/lib/linter/schema.spec.d.ts +1 -0
- package/lib/linter/schema.spec.js +483 -0
- package/lib/linter/schema.spec.js.map +1 -0
- package/lib/linter/segmentSchema.spec.d.ts +1 -0
- package/lib/linter/segmentSchema.spec.js +231 -0
- package/lib/linter/segmentSchema.spec.js.map +1 -0
- package/lib/tester/testFeature.js +5 -3
- package/lib/tester/testFeature.js.map +1 -1
- package/lib/utils/git.js +3 -0
- package/lib/utils/git.js.map +1 -1
- package/package.json +5 -5
- package/src/builder/buildDatafile.ts +17 -1
- package/src/config/projectConfig.ts +3 -0
- package/src/datasource/datasource.ts +23 -0
- package/src/datasource/filesystemAdapter.ts +7 -0
- package/src/generate-code/typescript.ts +333 -52
- package/src/linter/conditionSchema.spec.ts +446 -0
- package/src/linter/featureSchema.spec.ts +1218 -0
- package/src/linter/featureSchema.ts +747 -70
- package/src/linter/lintProject.ts +84 -0
- package/src/linter/schema.spec.ts +617 -0
- package/src/linter/schema.ts +462 -0
- package/src/linter/segmentSchema.spec.ts +273 -0
- package/src/tester/testFeature.ts +5 -3
- package/src/utils/git.ts +2 -0
- package/lib/linter/propertySchema.d.ts +0 -5
- package/lib/linter/propertySchema.js +0 -43
- package/lib/linter/propertySchema.js.map +0 -1
- package/src/linter/propertySchema.ts +0 -47
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getFeatureZodSchema = getFeatureZodSchema;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const schema_1 = require("./schema");
|
|
6
6
|
const tagRegex = /^[a-z0-9-]+$/;
|
|
7
7
|
function isArrayOfStrings(value) {
|
|
8
8
|
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
@@ -18,6 +18,201 @@ function getVariableLabel(variableSchema, variableKey, path) {
|
|
|
18
18
|
variableSchema?.key ??
|
|
19
19
|
(path.length > 0 ? String(path[path.length - 1]) : "variable"));
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve variable schema to the Schema used for value validation.
|
|
23
|
+
* When variable has `schema` (reference), returns the parsed Schema from schemasByKey; otherwise returns the inline variable schema.
|
|
24
|
+
*/
|
|
25
|
+
function resolveVariableSchema(variableSchema, schemasByKey) {
|
|
26
|
+
if (variableSchema.schema) {
|
|
27
|
+
return schemasByKey?.[variableSchema.schema] ?? null;
|
|
28
|
+
}
|
|
29
|
+
return variableSchema;
|
|
30
|
+
}
|
|
31
|
+
/** Resolve a schema by following schema references (schema: key). Used for nested schemas that may have oneOf. */
|
|
32
|
+
function resolveSchemaRefs(schema, schemasByKey) {
|
|
33
|
+
if (schema.schema && schemasByKey?.[schema.schema]) {
|
|
34
|
+
return resolveSchemaRefs(schemasByKey[schema.schema], schemasByKey);
|
|
35
|
+
}
|
|
36
|
+
return schema;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns true if the value matches the given schema (const, enum, type, object properties, array items, or exactly one of oneOf).
|
|
40
|
+
* Used for oneOf validation: value must match exactly one branch.
|
|
41
|
+
*/
|
|
42
|
+
function valueMatchesSchema(schema, value, schemasByKey) {
|
|
43
|
+
const resolved = resolveSchemaRefs(schema, schemasByKey);
|
|
44
|
+
if (resolved.oneOf && Array.isArray(resolved.oneOf) && resolved.oneOf.length > 0) {
|
|
45
|
+
const matchCount = resolved.oneOf.filter((branch) => valueMatchesSchema(branch, value, schemasByKey)).length;
|
|
46
|
+
return matchCount === 1;
|
|
47
|
+
}
|
|
48
|
+
if (resolved.const !== undefined) {
|
|
49
|
+
return valueDeepEqual(value, resolved.const);
|
|
50
|
+
}
|
|
51
|
+
if (resolved.enum !== undefined && Array.isArray(resolved.enum)) {
|
|
52
|
+
return resolved.enum.some((e) => valueDeepEqual(value, e));
|
|
53
|
+
}
|
|
54
|
+
const type = resolved.type;
|
|
55
|
+
if (!type)
|
|
56
|
+
return false;
|
|
57
|
+
if (type === "string") {
|
|
58
|
+
if (typeof value !== "string")
|
|
59
|
+
return false;
|
|
60
|
+
const s = value;
|
|
61
|
+
if (resolved.minLength !== undefined && s.length < resolved.minLength)
|
|
62
|
+
return false;
|
|
63
|
+
if (resolved.maxLength !== undefined && s.length > resolved.maxLength)
|
|
64
|
+
return false;
|
|
65
|
+
if (resolved.pattern !== undefined) {
|
|
66
|
+
try {
|
|
67
|
+
if (!new RegExp(resolved.pattern).test(s))
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (type === "boolean")
|
|
77
|
+
return typeof value === "boolean";
|
|
78
|
+
if (type === "integer") {
|
|
79
|
+
if (typeof value !== "number" || !Number.isInteger(value))
|
|
80
|
+
return false;
|
|
81
|
+
if (resolved.minimum !== undefined && value < resolved.minimum)
|
|
82
|
+
return false;
|
|
83
|
+
if (resolved.maximum !== undefined && value > resolved.maximum)
|
|
84
|
+
return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
if (type === "double") {
|
|
88
|
+
if (typeof value !== "number")
|
|
89
|
+
return false;
|
|
90
|
+
if (resolved.minimum !== undefined && value < resolved.minimum)
|
|
91
|
+
return false;
|
|
92
|
+
if (resolved.maximum !== undefined && value > resolved.maximum)
|
|
93
|
+
return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (type === "json")
|
|
97
|
+
return typeof value === "string";
|
|
98
|
+
if (type === "object") {
|
|
99
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
100
|
+
return false;
|
|
101
|
+
const props = resolved.properties;
|
|
102
|
+
if (!props || typeof props !== "object")
|
|
103
|
+
return true;
|
|
104
|
+
const obj = value;
|
|
105
|
+
const required = new Set(resolved.required || []);
|
|
106
|
+
for (const key of required) {
|
|
107
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key))
|
|
108
|
+
return false;
|
|
109
|
+
if (!valueMatchesSchema(props[key], obj[key], schemasByKey))
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
for (const key of Object.keys(obj)) {
|
|
113
|
+
const propSchema = props[key];
|
|
114
|
+
if (!propSchema)
|
|
115
|
+
return false;
|
|
116
|
+
if (!valueMatchesSchema(propSchema, obj[key], schemasByKey))
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (type === "array") {
|
|
122
|
+
if (!Array.isArray(value))
|
|
123
|
+
return false;
|
|
124
|
+
const arr = value;
|
|
125
|
+
if (resolved.minItems !== undefined && arr.length < resolved.minItems)
|
|
126
|
+
return false;
|
|
127
|
+
if (resolved.maxItems !== undefined && arr.length > resolved.maxItems)
|
|
128
|
+
return false;
|
|
129
|
+
if (resolved.uniqueItems) {
|
|
130
|
+
for (let i = 0; i < arr.length; i++) {
|
|
131
|
+
for (let j = i + 1; j < arr.length; j++) {
|
|
132
|
+
if (valueDeepEqual(arr[i], arr[j]))
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const itemSchema = resolved.items;
|
|
138
|
+
if (!itemSchema || typeof itemSchema !== "object")
|
|
139
|
+
return arr.every((v) => typeof v === "string");
|
|
140
|
+
return arr.every((item) => valueMatchesSchema(itemSchema, item, schemasByKey));
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
/** Deep equality for variable values (primitives, plain objects, arrays). */
|
|
145
|
+
function valueDeepEqual(a, b) {
|
|
146
|
+
if (a === b)
|
|
147
|
+
return true;
|
|
148
|
+
if (typeof a !== typeof b)
|
|
149
|
+
return false;
|
|
150
|
+
if (a === null || b === null)
|
|
151
|
+
return a === b;
|
|
152
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
153
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
154
|
+
return false;
|
|
155
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
156
|
+
if (a.length !== b.length)
|
|
157
|
+
return false;
|
|
158
|
+
return a.every((v, i) => valueDeepEqual(v, b[i]));
|
|
159
|
+
}
|
|
160
|
+
const keysA = Object.keys(a).sort();
|
|
161
|
+
const keysB = Object.keys(b).sort();
|
|
162
|
+
if (keysA.length !== keysB.length || keysA.some((k, i) => k !== keysB[i]))
|
|
163
|
+
return false;
|
|
164
|
+
return keysA.every((k) => valueDeepEqual(a[k], b[k]));
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Recursively validates that every `required` array (at this level and in nested
|
|
170
|
+
* object/array schemas) only contains keys that exist in the same level's `properties`.
|
|
171
|
+
* Adds Zod issues with the correct path for invalid required field names.
|
|
172
|
+
*/
|
|
173
|
+
function refineRequiredKeysInSchema(schema, pathPrefix, ctx) {
|
|
174
|
+
if (!schema || typeof schema !== "object")
|
|
175
|
+
return;
|
|
176
|
+
const effectiveType = schema.type;
|
|
177
|
+
const properties = schema.properties;
|
|
178
|
+
const required = schema.required;
|
|
179
|
+
const items = schema.items;
|
|
180
|
+
if (effectiveType === "object" &&
|
|
181
|
+
Array.isArray(required) &&
|
|
182
|
+
required.length > 0 &&
|
|
183
|
+
properties &&
|
|
184
|
+
typeof properties === "object") {
|
|
185
|
+
const allowedKeys = Object.keys(properties);
|
|
186
|
+
required.forEach((key, index) => {
|
|
187
|
+
if (!allowedKeys.includes(key)) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
190
|
+
message: `Unknown required field "${key}". \`required\` must only contain property names defined in \`properties\`. Allowed: ${allowedKeys.length ? allowedKeys.join(", ") : "(none)"}.`,
|
|
191
|
+
path: [...pathPrefix, "required", index],
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (properties && typeof properties === "object") {
|
|
197
|
+
for (const key of Object.keys(properties)) {
|
|
198
|
+
const nested = properties[key];
|
|
199
|
+
if (nested && typeof nested === "object") {
|
|
200
|
+
refineRequiredKeysInSchema(nested, [...pathPrefix, "properties", key], ctx);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (items && typeof items === "object" && !Array.isArray(items)) {
|
|
205
|
+
refineRequiredKeysInSchema(items, [...pathPrefix, "items"], ctx);
|
|
206
|
+
}
|
|
207
|
+
const oneOf = schema.oneOf;
|
|
208
|
+
if (oneOf && Array.isArray(oneOf)) {
|
|
209
|
+
oneOf.forEach((branch, i) => {
|
|
210
|
+
if (branch && typeof branch === "object") {
|
|
211
|
+
refineRequiredKeysInSchema(branch, [...pathPrefix, "oneOf", i], ctx);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
21
216
|
function typeOfValue(value) {
|
|
22
217
|
if (value === null)
|
|
23
218
|
return "null";
|
|
@@ -30,13 +225,45 @@ function typeOfValue(value) {
|
|
|
30
225
|
/**
|
|
31
226
|
* Validates a variable value against an array schema. Recursively validates each item
|
|
32
227
|
* when the schema defines `items` (nested arrays/objects use the same refinement).
|
|
228
|
+
* Enforces minItems, maxItems, and uniqueItems when set.
|
|
33
229
|
*/
|
|
34
|
-
function refineVariableValueArray(projectConfig, variableSchema, variableValue, path, ctx, variableKey) {
|
|
230
|
+
function refineVariableValueArray(projectConfig, variableSchema, variableValue, path, ctx, variableKey, schemasByKey) {
|
|
35
231
|
const label = getVariableLabel(variableSchema, variableKey, path);
|
|
232
|
+
const minItems = variableSchema.minItems;
|
|
233
|
+
const maxItems = variableSchema.maxItems;
|
|
234
|
+
const uniqueItems = variableSchema.uniqueItems;
|
|
235
|
+
if (minItems !== undefined && variableValue.length < minItems) {
|
|
236
|
+
ctx.addIssue({
|
|
237
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
238
|
+
message: `Variable "${label}" (type array) length (${variableValue.length}) is less than \`minItems\` (${minItems}).`,
|
|
239
|
+
path,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (maxItems !== undefined && variableValue.length > maxItems) {
|
|
243
|
+
ctx.addIssue({
|
|
244
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
245
|
+
message: `Variable "${label}" (type array) length (${variableValue.length}) is greater than \`maxItems\` (${maxItems}).`,
|
|
246
|
+
path,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (uniqueItems) {
|
|
250
|
+
for (let i = 0; i < variableValue.length; i++) {
|
|
251
|
+
for (let j = i + 1; j < variableValue.length; j++) {
|
|
252
|
+
if (valueDeepEqual(variableValue[i], variableValue[j])) {
|
|
253
|
+
ctx.addIssue({
|
|
254
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
255
|
+
message: `Variable "${label}" (type array) has duplicate items at indices ${i} and ${j} but \`uniqueItems\` is true.`,
|
|
256
|
+
path,
|
|
257
|
+
});
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
36
263
|
const itemSchema = variableSchema.items;
|
|
37
264
|
if (itemSchema) {
|
|
38
265
|
variableValue.forEach((item, index) => {
|
|
39
|
-
superRefineVariableValue(projectConfig, itemSchema, item, [...path, index], ctx, variableKey);
|
|
266
|
+
superRefineVariableValue(projectConfig, itemSchema, item, [...path, index], ctx, variableKey, schemasByKey);
|
|
40
267
|
});
|
|
41
268
|
}
|
|
42
269
|
else {
|
|
@@ -63,12 +290,12 @@ function refineVariableValueArray(projectConfig, variableSchema, variableValue,
|
|
|
63
290
|
* Validates a variable value against an object schema. Recursively validates each property
|
|
64
291
|
* when the schema defines `properties` (nested objects/arrays use the same refinement).
|
|
65
292
|
*/
|
|
66
|
-
function refineVariableValueObject(projectConfig, variableSchema, variableValue, path, ctx, variableKey) {
|
|
293
|
+
function refineVariableValueObject(projectConfig, variableSchema, variableValue, path, ctx, variableKey, schemasByKey) {
|
|
67
294
|
const label = getVariableLabel(variableSchema, variableKey, path);
|
|
68
295
|
const schemaProperties = variableSchema.properties;
|
|
69
296
|
if (schemaProperties && typeof schemaProperties === "object") {
|
|
70
297
|
const requiredKeys = variableSchema.required && variableSchema.required.length > 0
|
|
71
|
-
? variableSchema.required
|
|
298
|
+
? variableSchema.required.filter((k) => Object.prototype.hasOwnProperty.call(schemaProperties, k))
|
|
72
299
|
: Object.keys(schemaProperties);
|
|
73
300
|
for (const key of requiredKeys) {
|
|
74
301
|
if (!Object.prototype.hasOwnProperty.call(variableValue, key)) {
|
|
@@ -89,7 +316,7 @@ function refineVariableValueObject(projectConfig, variableSchema, variableValue,
|
|
|
89
316
|
});
|
|
90
317
|
}
|
|
91
318
|
else {
|
|
92
|
-
superRefineVariableValue(projectConfig, propSchema, variableValue[key], [...path, key], ctx, key);
|
|
319
|
+
superRefineVariableValue(projectConfig, propSchema, variableValue[key], [...path, key], ctx, key, schemasByKey);
|
|
93
320
|
}
|
|
94
321
|
}
|
|
95
322
|
}
|
|
@@ -116,7 +343,7 @@ function refineVariableValueObject(projectConfig, variableSchema, variableValue,
|
|
|
116
343
|
}
|
|
117
344
|
}
|
|
118
345
|
}
|
|
119
|
-
function superRefineVariableValue(projectConfig, variableSchema, variableValue, path, ctx, variableKey) {
|
|
346
|
+
function superRefineVariableValue(projectConfig, variableSchema, variableValue, path, ctx, variableKey, schemasByKey) {
|
|
120
347
|
const label = getVariableLabel(variableSchema, variableKey, path);
|
|
121
348
|
if (!variableSchema) {
|
|
122
349
|
const variableName = path.length > 0 && typeof path[path.length - 1] === "string"
|
|
@@ -130,6 +357,60 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
130
357
|
});
|
|
131
358
|
return;
|
|
132
359
|
}
|
|
360
|
+
const effectiveSchema = resolveVariableSchema(variableSchema, schemasByKey);
|
|
361
|
+
if (variableSchema.schema && effectiveSchema === null) {
|
|
362
|
+
ctx.addIssue({
|
|
363
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
364
|
+
message: `Schema "${variableSchema.schema}" could not be loaded for value validation.`,
|
|
365
|
+
path,
|
|
366
|
+
});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (!effectiveSchema) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const effectiveOneOf = effectiveSchema.oneOf;
|
|
373
|
+
if (effectiveOneOf !== undefined && Array.isArray(effectiveOneOf) && effectiveOneOf.length > 0) {
|
|
374
|
+
const matchCount = effectiveOneOf.filter((branch) => valueMatchesSchema(branch, variableValue, schemasByKey)).length;
|
|
375
|
+
if (matchCount === 0) {
|
|
376
|
+
ctx.addIssue({
|
|
377
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
378
|
+
message: `Variable "${label}" must match exactly one of the \`oneOf\` schemas (got ${JSON.stringify(variableValue)}; matched none).`,
|
|
379
|
+
path,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
else if (matchCount > 1) {
|
|
383
|
+
ctx.addIssue({
|
|
384
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
385
|
+
message: `Variable "${label}" must match exactly one of the \`oneOf\` schemas (matched ${matchCount}).`,
|
|
386
|
+
path,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const effectiveConst = effectiveSchema.const;
|
|
392
|
+
if (effectiveConst !== undefined) {
|
|
393
|
+
if (!valueDeepEqual(variableValue, effectiveConst)) {
|
|
394
|
+
ctx.addIssue({
|
|
395
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
396
|
+
message: `Variable "${label}" must equal the constant value defined in schema (got ${JSON.stringify(variableValue)}).`,
|
|
397
|
+
path,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const effectiveEnum = effectiveSchema.enum;
|
|
403
|
+
if (effectiveEnum !== undefined && Array.isArray(effectiveEnum) && effectiveEnum.length > 0) {
|
|
404
|
+
const allowed = effectiveEnum.some((v) => valueDeepEqual(variableValue, v));
|
|
405
|
+
if (!allowed) {
|
|
406
|
+
ctx.addIssue({
|
|
407
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
408
|
+
message: `Variable "${label}" must be one of the allowed enum values (got ${JSON.stringify(variableValue)}).`,
|
|
409
|
+
path,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
133
414
|
// Require a value (no undefined) for every variable usage
|
|
134
415
|
if (variableValue === undefined) {
|
|
135
416
|
ctx.addIssue({
|
|
@@ -139,9 +420,9 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
139
420
|
});
|
|
140
421
|
return;
|
|
141
422
|
}
|
|
142
|
-
const expectedType =
|
|
423
|
+
const expectedType = effectiveSchema.type;
|
|
143
424
|
const gotType = typeOfValue(variableValue);
|
|
144
|
-
// string — only string allowed
|
|
425
|
+
// string — only string allowed; schema minLength/maxLength/pattern applied when set
|
|
145
426
|
if (expectedType === "string") {
|
|
146
427
|
if (typeof variableValue !== "string") {
|
|
147
428
|
ctx.addIssue({
|
|
@@ -151,6 +432,37 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
151
432
|
});
|
|
152
433
|
return;
|
|
153
434
|
}
|
|
435
|
+
const strMinLen = effectiveSchema.minLength;
|
|
436
|
+
const strMaxLen = effectiveSchema.maxLength;
|
|
437
|
+
const strPattern = effectiveSchema.pattern;
|
|
438
|
+
if (strMinLen !== undefined && variableValue.length < strMinLen) {
|
|
439
|
+
ctx.addIssue({
|
|
440
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
441
|
+
message: `Variable "${label}" (type string) length (${variableValue.length}) is less than \`minLength\` (${strMinLen}).`,
|
|
442
|
+
path,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (strMaxLen !== undefined && variableValue.length > strMaxLen) {
|
|
446
|
+
ctx.addIssue({
|
|
447
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
448
|
+
message: `Variable "${label}" (type string) length (${variableValue.length}) is greater than \`maxLength\` (${strMaxLen}).`,
|
|
449
|
+
path,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
if (strPattern !== undefined) {
|
|
453
|
+
try {
|
|
454
|
+
if (!new RegExp(strPattern).test(variableValue)) {
|
|
455
|
+
ctx.addIssue({
|
|
456
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
457
|
+
message: `Variable "${label}" (type string) does not match \`pattern\`.`,
|
|
458
|
+
path,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
// invalid regex already reported at schema parse time
|
|
464
|
+
}
|
|
465
|
+
}
|
|
154
466
|
if (projectConfig.maxVariableStringLength &&
|
|
155
467
|
variableValue.length > projectConfig.maxVariableStringLength) {
|
|
156
468
|
ctx.addIssue({
|
|
@@ -185,6 +497,23 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
185
497
|
message: `Variable "${label}" (type integer) must be an integer; got ${variableValue}.`,
|
|
186
498
|
path,
|
|
187
499
|
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const intMin = effectiveSchema.minimum;
|
|
503
|
+
const intMax = effectiveSchema.maximum;
|
|
504
|
+
if (intMin !== undefined && variableValue < intMin) {
|
|
505
|
+
ctx.addIssue({
|
|
506
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
507
|
+
message: `Variable "${label}" (type integer) must be >= minimum (${intMin}); got ${variableValue}.`,
|
|
508
|
+
path,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
if (intMax !== undefined && variableValue > intMax) {
|
|
512
|
+
ctx.addIssue({
|
|
513
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
514
|
+
message: `Variable "${label}" (type integer) must be <= maximum (${intMax}); got ${variableValue}.`,
|
|
515
|
+
path,
|
|
516
|
+
});
|
|
188
517
|
}
|
|
189
518
|
return;
|
|
190
519
|
}
|
|
@@ -204,6 +533,23 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
204
533
|
message: `Variable "${label}" (type double) must be a finite number; got ${variableValue}.`,
|
|
205
534
|
path,
|
|
206
535
|
});
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const doubleMin = effectiveSchema.minimum;
|
|
539
|
+
const doubleMax = effectiveSchema.maximum;
|
|
540
|
+
if (doubleMin !== undefined && variableValue < doubleMin) {
|
|
541
|
+
ctx.addIssue({
|
|
542
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
543
|
+
message: `Variable "${label}" (type double) must be >= minimum (${doubleMin}); got ${variableValue}.`,
|
|
544
|
+
path,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
if (doubleMax !== undefined && variableValue > doubleMax) {
|
|
548
|
+
ctx.addIssue({
|
|
549
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
550
|
+
message: `Variable "${label}" (type double) must be <= maximum (${doubleMax}); got ${variableValue}.`,
|
|
551
|
+
path,
|
|
552
|
+
});
|
|
207
553
|
}
|
|
208
554
|
return;
|
|
209
555
|
}
|
|
@@ -228,7 +574,7 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
228
574
|
});
|
|
229
575
|
return;
|
|
230
576
|
}
|
|
231
|
-
refineVariableValueArray(projectConfig,
|
|
577
|
+
refineVariableValueArray(projectConfig, effectiveSchema, variableValue, path, ctx, variableKey, schemasByKey);
|
|
232
578
|
return;
|
|
233
579
|
}
|
|
234
580
|
// object — only plain object allowed (no null, no array)
|
|
@@ -243,7 +589,7 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
243
589
|
});
|
|
244
590
|
return;
|
|
245
591
|
}
|
|
246
|
-
refineVariableValueObject(projectConfig,
|
|
592
|
+
refineVariableValueObject(projectConfig, effectiveSchema, variableValue, path, ctx, variableKey, schemasByKey);
|
|
247
593
|
return;
|
|
248
594
|
}
|
|
249
595
|
// json — only string containing valid JSON allowed
|
|
@@ -286,7 +632,7 @@ function superRefineVariableValue(projectConfig, variableSchema, variableValue,
|
|
|
286
632
|
});
|
|
287
633
|
}
|
|
288
634
|
function refineForce({ ctx, parsedFeature, // eslint-disable-line
|
|
289
|
-
variableSchemaByKey, variationValues, force, pathPrefix, projectConfig, }) {
|
|
635
|
+
variableSchemaByKey, variationValues, force, pathPrefix, projectConfig, schemasByKey, }) {
|
|
290
636
|
force.forEach((f, fN) => {
|
|
291
637
|
// force[n].variation
|
|
292
638
|
if (f.variation) {
|
|
@@ -301,17 +647,37 @@ variableSchemaByKey, variationValues, force, pathPrefix, projectConfig, }) {
|
|
|
301
647
|
// force[n].variables[key]
|
|
302
648
|
if (f.variables) {
|
|
303
649
|
Object.keys(f.variables).forEach((variableKey) => {
|
|
304
|
-
|
|
650
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
651
|
+
if (!variableSchema) {
|
|
652
|
+
ctx.addIssue({
|
|
653
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
654
|
+
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
655
|
+
path: pathPrefix.concat([fN, "variables", variableKey]),
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
superRefineVariableValue(projectConfig, variableSchema, f.variables[variableKey], pathPrefix.concat([fN, "variables", variableKey]), ctx, variableKey, schemasByKey);
|
|
660
|
+
}
|
|
305
661
|
});
|
|
306
662
|
}
|
|
307
663
|
});
|
|
308
664
|
}
|
|
309
|
-
function refineRules({ ctx, parsedFeature, variableSchemaByKey, variationValues, rules, pathPrefix, projectConfig, }) {
|
|
665
|
+
function refineRules({ ctx, parsedFeature, variableSchemaByKey, variationValues, rules, pathPrefix, projectConfig, schemasByKey, }) {
|
|
310
666
|
rules.forEach((rule, ruleN) => {
|
|
311
667
|
// rules[n].variables[key]
|
|
312
668
|
if (rule.variables) {
|
|
313
669
|
Object.keys(rule.variables).forEach((variableKey) => {
|
|
314
|
-
|
|
670
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
671
|
+
if (!variableSchema) {
|
|
672
|
+
ctx.addIssue({
|
|
673
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
674
|
+
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
675
|
+
path: pathPrefix.concat([ruleN, "variables", variableKey]),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
superRefineVariableValue(projectConfig, variableSchema, rule.variables[variableKey], pathPrefix.concat([ruleN, "variables", variableKey]), ctx, variableKey, schemasByKey);
|
|
680
|
+
}
|
|
315
681
|
});
|
|
316
682
|
}
|
|
317
683
|
// rules[n].variationWeights
|
|
@@ -366,9 +732,9 @@ function refineRules({ ctx, parsedFeature, variableSchemaByKey, variationValues,
|
|
|
366
732
|
}
|
|
367
733
|
});
|
|
368
734
|
}
|
|
369
|
-
function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttributeKeys, availableSegmentKeys, availableFeatureKeys) {
|
|
370
|
-
const
|
|
371
|
-
const variableValueZodSchema =
|
|
735
|
+
function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttributeKeys, availableSegmentKeys, availableFeatureKeys, availableSchemaKeys = [], schemasByKey = {}) {
|
|
736
|
+
const schemaZodSchema = (0, schema_1.getSchemaZodSchema)(availableSchemaKeys);
|
|
737
|
+
const variableValueZodSchema = schema_1.valueZodSchema;
|
|
372
738
|
const variationValueZodSchema = zod_1.z.string().min(1);
|
|
373
739
|
const plainGroupSegment = zod_1.z.string().refine((value) => value === "*" || availableSegmentKeys.includes(value), (value) => ({
|
|
374
740
|
message: `Unknown segment key "${value}"`,
|
|
@@ -494,17 +860,98 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
494
860
|
.record(zod_1.z
|
|
495
861
|
.object({
|
|
496
862
|
deprecated: zod_1.z.boolean().optional(),
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
863
|
+
// Reference to a reusable schema (mutually exclusive with type/properties/required/items)
|
|
864
|
+
schema: zod_1.z
|
|
865
|
+
.string()
|
|
866
|
+
.refine((value) => availableSchemaKeys.includes(value), (value) => ({ message: `Unknown schema "${value}"` }))
|
|
867
|
+
.optional(),
|
|
868
|
+
// Inline schema (mutually exclusive with schema)
|
|
869
|
+
type: zod_1.z.union([zod_1.z.literal("json"), schema_1.propertyTypeEnum]).optional(),
|
|
870
|
+
items: schemaZodSchema.optional(),
|
|
871
|
+
properties: zod_1.z.record(schemaZodSchema).optional(),
|
|
872
|
+
required: zod_1.z.array(zod_1.z.string()).optional(),
|
|
873
|
+
enum: zod_1.z.array(variableValueZodSchema).optional(),
|
|
874
|
+
const: variableValueZodSchema.optional(),
|
|
875
|
+
oneOf: zod_1.z.array(schemaZodSchema).min(1).optional(),
|
|
876
|
+
minimum: zod_1.z.number().optional(),
|
|
877
|
+
maximum: zod_1.z.number().optional(),
|
|
878
|
+
minLength: zod_1.z.number().optional(),
|
|
879
|
+
maxLength: zod_1.z.number().optional(),
|
|
880
|
+
pattern: zod_1.z.string().optional(),
|
|
881
|
+
minItems: zod_1.z.number().optional(),
|
|
882
|
+
maxItems: zod_1.z.number().optional(),
|
|
883
|
+
uniqueItems: zod_1.z.boolean().optional(),
|
|
502
884
|
description: zod_1.z.string().optional(),
|
|
503
885
|
defaultValue: variableValueZodSchema,
|
|
504
886
|
disabledValue: variableValueZodSchema.optional(),
|
|
505
887
|
useDefaultWhenDisabled: zod_1.z.boolean().optional(),
|
|
506
888
|
})
|
|
507
|
-
.strict()
|
|
889
|
+
.strict()
|
|
890
|
+
.superRefine((variableSchema, ctx) => {
|
|
891
|
+
const hasRef = "schema" in variableSchema && variableSchema.schema != null;
|
|
892
|
+
const hasInline = "type" in variableSchema &&
|
|
893
|
+
variableSchema.type != null &&
|
|
894
|
+
variableSchema.type !== undefined;
|
|
895
|
+
const hasOneOf = "oneOf" in variableSchema &&
|
|
896
|
+
Array.isArray(variableSchema.oneOf) &&
|
|
897
|
+
variableSchema.oneOf.length > 0;
|
|
898
|
+
if (hasRef && (hasInline || hasOneOf)) {
|
|
899
|
+
ctx.addIssue({
|
|
900
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
901
|
+
message: "Variable schema cannot have both `schema` (reference) and inline properties (`type`, `oneOf`, `properties`, `required`, `items`). Use one or the other.",
|
|
902
|
+
path: [],
|
|
903
|
+
});
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (hasRef) {
|
|
907
|
+
const hasInlineStructure = ("type" in variableSchema && variableSchema.type != null) ||
|
|
908
|
+
("properties" in variableSchema && variableSchema.properties != null) ||
|
|
909
|
+
("required" in variableSchema && variableSchema.required != null) ||
|
|
910
|
+
("items" in variableSchema && variableSchema.items != null) ||
|
|
911
|
+
("oneOf" in variableSchema && variableSchema.oneOf != null);
|
|
912
|
+
const hasInlineValidation = ("minimum" in variableSchema && variableSchema.minimum !== undefined) ||
|
|
913
|
+
("maximum" in variableSchema && variableSchema.maximum !== undefined) ||
|
|
914
|
+
("minLength" in variableSchema && variableSchema.minLength !== undefined) ||
|
|
915
|
+
("maxLength" in variableSchema && variableSchema.maxLength !== undefined) ||
|
|
916
|
+
("pattern" in variableSchema && variableSchema.pattern !== undefined) ||
|
|
917
|
+
("minItems" in variableSchema && variableSchema.minItems !== undefined) ||
|
|
918
|
+
("maxItems" in variableSchema && variableSchema.maxItems !== undefined) ||
|
|
919
|
+
("uniqueItems" in variableSchema && variableSchema.uniqueItems !== undefined);
|
|
920
|
+
if (hasInlineStructure) {
|
|
921
|
+
ctx.addIssue({
|
|
922
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
923
|
+
message: "When `schema` is set, do not set `type`, `oneOf`, `properties`, `required`, or `items`.",
|
|
924
|
+
path: [],
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
if (hasInlineValidation) {
|
|
928
|
+
ctx.addIssue({
|
|
929
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
930
|
+
message: "When `schema` is set, do not set `minimum`, `maximum`, `minLength`, `maxLength`, `pattern`, `minItems`, `maxItems`, or `uniqueItems`; use the referenced schema to define these.",
|
|
931
|
+
path: [],
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
if (!hasInline && !hasOneOf) {
|
|
937
|
+
ctx.addIssue({
|
|
938
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
939
|
+
message: "Variable schema must have either `schema` (reference to a schema key), `type` (inline schema), or `oneOf` (inline oneOf schemas).",
|
|
940
|
+
path: [],
|
|
941
|
+
});
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (hasInline && hasOneOf) {
|
|
945
|
+
ctx.addIssue({
|
|
946
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
947
|
+
message: "Variable schema cannot have both `type` and `oneOf` at the top level. Use one or the other.",
|
|
948
|
+
path: [],
|
|
949
|
+
});
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
// Validate required ⊆ properties at this level and in all nested object schemas
|
|
953
|
+
refineRequiredKeysInSchema(variableSchema, [], ctx);
|
|
954
|
+
}))
|
|
508
955
|
.optional(),
|
|
509
956
|
disabledVariationValue: variationValueZodSchema.optional(),
|
|
510
957
|
variations: zod_1.z
|
|
@@ -574,6 +1021,10 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
574
1021
|
if (!value.variablesSchema) {
|
|
575
1022
|
return;
|
|
576
1023
|
}
|
|
1024
|
+
// Every variable value is validated against its schema from variablesSchema. Sources covered:
|
|
1025
|
+
// 1. variablesSchema[key].defaultValue 2. variablesSchema[key].disabledValue
|
|
1026
|
+
// 3. variations[n].variables[key] 4. variations[n].variableOverrides[key][].value
|
|
1027
|
+
// 5. rules[env][n].variables[key] 6. force[env][n].variables[key]
|
|
577
1028
|
const variableSchemaByKey = value.variablesSchema;
|
|
578
1029
|
const variationValues = [];
|
|
579
1030
|
if (value.variations) {
|
|
@@ -585,6 +1036,21 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
585
1036
|
const variableKeys = Object.keys(variableSchemaByKey);
|
|
586
1037
|
variableKeys.forEach((variableKey) => {
|
|
587
1038
|
const variableSchema = variableSchemaByKey[variableKey];
|
|
1039
|
+
// When type and enum are both present, all enum values must match the type
|
|
1040
|
+
const effectiveSchema = resolveVariableSchema(variableSchema, schemasByKey);
|
|
1041
|
+
if (effectiveSchema &&
|
|
1042
|
+
effectiveSchema.type &&
|
|
1043
|
+
Array.isArray(effectiveSchema.enum) &&
|
|
1044
|
+
effectiveSchema.enum.length > 0) {
|
|
1045
|
+
(0, schema_1.refineEnumMatchesType)(effectiveSchema, ["variablesSchema", variableKey], ctx);
|
|
1046
|
+
}
|
|
1047
|
+
// Inline variable schemas: validate minimum/maximum, minLength/maxLength/pattern, minItems/maxItems/uniqueItems
|
|
1048
|
+
if (!("schema" in variableSchema) || !variableSchema.schema) {
|
|
1049
|
+
const pathPrefix = ["variablesSchema", variableKey];
|
|
1050
|
+
(0, schema_1.refineMinimumMaximum)(variableSchema, pathPrefix, ctx);
|
|
1051
|
+
(0, schema_1.refineStringLengthPattern)(variableSchema, pathPrefix, ctx);
|
|
1052
|
+
(0, schema_1.refineArrayItems)(variableSchema, pathPrefix, ctx);
|
|
1053
|
+
}
|
|
588
1054
|
if (variableKey === "variation") {
|
|
589
1055
|
ctx.addIssue({
|
|
590
1056
|
code: zod_1.z.ZodIssueCode.custom,
|
|
@@ -593,38 +1059,55 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
593
1059
|
});
|
|
594
1060
|
}
|
|
595
1061
|
// defaultValue
|
|
596
|
-
superRefineVariableValue(projectConfig, variableSchema, variableSchema.defaultValue, ["variablesSchema", variableKey, "defaultValue"], ctx, variableKey);
|
|
1062
|
+
superRefineVariableValue(projectConfig, variableSchema, variableSchema.defaultValue, ["variablesSchema", variableKey, "defaultValue"], ctx, variableKey, schemasByKey);
|
|
597
1063
|
// disabledValue (only when present)
|
|
598
1064
|
if (variableSchema.disabledValue !== undefined) {
|
|
599
|
-
superRefineVariableValue(projectConfig, variableSchema, variableSchema.disabledValue, ["variablesSchema", variableKey, "disabledValue"], ctx, variableKey);
|
|
1065
|
+
superRefineVariableValue(projectConfig, variableSchema, variableSchema.disabledValue, ["variablesSchema", variableKey, "disabledValue"], ctx, variableKey, schemasByKey);
|
|
600
1066
|
}
|
|
601
1067
|
});
|
|
602
|
-
// variations
|
|
1068
|
+
// variations: validate variation.variables and variation.variableOverrides (each value against its variable schema)
|
|
603
1069
|
if (value.variations) {
|
|
604
1070
|
value.variations.forEach((variation, variationN) => {
|
|
605
|
-
if (!variation.variables) {
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
1071
|
// variations[n].variables[key]
|
|
609
|
-
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1072
|
+
if (variation.variables) {
|
|
1073
|
+
for (const variableKey of Object.keys(variation.variables)) {
|
|
1074
|
+
const variableValue = variation.variables[variableKey];
|
|
1075
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
1076
|
+
if (!variableSchema) {
|
|
1077
|
+
ctx.addIssue({
|
|
1078
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
1079
|
+
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
1080
|
+
path: ["variations", variationN, "variables", variableKey],
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
superRefineVariableValue(projectConfig, variableSchema, variableValue, ["variations", variationN, "variables", variableKey], ctx, variableKey, schemasByKey);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
// variations[n].variableOverrides[key][].value (validated even when variation.variables is absent)
|
|
1089
|
+
if (variation.variableOverrides) {
|
|
1090
|
+
for (const variableKey of Object.keys(variation.variableOverrides)) {
|
|
1091
|
+
const overrides = variation.variableOverrides[variableKey];
|
|
1092
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
1093
|
+
if (!variableSchema) {
|
|
1094
|
+
ctx.addIssue({
|
|
1095
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
1096
|
+
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
1097
|
+
path: ["variations", variationN, "variableOverrides", variableKey],
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
else if (Array.isArray(overrides)) {
|
|
1101
|
+
overrides.forEach((override, overrideN) => {
|
|
1102
|
+
superRefineVariableValue(projectConfig, variableSchema, override.value, [
|
|
1103
|
+
"variations",
|
|
1104
|
+
variationN,
|
|
1105
|
+
"variableOverrides",
|
|
1106
|
+
variableKey,
|
|
1107
|
+
overrideN,
|
|
1108
|
+
"value",
|
|
1109
|
+
], ctx, variableKey, schemasByKey);
|
|
1110
|
+
});
|
|
628
1111
|
}
|
|
629
1112
|
}
|
|
630
1113
|
}
|
|
@@ -643,6 +1126,7 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
643
1126
|
pathPrefix: ["rules", environmentKey],
|
|
644
1127
|
ctx,
|
|
645
1128
|
projectConfig,
|
|
1129
|
+
schemasByKey,
|
|
646
1130
|
});
|
|
647
1131
|
}
|
|
648
1132
|
// force
|
|
@@ -655,6 +1139,7 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
655
1139
|
pathPrefix: ["force", environmentKey],
|
|
656
1140
|
ctx,
|
|
657
1141
|
projectConfig,
|
|
1142
|
+
schemasByKey,
|
|
658
1143
|
});
|
|
659
1144
|
}
|
|
660
1145
|
}
|
|
@@ -671,6 +1156,7 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
671
1156
|
pathPrefix: ["rules"],
|
|
672
1157
|
ctx,
|
|
673
1158
|
projectConfig,
|
|
1159
|
+
schemasByKey,
|
|
674
1160
|
});
|
|
675
1161
|
}
|
|
676
1162
|
// force
|
|
@@ -683,6 +1169,7 @@ function getFeatureZodSchema(projectConfig, conditionsZodSchema, availableAttrib
|
|
|
683
1169
|
pathPrefix: ["force"],
|
|
684
1170
|
ctx,
|
|
685
1171
|
projectConfig,
|
|
1172
|
+
schemasByKey,
|
|
686
1173
|
});
|
|
687
1174
|
}
|
|
688
1175
|
}
|