Skip to content

Commit df7c19a

Browse files
Merge pull request #169 from splitio/fme-12310
update commons - events with metadata
2 parents 0dec0eb + 2efc403 commit df7c19a

File tree

9 files changed

+99
-23
lines changed

9 files changed

+99
-23
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
1.7.0 (January 28, 2026)
2+
- Updated @splitsoftware/splitio-commons package to version 2.11.0, which:
3+
- Added functionality to provide metadata alongside SDK update and READY events. Read more in our docs.
4+
15
1.6.1 (December 30, 2025)
26
- Updated @splitsoftware/splitio-commons package to version 2.10.1 and other transitive dependencies for vulnerability fixes.
37
- Updated the order of storage operations to prevent inconsistent states when using the `InLocalStorage` module and the browser’s `localStorage` fails due to quota limits.

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.6.1",
3+
"version": "1.7.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.10.1",
62+
"@splitsoftware/splitio-commons": "2.11.0",
6363
"tslib": "^2.3.1",
6464
"unfetch": "^4.2.0"
6565
},

src/__tests__/browserSuites/push-corner-cases.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export function testSplitKillOnReadyFromCache(fetchMock, assert) {
9696
});
9797
client.on(client.Event.SDK_READY, () => {
9898
const lapse = Date.now() - start;
99-
assert.true(nearlyEqual(lapse, MILLIS_SPLIT_CHANGES_RESPONSE), 'SDK_READY once split changes arrives');
99+
assert.true(nearlyEqual(lapse, MILLIS_SPLIT_CHANGES_RESPONSE, 200), 'SDK_READY once split changes arrives');
100100

101101
client.destroy().then(() => { assert.end(); });
102102
});

src/__tests__/browserSuites/ready-from-cache.spec.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export default function (fetchMock, assert) {
282282
events: 'https://events.baseurl/readyFromCacheWithData3'
283283
};
284284
localStorage.clear();
285-
t.plan(12 * 2 + 5);
285+
t.plan(12 * 2 + 5 + 4); // +4 for SdkReadyMetadata assertions on SDK_READY_FROM_CACHE and SDK_READY
286286

