@featurevisor/core 0.53.2 → 0.53.4

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 (84) hide show
  1. package/.eslintcache +1 -1
  2. package/CHANGELOG.md +16 -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/traffic.js.html +1 -1
  8. package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
  9. package/coverage/lcov-report/src/builder/index.html +1 -1
  10. package/coverage/lcov-report/src/builder/traffic.ts.html +1 -1
  11. package/lib/builder/buildDatafile.d.ts +1 -1
  12. package/lib/builder/buildDatafile.js +241 -168
  13. package/lib/builder/buildDatafile.js.map +1 -1
  14. package/lib/builder/buildProject.d.ts +1 -2
  15. package/lib/builder/buildProject.js +95 -45
  16. package/lib/builder/buildProject.js.map +1 -1
  17. package/lib/builder/getFeatureRanges.d.ts +1 -1
  18. package/lib/builder/getFeatureRanges.js +92 -31
  19. package/lib/builder/getFeatureRanges.js.map +1 -1
  20. package/lib/datasource/datasource.d.ts +23 -17
  21. package/lib/datasource/datasource.js +166 -69
  22. package/lib/datasource/datasource.js.map +1 -1
  23. package/lib/datasource/parsers.js +2 -2
  24. package/lib/datasource/parsers.js.map +1 -1
  25. package/lib/find-duplicate-segments/findDuplicateSegments.d.ts +1 -1
  26. package/lib/find-duplicate-segments/findDuplicateSegments.js +75 -18
  27. package/lib/find-duplicate-segments/findDuplicateSegments.js.map +1 -1
  28. package/lib/find-duplicate-segments/index.d.ts +1 -1
  29. package/lib/find-duplicate-segments/index.js +56 -9
  30. package/lib/find-duplicate-segments/index.js.map +1 -1
  31. package/lib/generate-code/index.d.ts +1 -1
  32. package/lib/generate-code/index.js +67 -23
  33. package/lib/generate-code/index.js.map +1 -1
  34. package/lib/generate-code/typescript.d.ts +1 -1
  35. package/lib/generate-code/typescript.js +139 -72
  36. package/lib/generate-code/typescript.js.map +1 -1
  37. package/lib/linter/checkCircularDependency.d.ts +1 -1
  38. package/lib/linter/checkCircularDependency.js +78 -22
  39. package/lib/linter/checkCircularDependency.js.map +1 -1
  40. package/lib/linter/groupSchema.js +79 -28
  41. package/lib/linter/groupSchema.js.map +1 -1
  42. package/lib/linter/lintProject.js +119 -103
  43. package/lib/linter/lintProject.js.map +1 -1
  44. package/lib/restore.d.ts +1 -1
  45. package/lib/restore.js +53 -11
  46. package/lib/restore.js.map +1 -1
  47. package/lib/site/exportSite.d.ts +1 -1
  48. package/lib/site/exportSite.js +64 -21
  49. package/lib/site/exportSite.js.map +1 -1
  50. package/lib/site/generateSiteSearchIndex.d.ts +1 -1
  51. package/lib/site/generateSiteSearchIndex.js +203 -111
  52. package/lib/site/generateSiteSearchIndex.js.map +1 -1
  53. package/lib/tester/testFeature.d.ts +1 -1
  54. package/lib/tester/testFeature.js +130 -60
  55. package/lib/tester/testFeature.js.map +1 -1
  56. package/lib/tester/testProject.d.ts +1 -1
  57. package/lib/tester/testProject.js +105 -48
  58. package/lib/tester/testProject.js.map +1 -1
  59. package/lib/tester/testSegment.d.ts +1 -1
  60. package/lib/tester/testSegment.js +69 -21
  61. package/lib/tester/testSegment.js.map +1 -1
  62. package/lib/utils.d.ts +0 -2
  63. package/lib/utils.js +1 -15
  64. package/lib/utils.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/builder/buildDatafile.ts +9 -9
  67. package/src/builder/buildProject.ts +4 -24
  68. package/src/builder/getFeatureRanges.ts +4 -4
  69. package/src/datasource/datasource.ts +66 -60
  70. package/src/datasource/parsers.ts +2 -2
  71. package/src/find-duplicate-segments/findDuplicateSegments.ts +9 -6
  72. package/src/find-duplicate-segments/index.ts +5 -2
  73. package/src/generate-code/index.ts +2 -2
  74. package/src/generate-code/typescript.ts +42 -25
  75. package/src/linter/checkCircularDependency.ts +4 -4
  76. package/src/linter/groupSchema.ts +3 -3
  77. package/src/linter/lintProject.ts +53 -60
  78. package/src/restore.ts +1 -1
  79. package/src/site/exportSite.ts +2 -2
  80. package/src/site/generateSiteSearchIndex.ts +14 -14
  81. package/src/tester/testFeature.ts +12 -13
  82. package/src/tester/testProject.ts +8 -5
  83. package/src/tester/testSegment.ts +3 -3
  84. package/src/utils.ts +0 -18
