@atlaspack/feature-flags 2.12.1-dev.3401 → 2.12.1-dev.3443

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/lib/index.js CHANGED
@@ -5,15 +5,20 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.DEFAULT_FEATURE_FLAGS = void 0;
7
7
  exports.getFeatureFlag = getFeatureFlag;
8
+ exports.runWithConsistencyCheck = runWithConsistencyCheck;
8
9
  exports.setFeatureFlags = setFeatureFlags;
9
10
  // We need to do these gymnastics as we don't want flow-to-ts to touch DEFAULT_FEATURE_FLAGS,
10
11
  // but we want to export FeatureFlags for Flow
11
12
  const DEFAULT_FEATURE_FLAGS = exports.DEFAULT_FEATURE_FLAGS = {
13
+ exampleConsistencyCheckFeature: 'OLD',
12
14
  exampleFeature: false,
13
15
  atlaspackV3: false,
14
16
  useWatchmanWatcher: false,
15
17
  importRetry: false,
16
- ownedResolverStructures: false
18
+ fixQuadraticCacheInvalidation: 'OLD',
19
+ fastOptimizeInlineRequires: false,
20
+ useLmdbJsLite: false,
21
+ conditionalBundlingApi: false
17
22
  };
18
23
  let featureFlagValues = {
19
24
  ...DEFAULT_FEATURE_FLAGS
@@ -22,5 +27,35 @@ function setFeatureFlags(flags) {
22
27
  featureFlagValues = flags;
23
28
  }
24
29
  function getFeatureFlag(flagName) {
25
- return featureFlagValues[flagName];
30
+ const value = featureFlagValues[flagName];
31
+ return value === true || value === 'NEW';
32
+ }
33
+ /**
34
+ * Run a function with a consistency check.
35
+ */
36
+ function runWithConsistencyCheck(flag, oldFn, newFn, diffFn, report) {
37
+ const value = featureFlagValues[flag];
38
+ if (!value || value === false || value === 'OLD') {
39
+ return oldFn();
40
+ }
41
+ if (value === true || value === 'NEW') {
42
+ return newFn();
43
+ }
44
+ const oldStartTime = performance.now();
45
+ const oldResult = oldFn();
46
+ const oldExecutionTimeMs = performance.now() - oldStartTime;
47
+ const newStartTime = performance.now();
48
+ const newResult = newFn();
49
+ const newExecutionTimeMs = performance.now() - newStartTime;
50
+ const diff = diffFn(oldResult, newResult);
51
+ report({
52
+ isDifferent: diff.isDifferent,
53
+ oldExecutionTimeMs,
54
+ newExecutionTimeMs,
55
+ custom: diff.custom
56
+ }, oldResult, newResult);
57
+ if (value === 'NEW_AND_CHECK') {
58
+ return newResult;
59
+ }
60
+ return oldResult;
26
61
  }
package/lib/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export type FeatureFlags = {
2
2
  // This feature flag mostly exists to test the feature flag system, and doesn't have any build/runtime effect
3
3
  readonly exampleFeature: boolean;
4
+ readonly exampleConsistencyCheckFeature: ConsistencyCheckFeatureFlagValue;
4
5
 
5
6
  /**
6
7
  * Rust backed requests
@@ -18,7 +19,25 @@ export type FeatureFlags = {
18
19
  importRetry: boolean;
19
20
 
20
21
  /**
21
- * Enable resolver refactor into owned data structures.
22
+ * Enable Rust based LMDB wrapper library
22
23
  */
23
- ownedResolverStructures: boolean;
24
+ useLmdbJsLite: boolean;
25
+
26
+ /**
27
+ * Fixes quadratic cache invalidation issue
28
+ */
29
+ fixQuadraticCacheInvalidation: ConsistencyCheckFeatureFlagValue;
30
+
31
+ /**
32
+ * Enable rust based inline requires optimization
33
+ */
34
+ fastOptimizeInlineRequires: boolean;
35
+
36
+ /**
37
+ * Enables an experimental "conditional bundling" API - this allows the use of `importCond` syntax
38
+ * in order to have (consumer) feature flag driven bundling. This feature is very experimental,
39
+ * and requires server-side support.
40
+ */
41
+ conditionalBundlingApi: boolean;
24
42
  };
43
+ export type ConsistencyCheckFeatureFlagValue = "NEW" | "OLD" | "NEW_AND_CHECK" | "OLD_AND_CHECK";
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@atlaspack/feature-flags",
3
- "version": "2.12.1-dev.3401+b483af77f",
3
+ "version": "2.12.1-dev.3443+d1170cfc7",
4
4
  "description": "Provides internal feature-flags for the atlaspack codebase.",
5
- "license": "MIT",
5
+ "license": "(MIT OR Apache-2.0)",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -20,5 +20,5 @@
20
20
  "engines": {
21
21
  "node": ">= 16.0.0"
22
22
  },
23
- "gitHead": "b483af77f02d1258c8dad156e097b94f83671d8e"
23
+ "gitHead": "d1170cfc79beb290b2a066f472f68f71f7d7cb23"
24
24
  }
