From f4df1c9c9a55b1279643efcc3650facfbcc0774e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 20 Oct 2025 15:46:28 -0300 Subject: [PATCH 01/10] feat: add impressionsDisabled option to feature evaluation --- src/evaluator/index.ts | 20 ++++++++++++++------ src/sdkClient/client.ts | 6 +++--- types/splitio.d.ts | 8 ++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index e465b9bf..9b7e2d70 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -30,6 +30,7 @@ export function evaluateFeature( splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, + options?: SplitIO.EvaluationOptions ): MaybeThenable { let parsedSplit; @@ -47,6 +48,7 @@ export function evaluateFeature( split, attributes, storage, + options, )).catch( // Exception on async `getSplit` storage. For example, when the storage is redis or // pluggable and there is a connection issue and we can't retrieve the split to be evaluated @@ -60,6 +62,7 @@ export function evaluateFeature( parsedSplit, attributes, storage, + options, ); } @@ -69,6 +72,7 @@ export function evaluateFeatures( splitNames: string[], attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, + options?: SplitIO.EvaluationOptions, ): MaybeThenable> { let parsedSplits; @@ -80,13 +84,13 @@ export function evaluateFeatures( } return thenable(parsedSplits) ? - parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage)) + parsedSplits.then(splits => getEvaluations(log, key, splitNames, splits, attributes, storage, options)) .catch(() => { // Exception on async `getSplits` storage. For example, when the storage is redis or // pluggable and there is a connection issue and we can't retrieve the split to be evaluated return treatmentsException(splitNames); }) : - getEvaluations(log, key, splitNames, parsedSplits, attributes, storage); + getEvaluations(log, key, splitNames, parsedSplits, attributes, storage, options); } export function evaluateFeaturesByFlagSets( @@ -96,6 +100,7 @@ export function evaluateFeaturesByFlagSets( attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, method: string, + options?: SplitIO.EvaluationOptions, ): MaybeThenable> { let storedFlagNames: MaybeThenable[]>; @@ -111,7 +116,7 @@ export function evaluateFeaturesByFlagSets( } return featureFlags.size ? - evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage) : + evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage, options) : {}; } @@ -138,6 +143,7 @@ function getEvaluation( splitJSON: ISplit | null, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, + options?: SplitIO.EvaluationOptions, ): MaybeThenable { let evaluation: MaybeThenable = { treatment: CONTROL, @@ -154,14 +160,14 @@ function getEvaluation( return evaluation.then(result => { result.changeNumber = splitJSON.changeNumber; result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null; - result.impressionsDisabled = splitJSON.impressionsDisabled; + result.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled; return result; }); } else { evaluation.changeNumber = splitJSON.changeNumber; evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null; - evaluation.impressionsDisabled = splitJSON.impressionsDisabled; + evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled; } } @@ -175,6 +181,7 @@ function getEvaluations( splits: Record, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, + options?: SplitIO.EvaluationOptions, ): MaybeThenable> { const result: Record = {}; const thenables: Promise[] = []; @@ -184,7 +191,8 @@ function getEvaluations( key, splits[splitName], attributes, - storage + storage, + options ); if (thenable(evaluation)) { thenables.push(evaluation.then(res => { diff --git a/src/sdkClient/client.ts b/src/sdkClient/client.ts index 0e526f72..a48e738c 100644 --- a/src/sdkClient/client.ts +++ b/src/sdkClient/client.ts @@ -52,7 +52,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl }; const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ? - evaluateFeature(log, key, featureFlagName, attributes, storage) : + evaluateFeature(log, key, featureFlagName, attributes, storage, options) : isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected Promise.resolve(treatmentNotReady) : treatmentNotReady; @@ -81,7 +81,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl }; const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ? - evaluateFeatures(log, key, featureFlagNames, attributes, storage) : + evaluateFeatures(log, key, featureFlagNames, attributes, storage, options) : isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected Promise.resolve(treatmentsNotReady(featureFlagNames)) : treatmentsNotReady(featureFlagNames); @@ -110,7 +110,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl }; const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ? - evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) : + evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName, options) : isAsync ? Promise.resolve({}) : {}; diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 4062e012..9e93d2eb 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -838,8 +838,16 @@ declare namespace SplitIO { * Evaluation options object for getTreatment methods. */ type EvaluationOptions = { + /** + * Whether the evaluation/s will track impressions or not. + * + * @defaultValue `false` + */ + impressionsDisabled?: boolean; /** * Optional properties to append to the generated impression object sent to Split backend. + * + * @defaultValue `undefined` */ properties?: Properties; } From dd9a44729f0b983532db7229db105199f1835bc9 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 14 Nov 2025 11:57:30 -0300 Subject: [PATCH 02/10] split impressionsDisabled from properties --- src/utils/inputValidation/eventProperties.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/inputValidation/eventProperties.ts b/src/utils/inputValidation/eventProperties.ts index 1306431c..1bc84cc9 100644 --- a/src/utils/inputValidation/eventProperties.ts +++ b/src/utils/inputValidation/eventProperties.ts @@ -70,7 +70,12 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined { if (isObject(maybeOptions)) { const properties = validateEventProperties(log, maybeOptions.properties, method).properties; - return properties && Object.keys(properties).length > 0 ? { properties } : undefined; + let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined; + + const impressionsDisabled = maybeOptions.impressionsDisabled; + if (!impressionsDisabled) return options; + + return options ? { ...options, impressionsDisabled } : { impressionsDisabled }; } else if (maybeOptions) { log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']); } From f12130467f84465f68939d7b1031281e7d693f81 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Wed, 19 Nov 2025 18:02:12 -0300 Subject: [PATCH 03/10] Validate impressionsDisabled type --- .../__tests__/clientInputValidation.spec.ts | 20 ++++++++ .../__tests__/eventProperties.spec.ts | 48 ++++++++++++++++++- src/utils/inputValidation/eventProperties.ts | 15 +++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/sdkClient/__tests__/clientInputValidation.spec.ts b/src/sdkClient/__tests__/clientInputValidation.spec.ts index f2f5648f..a041e88d 100644 --- a/src/sdkClient/__tests__/clientInputValidation.spec.ts +++ b/src/sdkClient/__tests__/clientInputValidation.spec.ts @@ -107,4 +107,24 @@ describe('clientInputValidationDecorator', () => { expect(logSpy).not.toBeCalled(); }); + + test('should ignore the properties in the 4th argument if an empty object is passed', () => { + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: true })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, { impressionsDisabled: true }); + + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: {} })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); + + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: 'true' })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); + + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: null })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); + + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: false })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); // impressionsDisabled false is the default behavior, so we don't pass it along + + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { properties: undefined })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); + }); }); diff --git a/src/utils/inputValidation/__tests__/eventProperties.spec.ts b/src/utils/inputValidation/__tests__/eventProperties.spec.ts index d17f08bd..4908831f 100644 --- a/src/utils/inputValidation/__tests__/eventProperties.spec.ts +++ b/src/utils/inputValidation/__tests__/eventProperties.spec.ts @@ -1,7 +1,7 @@ -import { ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../../logger/constants'; +import { ERROR_NOT_BOOLEAN, ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../../logger/constants'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; -import { validateEventProperties } from '../eventProperties'; +import { validateEventProperties, validateImpressionsDisabled } from '../eventProperties'; function calculateSize(obj: any) { // we calculate the expected size. @@ -193,3 +193,47 @@ describe('INPUT VALIDATION for Event Properties', () => { expect(loggerMock.error).toBeCalledWith(ERROR_SIZE_EXCEEDED, ['some_method_eventProps']); // Should log an error. }); }); + +describe('INPUT VALIDATION for Impressions disabled', () => { + + afterEach(() => { loggerMock.mockClear(); }); + + const impressionsDisabledInvalidValues = [ + [], + () => { }, + 'something', + NaN, + -Infinity, + Infinity, + new Promise(res => res), + Symbol('asd'), + new Map() + ]; + + test('Not setting impressionsDisabled is acceptable', () => { + expect(validateImpressionsDisabled(loggerMock, undefined, 'some_method_eventProps')).toBeUndefined(); + + expect(loggerMock.error).not.toBeCalled(); // Should not log any errors. + expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. + }); + + test('When setting a value for impressionsDisabled, only booleans are acceptable', () => { + + impressionsDisabledInvalidValues.forEach(val => { + expect(validateImpressionsDisabled(loggerMock, val, 'some_method_eventProps')).toBeUndefined(); + expect(loggerMock.error).toBeCalledWith(ERROR_NOT_BOOLEAN, ['some_method_eventProps', 'impressionsDisabled']); // Should log an error. + loggerMock.error.mockClear(); + }); + + expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. + }); + + test('It should return the impressionsDisabled value when valid', () => { + expect(validateImpressionsDisabled(loggerMock, true, 'some_method_eventProps')).toBeTruthy(); + expect(validateImpressionsDisabled(loggerMock, false, 'some_method_eventProps')).toBeFalsy(); + + expect(loggerMock.error).not.toBeCalled(); // Should not log any errors. + expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. + }); + +}); diff --git a/src/utils/inputValidation/eventProperties.ts b/src/utils/inputValidation/eventProperties.ts index 1bc84cc9..583f7044 100644 --- a/src/utils/inputValidation/eventProperties.ts +++ b/src/utils/inputValidation/eventProperties.ts @@ -2,7 +2,7 @@ import { isObject, isString, isFiniteNumber, isBoolean } from '../lang'; import { objectAssign } from '../lang/objectAssign'; import SplitIO from '../../../types/splitio'; import { ILogger } from '../../logger/types'; -import { ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../logger/constants'; +import { ERROR_NOT_PLAIN_OBJECT, ERROR_NOT_BOOLEAN, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../logger/constants'; const ECMA_SIZES = { NULL: 0, // While on the JSON it's going to occupy more space, we'll take it as 0 for the approximation. @@ -14,6 +14,17 @@ const MAX_PROPERTIES_AMOUNT = 300; const MAX_EVENT_SIZE = 1024 * 32; const BASE_EVENT_SIZE = 1024; // We assume 1kb events without properties (avg measured) +export function validateImpressionsDisabled(log: ILogger, maybeImpressionsDisabled: any, method: string): boolean | undefined { + if (maybeImpressionsDisabled === undefined) return; + + if (maybeImpressionsDisabled === null || !isBoolean(maybeImpressionsDisabled)) { + log.error(ERROR_NOT_BOOLEAN, [method, 'impressionsDisabled']); + return; + } + + return maybeImpressionsDisabled; +} + export function validateEventProperties(log: ILogger, maybeProperties: any, method: string): { properties: SplitIO.Properties | null | false, size: number } { if (maybeProperties == undefined) return { properties: null, size: BASE_EVENT_SIZE }; // eslint-disable-line eqeqeq @@ -72,7 +83,7 @@ export function validateEvaluationOptions(log: ILogger, maybeOptions: any, metho const properties = validateEventProperties(log, maybeOptions.properties, method).properties; let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined; - const impressionsDisabled = maybeOptions.impressionsDisabled; + const impressionsDisabled = validateImpressionsDisabled(log, maybeOptions.impressionsDisabled, method); if (!impressionsDisabled) return options; return options ? { ...options, impressionsDisabled } : { impressionsDisabled }; From 57e244f80c0ae88b60e6885a934d4abb1ebb8947 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 25 Nov 2025 17:15:33 -0300 Subject: [PATCH 04/10] Remove innecesary boolean validation --- .../__tests__/eventProperties.spec.ts | 28 +------------------ src/utils/inputValidation/eventProperties.ts | 13 +-------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/src/utils/inputValidation/__tests__/eventProperties.spec.ts b/src/utils/inputValidation/__tests__/eventProperties.spec.ts index 4908831f..fec0962e 100644 --- a/src/utils/inputValidation/__tests__/eventProperties.spec.ts +++ b/src/utils/inputValidation/__tests__/eventProperties.spec.ts @@ -1,7 +1,7 @@ import { ERROR_NOT_BOOLEAN, ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../../logger/constants'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; -import { validateEventProperties, validateImpressionsDisabled } from '../eventProperties'; +import { validateEventProperties } from '../eventProperties'; function calculateSize(obj: any) { // we calculate the expected size. @@ -210,30 +210,4 @@ describe('INPUT VALIDATION for Impressions disabled', () => { new Map() ]; - test('Not setting impressionsDisabled is acceptable', () => { - expect(validateImpressionsDisabled(loggerMock, undefined, 'some_method_eventProps')).toBeUndefined(); - - expect(loggerMock.error).not.toBeCalled(); // Should not log any errors. - expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. - }); - - test('When setting a value for impressionsDisabled, only booleans are acceptable', () => { - - impressionsDisabledInvalidValues.forEach(val => { - expect(validateImpressionsDisabled(loggerMock, val, 'some_method_eventProps')).toBeUndefined(); - expect(loggerMock.error).toBeCalledWith(ERROR_NOT_BOOLEAN, ['some_method_eventProps', 'impressionsDisabled']); // Should log an error. - loggerMock.error.mockClear(); - }); - - expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. - }); - - test('It should return the impressionsDisabled value when valid', () => { - expect(validateImpressionsDisabled(loggerMock, true, 'some_method_eventProps')).toBeTruthy(); - expect(validateImpressionsDisabled(loggerMock, false, 'some_method_eventProps')).toBeFalsy(); - - expect(loggerMock.error).not.toBeCalled(); // Should not log any errors. - expect(loggerMock.warn).not.toBeCalled(); // It should have not logged any warnings. - }); - }); diff --git a/src/utils/inputValidation/eventProperties.ts b/src/utils/inputValidation/eventProperties.ts index 583f7044..7ffaaa28 100644 --- a/src/utils/inputValidation/eventProperties.ts +++ b/src/utils/inputValidation/eventProperties.ts @@ -14,17 +14,6 @@ const MAX_PROPERTIES_AMOUNT = 300; const MAX_EVENT_SIZE = 1024 * 32; const BASE_EVENT_SIZE = 1024; // We assume 1kb events without properties (avg measured) -export function validateImpressionsDisabled(log: ILogger, maybeImpressionsDisabled: any, method: string): boolean | undefined { - if (maybeImpressionsDisabled === undefined) return; - - if (maybeImpressionsDisabled === null || !isBoolean(maybeImpressionsDisabled)) { - log.error(ERROR_NOT_BOOLEAN, [method, 'impressionsDisabled']); - return; - } - - return maybeImpressionsDisabled; -} - export function validateEventProperties(log: ILogger, maybeProperties: any, method: string): { properties: SplitIO.Properties | null | false, size: number } { if (maybeProperties == undefined) return { properties: null, size: BASE_EVENT_SIZE }; // eslint-disable-line eqeqeq @@ -83,7 +72,7 @@ export function validateEvaluationOptions(log: ILogger, maybeOptions: any, metho const properties = validateEventProperties(log, maybeOptions.properties, method).properties; let options = properties && Object.keys(properties).length > 0 ? { properties } : undefined; - const impressionsDisabled = validateImpressionsDisabled(log, maybeOptions.impressionsDisabled, method); + const impressionsDisabled = maybeOptions.impressionsDisabled; if (!impressionsDisabled) return options; return options ? { ...options, impressionsDisabled } : { impressionsDisabled }; From f5b696036f504f04fc7b9a87ad4c57125809cb9d Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 25 Nov 2025 18:45:37 -0300 Subject: [PATCH 05/10] update changes file --- CHANGES.txt | 3 +++ package-lock.json | 4 ++-- package.json | 2 +- .../__tests__/clientInputValidation.spec.ts | 6 +++--- .../__tests__/eventProperties.spec.ts | 20 +------------------ src/utils/inputValidation/eventProperties.ts | 2 +- 6 files changed, 11 insertions(+), 26 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4c71ee9f..fdd3f1ef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +2.9.0 + - Added property `impressionsDisabled` in getTreatment(s) `evaluationOptions` parameter, to disable impressions per evaluations. + 2.8.0 (October 30, 2025) - Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs. - Added `client.getStatus()` method to retrieve the client readiness status properties (`isReady`, `isReadyFromCache`, etc). diff --git a/package-lock.json b/package-lock.json index a6093d8d..ff36cb55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.8.0", + "version": "2.8.1-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-commons", - "version": "2.8.0", + "version": "2.8.1-rc.1", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", diff --git a/package.json b/package.json index ab0a1d2f..8ea04d99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.8.0", + "version": "2.8.1-rc.1", "description": "Split JavaScript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sdkClient/__tests__/clientInputValidation.spec.ts b/src/sdkClient/__tests__/clientInputValidation.spec.ts index a041e88d..3c554c6b 100644 --- a/src/sdkClient/__tests__/clientInputValidation.spec.ts +++ b/src/sdkClient/__tests__/clientInputValidation.spec.ts @@ -112,11 +112,11 @@ describe('clientInputValidationDecorator', () => { expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: true })).toBe(EVALUATION_RESULT); expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, { impressionsDisabled: true }); - expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: {} })).toBe(EVALUATION_RESULT); + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: false })).toBe(EVALUATION_RESULT); expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); - expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: 'true' })).toBe(EVALUATION_RESULT); - expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); + expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: true })).toBe(EVALUATION_RESULT); + expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, { impressionsDisabled: true }); expect(clientWithValidation.getTreatment('key', 'ff', undefined, { impressionsDisabled: null })).toBe(EVALUATION_RESULT); expect(client.getTreatment).toHaveBeenLastCalledWith('key', 'ff', undefined, undefined); diff --git a/src/utils/inputValidation/__tests__/eventProperties.spec.ts b/src/utils/inputValidation/__tests__/eventProperties.spec.ts index fec0962e..d17f08bd 100644 --- a/src/utils/inputValidation/__tests__/eventProperties.spec.ts +++ b/src/utils/inputValidation/__tests__/eventProperties.spec.ts @@ -1,4 +1,4 @@ -import { ERROR_NOT_BOOLEAN, ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../../logger/constants'; +import { ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../../logger/constants'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { validateEventProperties } from '../eventProperties'; @@ -193,21 +193,3 @@ describe('INPUT VALIDATION for Event Properties', () => { expect(loggerMock.error).toBeCalledWith(ERROR_SIZE_EXCEEDED, ['some_method_eventProps']); // Should log an error. }); }); - -describe('INPUT VALIDATION for Impressions disabled', () => { - - afterEach(() => { loggerMock.mockClear(); }); - - const impressionsDisabledInvalidValues = [ - [], - () => { }, - 'something', - NaN, - -Infinity, - Infinity, - new Promise(res => res), - Symbol('asd'), - new Map() - ]; - -}); diff --git a/src/utils/inputValidation/eventProperties.ts b/src/utils/inputValidation/eventProperties.ts index 7ffaaa28..1bc84cc9 100644 --- a/src/utils/inputValidation/eventProperties.ts +++ b/src/utils/inputValidation/eventProperties.ts @@ -2,7 +2,7 @@ import { isObject, isString, isFiniteNumber, isBoolean } from '../lang'; import { objectAssign } from '../lang/objectAssign'; import SplitIO from '../../../types/splitio'; import { ILogger } from '../../logger/types'; -import { ERROR_NOT_PLAIN_OBJECT, ERROR_NOT_BOOLEAN, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../logger/constants'; +import { ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../logger/constants'; const ECMA_SIZES = { NULL: 0, // While on the JSON it's going to occupy more space, we'll take it as 0 for the approximation. From 7be328bf14a59cad1f7e5fbfbbdbe13060c6958b Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 12 Dec 2025 18:51:37 -0300 Subject: [PATCH 06/10] Update CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index fdd3f1ef..c1c28ef7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -2.9.0 +2.9.0 (December 16, 2025) - Added property `impressionsDisabled` in getTreatment(s) `evaluationOptions` parameter, to disable impressions per evaluations. 2.8.0 (October 30, 2025) From fba8bb807a9384910a683d6cb6822d6556e62acc Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 15 Dec 2025 13:31:32 -0300 Subject: [PATCH 07/10] Remove impressionsDisabled property from ts definitions --- CHANGES.txt | 2 +- package-lock.json | 4 ++-- package.json | 2 +- types/splitio.d.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index efa5cd34..8914a5b5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -2.9.1 (December 12, 2025) +2.9.1 (December 16, 2025) - Added property `impressionsDisabled` in getTreatment(s) `evaluationOptions` parameter, to disable impressions per evaluations. 2.9.0 (November 26, 2025) diff --git a/package-lock.json b/package-lock.json index 5eb02926..64dfb274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.1", + "version": "2.9.1-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.1", + "version": "2.9.1-rc.2", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", diff --git a/package.json b/package.json index 937612c2..cec135fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.1", + "version": "2.9.1-rc.2", "description": "Split JavaScript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 5fe3f542..1a505686 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -923,7 +923,7 @@ declare namespace SplitIO { * * @defaultValue `false` */ - impressionsDisabled?: boolean; + // impressionsDisabled?: boolean; /** * Optional properties to append to the generated impression object sent to Split backend. * From f26c5c727728d6a471c28b60d584cb94976c22a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Mon, 15 Dec 2025 14:03:24 -0300 Subject: [PATCH 08/10] Fix lint --- src/evaluator/index.ts | 2 ++ src/utils/inputValidation/eventProperties.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index 9b7e2d70..574a8337 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -160,6 +160,7 @@ function getEvaluation( return evaluation.then(result => { result.changeNumber = splitJSON.changeNumber; result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null; + // @ts-expect-error impressionsDisabled is not exposed in the public typings yet. result.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled; return result; @@ -167,6 +168,7 @@ function getEvaluation( } else { evaluation.changeNumber = splitJSON.changeNumber; evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null; + // @ts-expect-error impressionsDisabled is not exposed in the public typings yet. evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled; } } diff --git a/src/utils/inputValidation/eventProperties.ts b/src/utils/inputValidation/eventProperties.ts index 1bc84cc9..2a6eb73f 100644 --- a/src/utils/inputValidation/eventProperties.ts +++ b/src/utils/inputValidation/eventProperties.ts @@ -75,6 +75,7 @@ export function validateEvaluationOptions(log: ILogger, maybeOptions: any, metho const impressionsDisabled = maybeOptions.impressionsDisabled; if (!impressionsDisabled) return options; + // @ts-expect-error impressionsDisabled is not exposed in the public typings yet. return options ? { ...options, impressionsDisabled } : { impressionsDisabled }; } else if (maybeOptions) { log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']); From 226bc6751e39fd03cb6897d2d48970848625cdbf Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 15 Dec 2025 14:48:45 -0300 Subject: [PATCH 09/10] Update CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8914a5b5..a78d4576 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -2.9.1 (December 16, 2025) +2.10.0 (December 16, 2025) - Added property `impressionsDisabled` in getTreatment(s) `evaluationOptions` parameter, to disable impressions per evaluations. 2.9.0 (November 26, 2025) From af41862811625cbffb1be7c83996b1add06a063a Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Tue, 16 Dec 2025 15:09:07 -0300 Subject: [PATCH 10/10] Update version in package.json --- package-lock.json | 74 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 64dfb274..3f1c79d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.2", + "version": "2.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.2", + "version": "2.10.0", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -483,13 +483,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -2458,11 +2456,12 @@ } }, "node_modules/core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -3663,6 +3662,7 @@ "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz", "integrity": "sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.0.0", "@babel/runtime": "^7.0.0", @@ -3696,6 +3696,7 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, + "license": "MIT", "dependencies": { "punycode": "^2.1.0" } @@ -3704,13 +3705,15 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/fetch-mock/node_modules/whatwg-url": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -4466,8 +4469,9 @@ "node_modules/is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", + "dev": true, + "license": "MIT" }, "node_modules/is-symbol": { "version": "1.0.4", @@ -6494,7 +6498,9 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT" }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -6512,7 +6518,8 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lru-cache": { "version": "6.0.0", @@ -6946,7 +6953,8 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -7074,6 +7082,7 @@ "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.x" } @@ -7149,12 +7158,6 @@ "node": ">=4.0.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8498,13 +8501,10 @@ } }, "@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.14.0" - } + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "dev": true }, "@babel/template": { "version": "7.26.9", @@ -9980,9 +9980,9 @@ } }, "core-js": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", - "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", "dev": true }, "cross-env": { @@ -11440,7 +11440,7 @@ "is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", "dev": true }, "is-symbol": { @@ -13453,12 +13453,6 @@ "promise-queue": "^2.2.5" } }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/package.json b/package.json index cec135fd..b0edc6e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.9.1-rc.2", + "version": "2.10.0", "description": "Split JavaScript SDK common components", "main": "cjs/index.js", "module": "esm/index.js",