@featurevisor/core 2.17.0 → 2.19.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 +1692 -1196
- package/coverage/coverage-final.json +29 -22
- package/coverage/lcov-report/builder/allocator.ts.html +26 -26
- package/coverage/lcov-report/builder/buildDatafile.ts.html +2017 -0
- package/coverage/lcov-report/builder/buildScopedConditions.ts.html +35 -35
- package/coverage/lcov-report/builder/buildScopedDatafile.ts.html +213 -45
- package/coverage/lcov-report/builder/buildScopedSegments.ts.html +28 -28
- package/coverage/lcov-report/builder/convertToV1.ts.html +583 -0
- package/coverage/lcov-report/builder/getFeatureRanges.ts.html +268 -0
- package/coverage/lcov-report/builder/hashes.ts.html +412 -0
- package/coverage/lcov-report/builder/index.html +90 -30
- package/coverage/lcov-report/builder/mutateVariables.ts.html +66 -45
- package/coverage/lcov-report/builder/mutator.ts.html +95 -95
- package/coverage/lcov-report/builder/revision.ts.html +1 -1
- package/coverage/lcov-report/builder/traffic.ts.html +60 -57
- package/coverage/lcov-report/config/index.html +1 -1
- package/coverage/lcov-report/config/index.ts.html +2 -2
- package/coverage/lcov-report/config/projectConfig.ts.html +28 -28
- package/coverage/lcov-report/datasource/adapter.ts.html +2 -2
- package/coverage/lcov-report/datasource/datasource.ts.html +4 -4
- package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +34 -34
- 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 +39 -39
- 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 +17 -17
- package/coverage/lcov-report/linter/featureSchema.ts.html +879 -417
- package/coverage/lcov-report/linter/groupSchema.ts.html +1 -1
- package/coverage/lcov-report/linter/index.html +34 -34
- package/coverage/lcov-report/linter/lintProject.ts.html +25 -25
- package/coverage/lcov-report/linter/mutationNotation.ts.html +224 -122
- package/coverage/lcov-report/linter/printError.ts.html +1 -1
- package/coverage/lcov-report/linter/schema.ts.html +175 -85
- 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 +4 -4
- package/coverage/lcov-report/parsers/json.ts.html +2 -2
- package/coverage/lcov-report/parsers/yml.ts.html +6 -6
- 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/extractKeys.ts.html +493 -0
- package/coverage/lcov-report/utils/git.ts.html +3 -3
- package/coverage/lcov-report/utils/index.html +54 -9
- package/coverage/lcov-report/utils/index.ts.html +94 -0
- package/coverage/lcov-report/utils/pretty.ts.html +121 -0
- package/coverage/lcov.info +3206 -2260
- package/json-schema/attribute.json +25 -9
- package/json-schema/feature.json +319 -238
- package/json-schema/segment.json +76 -45
- package/lib/builder/buildDatafile.js +50 -3
- package/lib/builder/buildDatafile.js.map +1 -1
- package/lib/builder/buildDatafile.spec.d.ts +1 -0
- package/lib/builder/buildDatafile.spec.js +233 -0
- package/lib/builder/buildDatafile.spec.js.map +1 -0
- package/lib/builder/buildScopedDatafile.js +37 -1
- package/lib/builder/buildScopedDatafile.js.map +1 -1
- package/lib/builder/buildScopedDatafile.spec.js +289 -0
- package/lib/builder/buildScopedDatafile.spec.js.map +1 -1
- package/lib/builder/mutateVariables.d.ts +1 -1
- package/lib/builder/mutateVariables.js +4 -1
- package/lib/builder/mutateVariables.js.map +1 -1
- package/lib/builder/mutateVariables.spec.js +29 -0
- package/lib/builder/mutateVariables.spec.js.map +1 -1
- package/lib/builder/traffic.js +1 -0
- package/lib/builder/traffic.js.map +1 -1
- package/lib/builder/traffic.spec.js +26 -0
- package/lib/builder/traffic.spec.js.map +1 -1
- package/lib/generate-code/typescript.js +63 -20
- package/lib/generate-code/typescript.js.map +1 -1
- package/lib/linter/featureSchema.d.ts +237 -22
- package/lib/linter/featureSchema.js +175 -84
- package/lib/linter/featureSchema.js.map +1 -1
- package/lib/linter/featureSchema.spec.js +268 -0
- package/lib/linter/featureSchema.spec.js.map +1 -1
- package/lib/linter/mutationNotation.js +54 -19
- package/lib/linter/mutationNotation.js.map +1 -1
- package/lib/linter/mutationNotation.spec.js +49 -0
- package/lib/linter/mutationNotation.spec.js.map +1 -1
- package/lib/linter/schema.d.ts +1 -0
- package/lib/linter/schema.js +13 -0
- package/lib/linter/schema.js.map +1 -1
- package/lib/linter/schema.spec.js +51 -0
- package/lib/linter/schema.spec.js.map +1 -1
- package/package.json +5 -5
- package/src/builder/buildDatafile.spec.ts +267 -0
- package/src/builder/buildDatafile.ts +85 -4
- package/src/builder/buildScopedDatafile.spec.ts +330 -0
- package/src/builder/buildScopedDatafile.ts +57 -1
- package/src/builder/mutateVariables.spec.ts +45 -0
- package/src/builder/mutateVariables.ts +7 -0
- package/src/builder/traffic.spec.ts +33 -0
- package/src/builder/traffic.ts +1 -0
- package/src/generate-code/typescript.ts +87 -20
- package/src/linter/featureSchema.spec.ts +315 -0
- package/src/linter/featureSchema.ts +269 -115
- package/src/linter/mutationNotation.spec.ts +62 -0
- package/src/linter/mutationNotation.ts +47 -13
- package/src/linter/schema.spec.ts +72 -0
- package/src/linter/schema.ts +30 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
import type { DatafileContent, ExistingState, ParsedFeature } from "@featurevisor/types";
|
|
6
|
+
|
|
7
|
+
import type { ProjectConfig } from "../config";
|
|
8
|
+
import { parsers } from "../parsers";
|
|
9
|
+
import { buildDatafile } from "./buildDatafile";
|
|
10
|
+
|
|
11
|
+
function createProjectConfig(root: string, stringify = true): ProjectConfig {
|
|
12
|
+
return {
|
|
13
|
+
featuresDirectoryPath: path.join(root, "features"),
|
|
14
|
+
segmentsDirectoryPath: path.join(root, "segments-missing"),
|
|
15
|
+
attributesDirectoryPath: path.join(root, "attributes-missing"),
|
|
16
|
+
groupsDirectoryPath: path.join(root, "groups-missing"),
|
|
17
|
+
schemasDirectoryPath: path.join(root, "schemas-missing"),
|
|
18
|
+
testsDirectoryPath: path.join(root, "tests-missing"),
|
|
19
|
+
stateDirectoryPath: path.join(root, ".featurevisor"),
|
|
20
|
+
datafilesDirectoryPath: path.join(root, "datafiles"),
|
|
21
|
+
datafileNamePattern: "featurevisor-%s.json",
|
|
22
|
+
revisionFileName: "REVISION",
|
|
23
|
+
siteExportDirectoryPath: path.join(root, "out"),
|
|
24
|
+
environmentsDirectoryPath: path.join(root, "environments"),
|
|
25
|
+
environments: ["staging", "production"],
|
|
26
|
+
splitByEnvironment: false,
|
|
27
|
+
tags: ["all"],
|
|
28
|
+
scopes: [],
|
|
29
|
+
adapter: {},
|
|
30
|
+
plugins: [],
|
|
31
|
+
defaultBucketBy: "userId",
|
|
32
|
+
parser: parsers.yml,
|
|
33
|
+
prettyState: true,
|
|
34
|
+
prettyDatafile: false,
|
|
35
|
+
stringify,
|
|
36
|
+
enforceCatchAllRule: false,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createMockDatasource(feature: ParsedFeature) {
|
|
41
|
+
return {
|
|
42
|
+
listSchemas: async () => [],
|
|
43
|
+
readSchema: async () => {
|
|
44
|
+
throw new Error("readSchema should not be called");
|
|
45
|
+
},
|
|
46
|
+
listFeatures: async () => ["withRuleOverrides"],
|
|
47
|
+
readFeature: async () => feature,
|
|
48
|
+
listSegments: async () => [],
|
|
49
|
+
readSegment: async () => {
|
|
50
|
+
throw new Error("readSegment should not be called");
|
|
51
|
+
},
|
|
52
|
+
listAttributes: async () => [],
|
|
53
|
+
readAttribute: async () => {
|
|
54
|
+
throw new Error("readAttribute should not be called");
|
|
55
|
+
},
|
|
56
|
+
} as any;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createFeatureFixture(): ParsedFeature {
|
|
60
|
+
return {
|
|
61
|
+
key: "withRuleOverrides",
|
|
62
|
+
description: "Rule variableOverrides fixture",
|
|
63
|
+
tags: ["all"],
|
|
64
|
+
bucketBy: "userId",
|
|
65
|
+
variablesSchema: {
|
|
66
|
+
config: {
|
|
67
|
+
type: "object",
|
|
68
|
+
defaultValue: {
|
|
69
|
+
source: "default",
|
|
70
|
+
nested: { value: 1 },
|
|
71
|
+
list: ["d"],
|
|
72
|
+
rows: [
|
|
73
|
+
{ id: 1, label: "one" },
|
|
74
|
+
{ id: 2, label: "two" },
|
|
75
|
+
],
|
|
76
|
+
flag: false,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
fallbackConfig: {
|
|
80
|
+
type: "object",
|
|
81
|
+
defaultValue: {
|
|
82
|
+
origin: "default",
|
|
83
|
+
count: 1,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
variations: [{ value: "control", weight: 100 }],
|
|
88
|
+
rules: {
|
|
89
|
+
staging: [
|
|
90
|
+
{
|
|
91
|
+
key: "rule1",
|
|
92
|
+
segments: "*",
|
|
93
|
+
percentage: 100,
|
|
94
|
+
variables: {
|
|
95
|
+
config: {
|
|
96
|
+
source: "rule",
|
|
97
|
+
nested: { value: 10 },
|
|
98
|
+
list: ["base"],
|
|
99
|
+
rows: [
|
|
100
|
+
{ id: 1, label: "one" },
|
|
101
|
+
{ id: 2, label: "two" },
|
|
102
|
+
],
|
|
103
|
+
flag: true,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
variableOverrides: {
|
|
107
|
+
config: [
|
|
108
|
+
{
|
|
109
|
+
segments: { and: ["mobile", "touch"] },
|
|
110
|
+
value: {
|
|
111
|
+
"nested.value": 20,
|
|
112
|
+
"list:append": "m",
|
|
113
|
+
"rows[id=1]:after": { id: 15, label: "one-half" },
|
|
114
|
+
"rows[id=2]:remove": null,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
conditions: [{ attribute: "country", operator: "equals", value: "de" }],
|
|
119
|
+
value: {
|
|
120
|
+
"nested.value": 30,
|
|
121
|
+
"list:prepend": "de",
|
|
122
|
+
"rows[id=1]:before": { id: 0, label: "zero" },
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
conditions: [{ attribute: "device", operator: "equals", value: "desktop" }],
|
|
127
|
+
value: {
|
|
128
|
+
source: "full",
|
|
129
|
+
nested: { value: 999 },
|
|
130
|
+
list: ["x"],
|
|
131
|
+
rows: [{ id: 7, label: "seven" }],
|
|
132
|
+
flag: false,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
fallbackConfig: [
|
|
137
|
+
{
|
|
138
|
+
conditions: [{ attribute: "country", operator: "equals", value: "nl" }],
|
|
139
|
+
value: {
|
|
140
|
+
count: 5,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
production: [
|
|
148
|
+
{
|
|
149
|
+
key: "everyone",
|
|
150
|
+
segments: "*",
|
|
151
|
+
percentage: 100,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
} as ParsedFeature;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
describe("core: buildDatafile", function () {
|
|
159
|
+
let root: string;
|
|
160
|
+
let existingState: ExistingState;
|
|
161
|
+
|
|
162
|
+
beforeEach(function () {
|
|
163
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), "featurevisor-buildDatafile-"));
|
|
164
|
+
fs.mkdirSync(path.join(root, "features"), { recursive: true });
|
|
165
|
+
existingState = { features: {} };
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
afterEach(function () {
|
|
169
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("resolves rule variableOverrides mutations using rule variables as base and default fallback when missing", async function () {
|
|
173
|
+
const config = createProjectConfig(root, true);
|
|
174
|
+
const datasource = createMockDatasource(createFeatureFixture());
|
|
175
|
+
|
|
176
|
+
const result = (await buildDatafile(
|
|
177
|
+
config,
|
|
178
|
+
datasource,
|
|
179
|
+
{
|
|
180
|
+
schemaVersion: "2",
|
|
181
|
+
revision: "1",
|
|
182
|
+
environment: "staging",
|
|
183
|
+
},
|
|
184
|
+
existingState,
|
|
185
|
+
)) as DatafileContent;
|
|
186
|
+
|
|
187
|
+
const trafficRule = result.features.withRuleOverrides.traffic.find((t) => t.key === "rule1");
|
|
188
|
+
|
|
189
|
+
expect(trafficRule).toBeDefined();
|
|
190
|
+
expect(trafficRule?.variableOverrides).toBeDefined();
|
|
191
|
+
|
|
192
|
+
// Base comes from rule variables (source remains "rule"), not from schema default.
|
|
193
|
+
expect(trafficRule?.variableOverrides?.config[0].value).toEqual({
|
|
194
|
+
source: "rule",
|
|
195
|
+
nested: { value: 20 },
|
|
196
|
+
list: ["base", "m"],
|
|
197
|
+
rows: [
|
|
198
|
+
{ id: 1, label: "one" },
|
|
199
|
+
{ id: 15, label: "one-half" },
|
|
200
|
+
],
|
|
201
|
+
flag: true,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Independent override still uses the same rule-level base and preserves order.
|
|
205
|
+
expect(trafficRule?.variableOverrides?.config[1].value).toEqual({
|
|
206
|
+
source: "rule",
|
|
207
|
+
nested: { value: 30 },
|
|
208
|
+
list: ["de", "base"],
|
|
209
|
+
rows: [
|
|
210
|
+
{ id: 0, label: "zero" },
|
|
211
|
+
{ id: 1, label: "one" },
|
|
212
|
+
{ id: 2, label: "two" },
|
|
213
|
+
],
|
|
214
|
+
flag: true,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Full object override passes through unchanged.
|
|
218
|
+
expect(trafficRule?.variableOverrides?.config[2].value).toEqual({
|
|
219
|
+
source: "full",
|
|
220
|
+
nested: { value: 999 },
|
|
221
|
+
list: ["x"],
|
|
222
|
+
rows: [{ id: 7, label: "seven" }],
|
|
223
|
+
flag: false,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// No rule variable for fallbackConfig -> mutation base is variable schema defaultValue.
|
|
227
|
+
expect(trafficRule?.variableOverrides?.fallbackConfig[0].value).toEqual({
|
|
228
|
+
origin: "default",
|
|
229
|
+
count: 5,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Supports stringification for segments + conditions.
|
|
233
|
+
expect(typeof trafficRule?.variableOverrides?.config[0].segments).toBe("string");
|
|
234
|
+
expect(trafficRule?.variableOverrides?.config[0].segments).toEqual(
|
|
235
|
+
JSON.stringify({ and: ["mobile", "touch"] }),
|
|
236
|
+
);
|
|
237
|
+
expect(typeof trafficRule?.variableOverrides?.config[1].conditions).toBe("string");
|
|
238
|
+
expect(trafficRule?.variableOverrides?.config[1].conditions).toEqual(
|
|
239
|
+
JSON.stringify([{ attribute: "country", operator: "equals", value: "de" }]),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("keeps segments and conditions non-stringified when projectConfig.stringify is false", async function () {
|
|
244
|
+
const config = createProjectConfig(root, false);
|
|
245
|
+
const datasource = createMockDatasource(createFeatureFixture());
|
|
246
|
+
|
|
247
|
+
const result = (await buildDatafile(
|
|
248
|
+
config,
|
|
249
|
+
datasource,
|
|
250
|
+
{
|
|
251
|
+
schemaVersion: "2",
|
|
252
|
+
revision: "1",
|
|
253
|
+
environment: "staging",
|
|
254
|
+
},
|
|
255
|
+
existingState,
|
|
256
|
+
)) as DatafileContent;
|
|
257
|
+
|
|
258
|
+
const trafficRule = result.features.withRuleOverrides.traffic.find((t) => t.key === "rule1");
|
|
259
|
+
|
|
260
|
+
expect(trafficRule?.variableOverrides?.config[0].segments).toEqual({
|
|
261
|
+
and: ["mobile", "touch"],
|
|
262
|
+
});
|
|
263
|
+
expect(trafficRule?.variableOverrides?.config[1].conditions).toEqual([
|
|
264
|
+
{ attribute: "country", operator: "equals", value: "de" },
|
|
265
|
+
]);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -201,6 +201,30 @@ export async function buildDatafile(
|
|
|
201
201
|
for (const parsedRule of rules) {
|
|
202
202
|
const extractedSegmentKeys = extractSegmentKeysFromGroupSegments(parsedRule.segments);
|
|
203
203
|
extractedSegmentKeys.forEach((segmentKey) => segmentKeysUsedByTag.add(segmentKey));
|
|
204
|
+
|
|
205
|
+
if (parsedRule.variableOverrides) {
|
|
206
|
+
for (const variableOverrides of Object.values(parsedRule.variableOverrides)) {
|
|
207
|
+
for (const override of variableOverrides) {
|
|
208
|
+
if (typeof override.conditions !== "undefined") {
|
|
209
|
+
const extractedAttributeKeys = extractAttributeKeysFromConditions(
|
|
210
|
+
override.conditions,
|
|
211
|
+
);
|
|
212
|
+
extractedAttributeKeys.forEach((attributeKey) =>
|
|
213
|
+
attributeKeysUsedByTag.add(attributeKey),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (typeof override.segments !== "undefined") {
|
|
218
|
+
const extractedOverrideSegmentKeys = extractSegmentKeysFromGroupSegments(
|
|
219
|
+
override.segments as GroupSegment | GroupSegment[],
|
|
220
|
+
);
|
|
221
|
+
extractedOverrideSegmentKeys.forEach((segmentKey) =>
|
|
222
|
+
segmentKeysUsedByTag.add(segmentKey),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
204
228
|
}
|
|
205
229
|
|
|
206
230
|
const feature: Feature = {
|
|
@@ -226,6 +250,10 @@ export async function buildDatafile(
|
|
|
226
250
|
const variableKeys = Object.keys(variableOverrides);
|
|
227
251
|
|
|
228
252
|
for (const variableKey of variableKeys) {
|
|
253
|
+
const baseValue = mappedVariation.variables
|
|
254
|
+
? mappedVariation.variables[variableKey]
|
|
255
|
+
: undefined;
|
|
256
|
+
|
|
229
257
|
mappedVariation.variableOverrides[variableKey] = variableOverrides[
|
|
230
258
|
variableKey
|
|
231
259
|
].map((override: VariableOverride) => {
|
|
@@ -246,6 +274,7 @@ export async function buildDatafile(
|
|
|
246
274
|
parsedFeature.variablesSchema,
|
|
247
275
|
variableKey,
|
|
248
276
|
override.value,
|
|
277
|
+
baseValue,
|
|
249
278
|
),
|
|
250
279
|
};
|
|
251
280
|
}
|
|
@@ -267,6 +296,7 @@ export async function buildDatafile(
|
|
|
267
296
|
parsedFeature.variablesSchema,
|
|
268
297
|
variableKey,
|
|
269
298
|
override.value,
|
|
299
|
+
baseValue,
|
|
270
300
|
),
|
|
271
301
|
};
|
|
272
302
|
}
|
|
@@ -285,12 +315,63 @@ export async function buildDatafile(
|
|
|
285
315
|
existingState.features[featureKey],
|
|
286
316
|
featureRanges.get(featureKey) || [],
|
|
287
317
|
).map((t: Traffic) => {
|
|
318
|
+
const resolvedVariables = resolveMutationsForMultipleVariables(
|
|
319
|
+
parsedFeature.variablesSchema,
|
|
320
|
+
t.variables,
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
let resolvedVariableOverrides = t.variableOverrides;
|
|
324
|
+
|
|
325
|
+
if (t.variableOverrides) {
|
|
326
|
+
resolvedVariableOverrides = {};
|
|
327
|
+
const variableKeys = Object.keys(t.variableOverrides);
|
|
328
|
+
|
|
329
|
+
for (const variableKey of variableKeys) {
|
|
330
|
+
const overrides = t.variableOverrides[variableKey];
|
|
331
|
+
const baseValue = resolvedVariables ? resolvedVariables[variableKey] : undefined;
|
|
332
|
+
|
|
333
|
+
resolvedVariableOverrides[variableKey] = overrides.map(
|
|
334
|
+
(override: VariableOverride) => {
|
|
335
|
+
if (typeof override.conditions !== "undefined") {
|
|
336
|
+
return {
|
|
337
|
+
conditions:
|
|
338
|
+
projectConfig.stringify && typeof override.conditions !== "string"
|
|
339
|
+
? JSON.stringify(override.conditions)
|
|
340
|
+
: override.conditions,
|
|
341
|
+
value: resolveMutationsForSingleVariable(
|
|
342
|
+
parsedFeature.variablesSchema,
|
|
343
|
+
variableKey,
|
|
344
|
+
override.value,
|
|
345
|
+
baseValue,
|
|
346
|
+
),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (typeof override.segments !== "undefined") {
|
|
351
|
+
return {
|
|
352
|
+
segments:
|
|
353
|
+
projectConfig.stringify && typeof override.segments !== "string"
|
|
354
|
+
? JSON.stringify(override.segments)
|
|
355
|
+
: override.segments,
|
|
356
|
+
value: resolveMutationsForSingleVariable(
|
|
357
|
+
parsedFeature.variablesSchema,
|
|
358
|
+
variableKey,
|
|
359
|
+
override.value,
|
|
360
|
+
baseValue,
|
|
361
|
+
),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return override;
|
|
366
|
+
},
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
288
371
|
return {
|
|
289
372
|
...t,
|
|
290
|
-
variables:
|
|
291
|
-
|
|
292
|
-
t.variables,
|
|
293
|
-
),
|
|
373
|
+
variables: resolvedVariables,
|
|
374
|
+
variableOverrides: resolvedVariableOverrides,
|
|
294
375
|
segments:
|
|
295
376
|
typeof t.segments !== "string" && projectConfig.stringify
|
|
296
377
|
? JSON.stringify(t.segments)
|