From 06a1e668d5246dd701df68ffbad08f09d0321aa8 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 29 Apr 2026 22:05:25 -0300 Subject: [PATCH 1/4] Add event-related settings --- types/splitio.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 7732ba96..23a7fac4 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -2321,6 +2321,19 @@ declare namespace SplitIO { * @defaultValue `60` */ configsRefreshRate?: number; + /** + * The SDK posts the queued events data in bulks. This parameter controls the posting rate in seconds. + * + * @defaultValue `60` + */ + eventsPushRate?: number; + /** + * The maximum number of event items we want to queue. If we queue more values, it will trigger a flush and reset the timer. + * If you use a 0 here, the queue will have no maximum size. + * + * @defaultValue `500` + */ + eventsQueueSize?: number; /** * Logging level. * From ca2e4f5faf83f4a48ee9962a4ac4c3e9f5772b77 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Sat, 2 May 2026 13:07:28 -0300 Subject: [PATCH 2/4] Rename settings to pollingRate, pushRate and queueSize --- types/splitio.d.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 23a7fac4..b2118dd0 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -2316,24 +2316,23 @@ declare namespace SplitIO { */ authorizationKey: string; /** - * Configs definitions refresh rate for polling, in seconds. + * Polling rate for configs and segments refresh, in seconds. Minimum value: 5. * * @defaultValue `60` */ - configsRefreshRate?: number; + pollingRate?: number; /** - * The SDK posts the queued events data in bulks. This parameter controls the posting rate in seconds. + * Push rate for events and impressions, in seconds. Minimum value: 60. * * @defaultValue `60` */ - eventsPushRate?: number; + pushRate?: number; /** - * The maximum number of event items we want to queue. If we queue more values, it will trigger a flush and reset the timer. - * If you use a 0 here, the queue will have no maximum size. + * Maximum queue size for events and impressions. When the queue reaches this size, a flush is triggered. Minimum value: 1000. * - * @defaultValue `500` + * @defaultValue `10000` */ - eventsQueueSize?: number; + queueSize?: number; /** * Logging level. * From 7742f9e4021a8e22f6b1bee5cf5e1d7248a32f87 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 6 May 2026 18:45:33 -0300 Subject: [PATCH 3/4] Update SdkUpdateMetadata documentation to clarify updated data types --- types/splitio.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/splitio.d.ts b/types/splitio.d.ts index b2118dd0..a515e317 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -495,7 +495,7 @@ declare namespace SplitIO { } /** - * Metadata for the update event emitted when the SDK cache is updated with new data for flags or segments. + * Metadata for the update event emitted when the SDK cache is updated with new data for flags, configs, or segments. */ type SdkUpdateMetadata = { /** @@ -503,7 +503,7 @@ declare namespace SplitIO { */ type: SdkUpdateMetadataType; /** - * The names of the flags or segments that were updated. + * The names of the flags or configs that were updated. Empty array if the update is of type 'SEGMENTS_UPDATE'. */ names: string[]; } From a145ff1e4bde146e392a8bb736dad6a77ab5a43a Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 7 May 2026 17:14:38 -0300 Subject: [PATCH 4/4] Typo fix --- .../streaming/SSEHandler/NotificationKeeper.ts | 4 ++-- .../streaming/SSEHandler/__tests__/index.spec.ts | 8 ++++---- src/sync/streaming/SSEHandler/index.ts | 4 ++-- src/sync/streaming/constants.ts | 4 ++-- src/sync/streaming/pushManager.ts | 14 +++++++------- src/sync/streaming/types.ts | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sync/streaming/SSEHandler/NotificationKeeper.ts b/src/sync/streaming/SSEHandler/NotificationKeeper.ts index 8b40ae6d..15b9af2f 100644 --- a/src/sync/streaming/SSEHandler/NotificationKeeper.ts +++ b/src/sync/streaming/SSEHandler/NotificationKeeper.ts @@ -1,7 +1,7 @@ import { ITelemetryTracker } from '../../../trackers/types'; import { CONNECTION_ESTABLISHED, DISABLED, ENABLED, OCCUPANCY_PRI, OCCUPANCY_SEC, PAUSED, STREAMING_STATUS } from '../../../utils/constants'; import { StreamingEventType } from '../../submitters/types'; -import { ControlType, PUSH_SUBSYSTEM_UP, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN } from '../constants'; +import { ControlType, PUSH_SUBSYSTEM_UP, PUSH_NON_RETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN } from '../constants'; import { IPushEventEmitter } from '../types'; const CONTROL_CHANNEL_REGEXS = [/control_pri$/, /control_sec$/]; @@ -84,7 +84,7 @@ export function notificationKeeperFactory(pushEmitter: IPushEventEmitter, teleme c.cTime = timestamp; if (controlType === ControlType.STREAMING_DISABLED) { telemetryTracker.streamingEvent(STREAMING_STATUS, DISABLED); - pushEmitter.emit(PUSH_NONRETRYABLE_ERROR); + pushEmitter.emit(PUSH_NON_RETRYABLE_ERROR); } else if (hasPublishers) { if (controlType === ControlType.STREAMING_PAUSED && hasResumed) { telemetryTracker.streamingEvent(STREAMING_STATUS, PAUSED); diff --git a/src/sync/streaming/SSEHandler/__tests__/index.spec.ts b/src/sync/streaming/SSEHandler/__tests__/index.spec.ts index 90bdc8cd..7df9fc99 100644 --- a/src/sync/streaming/SSEHandler/__tests__/index.spec.ts +++ b/src/sync/streaming/SSEHandler/__tests__/index.spec.ts @@ -1,6 +1,6 @@ // @ts-nocheck import { SSEHandlerFactory } from '..'; -import { PUSH_SUBSYSTEM_UP, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, PUSH_RETRYABLE_ERROR, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, ControlType } from '../../constants'; +import { PUSH_SUBSYSTEM_UP, PUSH_NON_RETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, PUSH_RETRYABLE_ERROR, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, ControlType } from '../../constants'; import { loggerMock } from '../../../../logger/__tests__/sdkLogger.mock'; // update messages @@ -123,7 +123,7 @@ test('`handlerMessage` for CONTROL notifications (NotificationKeeper)', () => { expect(pushEmitter.emit).toHaveBeenLastCalledWith(ControlType.STREAMING_RESET); // must handle streaming reset sseHandler.handleMessage(controlStreamingDisabledSec); // testing STREAMING_DISABLED with second region - expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NONRETRYABLE_ERROR); // must emit PUSH_NONRETRYABLE_ERROR if received a STREAMING_DISABLED control message + expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NON_RETRYABLE_ERROR); // must emit PUSH_NON_RETRYABLE_ERROR if received a STREAMING_DISABLED control message const sseHandler2 = SSEHandlerFactory(loggerMock, pushEmitter, telemetryTracker); sseHandler2.handleOpen(); @@ -132,7 +132,7 @@ test('`handlerMessage` for CONTROL notifications (NotificationKeeper)', () => { expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_SUBSYSTEM_DOWN); sseHandler2.handleMessage(controlStreamingDisabled); - expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NONRETRYABLE_ERROR); // must emit PUSH_NONRETRYABLE_ERROR if received a STREAMING_DISABLED control message, even if streaming is off + expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NON_RETRYABLE_ERROR); // must emit PUSH_NON_RETRYABLE_ERROR if received a STREAMING_DISABLED control message, even if streaming is off }); @@ -213,7 +213,7 @@ test('handleError', () => { const ably4XXNonRecoverableError = { data: '{"message":"Token expired","code":42910,"statusCode":429}' }; sseHandler.handleError(ably4XXNonRecoverableError); - expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NONRETRYABLE_ERROR); // An Ably non-recoverable error must emit PUSH_NONRETRYABLE_ERROR + expect(pushEmitter.emit).toHaveBeenLastCalledWith(PUSH_NON_RETRYABLE_ERROR); // An Ably non-recoverable error must emit PUSH_NON_RETRYABLE_ERROR expect(telemetryTracker.streamingEvent).toHaveBeenLastCalledWith(ABLY_ERROR, 42910); const ably5XXError = { data: '{"message":"...","code":50000,"statusCode":500}' }; diff --git a/src/sync/streaming/SSEHandler/index.ts b/src/sync/streaming/SSEHandler/index.ts index 4de5ed9d..22d462b8 100644 --- a/src/sync/streaming/SSEHandler/index.ts +++ b/src/sync/streaming/SSEHandler/index.ts @@ -1,6 +1,6 @@ import { errorParser, messageParser } from './NotificationParser'; import { notificationKeeperFactory } from './NotificationKeeper'; -import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants'; +import { PUSH_RETRYABLE_ERROR, PUSH_NON_RETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants'; import { IPushEventEmitter } from '../types'; import { ISseEventHandler } from '../SSEClient/types'; import { INotificationError, INotificationMessage } from './types'; @@ -56,7 +56,7 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter, if (isRetryableError(errorWithParsedData)) { pushEmitter.emit(PUSH_RETRYABLE_ERROR); } else { - pushEmitter.emit(PUSH_NONRETRYABLE_ERROR); + pushEmitter.emit(PUSH_NON_RETRYABLE_ERROR); } }, diff --git a/src/sync/streaming/constants.ts b/src/sync/streaming/constants.ts index dd230a61..ce7215bf 100644 --- a/src/sync/streaming/constants.ts +++ b/src/sync/streaming/constants.ts @@ -6,7 +6,7 @@ export const SECONDS_BEFORE_EXPIRATION = 600; * emitted on SSE and Authenticate non-recoverable errors, STREAMING_DISABLED control notification and authentication with pushEnabled false * triggers `handleNonRetryableError` call */ -export const PUSH_NONRETRYABLE_ERROR = 'PUSH_NONRETRYABLE_ERROR'; +export const PUSH_NON_RETRYABLE_ERROR = 'PUSH_NON_RETRYABLE_ERROR'; /** * emitted on SSE and Authenticate recoverable errors * triggers `handleRetryableError` call @@ -19,7 +19,7 @@ export const PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'; export const PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP'; /** - * emitted on STREAMING_PAUSED control notification, OCCUPANCY equal to 0, PUSH_NONRETRYABLE_ERROR and PUSH_RETRYABLE_ERROR events. + * emitted on STREAMING_PAUSED control notification, OCCUPANCY equal to 0, PUSH_NON_RETRYABLE_ERROR and PUSH_RETRYABLE_ERROR events. * triggers `startPolling` and `stopWorkers` calls */ export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN'; diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index 535e8f1c..d5c9f140 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -11,7 +11,7 @@ import { authenticateFactory, hashUserKey } from './AuthClient'; import { forOwn } from '../../utils/lang'; import { SSEClient } from './SSEClient'; import { checkIfServerSide, getMatching } from '../../utils/key'; -import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants'; +import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NON_RETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants'; import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants'; import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types'; import { getDelay, isInBitmap, parseBitmap, parseCompressedData } from './parseUtils'; @@ -67,10 +67,10 @@ export function pushManagerFactory( // [Only for client-side] variable to flag that a new client was added. It is needed to reconnect streaming. let connectForNewClient = false; - // flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NONRETRYABLE_ERROR error. + // flag that indicates if `stop/disconnectPush` was called, either by the SyncManager, when the client is destroyed, or due to a PUSH_NON_RETRYABLE_ERROR error. // It is used to halt the `connectPush` process if it was in progress. let disconnected: boolean | undefined; - // flag that indicates a PUSH_NONRETRYABLE_ERROR, condition with which starting pushManager again is ignored. + // flag that indicates a PUSH_NON_RETRYABLE_ERROR, condition with which starting pushManager again is ignored. // true if STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE or Auth errors. let disabled: boolean | undefined; // `disabled` implies `disconnected === true` @@ -117,11 +117,11 @@ export function pushManagerFactory( function (authData) { if (disconnected) return; - // 'pushEnabled: false' is handled as a PUSH_NONRETRYABLE_ERROR instead of PUSH_SUBSYSTEM_DOWN, in order to + // 'pushEnabled: false' is handled as a PUSH_NON_RETRYABLE_ERROR instead of PUSH_SUBSYSTEM_DOWN, in order to // close the sseClient in case the org has been bloqued while the instance was connected to streaming if (!authData.pushEnabled) { log.info(STREAMING_DISABLED); - pushEmitter.emit(PUSH_NONRETRYABLE_ERROR); + pushEmitter.emit(PUSH_NON_RETRYABLE_ERROR); return; } @@ -140,7 +140,7 @@ export function pushManagerFactory( // Handle 4XX HTTP errors: 401 (invalid SDK Key) or 400 (using incorrect SDK Key, i.e., client-side SDK Key on server-side) if (error.statusCode >= 400 && error.statusCode < 500) { telemetryTracker.streamingEvent(AUTH_REJECTION); - pushEmitter.emit(PUSH_NONRETRYABLE_ERROR); + pushEmitter.emit(PUSH_NON_RETRYABLE_ERROR); return; } @@ -183,7 +183,7 @@ export function pushManagerFactory( /** Fallback to polling without retry due to: STREAMING_DISABLED control event, or 'pushEnabled: false', or non-recoverable SSE and Authentication errors */ - pushEmitter.on(PUSH_NONRETRYABLE_ERROR, function handleNonRetryableError() { + pushEmitter.on(PUSH_NON_RETRYABLE_ERROR, function handleNonRetryableError() { disabled = true; // Note: `stopWorkers` is been called twice, but it is not harmful disconnectPush(); diff --git a/src/sync/streaming/types.ts b/src/sync/streaming/types.ts index fcf5048e..00e3fb67 100644 --- a/src/sync/streaming/types.ts +++ b/src/sync/streaming/types.ts @@ -7,7 +7,7 @@ import { ControlType } from './constants'; // Internal SDK events, subscribed by SyncManager and PushManager export type PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP' export type PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN' -export type PUSH_NONRETRYABLE_ERROR = 'PUSH_NONRETRYABLE_ERROR' +export type PUSH_NON_RETRYABLE_ERROR = 'PUSH_NON_RETRYABLE_ERROR' export type PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR' // Update-type push notifications, handled by NotificationProcessor @@ -22,7 +22,7 @@ export type RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE'; export type CONTROL = 'CONTROL'; export type OCCUPANCY = 'OCCUPANCY'; -export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | RB_SEGMENT_UPDATE | ControlType.STREAMING_RESET +export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NON_RETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | RB_SEGMENT_UPDATE | ControlType.STREAMING_RESET type IParsedData = T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :