Skip to content

Commit eaf04b8

Browse files
Merge pull request #452 from splitio/evaluate-without-impressions
Evaluate without impressions: add `impressionsDisabled` option to feature evaluation
2 parents 661d7d0 + af41862 commit eaf04b8

File tree

8 files changed

+92
-51
lines changed

8 files changed

+92
-51
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2.10.0 (December 16, 2025)
2+
- Added property `impressionsDisabled` in getTreatment(s) `evaluationOptions` parameter, to disable impressions per evaluations.
3+
14
2.9.0 (November 26, 2025)
25
- Updated the SDK’s initial synchronization in Node.js (server-side) to use the `startup.requestTimeoutBeforeReady` and `startup.retriesOnFailureBeforeReady` options to control the timeout and retry behavior of segment requests.
36
- Updated the order of storage operations to prevent inconsistent states when using the `LOCALSTORAGE` storage type and the browser’s `localStorage` fails due to quota limits.

package-lock.json

Lines changed: 34 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-commons",
3-
"version": "2.9.0",
3+
"version": "2.10.0",
44
"description": "Split JavaScript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/evaluator/index.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export function evaluateFeature(
3030
splitName: string,
3131
attributes: SplitIO.Attributes | undefined,
3232
storage: IStorageSync | IStorageAsync,
33+
options?: SplitIO.EvaluationOptions
3334
): MaybeThenable<IEvaluationResult> {
3435
let parsedSplit;
3536

@@ -47,6 +48,7 @@ export function evaluateFeature(
4748
split,
4849
attributes,
4950
storage,
51+
options,
5052
)).catch(
5153
// Exception on async `getSplit` storage. For example, when the storage is redis or
5254
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
@@ -60,6 +62,7 @@ export function evaluateFeature(
6062
parsedSplit,
6163
attributes,
6264
storage,
65+
options,
6366
);
6467
}
6568

@@ -69,6 +72,7 @@ export function evaluateFeatures(
6972
splitNames: string[],
7073
attributes: SplitIO.Attributes | undefined,
7174
storage: IStorageSync | IStorageAsync,
75+
options?: SplitIO.EvaluationOptions,
7276
): MaybeThenable<Record<string, IEvaluationResult>> {
7377
let parsedSplits;
7478

@@ -80,13 +84,13 @@ export function evaluateFeatures(
8084
}
8185

8286
return thenable(parsedSplits) ?
83-
parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage))
87+
parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage, options))
8488
.catch(() => {
8589
// Exception on async `getSplits` storage. For example, when the storage is redis or
8690
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
8791
return treatmentsException(splitNames);
8892
}) :
89-
getEvaluations(log, key, splitNames, parsedSplits, attributes, storage);
93+
getEvaluations(log, key, splitNames, parsedSplits, attributes, storage, options);
9094
}
9195

9296
export function evaluateFeaturesByFlagSets(
@@ -96,6 +100,7 @@ export function evaluateFeaturesByFlagSets(
96100
attributes: SplitIO.Attributes | undefined,
97101
storage: IStorageSync | IStorageAsync,
98102
method: string,
103+
options?: SplitIO.EvaluationOptions,
99104
): MaybeThenable<Record<string, IEvaluationResult>> {
100105
let storedFlagNames: MaybeThenable<Set<string>[]>;
101106

@@ -111,7 +116,7 @@ export function evaluateFeaturesByFlagSets(
111116
}
112117

113118
return featureFlags.size ?
114-
evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage) :
119+
evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage, options) :
115120
{};
116121
}
117122

