UNPKG

@featurevisor/core

Version:

Core package of Featurevisor for Node.js usage

140 lines 5.32 kB
"use strict"; 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