package/src/index.js CHANGED
@@ -6,11 +6,15 @@ import type {FeatureFlags as _FeatureFlags} from './types';
6
6
  export type FeatureFlags = _FeatureFlags;
7
7
 
8
8
  export const DEFAULT_FEATURE_FLAGS: FeatureFlags = {
9
+ exampleConsistencyCheckFeature: 'OLD',
9
10
  exampleFeature: false,
10
11
  atlaspackV3: false,
11
12
  useWatchmanWatcher: false,
12
13
  importRetry: false,
13
- ownedResolverStructures: false,
14
+ fixQuadraticCacheInvalidation: 'OLD',
15
+ fastOptimizeInlineRequires: false,
16
+ useLmdbJsLite: false,
17
+ conditionalBundlingApi: false,
14
18
  };
15
19
 
16
20
  let featureFlagValues: FeatureFlags = {...DEFAULT_FEATURE_FLAGS};
@@ -20,5 +24,71 @@ export function setFeatureFlags(flags: FeatureFlags) {
20
24
  }
21
25
 
22
26
  export function getFeatureFlag(flagName: $Keys<FeatureFlags>): boolean {
23
- return featureFlagValues[flagName];
27
+ const value = featureFlagValues[flagName];
28
+ return value === true || value === 'NEW';
29
+ }
30
+
31
+ export type DiffResult<CustomDiagnostic> = {|
32
+ isDifferent: boolean,
33
+ custom: CustomDiagnostic,
34
+ |};
35
+
36
+ export type Diagnostic<CustomDiagnostic> = {|
37
+ isDifferent: boolean,
38
+ oldExecutionTimeMs: number,
39
+ newExecutionTimeMs: number,
40
+ custom: CustomDiagnostic,
41
+ |};
42
+
43
+ /**
44
+ * Run a function with a consistency check.
45
+ */
46
+ export function runWithConsistencyCheck<Result, CustomDiagnostic>(
47
+ flag: string,
48
+ oldFn: () => Result,
49
+ newFn: () => Result,
50
+ diffFn: (
51
+ oldResult: Result,
52
+ newResult: Result,
53
+ ) => DiffResult<CustomDiagnostic>,
54
+ report: (
55
+ diagnostic: Diagnostic<CustomDiagnostic>,
56
+ oldResult: Result,
57
+ newResult: Result,
58
+ ) => void,
59
+ ): Result {
60
+ const value = featureFlagValues[flag];
61
+ if (!value || value === false || value === 'OLD') {
62
+ return oldFn();
63
+ }
64
+ if (value === true || value === 'NEW') {
65
+ return newFn();
66
+ }
67
+
68
+ const oldStartTime = performance.now();
69
+ const oldResult = oldFn();
70
+ const oldExecutionTimeMs = performance.now() - oldStartTime;
71
+
72
+ const newStartTime = performance.now();
73
+ const newResult = newFn();
74
+ const newExecutionTimeMs = performance.now() - newStartTime;
75
+
76
+ const diff = diffFn(oldResult, newResult);
77
+
78
+ report(
79
+ {
80
+ isDifferent: diff.isDifferent,
81
+ oldExecutionTimeMs,
82
+ newExecutionTimeMs,
83
+ custom: diff.custom,
84
+ },
85
+ oldResult,
86
+ newResult,
87
+ );
88
+
89
+ if (value === 'NEW_AND_CHECK') {
90
+ return newResult;
91
+ }
92
+
93
+ return oldResult;
24
94
  }
