Skip to content

Commit fd8b306

Browse files
Merge branch 'main' into custom-logger
2 parents 77a56de + 3c7dedc commit fd8b306

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1119
-737
lines changed

CHANGES.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
2.6.0 (September 18, 2025)
2+
- Added `storage.wrapper` configuration option to allow the SDK to use a custom storage wrapper for the storage type `LOCALSTORAGE`. Default value is `window.localStorage`.
3+
4+
2.5.0 (September 10, 2025)
5+
- Added `factory.getRolloutPlan()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
6+
- Added `initialRolloutPlan` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.
7+
18
2.4.1 (June 3, 2025)
29
- Bugfix - Improved the Proxy fallback to flag spec version 1.2 to handle cases where the Proxy does not return an end-of-stream marker in 400 status code responses.
310

@@ -47,6 +54,15 @@
4754
- Removed internal ponyfills for `Map` and `Set` global objects, dropping support for IE and other outdated browsers. The SDK now requires the runtime environment to support these features natively or to provide a polyfill.
4855
- Removed the `sync.localhostMode` configuration option to plug the LocalhostMode module.
4956

57+
1.17.1 (July 25, 2025)
58+
- Updated the Redis storage to avoid lazy require of the `ioredis` dependency when the SDK is initialized.
59+
- Updated some transitive dependencies for vulnerability fixes.
60+
- Bugfix - Enhanced HTTP client module to implement timeouts for failing requests that might otherwise remain pending indefinitely on some Fetch API implementations, pausing the SDK synchronization process.
61+
- Bugfix - Properly handle rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
62+
- Bugfix - Sanitize the `SplitSDKMachineName` header value to avoid exceptions on HTTP/S requests when it contains non ISO-8859-1 characters (Related to issue https://github.com/splitio/javascript-client/issues/847).
63+
- Bugfix - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
64+
- Bugfix - Fixed an issue with the server-side polling manager that caused dangling timers when the SDK was destroyed before it was ready.
65+
5066
1.17.0 (September 6, 2024)
5167
- Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks.
5268
- Added `isTimedout` and `lastUpdate` properties to IStatusInterface to keep track of the timestamp of the last SDK event, used on React and Redux SDKs.

README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@ To learn more about Split, contact [email protected], or get started with feature f
2424

2525
Split has built and maintains SDKs for:
2626

27-
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
28-
* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
29-
* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
30-
* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK)
31-
* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin)
32-
* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
33-
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
34-
* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
35-
* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
36-
* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
37-
* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
38-
* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
39-
* PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK)
40-
* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
41-
* React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK)
42-
* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK)
43-
* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK)
44-
* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
27+
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/net-sdk/)
28+
* Android [Github](https://github.com/splitio/android-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/android-sdk/)
29+
* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/angular-utilities/)
30+
* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/elixir-thin-client-sdk/)
31+
* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/flutter-plugin/)
32+
* GO [Github](https://github.com/splitio/go-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/go-sdk/)
33+
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/ios-sdk/)
34+
* Java [Github](https://github.com/splitio/java-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/java-sdk/)
35+
* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/)
36+
* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/browser-sdk/)
37+
* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/nodejs-sdk/)
38+
* PHP [Github](https://github.com/splitio/php-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/php-sdk/)
39+
* PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/php-thin-client-sdk/)
40+
* Python [Github](https://github.com/splitio/python-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/python-sdk/)
41+
* React [Github](https://github.com/splitio/react-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-sdk/)
42+
* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/)
43+
* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/redux-sdk/)
44+
* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/ruby-sdk/)
4545

4646
For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20).
4747

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": "2.4.1",
3+
"version": "2.6.0",
44
"description": "Split JavaScript SDK common components",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",

src/evaluator/convertions/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
import { IBetweenMatcherData } from '../../dtos/types';
2+
13
export function zeroSinceHH(millisSinceEpoch: number): number {
24
return new Date(millisSinceEpoch).setUTCHours(0, 0, 0, 0);
35
}
46

57
export function zeroSinceSS(millisSinceEpoch: number): number {
68
return new Date(millisSinceEpoch).setUTCSeconds(0, 0);
79
}
10+
11+
export function betweenDateTimeTransform(betweenMatcherData: IBetweenMatcherData): IBetweenMatcherData {
12+
return {
13+
dataType: betweenMatcherData.dataType,
14+
start: zeroSinceSS(betweenMatcherData.start),
15+
end: zeroSinceSS(betweenMatcherData.end)
16+
};
17+
}

src/evaluator/matchersTransform/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { matcherTypes, matcherTypesMapper, matcherDataTypes } from '../matchers/
33
import { segmentTransform } from './segment';
44
import { whitelistTransform } from './whitelist';
55
import { numericTransform } from './unaryNumeric';
6-
import { zeroSinceHH, zeroSinceSS } from '../convertions';
6+
import { zeroSinceHH, zeroSinceSS, betweenDateTimeTransform } from '../convertions';
77
import { IBetweenMatcherData, IInLargeSegmentMatcherData, IInSegmentMatcherData, ISplitMatcher, IUnaryNumericMatcherData } from '../../dtos/types';
88
import { IMatcherDto } from '../types';
99

@@ -32,7 +32,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
3232
let type = matcherTypesMapper(matcherType);
3333
// As default input data type we use string (even for ALL_KEYS)
3434
let dataType = matcherDataTypes.STRING;
35-
let value = undefined;
35+
let value;
3636

3737
if (type === matcherTypes.IN_SEGMENT) {
3838
value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
@@ -60,8 +60,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
6060
dataType = matcherDataTypes.NUMBER;
6161

6262
if (value.dataType === 'DATETIME') {
63-
value.start = zeroSinceSS(value.start);
64-
value.end = zeroSinceSS(value.end);
63+
value = betweenDateTimeTransform(value);
6564
dataType = matcherDataTypes.DATETIME;
6665
}
6766
} else if (type === matcherTypes.BETWEEN_SEMVER) {

src/logger/messages/info.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
1111
[c.IMPRESSION, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Feature flag: %s. Key: %s. Evaluation: %s. Label: %s'],
1212
[c.IMPRESSION_QUEUEING, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Queueing corresponding impression.'],
1313
[c.NEW_SHARED_CLIENT, 'New shared client instance created.'],
14-
[c.NEW_FACTORY, 'New Split SDK instance created.'],
14+
[c.NEW_FACTORY, 'New Split SDK instance created. %s'],
1515
[c.EVENTS_TRACKER_SUCCESS, c.LOG_PREFIX_EVENTS_TRACKER + 'Successfully queued %s'],
1616
[c.IMPRESSIONS_TRACKER_SUCCESS, c.LOG_PREFIX_IMPRESSIONS_TRACKER + 'Successfully stored %s impression(s).'],
1717
[c.USER_CONSENT_UPDATED, 'UserConsent: consent status changed from %s to %s.'],

src/sdkClient/sdkClientMethodCS.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, L
99
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
1010
import { ISdkFactoryContext } from '../sdkFactory/types';
1111
import { buildInstanceId } from './identity';
12+
import { setRolloutPlan } from '../storages/setRolloutPlan';
13+
import { ISegmentsCacheSync } from '../storages/types';
1214

1315
/**
1416
* Factory of client method for the client-side API variant where TT is ignored.
1517
* Therefore, clients don't have a bound TT for the track method.
1618
*/
1719
export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.IBrowserClient {
18-
const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
20+
const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log, initialRolloutPlan } } = params;
1921

2022
const mainClientInstance = clientCSDecorator(
2123
log,
@@ -56,6 +58,10 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
5658
sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
5759
});
5860

61+
if (sharedStorage && initialRolloutPlan) {
62+
setRolloutPlan(log, initialRolloutPlan, { segments: sharedStorage.segments as ISegmentsCacheSync, largeSegments: sharedStorage.largeSegments as ISegmentsCacheSync }, matchingKey);
63+
}
64+
5965
// 3 possibilities:
6066
// - Standalone mode: both syncManager and sharedSyncManager are defined
6167
// - Consumer mode: both syncManager and sharedSyncManager are undefined

src/sdkFactory/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized
1414
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
1515
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
1616
import { DEBUG, OPTIMIZED } from '../utils/constants';
17+
import { setRolloutPlan } from '../storages/setRolloutPlan';
18+
import { IStorageSync } from '../storages/types';
19+
import { getMatching } from '../utils/key';
1720

1821
/**
1922
* Modular SDK factory
@@ -24,7 +27,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
2427
syncManagerFactory, SignalListener, impressionsObserverFactory,
2528
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
2629
filterAdapterFactory, lazyInit } = params;
27-
const { log, sync: { impressionsMode } } = settings;
30+
const { log, sync: { impressionsMode }, initialRolloutPlan, core: { key } } = settings;
2831

2932
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
3033
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
@@ -43,7 +46,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
4346

4447
const storage = storageFactory({
4548
settings,
46-
onReadyCb: (error) => {
49+
onReadyCb(error) {
4750
if (error) {
4851
// If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
4952
readiness.timeout();
@@ -52,11 +55,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
5255
readiness.splits.emit(SDK_SPLITS_ARRIVED);
5356
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
5457
},
55-
onReadyFromCacheCb: () => {
58+
onReadyFromCacheCb() {
5659
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
5760
}
5861
});
59-
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
62+
63+
if (initialRolloutPlan) {
64+
setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
65+
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
66+
}
67+
6068
const clients: Record<string, SplitIO.IBasicClient> = {};
6169
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
6270
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
@@ -105,7 +113,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
105113
initCallbacks.length = 0;
106114
}
107115

108-
log.info(NEW_FACTORY);
116+
log.info(NEW_FACTORY, [settings.version]);
109117

110118
// @ts-ignore
111119
return objectAssign({

src/storages/__tests__/RBSegmentsCacheSync.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { IRBSegmentsCacheSync } from '../types';
66
import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks';
77

88
const cacheInMemory = new RBSegmentsCacheInMemory();
9-
const cacheInLocal = new RBSegmentsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'));
9+
// eslint-disable-next-line no-undef
10+
const cacheInLocal = new RBSegmentsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user'), localStorage);
1011

1112
describe.each([cacheInMemory, cacheInLocal])('Rule-based segments cache sync (Memory & LocalStorage)', (cache: IRBSegmentsCacheSync) => {
1213

0 commit comments

Comments
 (0)