Skip to content
Merged
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 @@
11.10.0 (January 28, 2026)
- Updated @splitsoftware/splitio-commons package to version 2.11.0, which:
- Added functionality to provide metadata alongside SDK update and READY events. Read more in our docs.

11.9.1 (December 18, 2025)
- Bugfix - Updated @splitsoftware/splitio-commons package to version 2.10.1 to handle `null` prerequisites properly

Expand Down
57 changes: 38 additions & 19 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio",
"version": "11.9.1",
"version": "11.10.0",
"description": "Split SDK",
"files": [
"README.md",
Expand Down Expand Up @@ -38,7 +38,7 @@
"node": ">=14.0.0"
},
"dependencies": {
"@splitsoftware/splitio-commons": "2.10.1",
"@splitsoftware/splitio-commons": "2.11.0",
"bloom-filters": "^3.0.4",
"ioredis": "^4.28.0",
"js-yaml": "^3.13.1",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/browserSuites/push-corner-cases.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function testSplitKillOnReadyFromCache(fetchMock, assert) {
});
client.on(client.Event.SDK_READY, () => {
const lapse = Date.now() - start;
assert.true(nearlyEqual(lapse, MILLIS_SPLIT_CHANGES_RESPONSE), 'SDK_READY once split changes arrives');
assert.true(nearlyEqual(lapse, MILLIS_SPLIT_CHANGES_RESPONSE, 200), 'SDK_READY once split changes arrives');

client.destroy().then(() => { assert.end(); });
});
Expand Down
11 changes: 8 additions & 3 deletions src/__tests__/browserSuites/push-synchronization.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const EXPECTED_DELAY_AND_BACKOFF = 241 + 100;
export function testSynchronization(fetchMock, assert) {
// Force the backoff base of UpdateWorkers to reduce test time
Backoff.__TEST__BASE_MILLIS = 100;
assert.plan(34);
assert.plan(39); // +3 for FLAGS_UPDATE metadata, +2 for SEGMENTS_UPDATE metadata
fetchMock.reset();

let start, splitio, client, otherClient, keylistAddClient, keylistRemoveClient, bitmapTrueClient, sharedClients = [];
Expand All @@ -108,7 +108,10 @@ export function testSynchronization(fetchMock, assert) {

setTimeout(() => {
assert.equal(client.getTreatment('whitelist'), 'not_allowed', 'evaluation of initial Split');
client.once(client.Event.SDK_UPDATE, () => {
client.once(client.Event.SDK_UPDATE, (metadata) => {
assert.equal(metadata.type, 'FLAGS_UPDATE', 'SDK_UPDATE for SPLIT_UPDATE should have type FLAGS_UPDATE');
assert.true(Array.isArray(metadata.names), 'metadata.names should be an array');
assert.true(metadata.names.includes('whitelist'), 'metadata.names should include the updated whitelist split');
const lapse = Date.now() - start;
assert.true(nearlyEqual(lapse, MILLIS_FIRST_SPLIT_UPDATE_EVENT), 'SDK_UPDATE due to SPLIT_UPDATE event');
assert.equal(client.getTreatment('whitelist'), 'allowed', 'evaluation of updated Split');
Expand Down Expand Up @@ -202,7 +205,9 @@ export function testSynchronization(fetchMock, assert) {

const timestampUnboundEvent = Date.now();

client.once(client.Event.SDK_UPDATE, () => {
client.once(client.Event.SDK_UPDATE, (metadata) => {
assert.equal(metadata.type, 'SEGMENTS_UPDATE', 'SDK_UPDATE for MEMBERSHIPS_LS_UPDATE should have type SEGMENTS_UPDATE');
assert.true(Array.isArray(metadata.names), 'metadata.names should be an array');
assert.true(nearlyEqual(Date.now() - timestampUnboundEvent, EXPECTED_DELAY_AND_BACKOFF), 'SDK_UPDATE after fetching memberships with a delay');
assert.equal(client.getTreatment('in_large_segment'), 'yes', 'evaluation after myLargeSegment fetch');
});
Expand Down
27 changes: 16 additions & 11 deletions src/__tests__/browserSuites/ready-from-cache.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export default function (fetchMock, assert) {
events: 'https://events.baseurl/readyFromCacheWithData'
};
localStorage.clear();
t.plan(12 * 2 + 3);
t.plan(12 * 2 + 3 + 2); // +2 for SDK_READY and SDK_READY_FROM_CACHE metadata.initialCacheLoad asserts

fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=25&rbSince=-1', () => {
return new Promise(res => { setTimeout(() => res({ status: 200, body: { ff: { ...splitChangesMock1.ff, s: 25 } }, headers: {} }), 200); }); // 400ms is how long it'll take to reply with Splits, no SDK_READY should be emitted before that.
Expand Down Expand Up @@ -225,7 +225,8 @@ export default function (fetchMock, assert) {
t.end();
});

client.on(client.Event.SDK_READY_FROM_CACHE, () => {
client.on(client.Event.SDK_READY_FROM_CACHE, (metadata) => {
t.false(metadata.initialCacheLoad, 'SDK_READY_FROM_CACHE from cache should have initialCacheLoad false');
t.true(Date.now() - startTime < 400, 'It should emit SDK_READY_FROM_CACHE on every client if there was data in the cache and we subscribe on time. Should be considerably faster than actual readiness from the cloud.');
t.equal(client.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation');
});
Expand All @@ -238,7 +239,8 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation');
});

client.on(client.Event.SDK_READY, () => {
client.on(client.Event.SDK_READY, (metadata) => {
t.false(metadata.initialCacheLoad, 'SDK_READY when from cache first should have initialCacheLoad false');
t.true(Date.now() - startTime >= 400, 'It should emit SDK_READY too but after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
Expand Down Expand Up @@ -371,8 +373,8 @@ export default function (fetchMock, assert) {
t.true(Date.now() - startTime >= 400, 'It should emit SDK_READY too but after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client.whenReadyFromCache().then((isReady) => {
t.false(isReady, 'It should be ready from cache before ready (syncing with the cloud).');
client.whenReadyFromCache().then((metadata) => {
t.false(metadata.initialCacheLoad, 'It should be ready from cache before ready (syncing with the cloud).');
t.true(Date.now() - startTime < 50, 'It should resolve ready from cache promise almost immediately.');
});
client.whenReady().then(() => {
Expand All @@ -383,8 +385,8 @@ export default function (fetchMock, assert) {
t.true(Date.now() - startTime >= 700, 'It should emit SDK_READY too but after syncing with the cloud.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.whenReadyFromCache().then((isReady) => {
t.false(isReady, 'It should be ready from cache before ready (syncing with the cloud).');
client2.whenReadyFromCache().then((metadata) => {
t.false(metadata.initialCacheLoad, 'It should be ready from cache before ready (syncing with the cloud).');
});
client2.whenReady().then(() => {
t.true(Date.now() - startTime >= 700, 'It should resolve ready promise after syncing with the cloud.');
Expand Down Expand Up @@ -474,7 +476,9 @@ export default function (fetchMock, assert) {
t.end();
});

client.once(client.Event.SDK_READY_FROM_CACHE, () => {
client.once(client.Event.SDK_READY_FROM_CACHE, (metadata) => {
t.true(metadata.initialCacheLoad, 'SDK_READY_FROM_CACHE on fresh install should have initialCacheLoad true');
t.equal(metadata.lastUpdateTimestamp, undefined, 'lastUpdateTimestamp should be undefined when initialCacheLoad is true');
t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY');
});
client2.once(client2.Event.SDK_READY_FROM_CACHE, () => {
Expand All @@ -484,12 +488,13 @@ export default function (fetchMock, assert) {
t.true(nearlyEqual(Date.now() - startTime, CLIENT3_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY');
});

client.on(client.Event.SDK_READY, () => {
client.on(client.Event.SDK_READY, (metadata) => {
t.true(metadata.initialCacheLoad, 'SDK_READY on fresh install should have initialCacheLoad true');
t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should emit SDK_READY after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client.whenReadyFromCache().then((isReady) => {
t.true(isReady, 'It should be ready from cache and ready.');
client.whenReadyFromCache().then((metadata) => {
t.true(metadata.initialCacheLoad, 'It should be ready from cache and ready.');
t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should resolve ready from cache promise after syncing with the cloud.');
});
client.whenReady().then(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/browserSuites/ready-promise.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ export default function readyPromiseAssertions(fetchMock, assert) {

consoleSpy.log.resetHistory();
setTimeout(() => {
client.whenReadyFromCache().then((isReady) => t.true(isReady, 'SDK IS READY (& READY FROM CACHE) - Should resolve')).catch(() => t.fail('SDK TIMED OUT - Should not reject'));
client.whenReadyFromCache().then((metadata) => t.true(metadata.initialCacheLoad, 'SDK IS READY (& READY FROM CACHE) - Should resolve')).catch(() => t.fail('SDK TIMED OUT - Should not reject'));

assertGetTreatmentWhenReady(t, client);

Expand Down
Loading