Skip to content

Commit c7d8e4c

Browse files
Merge pull request #343 from splitio/development
Release v1.17.0
2 parents 7565f89 + 101b70a commit c7d8e4c

22 files changed

+492
-289
lines changed

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
1.17.0 (September 6, 2024)
2+
- Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks.
3+
- Added `isTimedout` and `lastUpdate` properties to IStatusInterface to keep track of the timestamp of the last SDK event, used on React and Redux SDKs.
4+
- Updated some transitive dependencies for vulnerability fixes.
5+
16
1.16.0 (June 13, 2024)
27
- Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in NodeJS.
38
- Updated the Redis storage to lazily import the `ioredis` dependency when the storage is created. This prevents errors when the SDK is imported or bundled in a .mjs file, as `ioredis` is a CommonJS module.

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-commons",
3-
"version": "1.16.0",
3+
"version": "1.17.0",
44
"description": "Split JavaScript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/logger/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export const ENGINE_MATCHER_ERROR = 329;
120120
// Log prefixes (a.k.a. tags or categories)
121121
export const LOG_PREFIX_SETTINGS = 'settings';
122122
export const LOG_PREFIX_INSTANTIATION = 'Factory instantiation';
123+
export const LOG_PREFIX_CLIENT_INSTANTIATION = 'Client instantiation';
123124
export const LOG_PREFIX_ENGINE = 'engine';
124125
export const LOG_PREFIX_ENGINE_COMBINER = LOG_PREFIX_ENGINE + ':combiner: ';
125126
export const LOG_PREFIX_ENGINE_MATCHER = LOG_PREFIX_ENGINE + ':matcher: ';

src/readiness/__tests__/readinessManager.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import { IReadinessManager } from '../types';
44
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';
55

66
const timeoutMs = 100;
7-
const statusFlagsCount = 5;
7+
const statusFlagsCount = 7;
88

99
function assertInitialStatus(readinessManager: IReadinessManager) {
1010
expect(readinessManager.isReady()).toBe(false);
1111
expect(readinessManager.isReadyFromCache()).toBe(false);
12+
expect(readinessManager.isTimedout()).toBe(false);
1213
expect(readinessManager.hasTimedout()).toBe(false);
1314
expect(readinessManager.isDestroyed()).toBe(false);
1415
expect(readinessManager.isOperational()).toBe(false);
16+
expect(readinessManager.lastUpdate()).toBe(0);
1517
}
1618

