@featurevisor/core 2.13.0 → 2.14.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 +906 -557
- package/coverage/coverage-final.json +11 -8
- 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 +38 -8
- package/coverage/lcov-report/builder/mutateVariables.ts.html +400 -0
- package/coverage/lcov-report/builder/mutator.ts.html +796 -0
- 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/projectConfig.ts.html +1 -1
- package/coverage/lcov-report/datasource/adapter.ts.html +1 -1
- package/coverage/lcov-report/datasource/datasource.ts.html +4 -4
- package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +23 -23
- package/coverage/lcov-report/datasource/index.html +1 -1
- package/coverage/lcov-report/datasource/index.ts.html +1 -1
- package/coverage/lcov-report/index.html +28 -28
- package/coverage/lcov-report/linter/attributeSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/checkCircularDependency.ts.html +1 -1
- package/coverage/lcov-report/linter/checkPercentageExceedingSlot.ts.html +1 -1
- package/coverage/lcov-report/linter/conditionSchema.ts.html +7 -7
- package/coverage/lcov-report/linter/featureSchema.ts.html +789 -318
- package/coverage/lcov-report/linter/groupSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/index.html +34 -19
- package/coverage/lcov-report/linter/lintProject.ts.html +25 -25
- package/coverage/lcov-report/linter/mutationNotation.ts.html +1309 -0
- package/coverage/lcov-report/linter/printError.ts.html +1 -1
- package/coverage/lcov-report/linter/schema.ts.html +69 -69
- package/coverage/lcov-report/linter/segmentSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/testSchema.ts.html +5 -5
- 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/index.ts.html +1 -1
- package/coverage/lcov-report/parsers/json.ts.html +1 -1
- package/coverage/lcov-report/parsers/yml.ts.html +2 -2
- package/coverage/lcov-report/tester/cliFormat.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-report/utils/git.ts.html +1 -1
- package/coverage/lcov-report/utils/index.html +1 -1
- package/coverage/lcov.info +1839 -1120
- package/lib/builder/buildDatafile.js +8 -3
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/builder/mutateVariables.d.ts +14 -0
- package/lib/builder/mutateVariables.js +90 -0
- package/lib/builder/mutateVariables.js.map +1 -0
- package/lib/builder/mutateVariables.spec.d.ts +1 -0
- package/lib/builder/mutateVariables.spec.js +1045 -0
- package/lib/builder/mutateVariables.spec.js.map +1 -0
- package/lib/builder/mutator.d.ts +20 -0
- package/lib/builder/mutator.js +223 -0
- package/lib/builder/mutator.js.map +1 -0
- package/lib/builder/mutator.spec.d.ts +1 -0
- package/lib/builder/mutator.spec.js +368 -0
- package/lib/builder/mutator.spec.js.map +1 -0
- package/lib/linter/featureSchema.d.ts +7 -7
- package/lib/linter/featureSchema.js +138 -62
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/featureSchema.spec.js +150 -0
- package/lib/linter/featureSchema.spec.js.map +1 -1
- package/lib/linter/mutationNotation.d.ts +47 -0
- package/lib/linter/mutationNotation.js +381 -0
- package/lib/linter/mutationNotation.js.map +1 -0
- package/lib/linter/mutationNotation.spec.d.ts +1 -0
- package/lib/linter/mutationNotation.spec.js +549 -0
- package/lib/linter/mutationNotation.spec.js.map +1 -0
- package/package.json +2 -2
- package/src/builder/buildDatafile.ts +29 -3
- package/src/builder/mutateVariables.spec.ts +1134 -0
- package/src/builder/mutateVariables.ts +105 -0
- package/src/builder/mutator.spec.ts +413 -0
- package/src/builder/mutator.ts +237 -0
- package/src/linter/featureSchema.spec.ts +182 -0
- package/src/linter/featureSchema.ts +254 -97
- package/src/linter/mutationNotation.spec.ts +642 -0
- package/src/linter/mutationNotation.ts +408 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type { Schema } from "@featurevisor/types";
|
|
1
|
+
import type { Schema, SchemaType } from "@featurevisor/types";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import { ProjectConfig } from "../config";
|
|
5
|
+
import {
|
|
6
|
+
isMutationKey,
|
|
7
|
+
validateMutationKey,
|
|
8
|
+
parsePathMapKey,
|
|
9
|
+
resolveSchemaAtPath,
|
|
10
|
+
} from "./mutationNotation";
|
|
5
11
|
import {
|
|
6
12
|
valueZodSchema,
|
|
7
13
|
propertyTypeEnum,
|
|
@@ -250,27 +256,42 @@ function valueDeepEqual(a: unknown, b: unknown): boolean {
|
|
|
250
256
|
return false;
|
|
251
257
|
}
|
|
252
258
|
|
|
259
|
+
type SchemaLikeForRequired = {
|
|
260
|
+
type?: string;
|
|
261
|
+
properties?: Record<string, unknown>;
|
|
262
|
+
required?: string[];
|
|
263
|
+
items?: unknown;
|
|
264
|
+
schema?: string;
|
|
265
|
+
oneOf?: unknown[];
|
|
266
|
+
};
|
|
267
|
+
|
|
253
268
|
/**
|
|
254
269
|
* Recursively validates that every `required` array (at this level and in nested
|
|
255
270
|
* object/array schemas) only contains keys that exist in the same level's `properties`.
|
|
256
271
|
* Adds Zod issues with the correct path for invalid required field names.
|
|
272
|
+
* When schemasByKey is provided, resolves schema references (schema: key) so that
|
|
273
|
+
* referenced and nested schemas are validated too.
|
|
257
274
|
*/
|
|
258
275
|
function refineRequiredKeysInSchema(
|
|
259
|
-
schema:
|
|
260
|
-
type?: string;
|
|
261
|
-
properties?: Record<string, unknown>;
|
|
262
|
-
required?: string[];
|
|
263
|
-
items?: unknown;
|
|
264
|
-
},
|
|
276
|
+
schema: SchemaLikeForRequired,
|
|
265
277
|
pathPrefix: (string | number)[],
|
|
266
278
|
ctx: z.RefinementCtx,
|
|
279
|
+
schemasByKey?: Record<string, Schema>,
|
|
267
280
|
): void {
|
|
268
281
|
if (!schema || typeof schema !== "object") return;
|
|
269
282
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
283
|
+
// Resolve schema reference so we validate the referenced schema (and can recurse into it)
|
|
284
|
+
let current: SchemaLikeForRequired = schema;
|
|
285
|
+
if (schema.schema && schemasByKey?.[schema.schema]) {
|
|
286
|
+
current = schemasByKey[schema.schema] as SchemaLikeForRequired;
|
|
287
|
+
refineRequiredKeysInSchema(current, pathPrefix, ctx, schemasByKey);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const effectiveType = current.type;
|
|
292
|
+
const properties = current.properties;
|
|
293
|
+
const required = current.required;
|
|
294
|
+
const items = current.items;
|
|
274
295
|
|
|
275
296
|
if (
|
|
276
297
|
effectiveType === "object" &&
|
|
@@ -296,9 +317,10 @@ function refineRequiredKeysInSchema(
|
|
|
296
317
|
const nested = properties[key];
|
|
297
318
|
if (nested && typeof nested === "object") {
|
|
298
319
|
refineRequiredKeysInSchema(
|
|
299
|
-
nested as
|
|
320
|
+
nested as SchemaLikeForRequired,
|
|
300
321
|
[...pathPrefix, "properties", key],
|
|
301
322
|
ctx,
|
|
323
|
+
schemasByKey,
|
|
302
324
|
);
|
|
303
325
|
}
|
|
304
326
|
}
|
|
@@ -306,20 +328,22 @@ function refineRequiredKeysInSchema(
|
|
|
306
328
|
|
|
307
329
|
if (items && typeof items === "object" && !Array.isArray(items)) {
|
|
308
330
|
refineRequiredKeysInSchema(
|
|
309
|
-
items as
|
|
331
|
+
items as SchemaLikeForRequired,
|
|
310
332
|
[...pathPrefix, "items"],
|
|
311
333
|
ctx,
|
|
334
|
+
schemasByKey,
|
|
312
335
|
);
|
|
313
336
|
}
|
|
314
337
|
|
|
315
|
-
const oneOf = (
|
|
338
|
+
const oneOf = (current as { oneOf?: unknown[] }).oneOf;
|
|
316
339
|
if (oneOf && Array.isArray(oneOf)) {
|
|
317
340
|
oneOf.forEach((branch, i) => {
|
|
318
341
|
if (branch && typeof branch === "object") {
|
|
319
342
|
refineRequiredKeysInSchema(
|
|
320
|
-
branch as
|
|
343
|
+
branch as SchemaLikeForRequired,
|
|
321
344
|
[...pathPrefix, "oneOf", i],
|
|
322
345
|
ctx,
|
|
346
|
+
schemasByKey,
|
|
323
347
|
);
|
|
324
348
|
}
|
|
325
349
|
});
|
|
@@ -504,15 +528,21 @@ function refineVariableValueObject(
|
|
|
504
528
|
}
|
|
505
529
|
}
|
|
506
530
|
|
|
531
|
+
/** Schema or variable schema (e.g. type "json", defaultValue); used for value validation. */
|
|
532
|
+
type VariableSchemaLike =
|
|
533
|
+
| (Omit<Schema, "type"> & { type?: SchemaType | "json" })
|
|
534
|
+
| null
|
|
535
|
+
| undefined;
|
|
536
|
+
|
|
507
537
|
function superRefineVariableValue(
|
|
508
538
|
projectConfig: ProjectConfig,
|
|
509
|
-
variableSchema,
|
|
510
|
-
variableValue,
|
|
511
|
-
path,
|
|
512
|
-
ctx,
|
|
539
|
+
variableSchema: VariableSchemaLike,
|
|
540
|
+
variableValue: unknown,
|
|
541
|
+
path: (string | number)[],
|
|
542
|
+
ctx: z.RefinementCtx,
|
|
513
543
|
variableKey?: string,
|
|
514
544
|
schemasByKey?: Record<string, Schema>,
|
|
515
|
-
) {
|
|
545
|
+
): void {
|
|
516
546
|
const label = getVariableLabel(variableSchema, variableKey, path);
|
|
517
547
|
|
|
518
548
|
if (!variableSchema) {
|
|
@@ -854,6 +884,66 @@ function superRefineVariableValue(
|
|
|
854
884
|
});
|
|
855
885
|
}
|
|
856
886
|
|
|
887
|
+
/**
|
|
888
|
+
* Validate a single variable entry (key + value) in variables map.
|
|
889
|
+
* Key may be a root variable name or mutation notation (e.g. "config.width", "items[0].name").
|
|
890
|
+
*/
|
|
891
|
+
function refineVariableEntry(
|
|
892
|
+
projectConfig: ProjectConfig,
|
|
893
|
+
variableSchemaByKey: Record<string, unknown>,
|
|
894
|
+
variableKey: string,
|
|
895
|
+
variableValue: unknown,
|
|
896
|
+
path: (string | number)[],
|
|
897
|
+
ctx: z.RefinementCtx,
|
|
898
|
+
schemasByKey?: Record<string, Schema>,
|
|
899
|
+
): void {
|
|
900
|
+
if (isMutationKey(variableKey)) {
|
|
901
|
+
const validation = validateMutationKey(
|
|
902
|
+
variableKey,
|
|
903
|
+
variableSchemaByKey as Record<string, Schema>,
|
|
904
|
+
schemasByKey,
|
|
905
|
+
);
|
|
906
|
+
if (!validation.valid && validation.error) {
|
|
907
|
+
ctx.addIssue({
|
|
908
|
+
code: z.ZodIssueCode.custom,
|
|
909
|
+
message: validation.error,
|
|
910
|
+
path,
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (validation.valueSchema) {
|
|
915
|
+
superRefineVariableValue(
|
|
916
|
+
projectConfig,
|
|
917
|
+
validation.valueSchema,
|
|
918
|
+
variableValue,
|
|
919
|
+
path,
|
|
920
|
+
ctx,
|
|
921
|
+
variableKey,
|
|
922
|
+
schemasByKey,
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const variableSchema = variableSchemaByKey[variableKey];
|
|
928
|
+
if (!variableSchema) {
|
|
929
|
+
ctx.addIssue({
|
|
930
|
+
code: z.ZodIssueCode.custom,
|
|
931
|
+
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
932
|
+
path,
|
|
933
|
+
});
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
superRefineVariableValue(
|
|
937
|
+
projectConfig,
|
|
938
|
+
variableSchema as Parameters<typeof superRefineVariableValue>[1],
|
|
939
|
+
variableValue,
|
|
940
|
+
path,
|
|
941
|
+
ctx,
|
|
942
|
+
variableKey,
|
|
943
|
+
schemasByKey,
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
857
947
|
function refineForce({
|
|
858
948
|
ctx,
|
|
859
949
|
parsedFeature, // eslint-disable-line
|
|
@@ -879,24 +969,15 @@ function refineForce({
|
|
|
879
969
|
// force[n].variables[key]
|
|
880
970
|
if (f.variables) {
|
|
881
971
|
Object.keys(f.variables).forEach((variableKey) => {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
projectConfig,
|
|
892
|
-
variableSchema,
|
|
893
|
-
f.variables[variableKey],
|
|
894
|
-
pathPrefix.concat([fN, "variables", variableKey]),
|
|
895
|
-
ctx,
|
|
896
|
-
variableKey,
|
|
897
|
-
schemasByKey,
|
|
898
|
-
);
|
|
899
|
-
}
|
|
972
|
+
refineVariableEntry(
|
|
973
|
+
projectConfig,
|
|
974
|
+
variableSchemaByKey,
|
|
975
|
+
variableKey,
|
|
976
|
+
f.variables[variableKey],
|
|
977
|
+
pathPrefix.concat([fN, "variables", variableKey]),
|
|
978
|
+
ctx,
|
|
979
|
+
schemasByKey,
|
|
980
|
+
);
|
|
900
981
|
});
|
|
901
982
|
}
|
|
902
983
|
});
|
|
@@ -916,24 +997,15 @@ function refineRules({
|
|
|
916
997
|
// rules[n].variables[key]
|
|
917
998
|
if (rule.variables) {
|
|
918
999
|
Object.keys(rule.variables).forEach((variableKey) => {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
projectConfig,
|
|
929
|
-
variableSchema,
|
|
930
|
-
rule.variables[variableKey],
|
|
931
|
-
pathPrefix.concat([ruleN, "variables", variableKey]),
|
|
932
|
-
ctx,
|
|
933
|
-
variableKey,
|
|
934
|
-
schemasByKey,
|
|
935
|
-
);
|
|
936
|
-
}
|
|
1000
|
+
refineVariableEntry(
|
|
1001
|
+
projectConfig,
|
|
1002
|
+
variableSchemaByKey,
|
|
1003
|
+
variableKey,
|
|
1004
|
+
rule.variables[variableKey],
|
|
1005
|
+
pathPrefix.concat([ruleN, "variables", variableKey]),
|
|
1006
|
+
ctx,
|
|
1007
|
+
schemasByKey,
|
|
1008
|
+
);
|
|
937
1009
|
});
|
|
938
1010
|
}
|
|
939
1011
|
|
|
@@ -1014,6 +1086,7 @@ export function getFeatureZodSchema(
|
|
|
1014
1086
|
) {
|
|
1015
1087
|
const schemaZodSchema = getSchemaZodSchema(availableSchemaKeys);
|
|
1016
1088
|
const variableValueZodSchema = valueZodSchema;
|
|
1089
|
+
const variableValueOrNullZodSchema = z.union([valueZodSchema, z.null()]);
|
|
1017
1090
|
|
|
1018
1091
|
const variationValueZodSchema = z.string().min(1);
|
|
1019
1092
|
|
|
@@ -1061,7 +1134,7 @@ export function getFeatureZodSchema(
|
|
|
1061
1134
|
|
|
1062
1135
|
enabled: z.boolean().optional(),
|
|
1063
1136
|
variation: variationValueZodSchema.optional(),
|
|
1064
|
-
variables: z.record(
|
|
1137
|
+
variables: z.record(variableValueOrNullZodSchema).optional(),
|
|
1065
1138
|
variationWeights: z.record(z.number().min(0).max(100)).optional(),
|
|
1066
1139
|
})
|
|
1067
1140
|
.strict(),
|
|
@@ -1109,7 +1182,7 @@ export function getFeatureZodSchema(
|
|
|
1109
1182
|
segments: groupSegmentsZodSchema,
|
|
1110
1183
|
enabled: z.boolean().optional(),
|
|
1111
1184
|
variation: variationValueZodSchema.optional(),
|
|
1112
|
-
variables: z.record(
|
|
1185
|
+
variables: z.record(variableValueOrNullZodSchema).optional(),
|
|
1113
1186
|
})
|
|
1114
1187
|
.strict(),
|
|
1115
1188
|
z
|
|
@@ -1117,7 +1190,7 @@ export function getFeatureZodSchema(
|
|
|
1117
1190
|
conditions: conditionsZodSchema,
|
|
1118
1191
|
enabled: z.boolean().optional(),
|
|
1119
1192
|
variation: variationValueZodSchema.optional(),
|
|
1120
|
-
variables: z.record(
|
|
1193
|
+
variables: z.record(variableValueOrNullZodSchema).optional(),
|
|
1121
1194
|
})
|
|
1122
1195
|
.strict(),
|
|
1123
1196
|
]),
|
|
@@ -1299,11 +1372,12 @@ export function getFeatureZodSchema(
|
|
|
1299
1372
|
});
|
|
1300
1373
|
return;
|
|
1301
1374
|
}
|
|
1302
|
-
// Validate required ⊆ properties at this level and in all nested object schemas
|
|
1375
|
+
// Validate required ⊆ properties at this level and in all nested object schemas (resolve refs when schemasByKey provided)
|
|
1303
1376
|
refineRequiredKeysInSchema(
|
|
1304
|
-
variableSchema as
|
|
1377
|
+
variableSchema as SchemaLikeForRequired,
|
|
1305
1378
|
[],
|
|
1306
1379
|
ctx,
|
|
1380
|
+
schemasByKey,
|
|
1307
1381
|
);
|
|
1308
1382
|
}),
|
|
1309
1383
|
)
|
|
@@ -1318,7 +1392,7 @@ export function getFeatureZodSchema(
|
|
|
1318
1392
|
description: z.string().optional(),
|
|
1319
1393
|
value: variationValueZodSchema,
|
|
1320
1394
|
weight: z.number().min(0).max(100),
|
|
1321
|
-
variables: z.record(
|
|
1395
|
+
variables: z.record(variableValueOrNullZodSchema).optional(),
|
|
1322
1396
|
variableOverrides: z
|
|
1323
1397
|
.record(
|
|
1324
1398
|
z.array(
|
|
@@ -1413,6 +1487,28 @@ export function getFeatureZodSchema(
|
|
|
1413
1487
|
variableKeys.forEach((variableKey) => {
|
|
1414
1488
|
const variableSchema = variableSchemaByKey[variableKey];
|
|
1415
1489
|
|
|
1490
|
+
// When variable references a schema by name, ensure it resolves and validate the referenced schema
|
|
1491
|
+
if ("schema" in variableSchema && variableSchema.schema) {
|
|
1492
|
+
const resolvedSchema = resolveVariableSchema(
|
|
1493
|
+
variableSchema as Parameters<typeof resolveVariableSchema>[0],
|
|
1494
|
+
schemasByKey,
|
|
1495
|
+
);
|
|
1496
|
+
if (!resolvedSchema) {
|
|
1497
|
+
ctx.addIssue({
|
|
1498
|
+
code: z.ZodIssueCode.custom,
|
|
1499
|
+
message: `Schema "${variableSchema.schema}" could not be loaded for variable "${variableKey}".`,
|
|
1500
|
+
path: ["variablesSchema", variableKey],
|
|
1501
|
+
});
|
|
1502
|
+
} else {
|
|
1503
|
+
refineRequiredKeysInSchema(
|
|
1504
|
+
resolvedSchema as SchemaLikeForRequired,
|
|
1505
|
+
["variablesSchema", variableKey],
|
|
1506
|
+
ctx,
|
|
1507
|
+
schemasByKey,
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1416
1512
|
// When type and enum are both present, all enum values must match the type
|
|
1417
1513
|
const effectiveSchema = resolveVariableSchema(variableSchema, schemasByKey);
|
|
1418
1514
|
if (
|
|
@@ -1487,32 +1583,21 @@ export function getFeatureZodSchema(
|
|
|
1487
1583
|
// variations[n].variables[key]
|
|
1488
1584
|
if (variation.variables) {
|
|
1489
1585
|
for (const variableKey of Object.keys(variation.variables)) {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
superRefineVariableValue(
|
|
1500
|
-
projectConfig,
|
|
1501
|
-
variableSchema,
|
|
1502
|
-
variableValue,
|
|
1503
|
-
["variations", variationN, "variables", variableKey],
|
|
1504
|
-
ctx,
|
|
1505
|
-
variableKey,
|
|
1506
|
-
schemasByKey,
|
|
1507
|
-
);
|
|
1508
|
-
}
|
|
1586
|
+
refineVariableEntry(
|
|
1587
|
+
projectConfig,
|
|
1588
|
+
variableSchemaByKey,
|
|
1589
|
+
variableKey,
|
|
1590
|
+
variation.variables[variableKey],
|
|
1591
|
+
["variations", variationN, "variables", variableKey],
|
|
1592
|
+
ctx,
|
|
1593
|
+
schemasByKey,
|
|
1594
|
+
);
|
|
1509
1595
|
}
|
|
1510
1596
|
}
|
|
1511
1597
|
|
|
1512
|
-
// variations[n].variableOverrides[key][].value (
|
|
1598
|
+
// variations[n].variableOverrides[key][].value (path-map or full value)
|
|
1513
1599
|
if (variation.variableOverrides) {
|
|
1514
1600
|
for (const variableKey of Object.keys(variation.variableOverrides)) {
|
|
1515
|
-
const overrides = variation.variableOverrides[variableKey];
|
|
1516
1601
|
const variableSchema = variableSchemaByKey[variableKey];
|
|
1517
1602
|
if (!variableSchema) {
|
|
1518
1603
|
ctx.addIssue({
|
|
@@ -1520,26 +1605,98 @@ export function getFeatureZodSchema(
|
|
|
1520
1605
|
message: `Variable "${variableKey}" is not defined in \`variablesSchema\`.`,
|
|
1521
1606
|
path: ["variations", variationN, "variableOverrides", variableKey],
|
|
1522
1607
|
});
|
|
1523
|
-
|
|
1524
|
-
|
|
1608
|
+
continue;
|
|
1609
|
+
}
|
|
1610
|
+
// When variable references a schema by name, ensure it can be resolved
|
|
1611
|
+
const variableSchemaWithRef = variableSchema as { schema?: string };
|
|
1612
|
+
if (variableSchemaWithRef.schema) {
|
|
1613
|
+
const resolvedVarSchema = resolveVariableSchema(
|
|
1614
|
+
variableSchema as Parameters<typeof resolveVariableSchema>[0],
|
|
1615
|
+
schemasByKey,
|
|
1616
|
+
);
|
|
1617
|
+
if (!resolvedVarSchema) {
|
|
1618
|
+
ctx.addIssue({
|
|
1619
|
+
code: z.ZodIssueCode.custom,
|
|
1620
|
+
message: `Schema "${variableSchemaWithRef.schema}" could not be loaded for variable "${variableKey}".`,
|
|
1621
|
+
path: ["variations", variationN, "variableOverrides", variableKey],
|
|
1622
|
+
});
|
|
1623
|
+
continue;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
const overrides = variation.variableOverrides[variableKey];
|
|
1627
|
+
if (!Array.isArray(overrides)) continue;
|
|
1628
|
+
overrides.forEach((override, overrideN) => {
|
|
1629
|
+
const overrideValue = override.value;
|
|
1630
|
+
const valuePath = [
|
|
1631
|
+
"variations",
|
|
1632
|
+
variationN,
|
|
1633
|
+
"variableOverrides",
|
|
1634
|
+
variableKey,
|
|
1635
|
+
overrideN,
|
|
1636
|
+
"value",
|
|
1637
|
+
];
|
|
1638
|
+
if (
|
|
1639
|
+
typeof overrideValue === "object" &&
|
|
1640
|
+
overrideValue !== null &&
|
|
1641
|
+
!Array.isArray(overrideValue)
|
|
1642
|
+
) {
|
|
1643
|
+
const pathMap = overrideValue as Record<string, unknown>;
|
|
1644
|
+
for (const pathKey of Object.keys(pathMap)) {
|
|
1645
|
+
const pathSegments = parsePathMapKey(pathKey);
|
|
1646
|
+
if (!pathSegments || pathSegments.length === 0) {
|
|
1647
|
+
ctx.addIssue({
|
|
1648
|
+
code: z.ZodIssueCode.custom,
|
|
1649
|
+
message: `Invalid mutation path "${pathKey}" in variableOverride for "${variableKey}".`,
|
|
1650
|
+
path: [...valuePath, pathKey],
|
|
1651
|
+
});
|
|
1652
|
+
continue;
|
|
1653
|
+
}
|
|
1654
|
+
const atPathSchema = resolveSchemaAtPath(
|
|
1655
|
+
variableSchema as Schema,
|
|
1656
|
+
pathSegments,
|
|
1657
|
+
schemasByKey,
|
|
1658
|
+
);
|
|
1659
|
+
if (!atPathSchema) {
|
|
1660
|
+
const effectiveSchema = resolveVariableSchema(
|
|
1661
|
+
variableSchema as Parameters<typeof resolveVariableSchema>[0],
|
|
1662
|
+
schemasByKey,
|
|
1663
|
+
);
|
|
1664
|
+
const isFlatObject =
|
|
1665
|
+
effectiveSchema?.type === "object" &&
|
|
1666
|
+
(!effectiveSchema.properties ||
|
|
1667
|
+
Object.keys(effectiveSchema.properties).length === 0);
|
|
1668
|
+
if (isFlatObject) {
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
ctx.addIssue({
|
|
1672
|
+
code: z.ZodIssueCode.custom,
|
|
1673
|
+
message: `Path "${pathKey}" is invalid for variable "${variableKey}" (not in schema).`,
|
|
1674
|
+
path: [...valuePath, pathKey],
|
|
1675
|
+
});
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
superRefineVariableValue(
|
|
1679
|
+
projectConfig,
|
|
1680
|
+
atPathSchema,
|
|
1681
|
+
pathMap[pathKey],
|
|
1682
|
+
[...valuePath, pathKey],
|
|
1683
|
+
ctx,
|
|
1684
|
+
pathKey,
|
|
1685
|
+
schemasByKey,
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
} else {
|
|
1525
1689
|
superRefineVariableValue(
|
|
1526
1690
|
projectConfig,
|
|
1527
|
-
variableSchema,
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
"variations",
|
|
1531
|
-
variationN,
|
|
1532
|
-
"variableOverrides",
|
|
1533
|
-
variableKey,
|
|
1534
|
-
overrideN,
|
|
1535
|
-
"value",
|
|
1536
|
-
],
|
|
1691
|
+
variableSchema as Parameters<typeof superRefineVariableValue>[1],
|
|
1692
|
+
overrideValue,
|
|
1693
|
+
valuePath,
|
|
1537
1694
|
ctx,
|
|
1538
1695
|
variableKey,
|
|
1539
1696
|
schemasByKey,
|
|
1540
1697
|
);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1543
1700
|
}
|
|
1544
1701
|
}
|
|
1545
1702
|
});
|