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
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
1.6.1 (December 30, 2025)
- Updated @splitsoftware/splitio-commons package to version 2.10.1 and other transitive dependencies for vulnerability fixes.
- Updated the order of storage operations to prevent inconsistent states when using the `InLocalStorage` module and the browser’s `localStorage` fails due to quota limits.
- Bugfix - Handle `null` prerequisites properly.

1.6.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).
Expand Down
57 changes: 30 additions & 27 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-browserjs",
"version": "1.6.0",
"version": "1.6.1",
"description": "Split SDK for JavaScript on Browser",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down Expand Up @@ -59,7 +59,7 @@
"bugs": "https://github.com/splitio/javascript-browser-client/issues",
"homepage": "https://github.com/splitio/javascript-browser-client#readme",
"dependencies": {
"@splitsoftware/splitio-commons": "2.8.0",
"@splitsoftware/splitio-commons": "2.10.1",
"tslib": "^2.3.1",
"unfetch": "^4.2.0"
},
Expand Down
26 changes: 21 additions & 5 deletions src/__tests__/browserSuites/ready-from-cache.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,34 @@ export const expectedHashWithFilter = '2ce5cc38'; // for SDK key '<fake-token-rf

export default function (fetchMock, assert) {

assert.test(t => { // Testing when we start from scratch
assert.test(t => { // Testing when we start from scratch, with an initial localStorage write operation fail (should retry splitChanges with -1)
const testUrls = {
sdk: 'https://sdk.baseurl/readyFromCacheEmpty',
events: 'https://events.baseurl/readyFromCacheEmpty'
};
localStorage.clear();

// simulate a localStorage failure when saving a FF and a membership
const originalSetItem = localStorage.setItem;
localStorage.setItem = (key, value) => {
if (key.includes('.nicolas@split.io.')) {
throw new Error('localStorage.setItem failed');
}
if (key.includes('.split.')) {
localStorage.setItem = originalSetItem;
throw new Error('localStorage.setItem failed');
}
return originalSetItem.call(localStorage, key, value);
};

t.plan(4);

fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 });
fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: membershipsNicolas });
fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', { status: 200, body: { 'ms': {} } });
fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', { status: 200, body: { 'ms': {} } });
fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 });
fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: splitChangesMock1 }); // retry
fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: membershipsNicolas });
fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: membershipsNicolas }); // retry
fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas2%40split.io', { status: 200, body: { 'ms': {} } });
fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas3%40split.io', { status: 200, body: { 'ms': {} } });

const splitio = SplitFactory({
...baseConfig,
Expand Down
13 changes: 10 additions & 3 deletions src/__tests__/consumer/browser_consumer_partial.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,14 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
sinon.stub(wrapperInstance, 'connect').callsFake(() => { Promise.reject(); });
const getSpy = sinon.spy(wrapperInstance, 'get');

const sdk = SplitFactory(config);
const sdk = SplitFactory({
...config,
fallbackTreatments: {
byFlag: {
'UT_IN_SEGMENT': 'fallback_treatment'
}
}
});

const client = sdk.client();

Expand All @@ -366,15 +373,15 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
assert.true(nearlyEqual(Date.now() - start, 0), 'SDK_READY_TIMED_OUT event is emitted immediately');

// Client methods behave as if the SDK is not ready
assert.equal(await client.getTreatment('UT_IN_SEGMENT'), 'control', 'treatment is control with label not ready.');
assert.equal(await client.getTreatment('UT_IN_SEGMENT'), 'fallback_treatment', 'treatment is fallback_treatment with label not ready.');
assert.true(await client.track('user', 'test.event', 18), 'event is tracked in memory (partial consumer mode).');

// Shared clients will also timeout immediately and behave as if the SDK is not ready
const otherClient = sdk.client('other_user');
otherClient.on(otherClient.Event.SDK_READY_TIMED_OUT, async () => {
assert.true(nearlyEqual(Date.now() - start, 0), 'SDK_READY_TIMED_OUT event is emitted immediately in shared client');

assert.equal(await otherClient.getTreatment('UT_IN_SEGMENT'), 'control', 'treatment is control with label not ready.');
assert.equal(await otherClient.getTreatment('UT_IN_SEGMENT'), 'fallback_treatment', 'treatment is fallback_treatment with label not ready.');
assert.true(await otherClient.track('user', 'test.event', 18), 'event is tracked in memory (partial consumer mode).');

await client.destroy();
Expand Down
2 changes: 1 addition & 1 deletion src/settings/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type SplitIO from '@splitsoftware/splitio-commons/types/splitio';
import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/logger/index';
import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants';

const packageVersion = '1.6.0';
const packageVersion = '1.6.1';

/**
* In browser, the default debug level, can be set via the `localStorage.splitio_debug` item.
Expand Down
14 changes: 14 additions & 0 deletions ts-tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,24 @@ client = client.removeAllListeners();

// Ready and destroy
let promise: Promise<void> = client.ready();
promise = client.whenReady();
promise = client.destroy();
promise = SDK.destroy();
// @TODO not public yet
// promise = client.flush();
const promiseWhenReadyFromCache: Promise<boolean> = client.whenReadyFromCache();

// Get readiness status
let status: SplitIO.ReadinessStatus = client.getStatus();
status = {
isReady: false,
isReadyFromCache: false,
isTimedout: false,
isDestroyed: false,
isOperational: false,
hasTimedout: false,
lastUpdate: 0
};

// We can call getTreatment without a key.
// treatment = client.getTreatment(splitKey, 'mySplit');
Expand Down