Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@eppo/js-client-sdk-common

Package Overview
Dependencies
Maintainers
8
Versions
74
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@eppo/js-client-sdk-common - npm Package Compare versions

Comparing version 4.0.1 to 4.0.2

dist/flag-evaluation-error.d.ts

5

dist/bandit-evaluator.js

@@ -80,3 +80,6 @@ "use strict";

actionScoreEntries.forEach(([actionKey, actionScore]) => {
if (currTopScore === null || actionScore > currTopScore) {
if (currTopScore === null ||
currTopAction === null ||
actionScore > currTopScore ||
(actionScore === currTopScore && actionKey < currTopAction)) {
currTopScore = actionScore;

@@ -83,0 +86,0 @@ currTopAction = actionKey;

1

dist/client/eppo-client.d.ts

@@ -169,2 +169,3 @@ import { IAssignmentLogger } from '../assignment-logger';

private getAssignmentVariation;
private parseVariationWithDetails;
private rethrowIfNotGraceful;

@@ -171,0 +172,0 @@ /**

@@ -14,2 +14,3 @@ "use strict";

const flag_evaluation_details_builder_1 = require("../flag-evaluation-details-builder");
const flag_evaluation_error_1 = require("../flag-evaluation-error");
const http_client_1 = require("../http-client");

@@ -413,4 +414,25 @@ const interfaces_1 = require("../interfaces");

const result = this.getAssignmentDetail(flagKey, subjectKey, subjectAttributes, expectedVariationType);
if (!result.variation) {
return this.parseVariationWithDetails(result, defaultValue, expectedVariationType);
}
catch (error) {
const eppoValue = this.rethrowIfNotGraceful(error, defaultValue);
if (error instanceof flag_evaluation_error_1.FlagEvaluationError && error.flagEvaluationDetails) {
return {
eppoValue,
flagEvaluationDetails: error.flagEvaluationDetails,
};
}
else {
const flagEvaluationDetails = new flag_evaluation_details_builder_1.FlagEvaluationDetailsBuilder('', [], '', '').buildForNoneResult('ASSIGNMENT_ERROR', `Assignment Error: ${error.message}`);
return {
eppoValue,
flagEvaluationDetails,
};
}
}
}
parseVariationWithDetails(result, defaultValue, expectedVariationType) {
try {
if (!result.variation || result.flagEvaluationDetails.flagEvaluationCode !== 'MATCH') {
return {
eppoValue: defaultValue,

@@ -427,6 +449,5 @@ flagEvaluationDetails: result.flagEvaluationDetails,

const eppoValue = this.rethrowIfNotGraceful(error, defaultValue);
const flagEvaluationDetails = new flag_evaluation_details_builder_1.FlagEvaluationDetailsBuilder('', [], '', '').buildForNoneResult('ASSIGNMENT_ERROR', `Assignment Error: ${error.message}`);
return {
eppoValue,
flagEvaluationDetails,
flagEvaluationDetails: result.flagEvaluationDetails,
};

@@ -467,3 +488,8 @@ }

if (!checkTypeMatch(expectedVariationType, flag.variationType)) {
throw new TypeError(`Variation value does not have the correct type. Found: ${flag.variationType} != ${expectedVariationType} for flag ${flagKey}`);
const errorMessage = `Variation value does not have the correct type. Found ${flag.variationType}, but expected ${expectedVariationType} for flag ${flagKey}`;
if (this.isGracefulFailureMode) {
const flagEvaluationDetails = flagEvaluationDetailsBuilder.buildForNoneResult('TYPE_MISMATCH', errorMessage);
return (0, evaluator_1.noneResult)(flagKey, subjectKey, subjectAttributes, flagEvaluationDetails);
}
throw new TypeError(errorMessage);
}

@@ -481,8 +507,2 @@ if (!flag.enabled) {

}
if ((result === null || result === void 0 ? void 0 : result.variation) && !checkValueTypeMatch(expectedVariationType, result.variation.value)) {
const { key: vKey, value: vValue } = result.variation;
const reason = `Expected variation type ${expectedVariationType} does not match for variation '${vKey}' with value ${vValue}`;
const flagEvaluationDetails = flagEvaluationDetailsBuilder.buildForNoneResult('TYPE_MISMATCH', reason);
return (0, evaluator_1.noneResult)(flagKey, subjectKey, subjectAttributes, flagEvaluationDetails);
}
try {

@@ -489,0 +509,0 @@ if (result === null || result === void 0 ? void 0 : result.doLog) {

@@ -21,3 +21,3 @@ import { IFlagEvaluationDetails } from './flag-evaluation-details-builder';

matchesShard(shard: Shard, subjectKey: string, totalShards: number): boolean;
private getMatchedEvaluationDetailsMessage;
private getMatchedEvaluationCodeAndDescription;
}

@@ -24,0 +24,0 @@ export declare function isInShardRange(shard: number, range: Range): boolean;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.matchesRules = exports.noneResult = exports.hashKey = exports.isInShardRange = exports.Evaluator = void 0;
const eppo_client_1 = require("./client/eppo-client");
const flag_evaluation_details_builder_1 = require("./flag-evaluation-details-builder");
const flag_evaluation_error_1 = require("./flag-evaluation-error");
const rules_1 = require("./rules");

@@ -9,4 +11,11 @@ const sharders_1 = require("./sharders");

constructor(sharder) {
this.getMatchedEvaluationDetailsMessage = (allocation, split, subjectKey) => {
this.getMatchedEvaluationCodeAndDescription = (variation, allocation, split, subjectKey, expectedVariationType) => {
var _a;
if (!(0, eppo_client_1.checkValueTypeMatch)(expectedVariationType, variation.value)) {
const { key: vKey, value: vValue } = variation;
return {
flagEvaluationCode: 'ASSIGNMENT_ERROR',
flagEvaluationDescription: `Variation (${vKey}) is configured for type ${expectedVariationType}, but is set to incompatible value (${vValue})`,
};
}
const hasDefinedRules = !!((_a = allocation.rules) === null || _a === void 0 ? void 0 : _a.length);

@@ -17,8 +26,17 @@ const isExperiment = allocation.splits.length > 1;

if (hasDefinedRules && isExperimentOrPartialRollout) {
return `Supplied attributes match rules defined in allocation "${allocation.key}" and ${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `Supplied attributes match rules defined in allocation "${allocation.key}" and ${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}".`,
};
}
if (hasDefinedRules && !isExperimentOrPartialRollout) {
return `Supplied attributes match rules defined in allocation "${allocation.key}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `Supplied attributes match rules defined in allocation "${allocation.key}".`,
};
}
return `${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}" defined in allocation "${allocation.key}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}" defined in allocation "${allocation.key}".`,
};
};

@@ -30,54 +48,63 @@ this.sharder = sharder !== null && sharder !== void 0 ? sharder : new sharders_1.MD5Sharder();

const flagEvaluationDetailsBuilder = new flag_evaluation_details_builder_1.FlagEvaluationDetailsBuilder(configDetails.configEnvironment.name, flag.allocations, configDetails.configFetchedAt, configDetails.configPublishedAt);
if (!flag.enabled) {
return noneResult(flag.key, subjectKey, subjectAttributes, flagEvaluationDetailsBuilder.buildForNoneResult('FLAG_UNRECOGNIZED_OR_DISABLED', `Unrecognized or disabled flag: ${flag.key}`));
}
const now = new Date();
const unmatchedAllocations = [];
for (let i = 0; i < flag.allocations.length; i++) {
const allocation = flag.allocations[i];
const addUnmatchedAllocation = (code) => {
unmatchedAllocations.push({
key: allocation.key,
allocationEvaluationCode: code,
orderPosition: i + 1,
});
};
if (allocation.startAt && now < new Date(allocation.startAt)) {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.BEFORE_START_TIME);
continue;
try {
if (!flag.enabled) {
return noneResult(flag.key, subjectKey, subjectAttributes, flagEvaluationDetailsBuilder.buildForNoneResult('FLAG_UNRECOGNIZED_OR_DISABLED', `Unrecognized or disabled flag: ${flag.key}`));
}
if (allocation.endAt && now > new Date(allocation.endAt)) {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.AFTER_END_TIME);
continue;
}
const { matched, matchedRule } = matchesRules((_a = allocation === null || allocation === void 0 ? void 0 : allocation.rules) !== null && _a !== void 0 ? _a : [], Object.assign({ id: subjectKey }, subjectAttributes), obfuscated);
if (matched) {
for (const split of allocation.splits) {
if (split.shards.every((shard) => this.matchesShard(shard, subjectKey, flag.totalShards))) {
const variation = flag.variations[split.variationKey];
const flagEvaluationDetails = flagEvaluationDetailsBuilder
.setMatch(i, variation, allocation, matchedRule, unmatchedAllocations, expectedVariationType)
.build('MATCH', this.getMatchedEvaluationDetailsMessage(allocation, split, subjectKey));
return {
flagKey: flag.key,
subjectKey,
subjectAttributes,
allocationKey: allocation.key,
variation,
extraLogging: (_b = split.extraLogging) !== null && _b !== void 0 ? _b : {},
doLog: allocation.doLog,
flagEvaluationDetails,
};
const now = new Date();
for (let i = 0; i < flag.allocations.length; i++) {
const allocation = flag.allocations[i];
const addUnmatchedAllocation = (code) => {
flagEvaluationDetailsBuilder.addUnmatchedAllocation({
key: allocation.key,
allocationEvaluationCode: code,
orderPosition: i + 1,
});
};
if (allocation.startAt && now < new Date(allocation.startAt)) {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.BEFORE_START_TIME);
continue;
}
if (allocation.endAt && now > new Date(allocation.endAt)) {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.AFTER_END_TIME);
continue;
}
const { matched, matchedRule } = matchesRules((_a = allocation === null || allocation === void 0 ? void 0 : allocation.rules) !== null && _a !== void 0 ? _a : [], Object.assign({ id: subjectKey }, subjectAttributes), obfuscated);
if (matched) {
for (const split of allocation.splits) {
if (split.shards.every((shard) => this.matchesShard(shard, subjectKey, flag.totalShards))) {
const variation = flag.variations[split.variationKey];
const { flagEvaluationCode, flagEvaluationDescription } = this.getMatchedEvaluationCodeAndDescription(variation, allocation, split, subjectKey, expectedVariationType);
const flagEvaluationDetails = flagEvaluationDetailsBuilder
.setMatch(i, variation, allocation, matchedRule, expectedVariationType)
.build(flagEvaluationCode, flagEvaluationDescription);
return {
flagKey: flag.key,
subjectKey,
subjectAttributes,
allocationKey: allocation.key,
variation,
extraLogging: (_b = split.extraLogging) !== null && _b !== void 0 ? _b : {},
doLog: allocation.doLog,
flagEvaluationDetails,
};
}
}
// matched, but does not fall within split range
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.TRAFFIC_EXPOSURE_MISS);
}
// matched, but does not fall within split range
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.TRAFFIC_EXPOSURE_MISS);
else {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.FAILING_RULE);
}
}
else {
addUnmatchedAllocation(flag_evaluation_details_builder_1.AllocationEvaluationCode.FAILING_RULE);
return noneResult(flag.key, subjectKey, subjectAttributes, flagEvaluationDetailsBuilder.buildForNoneResult('DEFAULT_ALLOCATION_NULL', 'No allocations matched. Falling back to "Default Allocation", serving NULL'));
}
catch (err) {
const flagEvaluationDetails = flagEvaluationDetailsBuilder.gracefulBuild('ASSIGNMENT_ERROR', `Assignment Error: ${err.message}`);
if (flagEvaluationDetails) {
const flagEvaluationError = new flag_evaluation_error_1.FlagEvaluationError(err.message);
flagEvaluationError.flagEvaluationDetails = flagEvaluationDetails;
throw flagEvaluationError;
}
throw err;
}
return noneResult(flag.key, subjectKey, subjectAttributes, flagEvaluationDetailsBuilder
.setNoMatchFound(unmatchedAllocations)
.build('DEFAULT_ALLOCATION_NULL', 'No allocations matched. Falling back to "Default Allocation", serving NULL'));
}

@@ -84,0 +111,0 @@ matchesShard(shard, subjectKey, totalShards) {

@@ -42,11 +42,13 @@ import { Allocation, Variation, VariationType } from './interfaces';

private matchedAllocation;
private unmatchedAllocations;
private unevaluatedAllocations;
private readonly unmatchedAllocations;
private readonly unevaluatedAllocations;
constructor(environmentName: string, allocations: Allocation[], configFetchedAt: string, configPublishedAt: string);
addUnmatchedAllocation: (allocationEvaluation: AllocationEvaluation) => void;
setNone: () => FlagEvaluationDetailsBuilder;
setNoMatchFound: (unmatchedAllocations?: Array<AllocationEvaluation>) => FlagEvaluationDetailsBuilder;
setMatch: (indexPosition: number, variation: Variation, allocation: Allocation, matchedRule: Rule | null, unmatchedAllocations: Array<AllocationEvaluation>, expectedVariationType: VariationType | undefined) => FlagEvaluationDetailsBuilder;
setMatch: (indexPosition: number, variation: Variation, allocation: Allocation, matchedRule: Rule | null, expectedVariationType: VariationType | undefined) => FlagEvaluationDetailsBuilder;
buildForNoneResult: (flagEvaluationCode: FlagEvaluationCode, flagEvaluationDescription: string) => IFlagEvaluationDetails;
build: (flagEvaluationCode: FlagEvaluationCode, flagEvaluationDescription: string) => IFlagEvaluationDetails;
gracefulBuild: (flagEvaluationCode: FlagEvaluationCode, flagEvaluationDescription: string) => IFlagEvaluationDetails | null;
private calculateUnevaluatedAllocations;
}
//# sourceMappingURL=flag-evaluation-details-builder.d.ts.map

@@ -35,2 +35,5 @@ "use strict";

this.unevaluatedAllocations = [];
this.addUnmatchedAllocation = (allocationEvaluation) => {
this.unmatchedAllocations.push(allocationEvaluation);
};
this.setNone = () => {

@@ -41,20 +44,5 @@ this.variationKey = null;

this.matchedAllocation = null;
this.unmatchedAllocations = [];
this.unevaluatedAllocations = this.allocations.map((allocation, i) => ({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: i + 1,
}));
return this;
};
this.setNoMatchFound = (unmatchedAllocations = []) => {
this.variationKey = null;
this.variationValue = null;
this.matchedAllocation = null;
this.matchedRule = null;
this.unmatchedAllocations = unmatchedAllocations;
this.unevaluatedAllocations = [];
return this;
};
this.setMatch = (indexPosition, variation, allocation, matchedRule, unmatchedAllocations, expectedVariationType) => {
this.setMatch = (indexPosition, variation, allocation, matchedRule, expectedVariationType) => {
this.variationKey = variation.key;

@@ -73,10 +61,2 @@ // variation.value needs to be parsed into a JSON object if the variation type is JSON

};
this.unmatchedAllocations = unmatchedAllocations;
const unevaluatedStartIndex = indexPosition + 1;
const unevaluatedStartOrderPosition = unevaluatedStartIndex + 1; // orderPosition is 1-indexed to match UI
this.unevaluatedAllocations = this.allocations.slice(unevaluatedStartIndex).map((allocation, i) => ({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: unevaluatedStartOrderPosition + i,
}));
return this;

@@ -98,4 +78,23 @@ };

unmatchedAllocations: this.unmatchedAllocations,
unevaluatedAllocations: this.unevaluatedAllocations,
unevaluatedAllocations: this.calculateUnevaluatedAllocations(),
});
this.gracefulBuild = (flagEvaluationCode, flagEvaluationDescription) => {
try {
return this.build(flagEvaluationCode, flagEvaluationDescription);
}
catch (err) {
return null;
}
};
this.calculateUnevaluatedAllocations = () => {
const unevaluatedStartIndex = this.matchedAllocation
? this.unmatchedAllocations.length + 1
: this.unmatchedAllocations.length;
const unevaluatedStartOrderPosition = unevaluatedStartIndex + 1; // orderPosition is 1-indexed to match UI
return this.allocations.slice(unevaluatedStartIndex).map((allocation, i) => ({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: unevaluatedStartOrderPosition + i,
}));
};
this.setNone();

@@ -102,0 +101,0 @@ }

{
"name": "@eppo/js-client-sdk-common",
"version": "4.0.1",
"version": "4.0.2",
"description": "Eppo SDK for client-side JavaScript applications (base for both web and react native)",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -147,3 +147,8 @@ import { BANDIT_ASSIGNMENT_SHARDS } from './constants';

actionScoreEntries.forEach(([actionKey, actionScore]) => {
if (currTopScore === null || actionScore > currTopScore) {
if (
currTopScore === null ||
currTopAction === null ||
actionScore > currTopScore ||
(actionScore === currTopScore && actionKey < currTopAction)
) {
currTopScore = actionScore;

@@ -150,0 +155,0 @@ currTopAction = actionKey;

@@ -27,2 +27,3 @@ import ApiEndpoints from '../api-endpoints';

} from '../flag-evaluation-details-builder';
import { FlagEvaluationError } from '../flag-evaluation-error';
import FetchHttpClient from '../http-client';

@@ -683,4 +684,32 @@ import {

);
return this.parseVariationWithDetails(result, defaultValue, expectedVariationType);
} catch (error) {
const eppoValue = this.rethrowIfNotGraceful(error, defaultValue);
if (error instanceof FlagEvaluationError && error.flagEvaluationDetails) {
return {
eppoValue,
flagEvaluationDetails: error.flagEvaluationDetails,
};
} else {
const flagEvaluationDetails = new FlagEvaluationDetailsBuilder(
'',
[],
'',
'',
).buildForNoneResult('ASSIGNMENT_ERROR', `Assignment Error: ${error.message}`);
return {
eppoValue,
flagEvaluationDetails,
};
}
}
}
if (!result.variation) {
private parseVariationWithDetails(
result: FlagEvaluation,
defaultValue: EppoValue,
expectedVariationType: VariationType,
): { eppoValue: EppoValue; flagEvaluationDetails: IFlagEvaluationDetails } {
try {
if (!result.variation || result.flagEvaluationDetails.flagEvaluationCode !== 'MATCH') {
return {

@@ -691,3 +720,2 @@ eppoValue: defaultValue,

}
return {

@@ -699,11 +727,5 @@ eppoValue: EppoValue.valueOf(result.variation.value, expectedVariationType),

const eppoValue = this.rethrowIfNotGraceful(error, defaultValue);
const flagEvaluationDetails = new FlagEvaluationDetailsBuilder(
'',
[],
'',
'',
).buildForNoneResult('ASSIGNMENT_ERROR', `Assignment Error: ${error.message}`);
return {
eppoValue,
flagEvaluationDetails,
flagEvaluationDetails: result.flagEvaluationDetails,
};

@@ -757,5 +779,11 @@ }

if (!checkTypeMatch(expectedVariationType, flag.variationType)) {
throw new TypeError(
`Variation value does not have the correct type. Found: ${flag.variationType} != ${expectedVariationType} for flag ${flagKey}`,
);
const errorMessage = `Variation value does not have the correct type. Found ${flag.variationType}, but expected ${expectedVariationType} for flag ${flagKey}`;
if (this.isGracefulFailureMode) {
const flagEvaluationDetails = flagEvaluationDetailsBuilder.buildForNoneResult(
'TYPE_MISMATCH',
errorMessage,
);
return noneResult(flagKey, subjectKey, subjectAttributes, flagEvaluationDetails);
}
throw new TypeError(errorMessage);
}

@@ -786,12 +814,2 @@

if (result?.variation && !checkValueTypeMatch(expectedVariationType, result.variation.value)) {
const { key: vKey, value: vValue } = result.variation;
const reason = `Expected variation type ${expectedVariationType} does not match for variation '${vKey}' with value ${vValue}`;
const flagEvaluationDetails = flagEvaluationDetailsBuilder.buildForNoneResult(
'TYPE_MISMATCH',
reason,
);
return noneResult(flagKey, subjectKey, subjectAttributes, flagEvaluationDetails);
}
try {

@@ -798,0 +816,0 @@ if (result?.doLog) {

@@ -0,7 +1,9 @@

import { checkValueTypeMatch } from './client/eppo-client';
import {
AllocationEvaluation,
AllocationEvaluationCode,
IFlagEvaluationDetails,
FlagEvaluationDetailsBuilder,
FlagEvaluationCode,
} from './flag-evaluation-details-builder';
import { FlagEvaluationError } from './flag-evaluation-error';
import {

@@ -53,4 +55,74 @@ Flag,

);
try {
if (!flag.enabled) {
return noneResult(
flag.key,
subjectKey,
subjectAttributes,
flagEvaluationDetailsBuilder.buildForNoneResult(
'FLAG_UNRECOGNIZED_OR_DISABLED',
`Unrecognized or disabled flag: ${flag.key}`,
),
);
}
if (!flag.enabled) {
const now = new Date();
for (let i = 0; i < flag.allocations.length; i++) {
const allocation = flag.allocations[i];
const addUnmatchedAllocation = (code: AllocationEvaluationCode) => {
flagEvaluationDetailsBuilder.addUnmatchedAllocation({
key: allocation.key,
allocationEvaluationCode: code,
orderPosition: i + 1,
});
};
if (allocation.startAt && now < new Date(allocation.startAt)) {
addUnmatchedAllocation(AllocationEvaluationCode.BEFORE_START_TIME);
continue;
}
if (allocation.endAt && now > new Date(allocation.endAt)) {
addUnmatchedAllocation(AllocationEvaluationCode.AFTER_END_TIME);
continue;
}
const { matched, matchedRule } = matchesRules(
allocation?.rules ?? [],
{ id: subjectKey, ...subjectAttributes },
obfuscated,
);
if (matched) {
for (const split of allocation.splits) {
if (
split.shards.every((shard) => this.matchesShard(shard, subjectKey, flag.totalShards))
) {
const variation = flag.variations[split.variationKey];
const { flagEvaluationCode, flagEvaluationDescription } =
this.getMatchedEvaluationCodeAndDescription(
variation,
allocation,
split,
subjectKey,
expectedVariationType,
);
const flagEvaluationDetails = flagEvaluationDetailsBuilder
.setMatch(i, variation, allocation, matchedRule, expectedVariationType)
.build(flagEvaluationCode, flagEvaluationDescription);
return {
flagKey: flag.key,
subjectKey,
subjectAttributes,
allocationKey: allocation.key,
variation,
extraLogging: split.extraLogging ?? {},
doLog: allocation.doLog,
flagEvaluationDetails,
};
}
}
// matched, but does not fall within split range
addUnmatchedAllocation(AllocationEvaluationCode.TRAFFIC_EXPOSURE_MISS);
} else {
addUnmatchedAllocation(AllocationEvaluationCode.FAILING_RULE);
}
}
return noneResult(

@@ -61,81 +133,18 @@ flag.key,

flagEvaluationDetailsBuilder.buildForNoneResult(
'FLAG_UNRECOGNIZED_OR_DISABLED',
`Unrecognized or disabled flag: ${flag.key}`,
'DEFAULT_ALLOCATION_NULL',
'No allocations matched. Falling back to "Default Allocation", serving NULL',
),
);
}
const now = new Date();
const unmatchedAllocations: Array<AllocationEvaluation> = [];
for (let i = 0; i < flag.allocations.length; i++) {
const allocation = flag.allocations[i];
const addUnmatchedAllocation = (code: AllocationEvaluationCode) => {
unmatchedAllocations.push({
key: allocation.key,
allocationEvaluationCode: code,
orderPosition: i + 1,
});
};
if (allocation.startAt && now < new Date(allocation.startAt)) {
addUnmatchedAllocation(AllocationEvaluationCode.BEFORE_START_TIME);
continue;
}
if (allocation.endAt && now > new Date(allocation.endAt)) {
addUnmatchedAllocation(AllocationEvaluationCode.AFTER_END_TIME);
continue;
}
const { matched, matchedRule } = matchesRules(
allocation?.rules ?? [],
{ id: subjectKey, ...subjectAttributes },
obfuscated,
} catch (err) {
const flagEvaluationDetails = flagEvaluationDetailsBuilder.gracefulBuild(
'ASSIGNMENT_ERROR',
`Assignment Error: ${err.message}`,
);
if (matched) {
for (const split of allocation.splits) {
if (
split.shards.every((shard) => this.matchesShard(shard, subjectKey, flag.totalShards))
) {
const variation = flag.variations[split.variationKey];
const flagEvaluationDetails = flagEvaluationDetailsBuilder
.setMatch(
i,
variation,
allocation,
matchedRule,
unmatchedAllocations,
expectedVariationType,
)
.build(
'MATCH',
this.getMatchedEvaluationDetailsMessage(allocation, split, subjectKey),
);
return {
flagKey: flag.key,
subjectKey,
subjectAttributes,
allocationKey: allocation.key,
variation,
extraLogging: split.extraLogging ?? {},
doLog: allocation.doLog,
flagEvaluationDetails,
};
}
}
// matched, but does not fall within split range
addUnmatchedAllocation(AllocationEvaluationCode.TRAFFIC_EXPOSURE_MISS);
} else {
addUnmatchedAllocation(AllocationEvaluationCode.FAILING_RULE);
if (flagEvaluationDetails) {
const flagEvaluationError = new FlagEvaluationError(err.message);
flagEvaluationError.flagEvaluationDetails = flagEvaluationDetails;
throw flagEvaluationError;
}
throw err;
}
return noneResult(
flag.key,
subjectKey,
subjectAttributes,
flagEvaluationDetailsBuilder
.setNoMatchFound(unmatchedAllocations)
.build(
'DEFAULT_ALLOCATION_NULL',
'No allocations matched. Falling back to "Default Allocation", serving NULL',
),
);
}

@@ -148,7 +157,16 @@

private getMatchedEvaluationDetailsMessage = (
private getMatchedEvaluationCodeAndDescription = (
variation: Variation,
allocation: Allocation,
split: Split,
subjectKey: string,
): string => {
expectedVariationType: VariationType | undefined,
): { flagEvaluationCode: FlagEvaluationCode; flagEvaluationDescription: string } => {
if (!checkValueTypeMatch(expectedVariationType, variation.value)) {
const { key: vKey, value: vValue } = variation;
return {
flagEvaluationCode: 'ASSIGNMENT_ERROR',
flagEvaluationDescription: `Variation (${vKey}) is configured for type ${expectedVariationType}, but is set to incompatible value (${vValue})`,
};
}
const hasDefinedRules = !!allocation.rules?.length;

@@ -160,8 +178,17 @@ const isExperiment = allocation.splits.length > 1;

if (hasDefinedRules && isExperimentOrPartialRollout) {
return `Supplied attributes match rules defined in allocation "${allocation.key}" and ${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `Supplied attributes match rules defined in allocation "${allocation.key}" and ${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}".`,
};
}
if (hasDefinedRules && !isExperimentOrPartialRollout) {
return `Supplied attributes match rules defined in allocation "${allocation.key}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `Supplied attributes match rules defined in allocation "${allocation.key}".`,
};
}
return `${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}" defined in allocation "${allocation.key}".`;
return {
flagEvaluationCode: 'MATCH',
flagEvaluationDescription: `${subjectKey} belongs to the range of traffic assigned to "${split.variationKey}" defined in allocation "${allocation.key}".`,
};
};

@@ -168,0 +195,0 @@ }

@@ -52,4 +52,4 @@ import { Allocation, Variation, VariationType } from './interfaces';

private matchedAllocation: IFlagEvaluationDetails['matchedAllocation'] = null;
private unmatchedAllocations: IFlagEvaluationDetails['unmatchedAllocations'] = [];
private unevaluatedAllocations: IFlagEvaluationDetails['unevaluatedAllocations'] = [];
private readonly unmatchedAllocations: IFlagEvaluationDetails['unmatchedAllocations'] = [];
private readonly unevaluatedAllocations: IFlagEvaluationDetails['unevaluatedAllocations'] = [];

@@ -65,2 +65,6 @@ constructor(

addUnmatchedAllocation = (allocationEvaluation: AllocationEvaluation) => {
this.unmatchedAllocations.push(allocationEvaluation);
};
setNone = (): FlagEvaluationDetailsBuilder => {

@@ -71,25 +75,5 @@ this.variationKey = null;

this.matchedAllocation = null;
this.unmatchedAllocations = [];
this.unevaluatedAllocations = this.allocations.map(
(allocation, i): AllocationEvaluation => ({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: i + 1,
}),
);
return this;
};
setNoMatchFound = (
unmatchedAllocations: Array<AllocationEvaluation> = [],
): FlagEvaluationDetailsBuilder => {
this.variationKey = null;
this.variationValue = null;
this.matchedAllocation = null;
this.matchedRule = null;
this.unmatchedAllocations = unmatchedAllocations;
this.unevaluatedAllocations = [];
return this;
};
setMatch = (

@@ -100,3 +84,2 @@ indexPosition: number,

matchedRule: Rule | null,
unmatchedAllocations: Array<AllocationEvaluation>,
expectedVariationType: VariationType | undefined,

@@ -117,13 +100,2 @@ ): FlagEvaluationDetailsBuilder => {

};
this.unmatchedAllocations = unmatchedAllocations;
const unevaluatedStartIndex = indexPosition + 1;
const unevaluatedStartOrderPosition = unevaluatedStartIndex + 1; // orderPosition is 1-indexed to match UI
this.unevaluatedAllocations = this.allocations.slice(unevaluatedStartIndex).map(
(allocation, i) =>
({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: unevaluatedStartOrderPosition + i,
} as AllocationEvaluation),
);
return this;

@@ -153,4 +125,30 @@ };

unmatchedAllocations: this.unmatchedAllocations,
unevaluatedAllocations: this.unevaluatedAllocations,
unevaluatedAllocations: this.calculateUnevaluatedAllocations(),
});
gracefulBuild = (
flagEvaluationCode: FlagEvaluationCode,
flagEvaluationDescription: string,
): IFlagEvaluationDetails | null => {
try {
return this.build(flagEvaluationCode, flagEvaluationDescription);
} catch (err) {
return null;
}
};
private calculateUnevaluatedAllocations = (): Array<AllocationEvaluation> => {
const unevaluatedStartIndex = this.matchedAllocation
? this.unmatchedAllocations.length + 1
: this.unmatchedAllocations.length;
const unevaluatedStartOrderPosition = unevaluatedStartIndex + 1; // orderPosition is 1-indexed to match UI
return this.allocations.slice(unevaluatedStartIndex).map(
(allocation, i) =>
({
key: allocation.key,
allocationEvaluationCode: AllocationEvaluationCode.UNEVALUATED,
orderPosition: unevaluatedStartOrderPosition + i,
} as AllocationEvaluation),
);
};
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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