287287
fetchMock.get(testUrls.sdk + '/splitChanges?s=1.3&since=25&rbSince=-1', () => {
288288
t.equal(localStorage.getItem('readyFromCache_3.SPLITIO.split.always_on'), alwaysOnSplitInverted, 'feature flags must not be cleaned from cache');
@@ -330,9 +330,13 @@ export default function (fetchMock, assert) {
330330
t.end();
331331
});
332332

333-
client.on(client.Event.SDK_READY_FROM_CACHE, () => {
333+
client.on(client.Event.SDK_READY_FROM_CACHE, (metadata) => {
334334
t.true(Date.now() - startTime < 400, 'It should emit SDK_READY_FROM_CACHE on every client if there was data in the cache and we subscribe on time. Should be considerably faster than actual readiness from the cloud.');
335335
t.equal(client.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation');
336+
// SdkReadyMetadata: when ready from cache, initialCacheLoad is false; lastUpdateTimestamp is number or undefined
337+
t.true(metadata != null && typeof metadata.initialCacheLoad === 'boolean', 'SDK_READY_FROM_CACHE must receive SdkReadyMetadata with initialCacheLoad');
338+
t.true(metadata.initialCacheLoad === false, 'When ready from existing cache, initialCacheLoad must be false');
339+
t.true(metadata.lastUpdateTimestamp === undefined || typeof metadata.lastUpdateTimestamp === 'number', 'SdkReadyMetadata.lastUpdateTimestamp must be number or undefined');
336340

337341
const client4 = splitio.client('nicolas4@split.io');
338342
t.equal(client4.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control');
@@ -350,9 +354,10 @@ export default function (fetchMock, assert) {
350354
t.equal(client3.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation');
351355
});
352356

353-
client.on(client.Event.SDK_READY, () => {
357+
client.on(client.Event.SDK_READY, (metadata) => {
354358
t.true(Date.now() - startTime >= 400, 'It should emit SDK_READY too but after syncing with the cloud.');
355359
t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.');
360+
t.true(metadata != null && typeof metadata.initialCacheLoad === 'boolean', 'SDK_READY must receive SdkReadyMetadata with initialCacheLoad');
356361
});
357362
client.ready().then(() => {
358363
t.true(Date.now() - startTime >= 400, 'It should resolve ready promise after syncing with the cloud.');

src/__tests__/browserSuites/ready-promise.spec.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ export default function readyPromiseAssertions(fetchMock, assert) {
119119
const manager = splitio.manager();
120120

121121
manager.whenReady()
122-
.then(() => {
122+
.then((metadata) => {
123+
t.true(metadata != null && typeof metadata.initialCacheLoad === 'boolean', 'whenReady must resolve with SdkReadyMetadata.initialCacheLoad');
124+
t.true(metadata.lastUpdateTimestamp === undefined || typeof metadata.lastUpdateTimestamp === 'number', 'whenReady SdkReadyMetadata.lastUpdateTimestamp must be number or undefined');
123125
t.pass('### SDK IS READY - the retry request is under the limits.');
124126
assertGetTreatmentWhenReady(t, client);
125127

@@ -521,7 +523,10 @@ export default function readyPromiseAssertions(fetchMock, assert) {
521523

522524
consoleSpy.log.resetHistory();
523525
setTimeout(() => {
524-
client.whenReadyFromCache().then((isReady) => t.true(isReady, 'SDK IS READY (& READY FROM CACHE) - Should resolve')).catch(() => t.fail('SDK TIMED OUT - Should not reject'));
526+
client.whenReadyFromCache().then((metadata) => {
527+
t.true(metadata != null && typeof metadata.initialCacheLoad === 'boolean', 'whenReadyFromCache must resolve with SdkReadyMetadata');
528+
t.true(metadata.lastUpdateTimestamp === undefined || typeof metadata.lastUpdateTimestamp === 'number', 'whenReadyFromCache SdkReadyMetadata.lastUpdateTimestamp must be number or undefined');
529+
}).catch(() => t.fail('SDK TIMED OUT - Should not reject'));
525530

526531
assertGetTreatmentWhenReady(t, client);
527532

src/__tests__/offline/browser.spec.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,17 @@ tape('Browser offline mode', function (assert) {
100100
for (let i = 0; i < factories.length; i++) {
101101
const factory = factories[i], client = factory.client(), manager = factory.manager(), client2 = factory.client('other');
102102

103-
client.on(client.Event.SDK_READY, () => {
103+
client.on(client.Event.SDK_READY, (metadata) => {
104104
assert.deepEqual(manager.names(), ['testing_split', 'testing_split_with_config']);
105105
assert.equal(client.getTreatment('testing_split_with_config'), 'off');
106+
assert.true(metadata != null && typeof metadata.initialCacheLoad === 'boolean', 'SDK_READY must receive SdkReadyMetadata with initialCacheLoad');
106107
readyCount++;
107108
});
108-
client.on(client.Event.SDK_UPDATE, () => {
109+
client.on(client.Event.SDK_UPDATE, (metadata) => {
109110
assert.deepEqual(manager.names().sort(), ['testing_split', 'testing_split_2', 'testing_split_3', 'testing_split_with_config']);
110111
assert.equal(client.getTreatment('testing_split_with_config'), 'nope');
112+
assert.true(metadata != null && typeof metadata.type === 'string' && ['FLAGS_UPDATE', 'SEGMENTS_UPDATE'].includes(metadata.type), 'SDK_UPDATE must receive SdkUpdateMetadata with type FLAGS_UPDATE or SEGMENTS_UPDATE');
113+
assert.true(Array.isArray(metadata.names), 'SDK_UPDATE must receive SdkUpdateMetadata with names array');
111114
updateCount++;
112115
});
113116
client.on(client.Event.SDK_READY_TIMED_OUT, () => {
@@ -252,8 +255,10 @@ tape('Browser offline mode', function (assert) {
252255
setTimeout(() => { factory.settings.features = { testing_split: 'on', testing_split_with_config: { treatment: 'off', config: '{ "color": "blue" }' } }; }, 750);
253256

254257
// once updated, test again.
255-
client.once(client.Event.SDK_UPDATE, function () {
258+
client.once(client.Event.SDK_UPDATE, function (metadata) {
256259
assert.true((Date.now() - readyTimestamp) > 1000, 'Should only emit SDK_UPDATE after a real update.');
260+
assert.true(metadata != null && typeof metadata.type === 'string' && ['FLAGS_UPDATE', 'SEGMENTS_UPDATE'].includes(metadata.type), 'SDK_UPDATE must receive SdkUpdateMetadata with type');
261+
assert.true(Array.isArray(metadata.names), 'SDK_UPDATE must receive SdkUpdateMetadata with names array');
257262

258263
client.once(client.Event.SDK_UPDATE, function () { assert.fail('Should not emit a second SDK_UPDATE event'); });
259264

src/settings/defaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type SplitIO from '@splitsoftware/splitio-commons/types/splitio';
22
import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/logger/index';
33
import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants';
44

5-
const packageVersion = '1.6.1';
5+
const packageVersion = '1.7.0';
66

77
/**
88
* In browser, the default debug level, can be set via the `localStorage.splitio_debug` item.

ts-tests/index.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,12 @@ client = client.removeAllListeners();
278278

279279
// Ready and destroy
280280
let promise: Promise<void> = client.ready();
281-
promise = client.whenReady();
282281
promise = client.destroy();
283282
promise = SDK.destroy();
284283
// @TODO not public yet
285284
// promise = client.flush();
286-
const promiseWhenReadyFromCache: Promise<boolean> = client.whenReadyFromCache();
285+
let promiseWithMetadata: Promise<SplitIO.SdkReadyMetadata> = client.whenReady();
286+
promiseWithMetadata = client.whenReadyFromCache();
287287

288288
// Get readiness status
289289
let status: SplitIO.ReadinessStatus = client.getStatus();
@@ -374,6 +374,63 @@ tracked = client.track('myTrafficType', 'myEventType', 10);
374374
tracked = client.track('myTrafficType', 'myEventType', null, { prop1: 1, prop2: '2', prop3: false, prop4: null });
375375
// tracked = client.track('myEventType', undefined, { prop1: 1, prop2: '2', prop3: false, prop4: null }); // Not valid in Browser JS SDK
376376

377+
/*** Tests for SDK Update Metadata ***/
378+
379+
// Using addListener with typed metadata
380+
client.addListener(client.Event.SDK_UPDATE, (metadata: SplitIO.SdkUpdateMetadata) => {
381+
const type: SplitIO.SdkUpdateMetadataType = metadata.type;
382+
const names: string[] = metadata.names;
383+
});
384+
client.addListener(client.Event.SDK_READY, (metadata: SplitIO.SdkReadyMetadata) => {
385+
const fromCache: boolean = metadata.initialCacheLoad;
386+
const timestamp: number = metadata.lastUpdateTimestamp;
387+
});
388+
client.addListener(client.Event.SDK_READY_FROM_CACHE, (metadata: SplitIO.SdkReadyMetadata) => {
389+
const fromCache: boolean = metadata.initialCacheLoad;
390+
const timestamp: number = metadata.lastUpdateTimestamp;
391+
});
392+
client.addListener(client.Event.SDK_UPDATE, () => { });
393+
client.addListener(client.Event.SDK_READY, () => { });
394+
client.addListener(client.Event.SDK_READY_FROM_CACHE, () => { });
395+
396+
// Using once with typed metadata
397+
client.once(client.Event.SDK_UPDATE, (metadata: SplitIO.SdkUpdateMetadata) => {
398+
const type: SplitIO.SdkUpdateMetadataType = metadata.type;
399+
const names: string[] = metadata.names;
400+
});
401+
client.once(client.Event.SDK_READY, (metadata: SplitIO.SdkReadyMetadata) => {
402+
const fromCache: boolean = metadata.initialCacheLoad;
403+
const timestamp: number = metadata.lastUpdateTimestamp;
404+
});
405+
client.once(client.Event.SDK_READY_FROM_CACHE, (metadata: SplitIO.SdkReadyMetadata) => {
406+
const fromCache: boolean = metadata.initialCacheLoad;
407+
const timestamp: number = metadata.lastUpdateTimestamp;
408+
});
409+
client.once(client.Event.SDK_UPDATE, () => { });
410+
client.once(client.Event.SDK_READY, () => { });
411+
client.once(client.Event.SDK_READY_FROM_CACHE, () => { });
412+
413+
// SDK_READY event listener with metadata
414+
client.on(client.Event.SDK_READY, (metadata: SplitIO.SdkReadyMetadata) => {
415+
const fromCache: boolean = metadata.initialCacheLoad;
416+
const timestamp: number = metadata.lastUpdateTimestamp;
417+
});
418+
419+
// SDK_READY_FROM_CACHE event listener with metadata
420+
client.on(client.Event.SDK_READY_FROM_CACHE, (metadata: SplitIO.SdkReadyMetadata) => {
421+
const fromCache: boolean = metadata.initialCacheLoad;
422+
const timestamp: number = metadata.lastUpdateTimestamp;
423+
});
424+
425+
// SDK_UPDATE event listener with metadata
426+
client.on(client.Event.SDK_UPDATE, (metadata: SplitIO.SdkUpdateMetadata) => {
427+
const type: SplitIO.SdkUpdateMetadataType = metadata.type;
428+
const names: string[] = metadata.names;
429+
});
430+
client.on(client.Event.SDK_UPDATE, () => { });
431+
client.on(client.Event.SDK_READY, () => { });
432+
client.on(client.Event.SDK_READY_FROM_CACHE, () => { });
433+
377434
/*** Repeating tests for Async Client ***/
378435

379436
// Events constants we get (same as for sync client, just for interface checking)

0 commit comments

Comments
 (0)