Skip to content

Commit e81bd2f

Browse files
Merge pull request #354 from splitio/init_function_for_side_effects
Add internal `init` function in SdkFactory to wrap initialization side effects
2 parents fb616ca + 57ba406 commit e81bd2f

22 files changed

+125
-83
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
2.0.0 (September XX, 2024)
1+
2.0.0 (October XX, 2024)
22
- Added support for targeting rules based on large segments.
33
- Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory.
4+
- Updated the handling of timers and async operations by moving them into an `init` factory method to enable lazy initialization of the SDK. This update is intended for the React SDK.
45
- Bugfixing - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
56
- BREAKING CHANGES:
67
- Updated default flag spec version to 1.2.

package-lock.json

Lines changed: 2 additions & 2 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.17.1-rc.1",
3+
"version": "1.17.1-rc.3",
44
"description": "Split JavaScript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/readiness/__tests__/readinessManager.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,14 @@ test('READINESS MANAGER / Segment updates should not be propagated', (done) => {
157157
});
158158
});
159159

160-
describe('READINESS MANAGER / Timeout ready event', () => {
160+
describe('READINESS MANAGER / Timeout event', () => {
161161
let readinessManager: IReadinessManager;
162162
let timeoutCounter: number;
163163

164164
beforeEach(() => {
165165
// Schedule timeout to be fired before SDK_READY
166166
readinessManager = readinessManagerFactory(EventEmitter, settingsWithTimeout);
167+
readinessManager.init(); // Start the timeout
167168
timeoutCounter = 0;
168169

169170
readinessManager.gate.on(SDK_READY_TIMED_OUT, () => {
@@ -212,6 +213,7 @@ test('READINESS MANAGER / Cancel timeout if ready fired', (done) => {
212213
let sdkReadyTimedoutCalled = false;
213214

214215
const readinessManager = readinessManagerFactory(EventEmitter, settingsWithTimeout);
216+
readinessManager.init(); // Start the timeout
215217

216218
readinessManager.gate.on(SDK_READY_TIMED_OUT, () => { sdkReadyTimedoutCalled = true; });
217219
readinessManager.gate.once(SDK_READY, () => { sdkReadyCalled = true; });

src/readiness/readinessManager.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ function splitsEventEmitterFactory(EventEmitter: new () => IEventEmitter): ISpli
77
const splitsEventEmitter = objectAssign(new EventEmitter(), {
88
splitsArrived: false,
99
splitsCacheLoaded: false,
10+
initialized: false,
11+
initCallbacks: []
1012
});
1113

1214
// `isSplitKill` condition avoids an edge-case of wrongly emitting SDK_READY if:
@@ -56,16 +58,17 @@ export function readinessManagerFactory(
5658
// emit SDK_READY_TIMED_OUT
5759
let hasTimedout = false;
5860

59-
function timeout() {
60-
if (hasTimedout) return;
61+
function timeout() { // eslint-disable-next-line no-use-before-define
62+
if (hasTimedout || isReady) return;
6163
hasTimedout = true;
6264
syncLastUpdate();
6365
gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
6466
}
6567

6668
let readyTimeoutId: ReturnType<typeof setTimeout>;
6769
if (readyTimeout > 0) {
68-
readyTimeoutId = setTimeout(timeout, readyTimeout);
70+
if (splits.initialized) readyTimeoutId = setTimeout(timeout, readyTimeout);
71+
else splits.initCallbacks.push(() => { readyTimeoutId = setTimeout(timeout, readyTimeout); });
6972
}
7073

7174
// emit SDK_READY and SDK_UPDATE
@@ -132,6 +135,12 @@ export function readinessManagerFactory(
132135
// tracking and evaluations, while keeping event listeners to emit SDK_READY_TIMED_OUT event
133136
setDestroyed() { isDestroyed = true; },
134137

138+
init() {
139+
if (splits.initialized) return;
140+
splits.initialized = true;
141+
splits.initCallbacks.forEach(cb => cb());
142+
},
143+
135144
destroy() {
136145
isDestroyed = true;
137146
syncLastUpdate();

src/readiness/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export interface ISplitsEventEmitter extends IEventEmitter {
1212
once(event: ISplitsEvent, listener: (...args: any[]) => void): this;
1313
splitsArrived: boolean
1414
splitsCacheLoaded: boolean
15+
initialized: boolean,
16+
initCallbacks: (() => void)[]
1517
}
1618

1719
/** Segments data emitter */
@@ -59,6 +61,7 @@ export interface IReadinessManager {
5961
timeout(): void,
6062
setDestroyed(): void,
6163
destroy(): void,
64+
init(): void,
6265

6366
/** for client-side */
6467
shared(): IReadinessManager,

src/sdkClient/__tests__/sdkClientMethodCS.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ const storageMock = {
1414
})
1515
};
1616

17-
const partialSdkReadinessManagers: { sdkStatus: jest.Mock, readinessManager: { destroy: jest.Mock } }[] = [];
17+
const partialSdkReadinessManagers: { sdkStatus: jest.Mock, readinessManager: { init: jest.Mock, destroy: jest.Mock } }[] = [];
1818

1919
const sdkReadinessManagerMock = {
2020
sdkStatus: jest.fn(),
21-
readinessManager: { destroy: jest.fn() },
21+
readinessManager: { init: jest.fn(), destroy: jest.fn() },
2222
shared: jest.fn(() => {
2323
partialSdkReadinessManagers.push({
2424
sdkStatus: jest.fn(),
25-
readinessManager: { destroy: jest.fn() },
25+
readinessManager: { init: jest.fn(), destroy: jest.fn() },
2626
});
2727
return partialSdkReadinessManagers[partialSdkReadinessManagers.length - 1];
2828
})
@@ -46,7 +46,7 @@ const params = {
4646
signalListener: { stop: jest.fn() },
4747
settings: settingsWithKey,
4848
telemetryTracker: telemetryTrackerFactory(),
49-
clients: {}
49+
clients: {},
5050
};
5151

5252
const invalidAttributes = [

src/sdkClient/sdkClientMethodCS.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
7575
validKey
7676
);
7777

78-
sharedSyncManager && sharedSyncManager.start();
79-
8078
log.info(NEW_SHARED_CLIENT);
8179
} else {
8280
log.debug(RETRIEVE_CLIENT_EXISTING);

src/sdkClient/sdkClientMethodCSWithTT.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
8686
validTrafficType
8787
);
8888

89-
sharedSyncManager && sharedSyncManager.start();
90-
9189
log.info(NEW_SHARED_CLIENT);
9290
} else {
9391
log.debug(RETRIEVE_CLIENT_EXISTING);

src/sdkFactory/index.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,20 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
2323
const { settings, platform, storageFactory, splitApiFactory, extraProps,
2424
syncManagerFactory, SignalListener, impressionsObserverFactory,
2525
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
26-
filterAdapterFactory } = params;
26+
filterAdapterFactory, lazyInit } = params;
2727
const { log, sync: { impressionsMode } } = settings;
2828

2929
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
3030
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
3131

32-
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
33-
validateAndTrackApiKey(log, settings.core.authorizationKey);
32+
// initialization
33+
let hasInit = false;
34+
const initCallbacks: (() => void)[] = [];
35+
36+
function whenInit(cb: () => void) {
37+
if (hasInit) cb();
38+
else initCallbacks.push(cb);
39+
}
3440

3541
const sdkReadinessManager = sdkReadinessManagerFactory(platform.EventEmitter, settings);
3642
const readiness = sdkReadinessManager.readinessManager;
@@ -67,8 +73,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
6773
strategy = strategyDebugFactory(observer);
6874
}
6975

70-
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
71-
const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
76+
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, whenInit, integrationsManager, storage.telemetry);
77+
const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
7278

7379
// splitApi is used by SyncManager and Browser signal listener
7480
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
@@ -85,8 +91,21 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
8591
const clientMethod = sdkClientMethodFactory(ctx);
8692
const managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
8793

88-
syncManager && syncManager.start();
89-
signalListener && signalListener.start();
94+
95+
function init() {
96+
if (hasInit) return;
97+
hasInit = true;
98+
99+
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
100+
validateAndTrackApiKey(log, settings.core.authorizationKey);
101+
readiness.init();
102+
uniqueKeysTracker && uniqueKeysTracker.start();
103+
syncManager && syncManager.start();
104+
signalListener && signalListener.start();
105+
106+
initCallbacks.forEach((cb) => cb());
107+
initCallbacks.length = 0;
108+
}
90109

91110
log.info(NEW_FACTORY);
92111

@@ -107,7 +126,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
107126
settings,
108127

109128
destroy() {
110-
return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => {});
129+
return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
111130
}
112-
}, extraProps && extraProps(ctx));
131+
}, extraProps && extraProps(ctx), lazyInit ? { init } : init());
113132
}

0 commit comments

Comments
 (0)