@@ -1,6 +1,4 @@
1
1
  // for use in node only
2
- import * as fs from "fs";
3
-
4
2
  import * as Joi from "joi";
5
3
 
6
4
  import { Datasource } from "../datasource";
@@ -25,13 +23,13 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
25
23
  const availableFeatureKeys: string[] = [];
26
24
 
27
25
  // lint attributes
28
- const attributes = datasource.listAttributes();
26
+ const attributes = await datasource.listAttributes();
29
27
  console.log(`Linting ${attributes.length} attributes...\n`);
30
28
 
31
29
  const attributeJoiSchema = getAttributeJoiSchema();
32
30
 
33
31
  for (const key of attributes) {
34
- const parsed = datasource.readAttribute(key);
32
+ const parsed = await datasource.readAttribute(key);
35
33
  availableAttributeKeys.push(key);
36
34
 
37
35
  try {
@@ -50,14 +48,14 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
50
48
  }
51
49
 
52
50
  // lint segments
53
- const segments = datasource.listSegments();
51
+ const segments = await datasource.listSegments();
54
52
  console.log(`\nLinting ${segments.length} segments...\n`);
55
53
 
56
54
  const conditionsJoiSchema = getConditionsJoiSchema(projectConfig, availableAttributeKeys);
57
55
  const segmentJoiSchema = getSegmentJoiSchema(projectConfig, conditionsJoiSchema);
58
56
 
59
57
  for (const key of segments) {
60
- const parsed = datasource.readSegment(key);
58
+ const parsed = await datasource.readSegment(key);
61
59
  availableSegmentKeys.push(key);
62
60
 
63
61
  try {
@@ -75,38 +73,8 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
75
73
  }
76
74
  }
77
75
 
78
- // lint groups
79
-
80
- if (fs.existsSync(projectConfig.groupsDirectoryPath)) {
81
- const groups = datasource.listGroups();
82
- console.log(`\nLinting ${groups.length} groups...\n`);
83
-
84
- // @TODO: feature it slots can be from availableFeatureKeys only
85
- const groupJoiSchema = getGroupJoiSchema(projectConfig, datasource, availableFeatureKeys);
86
-
87
- for (const key of groups) {
88
- const parsed = datasource.readGroup(key);
89
-
90
- try {
91
- await groupJoiSchema.validateAsync(parsed);
92
- } catch (e) {
93
- console.log(" =>", key);
94
-
95
- if (e instanceof Joi.ValidationError) {
96
- printJoiError(e);
97
- } else {
98
- console.log(e);
99
- }
100
-
101
- hasError = true;
102
- }
103
- }
104
- }
105
-
106
- // @TODO: feature cannot exist in multiple groups
107
-
108
76
  // lint features
109
- const features = datasource.listFeatures();
77
+ const features = await datasource.listFeatures();
110
78
  console.log(`\nLinting ${features.length} features...\n`);
111
79
 
112
80
  const featureJoiSchema = getFeatureJoiSchema(
@@ -117,7 +85,7 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
117
85
  );
118
86
 
119
87
  for (const key of features) {
120
- const parsed = datasource.readFeature(key);
88
+ const parsed = await datasource.readFeature(key);
121
89
  availableFeatureKeys.push(key);
122
90
 
123
91
  try {
@@ -136,7 +104,7 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
136
104
 
137
105
  if (parsed.required) {
138
106
  try {
139
- checkForCircularDependencyInRequired(datasource, key, parsed.required);
107
+ await checkForCircularDependencyInRequired(datasource, key, parsed.required);
140
108
  } catch (e) {
141
109
  console.log(" =>", key);
142
110
  console.log(" => Error:", e.message);
@@ -145,33 +113,58 @@ export async function lintProject(projectConfig: ProjectConfig): Promise<boolean
145
113
  }
146
114
  }
147
115
 
148
- // lint tests
149
- if (fs.existsSync(projectConfig.testsDirectoryPath)) {
150
- const tests = datasource.listTests();
151
- console.log(`\nLinting ${tests.length} tests...\n`);
116
+ // lint groups
117
+ const groups = await datasource.listGroups();
118
+ console.log(`\nLinting ${groups.length} groups...\n`);
152
119
 
153
- const testsJoiSchema = getTestsJoiSchema(
154
- projectConfig,
155
- availableFeatureKeys,
156
- availableSegmentKeys,
157
- );
120
+ // @TODO: feature it slots can be from availableFeatureKeys only
121
+ const groupJoiSchema = getGroupJoiSchema(projectConfig, datasource, availableFeatureKeys);
158
122
 
159
- for (const key of tests) {
160
- const parsed = datasource.readTest(key);
123
+ for (const key of groups) {
124
+ const parsed = await datasource.readGroup(key);
161
125
 
162
- try {
163
- await testsJoiSchema.validateAsync(parsed);
164
- } catch (e) {
165
- console.log(" =>", key);
126
+ try {
127
+ await groupJoiSchema.validateAsync(parsed);
128
+ } catch (e) {
129
+ console.log(" =>", key);
130
+
131
+ if (e instanceof Joi.ValidationError) {
132
+ printJoiError(e);
133
+ } else {
134
+ console.log(e);
135
+ }
136
+
137
+ hasError = true;
138
+ }
139
+ }
166
140
 
167
- if (e instanceof Joi.ValidationError) {
168
- printJoiError(e);
169
- } else {
170
- console.log(e);
171
- }
141
+ // @TODO: feature cannot exist in multiple groups
172
142
 
173
- hasError = true;
143
+ // lint tests
144
+ const tests = await datasource.listTests();
145
+ console.log(`\nLinting ${tests.length} tests...\n`);
146
+
147
+ const testsJoiSchema = getTestsJoiSchema(
148
+ projectConfig,
149
+ availableFeatureKeys,
150
+ availableSegmentKeys,
151
+ );
152
+
153
+ for (const key of tests) {
154
+ const parsed = await datasource.readTest(key);
155
+
156
+ try {
157
+ await testsJoiSchema.validateAsync(parsed);
158
+ } catch (e) {
159
+ console.log(" =>", key);
160
+
161
+ if (e instanceof Joi.ValidationError) {
162
+ printJoiError(e);
163
+ } else {
164
+ console.log(e);
174
165
  }
166
+
167
+ hasError = true;
175
168
  }
176
169
  }
177
170
 
package/src/restore.ts CHANGED
@@ -3,7 +3,7 @@ import { execSync } from "child_process";
3
3
 
4
4
  import { ProjectConfig } from "./config";
5
5
 
6
- export function restoreProject(rootDirectoryPath, projectConfig: ProjectConfig) {
6
+ export async function restoreProject(rootDirectoryPath, projectConfig: ProjectConfig) {
7
7
  const relativeStateDirPath = path.relative(rootDirectoryPath, projectConfig.stateDirectoryPath);
8
8
  const cmd = `git checkout -- ${relativeStateDirPath}${path.sep}`;
9
9
 
@@ -9,7 +9,7 @@ import { generateHistory } from "./generateHistory";
9
9
  import { getRepoDetails } from "./getRepoDetails";
10
10
  import { generateSiteSearchIndex } from "./generateSiteSearchIndex";
11
11
 
12
- export function exportSite(rootDirectoryPath: string, projectConfig: ProjectConfig) {
12
+ export async function exportSite(rootDirectoryPath: string, projectConfig: ProjectConfig) {
13
13
  const hasError = false;
14
14
 
15
15
  mkdirp.sync(projectConfig.siteExportDirectoryPath);
@@ -30,7 +30,7 @@ export function exportSite(rootDirectoryPath: string, projectConfig: ProjectConf
30
30
 
31
31
  // site search index
32
32
  const repoDetails = getRepoDetails();
33
- const searchIndex = generateSiteSearchIndex(
33
+ const searchIndex = await generateSiteSearchIndex(
34
34
  rootDirectoryPath,
35
35
  projectConfig,
36
36
  fullHistory,
@@ -15,12 +15,12 @@ import { getRelativePaths } from "./getRelativePaths";
15
15
  import { getLastModifiedFromHistory } from "./getLastModifiedFromHistory";
16
16
  import { RepoDetails } from "./getRepoDetails";
17
17
 
18
- export function generateSiteSearchIndex(
18
+ export async function generateSiteSearchIndex(
19
19
  rootDirectoryPath: string,
20
20
  projectConfig: ProjectConfig,
21
21
  fullHistory: HistoryEntry[],
22
22
  repoDetails: RepoDetails | undefined,
23
- ): SearchIndex {
23
+ ): Promise<SearchIndex> {
24
24
  const result: SearchIndex = {
25
25
  links: undefined,
26
26
  entities: {
@@ -77,9 +77,9 @@ export function generateSiteSearchIndex(
77
77
  } = {};
78
78
 
79
79
  // features
80
- const featureFiles = datasource.listFeatures();
81
- featureFiles.forEach((entityName) => {
82
- const parsed = datasource.readFeature(entityName);
80
+ const featureFiles = await datasource.listFeatures();
81
+ for (const entityName of featureFiles) {
82
+ const parsed = await datasource.readFeature(entityName);
83
83
 
84
84
  if (Array.isArray(parsed.variations)) {
85
85
  parsed.variations.forEach((variation) => {
@@ -160,12 +160,12 @@ export function generateSiteSearchIndex(
160
160
  key: entityName,
161
161
  lastModified: getLastModifiedFromHistory(fullHistory, "feature", entityName),
162
162
  });
163
- });
163
+ }
164
164
 
165
165
  // segments
166
- const segmentFiles = datasource.listSegments();
167
- segmentFiles.forEach((entityName) => {
168
- const parsed = datasource.readSegment(entityName);
166
+ const segmentFiles = await datasource.listSegments();
167
+ for (const entityName of segmentFiles) {
168
+ const parsed = await datasource.readSegment(entityName);
169
169
 
170
170
  extractAttributeKeysFromConditions(parsed.conditions as Condition | Condition[]).forEach(
171
171
  (attributeKey) => {
@@ -183,12 +183,12 @@ export function generateSiteSearchIndex(
183
183
  lastModified: getLastModifiedFromHistory(fullHistory, "segment", entityName),
184
184
  usedInFeatures: Array.from(segmentsUsedInFeatures[entityName] || []),
185
185
  });
186
- });
186
+ }
187
187
 
188
188
  // attributes
189
- const attributeFiles = datasource.listAttributes();
190
- attributeFiles.forEach((entityName) => {
191
- const parsed = datasource.readAttribute(entityName);
189
+ const attributeFiles = await datasource.listAttributes();
190
+ for (const entityName of attributeFiles) {
191
+ const parsed = await datasource.readAttribute(entityName);
192
192
 
193
193
  result.entities.attributes.push({
194
194
  ...parsed,
@@ -197,7 +197,7 @@ export function generateSiteSearchIndex(
197
197
  usedInFeatures: Array.from(attributesUsedInFeatures[entityName] || []),
198
198
  usedInSegments: Array.from(attributesUsedInSegments[entityName] || []),
199
199
  });
200
- });
200
+ }
201
201
 
202
202
  return result;
203
203
  }
@@ -1,35 +1,36 @@
1
- import * as fs from "fs";
2
-
3
- import { TestFeature, ExistingState } from "@featurevisor/types";
1
+ import { TestFeature } from "@featurevisor/types";
4
2
  import { createInstance, MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
5
3
 
6
4
  import { Datasource } from "../datasource";
7
5
  import { ProjectConfig } from "../config";
8
- import { buildDatafile, getExistingStateFilePath } from "../builder";
6
+ import { buildDatafile } from "../builder";
9
7
  import { SCHEMA_VERSION } from "../config";
10
8
 
11
9
  import { checkIfArraysAreEqual } from "./checkIfArraysAreEqual";
12
10
  import { checkIfObjectsAreEqual } from "./checkIfObjectsAreEqual";
13
11
  import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
14
12
 
15
- export function testFeature(
13
+ export async function testFeature(
16
14
  datasource: Datasource,
17
15
  projectConfig: ProjectConfig,
18
16
  test: TestFeature,
19
- ): boolean {
17
+ ): Promise<boolean> {
20
18
  let hasError = false;
21
19
  const featureKey = test.feature;
22
20
 
23
21
  console.log(CLI_FORMAT_BOLD, ` Feature "${featureKey}":`);
24
22
 
25
- test.assertions.forEach(function (assertion, aIndex) {
23
+ for (let aIndex = 0; aIndex < test.assertions.length; aIndex++) {
24
+ const assertion = test.assertions[aIndex];
26
25
  const description = assertion.description || `at ${assertion.at}%`;
27
26
 
28
27
  console.log(` Assertion #${aIndex + 1}: (${assertion.environment}) ${description}`);
29
28
 
30
- const featuresToInclude = Array.from(datasource.getRequiredFeaturesChain(test.feature));
29
+ const requiredChain = await datasource.getRequiredFeaturesChain(test.feature);
30
+ const featuresToInclude = Array.from(requiredChain);
31
31
 
32
- const datafileContent = buildDatafile(
32
+ const existingState = await datasource.readState(assertion.environment);
33
+ const datafileContent = await buildDatafile(
33
34
  projectConfig,
34
35
  datasource,
35
36
  {
@@ -38,9 +39,7 @@ export function testFeature(
38
39
  environment: assertion.environment,
39
40
  features: featuresToInclude,
40
41
  },
41
- JSON.parse(
42
- fs.readFileSync(getExistingStateFilePath(projectConfig, assertion.environment), "utf8"),
43
- ) as ExistingState,
42
+ existingState,
44
43
  );
45
44
 
46
45
  const sdk = createInstance({
@@ -110,7 +109,7 @@ export function testFeature(
110
109
  }
111
110
  });
112
111
  }
113
- });
112
+ }
114
113
 
115
114
  return hasError;
116
115
  }
@@ -9,7 +9,10 @@ import { testSegment } from "./testSegment";
9
9
  import { testFeature } from "./testFeature";
10
10
  import { CLI_FORMAT_BOLD, CLI_FORMAT_GREEN, CLI_FORMAT_RED } from "./cliFormat";
11
11
 
12
- export function testProject(rootDirectoryPath: string, projectConfig: ProjectConfig): boolean {
12
+ export async function testProject(
13
+ rootDirectoryPath: string,
14
+ projectConfig: ProjectConfig,
15
+ ): Promise<boolean> {
13
16
  let hasError = false;
14
17
  const datasource = new Datasource(projectConfig);
15
18
 
@@ -20,7 +23,7 @@ export function testProject(rootDirectoryPath: string, projectConfig: ProjectCon
20
23
  return hasError;
21
24
  }
22
25
 
23
- const testFiles = datasource.listTests();
26
+ const testFiles = await datasource.listTests();
24
27
 
25
28
  if (testFiles.length === 0) {
26
29
  console.error(`No tests found in: ${projectConfig.testsDirectoryPath}`);
@@ -35,13 +38,13 @@ export function testProject(rootDirectoryPath: string, projectConfig: ProjectCon
35
38
  console.log("");
36
39
  console.log(CLI_FORMAT_BOLD, `Testing: ${testFilePath.replace(rootDirectoryPath, "")}`);
37
40
 
38
- const t = datasource.readTest(testFile);
41
+ const t = await datasource.readTest(testFile);
39
42
 
40
43
  if ((t as TestSegment).segment) {
41
44
  // segment testing
42
45
  const test = t as TestSegment;
43
46
 
44
- const segmentHasError = testSegment(datasource, test);
47
+ const segmentHasError = await testSegment(datasource, test);
45
48
 
46
49
  if (segmentHasError) {
47
50
  hasError = true;
@@ -50,7 +53,7 @@ export function testProject(rootDirectoryPath: string, projectConfig: ProjectCon
50
53
  // feature testing
51
54
  const test = t as TestFeature;
52
55
 
53
- const featureHasError = testFeature(datasource, projectConfig, test);
56
+ const featureHasError = await testFeature(datasource, projectConfig, test);
54
57
 
55
58
  if (featureHasError) {
56
59
  hasError = true;
@@ -5,14 +5,14 @@ import { Datasource } from "../datasource";
5
5
 
6
6
  import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
7
7
 
8
- export function testSegment(datasource: Datasource, test: TestSegment): boolean {
8
+ export async function testSegment(datasource: Datasource, test: TestSegment): Promise<boolean> {
9
9
  let hasError = false;
10
10
 
11
11
  const segmentKey = test.segment;
12
12
 
13
13
  console.log(CLI_FORMAT_BOLD, ` Segment "${segmentKey}":`);
14
14
 
15
- const segmentExists = datasource.entityExists("segment", segmentKey);
15
+ const segmentExists = await datasource.entityExists("segment", segmentKey);
16
16
 
17
17
  if (!segmentExists) {
18
18
  console.error(CLI_FORMAT_RED, ` Segment does not exist: ${segmentKey}`);
@@ -21,7 +21,7 @@ export function testSegment(datasource: Datasource, test: TestSegment): boolean
21
21
  return hasError;
22
22
  }
23
23
 
24
- const parsedSegment = datasource.readSegment(segmentKey);
24
+ const parsedSegment = await datasource.readSegment(segmentKey);
25
25
  const conditions = parsedSegment.conditions as Condition | Condition[];
26
26
 
27
27
  test.assertions.forEach(function (assertion, aIndex) {
package/src/utils.ts CHANGED
@@ -1,23 +1,5 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
-
4
- import * as yaml from "js-yaml";
5
-
6
1
  import { Condition, AttributeKey, GroupSegment, SegmentKey } from "@featurevisor/types";
7
2
 
8
- export function getYAMLFiles(directoryPath: string) {
9
- const files = fs.readdirSync(directoryPath);
10
- const yamlFiles = files.filter((file) => file.endsWith(".yml"));
11
-
12
- return yamlFiles.map((file) => path.join(directoryPath, file));
13
- }
14
-
15
- export function parseYaml(content: string) {
16
- const parsed = yaml.load(content);
17
-
18
- return parsed;
19
- }
20
-
21
3
  export function extractSegmentKeysFromGroupSegments(
22
4
  segments: GroupSegment | GroupSegment[],
23
5
  ): Set<SegmentKey> {