Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7d77fac
Draft implementation of loadData and getSnapshot methods
EmilianoSanchez Oct 9, 2024
4168532
Merge branch 'breaking_changes_baseline' into data_loader_for_ssr
EmilianoSanchez Oct 18, 2024
248b1b1
Merge branch 'breaking_changes_baseline' into data_loader_for_ssr
EmilianoSanchez Oct 18, 2024
b8b12cd
Update data loader to support memberships
EmilianoSanchez Oct 18, 2024
325ecda
rc
EmilianoSanchez Oct 18, 2024
a6598a4
Merge branch 'development' into data_loader_for_ssr
EmilianoSanchez Aug 22, 2025
60ccbf8
Add RBSegments
EmilianoSanchez Aug 22, 2025
e618b7f
Polishing
EmilianoSanchez Aug 26, 2025
ed482ae
rc
EmilianoSanchez Aug 26, 2025
b70d121
Polishing
EmilianoSanchez Aug 26, 2025
f7dd0a1
Rename new methods
EmilianoSanchez Aug 26, 2025
b937db5
Remove outdated validation utils
EmilianoSanchez Aug 27, 2025
a95edb9
refactor type definitions
EmilianoSanchez Aug 27, 2025
5b84df3
refactor: restructure rollout plan data format and improve data loading
EmilianoSanchez Sep 2, 2025
c20e74f
refactor: do not mutate FF definitions when parsing matchers
EmilianoSanchez Sep 4, 2025
c65b3d0
refactor: call setRolloutPlan outside storage, to generalize to any s…
EmilianoSanchez Sep 4, 2025
9ddadd6
refactor: mode rollout plan validation
EmilianoSanchez Sep 5, 2025
590daa2
Separate getRolloutPlan and setRolloutPlan for bundle size reduction
EmilianoSanchez Sep 5, 2025
4ee373f
Polishing
EmilianoSanchez Sep 5, 2025
6f1ff41
Merge branch 'FME-9871-data-loader-utils' into data_loader_for_ssr
EmilianoSanchez Sep 5, 2025
705057d
rc
EmilianoSanchez Sep 5, 2025
09263b2
Stable version
EmilianoSanchez Sep 10, 2025
cf45f70
Fix type definition comment
EmilianoSanchez Sep 10, 2025
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
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2.5.0 (September 10, 2025)
- Added `factory.getRolloutPlan()` method for standalone server-side SDKs, which returns the rollout plan snapshot from the storage.
- Added `initialRolloutPlan` configuration option for standalone client-side SDKs, which allows preloading the SDK storage with a snapshot of the rollout plan.

2.4.1 (June 3, 2025)
- 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.

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio-commons",
"version": "2.4.1",
"version": "2.5.0",
"description": "Split JavaScript SDK common components",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down
8 changes: 7 additions & 1 deletion src/sdkClient/sdkClientMethodCS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, L
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
import { ISdkFactoryContext } from '../sdkFactory/types';
import { buildInstanceId } from './identity';
import { setRolloutPlan } from '../storages/setRolloutPlan';
import { ISegmentsCacheSync } from '../storages/types';

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

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

if (sharedStorage && initialRolloutPlan) {
setRolloutPlan(log, initialRolloutPlan, { segments: sharedStorage.segments as ISegmentsCacheSync, largeSegments: sharedStorage.largeSegments as ISegmentsCacheSync }, matchingKey);
}

// 3 possibilities:
// - Standalone mode: both syncManager and sharedSyncManager are defined
// - Consumer mode: both syncManager and sharedSyncManager are undefined
Expand Down
16 changes: 12 additions & 4 deletions src/sdkFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
import { DEBUG, OPTIMIZED } from '../utils/constants';
import { setRolloutPlan } from '../storages/setRolloutPlan';
import { IStorageSync } from '../storages/types';
import { getMatching } from '../utils/key';

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

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

