Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
3.2.0 (August 28, 2026)
- Added support for AI configs.
- Removed Configs SDK-related types and modules and moved them to the Configs SDK repository.

3.1.0 (July 23, 2026)
- Added support for rule-based segments in /api/v1/configs endpoint.
- Updated polling flow to fetch new referenced segments immediately.
Expand Down
102 changes: 62 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio-commons",
"version": "3.1.0",
"version": "3.1.1-rc.4",
"description": "Split JavaScript SDK common components",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down
7 changes: 7 additions & 0 deletions src/dtos/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ export interface IRBSegment extends TargetingEntity {
} | null
}

export type ConfigType = 'standard' | 'ai';
export type ConfigSubtype = 'llm_call';

export interface IDefinition extends TargetingEntity {
trafficTypeName: string;
sets?: string[] | null;
Expand All @@ -231,6 +234,10 @@ export interface IDefinition extends TargetingEntity {
configurations?: {
[treatmentName: string]: string | SplitIO.JsonObject
} | null;
/** Definition classification. Absent means a feature flag. */
type?: ConfigType;
/** Only meaningful when `type === 'ai'`. */
subtype?: ConfigSubtype;
}

/** Interface of the parsed JSON response of `/splitChanges` */
Expand Down
6 changes: 3 additions & 3 deletions src/evaluator/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { CONTROL } from '../utils/constants';
import { IDefinition, MaybeThenable } from '../dtos/types';
import SplitIO from '../../types/splitio';
import { IStorageAsync, IStorageSync } from '../storages/types';
import { IEvaluation, IEvaluationResult, IDefinitionEvaluator } from './types';
import { IEvaluation, IDefinitionEvaluator } from './types';
import { ILogger } from '../logger/types';
import { ENGINE_DEFAULT } from '../logger/constants';
import { prerequisitesMatcherContext } from './matchers/prerequisites';

function evaluationResult(result: IEvaluation | undefined, defaultTreatment: string): IEvaluationResult {
function evaluationResult(result: IEvaluation | undefined, defaultTreatment: string): IEvaluation {
return {
treatment: get(result, 'treatment', defaultTreatment),
label: get(result, 'label', NO_CONDITION_MATCH)
Expand All @@ -29,7 +29,7 @@ export function engineParser(log: ILogger, split: IDefinition, storage: IStorage

return {

getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: IDefinitionEvaluator): MaybeThenable<IEvaluationResult> {
getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: IDefinitionEvaluator): MaybeThenable<IEvaluation> {

const parsedKey = keyParser(key);

Expand Down
34 changes: 17 additions & 17 deletions src/evaluator/__tests__/evaluate-feature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ test('EVALUATOR / should return label exception, treatment control and config nu
});


test('EVALUATOR / should return right label, treatment and config if storage returns without errors.', async () => {
test('EVALUATOR / should return right label, treatment and definition if storage returns without errors.', async () => {
const expectedOutput = {
treatment: 'on', label: 'in segment all',
config: '{color:\'black\'}', changeNumber: 1487277320548
treatment: 'on', label: 'in segment all', definition: splitsMock['config'],
config: '{color:\'black\'}'
};
const expectedOutputControl = {
treatment: 'control', label: DEFINITION_NOT_FOUND, config: null
Expand All @@ -63,7 +63,7 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationWithConfig).toEqual(expectedOutput); // If the split is retrieved successfully we should get the right evaluation result, label and config.
expect(evaluationWithConfig).toEqual(expectedOutput); // If the split is retrieved successfully we should get the right evaluation result, label and definition.

const evaluationNotFound = evaluateFeature(
loggerMock,
Expand All @@ -81,7 +81,7 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluation).toEqual({ ...expectedOutput, config: null }); // If the split is retrieved successfully we should get the right evaluation result, label and config. If Split has no config it should have config equal null.
expect(evaluation).toEqual({ ...expectedOutput, definition: splitsMock['regular'], config: null }); // If the split is retrieved successfully we should get the right evaluation result, label and config. If Split has no config it should have config equal null.

const evaluationKilled = evaluateFeature(
loggerMock,
Expand All @@ -90,8 +90,8 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationKilled).toEqual({ ...expectedOutput, treatment: 'off', config: null, label: SPLIT_KILLED });
// If the split is retrieved but is killed, we should get the right evaluation result, label and config.
expect(evaluationKilled).toEqual({ ...expectedOutput, treatment: 'off', label: SPLIT_KILLED, definition: splitsMock['killed'], config: null });
// If the split is retrieved but is killed, we should get the right evaluation result, label and definition.

const evaluationArchived = evaluateFeature(
loggerMock,
Expand All @@ -100,8 +100,8 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationArchived).toEqual({ ...expectedOutput, treatment: 'control', label: SPLIT_ARCHIVED, config: null });
// If the split is retrieved but is archived, we should get the right evaluation result, label and config.
expect(evaluationArchived).toEqual({ ...expectedOutput, treatment: 'control', label: SPLIT_ARCHIVED, definition: splitsMock['archived'], config: null });
// If the split is retrieved but is archived, we should get the right evaluation result, label and definition.

const evaluationtrafficAlocation1 = evaluateFeature(
loggerMock,
Expand All @@ -110,8 +110,8 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationtrafficAlocation1).toEqual({ ...expectedOutput, label: NOT_IN_SPLIT, config: null, treatment: 'off' });
// If the split is retrieved but is not in split (out of Traffic Allocation), we should get the right evaluation result, label and config.
expect(evaluationtrafficAlocation1).toEqual({ ...expectedOutput, label: NOT_IN_SPLIT, treatment: 'off', definition: splitsMock['trafficAlocation1'], config: null });
// If the split is retrieved but is not in split (out of Traffic Allocation), we should get the right evaluation result, label and definition.

const evaluationKilledWithConfig = evaluateFeature(
loggerMock,
Expand All @@ -120,8 +120,8 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationKilledWithConfig).toEqual({ ...expectedOutput, treatment: 'off', label: SPLIT_KILLED });
// If the split is retrieved but is killed, we should get the right evaluation result, label and config.
expect(evaluationKilledWithConfig).toEqual({ ...expectedOutput, treatment: 'off', label: SPLIT_KILLED, definition: splitsMock['killedWithConfig'] });
// If the split is retrieved but is killed, we should get the right evaluation result, label and definition.

const evaluationArchivedWithConfig = evaluateFeature(
loggerMock,
Expand All @@ -130,8 +130,8 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationArchivedWithConfig).toEqual({ ...expectedOutput, treatment: 'control', label: SPLIT_ARCHIVED, config: null });
// If the split is retrieved but is archived, we should get the right evaluation result, label and config.
expect(evaluationArchivedWithConfig).toEqual({ ...expectedOutput, treatment: 'control', label: SPLIT_ARCHIVED, definition: splitsMock['archivedWithConfig'], config: null });
// If the split is retrieved but is archived, we should get the right evaluation result, label and definition.

const evaluationtrafficAlocation1WithConfig = evaluateFeature(
loggerMock,
Expand All @@ -140,7 +140,7 @@ test('EVALUATOR / should return right label, treatment and config if storage ret
undefined,
mockStorage,
);
expect(evaluationtrafficAlocation1WithConfig).toEqual({ ...expectedOutput, label: NOT_IN_SPLIT, treatment: 'off' });
// If the split is retrieved but is not in split (out of Traffic Allocation), we should get the right evaluation result, label and config.
expect(evaluationtrafficAlocation1WithConfig).toEqual({ ...expectedOutput, label: NOT_IN_SPLIT, treatment: 'off', definition: splitsMock['trafficAlocation1WithConfig'] });
// If the split is retrieved but is not in split (out of Traffic Allocation), we should get the right evaluation result, label and definition.

});
Loading