From 63852c35b5aff42efccd044e71227bd97ef527ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:13:05 +0000 Subject: [PATCH 01/11] Bump actions/download-artifact from 3 to 4.1.7 in /.github/workflows Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.1.7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index f55585a..1926bfa 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -74,7 +74,7 @@ jobs: steps: - name: Download assets - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: assets path: umd @@ -117,7 +117,7 @@ jobs: steps: - name: Download assets - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: assets path: umd From 29d56fdcd6a9c4a4952180f641fb69fff71309d3 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 20 Dec 2024 13:11:19 -0300 Subject: [PATCH 02/11] Update tests, changelog entry and prepare rc --- .github/workflows/ci-cd.yml | 4 +- CHANGES.txt | 6 + README.md | 2 +- package-lock.json | 30 ++--- package.json | 4 +- .../browserSuites/ready-from-cache.spec.js | 111 +++++++++++++++--- src/__tests__/testUtils/index.js | 4 +- src/settings/defaults.ts | 2 +- ts-tests/index.ts | 4 +- types/full/index.d.ts | 2 +- types/index.d.ts | 2 +- 11 files changed, 128 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index f55585a..8777341 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -47,7 +47,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/cache_expiration' || github.ref == 'refs/heads/main') }} uses: actions/upload-artifact@v3 with: name: assets @@ -58,7 +58,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cache_expiration' }} strategy: matrix: environment: diff --git a/CHANGES.txt b/CHANGES.txt index aa54aa4..52a42a0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,9 @@ +1.1.0 (January XX, 2025) + - Added two new configuration options for the SDK's `InLocalStorage` module to control the behavior of the persisted rollout plan cache in the browser: + - `expirationDays` to specify the validity period of the rollout plan cache in days. + - `clearOnInit` to clear the rollout plan cache on SDK initialization. + - Updated @splitsoftware/splitio-commons package to version 2.1.0. + 1.0.1 (November 11, 2024) - Bugfixing - Revert removal of TypeScript `SplitIO` namespace at `/types/splitio.d.ts` to allow explicit imports of types from the Browser SDK package. E.g., `import type { IClientSideSettings } from '@splitsoftware/splitio-browserjs/types/splitio';`. diff --git a/README.md b/README.md index 2c68837..c3a9f82 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,4 @@ For a comprehensive list of open source projects visit our [Github page](https:/ **Learn more about Split:** -Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](http://help.split.io) for more detailed information. +Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](https://help.split.io) for more detailed information. diff --git a/package-lock.json b/package-lock.json index 5485e80..5d06b65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.0.1", + "version": "1.1.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-browserjs", - "version": "1.0.1", + "version": "1.1.0-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0", + "@splitsoftware/splitio-commons": "2.1.0-rc.0", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, @@ -1542,9 +1542,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.1.0-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0-rc.0.tgz", + "integrity": "sha512-5JJs9ZlJ0to15Azk243wNL22sU7U9quZDcC+8o9NHRBwnrya8euFjuzznqXmGAyyj/Gklrbohwfg+1SOmXXfQg==", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -3039,9 +3039,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -10688,9 +10688,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.1.0-rc.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0-rc.0.tgz", + "integrity": "sha512-5JJs9ZlJ0to15Azk243wNL22sU7U9quZDcC+8o9NHRBwnrya8euFjuzznqXmGAyyj/Gklrbohwfg+1SOmXXfQg==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -11788,9 +11788,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", diff --git a/package.json b/package.json index 322a961..091a456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.0.1", + "version": "1.1.0-rc.0", "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.0.0", + "@splitsoftware/splitio-commons": "2.1.0-rc.0", "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 b41220f..c7ecd22 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -1,3 +1,5 @@ +import sinon from 'sinon'; +import { nearlyEqual } from '../testUtils'; import { getStorageHash } from '@splitsoftware/splitio-commons/src/storages/KeyBuilder'; import { SplitFactory, InLocalStorage } from '../../'; @@ -5,8 +7,6 @@ import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json'; import membershipsNicolas from '../mocks/memberships.nicolas@split.io.json'; -import { nearlyEqual } from '../testUtils'; - const DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days const alwaysOnSplitInverted = JSON.stringify({ @@ -177,7 +177,6 @@ export default function (fetchMock, assert) { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -245,7 +244,7 @@ export default function (fetchMock, assert) { }); }); - assert.test(t => { // Testing when we start with cached data and not expired (lastUpdate item higher than expirationTimestamp) + assert.test(t => { // Testing when we start with cached data and not expired (lastUpdate timestamp higher than default (10) expirationDays ago) const testUrls = { sdk: 'https://sdk.baseurl/readyFromCacheWithData3', events: 'https://events.baseurl/readyFromCacheWithData3' @@ -287,7 +286,6 @@ export default function (fetchMock, assert) { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -362,7 +360,7 @@ export default function (fetchMock, assert) { }); }); - assert.test(t => { // Testing when we start with cached data but expired (lastUpdate item lower than expirationTimestamp) + assert.test(t => { // Testing when we start with cached data but expired (lastUpdate timestamp lower than custom (1) expirationDays ago) const testUrls = { sdk: 'https://sdk.baseurl/readyFromCacheWithData4', events: 'https://events.baseurl/readyFromCacheWithData4' @@ -372,7 +370,8 @@ export default function (fetchMock, assert) { fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { t.equal(localStorage.getItem('some_user_item'), 'user_item', 'user items at localStorage must not be changed'); t.equal(localStorage.getItem('readyFromCache_4.SPLITIO.hash'), expectedHashNullFilter, 'storage hash must not be changed'); - t.equal(localStorage.length, 2, 'feature flags cache data must be cleaned from localStorage'); + t.true(nearlyEqual(parseInt(localStorage.getItem('readyFromCache_4.SPLITIO.lastClear'), 10), Date.now()), 'storage lastClear timestamp must be updated'); + t.equal(localStorage.length, 3, 'feature flags cache data must be cleaned from localStorage'); return { status: 200, body: splitChangesMock1 }; }); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); @@ -390,7 +389,7 @@ export default function (fetchMock, assert) { localStorage.setItem('some_user_item', 'user_item'); localStorage.setItem('readyFromCache_4.SPLITIO.splits.till', 25); - localStorage.setItem('readyFromCache_4.SPLITIO.splits.lastUpdated', Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS - 1); // -1 to ensure having an expired lastUpdated item + localStorage.setItem('readyFromCache_4.SPLITIO.splits.lastUpdated', Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS / 10 - 1); // -1 to ensure having an expired lastUpdated item localStorage.setItem('readyFromCache_4.SPLITIO.split.always_on', alwaysOnSplitInverted); localStorage.setItem('readyFromCache_4.SPLITIO.hash', expectedHashNullFilter); @@ -398,13 +397,13 @@ export default function (fetchMock, assert) { const splitio = SplitFactory({ ...baseConfig, storage: InLocalStorage({ - prefix: 'readyFromCache_4' + prefix: 'readyFromCache_4', + expirationDays: 1 }), startup: { readyTimeout: 0.85 }, urls: testUrls, - debug: true }); const client = splitio.client(); const client2 = splitio.client('nicolas2@split.io'); @@ -502,7 +501,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p2__split', 'p1__split'] }, { type: 'byName', values: ['p2__split', null] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -547,7 +545,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p2__split', 'p1__split'] }, { type: 'byName', values: ['p2__split', null] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -597,7 +594,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: [undefined, true, 'p2__split'] }, { type: 'byPrefix', values: ['p1'] }, { type: 'byName', values: ['p2__split'] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -642,13 +638,13 @@ export default function (fetchMock, assert) { const splitio = SplitFactory({ ...baseConfig, storage: InLocalStorage({ - prefix: 'readyFromCache_7' + prefix: 'readyFromCache_7', + expirationDays: 0, // invalid value, will use default (10) }), urls: testUrls, sync: { splitFilters: [{ type: 'byPrefix', values: ['p2'] }, { type: 'byPrefix', values: ['p1', ''] }, { type: '', values: [] }, {}, { type: 'byPrefix' }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -710,7 +706,6 @@ export default function (fetchMock, assert) { }), urls: testUrls, sync: syncParam, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -764,7 +759,6 @@ export default function (fetchMock, assert) { sync: { splitFilters: [{ type: 'byName', values: ['p3__split'] }, { type: 'byPrefix', values: [' p2', ' p2', ' p2', ' p2', 'no exist trim '] }, { type: 'byName', values: ['no_exist', ' no exist trim '] }] }, - debug: true }); const client = splitio.client(); const manager = splitio.manager(); @@ -788,4 +782,87 @@ export default function (fetchMock, assert) { }); }); + assert.test(async t => { // Testing clearOnInit config true + sinon.spy(console, 'log'); + + const testUrls = { + sdk: 'https://sdk.baseurl/readyFromCache_10', + events: 'https://events.baseurl/readyFromCache_10' + }; + const clearOnInitConfig = { + ...baseConfig, + storage: InLocalStorage({ + type: 'LOCALSTORAGE', + prefix: 'readyFromCache_10', + clearOnInit: true + }), + urls: testUrls, + debug: true + }; + + // Start with cached data but without lastClear item (JS SDK below 11.1.0) -> cache cleanup + localStorage.clear(); + localStorage.setItem('readyFromCache_10.SPLITIO.splits.till', 25); + localStorage.setItem('readyFromCache_10.SPLITIO.split.p1__split', JSON.stringify(splitDeclarations.p1__split)); + localStorage.setItem('readyFromCache_10.SPLITIO.split.p2__split', JSON.stringify(splitDeclarations.p2__split)); + localStorage.setItem('readyFromCache_10.SPLITIO.hash', expectedHashNullFilter); + + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); + + let splitio = SplitFactory(clearOnInitConfig); + let client = splitio.client(); + let manager = splitio.manager(); + + 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'); + + client.once(client.Event.SDK_READY_FROM_CACHE, () => t.fail('It should not emit SDK_READY_FROM_CACHE because clearOnInit is true.')); + + await client.ready(); + t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + + await splitio.destroy(); + t.equal(localStorage.getItem('readyFromCache_10.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); + t.equal(localStorage.getItem('readyFromCache_10.SPLITIO.hash'), expectedHashNullFilter, 'Storage hash must not be changed'); + t.true(nearlyEqual(parseInt(localStorage.getItem('readyFromCache_10.SPLITIO.lastClear')), Date.now()), 'lastClear timestamp must be set'); + + // Start again with cached data and lastClear item within the last 24 hours -> no cache cleanup + console.log.resetHistory(); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + + splitio = SplitFactory(clearOnInitConfig); + client = splitio.client(); + manager = splitio.manager(); + + await new Promise(res => client.once(client.Event.SDK_READY_FROM_CACHE, res)); + + t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + t.false(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'); + + await splitio.destroy(); + + // Start again with cached data and lastClear item older than 24 hours -> cache cleanup + console.log.resetHistory(); + localStorage.setItem('readyFromCache_10.SPLITIO.lastClear', Date.now() - 25 * 60 * 60 * 1000); // 25 hours ago + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + + splitio = SplitFactory(clearOnInitConfig); + client = splitio.client(); + manager = splitio.manager(); + + client.once(client.Event.SDK_READY_FROM_CACHE, () => t.fail('It should not emit SDK_READY_FROM_CACHE because clearOnInit is true.')); + + await new Promise(res => client.once(client.Event.SDK_READY, res)); + + t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + 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'); + + await splitio.destroy(); + + console.log.restore(); + t.end(); + }); + } diff --git a/src/__tests__/testUtils/index.js b/src/__tests__/testUtils/index.js index 5994a3c..35c601f 100644 --- a/src/__tests__/testUtils/index.js +++ b/src/__tests__/testUtils/index.js @@ -18,8 +18,8 @@ export function nearlyEqual(actual, expected, epsilon = DEFAULT_ERROR_MARGIN) { * - when `?since=-1`, it returns the given segment `keys` in `added` list. * - otherwise, it returns empty `added` and `removed` lists, and the same since and till values. * - * @param {Object} fetchMock see http://www.wheresrhys.co.uk/fetch-mock - * @param {string|RegExp|...} matcher see http://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher + * @param {Object} fetchMock see https://www.wheresrhys.co.uk/fetch-mock + * @param {string|RegExp|...} matcher see https://www.wheresrhys.co.uk/fetch-mock/#api-mockingmock_matcher * @param {string[]} keys array of segment keys to fetch * @param {number} changeNumber optional changeNumber */ diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 7d76258..804eb00 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.0.1'; +const packageVersion = '1.1.0-rc.0'; /** * 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 bdc4073..81775e4 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -110,7 +110,9 @@ let impressionData: SplitIO.ImpressionData; let syncStorage: SplitIO.StorageSync; let syncStorageFactory: SplitIO.StorageSyncFactory = InLocalStorage(); let localStorageOptions: SplitIO.InLocalStorageOptions = { - prefix: 'PREFIX' + prefix: 'PREFIX', + expirationDays: 1, + clearOnInit: true }; syncStorageFactory = InLocalStorage(localStorageOptions); diff --git a/types/full/index.d.ts b/types/full/index.d.ts index 7ce5b8b..e765b7f 100644 --- a/types/full/index.d.ts +++ b/types/full/index.d.ts @@ -1,5 +1,5 @@ // Declaration file for JavaScript Browser Split Software SDK -// Project: http://www.split.io/ +// Project: https://www.split.io/ // Definitions by: Nico Zelaya /// diff --git a/types/index.d.ts b/types/index.d.ts index 040dfaa..c33e845 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,5 +1,5 @@ // Declaration file for JavaScript Browser Split Software SDK -// Project: http://www.split.io/ +// Project: https://www.split.io/ // Definitions by: Nico Zelaya /// From d5d97e9a9610c95d1497bcf6ffcd9bf8f8315527 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 20 Dec 2024 13:25:27 -0300 Subject: [PATCH 03/11] Rollback ci-cd --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8777341..f55585a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -47,7 +47,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/cache_expiration' || github.ref == 'refs/heads/main') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }} uses: actions/upload-artifact@v3 with: name: assets @@ -58,7 +58,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cache_expiration' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: From 9ebbab65a73c8b79bd55c3738f1310b02a3f5aad Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 17 Jan 2025 19:36:09 -0300 Subject: [PATCH 04/11] Fix tests --- CHANGES.txt | 2 +- .../browserSuites/ready-from-cache.spec.js | 101 +++++++++--------- 2 files changed, 50 insertions(+), 53 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dd3e476..c995090 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ - Added two new configuration options for the SDK's `InLocalStorage` module to control the behavior of the persisted rollout plan cache in the browser: - `expirationDays` to specify the validity period of the rollout plan cache in days. - `clearOnInit` to clear the rollout plan cache on SDK initialization. - - Updated SDK_READY_FROM_CACHE event when using the `LOCALSTORAGE` storage type to be emitted alongside the SDK_READY event if it has not already been emitted. + - Updated SDK_READY_FROM_CACHE event when using `InLocalStorage` to be emitted alongside the SDK_READY event if it has not already been emitted. - Updated @splitsoftware/splitio-commons package to version 2.2.0. 1.1.0 (January 17, 2025) diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index c7ecd22..2afb8cf 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -94,7 +94,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCacheEmpty' }; localStorage.clear(); - t.plan(3); + t.plan(4); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: splitChangesMock1 }); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); @@ -122,18 +122,17 @@ export default function (fetchMock, assert) { t.end(); }); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is no cache.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client.__getStatus().isReadyFromCache, 'Client should emit SDK_READY and it should be ready from cache'); }); client2.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client.__getStatus().isReadyFromCache, 'Client should emit SDK_READY and it should be ready from cache'); }); client3.on(client.Event.SDK_READY, () => { - t.pass('It should emit SDK_READY alone, since there was no cache.'); + t.true(client.__getStatus().isReadyFromCache, 'Client should emit SDK_READY and it should be ready from cache'); }); }); @@ -146,17 +145,17 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 3); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { ...splitChangesMock1, since: 25 }, headers: {} }), 200); }); // 400ms is how long it'll take to reply with Splits, no SDK_READY should be emitted before that. }); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s }); fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200); @@ -252,18 +251,18 @@ export default function (fetchMock, assert) { localStorage.clear(); t.plan(12 * 2 + 5); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=25', () => { t.equal(localStorage.getItem('readyFromCache_3.SPLITIO.split.always_on'), alwaysOnSplitInverted, 'feature flags must not be cleaned from cache'); return new Promise(res => { setTimeout(() => res({ status: 200, body: { ...splitChangesMock1, since: 25 }, headers: {} }), 200); }); // 400ms is how long it'll take to reply with Splits, no SDK_READY should be emitted before that. }); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { 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': {} }); @@ -361,13 +360,15 @@ export default function (fetchMock, assert) { }); assert.test(t => { // Testing when we start with cached data but expired (lastUpdate timestamp lower than custom (1) expirationDays ago) + const CLIENT_READY_MS = 400, CLIENT2_READY_MS = 700, CLIENT3_READY_MS = 1000; + const testUrls = { sdk: 'https://sdk.baseurl/readyFromCacheWithData4', events: 'https://events.baseurl/readyFromCacheWithData4' }; localStorage.clear(); - fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', function () { + fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=-1', () => { t.equal(localStorage.getItem('some_user_item'), 'user_item', 'user items at localStorage must not be changed'); t.equal(localStorage.getItem('readyFromCache_4.SPLITIO.hash'), expectedHashNullFilter, 'storage hash must not be changed'); t.true(nearlyEqual(parseInt(localStorage.getItem('readyFromCache_4.SPLITIO.lastClear'), 10), Date.now()), 'storage lastClear timestamp must be updated'); @@ -375,14 +376,14 @@ export default function (fetchMock, assert) { return { status: 200, body: splitChangesMock1 }; }); fetchMock.get(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), 400); }); // First client gets segments before splits. No segment cache loading (yet) + fetchMock.get(testUrls.sdk + '/memberships/nicolas%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: membershipsNicolas, headers: {} }), CLIENT_READY_MS); }); // First client gets segments before splits. No segment cache loading (yet) }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 700); }); // Second client gets segments after 700ms + fetchMock.get(testUrls.sdk + '/memberships/nicolas2%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), CLIENT2_READY_MS); }); // Second client gets segments after 700ms }); - fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', function () { - return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), 1000); }); // Third client memberships will come after 1s + fetchMock.get(testUrls.sdk + '/memberships/nicolas3%40split.io', () => { + return new Promise(res => { setTimeout(() => res({ status: 200, body: { 'ms': {} }, headers: {} }), CLIENT3_READY_MS); }); // Third client memberships will come after 1s }); fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200); fetchMock.postOnce(testUrls.events + '/testImpressions/count', 200); @@ -418,37 +419,34 @@ export default function (fetchMock, assert) { }); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + 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, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + t.true(nearlyEqual(Date.now() - startTime, CLIENT2_READY_MS), 'It should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client3.once(client3.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if there is expired cache.'); - t.end(); + 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, () => { - t.true(Date.now() - startTime >= 400, 'It should emit SDK_READY after syncing with the cloud.'); + 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(() => { - t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.'); + 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(Date.now() - startTime >= 700, 'It should emit SDK_READY after syncing with the cloud.'); + 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(() => { - t.true(Date.now() - startTime >= 700, 'It should resolve ready promise after syncing with the cloud.'); + 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(() => { - t.true(Date.now() - startTime >= 1000, 'It should resolve ready promise after syncing with the cloud.'); + 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.'); // Last cb: destroy clients and check that localstorage has the expected items @@ -481,7 +479,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_5' }; localStorage.clear(); - t.plan(7); + t.plan(8); fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1&names=p1__split,p2__split', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); @@ -506,8 +504,7 @@ export default function (fetchMock, assert) { const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE because localStorage is cleaned and there isn\'t cached feature flags'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -531,7 +528,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_5B' }; localStorage.clear(); - t.plan(5); + t.plan(6); fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1&names=p1__split,p2__split', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); @@ -550,8 +547,7 @@ export default function (fetchMock, assert) { const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if cache is empty.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -622,7 +618,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_7' }; localStorage.clear(); - t.plan(6); + t.plan(7); fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1&prefixes=p1,p2', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); @@ -650,8 +646,7 @@ export default function (fetchMock, assert) { const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE if cache has expired.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -687,7 +682,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_8' }; localStorage.clear(); - t.plan(7); + t.plan(8); fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1', { status: 200, body: { splits: [splitDeclarations.p1__split, splitDeclarations.p2__split, splitDeclarations.p3__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); @@ -711,8 +706,7 @@ export default function (fetchMock, assert) { const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE because all feature flags were removed from cache since the filter query changed.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -739,7 +733,7 @@ export default function (fetchMock, assert) { events: 'https://events.baseurl/readyFromCache_9' }; localStorage.clear(); - t.plan(6); + t.plan(7); fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=-1&names=no%20exist%20trim,no_exist,p3__split&prefixes=no%20exist%20trim,p2', { status: 200, body: { splits: [splitDeclarations.p2__split, splitDeclarations.p3__split], since: -1, till: 1457552620999 } }, { delay: 10 }); // short delay to let emit SDK_READY_FROM_CACHE fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: { ms: {} } }); @@ -764,8 +758,7 @@ export default function (fetchMock, assert) { const manager = splitio.manager(); client.once(client.Event.SDK_READY_FROM_CACHE, () => { - t.fail('It should not emit SDK_READY_FROM_CACHE because all feature flags were removed from cache since the filter query changed.'); - t.end(); + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY'); }); client.once(client.Event.SDK_READY, () => { @@ -817,10 +810,12 @@ export default function (fetchMock, assert) { 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'); - client.once(client.Event.SDK_READY_FROM_CACHE, () => t.fail('It should not emit SDK_READY_FROM_CACHE because clearOnInit is true.')); + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true'); + }); await client.ready(); - t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); await splitio.destroy(); t.equal(localStorage.getItem('readyFromCache_10.SPLITIO.splits.till'), '1457552620999', 'splits.till must correspond to the till of the last successfully fetched Splits'); @@ -837,7 +832,7 @@ export default function (fetchMock, assert) { await new Promise(res => client.once(client.Event.SDK_READY_FROM_CACHE, res)); - t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); t.false(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'); await splitio.destroy(); @@ -852,11 +847,13 @@ export default function (fetchMock, assert) { client = splitio.client(); manager = splitio.manager(); - client.once(client.Event.SDK_READY_FROM_CACHE, () => t.fail('It should not emit SDK_READY_FROM_CACHE because clearOnInit is true.')); + client.once(client.Event.SDK_READY_FROM_CACHE, () => { + t.true(client.__getStatus().isReady, 'Client should emit SDK_READY_FROM_CACHE alongside SDK_READY, because clearOnInit is true'); + }); await new Promise(res => client.once(client.Event.SDK_READY, res)); - t.equal(manager.names().sort().length, 32, 'active splits should be present for evaluation'); + t.equal(manager.names().sort().length, 33, 'active splits should be present for evaluation'); 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'); await splitio.destroy(); From 27ae83eff89fc52adf675a1428e1289e2d59a5b7 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 28 Feb 2025 16:47:16 -0300 Subject: [PATCH 05/11] Update CI/CD workflows to use ubuntu-latest --- .github/workflows/ci-cd.yml | 7 +++---- .github/workflows/sonar-scan.yml | 2 +- .github/workflows/update-license-year.yml | 2 +- README.md | 1 + karma/config.js | 13 +++++++++---- package-lock.json | 12 ++++++------ 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c94916b..470b1d5 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -19,8 +19,7 @@ permissions: jobs: build: name: Build - # @TODO rollback to ubuntu-latest eventually. ATM, `npm run test-browser` fails when using ubuntu-latest (ubuntu-22.04) with "ERROR [launcher]: Cannot start ChromeHeadless" - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 @@ -56,7 +55,7 @@ jobs: upload-stage: name: Upload assets - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: build if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: @@ -99,7 +98,7 @@ jobs: upload-prod: name: Upload assets - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: build if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} strategy: diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml index c114c56..f10bda2 100644 --- a/.github/workflows/sonar-scan.yml +++ b/.github/workflows/sonar-scan.yml @@ -12,7 +12,7 @@ on: jobs: build: name: Build - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/update-license-year.yml b/.github/workflows/update-license-year.yml index 199a0ef..7e0a945 100644 --- a/.github/workflows/update-license-year.yml +++ b/.github/workflows/update-license-year.yml @@ -10,7 +10,7 @@ permissions: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 diff --git a/README.md b/README.md index 2c68837..1ce963d 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Split has built and maintains SDKs for: * .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK) * Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK) * Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities) +* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK) * Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin) * GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK) * iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK) diff --git a/karma/config.js b/karma/config.js index 40cbe1c..83b3539 100644 --- a/karma/config.js +++ b/karma/config.js @@ -24,10 +24,15 @@ module.exports = { 'tap' ], - // Run on Chrome Headless. Use 'Chrome' instead to run on full browser for debugging - browsers: [ - 'ChromeHeadless' - ], + // Run on Chrome Headless + customLaunchers: { + ChromeHeadlessNoSandbox: { + base: 'ChromeHeadless', + // Flags required to run in ubuntu-22.04 or above (https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md) + flags: ['--no-sandbox', '--disable-setuid-sandbox'] + } + }, + browsers: ['ChromeHeadlessNoSandbox'], // Continuous Integration mode // if true, it capture browsers, run tests and exit. Set false for debugging diff --git a/package-lock.json b/package-lock.json index 9b030df..7153ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8146,9 +8146,9 @@ "dev": true }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -15617,9 +15617,9 @@ } }, "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" From dee5030e32134b2a81a578cdb033e16e165a4795 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 26 Mar 2025 16:10:41 -0300 Subject: [PATCH 06/11] Rename some variables in test suite for readability --- .../browserSuites/impressions.debug.spec.js | 6 +++--- src/__tests__/browserSuites/impressions.spec.js | 16 ++++++++-------- src/__tests__/mocks/splitchanges.since.-1.json | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/__tests__/browserSuites/impressions.debug.spec.js b/src/__tests__/browserSuites/impressions.debug.spec.js index 5f90b70..ce40a0f 100644 --- a/src/__tests__/browserSuites/impressions.debug.spec.js +++ b/src/__tests__/browserSuites/impressions.debug.spec.js @@ -75,7 +75,7 @@ export default function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }] + pf: [{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }] }, 'We should generate impression count for the feature with track impressions disabled.'); return 200; @@ -83,7 +83,7 @@ export default function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ fs: ['always_on_track_impressions_false'], k: 'facundo@split.io' }] + keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }] }, 'We should track unique keys for the feature with track impressions disabled.'); return 200; @@ -95,6 +95,6 @@ export default function (fetchMock, assert) { client.getTreatment('split_with_config'); client.getTreatment('split_with_config'); client.getTreatment('split_with_config'); - assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on'); + assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on'); }); } diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index 33e6010..d271beb 100644 --- a/src/__tests__/browserSuites/impressions.spec.js +++ b/src/__tests__/browserSuites/impressions.spec.js @@ -53,7 +53,7 @@ export default function (fetchMock, assert) { const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0]; const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0]; - const alwaysOnWithTrackImpressionsFalse = resp.filter(e => e.f === 'always_on_track_impressions_false'); + const alwaysOnWithImpressionsDisabledTrue = resp.filter(e => e.f === 'always_on_impressions_disabled_true'); assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.'); assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.'); @@ -61,7 +61,7 @@ export default function (fetchMock, assert) { assert.true(splitWithConfigImpr, 'Split evaluated with config should have generated an impression too.'); assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.'); assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.'); - assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0); + assert.equal(alwaysOnWithImpressionsDisabledTrue.length, 0); const { k, @@ -100,21 +100,21 @@ export default function (fetchMock, assert) { // finding these validate the feature names collection too const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0]; - const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0]; + const alwaysOnWithImpressionsDisabledTrue = data.pf.filter(e => e.f === 'always_on_impressions_disabled_true')[0]; assert.equal(splitWithConfigImpr.rc, 2); assert.equal(typeof splitWithConfigImpr.m, 'number'); assert.equal(splitWithConfigImpr.m, truncatedTimeFrame); - assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1); - assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number'); - assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame); + assert.equal(alwaysOnWithImpressionsDisabledTrue.rc, 1); + assert.equal(typeof alwaysOnWithImpressionsDisabledTrue.m, 'number'); + assert.equal(alwaysOnWithImpressionsDisabledTrue.m, truncatedTimeFrame); return 200; }); fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => { assert.deepEqual(JSON.parse(opts.body), { - keys: [{ fs: [ 'always_on_track_impressions_false' ], k: 'facundo@split.io' }] + keys: [{ fs: [ 'always_on_impressions_disabled_true' ], k: 'facundo@split.io' }] }, 'We should only track unique keys for features flags with track impressions disabled.'); return 200; @@ -132,6 +132,6 @@ export default function (fetchMock, assert) { client.getTreatmentWithConfig('split_with_config'); // Impression should not be tracked - assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on'); + assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on'); }); } diff --git a/src/__tests__/mocks/splitchanges.since.-1.json b/src/__tests__/mocks/splitchanges.since.-1.json index 372b030..c479eae 100644 --- a/src/__tests__/mocks/splitchanges.since.-1.json +++ b/src/__tests__/mocks/splitchanges.since.-1.json @@ -1387,7 +1387,7 @@ "environment": null, "trafficTypeId": null, "trafficTypeName": null, - "name": "always_on_track_impressions_false", + "name": "always_on_impressions_disabled_true", "impressionsDisabled": true, "seed": -790401604, "status": "ACTIVE", From f7a03773159c74fb7b46baf4e1d4d945e7e4b6c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 22:32:41 +0000 Subject: [PATCH 07/11] Bump tar-fs from 2.1.1 to 2.1.2 Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.1 to 2.1.2. - [Commits](https://github.com/mafintosh/tar-fs/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: tar-fs dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7153ba4..fac1c3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8683,10 +8683,11 @@ } }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "dev": true, + "license": "MIT", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -16032,9 +16033,9 @@ } }, "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", + "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", "dev": true, "requires": { "chownr": "^1.1.1", From e1c5b4f7185bc03aba55d7ed6c2e0a6468341e7b Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 31 Mar 2025 14:23:55 -0300 Subject: [PATCH 08/11] Upgrade JS-commons dependency --- CHANGES.txt | 5 +- package-lock.json | 468 ++++-------------- package.json | 4 +- .../impressions-listener.spec.js | 8 +- src/settings/defaults.ts | 2 +- ts-tests/index.ts | 56 ++- 6 files changed, 143 insertions(+), 400 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c995090..c0c9895 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,9 @@ -1.2.0 (January XX, 2025) +1.2.0 (March 31, 2025) + - Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs. - Added two new configuration options for the SDK's `InLocalStorage` module to control the behavior of the persisted rollout plan cache in the browser: - `expirationDays` to specify the validity period of the rollout plan cache in days. - `clearOnInit` to clear the rollout plan cache on SDK initialization. - - Updated SDK_READY_FROM_CACHE event when using `InLocalStorage` to be emitted alongside the SDK_READY event if it has not already been emitted. + - Updated SDK_READY_FROM_CACHE event when using `InLocalStorage` module to be emitted alongside the SDK_READY event if it has not already been emitted. - Updated @splitsoftware/splitio-commons package to version 2.2.0. 1.1.0 (January 17, 2025) diff --git a/package-lock.json b/package-lock.json index 9cb432e..cff2569 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.0", + "version": "1.1.1-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.0", + "version": "1.1.1-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.1.0-rc.2", + "@splitsoftware/splitio-commons": "2.2.0", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, @@ -70,89 +70,20 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { "version": "7.23.2", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", @@ -340,19 +271,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -367,109 +300,28 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "@babel/types": "^7.27.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -640,10 +492,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, + "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -654,14 +507,15 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -698,14 +552,14 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1542,9 +1396,10 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.1.0-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0-rc.2.tgz", - "integrity": "sha512-5zn+w6bw9CdXnx3chMzcLqpZ7rghNJ1iYdwKTwYPUD5NvCac7og+A3aKdvbxFG+zm773e3hffJd4EharfoWwog==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", + "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -6074,7 +5929,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -8815,15 +8671,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9563,71 +9410,14 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/compat-data": { @@ -9772,15 +9562,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true }, "@babel/helper-validator-option": { @@ -9790,91 +9580,24 @@ "dev": true }, "@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" } }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/types": "^7.27.0" } }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -9993,9 +9716,9 @@ } }, "@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, "optional": true, "peer": true, @@ -10004,14 +9727,14 @@ } }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" } }, "@babel/traverse": { @@ -10041,14 +9764,13 @@ } }, "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" } }, "@bcoe/v8-coverage": { @@ -10689,9 +10411,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "2.1.0-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.1.0-rc.2.tgz", - "integrity": "sha512-5zn+w6bw9CdXnx3chMzcLqpZ7rghNJ1iYdwKTwYPUD5NvCac7og+A3aKdvbxFG+zm773e3hffJd4EharfoWwog==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", + "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -16139,12 +15861,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 6c61f75..4845527 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.0", + "version": "1.1.1-rc.0", "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.1.0-rc.2", + "@splitsoftware/splitio-commons": "2.2.0", "tslib": "^2.3.1", "unfetch": "^4.2.0" }, diff --git a/src/__tests__/browserSuites/impressions-listener.spec.js b/src/__tests__/browserSuites/impressions-listener.spec.js index ca47995..d46c555 100644 --- a/src/__tests__/browserSuites/impressions-listener.spec.js +++ b/src/__tests__/browserSuites/impressions-listener.spec.js @@ -46,7 +46,7 @@ export default function (assert) { const testAttrs = { is_test: true }; // Impression listener is shared across all client instances and does not get affected by configurations. - client.getTreatment('hierarchical_splits_test'); + client.getTreatment('hierarchical_splits_test', undefined, { properties: { prop1: 'prop-value' } }); client2.getTreatment('qc_team'); client2.getTreatmentWithConfig('qc_team'); // Validate that the impression is the same. client3.getTreatment('qc_team', testAttrs); @@ -58,7 +58,8 @@ export default function (assert) { treatment: 'no', bucketingKey: 'impr_bucketing_2', label: 'default rule', - pt: undefined + pt: undefined, + properties: undefined }; assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.'); @@ -71,7 +72,8 @@ export default function (assert) { bucketingKey: undefined, label: 'expected label', changeNumber: 2828282828, - pt: undefined + pt: undefined, + properties: '{"prop1":"prop-value"}' }, attributes: undefined, ...metaData diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index be04ade..5706151 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.1.0'; +const packageVersion = '1.1.1-rc.0'; /** * 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 6e74a14..c66a7b5 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -89,6 +89,14 @@ const attributes: SplitIO.Attributes = { attr6: [1, 2], attr7: true }; +const evaluationOptions: SplitIO.EvaluationOptions = { + properties: { + prop1: 1, + prop2: '2', + prop3: true, + prop4: null + } +}; // const splitKeyObj: SplitIO.SplitKeyObject = { // matchingKey: 'matchingKey', // bucketingKey: 'bucketingKey' @@ -267,58 +275,66 @@ promise = SDK.destroy(); // We can call getTreatment without a key. // treatment = client.getTreatment(splitKey, 'mySplit'); treatment = client.getTreatment('mySplit'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatment = client.getTreatment(splitKey, 'mySplit', attributes); treatment = client.getTreatment('mySplit', attributes); +treatment = client.getTreatment('mySplit', undefined, evaluationOptions); // We can call getTreatments without a key. // treatments = client.getTreatments(splitKey, ['mySplit']); treatments = client.getTreatments(['mySplit']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatments = client.getTreatments(splitKey, ['mySplit'], attributes); treatments = client.getTreatments(['mySplit'], attributes); +treatments = client.getTreatments(['mySplit'], undefined, evaluationOptions); // We can call getTreatmentWithConfig without a key. // treatmentWithConfig = client.getTreatmentWithConfig(splitKey, 'mySplit'); treatmentWithConfig = client.getTreatmentWithConfig('mySplit'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatmentWithConfig = client.getTreatmentWithConfig(splitKey, 'mySplit', attributes); treatmentWithConfig = client.getTreatmentWithConfig('mySplit', attributes); +treatmentWithConfig = client.getTreatmentWithConfig('mySplit', undefined, evaluationOptions); // We can call getTreatmentsWithConfig without a key. // treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit']); treatmentsWithConfig = client.getTreatmentsWithConfig(['mySplit']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatmentsWithConfig = client.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); treatmentsWithConfig = client.getTreatmentsWithConfig(['mySplit'], attributes); +treatmentsWithConfig = client.getTreatmentsWithConfig(['mySplit'], undefined, evaluationOptions); // We can call getTreatmentsByFlagSet without a key. // treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a'); treatments = client.getTreatmentsByFlagSet('set_a'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatments = client.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); treatments = client.getTreatmentsByFlagSet('set_a', attributes); +treatments = client.getTreatmentsByFlagSet('set_a', undefined, evaluationOptions); // We can call getTreatmentsByFlagSets without a key. // treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a']); treatments = client.getTreatmentsByFlagSets(['set_a']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatments = client.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); treatments = client.getTreatmentsByFlagSets(['set_a'], attributes); +treatments = client.getTreatmentsByFlagSets(['set_a'], undefined, evaluationOptions); // We can call getTreatmentsWithConfigByFlagSet without a key. // treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet('set_a'); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet('set_a', attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSet('set_a', undefined, evaluationOptions); // We can call getTreatmentsWithConfigByFlagSets without a key. // treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(['set_a']); -// Attributes parameter is optional. +// Attributes and EvaluationOptions parameters are optional. // treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(['set_a'], attributes); +treatmentsWithConfig = client.getTreatmentsWithConfigByFlagSets(['set_a'], undefined, evaluationOptions); // We can call track without a key. Traffic type can also be binded to the client. // tracked = client.track(splitKey, 'myTrafficType', 'myEventType'); // all params @@ -357,57 +373,65 @@ promise = AsyncSDK.destroy(); // We can call getTreatment asyncTreatment = asyncClient.getTreatment('mySplit'); // asyncTreatment = asyncClient.getTreatment(splitKey, 'mySplit'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatment = asyncClient.getTreatment('mySplit', attributes); +asyncTreatment = asyncClient.getTreatment('mySplit', undefined, evaluationOptions); // asyncTreatment = asyncClient.getTreatment(splitKey, 'mySplit', attributes); // We can call getTreatments asyncTreatments = asyncClient.getTreatments(['mySplit']); // asyncTreatments = asyncClient.getTreatments(splitKey, ['mySplit']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatments = asyncClient.getTreatments(['mySplit'], attributes); +asyncTreatments = asyncClient.getTreatments(['mySplit'], undefined, evaluationOptions); // asyncTreatments = asyncClient.getTreatments(splitKey, ['mySplit'], attributes); // We can call getTreatmentWithConfig asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig('mySplit'); // asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig(splitKey, 'mySplit'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig('mySplit', attributes); +asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig('mySplit', undefined, evaluationOptions); // asyncTreatmentWithConfig = asyncClient.getTreatmentWithConfig(splitKey, 'mySplit', attributes); // We can call getTreatments but always with a key. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(['mySplit']); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(['mySplit'], attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(['mySplit'], undefined, evaluationOptions); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfig(splitKey, ['mySplit'], attributes); // We can call getTreatmentsByFlagSet asyncTreatments = asyncClient.getTreatmentsByFlagSet('set_a'); // asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatments = asyncClient.getTreatmentsByFlagSet('set_a', attributes); +asyncTreatments = asyncClient.getTreatmentsByFlagSet('set_a', undefined, evaluationOptions); // asyncTreatments = asyncClient.getTreatmentsByFlagSet(splitKey, 'set_a', attributes); // We can call getTreatmentsByFlagSets asyncTreatments = asyncClient.getTreatmentsByFlagSets(['set_a']); // asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatments = asyncClient.getTreatmentsByFlagSets(['set_a'], attributes); +asyncTreatments = asyncClient.getTreatmentsByFlagSets(['set_a'], undefined, evaluationOptions); // asyncTreatments = asyncClient.getTreatmentsByFlagSets(splitKey, ['set_a'], attributes); // We can call getTreatmentsWithConfigByFlagSet asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet('set_a'); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a'); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet('set_a', attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet('set_a', undefined, evaluationOptions); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSet(splitKey, 'set_a', attributes); // We can call getTreatmentsByFlagSets but always with a key. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(['set_a']); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a']); -// Attributes parameter is optional +// Attributes and EvaluationOptions parameters are optional. asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(['set_a'], attributes); +asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(['set_a'], undefined, evaluationOptions); // asyncTreatmentsWithConfig = asyncClient.getTreatmentsWithConfigByFlagSets(splitKey, ['set_a'], attributes); // We can call track. From 0038d4c50b32c3808cdd4323b132284931ff296d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 31 Mar 2025 14:24:30 -0300 Subject: [PATCH 09/11] rc --- .github/workflows/ci-cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 470b1d5..19f1f21 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -46,7 +46,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/cache_expiration' || github.ref == 'refs/heads/main') }} uses: actions/upload-artifact@v4 with: name: assets @@ -57,7 +57,7 @@ jobs: name: Upload assets runs-on: ubuntu-latest needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cache_expiration' }} strategy: matrix: environment: From 679c8de85783bf7cc8e2cbfeb943d76d402e1a32 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 31 Mar 2025 14:34:43 -0300 Subject: [PATCH 10/11] Test fix --- src/__tests__/browserSuites/impressions-listener.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/browserSuites/impressions-listener.spec.js b/src/__tests__/browserSuites/impressions-listener.spec.js index d46c555..9907eab 100644 --- a/src/__tests__/browserSuites/impressions-listener.spec.js +++ b/src/__tests__/browserSuites/impressions-listener.spec.js @@ -72,7 +72,6 @@ export default function (assert) { bucketingKey: undefined, label: 'expected label', changeNumber: 2828282828, - pt: undefined, properties: '{"prop1":"prop-value"}' }, attributes: undefined, From d6bfed3b0d085f0e36bd82856358873ebe0f6cce Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 31 Mar 2025 15:02:07 -0300 Subject: [PATCH 11/11] stable version --- .github/workflows/ci-cd.yml | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- src/settings/defaults.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 19f1f21..470b1d5 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -46,7 +46,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/cache_expiration' || github.ref == 'refs/heads/main') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }} uses: actions/upload-artifact@v4 with: name: assets @@ -57,7 +57,7 @@ jobs: name: Upload assets runs-on: ubuntu-latest needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/cache_expiration' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: diff --git a/package-lock.json b/package-lock.json index cff2569..feb5856 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.1-rc.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.1-rc.0", + "version": "1.2.0", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "2.2.0", diff --git a/package.json b/package.json index 4845527..a9a01de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "1.1.1-rc.0", + "version": "1.2.0", "description": "Split SDK for JavaScript on Browser", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 5706151..6d9393b 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.1.1-rc.0'; +const packageVersion = '1.2.0'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item.