const storage = storageFactory({
settings,
onReadyCb: (error) => {
onReadyCb(error) {
if (error) {
// If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
readiness.timeout();
Expand All @@ -52,11 +55,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
readiness.splits.emit(SDK_SPLITS_ARRIVED);
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
},
onReadyFromCacheCb: () => {
onReadyFromCacheCb() {
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
}
});
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`

if (initialRolloutPlan) {
setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
}

const clients: Record<string, SplitIO.IBasicClient> = {};
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
Expand Down
4 changes: 1 addition & 3 deletions src/storages/getRolloutPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import { IMembershipsResponse, IMySegmentsResponse } from '../dtos/types';

/**
* Gets the rollout plan snapshot from the given synchronous storage.
* If `keys` are provided, the memberships for those keys is returned, to protect segments data.
* Otherwise, the segments data is returned.
*/
export function getRolloutPlan(log: ILogger, storage: IStorageSync, options: SplitIO.RolloutPlanOptions = {}): RolloutPlan {

const { keys, exposeSegments } = options;
const { splits, segments, rbSegments } = storage;

log.debug(`storage: get feature flags${keys ? `, and memberships for keys ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);
log.debug(`storage: get feature flags${keys ? `, and memberships for keys: ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);

return {
splitChanges: {
Expand Down
4 changes: 2 additions & 2 deletions src/storages/inLocalStorage/validateCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const MILLIS_IN_A_DAY = 86400000;
* @returns `true` if cache should be cleared, `false` otherwise
*/
function validateExpiration(options: SplitIO.InLocalStorageOptions, settings: ISettings, keys: KeyBuilderCS, currentTimestamp: number, isThereCache: boolean) {
const { log } = settings;
const { log, initialRolloutPlan } = settings;

// Check expiration
const lastUpdatedTimestamp = parseInt(localStorage.getItem(keys.buildLastUpdatedKey()) as string, 10);
Expand All @@ -41,7 +41,7 @@ function validateExpiration(options: SplitIO.InLocalStorageOptions, settings: IS
} catch (e) {
log.error(LOG_PREFIX + e);
}
if (isThereCache) {
if (isThereCache && !initialRolloutPlan) {
log.info(LOG_PREFIX + 'SDK key, flags filter criteria, or flags spec version has changed. Cleaning up cache');
return true;
}
Expand Down
2 changes: 0 additions & 2 deletions src/storages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,6 @@ export interface IStorageAsync extends IStorageBase<

/** StorageFactory */

export type DataLoader = (storage: IStorageSync, matchingKey: string) => void

export interface IStorageFactoryParams {
settings: ISettings,
/**
Expand Down
4 changes: 4 additions & 0 deletions src/utils/settingsValidation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ISettingsValidationParams } from './types';
import { ISettings } from '../../types';
import { validateKey } from '../inputValidation/key';
import { ERROR_MIN_CONFIG_PARAM, LOG_PREFIX_CLIENT_INSTANTIATION } from '../../logger/constants';
import { validateRolloutPlan } from '../../storages/setRolloutPlan';

// Exported for telemetry
export const base = {
Expand Down Expand Up @@ -152,6 +153,9 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
// @ts-ignore, modify readonly prop
if (storage) withDefaults.storage = storage(withDefaults);

// @ts-ignore, modify readonly prop
if (withDefaults.initialRolloutPlan) withDefaults.initialRolloutPlan = validateRolloutPlan(log, withDefaults);

// Validate key and TT (for client-side)
const maybeKey = withDefaults.core.key;
if (validationParams.acceptKey) {
Expand Down
9 changes: 8 additions & 1 deletion types/splitio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1594,7 +1594,14 @@ declare namespace SplitIO {
/**
* Returns the current snapshot of the SDK rollout plan in cache.
*
* @param keys - Optional list of keys to generate the rollout plan snapshot with the memberships of the given keys, rather than the complete segments data.
* Wait for the SDK client to be ready before calling this method.
*
* ```js
* await factory.client().ready();
* const rolloutPlan = factory.getRolloutPlan();
* ```
*
* @param options - An object of type RolloutPlanOptions for advanced options.
* @returns The current snapshot of the SDK rollout plan.
*/
getRolloutPlan(options?: RolloutPlanOptions): RolloutPlan;
Expand Down