New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@bucketco/flag-evaluation

Package Overview
Dependencies
Maintainers
0
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bucketco/flag-evaluation - npm Package Compare versions

Comparing version 0.0.4 to 0.0.5

76

./dist/index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.evaluate = exports.evaluateRuleWithContext = exports.rejectedDueToPartialRollout = exports.hashInt = exports.evaluateFlag = exports.unflattenJSON = exports.flattenJSON = void 0;
exports.evaluate = exports.hashInt = exports.evaluateFlag = exports.unflattenJSON = exports.flattenJSON = void 0;
const node_crypto_1 = require("node:crypto");

@@ -52,20 +52,6 @@ function flattenJSON(data) {

function evaluateFlag({ context, flag, }) {
var _a;
const flatContext = flattenJSON(context);
const missingContextFieldsSet = new Set();
for (const rule of flag.rules) {
(_a = rule.filter) === null || _a === void 0 ? void 0 : _a.map((r) => r.field).filter((field) => !(field in flatContext)).forEach((field) => missingContextFieldsSet.add(field));
if (rule.partialRolloutAttribute &&
!(rule.partialRolloutAttribute in flatContext)) {
missingContextFieldsSet.add(rule.partialRolloutAttribute);
}
}
const ruleEvaluationResults = flag.rules.map((rule) => evaluateRecursively(rule.filter, flatContext, missingContextFieldsSet));
const missingContextFields = Array.from(missingContextFieldsSet);
const ruleEvaluationResults = flag.rules.map((rule) => {
return evaluateRuleWithContext({
context: flatContext,
rule,
key: flag.key,
});
});
const firstIdx = ruleEvaluationResults.findIndex(Boolean);

@@ -93,33 +79,35 @@ return {

exports.hashInt = hashInt;
function rejectedDueToPartialRollout({ rule, context, key, }) {
if (rule.partialRolloutAttribute === undefined ||
rule.partialRolloutThreshold === undefined) {
return false;
}
// reject if the partial rollout attribute is not present in the context
if (!(rule.partialRolloutAttribute in context)) {
return true;
}
// not included in the partial rollout if the hash is above the threshold
return (hashInt(`${key}.${context[rule.partialRolloutAttribute]}`) >
rule.partialRolloutThreshold);
}
exports.rejectedDueToPartialRollout = rejectedDueToPartialRollout;
function evaluateRuleWithContext({ context, key, rule, }) {
const match = (rule.filter || []).every((filter) => {
var _a;
if (!(filter.field in context)) {
function evaluateRecursively(filter, context, missingContextFieldsSet) {
switch (filter.type) {
case "constant":
return filter.value;
case "context":
if (!(filter.field in context)) {
missingContextFieldsSet.add(filter.field);
return false;
}
return evaluate(context[filter.field], filter.operator, filter.values || []);
case "rolloutPercentage":
if (filter.partialRolloutThreshold === 100000) {
return true;
}
missingContextFieldsSet.add(filter.partialRolloutAttribute);
if (!(filter.partialRolloutAttribute in context)) {
return false;
}
return (hashInt(`${filter.flagKey}.${context[filter.partialRolloutAttribute]}`) > filter.partialRolloutThreshold);
case "group":
return filter.filters.reduce((acc, current) => {
if (filter.operator === "and") {
return (acc &&
evaluateRecursively(current, context, missingContextFieldsSet));
}
return (acc || evaluateRecursively(current, context, missingContextFieldsSet));
}, filter.operator === "and");
case "negation":
return !evaluateRecursively(filter.filter, context, missingContextFieldsSet);
default:
return false;
}
return evaluate(context[filter.field], filter.operator, ((_a = filter === null || filter === void 0 ? void 0 : filter.values) === null || _a === void 0 ? void 0 : _a.length) ? filter.values : [""]);
});
if (!match) {
return false;
}
if (rejectedDueToPartialRollout({ rule, context, key })) {
return false;
}
return true;
}
exports.evaluateRuleWithContext = evaluateRuleWithContext;
function evaluate(fieldValue, op, values) {

@@ -126,0 +114,0 @@ const value = values[0];

@@ -0,5 +1,16 @@

export type FilterClass = {
type: string;
};
export type FilterGroup<T extends FilterClass> = {
type: "group";
operator: "and" | "or";
filters: FilterTree<T>[];
};
export type FilterNegation<T extends FilterClass> = {
type: "negation";
filter: FilterTree<T>;
};
export type FilterTree<T extends FilterClass> = FilterGroup<T> | FilterNegation<T> | T;
export interface Rule {
filter?: ContextFilter[];
partialRolloutThreshold?: number;
partialRolloutAttribute?: string;
filter: RuleFilter;
}

@@ -24,2 +35,3 @@ export type FlagData = {

export type ContextFilter = {
type: "context";
field: string;

@@ -29,2 +41,13 @@ operator: ContextFilterOp;

};
export type PercentageRolloutFilter = {
type: "rolloutPercentage";
flagKey: string;
partialRolloutAttribute: string;
partialRolloutThreshold: number;
};
export type ConstantFilter = {
type: "constant";
value: boolean;
};
export type RuleFilter = FilterTree<ContextFilter | PercentageRolloutFilter | ConstantFilter>;
export declare function flattenJSON(data: object): Record<string, string>;

@@ -34,13 +57,3 @@ export declare function unflattenJSON(data: Record<string, any>): Record<string, any>;

export declare function hashInt(hashInput: string): number;
export declare function rejectedDueToPartialRollout({ rule, context, key, }: {
rule: Rule;
context: Record<string, string>;
key: string;
}): boolean;
export declare function evaluateRuleWithContext({ context, key, rule, }: {
key: string;
context: Record<string, string>;
rule: Rule;
}): boolean;
export declare function evaluate(fieldValue: string, op: ContextFilterOp, values: string[]): boolean;
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.evaluate = exports.evaluateRuleWithContext = exports.rejectedDueToPartialRollout = exports.hashInt = exports.evaluateFlag = exports.unflattenJSON = exports.flattenJSON = void 0;
exports.evaluate = exports.hashInt = exports.evaluateFlag = exports.unflattenJSON = exports.flattenJSON = void 0;
const node_crypto_1 = require("node:crypto");

@@ -52,20 +52,6 @@ function flattenJSON(data) {

function evaluateFlag({ context, flag, }) {
var _a;
const flatContext = flattenJSON(context);
const missingContextFieldsSet = new Set();
for (const rule of flag.rules) {
(_a = rule.filter) === null || _a === void 0 ? void 0 : _a.map((r) => r.field).filter((field) => !(field in flatContext)).forEach((field) => missingContextFieldsSet.add(field));
if (rule.partialRolloutAttribute &&
!(rule.partialRolloutAttribute in flatContext)) {
missingContextFieldsSet.add(rule.partialRolloutAttribute);
}
}
const ruleEvaluationResults = flag.rules.map((rule) => evaluateRecursively(rule.filter, flatContext, missingContextFieldsSet));
const missingContextFields = Array.from(missingContextFieldsSet);
const ruleEvaluationResults = flag.rules.map((rule) => {
return evaluateRuleWithContext({
context: flatContext,
rule,
key: flag.key,
});
});
const firstIdx = ruleEvaluationResults.findIndex(Boolean);

@@ -93,33 +79,35 @@ return {

exports.hashInt = hashInt;
function rejectedDueToPartialRollout({ rule, context, key, }) {
if (rule.partialRolloutAttribute === undefined ||
rule.partialRolloutThreshold === undefined) {
return false;
}
// reject if the partial rollout attribute is not present in the context
if (!(rule.partialRolloutAttribute in context)) {
return true;
}
// not included in the partial rollout if the hash is above the threshold
return (hashInt(`${key}.${context[rule.partialRolloutAttribute]}`) >
rule.partialRolloutThreshold);
}
exports.rejectedDueToPartialRollout = rejectedDueToPartialRollout;
function evaluateRuleWithContext({ context, key, rule, }) {
const match = (rule.filter || []).every((filter) => {
var _a;
if (!(filter.field in context)) {
function evaluateRecursively(filter, context, missingContextFieldsSet) {
switch (filter.type) {
case "constant":
return filter.value;
case "context":
if (!(filter.field in context)) {
missingContextFieldsSet.add(filter.field);
return false;
}
return evaluate(context[filter.field], filter.operator, filter.values || []);
case "rolloutPercentage":
if (filter.partialRolloutThreshold === 100000) {
return true;
}
missingContextFieldsSet.add(filter.partialRolloutAttribute);
if (!(filter.partialRolloutAttribute in context)) {
return false;
}
return (hashInt(`${filter.flagKey}.${context[filter.partialRolloutAttribute]}`) > filter.partialRolloutThreshold);
case "group":
return filter.filters.reduce((acc, current) => {
if (filter.operator === "and") {
return (acc &&
evaluateRecursively(current, context, missingContextFieldsSet));
}
return (acc || evaluateRecursively(current, context, missingContextFieldsSet));
}, filter.operator === "and");
case "negation":
return !evaluateRecursively(filter.filter, context, missingContextFieldsSet);
default:
return false;
}
return evaluate(context[filter.field], filter.operator, ((_a = filter === null || filter === void 0 ? void 0 : filter.values) === null || _a === void 0 ? void 0 : _a.length) ? filter.values : [""]);
});
if (!match) {
return false;
}
if (rejectedDueToPartialRollout({ rule, context, key })) {
return false;
}
return true;
}
exports.evaluateRuleWithContext = evaluateRuleWithContext;
function evaluate(fieldValue, op, values) {

@@ -126,0 +114,0 @@ const value = values[0];

{
"name": "@bucketco/flag-evaluation",
"version": "0.0.4",
"version": "0.0.5",
"license": "MIT",

@@ -28,10 +28,10 @@ "repository": {

"devDependencies": {
"@bucketco/eslint-config": "0.0.2",
"@bucketco/tsconfig": "0.0.2",
"@bucketco/eslint-config": "^0.0.2",
"@bucketco/tsconfig": "^0.0.2",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.0",
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"prettier": "^3.1.1",
"prettier": "^3.3.3",
"ts-jest": "^29.1.4",

@@ -41,3 +41,3 @@ "ts-node": "^10.9.2",

},
"gitHead": "bd0171e757944d78ea0815d5155dc89ddf37f21f"
"gitHead": "9cf8f0887db8dc51e335e1260b5b77b730f5d0df"
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc