Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a719bb7
[FME-12059] SDK_UPDATE with metadata
ZamoraEmmanuel Jan 5, 2026
ef651cc
Fix types
ZamoraEmmanuel Jan 6, 2026
f4a6988
Update metadata properties
ZamoraEmmanuel Jan 12, 2026
631366d
Move enum and type to namespace
ZamoraEmmanuel Jan 12, 2026
6f054c9
Remove names when segments update
ZamoraEmmanuel Jan 12, 2026
c9795ea
avoid metadata when rbs update
ZamoraEmmanuel Jan 13, 2026
cb807b5
Merge branch 'development' of github.com:splitio/javascript-commons i…
ZamoraEmmanuel Jan 15, 2026
27dc057
Add ready metadata and polling
ZamoraEmmanuel Jan 17, 2026
22ef058
Fix ts and add changes file
ZamoraEmmanuel Jan 18, 2026
c60ab9e
Remove enum from namespace
ZamoraEmmanuel Jan 18, 2026
ffe940a
Add SdkUpdateMetadataKeys type
ZamoraEmmanuel Jan 18, 2026
fe6d284
remove enum
ZamoraEmmanuel Jan 19, 2026
5464fbc
sdk_ready metadata
ZamoraEmmanuel Jan 21, 2026
dc47929
Fix test
ZamoraEmmanuel Jan 21, 2026
e388a13
Use initalCacheLad instead of isCacheValid
ZamoraEmmanuel Jan 22, 2026
93690bf
Merge pull request #465 from splitio/initial-cache
ZamoraEmmanuel Jan 22, 2026
ff585ca
Merge pull request #463 from splitio/ready-metadata
ZamoraEmmanuel Jan 22, 2026
6c46d7c
Merge branch 'development' into fme-12059
ZamoraEmmanuel Jan 22, 2026
fc78250
Simplify SDK_READY metadata handling by removing redundant logic
EmilianoSanchez Jan 22, 2026
1fcebd4
Polishing
EmilianoSanchez Jan 22, 2026
25fe09a
Fix message
ZamoraEmmanuel Jan 22, 2026
2ce34d1
Enforce semicolon delimiter style in TypeScript type definitions
EmilianoSanchez Jan 23, 2026
8674fdf
Update whenReady and whenReadyFromCache to return SdkReadyMetadata ob…
EmilianoSanchez Jan 23, 2026
e57a2c6
Merge pull request #466 from splitio/fme-12059-promises
EmilianoSanchez Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
2.11.0 (January XX, 2026)
- Added metadata to SDK_UPDATE events to indicate the type of update (FLAGS_UPDATE or SEGMENTS_UPDATE) and the names of updated flags or segments.
- Added metadata to SDK_READY and SDK_READY_FROM_CACHE events, including `initialCacheLoad` (boolean indicating if SDK was loaded from cache) and `lastUpdateTimestamp` (Int64 milliseconds since epoch).
- Added metadata to SDK_READY and SDK_READY_FROM_CACHE events, including `initialCacheLoad` (boolean: `true` for fresh install/first app launch, `false` for warm cache/second app launch) and `lastUpdateTimestamp` (milliseconds since epoch).

2.10.1 (December 18, 2025)
- Bugfix - Handle `null` prerequisites properly.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio-commons",
"version": "2.10.2-rc.1",
"version": "2.10.2-rc.5",
"description": "Split JavaScript SDK common components",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down
56 changes: 25 additions & 31 deletions src/readiness/__tests__/readinessManager.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { readinessManagerFactory } from '../readinessManager';
import { EventEmitter } from '../../utils/MinEvents';
import { IReadinessManager } from '../types';
import { SDK_READY, SDK_UPDATE, SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_READY_FROM_CACHE, SDK_SPLITS_CACHE_LOADED, SDK_READY_TIMED_OUT } from '../constants';
import { SDK_READY, SDK_UPDATE, SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_READY_FROM_CACHE, SDK_SPLITS_CACHE_LOADED, SDK_READY_TIMED_OUT, FLAGS_UPDATE, SEGMENTS_UPDATE } from '../constants';
import { ISettings } from '../../types';
import { SdkUpdateMetadata, SdkReadyMetadata } from '../../../types/splitio';
import { SdkUpdateMetadataKeys } from '../../sync/polling/types';

