@featurevisor/core 1.13.0 → 1.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.
Files changed (31) hide show
  1. package/.eslintcache +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/coverage/clover.xml +2 -2
  4. package/coverage/lcov-report/index.html +1 -1
  5. package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
  6. package/coverage/lcov-report/lib/builder/index.html +1 -1
  7. package/coverage/lcov-report/lib/builder/revision.js.html +1 -1
  8. package/coverage/lcov-report/lib/builder/traffic.js.html +1 -1
  9. package/coverage/lcov-report/lib/tester/checkIfObjectsAreEqual.js.html +1 -1
  10. package/coverage/lcov-report/lib/tester/index.html +1 -1
  11. package/coverage/lcov-report/lib/tester/matrix.js.html +1 -1
  12. package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
  13. package/coverage/lcov-report/src/builder/index.html +1 -1
  14. package/coverage/lcov-report/src/builder/revision.ts.html +1 -1
  15. package/coverage/lcov-report/src/builder/traffic.ts.html +1 -1
  16. package/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +1 -1
  17. package/coverage/lcov-report/src/tester/index.html +1 -1
  18. package/coverage/lcov-report/src/tester/matrix.ts.html +1 -1
  19. package/lib/benchmark/index.d.ts +18 -0
  20. package/lib/benchmark/index.js +139 -0
  21. package/lib/benchmark/index.js.map +1 -0
  22. package/lib/find-usage/index.d.ts +14 -0
  23. package/lib/find-usage/index.js +164 -136
  24. package/lib/find-usage/index.js.map +1 -1
  25. package/lib/index.d.ts +1 -0
  26. package/lib/index.js +1 -0
  27. package/lib/index.js.map +1 -1
  28. package/package.json +2 -2
  29. package/src/benchmark/index.ts +150 -0
  30. package/src/find-usage/index.ts +131 -76
  31. package/src/index.ts +1 -0
@@ -6,19 +6,49 @@ import {
6
6
  extractSegmentKeysFromGroupSegments,
7
7
  } from "../utils/extractKeys";
8
8
 