package/src/types.js CHANGED
@@ -3,6 +3,7 @@
3
3
  export type FeatureFlags = {|
4
4
  // This feature flag mostly exists to test the feature flag system, and doesn't have any build/runtime effect
5
5
  +exampleFeature: boolean,
6
+ +exampleConsistencyCheckFeature: ConsistencyCheckFeatureFlagValue,
6
7
  /**
7
8
  * Rust backed requests
8
9
  */
@@ -16,7 +17,27 @@ export type FeatureFlags = {|
16
17
  */
17
18
  importRetry: boolean,
18
19
  /**
19
- * Enable resolver refactor into owned data structures.
20
+ * Enable Rust based LMDB wrapper library
20
21
  */
21
- ownedResolverStructures: boolean,
22
+ useLmdbJsLite: boolean,
23
+ /**
24
+ * Fixes quadratic cache invalidation issue
25
+ */
26
+ fixQuadraticCacheInvalidation: ConsistencyCheckFeatureFlagValue,
27
+ /**
28
+ * Enable rust based inline requires optimization
29
+ */
30
+ fastOptimizeInlineRequires: boolean,
31
+ /**
32
+ * Enables an experimental "conditional bundling" API - this allows the use of `importCond` syntax
33
+ * in order to have (consumer) feature flag driven bundling. This feature is very experimental,
34
+ * and requires server-side support.
35
+ */
36
+ conditionalBundlingApi: boolean,
22
37
  |};
38
+
39
+ export type ConsistencyCheckFeatureFlagValue =
40
+ | 'NEW'
41
+ | 'OLD'
42
+ | 'NEW_AND_CHECK'
43
+ | 'OLD_AND_CHECK';
@@ -1,6 +1,12 @@
1
1
  // @flow strict
2
2
  import assert from 'assert';
3
- import {getFeatureFlag, DEFAULT_FEATURE_FLAGS, setFeatureFlags} from '../src';
3
+ import {
4
+ getFeatureFlag,
5
+ DEFAULT_FEATURE_FLAGS,
6
+ setFeatureFlags,
7
+ runWithConsistencyCheck,
8
+ } from '../src';
9
+ import sinon from 'sinon';
4
10
 
5
11
  describe('feature-flag test', () => {
6
12
  beforeEach(() => {
@@ -18,4 +24,82 @@ describe('feature-flag test', () => {
18
24
  setFeatureFlags({...DEFAULT_FEATURE_FLAGS, exampleFeature: true});
19
25
  assert.equal(getFeatureFlag('exampleFeature'), true);
20
26
  });
27
+
28
+ describe('consistency checks', () => {
29
+ it('runs the old function if the flag is off', () => {
30
+ setFeatureFlags({
31
+ ...DEFAULT_FEATURE_FLAGS,
32
+ exampleConsistencyCheckFeature: 'OLD',
33
+ });
34
+ const result = runWithConsistencyCheck(
35
+ 'exampleConsistencyCheckFeature',
36
+ () => 'old',
37
+ () => 'new',
38
+ sinon.spy(),
39
+ sinon.spy(),
40
+ );
41
+ assert.equal(result, 'old');
42
+ });
43
+
44
+ it('runs the new function if the flag is on', () => {
45
+ setFeatureFlags({
46
+ ...DEFAULT_FEATURE_FLAGS,
47
+ exampleConsistencyCheckFeature: 'NEW',
48
+ });
49
+ const result = runWithConsistencyCheck(
50
+ 'exampleConsistencyCheckFeature',
51
+ () => 'old',
52
+ () => 'new',
53
+ sinon.spy(),
54
+ sinon.spy(),
55
+ );
56
+ assert.equal(result, 'new');
57
+ });
58
+
59
+ it('diffs old and new values if there is a diff value', () => {
60
+ setFeatureFlags({
61
+ ...DEFAULT_FEATURE_FLAGS,
62
+ exampleConsistencyCheckFeature: 'OLD_AND_CHECK',
63
+ });
64
+ const reportSpy = sinon.spy();
65
+ const result = runWithConsistencyCheck(
66
+ 'exampleConsistencyCheckFeature',
67
+ () => 'old',
68
+ () => 'new',
69
+ () => ({isDifferent: false, custom: 'diff'}),
70
+ reportSpy,
71
+ );
72
+
73
+ assert.equal(result, 'old');
74
+ sinon.assert.calledWith(reportSpy, {
75
+ isDifferent: false,
76
+ oldExecutionTimeMs: sinon.match.number,
77
+ newExecutionTimeMs: sinon.match.number,
78
+ custom: 'diff',
79
+ });
80
+ });
81
+
82
+ it('diffs old and new values if there is a diff new value', () => {
83
+ setFeatureFlags({
84
+ ...DEFAULT_FEATURE_FLAGS,
85
+ exampleConsistencyCheckFeature: 'NEW_AND_CHECK',
86
+ });
87
+ const reportSpy = sinon.spy();
88
+ const result = runWithConsistencyCheck(
89
+ 'exampleConsistencyCheckFeature',
90
+ () => 'old',
91
+ () => 'new',
92
+ () => ({isDifferent: true, custom: 'diff'}),
93
+ reportSpy,
94
+ );
95
+
96
+ assert.equal(result, 'new');
97
+ sinon.assert.calledWith(reportSpy, {
98
+ isDifferent: true,
99
+ oldExecutionTimeMs: sinon.match.number,
100
+ newExecutionTimeMs: sinon.match.number,
101
+ custom: 'diff',
102
+ });
103
+ });
104
+ });
21
105
  });