@featurevisor/types 1.35.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.35.3](https://github.com/featurevisor/featurevisor/compare/v1.35.2...v1.35.3) (2025-04-12)
7
+
8
+ **Note:** Version bump only for package @featurevisor/types
9
+
10
+
11
+
12
+
13
+
6
14
  # [1.35.0](https://github.com/featurevisor/featurevisor/compare/v1.34.2...v1.35.0) (2025-03-13)
7
15
 
8
16
 
package/README.md CHANGED
@@ -4,12 +4,6 @@
4
4
 
5
5
  Visit [https://featurevisor.com](https://featurevisor.com) for more information.
6
6
 
7
- ## Installation
8
-
9
- ```
10
- $ npm install --save @featurevisor/types
11
- ```
12
-
13
7
  ## License
14
8
 
15
9
  MIT © [Fahad Heylaal](https://fahad19.com)
package/lib/index.d.ts CHANGED
@@ -1,22 +1,31 @@
1
1
  export type AttributeKey = string;
2
- export type AttributeValue = string | number | boolean | Date | null | undefined;
2
+ export interface AttributeObjectValue {
3
+ [key: AttributeKey]: AttributeValue;
4
+ }
5
+ export type AttributeValue = string | number | boolean | Date | null | undefined | string[] | AttributeObjectValue;
3
6
  export interface Context {
4
7
  [key: AttributeKey]: AttributeValue;
5
8
  }
6
- export type AttributeType = "boolean" | "string" | "integer" | "double" | "date" | "semver";
9
+ export type AttributeType = "boolean" | "string" | "integer" | "double" | "date" | "semver" | "object" | "array";
7
10
  export interface Attribute {
8
11
  archived?: boolean;
9
- key: AttributeKey;
12
+ key?: AttributeKey;
10
13
  type: AttributeType;
11
- capture?: boolean;
12
14
  description?: string;
15
+ properties?: {
16
+ [key: AttributeKey]: {
17
+ type: "boolean" | "string" | "integer" | "double" | "date" | "semver" | "array";
18
+ description?: string;
19
+ };
20
+ };
13
21
  }
14
- export type Operator = "equals" | "notEquals" | "greaterThan" | "greaterThanOrEquals" | "lessThan" | "lessThanOrEquals" | "contains" | "notContains" | "startsWith" | "endsWith" | "semverEquals" | "semverNotEquals" | "semverGreaterThan" | "semverGreaterThanOrEquals" | "semverLessThan" | "semverLessThanOrEquals" | "before" | "after" | "in" | "notIn";
22
+ export type Operator = "equals" | "notEquals" | "exists" | "notExists" | "greaterThan" | "greaterThanOrEquals" | "lessThan" | "lessThanOrEquals" | "contains" | "notContains" | "startsWith" | "endsWith" | "semverEquals" | "semverNotEquals" | "semverGreaterThan" | "semverGreaterThanOrEquals" | "semverLessThan" | "semverLessThanOrEquals" | "before" | "after" | "includes" | "notIncludes" | "matches" | "notMatches" | "in" | "notIn";
15
23
  export type ConditionValue = string | number | boolean | Date | null | undefined | string[];
16
24
  export interface PlainCondition {
17
25
  attribute: AttributeKey;
18
26
  operator: Operator;
19
- value: ConditionValue;
27
+ value?: ConditionValue;
28
+ regexFlags?: string;
20
29
  }
21
30
  export interface AndCondition {
22
31
  and: Condition[];
@@ -28,12 +37,12 @@ export interface NotCondition {
28
37
  not: Condition[];
29
38
  }
30
39
  export type AndOrNotCondition = AndCondition | OrCondition | NotCondition;
31
- export type Condition = PlainCondition | AndOrNotCondition;
40
+ export type Condition = PlainCondition | AndOrNotCondition | string;
32
41
  export type SegmentKey = string;
33
42
  export interface Segment {
34
43
  archived?: boolean;
35
- key: SegmentKey;
36
- conditions: Condition | Condition[] | string;
44
+ key?: SegmentKey;
45
+ conditions: Condition | Condition[];
37
46
  description?: string;
38
47
  }
39
48
  export type PlainGroupSegment = SegmentKey;
@@ -61,44 +70,45 @@ export interface VariableOverrideSegments {
61
70
  export interface VariableOverrideConditions {
62
71
  conditions: Condition | Condition[];
63
72
  }
64
- export interface VariableOverrideBase {
65
- value: VariableValue;
66
- }
67
73
  export type VariableOverrideSegmentsOrConditions = VariableOverrideSegments | VariableOverrideConditions;
68
74
  export interface VariableOverride {
69
75
  value: VariableValue;
70
76
  conditions?: Condition | Condition[];
71
77
  segments?: GroupSegment | GroupSegment[];
72
78
  }
73
- export interface Variable {
79
+ export interface VariableV1 {
74
80
  key: VariableKey;
75
81
  value: VariableValue;
76
82
  description?: string;
77
83
  overrides?: VariableOverride[];
78
84
  }
85
+ export interface VariationV1 {
86
+ description?: string;
87
+ value: VariationValue;
88
+ weight?: Weight;
89
+ variables?: VariableV1[];
90
+ }
79
91
  export interface Variation {
80
92
  description?: string;
81
93
  value: VariationValue;
82
94
  weight?: Weight;
83
- variables?: Variable[];
95
+ variables?: {
96
+ [key: VariableKey]: VariableValue;
97
+ };
98
+ variableOverrides?: {
99
+ [key: VariableKey]: VariableOverride[];
100
+ };
84
101
  }
85
102
  export interface VariableSchema {
86
103
  deprecated?: boolean;
87
- key: VariableKey;
104
+ key?: VariableKey;
88
105
  type: VariableType;
89
106
  defaultValue: VariableValue;
90
107
  description?: string;
108
+ useDefaultWhenDisabled?: boolean;
109
+ disabledValue?: VariableValue;
91
110
  }
92
111
  export type FeatureKey = string;
93
- export interface Force {
94
- conditions?: Condition | Condition[];
95
- segments?: GroupSegment | GroupSegment[];
96
- enabled?: boolean;
97
- variation?: VariationValue;
98
- variables?: {
99
- [key: string]: VariableValue;
100
- };
101
- }
102
112
  export interface Slot {
103
113
  feature: FeatureKey | false;
104
114
  percentage: Weight;
@@ -128,7 +138,10 @@ export interface Traffic {
128
138
  variables?: {
129
139
  [key: string]: VariableValue;
130
140
  };
131
- allocation: Allocation[];
141
+ variationWeights?: {
142
+ [key: string]: Weight;
143
+ };
144
+ allocation?: Allocation[];
132
145
  }
133
146
  export type PlainBucketBy = AttributeKey;
134
147
  export type AndBucketBy = AttributeKey[];
@@ -142,29 +155,40 @@ export interface RequiredWithVariation {
142
155
  }
143
156
  export type Required = FeatureKey | RequiredWithVariation;
144
157
  export interface Feature {
145
- key: FeatureKey;
158
+ key?: FeatureKey;
159
+ hash?: string;
146
160
  deprecated?: boolean;
147
161
  required?: Required[];
148
- variablesSchema?: VariableSchema[] | Record<VariableKey, VariableSchema>;
162
+ variablesSchema?: Record<VariableKey, VariableSchema>;
163
+ disabledVariationValue?: VariationValue;
149
164
  variations?: Variation[];
150
165
  bucketBy: BucketBy;
151
166
  traffic: Traffic[];
152
167
  force?: Force[];
153
168
  ranges?: Range[];
154
169
  }
170
+ export interface FeatureV1 {
171
+ key?: FeatureKey;
172
+ hash?: string;
173
+ deprecated?: boolean;
174
+ required?: Required[];
175
+ bucketBy: BucketBy;
176
+ traffic: Traffic[];
177
+ force?: Force[];
178
+ ranges?: Range[];
179
+ variablesSchema?: VariableSchema[];
180
+ variations?: VariationV1[];
181
+ }
155
182
  export interface DatafileContentV1 {
156
183
  schemaVersion: string;
157
184
  revision: string;
158
185
  attributes: Attribute[];
159
186
  segments: Segment[];
160
- features: Feature[];
187
+ features: FeatureV1[];
161
188
  }
162
- export interface DatafileContentV2 {
189
+ export interface DatafileContent {
163
190
  schemaVersion: string;
164
191
  revision: string;
165
- attributes: {
166
- [key: AttributeKey]: Attribute;
167
- };
168
192
  segments: {
169
193
  [key: SegmentKey]: Segment;
170
194
  };
@@ -172,23 +196,23 @@ export interface DatafileContentV2 {
172
196
  [key: FeatureKey]: Feature;
173
197
  };
174
198
  }
175
- export type DatafileContent = DatafileContentV1 | DatafileContentV2;
176
- export interface OverrideFeature {
199
+ export interface EvaluatedFeature {
177
200
  enabled: boolean;
178
201
  variation?: VariationValue;
179
202
  variables?: {
180
203
  [key: VariableKey]: VariableValue;
181
204
  };
182
205
  }
183
- export interface StickyFeatures {
184
- [key: FeatureKey]: OverrideFeature;
206
+ export interface EvaluatedFeatures {
207
+ [key: FeatureKey]: EvaluatedFeature;
185
208
  }
186
- export type InitialFeatures = StickyFeatures;
209
+ export type StickyFeatures = EvaluatedFeatures;
187
210
  /**
188
211
  * YAML-only type
189
212
  */
190
213
  export type Weight = number;
191
214
  export type EnvironmentKey = string;
215
+ export type Tag = string;
192
216
  export type RuleKey = string;
193
217
  export interface Rule {
194
218
  key: RuleKey;
@@ -200,13 +224,28 @@ export interface Rule {
200
224
  variables?: {
201
225
  [key: string]: VariableValue;
202
226
  };
227
+ variationWeights?: {
228
+ [key: string]: Weight;
229
+ };
230
+ }
231
+ export interface RulesByEnvironment {
232
+ [key: EnvironmentKey]: Rule[];
233
+ }
234
+ export interface Force {
235
+ conditions?: Condition | Condition[];
236
+ segments?: GroupSegment | GroupSegment[];
237
+ enabled?: boolean;
238
+ variation?: VariationValue;
239
+ variables?: {
240
+ [key: string]: VariableValue;
241
+ };
242
+ }
243
+ export interface ForceByEnvironment {
244
+ [key: EnvironmentKey]: Force[];
203
245
  }
204
- export type Tag = string;
205
246
  export type Expose = boolean | Tag[];
206
- export interface Environment {
207
- expose?: Expose;
208
- rules: Rule[];
209
- force?: Force[];
247
+ export interface ExposeByEnvironment {
248
+ [key: EnvironmentKey]: Expose;
210
249
  }
211
250
  export interface ParsedFeature {
212
251
  key: FeatureKey;
@@ -216,14 +255,12 @@ export interface ParsedFeature {
216
255
  tags: Tag[];
217
256
  required?: Required[];
218
257
  bucketBy: BucketBy;
219
- variablesSchema?: VariableSchema[];
258
+ disabledVariationValue?: VariationValue;
259
+ variablesSchema?: Record<VariableKey, VariableSchema>;
220
260
  variations?: Variation[];
221
- environments?: {
222
- [key: EnvironmentKey]: Environment;
223
- };
224
- expose?: Expose;
225
- rules?: Rule[];
226
- force?: Force[];
261
+ expose?: ExposeByEnvironment | Expose;
262
+ force?: ForceByEnvironment | Force[];
263
+ rules?: RulesByEnvironment | Rule[];
227
264
  }
228
265
  /**
229
266
  * For maintaining old allocations info,
@@ -231,6 +268,7 @@ export interface ParsedFeature {
231
268
  * with consistent bucketing
232
269
  */
233
270
  export interface ExistingFeature {
271
+ hash?: string;
234
272
  variations?: {
235
273
  value: VariationValue;
236
274
  weight: Weight;
@@ -238,7 +276,7 @@ export interface ExistingFeature {
238
276
  traffic: {
239
277
  key: RuleKey;
240
278
  percentage: Percentage;
241
- allocation: Allocation[];
279
+ allocation?: Allocation[];
242
280
  }[];
243
281
  ranges?: Range[];
244
282
  }
@@ -254,19 +292,48 @@ export interface ExistingState {
254
292
  export interface AssertionMatrix {
255
293
  [key: string]: AttributeValue[];
256
294
  }
295
+ export interface ExpectedEvaluations {
296
+ flag?: Record<string, any>;
297
+ variation?: Record<string, any>;
298
+ variables?: {
299
+ [key: VariableKey]: Record<string, any>;
300
+ };
301
+ }
302
+ export interface FeatureChildAssertion {
303
+ sticky?: StickyFeatures;
304
+ context?: Context;
305
+ defaultVariationValue?: VariationValue;
306
+ defaultVariableValues?: {
307
+ [key: string]: VariableValue;
308
+ };
309
+ expectedToBeEnabled?: boolean;
310
+ expectedVariation?: VariationValue;
311
+ expectedVariables?: {
312
+ [key: VariableKey]: VariableValue;
313
+ };
314
+ expectedEvaluations?: ExpectedEvaluations;
315
+ }
257
316
  export interface FeatureAssertion {
258
317
  matrix?: AssertionMatrix;
259
318
  description?: string;
260
319
  environment: EnvironmentKey;
261
- at: Weight;
262
- context: Context;
263
- expectedToBeEnabled: boolean;
320
+ at?: Weight;
321
+ sticky?: StickyFeatures;
322
+ context?: Context;
323
+ defaultVariationValue?: VariationValue;
324
+ defaultVariableValues?: {
325
+ [key: string]: VariableValue;
326
+ };
327
+ expectedToBeEnabled?: boolean;
264
328
  expectedVariation?: VariationValue;
265
329
  expectedVariables?: {
266
330
  [key: VariableKey]: VariableValue;
267
331
  };
332
+ expectedEvaluations?: ExpectedEvaluations;
333
+ children?: FeatureChildAssertion[];
268
334
  }
269
335
  export interface TestFeature {
336
+ key?: string;
270
337
  feature: FeatureKey;
271
338
  assertions: FeatureAssertion[];
272
339
  }
@@ -277,16 +344,22 @@ export interface SegmentAssertion {
277
344
  expectedToMatch: boolean;
278
345
  }
279
346
  export interface TestSegment {
347
+ key?: string;
280
348
  segment: SegmentKey;
281
349
  assertions: SegmentAssertion[];
282
350
  }
283
351
  export type Test = TestSegment | TestFeature;
284
352
  export interface TestResultAssertionError {
285
- type: "flag" | "variation" | "variable" | "segment";
353
+ type: "flag" | "variation" | "variable" | "segment" | "evaluation";
286
354
  expected: string | number | boolean | Date | null | undefined;
287
355
  actual: string | number | boolean | Date | null | undefined;
288
356
  message?: string;
289
- details?: object;
357
+ details?: {
358
+ evaluationType?: string;
359
+ evaluationKey?: string;
360
+ childIndex?: number;
361
+ [key: string]: any;
362
+ };
290
363
  }
291
364
  export interface TestResultAssertion {
292
365
  description: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featurevisor/types",
3
- "version": "1.35.0",
3
+ "version": "2.0.0",
4
4
  "description": "Common Typescript types for Featurevisor",
5
5
  "main": "dist/index.js",
6
6
  "module": "lib/index.js",
@@ -40,5 +40,5 @@
40
40
  "url": "https://github.com/featurevisor/featurevisor/issues"
41
41
  },
42
42
  "license": "MIT",
43
- "gitHead": "6c9ecabb7e39f77fc89e64395fedfc9570324fb7"
43
+ "gitHead": "9817e05a07735294c750ee921991509b67015afd"
44
44
  }
package/src/index.ts CHANGED
@@ -1,24 +1,59 @@
1
1
  export type AttributeKey = string;
2
2
 
3
- export type AttributeValue = string | number | boolean | Date | null | undefined;
3
+ export interface AttributeObjectValue {
4
+ [key: AttributeKey]: AttributeValue;
5
+ }
6
+
7
+ export type AttributeValue =
8
+ | string
9
+ | number
10
+ | boolean
11
+ | Date
12
+ | null
13
+ | undefined
14
+ | string[]
15
+ | AttributeObjectValue;
4
16
 
5
17
  export interface Context {
6
18
  [key: AttributeKey]: AttributeValue;
7
19
  }
8
20
 
9
- export type AttributeType = "boolean" | "string" | "integer" | "double" | "date" | "semver";
21
+ export type AttributeType =
22
+ | "boolean"
23
+ | "string"
24
+ | "integer"
25
+ | "double"
26
+ | "date"
27
+ | "semver"
28
+ | "object"
29
+ | "array";
10
30
 
11
31
  export interface Attribute {
12
32
  archived?: boolean; // only available in YAML files
13
- key: AttributeKey;
33
+ key?: AttributeKey; // needed for supporting v1 datafile generation
14
34
  type: AttributeType;
15
- capture?: boolean;
16
35
  description?: string; // only available in YAML files
36
+ properties?: {
37
+ [key: AttributeKey]: {
38
+ type:
39
+ | "boolean"
40
+ | "string"
41
+ | "integer"
42
+ | "double"
43
+ | "date"
44
+ | "semver"
45
+ // | "object" // NOTE: avoid nesting for now
46
+ | "array";
47
+ description?: string;
48
+ };
49
+ };
17
50
  }
18
51
 
19
52
  export type Operator =
20
53
  | "equals"
21
54
  | "notEquals"
55
+ | "exists"
56
+ | "notExists"
22
57
 
23
58
  // numeric
24
59
  | "greaterThan"
@@ -44,6 +79,14 @@ export type Operator =
44
79
  | "before"
45
80
  | "after"
46
81
 
82
+ // array of strings
83
+ | "includes"
84
+ | "notIncludes"
85
+
86
+ // regex
87
+ | "matches"
88
+ | "notMatches"
89
+
47
90
  // array of strings
48
91
  | "in"
49
92
  | "notIn";
@@ -53,7 +96,8 @@ export type ConditionValue = string | number | boolean | Date | null | undefined
53
96
  export interface PlainCondition {
54
97
  attribute: AttributeKey;
55
98
  operator: Operator;
56
- value: ConditionValue;
99
+ value?: ConditionValue; // for all operators, except for "exists" and "notExists"
100
+ regexFlags?: string; // for regex operators only (matches, notMatches)
57
101
  }
58
102
 
59
103
  export interface AndCondition {
@@ -70,14 +114,14 @@ export interface NotCondition {
70
114
 
71
115
  export type AndOrNotCondition = AndCondition | OrCondition | NotCondition;
72
116
 
73
- export type Condition = PlainCondition | AndOrNotCondition;
117
+ export type Condition = PlainCondition | AndOrNotCondition | string;
74
118
 
75
119
  export type SegmentKey = string;
76
120
 
77
121
  export interface Segment {
78
122
  archived?: boolean; // only available in YAML files
79
- key: SegmentKey;
80
- conditions: Condition | Condition[] | string; // string only when stringified for datafile
123
+ key?: SegmentKey; // needed for supporting v1 datafile generation
124
+ conditions: Condition | Condition[]; // string only when stringified for datafile
81
125
  description?: string; // only available in YAML files
82
126
  }
83
127
 
@@ -131,62 +175,56 @@ export interface VariableOverrideConditions {
131
175
  conditions: Condition | Condition[];
132
176
  }
133
177
 
134
- export interface VariableOverrideBase {
135
- value: VariableValue;
136
- }
137
-
138
178
  export type VariableOverrideSegmentsOrConditions =
139
179
  | VariableOverrideSegments
140
180
  | VariableOverrideConditions;
141
181
 
142
- // export type VariableOverride = VariableOverrideBase & VariableOverrideSegmentsOrConditions;
143
-
144
182
  export interface VariableOverride {
145
183
  value: VariableValue;
146
184
 
147
185
  // one of the below must be present in YAML files
148
- // @TODO: try with above commented out TypeScript later
149
186
  conditions?: Condition | Condition[];
150
187
  segments?: GroupSegment | GroupSegment[];
151
188
  }
152
189
 
153
- export interface Variable {
190
+ export interface VariableV1 {
154
191
  key: VariableKey;
155
192
  value: VariableValue;
156
193
  description?: string; // only available in YAML files
157
194
  overrides?: VariableOverride[];
158
195
  }
159
196
 
197
+ export interface VariationV1 {
198
+ description?: string; // only available in YAML files
199
+ value: VariationValue;
200
+ weight?: Weight; // 0 to 100 (available from parsed YAML, but not in datafile)
201
+ variables?: VariableV1[];
202
+ }
203
+
160
204
  export interface Variation {
161
205
  description?: string; // only available in YAML files
162
206
  value: VariationValue;
163
207
  weight?: Weight; // 0 to 100 (available from parsed YAML, but not in datafile)
164
- variables?: Variable[];
208
+ variables?: {
209
+ [key: VariableKey]: VariableValue;
210
+ };
211
+ variableOverrides?: {
212
+ [key: VariableKey]: VariableOverride[];
213
+ };
165
214
  }
166
215
 
167
216
  export interface VariableSchema {
168
217
  deprecated?: boolean;
169
- key: VariableKey;
218
+ key?: VariableKey; // @NOTE: remove
170
219
  type: VariableType;
171
220
  defaultValue: VariableValue;
172
221
  description?: string; // only available in YAML files
222
+ useDefaultWhenDisabled?: boolean;
223
+ disabledValue?: VariableValue;
173
224
  }
174
225
 
175
226
  export type FeatureKey = string;
176
227
 
177
- export interface Force {
178
- // one of the below must be present in YAML
179
- // @TODO: make it better with TypeScript
180
- conditions?: Condition | Condition[];
181
- segments?: GroupSegment | GroupSegment[];
182
-
183
- enabled?: boolean;
184
- variation?: VariationValue;
185
- variables?: {
186
- [key: string]: VariableValue;
187
- };
188
- }
189
-
190
228
  export interface Slot {
191
229
  feature: FeatureKey | false;
192
230
  percentage: Weight; // 0 to 100
@@ -210,7 +248,7 @@ export type Range = [Percentage, Percentage]; // 0 to 100k
210
248
 
211
249
  export interface Allocation {
212
250
  variation: VariationValue;
213
- range: Range; // @TODO: in future, turn it into `ranges`, so that Allocations with same variation do not repeat
251
+ range: Range;
214
252
  }
215
253
 
216
254
  export interface Traffic {
@@ -223,8 +261,11 @@ export interface Traffic {
223
261
  variables?: {
224
262
  [key: string]: VariableValue;
225
263
  };
264
+ variationWeights?: {
265
+ [key: string]: Weight;
266
+ };
226
267
 
227
- allocation: Allocation[];
268
+ allocation?: Allocation[];
228
269
  }
229
270
 
230
271
  export type PlainBucketBy = AttributeKey;
@@ -242,10 +283,12 @@ export interface RequiredWithVariation {
242
283
  export type Required = FeatureKey | RequiredWithVariation;
243
284
 
244
285
  export interface Feature {
245
- key: FeatureKey;
286
+ key?: FeatureKey; // needed for supporting v1 datafile generation
287
+ hash?: string;
246
288
  deprecated?: boolean;
247
289
  required?: Required[];
248
- variablesSchema?: VariableSchema[] | Record<VariableKey, VariableSchema>;
290
+ variablesSchema?: Record<VariableKey, VariableSchema>;
291
+ disabledVariationValue?: VariationValue;
249
292
  variations?: Variation[];
250
293
  bucketBy: BucketBy;
251
294
  traffic: Traffic[];
@@ -253,20 +296,31 @@ export interface Feature {
253
296
  ranges?: Range[]; // if in a Group (mutex), these are the available slot ranges
254
297
  }
255
298
 
299
+ export interface FeatureV1 {
300
+ key?: FeatureKey;
301
+ hash?: string;
302
+ deprecated?: boolean;
303
+ required?: Required[];
304
+ bucketBy: BucketBy;
305
+ traffic: Traffic[];
306
+ force?: Force[];
307
+ ranges?: Range[]; // if in a Group (mutex), these are the available slot ranges
308
+
309
+ variablesSchema?: VariableSchema[];
310
+ variations?: VariationV1[];
311
+ }
312
+
256
313
  export interface DatafileContentV1 {
257
314
  schemaVersion: string;
258
315
  revision: string;
259
316
  attributes: Attribute[];
260
317
  segments: Segment[];
261
- features: Feature[];
318
+ features: FeatureV1[];
262
319
  }
263
320
 
264
- export interface DatafileContentV2 {
321
+ export interface DatafileContent {
265
322
  schemaVersion: string;
266
323
  revision: string;
267
- attributes: {
268
- [key: AttributeKey]: Attribute;
269
- };
270
324
  segments: {
271
325
  [key: SegmentKey]: Segment;
272
326
  };
@@ -275,9 +329,7 @@ export interface DatafileContentV2 {
275
329
  };
276
330
  }
277
331
 
278
- export type DatafileContent = DatafileContentV1 | DatafileContentV2;
279
-
280
- export interface OverrideFeature {
332
+ export interface EvaluatedFeature {
281
333
  enabled: boolean;
282
334
  variation?: VariationValue;
283
335
  variables?: {
@@ -285,11 +337,11 @@ export interface OverrideFeature {
285
337
  };
286
338
  }
287
339
 
288
- export interface StickyFeatures {
289
- [key: FeatureKey]: OverrideFeature;
340
+ export interface EvaluatedFeatures {
341
+ [key: FeatureKey]: EvaluatedFeature;
290
342
  }
291
343
 
292
- export type InitialFeatures = StickyFeatures;
344
+ export type StickyFeatures = EvaluatedFeatures;
293
345
 
294
346
  /**
295
347
  * YAML-only type
@@ -298,6 +350,8 @@ export type Weight = number; // 0 to 100
298
350
 
299
351
  export type EnvironmentKey = string; // ideally "production", "staging", "testing", or "development" only
300
352
 
353
+ export type Tag = string;
354
+
301
355
  export type RuleKey = string;
302
356
 
303
357
  export interface Rule {
@@ -311,16 +365,35 @@ export interface Rule {
311
365
  variables?: {
312
366
  [key: string]: VariableValue;
313
367
  };
368
+ variationWeights?: {
369
+ [key: string]: Weight;
370
+ };
314
371
  }
315
372
 
316
- export type Tag = string;
373
+ export interface RulesByEnvironment {
374
+ [key: EnvironmentKey]: Rule[];
375
+ }
376
+
377
+ export interface Force {
378
+ // one of the below must be present in YAML
379
+ conditions?: Condition | Condition[];
380
+ segments?: GroupSegment | GroupSegment[];
381
+
382
+ enabled?: boolean;
383
+ variation?: VariationValue;
384
+ variables?: {
385
+ [key: string]: VariableValue;
386
+ };
387
+ }
388
+
389
+ export interface ForceByEnvironment {
390
+ [key: EnvironmentKey]: Force[];
391
+ }
317
392
 
318
393
  export type Expose = boolean | Tag[];
319
394
 
320
- export interface Environment {
321
- expose?: Expose;
322
- rules: Rule[];
323
- force?: Force[];
395
+ export interface ExposeByEnvironment {
396
+ [key: EnvironmentKey]: Expose;
324
397
  }
325
398
 
326
399
  export interface ParsedFeature {
@@ -336,18 +409,14 @@ export interface ParsedFeature {
336
409
 
337
410
  bucketBy: BucketBy;
338
411
 
339
- variablesSchema?: VariableSchema[];
340
- variations?: Variation[];
412
+ disabledVariationValue?: VariationValue;
341
413
 
342
- // if using environments
343
- environments?: {
344
- [key: EnvironmentKey]: Environment;
345
- };
414
+ variablesSchema?: Record<VariableKey, VariableSchema>;
415
+ variations?: Variation[];
346
416
 
347
- // if not using environments
348
- expose?: Expose;
349
- rules?: Rule[];
350
- force?: Force[];
417
+ expose?: ExposeByEnvironment | Expose;
418
+ force?: ForceByEnvironment | Force[];
419
+ rules?: RulesByEnvironment | Rule[];
351
420
  }
352
421
 
353
422
  /**
@@ -356,16 +425,15 @@ export interface ParsedFeature {
356
425
  * with consistent bucketing
357
426
  */
358
427
  export interface ExistingFeature {
428
+ hash?: string;
359
429
  variations?: {
360
- // @TODO: use Exclude with Variation?
361
430
  value: VariationValue;
362
431
  weight: Weight;
363
432
  }[];
364
433
  traffic: {
365
- // @TODO: use Exclude with Traffic?
366
434
  key: RuleKey;
367
435
  percentage: Percentage;
368
- allocation: Allocation[];
436
+ allocation?: Allocation[];
369
437
  }[];
370
438
  ranges?: Range[]; // if in a Group (mutex), these are the available slot ranges
371
439
  }
@@ -385,20 +453,57 @@ export interface AssertionMatrix {
385
453
  [key: string]: AttributeValue[];
386
454
  }
387
455
 
456
+ export interface ExpectedEvaluations {
457
+ flag?: Record<string, any>;
458
+ variation?: Record<string, any>;
459
+ variables?: {
460
+ [key: VariableKey]: Record<string, any>;
461
+ };
462
+ }
463
+
464
+ export interface FeatureChildAssertion {
465
+ sticky?: StickyFeatures;
466
+ context?: Context;
467
+
468
+ defaultVariationValue?: VariationValue;
469
+ defaultVariableValues?: {
470
+ [key: string]: VariableValue;
471
+ };
472
+
473
+ expectedToBeEnabled?: boolean;
474
+ expectedVariation?: VariationValue;
475
+ expectedVariables?: {
476
+ [key: VariableKey]: VariableValue;
477
+ };
478
+ expectedEvaluations?: ExpectedEvaluations;
479
+ }
480
+
388
481
  export interface FeatureAssertion {
389
482
  matrix?: AssertionMatrix;
390
483
  description?: string;
391
484
  environment: EnvironmentKey;
392
- at: Weight; // bucket weight: 0 to 100
393
- context: Context;
394
- expectedToBeEnabled: boolean;
485
+ at?: Weight; // bucket weight: 0 to 100
486
+
487
+ sticky?: StickyFeatures;
488
+ context?: Context;
489
+
490
+ defaultVariationValue?: VariationValue;
491
+ defaultVariableValues?: {
492
+ [key: string]: VariableValue;
493
+ };
494
+
495
+ expectedToBeEnabled?: boolean;
395
496
  expectedVariation?: VariationValue;
396
497
  expectedVariables?: {
397
498
  [key: VariableKey]: VariableValue;
398
499
  };
500
+ expectedEvaluations?: ExpectedEvaluations;
501
+
502
+ children?: FeatureChildAssertion[];
399
503
  }
400
504
 
401
505
  export interface TestFeature {
506
+ key?: string; // file path
402
507
  feature: FeatureKey;
403
508
  assertions: FeatureAssertion[];
404
509
  }
@@ -411,6 +516,7 @@ export interface SegmentAssertion {
411
516
  }
412
517
 
413
518
  export interface TestSegment {
519
+ key?: string; // file path
414
520
  segment: SegmentKey;
415
521
  assertions: SegmentAssertion[];
416
522
  }
@@ -418,11 +524,16 @@ export interface TestSegment {
418
524
  export type Test = TestSegment | TestFeature;
419
525
 
420
526
  export interface TestResultAssertionError {
421
- type: "flag" | "variation" | "variable" | "segment";
527
+ type: "flag" | "variation" | "variable" | "segment" | "evaluation";
422
528
  expected: string | number | boolean | Date | null | undefined;
423
529
  actual: string | number | boolean | Date | null | undefined;
424
530
  message?: string;
425
- details?: object;
531
+ details?: {
532
+ evaluationType?: string; // e.g., "flag", "variation", "variable"
533
+ evaluationKey?: string; // e.g., "myFeatureKey", "myVariableKey"
534
+ childIndex?: number; // for children assertions
535
+ [key: string]: any;
536
+ };
426
537
  }
427
538
 
428
539
  export interface TestResultAssertion {