@@ -138,6 +143,7 @@ function getEvaluation(
138143
splitJSON: ISplit | null,
139144
attributes: SplitIO.Attributes | undefined,
140145
storage: IStorageSync | IStorageAsync,
146+
options?: SplitIO.EvaluationOptions,
141147
): MaybeThenable<IEvaluationResult> {
142148
let evaluation: MaybeThenable<IEvaluationResult> = {
143149
treatment: CONTROL,
@@ -154,14 +160,16 @@ function getEvaluation(
154160
return evaluation.then(result => {
155161
result.changeNumber = splitJSON.changeNumber;
156162
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
157-
result.impressionsDisabled = splitJSON.impressionsDisabled;
163+
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
164+
result.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
158165

159166
return result;
160167
});
161168
} else {
162169
evaluation.changeNumber = splitJSON.changeNumber;
163170
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
164-
evaluation.impressionsDisabled = splitJSON.impressionsDisabled;
171+
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
172+
evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
165173
}
166174
}
167175

@@ -175,6 +183,7 @@ function getEvaluations(
175183
splits: Record<string, ISplit | null>,
176184
attributes: SplitIO.Attributes | undefined,
177185
storage: IStorageSync | IStorageAsync,
186+
options?: SplitIO.EvaluationOptions,
178187
): MaybeThenable<Record<string, IEvaluationResult>> {
179188
const result: Record<string, IEvaluationResult> = {};
180189
const thenables: Promise<void>[] = [];
@@ -184,7 +193,8 @@ function getEvaluations(
184193
key,
185194
splits[splitName],
186195
attributes,
187-
storage
196+
storage,
197+
options
188198
);
189199
if (thenable(evaluation)) {
190200
thenables.push(evaluation.then(res => {

src/sdkClient/__tests__/clientInputValidation.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,24 @@ describe('clientInputValidationDecorator', () => {
107107

108108
expect(logSpy).not.toBeCalled();
109109
});
110+
111+
test('should ignore the properties in the 4th argument if an empty object is passed', () => {
112+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: true })).toBe(EVALUATION_RESULT);
113+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, { impressionsDisabled: true });
114+
115+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: false })).toBe(EVALUATION_RESULT);
116+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined);
117+
118+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: true })).toBe(EVALUATION_RESULT);
119+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, { impressionsDisabled: true });
120+
121+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: null })).toBe(EVALUATION_RESULT);
122+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined);
123+
124+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: false })).toBe(EVALUATION_RESULT);
125+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); // impressionsDisabled false is the default behavior, so we don't pass it along
126+
127+
expect(clientWithValidation.getTreatment('key', 'ff', undefined, { properties: undefined })).toBe(EVALUATION_RESULT);
128+
expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined);
129+
});
110130
});

src/sdkClient/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
5252
};
5353

5454
const evaluation = readinessManager.isReadyFromCache() ?
55-
evaluateFeature(log, key, featureFlagName, attributes, storage) :
55+
evaluateFeature(log, key, featureFlagName, attributes, storage, options) :
5656
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
5757
Promise.resolve(treatmentNotReady) :
5858
treatmentNotReady;
@@ -81,7 +81,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
8181
};
8282

8383
const evaluations = readinessManager.isReadyFromCache() ?
84-
evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
84+
evaluateFeatures(log, key, featureFlagNames, attributes, storage, options) :
8585
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
8686
Promise.resolve(treatmentsNotReady(featureFlagNames)) :
8787
treatmentsNotReady(featureFlagNames);
@@ -110,7 +110,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
110110
};
111111

112112
const evaluations = readinessManager.isReadyFromCache() ?
113-
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
113+
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName, options) :
114114
isAsync ?
115115
Promise.resolve({}) :
116116
{};

src/utils/inputValidation/eventProperties.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth
7070
export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined {
7171
if (isObject(maybeOptions)) {
7272
const properties = validateEventProperties(log, maybeOptions.properties, method).properties;
73-
return properties && Object.keys(properties).length > 0 ? { properties } : undefined;
73+
let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined;
74+
75+
const impressionsDisabled = maybeOptions.impressionsDisabled;
76+
if (!impressionsDisabled) return options;
77+
78+
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
79+
return options ? { ...options, impressionsDisabled } : { impressionsDisabled };
7480
} else if (maybeOptions) {
7581
log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']);
7682
}

0 commit comments

Comments
 (0)