const settings = {
startup: {
Expand Down Expand Up @@ -101,15 +100,13 @@ test('READINESS MANAGER / Ready from cache event should be fired once', (done) =
counter++;
});

readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: undefined });
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: undefined });
setTimeout(() => {
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: undefined });
}, 0);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: undefined });
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: undefined });

setTimeout(() => {
expect(counter).toBe(1); // should be called only once
Expand Down Expand Up @@ -311,7 +308,7 @@ test('READINESS MANAGER / SDK_UPDATE should emit with metadata', () => {
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);

const metadata: SdkUpdateMetadata = {
type: SdkUpdateMetadataKeys.FLAGS_UPDATE,
type: FLAGS_UPDATE,
names: ['flag1', 'flag2']
};

Expand Down Expand Up @@ -350,7 +347,7 @@ test('READINESS MANAGER / SDK_UPDATE should forward metadata from segments', ()
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);

const metadata: SdkUpdateMetadata = {
type: SdkUpdateMetadataKeys.SEGMENTS_UPDATE,
type: SEGMENTS_UPDATE,
names: []
};

Expand All @@ -367,19 +364,21 @@ test('READINESS MANAGER / SDK_UPDATE should forward metadata from segments', ()
test('READINESS MANAGER / SDK_READY_FROM_CACHE should emit with metadata when cache is loaded', () => {
const readinessManager = readinessManagerFactory(EventEmitter, settings);

const cacheTimestamp = Date.now() - 1000 * 60 * 60; // 1 hour ago
let receivedMetadata: SdkReadyMetadata | undefined;
readinessManager.gate.on(SDK_READY_FROM_CACHE, (meta: SdkReadyMetadata) => {
receivedMetadata = meta;
});

// Emit cache loaded event
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
// Emit cache loaded event with timestamp
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, {
initialCacheLoad: false,
lastUpdateTimestamp: cacheTimestamp
});

expect(receivedMetadata).toBeDefined();
expect(receivedMetadata!.initialCacheLoad).toBe(true);
expect(receivedMetadata!.lastUpdateTimestamp).toBeGreaterThan(0);
// Allow small timing difference (up to 10ms)
expect(receivedMetadata!.lastUpdateTimestamp).toBeLessThanOrEqual(Date.now() + 10);
expect(receivedMetadata!.initialCacheLoad).toBe(false);
expect(receivedMetadata!.lastUpdateTimestamp).toBe(cacheTimestamp);
});

test('READINESS MANAGER / SDK_READY_FROM_CACHE should emit with metadata when SDK becomes ready without cache', () => {
Expand All @@ -395,17 +394,16 @@ test('READINESS MANAGER / SDK_READY_FROM_CACHE should emit with metadata when SD
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);

expect(receivedMetadata).toBeDefined();
expect(receivedMetadata!.initialCacheLoad).toBe(false);
expect(receivedMetadata!.lastUpdateTimestamp).toBeGreaterThan(0);
// Allow small timing difference (up to 10ms)
expect(receivedMetadata!.lastUpdateTimestamp).toBeLessThanOrEqual(Date.now() + 10);
expect(receivedMetadata!.initialCacheLoad).toBe(true);
expect(receivedMetadata!.lastUpdateTimestamp).toBeUndefined();
});

test('READINESS MANAGER / SDK_READY should emit with metadata when ready from cache', () => {
const readinessManager = readinessManagerFactory(EventEmitter, settings);

// First emit cache loaded
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
const cacheTimestamp = Date.now() - 1000 * 60 * 60; // 1 hour ago
// First emit cache loaded with timestamp
readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: cacheTimestamp });

let receivedMetadata: SdkReadyMetadata | undefined;
readinessManager.gate.on(SDK_READY, (meta: SdkReadyMetadata) => {
Expand All @@ -417,10 +415,8 @@ test('READINESS MANAGER / SDK_READY should emit with metadata when ready from ca
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);

expect(receivedMetadata).toBeDefined();
expect(receivedMetadata!.initialCacheLoad).toBe(true); // Was ready from cache first
expect(receivedMetadata!.lastUpdateTimestamp).toBeGreaterThan(0);
// Allow small timing difference (up to 10ms)
expect(receivedMetadata!.lastUpdateTimestamp).toBeLessThanOrEqual(Date.now() + 10);
expect(receivedMetadata!.initialCacheLoad).toBe(false); // Was ready from cache first
expect(receivedMetadata!.lastUpdateTimestamp).toBe(cacheTimestamp);
});

test('READINESS MANAGER / SDK_READY should emit with metadata when ready without cache', () => {
Expand All @@ -436,8 +432,6 @@ test('READINESS MANAGER / SDK_READY should emit with metadata when ready without
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);

expect(receivedMetadata).toBeDefined();
expect(receivedMetadata!.initialCacheLoad).toBe(false); // Was not ready from cache
expect(receivedMetadata!.lastUpdateTimestamp).toBeGreaterThan(0);
// Allow small timing difference (up to 10ms)
expect(receivedMetadata!.lastUpdateTimestamp).toBeLessThanOrEqual(Date.now() + 10);
expect(receivedMetadata!.initialCacheLoad).toBe(true); // Was not ready from cache
expect(receivedMetadata!.lastUpdateTimestamp).toBeUndefined(); // No cache timestamp when fresh install
});
2 changes: 1 addition & 1 deletion src/readiness/__tests__/sdkReadinessManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ describe('SDK Readiness Manager - Promises', () => {
const sdkReadinessManager = sdkReadinessManagerFactory(EventEmitter, fullSettings);

// make the SDK ready from cache
sdkReadinessManager.readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED);
sdkReadinessManager.readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: null });
expect(await sdkReadinessManager.sdkStatus.whenReadyFromCache()).toBe(false);

// validate error log for SDK_READY_FROM_CACHE
Expand Down
4 changes: 4 additions & 0 deletions src/readiness/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ export const SDK_READY_TIMED_OUT = 'init::timeout';
export const SDK_READY = 'init::ready';
export const SDK_READY_FROM_CACHE = 'init::cache-ready';
export const SDK_UPDATE = 'state::update';

// SdkUpdateMetadata types:
export const FLAGS_UPDATE = 'FLAGS_UPDATE';
export const SEGMENTS_UPDATE = 'SEGMENTS_UPDATE';
24 changes: 11 additions & 13 deletions src/readiness/readinessManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { objectAssign } from '../utils/lang/objectAssign';
import { ISettings } from '../types';
import SplitIO from '../../types/splitio';
import SplitIO, { SdkReadyMetadata } from '../../types/splitio';
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';

Expand Down Expand Up @@ -55,6 +55,7 @@ export function readinessManagerFactory(

// emit SDK_READY_FROM_CACHE
let isReadyFromCache = false;
let cacheLastUpdateTimestamp: number | undefined = undefined;
if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
else splits.once(SDK_SPLITS_CACHE_LOADED, checkIsReadyFromCache);

Expand Down Expand Up @@ -84,17 +85,14 @@ export function readinessManagerFactory(
splits.initCallbacks.push(__init);
if (splits.hasInit) __init();

function checkIsReadyFromCache() {
function checkIsReadyFromCache(cacheMetadata: SdkReadyMetadata) {
cacheLastUpdateTimestamp = cacheMetadata.lastUpdateTimestamp;
isReadyFromCache = true;
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
if (!isReady && !isDestroyed) {
try {
syncLastUpdate();
const metadata: SplitIO.SdkReadyMetadata = {
initialCacheLoad: true,
lastUpdateTimestamp: lastUpdate
};
gate.emit(SDK_READY_FROM_CACHE, metadata);
gate.emit(SDK_READY_FROM_CACHE, cacheMetadata);
} catch (e) {
// throws user callback exceptions in next tick
setTimeout(() => { throw e; }, 0);
Expand All @@ -121,15 +119,15 @@ export function readinessManagerFactory(
const wasReadyFromCache = isReadyFromCache;
if (!isReadyFromCache) {
isReadyFromCache = true;
const metadataFromCache: SplitIO.SdkReadyMetadata = {
initialCacheLoad: false,
lastUpdateTimestamp: lastUpdate
const metadataReadyFromCache: SplitIO.SdkReadyMetadata = {
initialCacheLoad: true, // Fresh install, no cache existed
lastUpdateTimestamp: undefined // No cache timestamp when fresh install
};
gate.emit(SDK_READY_FROM_CACHE, metadataFromCache);
gate.emit(SDK_READY_FROM_CACHE, metadataReadyFromCache);
}
const metadataReady: SplitIO.SdkReadyMetadata = {
initialCacheLoad: wasReadyFromCache,
lastUpdateTimestamp: lastUpdate
initialCacheLoad: !wasReadyFromCache, // true if not ready from cache (initial load), false if ready from cache
lastUpdateTimestamp: wasReadyFromCache ? cacheLastUpdateTimestamp : undefined
};
gate.emit(SDK_READY, metadataReady);
} catch (e) {
Expand Down
6 changes: 3 additions & 3 deletions src/sdkFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
return;
}
readiness.splits.emit(SDK_SPLITS_ARRIVED);
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
Comment thread
ZamoraEmmanuel marked this conversation as resolved.
Outdated
},
onReadyFromCacheCb() {
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
}
});

const fallbackTreatmentsCalculator = new FallbackTreatmentsCalculator(settings.fallbackTreatments);

if (initialRolloutPlan) {
setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
}

const clients: Record<string, SplitIO.IBasicClient> = {};
Expand Down
42 changes: 30 additions & 12 deletions src/storages/inLocalStorage/__tests__/validateCache.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ describe.each(storages)('validateCache', (storage) => {
for (let i = 0; i < storage.length; i++) storage.removeItem(storage.key(i) as string);
});

test('if there is no cache, it should return false', async () => {
expect(await validateCache({}, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(false);
test('if there is no cache, it should return initialCacheLoad: true', async () => {
const result = await validateCache({}, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result.initialCacheLoad).toBe(true);
expect(result.lastUpdateTimestamp).toBeUndefined();

expect(logSpy).not.toHaveBeenCalled();

Expand All @@ -44,12 +46,16 @@ describe.each(storages)('validateCache', (storage) => {
expect(storage.getItem(keys.buildLastClear())).toBeNull();
});

test('if there is cache and it must not be cleared, it should return true', async () => {
test('if there is cache and it must not be cleared, it should return initialCacheLoad: false', async () => {
const lastUpdateTimestamp = Date.now() - 1000 * 60 * 60; // 1 hour ago
storage.setItem(keys.buildSplitsTillKey(), '1');
storage.setItem(keys.buildHashKey(), FULL_SETTINGS_HASH);
storage.setItem(keys.buildLastUpdatedKey(), lastUpdateTimestamp + '');
await storage.save && storage.save();

expect(await validateCache({}, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(true);
const result = await validateCache({}, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result.initialCacheLoad).toBe(false);
expect(result.lastUpdateTimestamp).toBe(lastUpdateTimestamp);

expect(logSpy).not.toHaveBeenCalled();

Expand All @@ -63,13 +69,15 @@ describe.each(storages)('validateCache', (storage) => {
expect(storage.getItem(keys.buildLastClear())).toBeNull();
});

test('if there is cache and it has expired, it should clear cache and return false', async () => {
test('if there is cache and it has expired, it should clear cache and return initialCacheLoad: true', async () => {
storage.setItem(keys.buildSplitsTillKey(), '1');
storage.setItem(keys.buildHashKey(), FULL_SETTINGS_HASH);
storage.setItem(keys.buildLastUpdatedKey(), Date.now() - 1000 * 60 * 60 * 24 * 2 + ''); // 2 days ago
await storage.save && storage.save();

expect(await validateCache({ expirationDays: 1 }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(false);
const result = await validateCache({ expirationDays: 1 }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result.initialCacheLoad).toBe(true);
expect(result.lastUpdateTimestamp).toBeUndefined();

expect(logSpy).toHaveBeenCalledWith('storage:localstorage: Cache expired more than 1 days ago. Cleaning up cache');

Expand All @@ -82,12 +90,14 @@ describe.each(storages)('validateCache', (storage) => {
expect(nearlyEqual(parseInt(storage.getItem(keys.buildLastClear()) as string), Date.now())).toBe(true);
});

test('if there is cache and its hash has changed, it should clear cache and return false', async () => {
test('if there is cache and its hash has changed, it should clear cache and return initialCacheLoad: true', async () => {
storage.setItem(keys.buildSplitsTillKey(), '1');
storage.setItem(keys.buildHashKey(), FULL_SETTINGS_HASH);
await storage.save && storage.save();

expect(await validateCache({}, storage, { ...fullSettings, core: { ...fullSettings.core, authorizationKey: 'another-sdk-key' } }, keys, splits, rbSegments, segments, largeSegments)).toBe(false);
const result = await validateCache({}, storage, { ...fullSettings, core: { ...fullSettings.core, authorizationKey: 'another-sdk-key' } }, keys, splits, rbSegments, segments, largeSegments);
expect(result.initialCacheLoad).toBe(true);
expect(result.lastUpdateTimestamp).toBeUndefined();

expect(logSpy).toHaveBeenCalledWith('storage:localstorage: SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');

Expand All @@ -100,14 +110,16 @@ describe.each(storages)('validateCache', (storage) => {
expect(nearlyEqual(parseInt(storage.getItem(keys.buildLastClear()) as string), Date.now())).toBe(true);
});

test('if there is cache and clearOnInit is true, it should clear cache and return false', async () => {
test('if there is cache and clearOnInit is true, it should clear cache and return initialCacheLoad: true', async () => {
// Older cache version (without last clear)
storage.removeItem(keys.buildLastClear());
storage.setItem(keys.buildSplitsTillKey(), '1');
storage.setItem(keys.buildHashKey(), FULL_SETTINGS_HASH);
await storage.save && storage.save();

expect(await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(false);
const result = await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result.initialCacheLoad).toBe(true);
expect(result.lastUpdateTimestamp).toBeUndefined();

expect(logSpy).toHaveBeenCalledWith('storage:localstorage: clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');

Expand All @@ -122,14 +134,20 @@ describe.each(storages)('validateCache', (storage) => {

// If cache is cleared, it should not clear again until a day has passed
logSpy.mockClear();
const lastUpdateTimestamp = Date.now() - 1000 * 60 * 60; // 1 hour ago
storage.setItem(keys.buildSplitsTillKey(), '1');
expect(await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(true);
storage.setItem(keys.buildLastUpdatedKey(), lastUpdateTimestamp + '');
const result2 = await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result2.initialCacheLoad).toBe(false);
expect(result2.lastUpdateTimestamp).toBe(lastUpdateTimestamp);
expect(logSpy).not.toHaveBeenCalled();
expect(storage.getItem(keys.buildLastClear())).toBe(lastClear); // Last clear should not have changed

// If a day has passed, it should clear again
storage.setItem(keys.buildLastClear(), (Date.now() - 1000 * 60 * 60 * 24 - 1) + '');
expect(await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments)).toBe(false);
const result3 = await validateCache({ clearOnInit: true }, storage, fullSettings, keys, splits, rbSegments, segments, largeSegments);
expect(result3.initialCacheLoad).toBe(true);
expect(result3.lastUpdateTimestamp).toBeUndefined();
expect(logSpy).toHaveBeenCalledWith('storage:localstorage: clearOnInit was set and cache was not cleared in the last 24 hours. Cleaning up cache');
expect(splits.clear).toHaveBeenCalledTimes(2);
expect(rbSegments.clear).toHaveBeenCalledTimes(2);
Expand Down
Loading