@atlaspack/feature-flags
Advanced tools
Comparing version 2.12.1-dev.3401 to 2.12.1-dev.3443
@@ -8,2 +8,3 @@ "use strict"; | ||
exports.getFeatureFlag = getFeatureFlag; | ||
exports.runWithConsistencyCheck = runWithConsistencyCheck; | ||
exports.setFeatureFlags = setFeatureFlags; | ||
@@ -13,2 +14,3 @@ // We need to do these gymnastics as we don't want flow-to-ts to touch DEFAULT_FEATURE_FLAGS, | ||
const DEFAULT_FEATURE_FLAGS = exports.DEFAULT_FEATURE_FLAGS = { | ||
exampleConsistencyCheckFeature: 'OLD', | ||
exampleFeature: false, | ||
@@ -18,3 +20,6 @@ atlaspackV3: false, | ||
importRetry: false, | ||
ownedResolverStructures: false | ||
fixQuadraticCacheInvalidation: 'OLD', | ||
fastOptimizeInlineRequires: false, | ||
useLmdbJsLite: false, | ||
conditionalBundlingApi: false | ||
}; | ||
@@ -28,3 +33,33 @@ let featureFlagValues = { | ||
function getFeatureFlag(flagName) { | ||
return featureFlagValues[flagName]; | ||
const value = featureFlagValues[flagName]; | ||
return value === true || value === 'NEW'; | ||
} | ||
/** | ||
* Run a function with a consistency check. | ||
*/ | ||
function runWithConsistencyCheck(flag, oldFn, newFn, diffFn, report) { | ||
const value = featureFlagValues[flag]; | ||
if (!value || value === false || value === 'OLD') { | ||
return oldFn(); | ||
} | ||
if (value === true || value === 'NEW') { | ||
return newFn(); | ||
} | ||
const oldStartTime = performance.now(); | ||
const oldResult = oldFn(); | ||
const oldExecutionTimeMs = performance.now() - oldStartTime; | ||
const newStartTime = performance.now(); | ||
const newResult = newFn(); | ||
const newExecutionTimeMs = performance.now() - newStartTime; | ||
const diff = diffFn(oldResult, newResult); | ||
report({ | ||
isDifferent: diff.isDifferent, | ||
oldExecutionTimeMs, | ||
newExecutionTimeMs, | ||
custom: diff.custom | ||
}, oldResult, newResult); | ||
if (value === 'NEW_AND_CHECK') { | ||
return newResult; | ||
} | ||
return oldResult; | ||
} |
export type FeatureFlags = { | ||
// This feature flag mostly exists to test the feature flag system, and doesn't have any build/runtime effect | ||
readonly exampleFeature: boolean; | ||
readonly exampleConsistencyCheckFeature: ConsistencyCheckFeatureFlagValue; | ||
@@ -21,5 +22,23 @@ /** | ||
/** | ||
* Enable resolver refactor into owned data structures. | ||
* Enable Rust based LMDB wrapper library | ||
*/ | ||
ownedResolverStructures: boolean; | ||
useLmdbJsLite: boolean; | ||
/** | ||
* Fixes quadratic cache invalidation issue | ||
*/ | ||
fixQuadraticCacheInvalidation: ConsistencyCheckFeatureFlagValue; | ||
/** | ||
* Enable rust based inline requires optimization | ||
*/ | ||
fastOptimizeInlineRequires: boolean; | ||
/** | ||
* Enables an experimental "conditional bundling" API - this allows the use of `importCond` syntax | ||
* in order to have (consumer) feature flag driven bundling. This feature is very experimental, | ||
* and requires server-side support. | ||
*/ | ||
conditionalBundlingApi: boolean; | ||
}; | ||
export type ConsistencyCheckFeatureFlagValue = "NEW" | "OLD" | "NEW_AND_CHECK" | "OLD_AND_CHECK"; |
{ | ||
"name": "@atlaspack/feature-flags", | ||
"version": "2.12.1-dev.3401+b483af77f", | ||
"version": "2.12.1-dev.3443+d1170cfc7", | ||
"description": "Provides internal feature-flags for the atlaspack codebase.", | ||
"license": "MIT", | ||
"license": "(MIT OR Apache-2.0)", | ||
"publishConfig": { | ||
@@ -23,3 +23,3 @@ "access": "public" | ||
}, | ||
"gitHead": "b483af77f02d1258c8dad156e097b94f83671d8e" | ||
"gitHead": "d1170cfc79beb290b2a066f472f68f71f7d7cb23" | ||
} |
@@ -9,2 +9,3 @@ // @flow strict | ||
export const DEFAULT_FEATURE_FLAGS: FeatureFlags = { | ||
exampleConsistencyCheckFeature: 'OLD', | ||
exampleFeature: false, | ||
@@ -14,3 +15,6 @@ atlaspackV3: false, | ||
importRetry: false, | ||
ownedResolverStructures: false, | ||
fixQuadraticCacheInvalidation: 'OLD', | ||
fastOptimizeInlineRequires: false, | ||
useLmdbJsLite: false, | ||
conditionalBundlingApi: false, | ||
}; | ||
@@ -25,3 +29,69 @@ | ||
export function getFeatureFlag(flagName: $Keys<FeatureFlags>): boolean { | ||
return featureFlagValues[flagName]; | ||
const value = featureFlagValues[flagName]; | ||
return value === true || value === 'NEW'; | ||
} | ||
export type DiffResult<CustomDiagnostic> = {| | ||
isDifferent: boolean, | ||
custom: CustomDiagnostic, | ||
|}; | ||
export type Diagnostic<CustomDiagnostic> = {| | ||
isDifferent: boolean, | ||
oldExecutionTimeMs: number, | ||
newExecutionTimeMs: number, | ||
custom: CustomDiagnostic, | ||
|}; | ||
/** | ||
* Run a function with a consistency check. | ||
*/ | ||
export function runWithConsistencyCheck<Result, CustomDiagnostic>( | ||
flag: string, | ||
oldFn: () => Result, | ||
newFn: () => Result, | ||
diffFn: ( | ||
oldResult: Result, | ||
newResult: Result, | ||
) => DiffResult<CustomDiagnostic>, | ||
report: ( | ||
diagnostic: Diagnostic<CustomDiagnostic>, | ||
oldResult: Result, | ||
newResult: Result, | ||
) => void, | ||
): Result { | ||
const value = featureFlagValues[flag]; | ||
if (!value || value === false || value === 'OLD') { | ||
return oldFn(); | ||
} | ||
if (value === true || value === 'NEW') { | ||
return newFn(); | ||
} | ||
const oldStartTime = performance.now(); | ||
const oldResult = oldFn(); | ||
const oldExecutionTimeMs = performance.now() - oldStartTime; | ||
const newStartTime = performance.now(); | ||
const newResult = newFn(); | ||
const newExecutionTimeMs = performance.now() - newStartTime; | ||
const diff = diffFn(oldResult, newResult); | ||
report( | ||
{ | ||
isDifferent: diff.isDifferent, | ||
oldExecutionTimeMs, | ||
newExecutionTimeMs, | ||
custom: diff.custom, | ||
}, | ||
oldResult, | ||
newResult, | ||
); | ||
if (value === 'NEW_AND_CHECK') { | ||
return newResult; | ||
} | ||
return oldResult; | ||
} |
@@ -6,2 +6,3 @@ // @flow strict | ||
+exampleFeature: boolean, | ||
+exampleConsistencyCheckFeature: ConsistencyCheckFeatureFlagValue, | ||
/** | ||
@@ -20,5 +21,25 @@ * Rust backed requests | ||
/** | ||
* Enable resolver refactor into owned data structures. | ||
* Enable Rust based LMDB wrapper library | ||
*/ | ||
ownedResolverStructures: boolean, | ||
useLmdbJsLite: boolean, | ||
/** | ||
* Fixes quadratic cache invalidation issue | ||
*/ | ||
fixQuadraticCacheInvalidation: ConsistencyCheckFeatureFlagValue, | ||
/** | ||
* Enable rust based inline requires optimization | ||
*/ | ||
fastOptimizeInlineRequires: boolean, | ||
/** | ||
* Enables an experimental "conditional bundling" API - this allows the use of `importCond` syntax | ||
* in order to have (consumer) feature flag driven bundling. This feature is very experimental, | ||
* and requires server-side support. | ||
*/ | ||
conditionalBundlingApi: boolean, | ||
|}; | ||
export type ConsistencyCheckFeatureFlagValue = | ||
| 'NEW' | ||
| 'OLD' | ||
| 'NEW_AND_CHECK' | ||
| 'OLD_AND_CHECK'; |
// @flow strict | ||
import assert from 'assert'; | ||
import {getFeatureFlag, DEFAULT_FEATURE_FLAGS, setFeatureFlags} from '../src'; | ||
import { | ||
getFeatureFlag, | ||
DEFAULT_FEATURE_FLAGS, | ||
setFeatureFlags, | ||
runWithConsistencyCheck, | ||
} from '../src'; | ||
import sinon from 'sinon'; | ||
@@ -21,2 +27,80 @@ describe('feature-flag test', () => { | ||
}); | ||
describe('consistency checks', () => { | ||
it('runs the old function if the flag is off', () => { | ||
setFeatureFlags({ | ||
...DEFAULT_FEATURE_FLAGS, | ||
exampleConsistencyCheckFeature: 'OLD', | ||
}); | ||
const result = runWithConsistencyCheck( | ||
'exampleConsistencyCheckFeature', | ||
() => 'old', | ||
() => 'new', | ||
sinon.spy(), | ||
sinon.spy(), | ||
); | ||
assert.equal(result, 'old'); | ||
}); | ||
it('runs the new function if the flag is on', () => { | ||
setFeatureFlags({ | ||
...DEFAULT_FEATURE_FLAGS, | ||
exampleConsistencyCheckFeature: 'NEW', | ||
}); | ||
const result = runWithConsistencyCheck( | ||
'exampleConsistencyCheckFeature', | ||
() => 'old', | ||
() => 'new', | ||
sinon.spy(), | ||
sinon.spy(), | ||
); | ||
assert.equal(result, 'new'); | ||
}); | ||
it('diffs old and new values if there is a diff value', () => { | ||
setFeatureFlags({ | ||
...DEFAULT_FEATURE_FLAGS, | ||
exampleConsistencyCheckFeature: 'OLD_AND_CHECK', | ||
}); | ||
const reportSpy = sinon.spy(); | ||
const result = runWithConsistencyCheck( | ||
'exampleConsistencyCheckFeature', | ||
() => 'old', | ||
() => 'new', | ||
() => ({isDifferent: false, custom: 'diff'}), | ||
reportSpy, | ||
); | ||
assert.equal(result, 'old'); | ||
sinon.assert.calledWith(reportSpy, { | ||
isDifferent: false, | ||
oldExecutionTimeMs: sinon.match.number, | ||
newExecutionTimeMs: sinon.match.number, | ||
custom: 'diff', | ||
}); | ||
}); | ||
it('diffs old and new values if there is a diff new value', () => { | ||
setFeatureFlags({ | ||
...DEFAULT_FEATURE_FLAGS, | ||
exampleConsistencyCheckFeature: 'NEW_AND_CHECK', | ||
}); | ||
const reportSpy = sinon.spy(); | ||
const result = runWithConsistencyCheck( | ||
'exampleConsistencyCheckFeature', | ||
() => 'old', | ||
() => 'new', | ||
() => ({isDifferent: true, custom: 'diff'}), | ||
reportSpy, | ||
); | ||
assert.equal(result, 'new'); | ||
sinon.assert.calledWith(reportSpy, { | ||
isDifferent: true, | ||
oldExecutionTimeMs: sinon.match.number, | ||
newExecutionTimeMs: sinon.match.number, | ||
custom: 'diff', | ||
}); | ||
}); | ||
}); | ||
}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
21537
312
2