9
- export async function findSegmentUsage(
10
- deps: Dependencies,
11
- segmentKey: SegmentKey,
12
- ): Promise<Set<FeatureKey>> {
9
+ export interface UsageInFeatures {
10
+ [featureKey: string]: {
11
+ features: Set<FeatureKey>;
12
+ segments: Set<SegmentKey>;
13
+ attributes: Set<AttributeKey>;
14
+ };
15
+ }
16
+
17
+ export async function findAllUsageInFeatures(deps: Dependencies): Promise<UsageInFeatures> {
13
18
  const { datasource, projectConfig } = deps;
19
+ const usageInFeatures: UsageInFeatures = {};
14
20
 
15
21
  const featureKeys = await datasource.listFeatures();
16
22
 
17
- const usedInFeatures = new Set<FeatureKey>();
18
-
19
23
  for (const featureKey of featureKeys) {
20
24
  const feature = await datasource.readFeature(featureKey);
21
- const segmentKeys = new Set<SegmentKey>();
25
+ usageInFeatures[featureKey] = {
26
+ features: new Set<FeatureKey>(),
27
+ segments: new Set<SegmentKey>(),
28
+ attributes: new Set<AttributeKey>(),
29
+ };
30
+
31
+ // required
32
+ if (feature.required) {
33
+ feature.required.forEach((required) => {
34
+ if (typeof required === "string") {
35
+ usageInFeatures[featureKey].features.add(required);
36
+ } else if (typeof required === "object" && required.key) {
37
+ usageInFeatures[featureKey].features.add(required.key);
38
+ }
39
+ });
40
+ }
41
+
42
+ // bucketBy
43
+ if (feature.bucketBy) {
44
+ if (typeof feature.bucketBy === "string") {
45
+ usageInFeatures[featureKey].attributes.add(feature.bucketBy);
46
+ } else if (Array.isArray(feature.bucketBy)) {
47
+ feature.bucketBy.forEach((b) => usageInFeatures[featureKey].attributes.add(b));
48
+ } else if (typeof feature.bucketBy === "object" && feature.bucketBy.or) {
49
+ feature.bucketBy.or.forEach((b) => usageInFeatures[featureKey].attributes.add(b));
50
+ }
51
+ }
22
52
 
23
53
  // variable overrides inside variations
24
54
  projectConfig.environments.forEach((environment) => {
@@ -30,7 +60,13 @@ export async function findSegmentUsage(
30
60
  variable.overrides.forEach((override) => {
31
61
  if (override.segments) {
32
62
  extractSegmentKeysFromGroupSegments(override.segments).forEach((segmentKey) =>
33
- segmentKeys.add(segmentKey),
63
+ usageInFeatures[featureKey].segments.add(segmentKey),
64
+ );
65
+ }
66
+
67
+ if (override.conditions) {
68
+ extractAttributeKeysFromConditions(override.conditions).forEach(
69
+ (attributeKey) => usageInFeatures[featureKey].attributes.add(attributeKey),
34
70
  );
35
71
  }
36
72
  });
@@ -45,7 +81,13 @@ export async function findSegmentUsage(
45
81
  feature.environments[environment].force?.forEach((force) => {
46
82
  if (force.segments) {
47
83
  extractSegmentKeysFromGroupSegments(force.segments).forEach((segmentKey) =>
48
- segmentKeys.add(segmentKey),
84
+ usageInFeatures[featureKey].segments.add(segmentKey),
85
+ );
86
+ }
87
+
88
+ if (force.conditions) {
89
+ extractAttributeKeysFromConditions(force.conditions).forEach((attributeKey) =>
90
+ usageInFeatures[featureKey].attributes.add(attributeKey),
49
91
  );
50
92
  }
51
93
  });
@@ -55,13 +97,54 @@ export async function findSegmentUsage(
55
97
  if (feature.environments[environment].rules) {
56
98
  feature.environments[environment].rules?.forEach((rule) => {
57
99
  extractSegmentKeysFromGroupSegments(rule.segments).forEach((segmentKey) =>
58
- segmentKeys.add(segmentKey),
100
+ usageInFeatures[featureKey].segments.add(segmentKey),
59
101
  );
60
102
  });
61
103
  }
62
104
  });
105
+ }
63
106
 
64
- if (segmentKeys.has(segmentKey)) {
107
+ return usageInFeatures;
108
+ }
109
+
110
+ export interface UsageInSegments {
111
+ [segmentKey: string]: {
112
+ attributes: Set<AttributeKey>;
113
+ };
114
+ }
115
+
116
+ export async function findAllUsageInSegments(deps: Dependencies): Promise<UsageInSegments> {
117
+ const { datasource } = deps;
118
+ const usageInSegments: UsageInSegments = {};
119
+
120
+ const segmentKeys = await datasource.listSegments();
121
+
122
+ for (const segmentKey of segmentKeys) {
123
+ const segment = await datasource.readSegment(segmentKey);
124
+ usageInSegments[segmentKey] = {
125
+ attributes: new Set<AttributeKey>(),
126
+ };
127
+
128
+ extractAttributeKeysFromConditions(segment.conditions as Condition | Condition[]).forEach(
129
+ (attributeKey) => {
130
+ usageInSegments[segmentKey].attributes.add(attributeKey);
131
+ },
132
+ );
133
+ }
134
+
135
+ return usageInSegments;
136
+ }
137
+
138
+ export async function findSegmentUsage(
139
+ deps: Dependencies,
140
+ segmentKey: SegmentKey,
141
+ ): Promise<Set<FeatureKey>> {
142
+ const usedInFeatures = new Set<FeatureKey>();
143
+
144
+ const usageInFeatures = await findAllUsageInFeatures(deps);
145
+
146
+ for (const featureKey in usageInFeatures) {
147
+ if (usageInFeatures[featureKey].segments.has(segmentKey)) {
65
148
  usedInFeatures.add(featureKey);
66
149
  }
67
150
  }
@@ -78,69 +161,22 @@ export async function findAttributeUsage(
78
161
  deps: Dependencies,
79
162
  attributeKey: AttributeKey,
80
163
  ): Promise<AttributeUsage> {
81
- const { datasource, projectConfig } = deps;
82
-
83
164
  const usedIn: AttributeUsage = {
84
165
  features: new Set<FeatureKey>(),
85
166
  segments: new Set<SegmentKey>(),
86
167
  };
87
168
 
88
- // features
89
- const featureKeys = await datasource.listFeatures();
90
- for (const featureKey of featureKeys) {
91
- const feature = await datasource.readFeature(featureKey);
92
- const attributeKeys = new Set<AttributeKey>();
169
+ const usageInFeatures = await findAllUsageInFeatures(deps);
170
+ const usageInSegments = await findAllUsageInSegments(deps);
93
171
 
94
- // variable overrides inside variations
95
- projectConfig.environments.forEach((environment) => {
96
- if (feature.variations) {
97
- feature.variations.forEach((variation) => {
98
- if (variation.variables) {
99
- variation.variables.forEach((variable) => {
100
- if (variable.overrides) {
101
- variable.overrides.forEach((override) => {
102
- if (override.conditions) {
103
- extractAttributeKeysFromConditions(override.conditions).forEach(
104
- (attributeKey) => attributeKeys.add(attributeKey),
105
- );
106
- }
107
- });
108
- }
109
- });
110
- }
111
- });
112
- }
113
-
114
- // force
115
- if (feature.environments[environment].force) {
116
- feature.environments[environment].force?.forEach((force) => {
117
- if (force.conditions) {
118
- extractAttributeKeysFromConditions(force.conditions).forEach((attributeKey) =>
119
- attributeKeys.add(attributeKey),
120
- );
121
- }
122
- });
123
- }
124
- });
125
-
126
- if (attributeKeys.has(attributeKey)) {
172
+ for (const featureKey in usageInFeatures) {
173
+ if (usageInFeatures[featureKey].attributes.has(attributeKey)) {
127
174
  usedIn.features.add(featureKey);
128
175
  }
129
176
  }
130
177
 
131
- // segments
132
- const segmentKeys = await datasource.listSegments();
133
- for (const segmentKey of segmentKeys) {
134
- const segment = await datasource.readSegment(segmentKey);
135
- const attributeKeys = new Set<AttributeKey>();
136
-
137
- extractAttributeKeysFromConditions(segment.conditions as Condition | Condition[]).forEach(
138
- (attributeKey) => {
139
- attributeKeys.add(attributeKey);
140
- },
141
- );
142
-
143
- if (attributeKeys.has(attributeKey)) {
178
+ for (const segmentKey in usageInSegments) {
179
+ if (usageInSegments[segmentKey].attributes.has(attributeKey)) {
144
180
  usedIn.segments.add(segmentKey);
145
181
  }
146
182
  }
@@ -150,34 +186,53 @@ export async function findAttributeUsage(
150
186
 
151
187
  export async function findUnusedSegments(deps: Dependencies): Promise<Set<SegmentKey>> {
152
188
  const { datasource } = deps;
153
-
154
- const segmentKeys = await datasource.listSegments();
155
189
  const unusedSegments = new Set<SegmentKey>();
156
190
 
157
- for (const segmentKey of segmentKeys) {
158
- const usedInFeatures = await findSegmentUsage(deps, segmentKey);
191
+ const allSegmentKeys = await datasource.listSegments();
192
+ const usageInFeatures = await findAllUsageInFeatures(deps);
193
+ const usedSegmentKeys = new Set<SegmentKey>();
159
194
 
160
- if (usedInFeatures.size === 0) {
195
+ for (const featureKey in usageInFeatures) {
196
+ usageInFeatures[featureKey].segments.forEach((segmentKey) => {
197
+ usedSegmentKeys.add(segmentKey);
198
+ });
199
+ }
200
+
201
+ allSegmentKeys.forEach((segmentKey) => {
202
+ if (!usedSegmentKeys.has(segmentKey)) {
161
203
  unusedSegments.add(segmentKey);
162
204
  }
163
- }
205
+ });
164
206
 
165
207
  return unusedSegments;
166
208
  }
167
209
 
168
210
  export async function findUnusedAttributes(deps: Dependencies): Promise<Set<AttributeKey>> {
169
211
  const { datasource } = deps;
170
-
171
- const attributeKeys = await datasource.listAttributes();
172
212
  const unusedAttributes = new Set<AttributeKey>();
173
213
 
174
- for (const attributeKey of attributeKeys) {
175
- const usedIn = await findAttributeUsage(deps, attributeKey);
214
+ const allAttributeKeys = await datasource.listAttributes();
215
+ const usageInFeatures = await findAllUsageInFeatures(deps);
216
+ const usageInSegments = await findAllUsageInSegments(deps);
217
+ const usedAttributeKeys = new Set<AttributeKey>();
176
218
 
177
- if (usedIn.features.size === 0 && usedIn.segments.size === 0) {
219
+ for (const featureKey in usageInFeatures) {
220
+ usageInFeatures[featureKey].attributes.forEach((attributeKey) => {
221
+ usedAttributeKeys.add(attributeKey);
222
+ });
223
+ }
224
+
225
+ for (const segmentKey in usageInSegments) {
226
+ usageInSegments[segmentKey].attributes.forEach((attributeKey) => {
227
+ usedAttributeKeys.add(attributeKey);
228
+ });
229
+ }
230
+
231
+ allAttributeKeys.forEach((attributeKey) => {
232
+ if (!usedAttributeKeys.has(attributeKey)) {
178
233
  unusedAttributes.add(attributeKey);
179
234
  }
180
- }
235
+ });
181
236
 
182
237
  return unusedAttributes;
183
238
  }
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from "./find-duplicate-segments";
10
10
  export * from "./find-usage";
11
11
  export * from "./dependencies";
12
12
  export * from "./datasource";
13
+ export * from "./benchmark";