From a446160b729b59ec2936ba72b8358363a91bb479 Mon Sep 17 00:00:00 2001 From: Noelia Melina Urruchua Date: Fri, 21 Nov 2025 10:07:19 -0300 Subject: [PATCH 1/2] Replace SonarSource/sonarcloud-github-action --- .github/workflows/ci-cd.yml | 18 +++++------------- .github/workflows/sonar-scan.yml | 9 +++++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index eade2ac..f84bc4f 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -22,10 +22,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 'lts/*' cache: 'npm' @@ -47,7 +47,7 @@ jobs: - name: Store assets if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: assets path: umd/ @@ -62,18 +62,14 @@ jobs: matrix: environment: - stage - # - stage-eu include: - environment: stage account_id: "079419646996" bucket: split-public-stage - # - environment: stage-eu - # account_id: "901851837056" - # bucket: split-public-stage-eu-west-1 steps: - name: Download assets - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: assets path: umd @@ -105,18 +101,14 @@ jobs: matrix: environment: - prod - # - prod-eu include: - environment: prod account_id: "825951051969" bucket: split-public - # - environment: prod-eu - # account_id: "842946900133" - # bucket: split-public-eu-west-1 steps: - name: Download assets - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: assets path: umd diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml index e19a6b9..abb04e2 100644 --- a/.github/workflows/sonar-scan.yml +++ b/.github/workflows/sonar-scan.yml @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 'lts/*' cache: 'npm' @@ -36,7 +36,7 @@ jobs: - name: SonarQube Scan (Push) if: github.event_name == 'push' - uses: SonarSource/sonarcloud-github-action@v1.6 + uses: SonarSource/sonarqube-scan-action@v6 env: SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} with: @@ -47,9 +47,10 @@ jobs: -Dsonar.projectKey=${{ github.event.repository.name }} -Dsonar.links.ci="https://github.com/splitio/${{ github.event.repository.name }}/actions" -Dsonar.links.scm="https://github.com/splitio/${{ github.event.repository.name }}" + - name: SonarQube Scan (Pull Request) if: github.event_name == 'pull_request' - uses: SonarSource/sonarcloud-github-action@v1.6 + uses: SonarSource/sonarqube-scan-action@v6 env: SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} with: From 13d247027eaba57ce09185ca8f88be5e22cae1d8 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 30 Dec 2025 13:59:02 -0300 Subject: [PATCH 2/2] Release v1.6.1 --- CHANGES.txt | 5 ++ package-lock.json | 57 ++++++++++--------- package.json | 4 +- .../browserSuites/ready-from-cache.spec.js | 26 +++++++-- .../consumer/browser_consumer_partial.spec.js | 13 ++++- src/settings/defaults.ts | 2 +- ts-tests/index.ts | 14 +++++ 7 files changed, 83 insertions(+), 38 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c021991..24480f0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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). diff --git a/package-lock.json b/package-lock.json index aa1e9c5..1b33f57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.6.0", + "version": "1.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-browserjs", - "version": "1.6.0", + "version": "1.6.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.8.0", + "@splitsoftware/splitio-commons": "2.10.1", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, @@ -708,10 +708,11 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1396,9 +1397,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.8.0.tgz", - "integrity": "sha512-QgHUreMOEDwf4GZzVPu4AzkZJvuaeSoHsiJc4tT3CxSIYl2bKMz1SSDlI1tW/oVbIFeWjkrIp2lCYEyUBgcvyA==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.10.1.tgz", + "integrity": "sha512-yYmWC1stuH2DvxEXP9xZFOOvw3SkRDjE5UwZ68y011Oev9ILXSN7E2Yd6w8ag85cwbF5GjtaDgT7bOURhyvKww==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -6004,10 +6005,11 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -6516,10 +6518,11 @@ } }, "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", "dev": true, + "license": "MIT", "dependencies": { "dom-walk": "^0.1.0" } @@ -9961,9 +9964,9 @@ } }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -10493,9 +10496,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.8.0.tgz", - "integrity": "sha512-QgHUreMOEDwf4GZzVPu4AzkZJvuaeSoHsiJc4tT3CxSIYl2bKMz1SSDlI1tW/oVbIFeWjkrIp2lCYEyUBgcvyA==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.10.1.tgz", + "integrity": "sha512-yYmWC1stuH2DvxEXP9xZFOOvw3SkRDjE5UwZ68y011Oev9ILXSN7E2Yd6w8ag85cwbF5GjtaDgT7bOURhyvKww==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -13921,9 +13924,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -14324,9 +14327,9 @@ "dev": true }, "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", "dev": true, "requires": { "dom-walk": "^0.1.0" diff --git a/package.json b/package.json index c710d4b..4b1714b 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" }, diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index a2da273..e40d179 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -107,18 +107,34 @@ export const expectedHashWithFilter = '2ce5cc38'; // for SDK key ' { // 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, diff --git a/src/__tests__/consumer/browser_consumer_partial.spec.js b/src/__tests__/consumer/browser_consumer_partial.spec.js index a83386d..d274e8e 100644 --- a/src/__tests__/consumer/browser_consumer_partial.spec.js +++ b/src/__tests__/consumer/browser_consumer_partial.spec.js @@ -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(); @@ -366,7 +373,7 @@ 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 @@ -374,7 +381,7 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) { 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(); diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 199eb8d..f210370 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -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. diff --git a/ts-tests/index.ts b/ts-tests/index.ts index bc6feba..4d5503c 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -278,10 +278,24 @@ client = client.removeAllListeners(); // Ready and destroy let promise: Promise = client.ready(); +promise = client.whenReady(); promise = client.destroy(); promise = SDK.destroy(); // @TODO not public yet // promise = client.flush(); +const promiseWhenReadyFromCache: Promise = 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');