Skip to content

Commit 2a326b5

Browse files
Merge branch 'main' into cache_expiration
2 parents d5d97e9 + bad1b41 commit 2a326b5

17 files changed

+173
-90
lines changed

.github/workflows/ci-cd.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848

4949
- name: Store assets
5050
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }}
51-
uses: actions/upload-artifact@v3
51+
uses: actions/upload-artifact@v4
5252
with:
5353
name: assets
5454
path: umd/
@@ -74,7 +74,7 @@ jobs:
7474

7575
steps:
7676
- name: Download assets
77-
uses: actions/download-artifact@v3
77+
uses: actions/download-artifact@v4
7878
with:
7979
name: assets
8080
path: umd
@@ -84,7 +84,7 @@ jobs:
8484
working-directory: umd
8585

8686
- name: Configure AWS credentials
87-
uses: aws-actions/configure-aws-credentials@v1-node16
87+
uses: aws-actions/configure-aws-credentials@v4
8888
with:
8989
role-to-assume: arn:aws:iam::${{ matrix.account_id }}:role/gha-public-assets-role
9090
aws-region: us-east-1
@@ -117,7 +117,7 @@ jobs:
117117

118118
steps:
119119
- name: Download assets
120-
uses: actions/download-artifact@v3
120+
uses: actions/download-artifact@v4
121121
with:
122122
name: assets
123123
path: umd
@@ -127,7 +127,7 @@ jobs:
127127
working-directory: umd
128128

129129
- name: Configure AWS credentials
130-
uses: aws-actions/configure-aws-credentials@v1-node16
130+
uses: aws-actions/configure-aws-credentials@v4
131131
with:
132132
role-to-assume: arn:aws:iam::${{ matrix.account_id }}:role/gha-public-assets-role
133133
aws-region: us-east-1

CHANGES.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
1.1.0 (January XX, 2025)
1+
1.2.0 (January XX, 2025)
22
- Added two new configuration options for the SDK's `InLocalStorage` module to control the behavior of the persisted rollout plan cache in the browser:
33
- `expirationDays` to specify the validity period of the rollout plan cache in days.
44
- `clearOnInit` to clear the rollout plan cache on SDK initialization.
5-
- Updated @splitsoftware/splitio-commons package to version 2.1.0.
5+
- 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.
6+
- Updated @splitsoftware/splitio-commons package to version 2.2.0.
7+
8+
1.1.0 (January 17, 2025)
9+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
10+
- Bugfixing - Updated @splitsoftware/splitio-commons package to version 2.1.0, which properly handles rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
611

712
1.0.1 (November 11, 2024)
813
- 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';`.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2024 Split Software, Inc.
1+
Copyright © 2025 Split Software, Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-browserjs",
3-
"version": "1.1.0-rc.0",
3+
"version": "1.1.0",
44
"description": "Split SDK for JavaScript on Browser",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",
@@ -59,7 +59,7 @@
5959
"bugs": "https://github.com/splitio/javascript-browser-client/issues",
6060
"homepage": "https://github.com/splitio/javascript-browser-client#readme",
6161
"dependencies": {
62-
"@splitsoftware/splitio-commons": "2.1.0-rc.0",
62+
"@splitsoftware/splitio-commons": "2.1.0-rc.2",
6363
"tslib": "^2.3.1",
6464
"unfetch": "^4.2.0"
6565
},

src/__tests__/browserSuites/impressions-listener.spec.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,21 @@ export default function (assert) {
5757
keyName: 'marcio@split.io',
5858
treatment: 'no',
5959
bucketingKey: 'impr_bucketing_2',
60-
label: 'default rule'
60+
label: 'default rule',
61+
pt: undefined
6162
};
6263

