From 8674fdf1f246fda45179a7dd3d6c1720fb5f8280 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 23 Jan 2026 15:03:31 -0300 Subject: [PATCH] Update whenReady and whenReadyFromCache to return SdkReadyMetadata object --- .../__tests__/sdkReadinessManager.spec.ts | 15 ++++++++------- src/readiness/readinessManager.ts | 3 ++- src/readiness/sdkReadinessManager.ts | 10 +++++----- src/readiness/types.ts | 1 + types/splitio.d.ts | 11 ++++++----- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/readiness/__tests__/sdkReadinessManager.spec.ts b/src/readiness/__tests__/sdkReadinessManager.spec.ts index 63174eea..d052e0a5 100644 --- a/src/readiness/__tests__/sdkReadinessManager.spec.ts +++ b/src/readiness/__tests__/sdkReadinessManager.spec.ts @@ -220,11 +220,11 @@ describe('SDK Readiness Manager - Promises', () => { // make the SDK ready from cache sdkReadinessManager.readinessManager.splits.emit(SDK_SPLITS_CACHE_LOADED, { initialCacheLoad: false, lastUpdateTimestamp: null }); - expect(await sdkReadinessManager.sdkStatus.whenReadyFromCache()).toBe(false); + expect(await sdkReadinessManager.sdkStatus.whenReadyFromCache()).toEqual({ initialCacheLoad: false, lastUpdateTimestamp: null }); // validate error log for SDK_READY_FROM_CACHE expect(loggerMock.error).not.toBeCalled(); - sdkReadinessManager.readinessManager.gate.on(SDK_READY_FROM_CACHE, () => {}); + sdkReadinessManager.readinessManager.gate.on(SDK_READY_FROM_CACHE, () => { }); expect(loggerMock.error).toBeCalledWith(ERROR_CLIENT_LISTENER, ['SDK_READY_FROM_CACHE']); const readyFromCache = sdkReadinessManager.sdkStatus.whenReadyFromCache(); @@ -232,7 +232,8 @@ describe('SDK Readiness Manager - Promises', () => { // make the SDK ready emitReadyEvent(sdkReadinessManager.readinessManager); - expect(await sdkReadinessManager.sdkStatus.whenReadyFromCache()).toBe(true); + expect(await sdkReadinessManager.sdkStatus.whenReadyFromCache()).toEqual({ initialCacheLoad: false, lastUpdateTimestamp: null }); + expect(await sdkReadinessManager.sdkStatus.whenReady()).toEqual({ initialCacheLoad: false, lastUpdateTimestamp: null }); let testPassedCount = 0; function incTestPassedCount() { testPassedCount++; } @@ -260,12 +261,12 @@ describe('SDK Readiness Manager - Promises', () => { function incTestPassedCount() { testPassedCount++; } function throwTestFailed() { throw new Error('It should rejected, not resolved.'); } - await readyFromCacheForTimeout.then(throwTestFailed,incTestPassedCount); - await readyForTimeout.then(throwTestFailed,incTestPassedCount); + await readyFromCacheForTimeout.then(throwTestFailed, incTestPassedCount); + await readyForTimeout.then(throwTestFailed, incTestPassedCount); // any subsequent call to .whenReady() and .whenReadyFromCache() must be a rejected promise until the SDK is ready - await sdkReadinessManagerForTimedout.sdkStatus.whenReadyFromCache().then(throwTestFailed,incTestPassedCount); - await sdkReadinessManagerForTimedout.sdkStatus.whenReady().then(throwTestFailed,incTestPassedCount); + await sdkReadinessManagerForTimedout.sdkStatus.whenReadyFromCache().then(throwTestFailed, incTestPassedCount); + await sdkReadinessManagerForTimedout.sdkStatus.whenReady().then(throwTestFailed, incTestPassedCount); // make the SDK ready emitReadyEvent(sdkReadinessManagerForTimedout.readinessManager); diff --git a/src/readiness/readinessManager.ts b/src/readiness/readinessManager.ts index c0bbff78..95d2fe41 100644 --- a/src/readiness/readinessManager.ts +++ b/src/readiness/readinessManager.ts @@ -172,7 +172,8 @@ export function readinessManagerFactory( hasTimedout() { return hasTimedout; }, isDestroyed() { return isDestroyed; }, isOperational() { return (isReady || isReadyFromCache) && !isDestroyed; }, - lastUpdate() { return lastUpdate; } + lastUpdate() { return lastUpdate; }, + metadataReady() { return metadataReady; } }; } diff --git a/src/readiness/sdkReadinessManager.ts b/src/readiness/sdkReadinessManager.ts index d3b841de..5b40757e 100644 --- a/src/readiness/sdkReadinessManager.ts +++ b/src/readiness/sdkReadinessManager.ts @@ -120,9 +120,9 @@ export function sdkReadinessManagerFactory( }, whenReady() { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { if (readinessManager.isReady()) { - resolve(); + resolve(readinessManager.metadataReady()); } else if (readinessManager.hasTimedout()) { reject(TIMEOUT_ERROR); } else { @@ -133,13 +133,13 @@ export function sdkReadinessManagerFactory( }, whenReadyFromCache() { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { if (readinessManager.isReadyFromCache()) { - resolve(readinessManager.isReady()); + resolve(readinessManager.metadataReady()); } else if (readinessManager.hasTimedout()) { reject(TIMEOUT_ERROR); } else { - readinessManager.gate.once(SDK_READY_FROM_CACHE, () => resolve(readinessManager.isReady())); + readinessManager.gate.once(SDK_READY_FROM_CACHE, resolve); readinessManager.gate.once(SDK_READY_TIMED_OUT, () => reject(TIMEOUT_ERROR)); } }); diff --git a/src/readiness/types.ts b/src/readiness/types.ts index 68a78fa2..d4b317a1 100644 --- a/src/readiness/types.ts +++ b/src/readiness/types.ts @@ -60,6 +60,7 @@ export interface IReadinessManager { isDestroyed(): boolean, isOperational(): boolean, lastUpdate(): number, + metadataReady(): SplitIO.SdkReadyMetadata, timeout(): void, setDestroyed(): void, diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 79ab376e..b8753566 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -831,18 +831,19 @@ declare namespace SplitIO { * As it's meant to provide similar flexibility than event listeners, given that the SDK might be ready after a timeout event, the `whenReady` method will return a resolved promise once the SDK is ready. * You must handle the promise rejection to avoid an unhandled promise rejection error, or set the `startup.readyTimeout` configuration option to 0 to avoid the timeout and thus the rejection. * - * @returns A promise that resolves once the SDK_READY event is emitted or rejects if the SDK has timedout. + * @returns A promise that resolves once the SDK_READY event is emitted or rejects if the SDK has timedout. The promise resolves with a metadata object that contains the `initialCacheLoad` property, + * which indicates whether the SDK_READY event was emitted together with the SDK_READY_FROM_CACHE event (i.e., fresh install/first app launch) or not (warm cache/subsequent app launch). */ - whenReady(): Promise; + whenReady(): Promise; /** * Returns a promise that resolves when the SDK is ready for evaluations using cached data, which might not yet be synchronized with the backend (`SDK_READY_FROM_CACHE` event emitted), or rejected if the SDK has timedout (`SDK_READY_TIMED_OUT` event emitted). * As it's meant to provide similar flexibility than event listeners, given that the SDK might be ready from cache after a timeout event, the `whenReadyFromCache` method will return a resolved promise once the SDK is ready from cache. * You must handle the promise rejection to avoid an unhandled promise rejection error, or set the `startup.readyTimeout` configuration option to 0 to avoid the timeout and thus the rejection. * - * @returns A promise that resolves once the SDK_READY_FROM_CACHE event is emitted or rejects if the SDK has timedout. The promise resolves with a boolean value that - * indicates whether the SDK_READY_FROM_CACHE event was emitted together with the SDK_READY event (i.e., the SDK is ready and synchronized with the backend) or not. + * @returns A promise that resolves once the SDK_READY_FROM_CACHE event is emitted or rejects if the SDK has timedout. The promise resolves with a metadata object that contains the `initialCacheLoad` property, + * which indicates whether the SDK_READY_FROM_CACHE event was emitted together with the SDK_READY event (i.e., fresh install/first app launch) or not (warm cache/subsequent app launch). */ - whenReadyFromCache(): Promise; + whenReadyFromCache(): Promise; } /** * Common definitions between clients for different environments interface.