Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
13 changes: 10 additions & 3 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
0.7.1 (May XXX, 2025)
- Updated some transitive dependencies for vulnerability fixes.
1.0.0 (May 28, 2025)
- Added support for synchronizing feature flags with rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK.
- Added support for synchronizing feature flags with prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.
- Added support for synchronizing SDK impressions with properties.
- Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance Synchronizer HTTP request Headers for Authorization Frameworks.
- Updated @splitsoftware/splitio-commons package to version 2.3.0 and some transitive dependencies for vulnerability fixes and other improvements.
- BREAKING CHANGES:
- Dropped support for Node.js v8. The SDK now requires Node.js v14 or above.
- Removed internal ponyfills for the `Map` and `Set` global objects. The SDK now requires the runtime environment to support these features natively or provide a polyfill.

0.7.0 (August 5, 2024)
- Added `sync.requestOptions.agent` option to allow passing a custom Node.js HTTP(S) Agent with specific configurations for the Synchronizer requests, like custom TLS settings or a network proxy (See https://help.split.io/hc/en-us/articles/4421513571469-Split-JavaScript-synchronizer-tools#proxy).
- Updated some transitive dependencies for vulnerability fixes.
- Updated @splitsoftware/splitio-commons package to version 1.16.0 and some transitive dependencies for vulnerability fixes.

0.6.0 (May 13, 2024)
- Added a new configuration option `sync.flagSpecVersion` to specify the flags spec version of feature flag definitions to be fetched and stored.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This package includes a set of JavaScript synchronization tools built based on t
[![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware)

## Compatibility
Split sync tools supports Node.js version 8 or higher. To run the tools in other JavaScript environments, the target environment must support ES6 (ECMAScript 2015) syntax, and provide built-in support or a global polyfill for Promises and Web Fetch API.
Split sync tools supports Node.js version 14 or higher. To run the tools in other JavaScript environments, the target environment must support ES6 (ECMAScript 2015) syntax, and provide built-in support or a global polyfill for Promises, Web Fetch API, Map and Set.

## Getting started
Below is a simple example that describes the execution of the JavaScript Synchronizer:
Expand Down
26 changes: 17 additions & 9 deletions e2e/synchronizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('Synchronizer e2e tests', () => {

describe('Runs Synchronizer for the [FIRST] time, and', () => {
beforeAll(async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/ENDIOS_PEREZ?since=-1', { status: 200, body: responseMocks.segmentChanges[1] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=-1', { status: 200, body: responseMocks.segmentChanges[2] });
Expand Down Expand Up @@ -116,6 +116,14 @@ describe('Synchronizer e2e tests', () => {
expect(itemsSetB.sort()).toEqual(['TEST_DOC', 'TEST_MATIAS']);
expect(itemsInexistentSet).toEqual([]);
});

test('saves 1 rule-based segment', async () => {
const ruleBasedSegments = await _redisWrapper.getKeysByPrefix(`${REDIS_PREFIX}.rbsegment.*`);
expect(ruleBasedSegments).toHaveLength(1);

expect(await _redisWrapper.get(`${REDIS_PREFIX}.rbsegments.till`)).toBe('100');
});

});

describe('Runs SDK Consumer with DEBUG impressions mode, and', () => {
Expand Down Expand Up @@ -144,7 +152,7 @@ describe('Synchronizer e2e tests', () => {

describe('Runs Synchronizer a [SECOND] time and', () => {
beforeAll(async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346271', { status: 200, body: responseMocks.splitChanges[2] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=100', { status: 200, body: responseMocks.splitChanges[2] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] });

Expand Down Expand Up @@ -224,7 +232,7 @@ describe('Synchronizer e2e tests', () => {
});

test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272', { status: 200, body: responseMocks.splitChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100', { status: 200, body: responseMocks.splitChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] });

Expand Down Expand Up @@ -272,7 +280,7 @@ describe('Synchronizer e2e tests', () => {
});

test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272', { status: 200, body: responseMocks.splitChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100', { status: 200, body: responseMocks.splitChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] });

Expand Down Expand Up @@ -347,7 +355,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter

describe('Synchronizer runs the first time', () => {
beforeAll(async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });

Expand Down Expand Up @@ -402,7 +410,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter

describe('Synchronizer runs a second time, and', () => {
beforeAll(async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346271&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=100&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });

await _synchronizer.execute();
Expand Down Expand Up @@ -460,7 +468,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter
},
});

fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272&sets=set_b', { status: 500 });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100&sets=set_b', { status: 500 });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });

expect(await synchronizer.execute()).toBe(false);
Expand All @@ -477,7 +485,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter
},
});

fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1&sets=set_b', { status: 500 });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_b', { status: 500 });

expect(await synchronizer.execute()).toBe(false);
expect(keys.length).toBeGreaterThan(0);
Expand All @@ -491,7 +499,7 @@ describe('Synchronizer - only Splits & Segments mode', () => {
let executeImpressionsAndEventsCallSpy: jest.SpyInstance;

beforeAll(async () => {
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: responseMocks.splitChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=-1', { status: 200, body: responseMocks.segmentChanges[2] });
fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] });
Expand Down
3 changes: 1 addition & 2 deletions e2e/utils/SDKConsumerMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ const config = {
* Function to run an example SDK in Consumer mode, in order to generate Events and Impressions
* to be then processed by the Synchronizer.
*
* @param {ImpressionsMode} impressionsMode Impressions mode.
* @returns {Promise}
* @param impressionsMode - Impressions mode.
*/
export default function runSDKConsumer(impressionsMode: ImpressionsMode) {
const factory = SplitFactory({
Expand Down
4 changes: 2 additions & 2 deletions e2e/utils/redisAdapterWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { noopLogger } from '../../src/submitters/__tests__/commonUtils';
* Creates a storage wrapper that uses our RedisAdapter.
* Operations fail until `connect` is resolved once the Redis 'ready' event is emitted.
*
* @param {Object} redisOptions Redis options with the format expected at `settings.storage.options`.
* @returns {IPluggableStorageWrapper} Storage wrapper instance.
* @param redisOptions - Redis options with the format expected at `settings.storage.options`.
* @returns Storage wrapper instance.
*/
export default function redisAdapterWrapper(redisOptions: Record<string, any>): IPluggableStorageWrapper {

Expand Down
Loading