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
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
11.7.1 (October 8, 2025)
- Bugfix - Updated @splitsoftware/splitio-commons package to version 2.7.1, which fixes the `debug` option to support log levels when the `logger` option is used.

11.7.0 (October 7, 2025)
- Added support for custom loggers: added `logger` configuration option and `factory.Logger.setLogger` method to allow the SDK to use a custom logger.
- Updated @splitsoftware/splitio-commons package to version 2.7.0.
Expand Down
18 changes: 9 additions & 9 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.7.0",
"version": "11.7.1",
"description": "Split SDK",
"files": [
"README.md",
Expand Down Expand Up @@ -38,7 +38,7 @@
"node": ">=14.0.0"
},
"dependencies": {
"@splitsoftware/splitio-commons": "2.7.0",
"@splitsoftware/splitio-commons": "2.7.9-rc.1",
"bloom-filters": "^3.0.4",
"ioredis": "^4.28.0",
"js-yaml": "^3.13.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,20 @@ 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.ready().then(() => {
client.whenReady().then(() => {
t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.on(client2.Event.SDK_READY, () => {
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.ready().then(() => {
client2.whenReady().then(() => {
t.true(Date.now() - startTime >= 700, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY, () => {
client3.ready().then(() => {
client3.whenReady().then(() => {
t.true(Date.now() - startTime >= 1000, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');

Expand All @@ -196,7 +196,7 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY_TIMED_OUT, () => {
client3.ready().catch(() => {
client3.whenReady().catch(() => {
t.true(Date.now() - startTime >= 850, 'It should reject ready promise before syncing memberships data with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with memberships data from cache.');
});
Expand Down Expand Up @@ -280,20 +280,20 @@ export default function (fetchMock, assert) {
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.ready().then(() => {
client.whenReady().then(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.on(client2.Event.SDK_READY, () => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should emit SDK_READY 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.ready().then(() => {
client2.whenReady().then(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY, () => {
client3.ready().then(() => {
client3.whenReady().then(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT3_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');

Expand All @@ -310,7 +310,7 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY_TIMED_OUT, () => {
client3.ready().catch(() => {
client3.whenReady().catch(() => {
t.true(Date.now() - startTime >= 850, 'It should reject ready promise before syncing memberships data with the cloud.');
t.equal(client3.getTreatment('always_on'), 'control', 'It should not evaluate treatments with memberships data from cache.');
});
Expand Down Expand Up @@ -359,7 +359,7 @@ export default function (fetchMock, assert) {
t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true');
});

await client.ready();
await client.whenReady();

t.true(console.log.calledWithMatch('clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache'), 'It should log a message about cleaning up cache');

Expand Down
56 changes: 40 additions & 16 deletions src/__tests__/browserSuites/ready-from-cache.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,20 +226,20 @@ 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.ready().then(() => {
client.whenReady().then(() => {
t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.on(client2.Event.SDK_READY, () => {
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.ready().then(() => {
client2.whenReady().then(() => {
t.true(Date.now() - startTime >= 700, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY, () => {
client3.ready().then(() => {
client3.whenReady().then(() => {
t.true(Date.now() - startTime >= 1000, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');

Expand All @@ -254,7 +254,7 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY_TIMED_OUT, () => {
client3.ready().catch(() => {
client3.whenReady().catch(() => {
t.true(Date.now() - startTime >= 850, 'It should reject ready promise before syncing memberships data with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with memberships data from cache.');
});
Expand All @@ -269,7 +269,7 @@ export default function (fetchMock, assert) {
events: 'https://events.baseurl/readyFromCacheWithData3'
};
localStorage.clear();
t.plan(12 * 2 + 5);
t.plan(12 * 2 + 14);

fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=25&rbSince=-1', () => {
t.equal(localStorage.getItem('readyFromCache_3.SPLITIO.split.always_on'), alwaysOnSplitInverted, 'feature flags must not be cleaned from cache');
Expand All @@ -285,8 +285,16 @@ export default function (fetchMock, assert) {
return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s
});
fetchMock.get(testUrls.sdk + '/memberships/nicolas4%40split.io', { 'ms': {} });
fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200);
fetchMock.postOnce(testUrls.events + '/testImpressions/count', 200);
fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', (_, opts) => {
const payload = JSON.parse(opts.body);
t.equal(payload.length, 1, 'Only one flag was evaluated');
t.equal(payload[0].i.length, 14, '14 impressions were queued, one per getTreatment call');
t.equal(payload[0].i.filter((imp) => imp.t === 'control').length, 2, '2 impressions were queued for control (not ready from cache)');
t.equal(payload[0].i.filter((imp) => imp.t === 'off').length, 4, '4 impressions were queued for off (ready from cache)');
t.equal(payload[0].i.filter((imp) => imp.t === 'on').length, 8, '8 impressions were queued for on (ready)');

return 200;
});

localStorage.setItem('some_user_item', 'user_item');
localStorage.setItem('readyFromCache_3.SPLITIO.splits.till', 25);
Expand All @@ -301,6 +309,9 @@ export default function (fetchMock, assert) {
type: 'LOCALSTORAGE',
prefix: 'readyFromCache_3'
},
sync: {
impressionsMode: 'DEBUG'
},
startup: {
readyTimeout: 0.85
},
Expand All @@ -320,6 +331,8 @@ export default function (fetchMock, assert) {

client.on(client.Event.SDK_READY_FROM_CACHE, () => {
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.false(client.__getStatus().isReady, 'It should not be ready yet');

t.equal(client.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation');

const client4 = splitio.client('nicolas4@split.io');
Expand All @@ -342,20 +355,27 @@ 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.ready().then(() => {
client.whenReadyFromCache().then((isReady) => {
t.false(isReady, '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(() => {
t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.on(client2.Event.SDK_READY, () => {
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.ready().then(() => {
client2.whenReadyFromCache().then((isReady) => {
t.false(isReady, '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.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY, () => {
client3.ready().then(() => {
client3.whenReady().then(() => {
t.true(Date.now() - startTime >= 1000, 'It should resolve ready promise after syncing with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');

Expand All @@ -370,7 +390,7 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY_TIMED_OUT, () => {
client3.ready().catch(() => {
client3.whenReady().catch(() => {
t.true(Date.now() - startTime >= 850, 'It should reject ready promise before syncing memberships data with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with memberships data from cache.');
});
Expand Down Expand Up @@ -452,20 +472,24 @@ export default function (fetchMock, assert) {
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.ready().then(() => {
client.whenReadyFromCache().then((isReady) => {
t.true(isReady, '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(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client2.on(client2.Event.SDK_READY, () => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should emit SDK_READY 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.ready().then(() => {
client2.whenReady().then(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY, () => {
client3.ready().then(() => {
client3.whenReady().then(() => {
t.true(nearlyEqual(Date.now() - startTime, CLIENT3_READY_MS), 'It should resolve ready promise after syncing with the cloud.');
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');

Expand All @@ -482,7 +506,7 @@ export default function (fetchMock, assert) {
t.equal(client3.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
});
client3.on(client3.Event.SDK_READY_TIMED_OUT, () => {
client3.ready().catch(() => {
client3.whenReady().catch(() => {
t.true(Date.now() - startTime >= 850, 'It should reject ready promise before syncing memberships data with the cloud.');
t.equal(client3.getTreatment('always_on'), 'control', 'It should not evaluate treatments with memberships data from cache.');
});
Expand Down Expand Up @@ -898,7 +922,7 @@ export default function (fetchMock, assert) {
t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true');
});

await client.ready();
await client.whenReady();

t.true(console.log.calledWithMatch('clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache'), 'It should log a message about cleaning up cache');

Expand Down
Loading