@featurevisor/core 1.11.1 → 1.13.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 (40) hide show
  1. package/.eslintcache +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/LICENSE +1 -1
  4. package/coverage/clover.xml +69 -69
  5. package/coverage/coverage-final.json +2 -2
  6. package/coverage/lcov-report/index.html +7 -7
  7. package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
  8. package/coverage/lcov-report/lib/builder/index.html +5 -5
  9. package/coverage/lcov-report/lib/builder/revision.js.html +1 -1
  10. package/coverage/lcov-report/lib/builder/traffic.js.html +5 -11
  11. package/coverage/lcov-report/lib/tester/checkIfObjectsAreEqual.js.html +1 -1
  12. package/coverage/lcov-report/lib/tester/index.html +1 -1
  13. package/coverage/lcov-report/lib/tester/matrix.js.html +1 -1
  14. package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
  15. package/coverage/lcov-report/src/builder/index.html +5 -5
  16. package/coverage/lcov-report/src/builder/revision.ts.html +1 -1
  17. package/coverage/lcov-report/src/builder/traffic.ts.html +6 -15
  18. package/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +1 -1
  19. package/coverage/lcov-report/src/tester/index.html +1 -1
  20. package/coverage/lcov-report/src/tester/matrix.ts.html +1 -1
  21. package/coverage/lcov.info +113 -117
  22. package/lib/builder/buildDatafile.js +23 -6
  23. package/lib/builder/buildDatafile.js.map +1 -1
  24. package/lib/builder/traffic.js +1 -3
  25. package/lib/builder/traffic.js.map +1 -1
  26. package/lib/config/projectConfig.d.ts +1 -0
  27. package/lib/config/projectConfig.js +3 -1
  28. package/lib/config/projectConfig.js.map +1 -1
  29. package/lib/find-usage/index.d.ts +17 -0
  30. package/lib/find-usage/index.js +379 -0
  31. package/lib/find-usage/index.js.map +1 -0
  32. package/lib/index.d.ts +1 -0
  33. package/lib/index.js +1 -0
  34. package/lib/index.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/builder/buildDatafile.ts +16 -6
  37. package/src/builder/traffic.ts +2 -5
  38. package/src/config/projectConfig.ts +4 -1
  39. package/src/find-usage/index.ts +279 -0
  40. package/src/index.ts +1 -0
