diff --git a/CHANGES.txt b/CHANGES.txt index 2f353229..c98101c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +3.1.0 (July 23, 2026) + - Added support for rule-based segments in /api/v1/configs endpoint. + - Updated polling flow to fetch new referenced segments immediately. + 3.0.0 (June 26, 2026) - Extracted SDK lifecycle methods (`init`, `flush`, and `destroy`) into a reusable `sdkLifecycle` module. - Extracted the `track` method into a standalone `trackMethod` factory for reuse across SDKs. diff --git a/package-lock.json b/package-lock.json index 0c2b5034..0e1b92eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-commons", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-commons", - "version": "3.0.0", + "version": "3.1.0", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -585,9 +585,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -1747,9 +1747,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -2217,9 +2217,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3401,9 +3401,9 @@ } }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -6361,10 +6361,11 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" diff --git a/package.json b/package.json index 9c031dfe..59afccdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "3.0.0", + "version": "3.1.0", "description": "Split JavaScript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/dtos/types.ts b/src/dtos/types.ts index 96236169..89951783 100644 --- a/src/dtos/types.ts +++ b/src/dtos/types.ts @@ -192,7 +192,7 @@ export interface IDefinitionCondition { matchers: IDefinitionMatcher[] } partitions?: IDefinitionPartition[] - label?: string + label?: string | null conditionType?: 'ROLLOUT' | 'WHITELIST' } diff --git a/src/evaluator/condition/index.ts b/src/evaluator/condition/index.ts index 996a38f1..d5b6a96c 100644 --- a/src/evaluator/condition/index.ts +++ b/src/evaluator/condition/index.ts @@ -7,12 +7,12 @@ import SplitIO from '../../../types/splitio'; import { ILogger } from '../../logger/types'; // Build Evaluation object if and only if matchingResult is true -function match(log: ILogger, matchingResult: boolean, bucketingKey: string | undefined, seed?: number, treatments?: { getTreatmentFor: (x: number) => string }, label?: string): IEvaluation | boolean | undefined { +function match(log: ILogger, matchingResult: boolean, bucketingKey: string | undefined, seed?: number, treatments?: { getTreatmentFor: (x: number) => string }, label?: string | null): IEvaluation | boolean | undefined { if (matchingResult) { return treatments ? // Feature flag { treatment: getTreatment(log, bucketingKey as string, seed, treatments), - label: label! + label: label ?? '' } : // Rule-based segment true; } @@ -22,7 +22,7 @@ function match(log: ILogger, matchingResult: boolean, bucketingKey: string | und } // Condition factory -export function conditionContext(log: ILogger, matcherEvaluator: (key: SplitIO.SplitKeyObject, attributes?: SplitIO.Attributes, splitEvaluator?: IDefinitionEvaluator) => MaybeThenable, treatments?: { getTreatmentFor: (x: number) => string }, label?: string, conditionType?: 'ROLLOUT' | 'WHITELIST'): IEvaluator { +export function conditionContext(log: ILogger, matcherEvaluator: (key: SplitIO.SplitKeyObject, attributes?: SplitIO.Attributes, splitEvaluator?: IDefinitionEvaluator) => MaybeThenable, treatments?: { getTreatmentFor: (x: number) => string }, label?: string | null, conditionType?: 'ROLLOUT' | 'WHITELIST'): IEvaluator { return function conditionEvaluator(key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: IDefinitionEvaluator) { diff --git a/src/sdkClient/sdkLifecycle.ts b/src/sdkClient/sdkLifecycle.ts index fa0cb602..6e4d1b95 100644 --- a/src/sdkClient/sdkLifecycle.ts +++ b/src/sdkClient/sdkLifecycle.ts @@ -1,5 +1,6 @@ import { releaseApiKey, validateAndTrackApiKey } from '../utils/inputValidation/apiKey'; import { ISdkFactoryContext } from '../sdkFactory/types'; +import { LOCALHOST_MODE } from '../utils/constants'; const COOLDOWN_TIME_IN_MILLIS = 1000; @@ -12,7 +13,7 @@ export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?: let hasInit = false; let lastActionTime = 0; - const signalListener = platform.SignalListener && new platform.SignalListener(params); + const signalListener = platform.SignalListener && settings.mode !== LOCALHOST_MODE ? new platform.SignalListener(params) : undefined; function __cooldown(func: Function, time: number) { const now = Date.now(); diff --git a/src/sdkFactory/types.ts b/src/sdkFactory/types.ts index 1dd5c7f7..14bffc56 100644 --- a/src/sdkFactory/types.ts +++ b/src/sdkFactory/types.ts @@ -105,11 +105,7 @@ export interface ISdkFactoryParams { // Sdk client method factory. // It Allows to distinguish SDK clients with the client-side API (`IBrowserSDK` and `IBrowserAsyncSDK`) or server-side API (`ISDK` and `IAsyncSDK`). - sdkClientMethodFactory: (params: ISdkFactoryContext) => ( - { (): SplitIO.IBrowserClient & { init(): void }; (key: SplitIO.SplitKey): SplitIO.IBrowserClient & { init(): void }; } | - (() => SplitIO.IClient & { init(): void }) | - (() => SplitIO.IAsyncClient & { init(): void }) - ) + sdkClientMethodFactory: (params: ISdkFactoryContext) => ({ (): SplitIO.IBrowserClient; (key: SplitIO.SplitKey): SplitIO.IBrowserClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient)) // Impression observer factory. impressionsObserverFactory: () => IImpressionObserver diff --git a/src/services/__tests__/splitApi.spec.ts b/src/services/__tests__/splitApi.spec.ts index a5aacd45..a77c1708 100644 --- a/src/services/__tests__/splitApi.spec.ts +++ b/src/services/__tests__/splitApi.spec.ts @@ -45,10 +45,10 @@ describe('serviceApi', () => { assertHeaders(settings, headers); expect(url).toBe(expectedFlagsUrl(-1, 100, settings.validateFilters || false, settings, -1)); - serviceApi.fetchConfigs(-1, false, 100); + serviceApi.fetchConfigs(-1, false, 100, -1); [url, { headers }] = fetchMock.mock.calls[4]; assertHeaders(settings, headers); - expect(url).toBe(expectedConfigsUrl(-1, 100, settings.validateFilters || false, settings)); + expect(url).toBe(expectedConfigsUrl(-1, 100, settings.validateFilters || false, settings, -1)); serviceApi.fetchConfigsSegmentChanges(-1, 'segmentName', false, 100); [url, { headers }] = fetchMock.mock.calls[5]; @@ -81,9 +81,9 @@ describe('serviceApi', () => { return `sdk/splitChanges?s=1.1&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${usesFilter ? filterQueryString : ''}${till ? '&till=' + till : ''}`; } - function expectedConfigsUrl(since: number, till: number, usesFilter: boolean, settings: ISettings) { + function expectedConfigsUrl(since: number, till: number, usesFilter: boolean, settings: ISettings, rbSince?: number) { const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString; - return `configs/v1/configs?since=${since}${usesFilter ? filterQueryString : ''}${till ? '&till=' + till : ''}`; + return `configs/v1/configs?since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${usesFilter ? filterQueryString : ''}${till ? '&till=' + till : ''}`; } }); diff --git a/src/services/serviceApi.ts b/src/services/serviceApi.ts index 518fbaab..207b3b08 100644 --- a/src/services/serviceApi.ts +++ b/src/services/serviceApi.ts @@ -70,8 +70,8 @@ export function serviceApiFactory( }, // @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error - fetchConfigs(since: number, noCache?: boolean, till?: number) { - const url = `${urls.configs}/api/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`; + fetchConfigs(since: number, noCache?: boolean, till?: number, rbSince?: number) { + const url = `${urls.configs}/api/v1/configs?since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`; return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined); }, diff --git a/src/services/splitApi.ts b/src/services/splitApi.ts index 935ed8d8..ba7ff38c 100644 --- a/src/services/splitApi.ts +++ b/src/services/splitApi.ts @@ -149,8 +149,8 @@ export function splitApiFactory( // Not used. Just here to satisfy the IServiceApi interface. - fetchConfigs(since: number, noCache?: boolean, till?: number) { - const url = `${urls.configs}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`; + fetchConfigs(since: number, noCache?: boolean, till?: number, rbSince?: number) { + const url = `${urls.configs}/v1/configs?since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`; return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined); }, diff --git a/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts b/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts index 0a1cc258..94ad2e55 100644 --- a/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +++ b/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts @@ -8,12 +8,10 @@ import { StorageAdapter } from '../types'; export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync { private readonly keys: MySegmentsKeyBuilder; - private readonly log: ILogger; private readonly storage: StorageAdapter; - constructor(log: ILogger, keys: MySegmentsKeyBuilder, storage: StorageAdapter) { + constructor(_log: ILogger, keys: MySegmentsKeyBuilder, storage: StorageAdapter) { super(); - this.log = log; this.keys = keys; this.storage = storage; } diff --git a/src/sync/polling/pollingManagerCS.ts b/src/sync/polling/pollingManagerCS.ts index c6e6d33a..0c9c434a 100644 --- a/src/sync/polling/pollingManagerCS.ts +++ b/src/sync/polling/pollingManagerCS.ts @@ -24,7 +24,7 @@ export function pollingManagerCSFactory( const { serviceApi, storage, readiness, settings } = params; const log = settings.log; - const definitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings, true); + const definitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings); // Map of matching keys to their corresponding MySegmentsSyncTask. const mySegmentsSyncTasks: Record = {}; diff --git a/src/sync/polling/pollingManagerSS.ts b/src/sync/polling/pollingManagerSS.ts index 712f6858..1117c7ab 100644 --- a/src/sync/polling/pollingManagerSS.ts +++ b/src/sync/polling/pollingManagerSS.ts @@ -17,8 +17,8 @@ export function pollingManagerSSFactory( const { storage, readiness, settings } = params; const log = settings.log; - const definitionsSyncTask: IDefinitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings); const segmentsSyncTask: ISegmentsSyncTask = segmentsSyncTaskFactory(segmentChangesFetcher, storage, readiness, settings); + const definitionsSyncTask: IDefinitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings, segmentsSyncTask); return { definitionsSyncTask, diff --git a/src/sync/polling/syncTasks/definitionsSyncTask.ts b/src/sync/polling/syncTasks/definitionsSyncTask.ts index aac2a112..6659cb3d 100644 --- a/src/sync/polling/syncTasks/definitionsSyncTask.ts +++ b/src/sync/polling/syncTasks/definitionsSyncTask.ts @@ -1,7 +1,7 @@ import { IStorageSync } from '../../../storages/types'; import { IReadinessManager } from '../../../readiness/types'; import { syncTaskFactory } from '../../syncTask'; -import { IDefinitionsSyncTask } from '../types'; +import { IDefinitionsSyncTask, ISegmentsSyncTask } from '../types'; import { ISettings } from '../../../types'; import { definitionChangesUpdaterFactory } from '../updaters/definitionChangesUpdater'; import { IDefinitionChangesFetcher } from '../fetchers/types'; @@ -14,7 +14,7 @@ export function definitionsSyncTaskFactory( storage: IStorageSync, readiness: IReadinessManager, settings: ISettings, - isClientSide?: boolean + segmentsSyncTask?: ISegmentsSyncTask // undefined for client-side ): IDefinitionsSyncTask { return syncTaskFactory( settings.log, @@ -26,7 +26,7 @@ export function definitionsSyncTaskFactory( readiness.definitions, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, - isClientSide + segmentsSyncTask ), settings.scheduler.featuresRefreshRate, 'definitionChangesUpdater', diff --git a/src/sync/polling/updaters/__tests__/definitionChangesUpdater.spec.ts b/src/sync/polling/updaters/__tests__/definitionChangesUpdater.spec.ts index 50e61d8c..7d412c66 100644 --- a/src/sync/polling/updaters/__tests__/definitionChangesUpdater.spec.ts +++ b/src/sync/polling/updaters/__tests__/definitionChangesUpdater.spec.ts @@ -290,7 +290,7 @@ describe('definitionChangesUpdater', () => { { sets: ['set_a'], shouldEmit: true }, /* should emit if flag is back in configured sets */ ]; - definitionChangesUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1, true); + definitionChangesUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1); let index = 0; let calls = 0; @@ -305,7 +305,7 @@ describe('definitionChangesUpdater', () => { // @ts-ignore splitFiltersValidation = { queryString: null, groupedFilters: { bySet: ['set_a'], byName: [], byPrefix: [] }, validFilters: [] }; storage.definitions.clear(); - definitionChangesUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1, true); + definitionChangesUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1); splitsEmitSpy.mockReset(); index = 0; for (const setMock of setMocks) { @@ -424,8 +424,8 @@ describe('definitionChangesUpdater', () => { readinessManager.definitions.definitionsArrived = false; readinessManager.segments.segmentsArrived = false; // Segments not ready - client-side should still emit - // Create client-side updater (isClientSide = true) - const clientSideUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1, true); + // Create client-side updater (segmentsSyncTask = undefined) + const clientSideUpdater = definitionChangesUpdaterFactory(loggerMock, splitChangesFetcher, storage, splitFiltersValidation, readinessManager.definitions, 1000, 1); const flag1 = { name: 'client-flag', status: 'ACTIVE', changeNumber: 300, conditions: [] } as unknown as IDefinition; fetchMock.once('*', { status: 200, body: { ff: { d: [flag1], t: 300 } } }); diff --git a/src/sync/polling/updaters/definitionChangesUpdater.ts b/src/sync/polling/updaters/definitionChangesUpdater.ts index bf23656c..45852e74 100644 --- a/src/sync/polling/updaters/definitionChangesUpdater.ts +++ b/src/sync/polling/updaters/definitionChangesUpdater.ts @@ -1,4 +1,4 @@ -import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types'; +import { IStorageBase } from '../../../storages/types'; import { IDefinitionChangesFetcher } from '../fetchers/types'; import { IRBSegment, IDefinition, IDefinitionChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types'; import { IDefinitionsEventEmitter } from '../../../readiness/types'; @@ -12,21 +12,11 @@ import { setToArray } from '../../../utils/lang/sets'; import { SPLIT_UPDATE } from '../../streaming/constants'; import { SdkUpdateMetadata } from '../../../../types/splitio'; import { ISplit } from '../fetchers/splitChangesFetcher'; +import { ISegmentsSyncTask } from '../types'; export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string }; type DefinitionChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise -// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment). -// Returns a promise that could be rejected. -// @TODO review together with Segments and MySegments storage APIs -function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise { - let registeredSegments = Promise.resolve(segments.getRegisteredSegments()); - return registeredSegments.then(segmentNames => { - return Promise.all(segmentNames.map(segmentName => segments.getChangeNumber(segmentName))) - .then(changeNumbers => changeNumbers.every(changeNumber => changeNumber !== undefined)); - }); -} - /** * Collect segments from a raw FF or RBS definition. * Exported for testing purposes. @@ -133,7 +123,7 @@ export function definitionChangesUpdaterFactory( definitionsEventEmitter?: IDefinitionsEventEmitter, requestTimeoutBeforeReady = 0, retriesOnFailureBeforeReady = 0, - isClientSide?: boolean + segmentsSyncTask?: ISegmentsSyncTask // undefined for client-side ): DefinitionChangesUpdater { const { definitions, rbSegments, segments } = storage; @@ -202,8 +192,8 @@ export function definitionChangesUpdaterFactory( startingUp = false; if (definitionsEventEmitter) { - // To emit SDK_DEFINITIONS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched - return Promise.resolve(!definitionsEventEmitter.definitionsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments)))) + // To emit SDK_DEFINITIONS_ARRIVED for server-side SDK, we must wait for all registered segments to be fetched + return Promise.resolve(!definitionsEventEmitter.definitionsArrived || ((ffChanged || rbsChanged) && (!segmentsSyncTask || segmentsSyncTask.execute(true)))) .catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */) .then(emitSplitsArrivedEvent => { // emit SDK events diff --git a/src/sync/polling/updaters/segmentChangesUpdater.ts b/src/sync/polling/updaters/segmentChangesUpdater.ts index e4c32342..ca3af541 100644 --- a/src/sync/polling/updaters/segmentChangesUpdater.ts +++ b/src/sync/polling/updaters/segmentChangesUpdater.ts @@ -76,7 +76,7 @@ export function segmentChangesUpdaterFactory( log.debug(`${LOG_PREFIX_SYNC}Started segments update`); // If not a segment name provided, read list of available segments names to be updated. - let segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments()); + const segmentsPromise = Promise.resolve(segmentName ? [segmentName] : segments.getRegisteredSegments()); return segmentsPromise.then(segmentNames => { const updaters = segmentNames.map(segmentName => updateSegment(segmentName, noCache, till, fetchOnlyNew, readyOnAlreadyExistentState ? retriesOnFailureBeforeReady : 0)); @@ -85,7 +85,7 @@ export function segmentChangesUpdaterFactory( // if at least one segment fetch succeeded, mark segments ready if (shouldUpdateFlags.some(update => update) || readyOnAlreadyExistentState) { readyOnAlreadyExistentState = false; - if (readiness) { + if (readiness && !fetchOnlyNew) { const metadata: SdkUpdateMetadata = { type: SEGMENTS_UPDATE, names: [] diff --git a/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts index 5a573689..f5ba913d 100644 --- a/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts @@ -7,7 +7,7 @@ import { IRBSegmentsCacheSync, IDefinitionsCacheSync, IStorageSync } from '../.. import { ITelemetryTracker } from '../../../trackers/types'; import { Backoff } from '../../../utils/Backoff'; import { SPLITS } from '../../../utils/constants'; -import { ISegmentsSyncTask, IDefinitionsSyncTask } from '../../polling/types'; +import { IDefinitionsSyncTask } from '../../polling/types'; import { InstantUpdate } from '../../polling/updaters/definitionChangesUpdater'; import { RB_SEGMENT_UPDATE } from '../constants'; import { parseFFUpdatePayload } from '../parseUtils'; @@ -18,7 +18,7 @@ import { IUpdateWorker } from './types'; /** * DefinitionsUpdateWorker factory */ -export function DefinitionsUpdateWorker(log: ILogger, storage: IStorageSync, definitionsSyncTask: IDefinitionsSyncTask, definitionsEventEmitter: IDefinitionsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData]> & { killDefinition(event: ISplitKillData): void } { +export function DefinitionsUpdateWorker(log: ILogger, storage: IStorageSync, definitionsSyncTask: IDefinitionsSyncTask, definitionsEventEmitter: IDefinitionsEventEmitter, telemetryTracker: ITelemetryTracker): IUpdateWorker<[updateData: ISplitUpdateData]> & { killDefinition(event: ISplitKillData): void } { const ff = DefinitionsUpdateWorker(storage.definitions); const rbs = DefinitionsUpdateWorker(storage.rbSegments); @@ -42,8 +42,6 @@ export function DefinitionsUpdateWorker(log: ILogger, storage: IStorageSync, def __handleDefinitionUpdateCall(); } else { if (instantUpdate) telemetryTracker.trackUpdatesFromSSE(SPLITS); - // fetch new registered segments for server-side API. Not retrying on error - if (segmentsSyncTask) segmentsSyncTask.execute(true); const attempts = backoff.attempts + 1; diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index 1fbcec36..1822ffb8 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -56,7 +56,7 @@ export function pushManagerFactory( // MySegmentsUpdateWorker (client-side) are initiated in `add` method const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments); // For server-side we pass the segmentsSyncTask, used by DefinitionsUpdateWorker to fetch new segments - const definitionsUpdateWorker = DefinitionsUpdateWorker(log, storage, pollingManager.definitionsSyncTask, readiness.definitions, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask); + const definitionsUpdateWorker = DefinitionsUpdateWorker(log, storage, pollingManager.definitionsSyncTask, readiness.definitions, telemetryTracker); // [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker const userKeyHashes: Record = {}; diff --git a/src/utils/EventEmitter.d.ts b/src/utils/EventEmitter.d.ts new file mode 100644 index 00000000..754737bd --- /dev/null +++ b/src/utils/EventEmitter.d.ts @@ -0,0 +1,3 @@ +import SplitIO from '../../types/splitio'; + +export declare const EventEmitter: new () => SplitIO.IEventEmitter;