6364
assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.');
64-
assert.true(listener.logImpression.getCall(0).calledWithMatch({
65+
assert.true(listener.logImpression.getCall(0).calledWithExactly({
6566
impression: {
6667
feature: 'hierarchical_splits_test',
6768
keyName: 'nicolas@split.io',
6869
treatment: 'on',
70+
time: listener.logImpression.getCall(0).args[0].impression.time,
6971
bucketingKey: undefined,
7072
label: 'expected label',
73+
changeNumber: 2828282828,
74+
pt: undefined
7175
},
7276
attributes: undefined,
7377
...metaData

src/__tests__/browserSuites/impressions.debug.spec.js

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import splitChangesMock1 from '../mocks/splitchanges.since.-1.json';
44
import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json';
55
import membershipsFacundo from '../mocks/memberships.facundo@split.io.json';
66
import { DEBUG } from '@splitsoftware/splitio-commons/src/utils/constants';
7+
import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time';
78
import { url } from '../testUtils';
89

910
const baseUrls = {
@@ -19,6 +20,8 @@ const settings = settingsFactory({
1920
streamingEnabled: false
2021
});
2122

23+
let truncatedTimeFrame;
24+
2225
export default function (fetchMock, assert) {
2326
// Mocking this specific route to make sure we only get the items we want to test from the handlers.
2427
fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 });
@@ -47,41 +50,21 @@ export default function (fetchMock, assert) {
4750
});
4851

4952
const client = splitio.client();
50-
const assertPayload = req => {
51-
const resp = JSON.parse(req.body);
52-
53-
assert.equal(resp.length, 1, 'We performed three evaluations so we should have 1 impressions type');
54-
55-
const alwaysOnWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
56-
57-
assert.equal(alwaysOnWithConfigImpr.i.length, 3);
58-
59-
function validateImpressionData(output, expected) {
60-
assert.equal(output.k, expected.keyName, 'Present impressions should have the correct key.');
61-
assert.equal(output.b, expected.bucketingKey, 'Present impressions should have the correct bucketingKey.');
62-
assert.equal(output.t, expected.treatment, 'Present impressions should have the correct treatment.');
63-
assert.equal(output.r, expected.label, 'Present impressions should have the correct label.');
64-
assert.equal(output.c, expected.changeNumber, 'Present impressions should have the correct changeNumber.');
65-
assert.equal(output.pt, expected.pt, 'Present impressions should have the correct previousTime.');
66-
}
67-
68-
validateImpressionData(alwaysOnWithConfigImpr.i[0], {
69-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
70-
bucketingKey: undefined, changeNumber: 828282828282, pt: undefined
71-
});
72-
validateImpressionData(alwaysOnWithConfigImpr.i[1], {
73-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
74-
bucketingKey: undefined, changeNumber: 828282828282, pt: alwaysOnWithConfigImpr.i[0].m
75-
});
76-
validateImpressionData(alwaysOnWithConfigImpr.i[2], {
77-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
78-
bucketingKey: undefined, changeNumber: 828282828282, pt: alwaysOnWithConfigImpr.i[1].m
79-
});
80-
};
8153

8254
fetchMock.postOnce(url(settings, '/testImpressions/bulk'), (url, req) => {
8355
assert.equal(req.headers.SplitSDKImpressionsMode, DEBUG);
84-
assertPayload(req);
56+
const data = JSON.parse(req.body);
57+
58+
assert.deepEqual(data, [{
59+
f: 'split_with_config',
60+
i: [{
61+
k: 'facundo@split.io', t: 'o.n', m: data[0].i[0].m, c: 828282828282, r: 'another expected label'
62+
}, {
63+
k: 'facundo@split.io', t: 'o.n', m: data[0].i[1].m, c: 828282828282, r: 'another expected label', pt: data[0].i[0].m,
64+
}, {
65+
k: 'facundo@split.io', t: 'o.n', m: data[0].i[2].m, c: 828282828282, r: 'another expected label', pt: data[0].i[1].m
66+
}]
67+
}]);
8568

8669
client.destroy().then(() => {
8770
assert.end();
@@ -90,9 +73,28 @@ export default function (fetchMock, assert) {
9073
return 200;
9174
});
9275

76+
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
77+
assert.deepEqual(JSON.parse(opts.body), {
78+
pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }]
79+
}, 'We should generate impression count for the feature with track impressions disabled.');
80+
81+
return 200;
82+
});
83+
84+
fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
85+
assert.deepEqual(JSON.parse(opts.body), {
86+
keys: [{ fs: ['always_on_track_impressions_false'], k: 'facundo@split.io' }]
87+
}, 'We should track unique keys for the feature with track impressions disabled.');
88+
89+
return 200;
90+
});
91+
9392
client.ready().then(() => {
93+
truncatedTimeFrame = truncateTimeFrame(Date.now());
94+
9495
client.getTreatment('split_with_config');
9596
client.getTreatment('split_with_config');
9697
client.getTreatment('split_with_config');
98+
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
9799
});
98100
}

src/__tests__/browserSuites/impressions.spec.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,19 @@ export default function (fetchMock, assert) {
4949
const assertPayload = req => {
5050
const resp = JSON.parse(req.body);
5151

52-
assert.equal(resp.length, 2, 'We performed three evaluations so we should have 2 impressions type');
52+
assert.equal(resp.length, 2, 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total');
5353

5454
const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0];
55-
const alwaysOnWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
55+
const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
56+
const alwaysOnWithTrackImpressionsFalse = resp.filter(e => e.f === 'always_on_track_impressions_false');
5657

5758
assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.');
5859
assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.');
5960
assert.false(resp.some(e => e.f === 'hierarchical_dep_hierarchical'), 'No matter how deep is the chain.');
60-
assert.true(alwaysOnWithConfigImpr, 'Split evaluated with config should have generated an impression too.');
61-
assert.false(Object.prototype.hasOwnProperty.call(alwaysOnWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
62-
assert.false(Object.prototype.hasOwnProperty.call(alwaysOnWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
61+
assert.true(splitWithConfigImpr, 'Split evaluated with config should have generated an impression too.');
62+
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
63+
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
64+
assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0);
6365

6466
const {
6567
k,
@@ -94,18 +96,26 @@ export default function (fetchMock, assert) {
9496
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
9597
const data = JSON.parse(opts.body);
9698

97-
assert.equal(data.pf.length, 1, 'We should generate impressions count for one feature.');
99+
assert.equal(data.pf.length, 2, 'We should generate impressions count for 2 features.');
98100

99101
// finding these validate the feature names collection too
100-
const dependencyChildImpr = data.pf.filter(e => e.f === 'hierarchical_splits_test')[0];
101-
const alwaysOnWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
102+
const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
103+
const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0];
102104

103-
assert.equal(dependencyChildImpr.rc, 1);
104-
assert.equal(typeof dependencyChildImpr.m, 'number');
105-
assert.equal(dependencyChildImpr.m, truncatedTimeFrame);
106-
assert.equal(alwaysOnWithConfigImpr.rc, 3);
107-
assert.equal(typeof alwaysOnWithConfigImpr.m, 'number');
108-
assert.equal(alwaysOnWithConfigImpr.m, truncatedTimeFrame);
105+
assert.equal(splitWithConfigImpr.rc, 2);
106+
assert.equal(typeof splitWithConfigImpr.m, 'number');
107+
assert.equal(splitWithConfigImpr.m, truncatedTimeFrame);
108+
assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1);
109+
assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number');
110+
assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame);
111+
112+
return 200;
113+
});
114+
115+
fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
116+
assert.deepEqual(JSON.parse(opts.body), {
117+
keys: [{ fs: [ 'always_on_track_impressions_false' ], k: 'facundo@split.io' }]
118+
}, 'We should only track unique keys for features flags with track impressions disabled.');
109119

110120
return 200;
111121
});
@@ -120,5 +130,8 @@ export default function (fetchMock, assert) {
120130
}, 'We should get an evaluation as always.');
121131
client.getTreatmentWithConfig('split_with_config');
122132
client.getTreatmentWithConfig('split_with_config');
133+
134+
// Impression should not be tracked
135+
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
123136
});
124137
}

src/__tests__/browserSuites/manager.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export default async function (settings, fetchMock, assert) {
4141
'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment),
4242
'configs': mockSplits.splits[index].configurations || {},
4343
'sets': mockSplits.splits[index].sets,
44-
'defaultTreatment': mockSplits.splits[index].defaultTreatment
44+
'defaultTreatment': mockSplits.splits[index].defaultTreatment,
45+
'impressionsDisabled': false
4546
});
4647

4748
assert.equal(manager.split('non_existent'), null, 'Trying to get a manager.split() of a Split that does not exist returns null.');

src/__tests__/browserSuites/telemetry.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) {
7171

7272
// @TODO check if iDe value is correct
7373
assert.deepEqual(data, {
74-
mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 32, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
74+
mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
7575
}, 'metrics/usage JSON payload should be the expected');
7676

7777
finish.next();
@@ -91,7 +91,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) {
9191
// @TODO check if iDe value is correct
9292
assert.deepEqual(data, {
9393
mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped
94-
tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 32, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
94+
tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
9595
}, '2nd metrics/usage JSON payload should be the expected');
9696
return 200;
9797
});

0 commit comments

Comments
 (0)