@@ -0,0 +1,279 @@
1
+ import { Condition, FeatureKey, SegmentKey, AttributeKey } from "@featurevisor/types";
2
+
3
+ import { Dependencies } from "../dependencies";
4
+ import {
5
+ extractAttributeKeysFromConditions,
6
+ extractSegmentKeysFromGroupSegments,
7
+ } from "../utils/extractKeys";
8
+
9
+ export async function findSegmentUsage(
10
+ deps: Dependencies,
11
+ segmentKey: SegmentKey,
12
+ ): Promise<Set<FeatureKey>> {
13
+ const { datasource, projectConfig } = deps;
14
+
15
+ const featureKeys = await datasource.listFeatures();
16
+
17
+ const usedInFeatures = new Set<FeatureKey>();
18
+
19
+ for (const featureKey of featureKeys) {
20
+ const feature = await datasource.readFeature(featureKey);
21
+ const segmentKeys = new Set<SegmentKey>();
22
+
23
+ // variable overrides inside variations
24
+ projectConfig.environments.forEach((environment) => {
25
+ if (feature.variations) {
26
+ feature.variations.forEach((variation) => {
27
+ if (variation.variables) {
28
+ variation.variables.forEach((variable) => {
29
+ if (variable.overrides) {
30
+ variable.overrides.forEach((override) => {
31
+ if (override.segments) {
32
+ extractSegmentKeysFromGroupSegments(override.segments).forEach((segmentKey) =>
33
+ segmentKeys.add(segmentKey),
34
+ );
35
+ }
36
+ });
37
+ }
38
+ });
39
+ }
40
+ });
41
+ }
42
+
43
+ // force
44
+ if (feature.environments[environment].force) {
45
+ feature.environments[environment].force?.forEach((force) => {
46
+ if (force.segments) {
47
+ extractSegmentKeysFromGroupSegments(force.segments).forEach((segmentKey) =>
48
+ segmentKeys.add(segmentKey),
49
+ );
50
+ }
51
+ });
52
+ }
53
+
54
+ // rules
55
+ if (feature.environments[environment].rules) {
56
+ feature.environments[environment].rules?.forEach((rule) => {
57
+ extractSegmentKeysFromGroupSegments(rule.segments).forEach((segmentKey) =>
58
+ segmentKeys.add(segmentKey),
59
+ );
60
+ });
61
+ }
62
+ });
63
+
64
+ if (segmentKeys.has(segmentKey)) {
65
+ usedInFeatures.add(featureKey);
66
+ }
67
+ }
68
+
69
+ return usedInFeatures;
70
+ }
71
+
72
+ export interface AttributeUsage {
73
+ features: Set<FeatureKey>;
74
+ segments: Set<SegmentKey>;
75
+ }
76
+
77
+ export async function findAttributeUsage(
78
+ deps: Dependencies,
79
+ attributeKey: AttributeKey,
80
+ ): Promise<AttributeUsage> {
81
+ const { datasource, projectConfig } = deps;
82
+
83
+ const usedIn: AttributeUsage = {
84
+ features: new Set<FeatureKey>(),
85
+ segments: new Set<SegmentKey>(),
86
+ };
87
+
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>();
93
+
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)) {
127
+ usedIn.features.add(featureKey);
128
+ }
129
+ }
130
+
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)) {
144
+ usedIn.segments.add(segmentKey);
145
+ }
146
+ }
147
+
148
+ return usedIn;
149
+ }
150
+
151
+ export async function findUnusedSegments(deps: Dependencies): Promise<Set<SegmentKey>> {
152
+ const { datasource } = deps;
153
+
154
+ const segmentKeys = await datasource.listSegments();
155
+ const unusedSegments = new Set<SegmentKey>();
156
+
157
+ for (const segmentKey of segmentKeys) {
158
+ const usedInFeatures = await findSegmentUsage(deps, segmentKey);
159
+
160
+ if (usedInFeatures.size === 0) {
161
+ unusedSegments.add(segmentKey);
162
+ }
163
+ }
164
+
165
+ return unusedSegments;
166
+ }
167
+
168
+ export async function findUnusedAttributes(deps: Dependencies): Promise<Set<AttributeKey>> {
169
+ const { datasource } = deps;
170
+
171
+ const attributeKeys = await datasource.listAttributes();
172
+ const unusedAttributes = new Set<AttributeKey>();
173
+
174
+ for (const attributeKey of attributeKeys) {
175
+ const usedIn = await findAttributeUsage(deps, attributeKey);
176
+
177
+ if (usedIn.features.size === 0 && usedIn.segments.size === 0) {
178
+ unusedAttributes.add(attributeKey);
179
+ }
180
+ }
181
+
182
+ return unusedAttributes;
183
+ }
184
+
185
+ export interface FindUsageOptions {
186
+ segment?: string;
187
+ attribute?: string;
188
+
189
+ unusedSegments?: boolean;
190
+ unusedAttributes?: boolean;
191
+ }
192
+
193
+ export async function findUsageInProject(deps: Dependencies, options: FindUsageOptions) {
194
+ console.log("");
195
+
196
+ // segment
197
+ if (options.segment) {
198
+ const usedInFeatures = await findSegmentUsage(deps, options.segment);
199
+
200
+ if (usedInFeatures.size === 0) {
201
+ console.log(`Segment "${options.segment}" is not used in any features.`);
202
+ } else {
203
+ console.log(`Segment "${options.segment}" is used in the following features:\n`);
204
+
205
+ usedInFeatures.forEach((featureKey) => {
206
+ console.log(` - ${featureKey}`);
207
+ });
208
+ }
209
+
210
+ return;
211
+ }
212
+
213
+ // attribute
214
+ if (options.attribute) {
215
+ const usedIn = await findAttributeUsage(deps, options.attribute);
216
+
217
+ if (usedIn.features.size === 0 && usedIn.segments.size === 0) {
218
+ console.log(`Attribute "${options.attribute}" is not used in any features or segments.`);
219
+
220
+ return;
221
+ }
222
+
223
+ if (usedIn.features.size > 0) {
224
+ console.log(`Attribute "${options.attribute}" is used in the following features:\n`);
225
+
226
+ usedIn.features.forEach((featureKey) => {
227
+ console.log(` - ${featureKey}`);
228
+ });
229
+
230
+ console.log("");
231
+ }
232
+
233
+ if (usedIn.segments.size > 0) {
234
+ console.log(`Attribute "${options.attribute}" is used in the following segments:\n`);
235
+
236
+ usedIn.segments.forEach((segmentKey) => {
237
+ console.log(` - ${segmentKey}`);
238
+ });
239
+ }
240
+
241
+ return;
242
+ }
243
+
244
+ // unused segments
245
+ if (options.unusedSegments) {
246
+ const unusedSegments = await findUnusedSegments(deps);
247
+
248
+ if (unusedSegments.size === 0) {
249
+ console.log("No unused segments found.");
250
+ } else {
251
+ console.log("Unused segments:\n");
252
+
253
+ unusedSegments.forEach((segmentKey) => {
254
+ console.log(` - ${segmentKey}`);
255
+ });
256
+ }
257
+
258
+ return;
259
+ }
260
+
261
+ // unused attributes
262
+ if (options.unusedAttributes) {
263
+ const unusedAttributes = await findUnusedAttributes(deps);
264
+
265
+ if (unusedAttributes.size === 0) {
266
+ console.log("No unused attributes found.");
267
+ } else {
268
+ console.log("Unused attributes:\n");
269
+
270
+ unusedAttributes.forEach((attributeKey) => {
271
+ console.log(` - ${attributeKey}`);
272
+ });
273
+ }
274
+
275
+ return;
276
+ }
277
+
278
+ console.log("Please specify a segment or attribute.");
279
+ }
package/src/index.ts CHANGED
@@ -7,5 +7,6 @@ export * from "./site";
7
7
  export * from "./generate-code";
8
8
  export * from "./restore";
9
9
  export * from "./find-duplicate-segments";
10
+ export * from "./find-usage";
10
11
  export * from "./dependencies";
11
12
  export * from "./datasource";