1719
test('READINESS MANAGER / Share splits but segments (without timeout enabled)', (done) => {
@@ -153,6 +155,7 @@ describe('READINESS MANAGER / Timeout ready event', () => {
153155
timeoutCounter = 0;
154156

155157
readinessManager.gate.on(SDK_READY_TIMED_OUT, () => {
158+
expect(readinessManager.isTimedout()).toBe(true);
156159
expect(readinessManager.hasTimedout()).toBe(true);
157160
if (!readinessManager.isReady()) timeoutCounter++;
158161
});
@@ -166,6 +169,8 @@ describe('READINESS MANAGER / Timeout ready event', () => {
166169
test('should be fired once', (done) => {
167170
readinessManager.gate.on(SDK_READY, () => {
168171
expect(readinessManager.isReady()).toBe(true);
172+
expect(readinessManager.isTimedout()).toBe(false);
173+
expect(readinessManager.hasTimedout()).toBe(true);
169174
expect(timeoutCounter).toBe(1);
170175
done();
171176
});
@@ -221,14 +226,24 @@ test('READINESS MANAGER / Destroy after it was ready but before timedout', () =>
221226
counter++;
222227
});
223228

229+
let lastUpdate = readinessManager.lastUpdate();
230+
expect(lastUpdate).toBe(0);
231+
224232
readinessManager.splits.emit(SDK_SPLITS_ARRIVED);
225233
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // ready state
226234

235+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
236+
lastUpdate = readinessManager.lastUpdate();
237+
227238
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); // fires an update
239+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
240+
lastUpdate = readinessManager.lastUpdate();
228241

229242
expect(readinessManager.isDestroyed()).toBe(false);
230243
readinessManager.destroy(); // Destroy the gate, removing all the listeners and clearing the ready timeout.
231244
expect(readinessManager.isDestroyed()).toBe(true);
245+
expect(readinessManager.lastUpdate()).toBeGreaterThan(lastUpdate);
246+
232247
readinessManager.destroy(); // no-op
233248
readinessManager.destroy(); // no-op
234249

src/readiness/__tests__/sdkReadinessManager.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ describe('SDK Readiness Manager - Event emitter', () => {
5050
});
5151

5252
expect(typeof sdkStatus.ready).toBe('function'); // The sdkStatus exposes a .ready() function.
53+
expect(typeof sdkStatus.__getStatus).toBe('function'); // The sdkStatus exposes a .__getStatus() function.
54+
expect(sdkStatus.__getStatus()).toEqual({
55+
isReady: false, isReadyFromCache: false, isTimedout: false, hasTimedout: false, isDestroyed: false, isOperational: false, lastUpdate: 0
56+
});
5357

5458
expect(typeof sdkStatus.Event).toBe('object'); // It also exposes the Event map,
5559
expect(sdkStatus.Event.SDK_READY).toBe(SDK_READY); // which contains the constants for the events, for backwards compatibility.

src/readiness/readinessManager.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export function readinessManagerFactory(
3939
const segments: ISegmentsEventEmitter = segmentsEventEmitterFactory(EventEmitter);
4040
const gate: IReadinessEventEmitter = new EventEmitter();
4141

42+
let lastUpdate = 0;
43+
function syncLastUpdate() {
44+
const dateNow = Date.now();
45+
// ensure lastUpdate is always increasing per event, is case Date.now() is mocked or its value is the same
46+
lastUpdate = dateNow > lastUpdate ? dateNow : lastUpdate + 1;
47+
}
48+
4249
// emit SDK_READY_FROM_CACHE
4350
let isReadyFromCache = false;
4451
if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
@@ -50,6 +57,7 @@ export function readinessManagerFactory(
5057
function timeout() {
5158
if (hasTimedout) return;
5259
hasTimedout = true;
60+
syncLastUpdate();
5361
gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
5462
}
5563

@@ -70,6 +78,7 @@ export function readinessManagerFactory(
7078
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
7179
if (!isReady) {
7280
try {
81+
syncLastUpdate();
7382
gate.emit(SDK_READY_FROM_CACHE);
7483
} catch (e) {
7584
// throws user callback exceptions in next tick
@@ -81,6 +90,7 @@ export function readinessManagerFactory(
8190
function checkIsReadyOrUpdate(diff: any) {
8291
if (isReady) {
8392
try {
93+
syncLastUpdate();
8494
gate.emit(SDK_UPDATE, diff);
8595
} catch (e) {
8696
// throws user callback exceptions in next tick
@@ -91,6 +101,7 @@ export function readinessManagerFactory(
91101
clearTimeout(readyTimeoutId);
92102
isReady = true;
93103
try {
104+
syncLastUpdate();
94105
gate.emit(SDK_READY);
95106
} catch (e) {
96107
// throws user callback exceptions in next tick
@@ -121,6 +132,7 @@ export function readinessManagerFactory(
121132

122133
destroy() {
123134
isDestroyed = true;
135+
syncLastUpdate();
124136

125137
segments.removeAllListeners();
126138
gate.removeAllListeners();
@@ -131,10 +143,12 @@ export function readinessManagerFactory(
131143
},
132144

133145
isReady() { return isReady; },
134-
hasTimedout() { return hasTimedout; },
135146
isReadyFromCache() { return isReadyFromCache; },
147+
isTimedout() { return hasTimedout && !isReady; },
148+
hasTimedout() { return hasTimedout; },
136149
isDestroyed() { return isDestroyed; },
137-
isOperational() { return (isReady || isReadyFromCache) && !isDestroyed; }
150+
isOperational() { return (isReady || isReadyFromCache) && !isDestroyed; },
151+
lastUpdate() { return lastUpdate; }
138152
};
139153

140154
}

src/readiness/sdkReadinessManager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,15 @@ export function sdkReadinessManagerFactory(
121121
return readyPromise;
122122
},
123123

124-
// Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
125124
__getStatus() {
126125
return {
127126
isReady: readinessManager.isReady(),
128127
isReadyFromCache: readinessManager.isReadyFromCache(),
129-
isOperational: readinessManager.isOperational(),
128+
isTimedout: readinessManager.isTimedout(),
130129
hasTimedout: readinessManager.hasTimedout(),
131130
isDestroyed: readinessManager.isDestroyed(),
131+
isOperational: readinessManager.isOperational(),
132+
lastUpdate: readinessManager.lastUpdate(),
132133
};
133134
},
134135
}

src/readiness/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ export interface IReadinessManager {
5050
/** Readiness status */
5151
isReady(): boolean,
5252
isReadyFromCache(): boolean,
53+
isTimedout(): boolean,
5354
hasTimedout(): boolean,
5455
isDestroyed(): boolean,
5556
isOperational(): boolean,
57+
lastUpdate(): number,
5658

5759
timeout(): void,
5860
setDestroyed(): void,

src/sdkClient/sdkClientMethodCS.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getMatching, keyParser } from '../utils/key';
55
import { sdkClientFactory } from './sdkClient';
66
import { ISyncManagerCS } from '../sync/types';
77
import { objectAssign } from '../utils/lang/objectAssign';
8-
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
8+
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
99
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
1010
import { ISdkFactoryContext } from '../sdkFactory/types';
1111

@@ -14,8 +14,6 @@ function buildInstanceId(key: SplitIO.SplitKey) {
1414
return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-`;
1515
}
1616

17-
const method = 'Client instantiation';
18-
1917
/**
2018
* Factory of client method for the client-side API variant where TT is ignored.
2119
* Therefore, clients don't have a bound TT for the track method.
@@ -43,7 +41,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
4341
}
4442

4543
// Validate the key value. The trafficType (2nd argument) is ignored
46-
const validKey = validateKey(log, key, method);
44+
const validKey = validateKey(log, key, LOG_PREFIX_CLIENT_INSTANTIATION);
4745
if (validKey === false) {
4846
throw new Error('Shared Client needs a valid key.');
4947
}

0 commit comments

Comments
 (0)