@featurevisor/core
Version:
Core package of Featurevisor for Node.js usage
140 lines • 5.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.assessDistributionPlugin = void 0;
exports.assessDistribution = assessDistribution;
const crypto_1 = require("crypto");
const sdk_1 = require("@featurevisor/sdk");
const builder_1 = require("../builder");
const config_1 = require("../config");
const utils_1 = require("../utils");
const UUID_LENGTHS = [4, 2, 2, 2, 6];
function generateUUID() {
return UUID_LENGTHS.map((len) => (0, crypto_1.randomBytes)(len).toString("hex")).join("-");
}
function printCounts(evaluations, n, sort = true) {
let entries = Object.entries(evaluations);
if (sort) {
entries = entries.sort((a, b) => {
if (a[1] > b[1]) {
return -1;
}
if (a[1] < b[1]) {
return 1;
}
return 0;
});
}
const longestValueLength = Object.keys(evaluations).reduce((acc, curr) => (curr.length > acc ? curr.length : acc), 0);
const highestCount = Object.values(evaluations).reduce((acc, curr) => (curr > acc ? curr : acc), 0);
const prettyHighestCountLength = (0, utils_1.prettyNumber)(highestCount).length;
for (const [value, count] of entries) {
console.log(` - ${value}:`.padEnd(longestValueLength + 5, " "), `\t${(0, utils_1.prettyNumber)(count).padStart(prettyHighestCountLength, " ")}`, `\t${(0, utils_1.prettyPercentage)(count, n).padStart(7, " ")}`);
}
}
function createContext(providedContext, populateUuid) {
const context = { ...providedContext };
if (populateUuid) {
for (const key of populateUuid) {
context[key] = generateUUID();
}
}
return context;
}
async function assessDistribution(deps, options) {
const { projectConfig, datasource } = deps;
console.log(`\nAssessing distribution for feature: "${options.feature}"...`);
/**
* Prepare datafile
*/
const datafileBuildStart = Date.now();
console.log(`\n\nBuilding datafile containing all features for "${options.environment}"...`);
const existingState = await datasource.readState(options.environment || false);
const datafileContent = await (0, builder_1.buildDatafile)(projectConfig, datasource, {
schemaVersion: options.schemaVersion || config_1.SCHEMA_VERSION,
revision: "include-all-features",
environment: options.environment || false,
inflate: options.inflate,
}, existingState);
const datafileBuildDuration = Date.now() - datafileBuildStart;
console.log(`Datafile build duration: ${datafileBuildDuration}ms`);
/**
* Initialize SDK
*/
const f = (0, sdk_1.createInstance)({
datafile: datafileContent,
logLevel: "warn",
});
console.log("\n\n...SDK initialized\n");
/**
* Evaluations
*/
let hasVariations = false;
const feature = f.getFeature(options.feature);
if (feature && feature.variations) {
hasVariations = true;
}
const flagEvaluations = {
enabled: 0,
disabled: 0,
};
const variationEvaluations = {};
console.log(`\nEvaluating ${(0, utils_1.prettyNumber)(options.n)} times...`);
for (let i = 0; i < options.n; i++) {
const context = createContext(options.context, options.populateUuid);
if (options.verbose) {
console.log(`[${i + 1}/${options.n}] Evaluating against context: ${JSON.stringify(context)}`);
}
// flag
const flagEvaluation = f.isEnabled(options.feature, context);
if (flagEvaluation) {
flagEvaluations.enabled++;
}
else {
flagEvaluations.disabled++;
}
// variation
if (hasVariations) {
const variationEvaluation = f.getVariation(options.feature, context);
if (!variationEvaluations[variationEvaluation]) {
variationEvaluations[variationEvaluation] = 0;
}
variationEvaluations[variationEvaluation]++;
}
}
/**
* Print results
*/
console.log("\n\nFlag evaluations:\n");
printCounts(flagEvaluations, options.n);
if (hasVariations) {
console.log("\n\nVariation evaluations:\n");
printCounts(variationEvaluations, options.n);
}
}
exports.assessDistributionPlugin = {
command: "assess-distribution",
handler: async ({ rootDirectoryPath, projectConfig, datasource, parsed }) => {
await assessDistribution({
rootDirectoryPath,
projectConfig,
datasource,
options: parsed,
}, {
environment: parsed.environment,
feature: parsed.feature,
n: parseInt(parsed.n, 10) || 1,
context: parsed.context ? JSON.parse(parsed.context) : {},
populateUuid: Array.isArray(parsed.populateUuid)
? parsed.populateUuid
: [parsed.populateUuid].filter(Boolean),
verbose: parsed.verbose,
});
},
examples: [
{
command: "assess-distribution --environment=production --feature=my_feature --context='{}' --populateUuid=userId -n=100",
description: "test traffic distribution a feature against provided context",
},
],
};
//# sourceMappingURL=index.js.map