From 981a1c0c92c9e616a4390615ea5caa453d72a788 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 4 Nov 2021 17:48:03 -0300 Subject: [PATCH 01/34] implementation --- README.md | 2 +- src/sdkClient/sdkClientMethodCS.ts | 12 +++- src/sdkClient/sdkClientMethodCSWithTT.ts | 14 +++-- src/sdkFactory/index.ts | 13 ++--- .../inLocalStorage/__tests__/index.spec.ts | 2 +- src/storages/inLocalStorage/index.ts | 16 ++++-- src/storages/inMemory/InMemoryStorage.ts | 2 +- src/storages/inMemory/InMemoryStorageCS.ts | 6 +- src/storages/inRedis/index.ts | 2 +- .../pluggable/__tests__/index.spec.ts | 2 +- src/storages/pluggable/index.ts | 10 +++- src/storages/types.ts | 40 ++++++-------- src/sync/submitters/submitterManager.ts | 22 ++++++++ src/sync/syncManagerOnline.ts | 55 ++++++++----------- src/types.ts | 7 ++- src/utils/settingsValidation/index.ts | 3 +- .../settingsValidation/storage/storageCS.ts | 8 +-- 17 files changed, 122 insertions(+), 94 deletions(-) create mode 100644 src/sync/submitters/submitterManager.ts diff --git a/README.md b/README.md index 51caf41d..5ebcc78a 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ Split has built and maintains SDKs for: * iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK) * Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK) * Javascript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK) -* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK) * Javascript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK) +* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK) * PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK) * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK) * React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK) diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index aeb29720..8939b8af 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -4,7 +4,7 @@ import { SplitIO } from '../types'; import { validateKey } from '../utils/inputValidation/key'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSyncCS } from '../storages/types'; +import { IStorageSync } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; @@ -58,8 +58,14 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - const sharedStorage = (storage as IStorageSyncCS).shared(matchingKey); - const sharedSyncManager = syncManager && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); + // @TODO remove shared method to unify storage interfaces + const sharedStorage = storage.shared ? storage.shared(matchingKey) : undefined; + + // Following type assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSync respectively + // Other options are: + // - Consumer mode: both syncManager and sharedStorage are defined + // - Consumer mode with submitters: only syncManager is defined + const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage as IStorageSync) : undefined; // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 684f3a5b..c2f3ef4b 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -5,7 +5,7 @@ import { validateKey } from '../utils/inputValidation/key'; import { validateTrafficType } from '../utils/inputValidation/trafficType'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSyncCS } from '../storages/types'; +import { IStorageSync } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; @@ -72,8 +72,14 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - const sharedStorage = (storage as IStorageSyncCS).shared(matchingKey); - const sharedSyncManager = (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); + // @TODO remove shared method to unify storage interfaces + const sharedStorage = storage.shared ? storage.shared(matchingKey) : undefined; + + // Following type assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSync respectively + // Other options are: + // - Consumer mode: both syncManager and sharedStorage are defined + // - Consumer mode with submitters: only syncManager is defined + const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage as IStorageSync) : undefined; // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. @@ -89,7 +95,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? validTrafficType ); - sharedSyncManager.start(); + sharedSyncManager && sharedSyncManager.start(); log.info(NEW_SHARED_CLIENT); } else { diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 0f89f516..295b3dac 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -31,14 +31,13 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout); const readinessManager = sdkReadinessManager.readinessManager; - // @TODO consider passing the settings object, so that each storage access only what it needs + const matchingKey = getMatching(settings.core.key); + const storageFactoryParams: IStorageFactoryParams = { - eventsQueueSize: settings.scheduler.eventsQueueSize, + settings, optimize: shouldBeOptimized(settings), - - // ATM, only used by InLocalStorage - matchingKey: getMatching(settings.core.key), - splitFiltersValidation: settings.sync.__splitFiltersValidation, + matchingKey, + metadata: metadataBuilder(settings), // Callback used in consumer mode (`syncManagerFactory` is undefined) to emit SDK_READY onReadyCb: !syncManagerFactory ? (error) => { @@ -46,8 +45,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. readinessManager.splits.emit(SDK_SPLITS_ARRIVED); readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); } : undefined, - metadata: metadataBuilder(settings), - log }; const storage = storageFactory(storageFactoryParams); diff --git a/src/storages/inLocalStorage/__tests__/index.spec.ts b/src/storages/inLocalStorage/__tests__/index.spec.ts index 417c8727..faa5157b 100644 --- a/src/storages/inLocalStorage/__tests__/index.spec.ts +++ b/src/storages/inLocalStorage/__tests__/index.spec.ts @@ -16,7 +16,7 @@ import { InLocalStorage } from '../index'; describe('IN LOCAL STORAGE', () => { // @ts-ignore - const internalSdkParams: IStorageFactoryParams = { log: loggerMock }; + const internalSdkParams: IStorageFactoryParams = { settings: { log: loggerMock, scheduler: {}, sync: {} } }; afterEach(() => { fakeInMemoryStorageFactory.mockClear(); diff --git a/src/storages/inLocalStorage/index.ts b/src/storages/inLocalStorage/index.ts index 6587208b..72e8f8be 100644 --- a/src/storages/inLocalStorage/index.ts +++ b/src/storages/inLocalStorage/index.ts @@ -1,7 +1,7 @@ import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; -import { IStorageFactoryParams, IStorageSyncCS, IStorageSyncFactory } from '../types'; +import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types'; import { validatePrefix } from '../KeyBuilder'; import KeyBuilderCS from '../KeyBuilderCS'; import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable'; @@ -25,24 +25,28 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn const prefix = validatePrefix(options.prefix); - function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { + function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSync { + const { + log, + scheduler: { eventsQueueSize }, + sync: { __splitFiltersValidation } + } = params.settings; // Fallback to InMemoryStorage if LocalStorage API is not available if (!isLocalStorageAvailable()) { - params.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage'); + log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage'); return InMemoryStorageCSFactory(params); } - const log = params.log; const keys = new KeyBuilderCS(prefix, params.matchingKey as string); const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS; return { - splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, params.splitFiltersValidation), + splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, __splitFiltersValidation), segments: new MySegmentsCacheInLocal(log, keys), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(params.eventsQueueSize), + events: new EventsCacheInMemory(eventsQueueSize), destroy() { this.splits = new SplitsCacheInMemory(); diff --git a/src/storages/inMemory/InMemoryStorage.ts b/src/storages/inMemory/InMemoryStorage.ts index 349ac6e1..2ae9c75a 100644 --- a/src/storages/inMemory/InMemoryStorage.ts +++ b/src/storages/inMemory/InMemoryStorage.ts @@ -21,7 +21,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS segments: new SegmentsCacheInMemory(), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(params.eventsQueueSize), + events: new EventsCacheInMemory(params.settings.scheduler.eventsQueueSize), // When using MEMORY we should clean all the caches to leave them empty destroy() { diff --git a/src/storages/inMemory/InMemoryStorageCS.ts b/src/storages/inMemory/InMemoryStorageCS.ts index d59e4c04..a4538a9b 100644 --- a/src/storages/inMemory/InMemoryStorageCS.ts +++ b/src/storages/inMemory/InMemoryStorageCS.ts @@ -2,7 +2,7 @@ import SplitsCacheInMemory from './SplitsCacheInMemory'; import MySegmentsCacheInMemory from './MySegmentsCacheInMemory'; import ImpressionsCacheInMemory from './ImpressionsCacheInMemory'; import EventsCacheInMemory from './EventsCacheInMemory'; -import { IStorageSyncCS, IStorageFactoryParams } from '../types'; +import { IStorageSync, IStorageFactoryParams } from '../types'; import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory'; import { STORAGE_MEMORY } from '../../utils/constants'; @@ -11,14 +11,14 @@ import { STORAGE_MEMORY } from '../../utils/constants'; * * @param params parameters required by EventsCacheSync */ -export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { +export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync { return { splits: new SplitsCacheInMemory(), segments: new MySegmentsCacheInMemory(), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(params.eventsQueueSize), + events: new EventsCacheInMemory(params.settings.scheduler.eventsQueueSize), // When using MEMORY we should clean all the caches to leave them empty destroy() { diff --git a/src/storages/inRedis/index.ts b/src/storages/inRedis/index.ts index b13413ee..8a5ab832 100644 --- a/src/storages/inRedis/index.ts +++ b/src/storages/inRedis/index.ts @@ -23,7 +23,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy const prefix = validatePrefix(options.prefix); - function InRedisStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { + function InRedisStorageFactory({ settings: { log }, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const redisClient = new RedisAdapter(log, options.options || {}); diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 8356a3ad..fbfe7121 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -15,7 +15,7 @@ import { assertStorageInterface } from '../../__tests__/testUtils'; describe('PLUGGABLE STORAGE', () => { const internalSdkParams: IStorageFactoryParams = { - log: loggerMock, + settings: { sync: {}, log: loggerMock }, metadata, onReadyCb: jest.fn() }; diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 2afb1bf4..71b9fb46 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -9,6 +9,8 @@ import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { validatePrefix } from '../KeyBuilder'; import { STORAGE_CUSTOM } from '../../utils/constants'; +import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; +import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; @@ -41,7 +43,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ settings, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { + const log = settings.log; + const onlyImpressionsAndEvents = settings.sync.onlyImpressionsAndEvents; const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); @@ -55,8 +59,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn return { splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), - impressions: new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), - events: new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), + impressions: onlyImpressionsAndEvents ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), + events: onlyImpressionsAndEvents ? new EventsCacheInMemory() : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). diff --git a/src/storages/types.ts b/src/storages/types.ts index 288e1fea..080074a9 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -1,7 +1,6 @@ -import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types'; -import { ILogger } from '../logger/types'; +import { MaybeThenable, IMetadata } from '../dtos/types'; import { StoredEventWithMetadata, StoredImpressionWithMetadata } from '../sync/submitters/types'; -import { SplitIO, ImpressionDTO } from '../types'; +import { SplitIO, ImpressionDTO, ISettings } from '../types'; /** * Interface of a custom wrapper storage. @@ -28,7 +27,7 @@ export interface ICustomStorageWrapper { * @returns {Promise} A promise that resolves if the operation success, whether the key was added or updated. * The promise rejects if the operation fails. */ - set: (key: string, value: string) => Promise + set: (key: string, value: string) => Promise /** * Add or update an item with a specified `key` and `value`. * @@ -47,7 +46,7 @@ export interface ICustomStorageWrapper { * @returns {Promise} A promise that resolves if the operation success, whether the key existed and was removed or it didn't exist. * The promise rejects if the operation fails, for example, if there is a connection error. */ - del: (key: string) => Promise + del: (key: string) => Promise /** * Returns all keys matching the given prefix. * @@ -140,20 +139,20 @@ export interface ICustomStorageWrapper { * @function addItems * @param {string} key Set key * @param {string} items Items to add - * @returns {Promise} A promise that resolves if the operation success. + * @returns {Promise} A promise that resolves if the operation success. * The promise rejects if the operation fails, for example, if there is a connection error or the key holds a value that is not a set. */ - addItems: (key: string, items: string[]) => Promise + addItems: (key: string, items: string[]) => Promise /** * Remove the specified `items` from the set stored at `key`. Those items that are not part of the set are ignored. * * @function removeItems * @param {string} key Set key * @param {string} items Items to remove - * @returns {Promise} A promise that resolves if the operation success. If key does not exist, the promise also resolves. + * @returns {Promise} A promise that resolves if the operation success. If key does not exist, the promise also resolves. * The promise rejects if the operation fails, for example, if there is a connection error or the key holds a value that is not a set. */ - removeItems: (key: string, items: string[]) => Promise + removeItems: (key: string, items: string[]) => Promise /** * Returns all the items of the `key` set. * @@ -271,7 +270,7 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase { export interface ISegmentsCacheAsync extends ISegmentsCacheBase { addToSegment(name: string, segmentKeys: string[]): Promise removeFromSegment(name: string, segmentKeys: string[]): Promise - isInSegment(name: string, key?: string): Promise + isInSegment(name: string, key: string): Promise registerSegments(names: string[]): Promise getRegisteredSegments(): Promise setChangeNumber(name: string, changeNumber: number): Promise @@ -396,6 +395,7 @@ export interface IStorageBase< latencies?: TLatenciesCache, counts?: TCountsCache, destroy(): void, + shared?(matchingKey: string): this } export type IStorageSync = IStorageBase< @@ -407,15 +407,11 @@ export type IStorageSync = IStorageBase< ICountsCacheSync > -export interface IStorageSyncCS extends IStorageSync { - shared(matchingKey: string): IStorageSync -} - export type IStorageAsync = IStorageBase< ISplitsCacheAsync, ISegmentsCacheAsync, - IImpressionsCacheAsync, - IEventsCacheAsync, + IImpressionsCacheAsync | IImpressionsCacheSync, + IEventsCacheAsync | IEventsCacheSync, ILatenciesCacheAsync, ICountsCacheAsync > @@ -425,19 +421,17 @@ export type IStorageAsync = IStorageBase< export type DataLoader = (storage: IStorageSync, matchingKey: string) => void export interface IStorageFactoryParams { - log: ILogger, - eventsQueueSize?: number, - optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */, + settings: ISettings, - // ATM, only used by InLocalStorage - matchingKey?: string, /* undefined on server-side SDKs */ - splitFiltersValidation?: ISplitFiltersValidation, + // Properties derived from settings + optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */, + matchingKey?: string, /* undefined on server-side SDKs. ATM, only used by InLocalStorage */ + metadata: IMetadata, // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, // it means that the storge fail to connect and shouldn't be used. // It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage. onReadyCb?: (error?: any) => void, - metadata: IMetadata, } export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'CUSTOM'; diff --git a/src/sync/submitters/submitterManager.ts b/src/sync/submitters/submitterManager.ts new file mode 100644 index 00000000..7828d947 --- /dev/null +++ b/src/sync/submitters/submitterManager.ts @@ -0,0 +1,22 @@ +import { syncTaskComposite } from '../syncTaskComposite'; +import { eventsSyncTaskFactory } from './eventsSyncTask'; +import { impressionsSyncTaskFactory } from './impressionsSyncTask'; +import { impressionCountsSyncTaskFactory } from './impressionCountsSyncTask'; +import { ISplitApi } from '../../services/types'; +import { IStorageSync } from '../../storages/types'; +import { ISettings } from '../../types'; + +export function submitterManagerFactory( + settings: ISettings, + storage: IStorageSync, + splitApi: ISplitApi, +) { + const log = settings.log; + const submitters = [ + impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), + eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) + // @TODO add telemetry submitter + ]; + if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); + return syncTaskComposite(submitters); +} diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index b0199a9c..d10323d3 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -1,8 +1,5 @@ import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from './types'; -import { syncTaskComposite } from './syncTaskComposite'; -import { eventsSyncTaskFactory } from './submitters/eventsSyncTask'; -import { impressionsSyncTaskFactory } from './submitters/impressionsSyncTask'; -import { impressionCountsSyncTaskFactory } from './submitters/impressionCountsSyncTask'; +import { submitterManagerFactory } from './submitters/submitterManager'; import { IReadinessManager } from '../readiness/types'; import { IStorageSync } from '../storages/types'; import { IPushManagerFactoryParams, IPushManager, IPushManagerCS } from './streaming/types'; @@ -19,7 +16,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '.. * @param pushManagerFactory optional to build a SyncManager with or without streaming support */ export function syncManagerOnlineFactory( - pollingManagerFactory: (...args: IPollingManagerFactoryParams) => IPollingManager, + pollingManagerFactory?: (...args: IPollingManagerFactoryParams) => IPollingManager, pushManagerFactory?: (...args: IPushManagerFactoryParams) => IPushManager | undefined ): (params: ISyncManagerFactoryParams) => ISyncManagerCS { @@ -37,30 +34,24 @@ export function syncManagerOnlineFactory( const log = settings.log; /** Polling Manager */ - const pollingManager = pollingManagerFactory(splitApi, storage, readiness, settings); + const pollingManager = pollingManagerFactory && pollingManagerFactory(splitApi, storage, readiness, settings); /** Push Manager */ - const pushManager = settings.streamingEnabled && pushManagerFactory ? + const pushManager = settings.streamingEnabled && pollingManager && pushManagerFactory ? pushManagerFactory(pollingManager, storage, readiness, splitApi.fetchAuth, platform, settings) : undefined; /** Submitter Manager */ - // It is not inyected via a factory as push and polling managers, because at the moment it is mandatory and the same for server-side and client-side variants - const submitters = [ - impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), - eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) - // @TODO add telemetry submitter - ]; - if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); - const submitter = syncTaskComposite(submitters); + // It is not inyected as push and polling managers, because at the moment it is mandatory + const submitter = submitterManagerFactory(settings, storage, splitApi); /** Sync Manager logic */ function startPolling() { - if (!pollingManager.isRunning()) { + if (!pollingManager!.isRunning()) { log.info(SYNC_START_POLLING); - pollingManager.start(); + pollingManager!.start(); } else { log.info(SYNC_CONTINUE_POLLING); } @@ -69,10 +60,10 @@ export function syncManagerOnlineFactory( function stopPollingAndSyncAll() { log.info(SYNC_STOP_POLLING); // if polling, stop - if (pollingManager.isRunning()) pollingManager.stop(); + if (pollingManager!.isRunning()) pollingManager!.stop(); // fetch splits and segments. There is no need to catch this promise (it is always resolved) - pollingManager.syncAll(); + pollingManager!.syncAll(); } if (pushManager) { @@ -91,15 +82,17 @@ export function syncManagerOnlineFactory( */ start() { // start syncing splits and segments - if (pushManager) { - // Doesn't call `syncAll` when the syncManager is resuming - if (startFirstTime) { - pollingManager.syncAll(); - startFirstTime = false; + if (pollingManager) { + if (pushManager) { + // Doesn't call `syncAll` when the syncManager is resuming + if (startFirstTime) { + pollingManager.syncAll(); + startFirstTime = false; + } + pushManager.start(); + } else { + pollingManager.start(); } - pushManager.start(); - } else { - pollingManager.start(); } // start periodic data recording (events, impressions, telemetry). @@ -113,7 +106,7 @@ export function syncManagerOnlineFactory( stop() { // stop syncing if (pushManager) pushManager.stop(); - if (pollingManager.isRunning()) pollingManager.stop(); + if (pollingManager && pollingManager.isRunning()) pollingManager.stop(); // stop periodic data recording (events, impressions, telemetry). if (submitter) submitter.stop(); @@ -129,8 +122,8 @@ export function syncManagerOnlineFactory( else return Promise.resolve(); }, - // [Only used for client-side] - // It assumes that polling and push managers implement the interfaces for client-side + // Only used for client-side in standalone mode. + // Therefore, polling manager is defined and both polling and push managers implement the interfaces for client-side shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager { const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage); @@ -139,7 +132,7 @@ export function syncManagerOnlineFactory( isRunning: mySegmentsSyncTask.isRunning, start() { if (pushManager) { - if (pollingManager.isRunning()) { + if (pollingManager!.isRunning()) { // if doing polling, we must start the periodic fetch of data if (storage.splits.usesSegments()) mySegmentsSyncTask.start(); } else { diff --git a/src/types.ts b/src/types.ts index 691c5beb..c77bd861 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ import { IIntegration, IIntegrationFactoryParams } from './integrations/types'; import { ILogger } from './logger/types'; /* eslint-disable no-use-before-define */ -import { IStorageFactoryParams, IStorageSyncCS, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; +import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; /** @@ -101,7 +101,8 @@ export interface ISettings { splitFilters: SplitIO.SplitFilter[], impressionsMode: SplitIO.ImpressionsMode, __splitFiltersValidation: ISplitFiltersValidation, - localhostMode?: SplitIO.LocalhostFactory + localhostMode?: SplitIO.LocalhostFactory, + onlyImpressionsAndEvents?: boolean }, readonly runtime: { ip: string | false @@ -841,7 +842,7 @@ export namespace SplitIO { * Defines which kind of storage we should instanciate. * @property {Object} storage */ - storage?: (params: IStorageFactoryParams) => IStorageSyncCS, + storage?: (params: IStorageFactoryParams) => IStorageSync | IStorageAsync, /** * List of URLs that the SDK will use as base for it's synchronization functionalities, applicable only when running as standalone. * Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy. diff --git a/src/utils/settingsValidation/index.ts b/src/utils/settingsValidation/index.ts index 1840fdb5..af75bb76 100644 --- a/src/utils/settingsValidation/index.ts +++ b/src/utils/settingsValidation/index.ts @@ -75,7 +75,8 @@ const base = { splitFilters: undefined, // impressions collection mode impressionsMode: OPTIMIZED, - localhostMode: undefined + localhostMode: undefined, + onlyImpressionsAndEvents: false }, runtime: { diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index 9cb762e2..841ca959 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -2,10 +2,10 @@ import { InMemoryStorageCSFactory } from '../../../storages/inMemory/InMemorySto import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; import { WARN_STORAGE_INVALID } from '../../../logger/constants'; -import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; -import { IStorageFactoryParams, IStorageSyncCS } from '../../../storages/types'; +import { LOCALHOST_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; +import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; -export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSyncCS { +export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync { const result = InMemoryStorageCSFactory(params); result.splits.checkCache = () => true; // to emit SDK_READY_FROM_CACHE return result; @@ -23,7 +23,7 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode? let { storage = InMemoryStorageCSFactory, log, mode } = settings; // If an invalid storage is provided, fallback into MEMORY - if (typeof storage !== 'function' || storage.type !== STORAGE_MEMORY && storage.type !== STORAGE_LOCALSTORAGE) { + if (typeof storage !== 'function' || [STORAGE_MEMORY, STORAGE_LOCALSTORAGE, STORAGE_CUSTOM].indexOf(storage.type) === -1) { storage = InMemoryStorageCSFactory; log.warn(WARN_STORAGE_INVALID); } From a4584ec38c666d5964effe4cf44eb88ecec0e09e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 5 Nov 2021 14:01:01 -0300 Subject: [PATCH 02/34] polishing --- src/sdkClient/sdkClientMethodCS.ts | 12 ++++++------ src/sdkClient/sdkClientMethodCSWithTT.ts | 10 +++++----- src/storages/inLocalStorage/index.ts | 4 ++-- src/storages/inMemory/InMemoryStorageCS.ts | 4 ++-- src/storages/types.ts | 5 ++++- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index 8939b8af..4e818cde 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -4,7 +4,7 @@ import { SplitIO } from '../types'; import { validateKey } from '../utils/inputValidation/key'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSync } from '../storages/types'; +import { IStorageSyncCS } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; @@ -58,14 +58,14 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // @TODO remove shared method to unify storage interfaces - const sharedStorage = storage.shared ? storage.shared(matchingKey) : undefined; + // @ts-ignore @TODO remove shared method to unify storage interfaces + const sharedStorage = storage.shared ? (storage as IStorageSyncCS).shared(matchingKey) : undefined; - // Following type assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSync respectively + // Next assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSyncCS respectively // Other options are: - // - Consumer mode: both syncManager and sharedStorage are defined + // - Consumer mode: both syncManager and sharedStorage are undefined // - Consumer mode with submitters: only syncManager is defined - const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage as IStorageSync) : undefined; + const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage) : undefined; // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index c2f3ef4b..12c1d880 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -5,7 +5,7 @@ import { validateKey } from '../utils/inputValidation/key'; import { validateTrafficType } from '../utils/inputValidation/trafficType'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSync } from '../storages/types'; +import { IStorageSyncCS } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; @@ -72,14 +72,14 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // @TODO remove shared method to unify storage interfaces - const sharedStorage = storage.shared ? storage.shared(matchingKey) : undefined; + // @ts-ignore @TODO remove shared method to unify storage interfaces + const sharedStorage = storage.shared ? (storage as IStorageSyncCS).shared(matchingKey) : undefined; // Following type assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSync respectively // Other options are: - // - Consumer mode: both syncManager and sharedStorage are defined + // - Consumer mode: both syncManager and sharedStorage are undefined // - Consumer mode with submitters: only syncManager is defined - const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage as IStorageSync) : undefined; + const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage) : undefined; // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. diff --git a/src/storages/inLocalStorage/index.ts b/src/storages/inLocalStorage/index.ts index 72e8f8be..87cd6dd1 100644 --- a/src/storages/inLocalStorage/index.ts +++ b/src/storages/inLocalStorage/index.ts @@ -1,7 +1,7 @@ import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; -import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types'; +import { IStorageFactoryParams, IStorageSyncCS, IStorageSyncFactory } from '../types'; import { validatePrefix } from '../KeyBuilder'; import KeyBuilderCS from '../KeyBuilderCS'; import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable'; @@ -25,7 +25,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn const prefix = validatePrefix(options.prefix); - function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSync { + function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { const { log, scheduler: { eventsQueueSize }, diff --git a/src/storages/inMemory/InMemoryStorageCS.ts b/src/storages/inMemory/InMemoryStorageCS.ts index a4538a9b..ba7a1510 100644 --- a/src/storages/inMemory/InMemoryStorageCS.ts +++ b/src/storages/inMemory/InMemoryStorageCS.ts @@ -2,7 +2,7 @@ import SplitsCacheInMemory from './SplitsCacheInMemory'; import MySegmentsCacheInMemory from './MySegmentsCacheInMemory'; import ImpressionsCacheInMemory from './ImpressionsCacheInMemory'; import EventsCacheInMemory from './EventsCacheInMemory'; -import { IStorageSync, IStorageFactoryParams } from '../types'; +import { IStorageSyncCS, IStorageFactoryParams } from '../types'; import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory'; import { STORAGE_MEMORY } from '../../utils/constants'; @@ -11,7 +11,7 @@ import { STORAGE_MEMORY } from '../../utils/constants'; * * @param params parameters required by EventsCacheSync */ -export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync { +export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { return { splits: new SplitsCacheInMemory(), diff --git a/src/storages/types.ts b/src/storages/types.ts index 080074a9..7d722942 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -395,7 +395,6 @@ export interface IStorageBase< latencies?: TLatenciesCache, counts?: TCountsCache, destroy(): void, - shared?(matchingKey: string): this } export type IStorageSync = IStorageBase< @@ -407,6 +406,10 @@ export type IStorageSync = IStorageBase< ICountsCacheSync > +export interface IStorageSyncCS extends IStorageSync { + shared(matchingKey: string): IStorageSync +} + export type IStorageAsync = IStorageBase< ISplitsCacheAsync, ISegmentsCacheAsync, From f4ebb8fb56b8d038b3690e6dbfba4378ecb18f79 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 5 Nov 2021 15:20:48 -0300 Subject: [PATCH 03/34] revert some refactors --- src/sdkClient/sdkClientMethodCS.ts | 15 ++++++------ src/sdkClient/sdkClientMethodCSWithTT.ts | 15 ++++++------ src/sdkFactory/index.ts | 23 ++++++++++++------- .../inLocalStorage/__tests__/index.spec.ts | 2 +- src/storages/inLocalStorage/index.ts | 12 ++++------ src/storages/inMemory/InMemoryStorage.ts | 5 +--- src/storages/inMemory/InMemoryStorageCS.ts | 2 +- src/storages/inRedis/index.ts | 2 +- .../pluggable/__tests__/index.spec.ts | 2 +- src/storages/pluggable/index.ts | 8 +++---- src/storages/types.ts | 21 +++++++++++------ src/sync/syncManagerOnline.ts | 4 ++-- src/sync/types.ts | 2 ++ src/types.ts | 4 ++-- src/utils/settingsValidation/index.ts | 3 +-- .../settingsValidation/storage/storageCS.ts | 4 ++-- 16 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index 4e818cde..0b60f307 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -58,14 +58,13 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // @ts-ignore @TODO remove shared method to unify storage interfaces - const sharedStorage = storage.shared ? (storage as IStorageSyncCS).shared(matchingKey) : undefined; - - // Next assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSyncCS respectively - // Other options are: - // - Consumer mode: both syncManager and sharedStorage are undefined - // - Consumer mode with submitters: only syncManager is defined - const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage) : undefined; + + // 3 combinations are possible: + // - Standalone mode: both syncManager and storage.shared are defined + // - Consumer mode: both syncManager and storage.shared are undefined + // - Consumer mode with submitters: syncManager is defined but not storage.shared + const sharedStorage = (storage as IStorageSyncCS).shared && (storage as IStorageSyncCS).shared(matchingKey); + const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 12c1d880..10e30586 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -72,14 +72,13 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // @ts-ignore @TODO remove shared method to unify storage interfaces - const sharedStorage = storage.shared ? (storage as IStorageSyncCS).shared(matchingKey) : undefined; - - // Following type assertions are safe: if syncManager and sharedStorage are defined (standalone mode), they implement ISyncManagerCS and IStorageSync respectively - // Other options are: - // - Consumer mode: both syncManager and sharedStorage are undefined - // - Consumer mode with submitters: only syncManager is defined - const sharedSyncManager = syncManager && sharedStorage ? (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage) : undefined; + + // 3 combinations are possible: + // - Standalone mode: both syncManager and storage.shared are defined + // - Consumer mode: both syncManager and storage.shared are undefined + // - Consumer mode with submitters: syncManager is defined but not storage.shared + const sharedStorage = (storage as IStorageSyncCS).shared && (storage as IStorageSyncCS).shared(matchingKey); + const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); // As shared clients reuse all the storage information, we don't need to check here if we // will use offline or online mode. We should stick with the original decision. diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 295b3dac..550952f8 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -31,20 +31,27 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout); const readinessManager = sdkReadinessManager.readinessManager; - const matchingKey = getMatching(settings.core.key); - + // @TODO consider passing the settings object, so that each storage access only what it needs const storageFactoryParams: IStorageFactoryParams = { - settings, + log, + eventsQueueSize: settings.scheduler.eventsQueueSize, optimize: shouldBeOptimized(settings), - matchingKey, - metadata: metadataBuilder(settings), - // Callback used in consumer mode (`syncManagerFactory` is undefined) to emit SDK_READY - onReadyCb: !syncManagerFactory ? (error) => { + // ATM, only used by InLocalStorage + matchingKey: getMatching(settings.core.key), + splitFiltersValidation: settings.sync.__splitFiltersValidation, + + // ATM, only used by CustomStorage. true for partial consumer mode + trackInMemory: settings.sync.onlyImpressionsAndEvents, + + // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined + // or only instantiates submitters, and therefore it is not able to emit readiness events. + onReadyCb: (error) => { if (error) return; // don't emit SDK_READY if storage failed to connect. readinessManager.splits.emit(SDK_SPLITS_ARRIVED); readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); - } : undefined, + }, + metadata: metadataBuilder(settings) }; const storage = storageFactory(storageFactoryParams); diff --git a/src/storages/inLocalStorage/__tests__/index.spec.ts b/src/storages/inLocalStorage/__tests__/index.spec.ts index faa5157b..417c8727 100644 --- a/src/storages/inLocalStorage/__tests__/index.spec.ts +++ b/src/storages/inLocalStorage/__tests__/index.spec.ts @@ -16,7 +16,7 @@ import { InLocalStorage } from '../index'; describe('IN LOCAL STORAGE', () => { // @ts-ignore - const internalSdkParams: IStorageFactoryParams = { settings: { log: loggerMock, scheduler: {}, sync: {} } }; + const internalSdkParams: IStorageFactoryParams = { log: loggerMock }; afterEach(() => { fakeInMemoryStorageFactory.mockClear(); diff --git a/src/storages/inLocalStorage/index.ts b/src/storages/inLocalStorage/index.ts index 87cd6dd1..6587208b 100644 --- a/src/storages/inLocalStorage/index.ts +++ b/src/storages/inLocalStorage/index.ts @@ -26,27 +26,23 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn const prefix = validatePrefix(options.prefix); function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { - const { - log, - scheduler: { eventsQueueSize }, - sync: { __splitFiltersValidation } - } = params.settings; // Fallback to InMemoryStorage if LocalStorage API is not available if (!isLocalStorageAvailable()) { - log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage'); + params.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Fallbacking into default MEMORY storage'); return InMemoryStorageCSFactory(params); } + const log = params.log; const keys = new KeyBuilderCS(prefix, params.matchingKey as string); const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS; return { - splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, __splitFiltersValidation), + splits: new SplitsCacheInLocal(log, keys, expirationTimestamp, params.splitFiltersValidation), segments: new MySegmentsCacheInLocal(log, keys), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(eventsQueueSize), + events: new EventsCacheInMemory(params.eventsQueueSize), destroy() { this.splits = new SplitsCacheInMemory(); diff --git a/src/storages/inMemory/InMemoryStorage.ts b/src/storages/inMemory/InMemoryStorage.ts index 2ae9c75a..173fbf3a 100644 --- a/src/storages/inMemory/InMemoryStorage.ts +++ b/src/storages/inMemory/InMemoryStorage.ts @@ -13,15 +13,12 @@ import { STORAGE_MEMORY } from '../../utils/constants'; */ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync { - // InMemory storage is always ready - if (params.onReadyCb) setTimeout(params.onReadyCb); - return { splits: new SplitsCacheInMemory(), segments: new SegmentsCacheInMemory(), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(params.settings.scheduler.eventsQueueSize), + events: new EventsCacheInMemory(params.eventsQueueSize), // When using MEMORY we should clean all the caches to leave them empty destroy() { diff --git a/src/storages/inMemory/InMemoryStorageCS.ts b/src/storages/inMemory/InMemoryStorageCS.ts index ba7a1510..d59e4c04 100644 --- a/src/storages/inMemory/InMemoryStorageCS.ts +++ b/src/storages/inMemory/InMemoryStorageCS.ts @@ -18,7 +18,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag segments: new MySegmentsCacheInMemory(), impressions: new ImpressionsCacheInMemory(), impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined, - events: new EventsCacheInMemory(params.settings.scheduler.eventsQueueSize), + events: new EventsCacheInMemory(params.eventsQueueSize), // When using MEMORY we should clean all the caches to leave them empty destroy() { diff --git a/src/storages/inRedis/index.ts b/src/storages/inRedis/index.ts index 8a5ab832..b13413ee 100644 --- a/src/storages/inRedis/index.ts +++ b/src/storages/inRedis/index.ts @@ -23,7 +23,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy const prefix = validatePrefix(options.prefix); - function InRedisStorageFactory({ settings: { log }, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { + function InRedisStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const redisClient = new RedisAdapter(log, options.options || {}); diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index fbfe7121..8356a3ad 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -15,7 +15,7 @@ import { assertStorageInterface } from '../../__tests__/testUtils'; describe('PLUGGABLE STORAGE', () => { const internalSdkParams: IStorageFactoryParams = { - settings: { sync: {}, log: loggerMock }, + log: loggerMock, metadata, onReadyCb: jest.fn() }; diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 71b9fb46..724dd18a 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -43,9 +43,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ settings, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { - const log = settings.log; - const onlyImpressionsAndEvents = settings.sync.onlyImpressionsAndEvents; + function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); @@ -59,8 +57,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn return { splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), - impressions: onlyImpressionsAndEvents ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), - events: onlyImpressionsAndEvents ? new EventsCacheInMemory() : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), + impressions: trackInMemory ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), + events: trackInMemory ? new EventsCacheInMemory() : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). diff --git a/src/storages/types.ts b/src/storages/types.ts index 7d722942..e3e6f846 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -1,6 +1,7 @@ -import { MaybeThenable, IMetadata } from '../dtos/types'; +import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types'; +import { ILogger } from '../logger/types'; import { StoredEventWithMetadata, StoredImpressionWithMetadata } from '../sync/submitters/types'; -import { SplitIO, ImpressionDTO, ISettings } from '../types'; +import { SplitIO, ImpressionDTO } from '../types'; /** * Interface of a custom wrapper storage. @@ -406,6 +407,7 @@ export type IStorageSync = IStorageBase< ICountsCacheSync > +// @TODO remove shared method to unify storage interfaces export interface IStorageSyncCS extends IStorageSync { shared(matchingKey: string): IStorageSync } @@ -424,17 +426,22 @@ export type IStorageAsync = IStorageBase< export type DataLoader = (storage: IStorageSync, matchingKey: string) => void export interface IStorageFactoryParams { - settings: ISettings, - - // Properties derived from settings + log: ILogger, + eventsQueueSize?: number, optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */, - matchingKey?: string, /* undefined on server-side SDKs. ATM, only used by InLocalStorage */ - metadata: IMetadata, + + // ATM, only used by InLocalStorage + matchingKey?: string, /* undefined on server-side SDKs */ + splitFiltersValidation?: ISplitFiltersValidation, + + // ATM, only used by CustomStorage. true for partial consumer mode + trackInMemory?: boolean, // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, // it means that the storge fail to connect and shouldn't be used. // It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage. onReadyCb?: (error?: any) => void, + metadata: IMetadata, } export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'CUSTOM'; diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index d10323d3..c7d1ee7b 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -122,8 +122,8 @@ export function syncManagerOnlineFactory( else return Promise.resolve(); }, - // Only used for client-side in standalone mode. - // Therefore, polling manager is defined and both polling and push managers implement the interfaces for client-side + // Only used for client-side in standalone mode: polling manager is defined and + // both polling and push managers implement the interfaces for client-side shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager { const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage); diff --git a/src/sync/types.ts b/src/sync/types.ts index 55566ef1..ebb4c22e 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types'; import { ISplitApi } from '../services/types'; import { IStorageSync } from '../storages/types'; import { ISettings } from '../types'; +import { IPollingManager } from './polling/types'; import { IPushManager } from './streaming/types'; export interface ITask { @@ -43,6 +44,7 @@ export interface ITimeTracker { export interface ISyncManager extends ITask { flush(): Promise, + pollingManager?: IPollingManager pushManager?: IPushManager } diff --git a/src/types.ts b/src/types.ts index c77bd861..8b9b1288 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ import { IIntegration, IIntegrationFactoryParams } from './integrations/types'; import { ILogger } from './logger/types'; /* eslint-disable no-use-before-define */ -import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; +import { IStorageFactoryParams, IStorageSyncCS, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; /** @@ -842,7 +842,7 @@ export namespace SplitIO { * Defines which kind of storage we should instanciate. * @property {Object} storage */ - storage?: (params: IStorageFactoryParams) => IStorageSync | IStorageAsync, + storage?: (params: IStorageFactoryParams) => IStorageSyncCS | IStorageAsync, /** * List of URLs that the SDK will use as base for it's synchronization functionalities, applicable only when running as standalone. * Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy. diff --git a/src/utils/settingsValidation/index.ts b/src/utils/settingsValidation/index.ts index af75bb76..1840fdb5 100644 --- a/src/utils/settingsValidation/index.ts +++ b/src/utils/settingsValidation/index.ts @@ -75,8 +75,7 @@ const base = { splitFilters: undefined, // impressions collection mode impressionsMode: OPTIMIZED, - localhostMode: undefined, - onlyImpressionsAndEvents: false + localhostMode: undefined }, runtime: { diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index 841ca959..19b5e29e 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -3,9 +3,9 @@ import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; import { WARN_STORAGE_INVALID } from '../../../logger/constants'; import { LOCALHOST_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; -import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; +import { IStorageFactoryParams, IStorageSyncCS } from '../../../storages/types'; -export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync { +export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSyncCS { const result = InMemoryStorageCSFactory(params); result.splits.checkCache = () => true; // to emit SDK_READY_FROM_CACHE return result; From 9dfccd76c0516184838b6be3732cf7e9de51bf04 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 5 Nov 2021 15:26:35 -0300 Subject: [PATCH 04/34] renamed PluggableStorage --> CustomStorage --- src/index.ts | 2 +- src/storages/pluggable/__tests__/index.spec.ts | 16 ++++++++-------- src/storages/pluggable/index.ts | 18 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index 33f4217a..179c3585 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ export { InMemoryStorageCSFactory } from './storages/inMemory/InMemoryStorageCS' export { InLocalStorage } from './storages/inLocalStorage'; // Pluggable storage factory for consumer/producer mode -export { PluggableStorage } from './storages/pluggable'; +export { CustomStorage } from './storages/pluggable'; // Pluggable storage factory for consumer/producer mode export { InRedisStorage } from './storages/inRedis'; diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 8356a3ad..3ebf6d58 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -9,7 +9,7 @@ const metadata = { s: 'version', i: 'ip', n: 'hostname' }; const prefix = 'some_prefix'; // Test target -import { PluggableStorage } from '../index'; +import { CustomStorage } from '../index'; import { assertStorageInterface } from '../../__tests__/testUtils'; describe('PLUGGABLE STORAGE', () => { @@ -25,7 +25,7 @@ describe('PLUGGABLE STORAGE', () => { }); test('creates a storage instance', async () => { - const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock }); + const storageFactory = CustomStorage({ prefix, wrapper: wrapperMock }); const storage = storageFactory(internalSdkParams); assertStorageInterface(storage); // the instance must implement the storage interface @@ -42,21 +42,21 @@ describe('PLUGGABLE STORAGE', () => { test('throws an exception if wrapper doesn\'t implement the expected interface', async () => { - expect(() => PluggableStorage({ wrapper: wrapperMock })).not.toThrow(); // not prefix but valid wrapper is OK + expect(() => CustomStorage({ wrapper: wrapperMock })).not.toThrow(); // not prefix but valid wrapper is OK // Throws exception if no object is passed as wrapper const errorNoValidWrapper = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; - expect(() => PluggableStorage()).toThrow(errorNoValidWrapper); - expect(() => PluggableStorage({ wrapper: undefined })).toThrow(errorNoValidWrapper); - expect(() => PluggableStorage({ wrapper: 'invalid wrapper' })).toThrow(errorNoValidWrapper); + expect(() => CustomStorage()).toThrow(errorNoValidWrapper); + expect(() => CustomStorage({ wrapper: undefined })).toThrow(errorNoValidWrapper); + expect(() => CustomStorage({ wrapper: 'invalid wrapper' })).toThrow(errorNoValidWrapper); // Throws exception if the given object is not a valid wrapper, informing which methods are missing const invalidWrapper = wrapperMockFactory(); invalidWrapper.connect = undefined; invalidWrapper.close = 'invalid function'; const errorNoValidWrapperInterface = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; - expect(() => PluggableStorage({ wrapper: invalidWrapper })).toThrow(errorNoValidWrapperInterface); - expect(() => PluggableStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); + expect(() => CustomStorage({ wrapper: invalidWrapper })).toThrow(errorNoValidWrapperInterface); + expect(() => CustomStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); }); }); diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 724dd18a..337ea94e 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -15,18 +15,18 @@ import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; -export interface PluggableStorageOptions { +export interface CustomStorageOptions { prefix?: string wrapper: ICustomStorageWrapper } /** - * Validate pluggable storage factory options. + * Validate custom storage factory options. * * @param options user options * @throws Will throw an error if the options are invalid. Example: wrapper is not provided or doesn't have some methods. */ -function validatePluggableStorageOptions(options: any) { +function validateCustomStorageOptions(options: any) { if (!isObject(options) || !isObject(options.wrapper)) throw new Error(NO_VALID_WRAPPER); const wrapper = options.wrapper; @@ -35,15 +35,15 @@ function validatePluggableStorageOptions(options: any) { } /** - * Pluggable storage factory for consumer server-side & client-side SplitFactory. + * Custom storage factory for consumer server-side & client-side SplitFactory. */ -export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyncFactory { +export function CustomStorage(options: CustomStorageOptions): IStorageAsyncFactory { - validatePluggableStorageOptions(options); + validateCustomStorageOptions(options); const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { + function CustomStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); @@ -68,6 +68,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn }; } - PluggableStorageFactory.type = STORAGE_CUSTOM; - return PluggableStorageFactory; + CustomStorageFactory.type = STORAGE_CUSTOM; + return CustomStorageFactory; } From 6b208b51733fa3ad937005ed4ca97ee7f9940af2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 5 Nov 2021 16:17:40 -0300 Subject: [PATCH 05/34] Revert "renamed PluggableStorage --> CustomStorage" This reverts commit 9dfccd76c0516184838b6be3732cf7e9de51bf04. --- src/index.ts | 2 +- src/storages/pluggable/__tests__/index.spec.ts | 16 ++++++++-------- src/storages/pluggable/index.ts | 18 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index 179c3585..33f4217a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ export { InMemoryStorageCSFactory } from './storages/inMemory/InMemoryStorageCS' export { InLocalStorage } from './storages/inLocalStorage'; // Pluggable storage factory for consumer/producer mode -export { CustomStorage } from './storages/pluggable'; +export { PluggableStorage } from './storages/pluggable'; // Pluggable storage factory for consumer/producer mode export { InRedisStorage } from './storages/inRedis'; diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 3ebf6d58..8356a3ad 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -9,7 +9,7 @@ const metadata = { s: 'version', i: 'ip', n: 'hostname' }; const prefix = 'some_prefix'; // Test target -import { CustomStorage } from '../index'; +import { PluggableStorage } from '../index'; import { assertStorageInterface } from '../../__tests__/testUtils'; describe('PLUGGABLE STORAGE', () => { @@ -25,7 +25,7 @@ describe('PLUGGABLE STORAGE', () => { }); test('creates a storage instance', async () => { - const storageFactory = CustomStorage({ prefix, wrapper: wrapperMock }); + const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock }); const storage = storageFactory(internalSdkParams); assertStorageInterface(storage); // the instance must implement the storage interface @@ -42,21 +42,21 @@ describe('PLUGGABLE STORAGE', () => { test('throws an exception if wrapper doesn\'t implement the expected interface', async () => { - expect(() => CustomStorage({ wrapper: wrapperMock })).not.toThrow(); // not prefix but valid wrapper is OK + expect(() => PluggableStorage({ wrapper: wrapperMock })).not.toThrow(); // not prefix but valid wrapper is OK // Throws exception if no object is passed as wrapper const errorNoValidWrapper = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; - expect(() => CustomStorage()).toThrow(errorNoValidWrapper); - expect(() => CustomStorage({ wrapper: undefined })).toThrow(errorNoValidWrapper); - expect(() => CustomStorage({ wrapper: 'invalid wrapper' })).toThrow(errorNoValidWrapper); + expect(() => PluggableStorage()).toThrow(errorNoValidWrapper); + expect(() => PluggableStorage({ wrapper: undefined })).toThrow(errorNoValidWrapper); + expect(() => PluggableStorage({ wrapper: 'invalid wrapper' })).toThrow(errorNoValidWrapper); // Throws exception if the given object is not a valid wrapper, informing which methods are missing const invalidWrapper = wrapperMockFactory(); invalidWrapper.connect = undefined; invalidWrapper.close = 'invalid function'; const errorNoValidWrapperInterface = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; - expect(() => CustomStorage({ wrapper: invalidWrapper })).toThrow(errorNoValidWrapperInterface); - expect(() => CustomStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); + expect(() => PluggableStorage({ wrapper: invalidWrapper })).toThrow(errorNoValidWrapperInterface); + expect(() => PluggableStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); }); }); diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 337ea94e..724dd18a 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -15,18 +15,18 @@ import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; -export interface CustomStorageOptions { +export interface PluggableStorageOptions { prefix?: string wrapper: ICustomStorageWrapper } /** - * Validate custom storage factory options. + * Validate pluggable storage factory options. * * @param options user options * @throws Will throw an error if the options are invalid. Example: wrapper is not provided or doesn't have some methods. */ -function validateCustomStorageOptions(options: any) { +function validatePluggableStorageOptions(options: any) { if (!isObject(options) || !isObject(options.wrapper)) throw new Error(NO_VALID_WRAPPER); const wrapper = options.wrapper; @@ -35,15 +35,15 @@ function validateCustomStorageOptions(options: any) { } /** - * Custom storage factory for consumer server-side & client-side SplitFactory. + * Pluggable storage factory for consumer server-side & client-side SplitFactory. */ -export function CustomStorage(options: CustomStorageOptions): IStorageAsyncFactory { +export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyncFactory { - validateCustomStorageOptions(options); + validatePluggableStorageOptions(options); const prefix = validatePrefix(options.prefix); - function CustomStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); @@ -68,6 +68,6 @@ export function CustomStorage(options: CustomStorageOptions): IStorageAsyncFacto }; } - CustomStorageFactory.type = STORAGE_CUSTOM; - return CustomStorageFactory; + PluggableStorageFactory.type = STORAGE_CUSTOM; + return PluggableStorageFactory; } From 1e668a50ee2c8f488e483c7e76d69b933039ab75 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 16:08:26 -0300 Subject: [PATCH 06/34] polishing --- src/listeners/browser.ts | 4 +- src/sdkClient/sdkClientMethodCS.ts | 20 +++++---- src/sdkClient/sdkClientMethodCSWithTT.ts | 20 +++++---- src/storages/inLocalStorage/index.ts | 4 +- src/storages/inMemory/InMemoryStorageCS.ts | 4 +- src/storages/pluggable/inMemoryWrapper.ts | 18 +++++++- src/storages/pluggable/index.ts | 41 +++++++++++++++---- src/storages/types.ts | 16 +++----- src/sync/syncManagerOnline.ts | 9 ++-- src/sync/types.ts | 2 +- src/types.ts | 4 +- .../settingsValidation/storage/storageCS.ts | 4 +- 12 files changed, 98 insertions(+), 48 deletions(-) diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index c5c4fdfd..37429224 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -63,6 +63,8 @@ export default class BrowserSignalListener implements ISignalListener { * using beacon API if possible, or falling back to regular post transport. */ flushData() { + if (!this.syncManager) return; // In consumer mode there is not sync manager + const eventsUrl = this.settings.urls.events; const extraMetadata = { // sim stands for Sync/Split Impressions Mode @@ -74,7 +76,7 @@ export default class BrowserSignalListener implements ISignalListener { if (this.storage.impressionCounts) this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, fromImpressionCountsCollector); // Close streaming connection - if (this.syncManager && this.syncManager.pushManager) this.syncManager.pushManager.stop(); + if (this.syncManager.pushManager) this.syncManager.pushManager.stop(); } private _flushData(url: string, cache: IRecorderCacheProducerSync, postService: (body: string) => Promise, fromCacheToPayload?: (cacheData: TState) => any, extraMetadata?: {}) { diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index 0b60f307..14de634d 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -4,10 +4,10 @@ import { SplitIO } from '../types'; import { validateKey } from '../utils/inputValidation/key'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSyncCS } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; +import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants'; function buildInstanceId(key: SplitIO.SplitKey) { // @ts-ignore @@ -59,11 +59,17 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // 3 combinations are possible: - // - Standalone mode: both syncManager and storage.shared are defined - // - Consumer mode: both syncManager and storage.shared are undefined - // - Consumer mode with submitters: syncManager is defined but not storage.shared - const sharedStorage = (storage as IStorageSyncCS).shared && (storage as IStorageSyncCS).shared(matchingKey); + const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => { + // Emit SDK_READY in consumer mode for shared clients + if (err) return; // don't emit SDK_READY if storage failed to connect. + sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); + }); + + // 3 possibilities: + // - Standalone mode: both syncManager and sharedSyncManager are defined + // - Consumer mode: both syncManager and sharedSyncManager are undefined + // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined + // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); // As shared clients reuse all the storage information, we don't need to check here if we @@ -71,7 +77,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? clientInstances[instanceId] = clientCSDecorator( sdkClientFactory(objectAssign({}, params, { sdkReadinessManager: sharedSdkReadiness, - storage: sharedStorage, + storage: sharedStorage || storage, syncManager: sharedSyncManager, signalListener: undefined, // only the main client "destroy" method stops the signal listener sharedClient: true diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 10e30586..7de70f98 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -5,10 +5,10 @@ import { validateKey } from '../utils/inputValidation/key'; import { validateTrafficType } from '../utils/inputValidation/trafficType'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; -import { IStorageSyncCS } from '../storages/types'; import { ISyncManagerCS } from '../sync/types'; import objectAssign from 'object-assign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; +import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants'; function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) { // @ts-ignore @@ -73,11 +73,17 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - // 3 combinations are possible: - // - Standalone mode: both syncManager and storage.shared are defined - // - Consumer mode: both syncManager and storage.shared are undefined - // - Consumer mode with submitters: syncManager is defined but not storage.shared - const sharedStorage = (storage as IStorageSyncCS).shared && (storage as IStorageSyncCS).shared(matchingKey); + const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => { + // Emit SDK_READY in consumer mode for shared clients + if (err) return; // don't emit SDK_READY if storage failed to connect. + sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); + }); + + // 3 possibilities: + // - Standalone mode: both syncManager and sharedSyncManager are defined + // - Consumer mode: both syncManager and sharedSyncManager are undefined + // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined + // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); // As shared clients reuse all the storage information, we don't need to check here if we @@ -85,7 +91,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? clientInstances[instanceId] = clientCSDecorator( sdkClientFactory(objectAssign({}, params, { sdkReadinessManager: sharedSdkReadiness, - storage: sharedStorage, + storage: sharedStorage || storage, syncManager: sharedSyncManager, signalListener: undefined, // only the main client "destroy" method stops the signal listener sharedClient: true diff --git a/src/storages/inLocalStorage/index.ts b/src/storages/inLocalStorage/index.ts index 6587208b..795fd95b 100644 --- a/src/storages/inLocalStorage/index.ts +++ b/src/storages/inLocalStorage/index.ts @@ -1,7 +1,7 @@ import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; -import { IStorageFactoryParams, IStorageSyncCS, IStorageSyncFactory } from '../types'; +import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types'; import { validatePrefix } from '../KeyBuilder'; import KeyBuilderCS from '../KeyBuilderCS'; import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable'; @@ -25,7 +25,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn const prefix = validatePrefix(options.prefix); - function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { + function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSync { // Fallback to InMemoryStorage if LocalStorage API is not available if (!isLocalStorageAvailable()) { diff --git a/src/storages/inMemory/InMemoryStorageCS.ts b/src/storages/inMemory/InMemoryStorageCS.ts index d59e4c04..8f0eff19 100644 --- a/src/storages/inMemory/InMemoryStorageCS.ts +++ b/src/storages/inMemory/InMemoryStorageCS.ts @@ -2,7 +2,7 @@ import SplitsCacheInMemory from './SplitsCacheInMemory'; import MySegmentsCacheInMemory from './MySegmentsCacheInMemory'; import ImpressionsCacheInMemory from './ImpressionsCacheInMemory'; import EventsCacheInMemory from './EventsCacheInMemory'; -import { IStorageSyncCS, IStorageFactoryParams } from '../types'; +import { IStorageSync, IStorageFactoryParams } from '../types'; import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory'; import { STORAGE_MEMORY } from '../../utils/constants'; @@ -11,7 +11,7 @@ import { STORAGE_MEMORY } from '../../utils/constants'; * * @param params parameters required by EventsCacheSync */ -export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSyncCS { +export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync { return { splits: new SplitsCacheInMemory(), diff --git a/src/storages/pluggable/inMemoryWrapper.ts b/src/storages/pluggable/inMemoryWrapper.ts index efb2c6db..b72de2ff 100644 --- a/src/storages/pluggable/inMemoryWrapper.ts +++ b/src/storages/pluggable/inMemoryWrapper.ts @@ -6,10 +6,13 @@ import { ISet, setToArray, _Set } from '../../utils/lang/sets'; * Creates a ICustomStorageWrapper implementation that stores items in memory. * The `_cache` property is the object were items are stored. * Intended for testing purposes. + * + * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves inmediatelly. */ -export function inMemoryWrapperFactory(): ICustomStorageWrapper & { _cache: Record> } { +export function inMemoryWrapperFactory(connDelay?: number): ICustomStorageWrapper & { _cache: Record>, _setConnDelay(connDelay: number): void } { let _cache: Record> = {}; + let _connDelay = connDelay; return { /** Holds items (for key-value operations), list of items (for list operations) and set of items (for set operations) */ @@ -110,7 +113,18 @@ export function inMemoryWrapperFactory(): ICustomStorageWrapper & { _cache: Reco }, // always connects and close - connect() { return Promise.resolve(); }, + connect() { + if (typeof _connDelay === 'number') { + return new Promise(res => setTimeout(res, _connDelay)); + } else { + return Promise.resolve(); + } + }, close() { return Promise.resolve(); }, + + // for testing + _setConnDelay(connDelay: number) { + _connDelay = connDelay; + } }; } diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 724dd18a..4a592d6f 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -34,6 +34,25 @@ function validatePluggableStorageOptions(options: any) { if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`); } +// subscription to wrapper connect event in order to emit SDK_READY event +function wrapperConnect(wrapper: ICustomStorageWrapper, onReadyCb: (error?: any) => void) { + wrapper.connect().then(() => { + onReadyCb(); + }).catch((e) => { + onReadyCb(e || new Error('Error connecting wrapper')); + }); +} + +// For consumer partial mode to have async return type in `client.track` method +// No need to promisify impressions cache +function promisifyEventsTrack(events: any) { + const origTrack = events.track; + events.track = function () { + return Promise.resolve(origTrack.apply(this, arguments)); + }; + return events; +} + /** * Pluggable storage factory for consumer server-side & client-side SplitFactory. */ @@ -43,27 +62,33 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory, eventsQueueSize }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); - // subscription to Wrapper connect event in order to emit SDK_READY event - wrapper.connect().then(() => { - if (onReadyCb) onReadyCb(); - }).catch((e) => { - if (onReadyCb) onReadyCb(e || new Error('Error connecting wrapper')); - }); + // emit SDK_READY event on main client + wrapperConnect(wrapper, onReadyCb); return { splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), impressions: trackInMemory ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), - events: trackInMemory ? new EventsCacheInMemory() : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), + events: trackInMemory ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). destroy() { return wrapper.close(); + }, + + // emits SDK_READY event on shared clients and returns a reference to the storage + shared(_, onReadyCb) { + wrapperConnect(wrapper, onReadyCb); + return { + ...this, + // no-op destroy, to close the wrapper only when the main client is destroyed + destroy() { } + }; } }; } diff --git a/src/storages/types.ts b/src/storages/types.ts index e3e6f846..c3e5dfd9 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -4,7 +4,7 @@ import { StoredEventWithMetadata, StoredImpressionWithMetadata } from '../sync/s import { SplitIO, ImpressionDTO } from '../types'; /** - * Interface of a custom wrapper storage. + * Interface of a custom storage wrapper. */ export interface ICustomStorageWrapper { @@ -169,7 +169,7 @@ export interface ICustomStorageWrapper { /** * Connects to the underlying storage. * It is meant for storages that requires to be connected to some database or server. Otherwise it can just return a resolved promise. - * Note: will be called once on SplitFactory instantiation. + * Note: will be called once on SplitFactory instantiation and once per each shared client instantiation. * * @function connect * @returns {Promise} A promise that resolves when the wrapper successfully connect to the underlying storage. @@ -179,7 +179,7 @@ export interface ICustomStorageWrapper { /** * Disconnects the underlying storage. * It is meant for storages that requires to be closed, in order to release resources. Otherwise it can just return a resolved promise. - * Note: will be called once on SplitFactory client destroy. + * Note: will be called once on SplitFactory main client destroy. * * @function close * @returns {Promise} A promise that resolves when the operation ends. @@ -396,6 +396,7 @@ export interface IStorageBase< latencies?: TLatenciesCache, counts?: TCountsCache, destroy(): void, + shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this } export type IStorageSync = IStorageBase< @@ -407,11 +408,6 @@ export type IStorageSync = IStorageBase< ICountsCacheSync > -// @TODO remove shared method to unify storage interfaces -export interface IStorageSyncCS extends IStorageSync { - shared(matchingKey: string): IStorageSync -} - export type IStorageAsync = IStorageBase< ISplitsCacheAsync, ISegmentsCacheAsync, @@ -434,13 +430,13 @@ export interface IStorageFactoryParams { matchingKey?: string, /* undefined on server-side SDKs */ splitFiltersValidation?: ISplitFiltersValidation, - // ATM, only used by CustomStorage. true for partial consumer mode + // ATM, only used by CustomStorage. True for partial consumer mode trackInMemory?: boolean, // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, // it means that the storge fail to connect and shouldn't be used. // It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage. - onReadyCb?: (error?: any) => void, + onReadyCb: (error?: any) => void, metadata: IMetadata, } diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index c7d1ee7b..b793f845 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -1,4 +1,4 @@ -import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from './types'; +import { ISyncManagerCS, ISyncManagerFactoryParams } from './types'; import { submitterManagerFactory } from './submitters/submitterManager'; import { IReadinessManager } from '../readiness/types'; import { IStorageSync } from '../storages/types'; @@ -122,9 +122,10 @@ export function syncManagerOnlineFactory( else return Promise.resolve(); }, - // Only used for client-side in standalone mode: polling manager is defined and - // both polling and push managers implement the interfaces for client-side - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager { + // Only used for client-side in standalone mode: if polling and push managers + // are defined, they implement the interfaces for client-side + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync) { + if (!pollingManager) return; const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage); diff --git a/src/sync/types.ts b/src/sync/types.ts index ebb4c22e..d7349535 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -49,7 +49,7 @@ export interface ISyncManager extends ITask { } export interface ISyncManagerCS extends ISyncManager { - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined } export interface ISyncManagerFactoryParams { diff --git a/src/types.ts b/src/types.ts index 8b9b1288..c77bd861 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ import { IIntegration, IIntegrationFactoryParams } from './integrations/types'; import { ILogger } from './logger/types'; /* eslint-disable no-use-before-define */ -import { IStorageFactoryParams, IStorageSyncCS, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; +import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; /** @@ -842,7 +842,7 @@ export namespace SplitIO { * Defines which kind of storage we should instanciate. * @property {Object} storage */ - storage?: (params: IStorageFactoryParams) => IStorageSyncCS | IStorageAsync, + storage?: (params: IStorageFactoryParams) => IStorageSync | IStorageAsync, /** * List of URLs that the SDK will use as base for it's synchronization functionalities, applicable only when running as standalone. * Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy. diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index 19b5e29e..841ca959 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -3,9 +3,9 @@ import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; import { WARN_STORAGE_INVALID } from '../../../logger/constants'; import { LOCALHOST_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; -import { IStorageFactoryParams, IStorageSyncCS } from '../../../storages/types'; +import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; -export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSyncCS { +export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync { const result = InMemoryStorageCSFactory(params); result.splits.checkCache = () => true; // to emit SDK_READY_FROM_CACHE return result; From c5ad75c52e7e90eaba84f156062e9fcf26a43165 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 16:16:44 -0300 Subject: [PATCH 07/34] handling storage.destroy async --- src/sdkClient/sdkClient.ts | 6 +++--- src/storages/types.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sdkClient/sdkClient.ts b/src/sdkClient/sdkClient.ts index dd325dcb..e3051864 100644 --- a/src/sdkClient/sdkClient.ts +++ b/src/sdkClient/sdkClient.ts @@ -37,11 +37,11 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie sdkReadinessManager.readinessManager.destroy(); signalListener && signalListener.stop(); - // Cleanup storage - storage.destroy(); - // Release the API Key if it is the main client if (!sharedClient) releaseApiKey(settings.core.authorizationKey); + + // Cleanup storage + return storage.destroy(); }); } } diff --git a/src/storages/types.ts b/src/storages/types.ts index c3e5dfd9..5cf07b1a 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -395,7 +395,7 @@ export interface IStorageBase< events: TEventsCache, latencies?: TLatenciesCache, counts?: TCountsCache, - destroy(): void, + destroy(): void | Promise, shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this } From 7fe07ccbbd86a174ba8541f90f24af9fc0653de6 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 16:23:13 -0300 Subject: [PATCH 08/34] polishing --- src/sdkFactory/index.ts | 3 --- src/storages/pluggable/index.ts | 18 +++--------------- src/storages/types.ts | 3 --- src/types.ts | 3 +-- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 550952f8..e55db71b 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -41,9 +41,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. matchingKey: getMatching(settings.core.key), splitFiltersValidation: settings.sync.__splitFiltersValidation, - // ATM, only used by CustomStorage. true for partial consumer mode - trackInMemory: settings.sync.onlyImpressionsAndEvents, - // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined // or only instantiates submitters, and therefore it is not able to emit readiness events. onReadyCb: (error) => { diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 4a592d6f..e00f87a5 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -9,8 +9,6 @@ import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { validatePrefix } from '../KeyBuilder'; import { STORAGE_CUSTOM } from '../../utils/constants'; -import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; -import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; @@ -43,16 +41,6 @@ function wrapperConnect(wrapper: ICustomStorageWrapper, onReadyCb: (error?: any) }); } -// For consumer partial mode to have async return type in `client.track` method -// No need to promisify impressions cache -function promisifyEventsTrack(events: any) { - const origTrack = events.track; - events.track = function () { - return Promise.resolve(origTrack.apply(this, arguments)); - }; - return events; -} - /** * Pluggable storage factory for consumer server-side & client-side SplitFactory. */ @@ -62,7 +50,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb, trackInMemory, eventsQueueSize }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); @@ -72,8 +60,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn return { splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), - impressions: trackInMemory ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), - events: trackInMemory ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), + impressions: new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), + events: new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). diff --git a/src/storages/types.ts b/src/storages/types.ts index 5cf07b1a..c26bc936 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -430,9 +430,6 @@ export interface IStorageFactoryParams { matchingKey?: string, /* undefined on server-side SDKs */ splitFiltersValidation?: ISplitFiltersValidation, - // ATM, only used by CustomStorage. True for partial consumer mode - trackInMemory?: boolean, - // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, // it means that the storge fail to connect and shouldn't be used. // It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage. diff --git a/src/types.ts b/src/types.ts index c77bd861..c12ced90 100644 --- a/src/types.ts +++ b/src/types.ts @@ -101,8 +101,7 @@ export interface ISettings { splitFilters: SplitIO.SplitFilter[], impressionsMode: SplitIO.ImpressionsMode, __splitFiltersValidation: ISplitFiltersValidation, - localhostMode?: SplitIO.LocalhostFactory, - onlyImpressionsAndEvents?: boolean + localhostMode?: SplitIO.LocalhostFactory }, readonly runtime: { ip: string | false From 09689dcba0913cfdd54755a6f8855a54e3b8e1e2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 16:45:55 -0300 Subject: [PATCH 09/34] polishing --- src/sdkClient/sdkClientMethodCS.ts | 8 +--- src/sdkClient/sdkClientMethodCSWithTT.ts | 8 +--- src/sdkFactory/index.ts | 4 +- src/storages/types.ts | 4 +- src/sync/submitters/submitterManager.ts | 22 --------- src/sync/syncManagerOnline.ts | 60 +++++++++++++----------- src/sync/types.ts | 4 +- 7 files changed, 40 insertions(+), 70 deletions(-) delete mode 100644 src/sync/submitters/submitterManager.ts diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index 14de634d..b741c235 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -58,17 +58,11 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => { + if (err) return; // Emit SDK_READY in consumer mode for shared clients - if (err) return; // don't emit SDK_READY if storage failed to connect. sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }); - - // 3 possibilities: - // - Standalone mode: both syncManager and sharedSyncManager are defined - // - Consumer mode: both syncManager and sharedSyncManager are undefined - // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 7de70f98..82aa544d 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -72,17 +72,11 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? const matchingKey = getMatching(validKey); const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout); - const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => { + if (err) return; // Emit SDK_READY in consumer mode for shared clients - if (err) return; // don't emit SDK_READY if storage failed to connect. sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }); - - // 3 possibilities: - // - Standalone mode: both syncManager and sharedSyncManager are defined - // - Consumer mode: both syncManager and sharedSyncManager are undefined - // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index e55db71b..0c116895 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -33,7 +33,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. // @TODO consider passing the settings object, so that each storage access only what it needs const storageFactoryParams: IStorageFactoryParams = { - log, eventsQueueSize: settings.scheduler.eventsQueueSize, optimize: shouldBeOptimized(settings), @@ -48,7 +47,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. readinessManager.splits.emit(SDK_SPLITS_ARRIVED); readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }, - metadata: metadataBuilder(settings) + metadata: metadataBuilder(settings), + log }; const storage = storageFactory(storageFactoryParams); diff --git a/src/storages/types.ts b/src/storages/types.ts index c26bc936..b9c7b0e6 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -411,8 +411,8 @@ export type IStorageSync = IStorageBase< export type IStorageAsync = IStorageBase< ISplitsCacheAsync, ISegmentsCacheAsync, - IImpressionsCacheAsync | IImpressionsCacheSync, - IEventsCacheAsync | IEventsCacheSync, + IImpressionsCacheAsync, + IEventsCacheAsync, ILatenciesCacheAsync, ICountsCacheAsync > diff --git a/src/sync/submitters/submitterManager.ts b/src/sync/submitters/submitterManager.ts deleted file mode 100644 index 7828d947..00000000 --- a/src/sync/submitters/submitterManager.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { syncTaskComposite } from '../syncTaskComposite'; -import { eventsSyncTaskFactory } from './eventsSyncTask'; -import { impressionsSyncTaskFactory } from './impressionsSyncTask'; -import { impressionCountsSyncTaskFactory } from './impressionCountsSyncTask'; -import { ISplitApi } from '../../services/types'; -import { IStorageSync } from '../../storages/types'; -import { ISettings } from '../../types'; - -export function submitterManagerFactory( - settings: ISettings, - storage: IStorageSync, - splitApi: ISplitApi, -) { - const log = settings.log; - const submitters = [ - impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), - eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) - // @TODO add telemetry submitter - ]; - if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); - return syncTaskComposite(submitters); -} diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index b793f845..b0199a9c 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -1,5 +1,8 @@ -import { ISyncManagerCS, ISyncManagerFactoryParams } from './types'; -import { submitterManagerFactory } from './submitters/submitterManager'; +import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from './types'; +import { syncTaskComposite } from './syncTaskComposite'; +import { eventsSyncTaskFactory } from './submitters/eventsSyncTask'; +import { impressionsSyncTaskFactory } from './submitters/impressionsSyncTask'; +import { impressionCountsSyncTaskFactory } from './submitters/impressionCountsSyncTask'; import { IReadinessManager } from '../readiness/types'; import { IStorageSync } from '../storages/types'; import { IPushManagerFactoryParams, IPushManager, IPushManagerCS } from './streaming/types'; @@ -16,7 +19,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '.. * @param pushManagerFactory optional to build a SyncManager with or without streaming support */ export function syncManagerOnlineFactory( - pollingManagerFactory?: (...args: IPollingManagerFactoryParams) => IPollingManager, + pollingManagerFactory: (...args: IPollingManagerFactoryParams) => IPollingManager, pushManagerFactory?: (...args: IPushManagerFactoryParams) => IPushManager | undefined ): (params: ISyncManagerFactoryParams) => ISyncManagerCS { @@ -34,24 +37,30 @@ export function syncManagerOnlineFactory( const log = settings.log; /** Polling Manager */ - const pollingManager = pollingManagerFactory && pollingManagerFactory(splitApi, storage, readiness, settings); + const pollingManager = pollingManagerFactory(splitApi, storage, readiness, settings); /** Push Manager */ - const pushManager = settings.streamingEnabled && pollingManager && pushManagerFactory ? + const pushManager = settings.streamingEnabled && pushManagerFactory ? pushManagerFactory(pollingManager, storage, readiness, splitApi.fetchAuth, platform, settings) : undefined; /** Submitter Manager */ - // It is not inyected as push and polling managers, because at the moment it is mandatory - const submitter = submitterManagerFactory(settings, storage, splitApi); + // It is not inyected via a factory as push and polling managers, because at the moment it is mandatory and the same for server-side and client-side variants + const submitters = [ + impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), + eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) + // @TODO add telemetry submitter + ]; + if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); + const submitter = syncTaskComposite(submitters); /** Sync Manager logic */ function startPolling() { - if (!pollingManager!.isRunning()) { + if (!pollingManager.isRunning()) { log.info(SYNC_START_POLLING); - pollingManager!.start(); + pollingManager.start(); } else { log.info(SYNC_CONTINUE_POLLING); } @@ -60,10 +69,10 @@ export function syncManagerOnlineFactory( function stopPollingAndSyncAll() { log.info(SYNC_STOP_POLLING); // if polling, stop - if (pollingManager!.isRunning()) pollingManager!.stop(); + if (pollingManager.isRunning()) pollingManager.stop(); // fetch splits and segments. There is no need to catch this promise (it is always resolved) - pollingManager!.syncAll(); + pollingManager.syncAll(); } if (pushManager) { @@ -82,17 +91,15 @@ export function syncManagerOnlineFactory( */ start() { // start syncing splits and segments - if (pollingManager) { - if (pushManager) { - // Doesn't call `syncAll` when the syncManager is resuming - if (startFirstTime) { - pollingManager.syncAll(); - startFirstTime = false; - } - pushManager.start(); - } else { - pollingManager.start(); + if (pushManager) { + // Doesn't call `syncAll` when the syncManager is resuming + if (startFirstTime) { + pollingManager.syncAll(); + startFirstTime = false; } + pushManager.start(); + } else { + pollingManager.start(); } // start periodic data recording (events, impressions, telemetry). @@ -106,7 +113,7 @@ export function syncManagerOnlineFactory( stop() { // stop syncing if (pushManager) pushManager.stop(); - if (pollingManager && pollingManager.isRunning()) pollingManager.stop(); + if (pollingManager.isRunning()) pollingManager.stop(); // stop periodic data recording (events, impressions, telemetry). if (submitter) submitter.stop(); @@ -122,10 +129,9 @@ export function syncManagerOnlineFactory( else return Promise.resolve(); }, - // Only used for client-side in standalone mode: if polling and push managers - // are defined, they implement the interfaces for client-side - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync) { - if (!pollingManager) return; + // [Only used for client-side] + // It assumes that polling and push managers implement the interfaces for client-side + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager { const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage); @@ -133,7 +139,7 @@ export function syncManagerOnlineFactory( isRunning: mySegmentsSyncTask.isRunning, start() { if (pushManager) { - if (pollingManager!.isRunning()) { + if (pollingManager.isRunning()) { // if doing polling, we must start the periodic fetch of data if (storage.splits.usesSegments()) mySegmentsSyncTask.start(); } else { diff --git a/src/sync/types.ts b/src/sync/types.ts index d7349535..55566ef1 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -3,7 +3,6 @@ import { IPlatform } from '../sdkFactory/types'; import { ISplitApi } from '../services/types'; import { IStorageSync } from '../storages/types'; import { ISettings } from '../types'; -import { IPollingManager } from './polling/types'; import { IPushManager } from './streaming/types'; export interface ITask { @@ -44,12 +43,11 @@ export interface ITimeTracker { export interface ISyncManager extends ITask { flush(): Promise, - pollingManager?: IPollingManager pushManager?: IPushManager } export interface ISyncManagerCS extends ISyncManager { - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager } export interface ISyncManagerFactoryParams { From d87220348d824bf63100e2158d0860ca28cbea41 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 21:46:57 -0300 Subject: [PATCH 10/34] added some tests --- src/listeners/browser.ts | 2 +- src/storages/pluggable/__tests__/index.spec.ts | 11 +++++++++++ src/types.ts | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index 37429224..4ac90bfc 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -63,7 +63,7 @@ export default class BrowserSignalListener implements ISignalListener { * using beacon API if possible, or falling back to regular post transport. */ flushData() { - if (!this.syncManager) return; // In consumer mode there is not sync manager + if (!this.syncManager) return; // In consumer mode there is not sync manager and data to flush const eventsUrl = this.settings.urls.events; const extraMetadata = { diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 8356a3ad..10b0f043 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -31,13 +31,24 @@ describe('PLUGGABLE STORAGE', () => { assertStorageInterface(storage); // the instance must implement the storage interface expect(wrapperMock.connect).toBeCalledTimes(1); // wrapper connect method should be called once when storage is created + // shared storage has the same cache items than storage instance, but a no-op destroy + const sharedOnReadyCb = jest.fn(); + const sharedStorage = storage.shared('some_key', sharedOnReadyCb); + assertStorageInterface(sharedStorage); + expect(sharedStorage.splits).toBe(storage.splits); + expect(wrapperMock.connect).toBeCalledTimes(2); + expect(await storage.splits.getSplit('some_split')).toBe(null); + expect(await sharedStorage.splits.getSplit('some_split')).toBe(null); + expect(wrapperMock.get).toBeCalledTimes(2); expect(wrapperMock.get).toBeCalledWith(`${prefix}.SPLITIO.split.some_split`); // keys prefix should be the provided one await storage.destroy(); + await sharedStorage.destroy(); expect(wrapperMock.close).toBeCalledTimes(1); // wrapper close method should be called once when storage is destroyed expect(internalSdkParams.onReadyCb).toBeCalledTimes(1); // onReady callback should be called when the wrapper connect resolved with true + expect(sharedOnReadyCb).toBeCalledTimes(1); }); test('throws an exception if wrapper doesn\'t implement the expected interface', async () => { diff --git a/src/types.ts b/src/types.ts index c12ced90..a2641970 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ import { IIntegration, IIntegrationFactoryParams } from './integrations/types'; import { ILogger } from './logger/types'; /* eslint-disable no-use-before-define */ -import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory } from './storages/types'; +import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types'; import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; /** @@ -85,7 +85,7 @@ export interface ISettings { retriesOnFailureBeforeReady: number, eventsFirstPushWindow: number }, - readonly storage: IStorageSyncFactory, + readonly storage: IStorageSyncFactory | IStorageAsyncFactory, readonly integrations?: Array<(params: IIntegrationFactoryParams) => IIntegration | void>, readonly urls: { events: string, From a9774f3a81fc6c8eb6f5195cce0d6f2c48562038 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 10 Nov 2021 23:36:51 -0300 Subject: [PATCH 11/34] fixed listener test --- src/listeners/__tests__/browser.spec.ts | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/listeners/__tests__/browser.spec.ts b/src/listeners/__tests__/browser.spec.ts index 3bd83169..f232b482 100644 --- a/src/listeners/__tests__/browser.spec.ts +++ b/src/listeners/__tests__/browser.spec.ts @@ -105,8 +105,8 @@ function triggerUnloadEvent() { /* Mocks end */ -test('Browser JS listener / Impressions optimized mode', () => { - +test('Browser JS listener / consumer mode', () => { + // No SyncManager ==> consumer mode const listener = new BrowserSignalListener(undefined, fullSettings, fakeStorageOptimized as IStorageSync, fakeSplitApi); listener.start(); @@ -116,6 +116,33 @@ test('Browser JS listener / Impressions optimized mode', () => { triggerUnloadEvent(); + // Unload event was triggered, but sendBeacon and post services should not be called + expect(global.window.navigator.sendBeacon).toBeCalledTimes(0); + expect(fakeSplitApi.postTestImpressionsBulk).not.toBeCalled(); + expect(fakeSplitApi.postEventsBulk).not.toBeCalled(); + expect(fakeSplitApi.postTestImpressionsCount).not.toBeCalled(); + + // pre-check and call stop + expect(global.window.removeEventListener).not.toBeCalled(); + listener.stop(); + + // removed correct listener from correct signal on stop. + expect((global.window.removeEventListener as jest.Mock).mock.calls).toEqual([[UNLOAD_DOM_EVENT, listener.flushData]]); +}); + +test('Browser JS listener / standalone mode / Impressions optimized mode', () => { + const syncManagerMock = {}; + + // @ts-expect-error + const listener = new BrowserSignalListener(syncManagerMock, fullSettings, fakeStorageOptimized as IStorageSync, fakeSplitApi); + + listener.start(); + + // Assigned right function to right signal. + expect((global.window.addEventListener as jest.Mock).mock.calls).toEqual([[UNLOAD_DOM_EVENT, listener.flushData]]); + + triggerUnloadEvent(); + // Unload event was triggered. Thus sendBeacon method should have been called three times. expect(global.window.navigator.sendBeacon).toBeCalledTimes(3); @@ -132,7 +159,7 @@ test('Browser JS listener / Impressions optimized mode', () => { expect((global.window.removeEventListener as jest.Mock).mock.calls).toEqual([[UNLOAD_DOM_EVENT, listener.flushData]]); }); -test('Browser JS listener / Impressions debug mode', () => { +test('Browser JS listener / standalone mode / Impressions debug mode', () => { const syncManagerMockWithPushManager = { pushManager: { stop: jest.fn() } }; // @ts-expect-error @@ -166,7 +193,7 @@ test('Browser JS listener / Impressions debug mode', () => { expect((global.window.removeEventListener as jest.Mock).mock.calls).toEqual([[UNLOAD_DOM_EVENT, listener.flushData]]); }); -test('Browser JS listener / Impressions debug mode without sendBeacon API', () => { +test('Browser JS listener / standalone mode / Impressions debug mode without sendBeacon API', () => { // remove sendBeacon API const sendBeacon = global.navigator.sendBeacon; // @ts-expect-error global.navigator.sendBeacon = undefined; From bfe62da149cceb57ccbf88019fbf13d5f5bc87f7 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 11 Nov 2021 10:08:23 -0300 Subject: [PATCH 12/34] implementation and tests --- package-lock.json | 2 +- package.json | 2 +- .../__tests__/sdkClientMethod.spec.ts | 12 ++-- src/sdkClient/sdkClient.ts | 5 +- src/sdkClient/sdkClientMethodCS.ts | 5 ++ src/sdkClient/sdkClientMethodCSWithTT.ts | 5 ++ src/sdkFactory/index.ts | 3 + src/storages/__tests__/testUtils.ts | 9 ++- .../pluggable/__tests__/index.spec.ts | 22 ++++++- src/storages/pluggable/index.ts | 21 +++++-- src/storages/types.ts | 9 ++- src/sync/submitters/submitterManager.ts | 22 +++++++ src/sync/syncManagerOnline.ts | 58 +++++++++---------- src/sync/types.ts | 2 +- src/types.ts | 2 +- src/utils/constants/index.ts | 8 ++- src/utils/settingsValidation/mode.ts | 4 +- .../storage/__tests__/storageCS.spec.ts | 29 +++++++--- .../settingsValidation/storage/storageCS.ts | 13 ++++- 19 files changed, 165 insertions(+), 68 deletions(-) create mode 100644 src/sync/submitters/submitterManager.ts diff --git a/package-lock.json b/package-lock.json index f32024b1..f912ec47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.0", + "version": "1.0.1-rc.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 01e594a7..bd4a8851 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.0", + "version": "1.0.1-rc.0", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sdkClient/__tests__/sdkClientMethod.spec.ts b/src/sdkClient/__tests__/sdkClientMethod.spec.ts index 5adc1d88..604f20f6 100644 --- a/src/sdkClient/__tests__/sdkClientMethod.spec.ts +++ b/src/sdkClient/__tests__/sdkClientMethod.spec.ts @@ -8,11 +8,11 @@ const errorMessage = 'Shared Client not supported by the storage mechanism. Crea const paramMocks = [ // No SyncManager (i.e., Async SDK) and No signal listener { - storage: { destroy: jest.fn() }, + storage: { destroy: jest.fn(() => Promise.resolve()) }, syncManager: undefined, sdkReadinessManager: { sdkStatus: jest.fn(), readinessManager: { destroy: jest.fn() } }, signalListener: undefined, - settings: { mode: CONSUMER_MODE, log: loggerMock } + settings: { mode: CONSUMER_MODE, log: loggerMock, core: { authorizationKey: 'api key '} } }, // SyncManager (i.e., Sync SDK) and Signal listener { @@ -20,11 +20,11 @@ const paramMocks = [ syncManager: { stop: jest.fn(), flush: jest.fn(() => Promise.resolve()) }, sdkReadinessManager: { sdkStatus: jest.fn(), readinessManager: { destroy: jest.fn() } }, signalListener: { stop: jest.fn() }, - settings: { mode: STANDALONE_MODE, log: loggerMock } + settings: { mode: STANDALONE_MODE, log: loggerMock, core: { authorizationKey: 'api key '} } } ]; -test.each(paramMocks)('sdkClientMethodFactory', (params) => { +test.each(paramMocks)('sdkClientMethodFactory', (params, done: any) => { // @ts-expect-error const sdkClientMethod = sdkClientMethodFactory(params); @@ -38,7 +38,7 @@ test.each(paramMocks)('sdkClientMethodFactory', (params) => { // multiple calls should return the same instance expect(sdkClientMethod()).toBe(client); - // `client.destroy` method should stop internal components (other client methods where validated in `client.spec.ts`) + // `client.destroy` method should stop internal components (other client methods are validated in `client.spec.ts`) client.destroy().then(() => { expect(params.sdkReadinessManager.readinessManager.destroy).toBeCalledTimes(1); expect(params.storage.destroy).toBeCalledTimes(1); @@ -48,6 +48,8 @@ test.each(paramMocks)('sdkClientMethodFactory', (params) => { expect(params.syncManager.flush).toBeCalledTimes(1); } if (params.signalListener) expect(params.signalListener.stop).toBeCalledTimes(1); + + done(); }); // calling the function with parameters should throw an error diff --git a/src/sdkClient/sdkClient.ts b/src/sdkClient/sdkClient.ts index e3051864..635dd976 100644 --- a/src/sdkClient/sdkClient.ts +++ b/src/sdkClient/sdkClient.ts @@ -1,6 +1,6 @@ import objectAssign from 'object-assign'; import { IStatusInterface, SplitIO } from '../types'; -import { CONSUMER_MODE } from '../utils/constants'; +import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../utils/constants'; import { releaseApiKey } from '../utils/inputValidation/apiKey'; import clientFactory from './client'; import clientInputValidationDecorator from './clientInputValidation'; @@ -21,8 +21,7 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie settings.log, clientFactory(params), sdkReadinessManager.readinessManager, - // @TODO isStorageSync could be extracted from the storage itself (e.g. `storage.isSync`) to simplify interfaces - settings.mode === CONSUMER_MODE ? false : true, + [CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false, ), // Sdk destroy diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index b741c235..22254aaf 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -63,6 +63,11 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? // Emit SDK_READY in consumer mode for shared clients sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }); + + // 3 possibilities: + // - Standalone mode: both syncManager and sharedSyncManager are defined + // - Consumer mode: both syncManager and sharedSyncManager are undefined + // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 82aa544d..6b8fa9ef 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -77,6 +77,11 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key? // Emit SDK_READY in consumer mode for shared clients sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }); + + // 3 possibilities: + // - Standalone mode: both syncManager and sharedSyncManager are defined + // - Consumer mode: both syncManager and sharedSyncManager are undefined + // - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined // @ts-ignore const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage); diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 0c116895..55359017 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -40,6 +40,9 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. matchingKey: getMatching(settings.core.key), splitFiltersValidation: settings.sync.__splitFiltersValidation, + // ATM, only used by CustomStorage. true for partial consumer mode + mode: settings.mode, + // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined // or only instantiates submitters, and therefore it is not able to emit readiness events. onReadyCb: (error) => { diff --git a/src/storages/__tests__/testUtils.ts b/src/storages/__tests__/testUtils.ts index b09e9c1b..4905adb2 100644 --- a/src/storages/__tests__/testUtils.ts +++ b/src/storages/__tests__/testUtils.ts @@ -1,4 +1,4 @@ -import { IStorageSync, IStorageAsync } from '../types'; +import { IStorageSync, IStorageAsync, IImpressionsCacheSync, IEventsCacheSync } from '../types'; // Assert that instances created by storage factories have the expected interface export function assertStorageInterface(storage: IStorageSync | IStorageAsync) { @@ -12,6 +12,13 @@ export function assertStorageInterface(storage: IStorageSync | IStorageAsync) { expect(!storage.impressionCounts || typeof storage.impressionCounts === 'object').toBeTruthy; } +export function assertSyncRecorderCacheInterface(cache: IEventsCacheSync | IImpressionsCacheSync) { + expect(typeof cache.isEmpty).toBe('function'); + expect(typeof cache.clear).toBe('function'); + expect(typeof cache.state).toBe('function'); + expect(typeof cache.track).toBe('function'); +} + // Split mocks export const splitWithUserTT = '{ "trafficTypeName": "user_tt", "conditions": [] }'; diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 10b0f043..65bd7856 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -10,7 +10,8 @@ const prefix = 'some_prefix'; // Test target import { PluggableStorage } from '../index'; -import { assertStorageInterface } from '../../__tests__/testUtils'; +import { assertStorageInterface, assertSyncRecorderCacheInterface } from '../../__tests__/testUtils'; +import { CONSUMER_PARTIAL_MODE } from '../../../utils/constants'; describe('PLUGGABLE STORAGE', () => { @@ -70,4 +71,23 @@ describe('PLUGGABLE STORAGE', () => { expect(() => PluggableStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); }); + test('creates a storage instance for partial consumer mode (events and impressions cache in memory)', async () => { + const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock }); + const storage = storageFactory({ ...internalSdkParams, mode: CONSUMER_PARTIAL_MODE }); + + assertStorageInterface(storage); + expect(wrapperMock.connect).toBeCalledTimes(1); + + // Sync cache + assertSyncRecorderCacheInterface(storage.events); + assertSyncRecorderCacheInterface(storage.impressions); + + // But event track is async + const eventResult = storage.events.track('some data'); + expect(typeof eventResult.then === 'function').toBeTruthy(); + expect(await eventResult).toBe(true); + + const impResult = storage.impressions.track(['some data']); + expect(impResult).toBe(undefined); + }); }); diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index e00f87a5..fefb9cdc 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -8,7 +8,9 @@ import { EventsCachePluggable } from './EventsCachePluggable'; import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { validatePrefix } from '../KeyBuilder'; -import { STORAGE_CUSTOM } from '../../utils/constants'; +import { CONSUMER_PARTIAL_MODE, STORAGE_CUSTOM } from '../../utils/constants'; +import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; +import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; @@ -41,6 +43,16 @@ function wrapperConnect(wrapper: ICustomStorageWrapper, onReadyCb: (error?: any) }); } +// Async return type in `client.track` method on consumer partial mode +// No need to promisify impressions cache +function promisifyEventsTrack(events: any) { + const origTrack = events.track; + events.track = function () { + return Promise.resolve(origTrack.apply(this, arguments)); + }; + return events; +} + /** * Pluggable storage factory for consumer server-side & client-side SplitFactory. */ @@ -50,9 +62,10 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); + const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE; // emit SDK_READY event on main client wrapperConnect(wrapper, onReadyCb); @@ -60,8 +73,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn return { splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), - impressions: new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), - events: new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), + impressions: isPartialConsumer ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), + events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). diff --git a/src/storages/types.ts b/src/storages/types.ts index b9c7b0e6..05c6d651 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -1,7 +1,7 @@ import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types'; import { ILogger } from '../logger/types'; import { StoredEventWithMetadata, StoredImpressionWithMetadata } from '../sync/submitters/types'; -import { SplitIO, ImpressionDTO } from '../types'; +import { SplitIO, ImpressionDTO, SDKMode } from '../types'; /** * Interface of a custom storage wrapper. @@ -411,8 +411,8 @@ export type IStorageSync = IStorageBase< export type IStorageAsync = IStorageBase< ISplitsCacheAsync, ISegmentsCacheAsync, - IImpressionsCacheAsync, - IEventsCacheAsync, + IImpressionsCacheAsync | IImpressionsCacheSync, + IEventsCacheAsync | IEventsCacheSync, ILatenciesCacheAsync, ICountsCacheAsync > @@ -430,6 +430,9 @@ export interface IStorageFactoryParams { matchingKey?: string, /* undefined on server-side SDKs */ splitFiltersValidation?: ISplitFiltersValidation, + // ATM, only used by CustomStorage. True for partial consumer mode + mode?: SDKMode, + // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, // it means that the storge fail to connect and shouldn't be used. // It is meant for emitting SDK_READY event in consumer mode, and for synchronizer to wait before using the storage. diff --git a/src/sync/submitters/submitterManager.ts b/src/sync/submitters/submitterManager.ts new file mode 100644 index 00000000..7828d947 --- /dev/null +++ b/src/sync/submitters/submitterManager.ts @@ -0,0 +1,22 @@ +import { syncTaskComposite } from '../syncTaskComposite'; +import { eventsSyncTaskFactory } from './eventsSyncTask'; +import { impressionsSyncTaskFactory } from './impressionsSyncTask'; +import { impressionCountsSyncTaskFactory } from './impressionCountsSyncTask'; +import { ISplitApi } from '../../services/types'; +import { IStorageSync } from '../../storages/types'; +import { ISettings } from '../../types'; + +export function submitterManagerFactory( + settings: ISettings, + storage: IStorageSync, + splitApi: ISplitApi, +) { + const log = settings.log; + const submitters = [ + impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), + eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) + // @TODO add telemetry submitter + ]; + if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); + return syncTaskComposite(submitters); +} diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index b0199a9c..c8264106 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -1,8 +1,5 @@ -import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from './types'; -import { syncTaskComposite } from './syncTaskComposite'; -import { eventsSyncTaskFactory } from './submitters/eventsSyncTask'; -import { impressionsSyncTaskFactory } from './submitters/impressionsSyncTask'; -import { impressionCountsSyncTaskFactory } from './submitters/impressionCountsSyncTask'; +import { ISyncManagerCS, ISyncManagerFactoryParams } from './types'; +import { submitterManagerFactory } from './submitters/submitterManager'; import { IReadinessManager } from '../readiness/types'; import { IStorageSync } from '../storages/types'; import { IPushManagerFactoryParams, IPushManager, IPushManagerCS } from './streaming/types'; @@ -19,7 +16,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '.. * @param pushManagerFactory optional to build a SyncManager with or without streaming support */ export function syncManagerOnlineFactory( - pollingManagerFactory: (...args: IPollingManagerFactoryParams) => IPollingManager, + pollingManagerFactory?: (...args: IPollingManagerFactoryParams) => IPollingManager, pushManagerFactory?: (...args: IPushManagerFactoryParams) => IPushManager | undefined ): (params: ISyncManagerFactoryParams) => ISyncManagerCS { @@ -37,30 +34,24 @@ export function syncManagerOnlineFactory( const log = settings.log; /** Polling Manager */ - const pollingManager = pollingManagerFactory(splitApi, storage, readiness, settings); + const pollingManager = pollingManagerFactory && pollingManagerFactory(splitApi, storage, readiness, settings); /** Push Manager */ - const pushManager = settings.streamingEnabled && pushManagerFactory ? + const pushManager = settings.streamingEnabled && pollingManager && pushManagerFactory ? pushManagerFactory(pollingManager, storage, readiness, splitApi.fetchAuth, platform, settings) : undefined; /** Submitter Manager */ - // It is not inyected via a factory as push and polling managers, because at the moment it is mandatory and the same for server-side and client-side variants - const submitters = [ - impressionsSyncTaskFactory(log, splitApi.postTestImpressionsBulk, storage.impressions, settings.scheduler.impressionsRefreshRate, settings.core.labelsEnabled), - eventsSyncTaskFactory(log, splitApi.postEventsBulk, storage.events, settings.scheduler.eventsPushRate, settings.startup.eventsFirstPushWindow) - // @TODO add telemetry submitter - ]; - if (storage.impressionCounts) submitters.push(impressionCountsSyncTaskFactory(log, splitApi.postTestImpressionsCount, storage.impressionCounts)); - const submitter = syncTaskComposite(submitters); + // It is not inyected as push and polling managers, because at the moment it is required + const submitter = submitterManagerFactory(settings, storage, splitApi); /** Sync Manager logic */ function startPolling() { - if (!pollingManager.isRunning()) { + if (!pollingManager!.isRunning()) { log.info(SYNC_START_POLLING); - pollingManager.start(); + pollingManager!.start(); } else { log.info(SYNC_CONTINUE_POLLING); } @@ -69,10 +60,10 @@ export function syncManagerOnlineFactory( function stopPollingAndSyncAll() { log.info(SYNC_STOP_POLLING); // if polling, stop - if (pollingManager.isRunning()) pollingManager.stop(); + if (pollingManager!.isRunning()) pollingManager!.stop(); // fetch splits and segments. There is no need to catch this promise (it is always resolved) - pollingManager.syncAll(); + pollingManager!.syncAll(); } if (pushManager) { @@ -91,15 +82,17 @@ export function syncManagerOnlineFactory( */ start() { // start syncing splits and segments - if (pushManager) { - // Doesn't call `syncAll` when the syncManager is resuming - if (startFirstTime) { - pollingManager.syncAll(); - startFirstTime = false; + if (pollingManager) { + if (pushManager) { + // Doesn't call `syncAll` when the syncManager is resuming + if (startFirstTime) { + pollingManager.syncAll(); + startFirstTime = false; + } + pushManager.start(); + } else { + pollingManager.start(); } - pushManager.start(); - } else { - pollingManager.start(); } // start periodic data recording (events, impressions, telemetry). @@ -113,7 +106,7 @@ export function syncManagerOnlineFactory( stop() { // stop syncing if (pushManager) pushManager.stop(); - if (pollingManager.isRunning()) pollingManager.stop(); + if (pollingManager && pollingManager.isRunning()) pollingManager.stop(); // stop periodic data recording (events, impressions, telemetry). if (submitter) submitter.stop(); @@ -130,8 +123,9 @@ export function syncManagerOnlineFactory( }, // [Only used for client-side] - // It assumes that polling and push managers implement the interfaces for client-side - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager { + // If polling and push managers are defined (standalone mode), they implement the interfaces for client-side + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync) { + if (!pollingManager) return; const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage); @@ -139,7 +133,7 @@ export function syncManagerOnlineFactory( isRunning: mySegmentsSyncTask.isRunning, start() { if (pushManager) { - if (pollingManager.isRunning()) { + if (pollingManager!.isRunning()) { // if doing polling, we must start the periodic fetch of data if (storage.splits.usesSegments()) mySegmentsSyncTask.start(); } else { diff --git a/src/sync/types.ts b/src/sync/types.ts index 55566ef1..0f0588d9 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -47,7 +47,7 @@ export interface ISyncManager extends ITask { } export interface ISyncManagerCS extends ISyncManager { - shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager + shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined } export interface ISyncManagerFactoryParams { diff --git a/src/types.ts b/src/types.ts index a2641970..94e6d461 100644 --- a/src/types.ts +++ b/src/types.ts @@ -52,7 +52,7 @@ type EventConsts = { * SDK Modes. * @typedef {string} SDKMode */ -export type SDKMode = 'standalone' | 'consumer' | 'localhost'; +export type SDKMode = 'standalone' | 'consumer' | 'localhost' | 'consumer_partial'; /** * Settings interface. This is a representation of the settings the SDK expose, that's why * most of it's props are readonly. Only features should be rewritten when localhost mode is active. diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index 51bcafca..e632938f 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -1,4 +1,5 @@ import { StorageType } from '../../storages/types'; +import { SDKMode } from '../../types'; // Special treatments export const CONTROL = 'control'; @@ -20,10 +21,11 @@ export const DEBUG = 'DEBUG'; export const OPTIMIZED = 'OPTIMIZED'; // SDK Modes -export const LOCALHOST_MODE = 'localhost'; -export const STANDALONE_MODE = 'standalone'; +export const LOCALHOST_MODE: SDKMode = 'localhost'; +export const STANDALONE_MODE: SDKMode = 'standalone'; export const PRODUCER_MODE = 'producer'; -export const CONSUMER_MODE = 'consumer'; +export const CONSUMER_MODE: SDKMode = 'consumer'; +export const CONSUMER_PARTIAL_MODE: SDKMode = 'consumer_partial'; // Storage types export const STORAGE_MEMORY: StorageType = 'MEMORY'; diff --git a/src/utils/settingsValidation/mode.ts b/src/utils/settingsValidation/mode.ts index 0b2464b2..dd11fe06 100644 --- a/src/utils/settingsValidation/mode.ts +++ b/src/utils/settingsValidation/mode.ts @@ -1,10 +1,10 @@ -import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE } from '../constants'; +import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants'; export default function mode(key: string, mode: string) { // Leaving the comparison as is, in case we change the mode name but not the setting. if (key === 'localhost') return LOCALHOST_MODE; - if ([STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE].indexOf(mode) === -1) throw Error('Invalid mode provided'); + if ([STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(mode) === -1) throw Error('Invalid mode provided'); return mode; } diff --git a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts index 72456b1f..c2dbc9eb 100644 --- a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts +++ b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts @@ -5,6 +5,9 @@ import { loggerMock as log } from '../../../../logger/__tests__/sdkLogger.mock'; const mockInLocalStorageFactory = () => { }; mockInLocalStorageFactory.type = 'LOCALSTORAGE'; +const mockCustomStorageFactory = () => { }; +mockCustomStorageFactory.type = 'CUSTOM'; + describe('storage validator for pluggable storage (client-side)', () => { afterEach(() => { @@ -13,24 +16,34 @@ describe('storage validator for pluggable storage (client-side)', () => { // Check different types, since `storage` param is defined by the user test('fallbacks to default InMemory storage if the storage is invalid or not provided', () => { - expect(validateStorageCS({ log })).toBe(InMemoryStorageCSFactory); - expect(validateStorageCS({ log, storage: undefined })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone' })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: undefined })).toBe(InMemoryStorageCSFactory); expect(log.warn).not.toBeCalled(); - expect(validateStorageCS({ log, storage: {} })).toBe(InMemoryStorageCSFactory); - expect(validateStorageCS({ log, storage: 'invalid storage' })).toBe(InMemoryStorageCSFactory); - expect(validateStorageCS({ log, storage: () => { } })).toBe(InMemoryStorageCSFactory); - expect(validateStorageCS({ log, storage: () => { }, mode: 'localhost' })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: {} })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: 'invalid storage' })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: () => { } })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'localhost', storage: () => { } })).toBe(InMemoryStorageCSFactory); expect(log.warn).toBeCalledTimes(4); }); test('returns the provided storage factory if it is valid', () => { - expect(validateStorageCS({ log, storage: mockInLocalStorageFactory })).toBe(mockInLocalStorageFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: mockInLocalStorageFactory })).toBe(mockInLocalStorageFactory); expect(log.warn).not.toBeCalled(); }); test('fallbacks to mock InLocalStorage storage if the storage is InLocalStorage and the mode localhost', () => { - expect(validateStorageCS({ log, storage: mockInLocalStorageFactory, mode: 'localhost' })).toBe(__InLocalStorageMockFactory); + expect(validateStorageCS({ log, mode: 'localhost', storage: mockInLocalStorageFactory })).toBe(__InLocalStorageMockFactory); + expect(log.warn).not.toBeCalled(); + }); + + test('throws error if the provided storage factory is not compatible with the mode', () => { + expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer modes'); + expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer modes'); + + expect(() => { validateStorageCS({ log, mode: 'standalone', storage: mockCustomStorageFactory }); }).toThrow('A CustomStorage instance cannot be used on standalone and localhost modes'); + expect(() => { validateStorageCS({ log, mode: 'localhost', storage: mockCustomStorageFactory }); }).toThrow('A CustomStorage instance cannot be used on standalone and localhost modes'); + expect(log.warn).not.toBeCalled(); }); diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index 841ca959..ad5d42fe 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -2,7 +2,7 @@ import { InMemoryStorageCSFactory } from '../../../storages/inMemory/InMemorySto import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; import { WARN_STORAGE_INVALID } from '../../../logger/constants'; -import { LOCALHOST_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; +import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync { @@ -19,7 +19,7 @@ __InLocalStorageMockFactory.type = STORAGE_MEMORY; * * @returns {Object} valid storage factory. It might be the default `InMemoryStorageCSFactory` if the provided storage is invalid. */ -export function validateStorageCS(settings: { log: ILogger, storage?: any, mode?: SDKMode }): ISettings['storage'] { +export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: SDKMode }): ISettings['storage'] { let { storage = InMemoryStorageCSFactory, log, mode } = settings; // If an invalid storage is provided, fallback into MEMORY @@ -33,6 +33,15 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode? return __InLocalStorageMockFactory; } + // @TODO check behaviour + if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) { + // Consumer modes require an async storage + if (storage.type !== STORAGE_CUSTOM) throw new Error('A CustomStorage instance is required on consumer modes'); + } else { + // Standalone and localhost modes require a sync storage + if (storage.type === STORAGE_CUSTOM) throw new Error('A CustomStorage instance cannot be used on standalone and localhost modes'); + } + // return default InMemory storage if provided one is not valid return storage; } From e7918dd65fd163495e3e8b8534c9ce3439ad6d53 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 11 Nov 2021 10:25:52 -0300 Subject: [PATCH 13/34] added comment --- src/sdkClient/sdkClient.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdkClient/sdkClient.ts b/src/sdkClient/sdkClient.ts index 635dd976..e0401347 100644 --- a/src/sdkClient/sdkClient.ts +++ b/src/sdkClient/sdkClient.ts @@ -21,6 +21,7 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie settings.log, clientFactory(params), sdkReadinessManager.readinessManager, + // storage is async if and only if mode is consumer or partial consumer [CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false, ), From ee3b142e314131c95ba71c2c57c31181f0ff8033 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 12 Nov 2021 10:02:14 -0300 Subject: [PATCH 14/34] impressions dedup --- package-lock.json | 2 +- package.json | 2 +- src/storages/pluggable/__tests__/index.spec.ts | 3 ++- src/storages/pluggable/index.ts | 4 +++- src/trackers/impressionObserver/utils.ts | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f912ec47..7f35f186 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.0", + "version": "1.0.1-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bd4a8851..75501e66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.0", + "version": "1.0.1-rc.1", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 65bd7856..2ee8bc70 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -73,7 +73,7 @@ describe('PLUGGABLE STORAGE', () => { test('creates a storage instance for partial consumer mode (events and impressions cache in memory)', async () => { const storageFactory = PluggableStorage({ prefix, wrapper: wrapperMock }); - const storage = storageFactory({ ...internalSdkParams, mode: CONSUMER_PARTIAL_MODE }); + const storage = storageFactory({ ...internalSdkParams, mode: CONSUMER_PARTIAL_MODE, optimize: true }); assertStorageInterface(storage); expect(wrapperMock.connect).toBeCalledTimes(1); @@ -81,6 +81,7 @@ describe('PLUGGABLE STORAGE', () => { // Sync cache assertSyncRecorderCacheInterface(storage.events); assertSyncRecorderCacheInterface(storage.impressions); + assertSyncRecorderCacheInterface(storage.impressionCounts); // But event track is async const eventResult = storage.events.track('some data'); diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index fefb9cdc..9f4c3b30 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -11,6 +11,7 @@ import { validatePrefix } from '../KeyBuilder'; import { CONSUMER_PARTIAL_MODE, STORAGE_CUSTOM } from '../../utils/constants'; import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; +import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; @@ -62,7 +63,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const prefix = validatePrefix(options.prefix); - function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize }: IStorageFactoryParams): IStorageAsync { + function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync { const keys = new KeyBuilderSS(prefix, metadata); const wrapper = wrapperAdapter(log, options.wrapper); const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE; @@ -74,6 +75,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn splits: new SplitsCachePluggable(log, keys, wrapper), segments: new SegmentsCachePluggable(log, keys, wrapper), impressions: isPartialConsumer ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata), + impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined, events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required diff --git a/src/trackers/impressionObserver/utils.ts b/src/trackers/impressionObserver/utils.ts index 3b921a8f..7fb73b85 100644 --- a/src/trackers/impressionObserver/utils.ts +++ b/src/trackers/impressionObserver/utils.ts @@ -1,11 +1,11 @@ -import { OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants'; +import { CONSUMER_PARTIAL_MODE, OPTIMIZED, PRODUCER_MODE, STANDALONE_MODE } from '../../utils/constants'; import { ISettings } from '../../types'; /** * Checks if impressions previous time should be added or not. */ export function shouldAddPt(settings: ISettings) { - return [PRODUCER_MODE, STANDALONE_MODE].indexOf(settings.mode) > -1 ? true : false; + return [PRODUCER_MODE, STANDALONE_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) > -1 ? true : false; } /** From 1e1b3be45f35f93b47170e8527f46e525b682447 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 12 Nov 2021 15:59:52 -0300 Subject: [PATCH 15/34] handling log errors in storage validation --- src/logger/constants.ts | 6 ++--- src/logger/messages/error.ts | 3 ++- src/logger/messages/warn.ts | 1 - .../storage/__tests__/storageCS.spec.ts | 22 ++++++++++--------- .../settingsValidation/storage/storageCS.ts | 16 +++++++++----- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/logger/constants.ts b/src/logger/constants.ts index 5ca7317c..ab711cac 100644 --- a/src/logger/constants.ts +++ b/src/logger/constants.ts @@ -91,9 +91,8 @@ export const WARN_INTEGRATION_INVALID = 218; export const WARN_SPLITS_FILTER_IGNORED = 219; export const WARN_SPLITS_FILTER_INVALID = 220; export const WARN_SPLITS_FILTER_EMPTY = 221; -export const WARN_STORAGE_INVALID = 222; -export const WARN_API_KEY = 223; -export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 224; +export const WARN_API_KEY = 222; +export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223; export const ERROR_ENGINE_COMBINER_IFELSEIF = 300; export const ERROR_LOGLEVEL_INVALID = 301; @@ -119,6 +118,7 @@ export const ERROR_EMPTY_ARRAY = 320; export const ERROR_INVALID_IMPRESSIONS_MODE = 321; export const ERROR_HTTP = 322; export const ERROR_LOCALHOST_MODULE_REQUIRED = 323; +export const ERROR_STORAGE_INVALID = 324; // Log prefixes (a.k.a. tags or categories) export const LOG_PREFIX_SETTINGS = 'settings'; diff --git a/src/logger/messages/error.ts b/src/logger/messages/error.ts index 75269bc9..2ce2d3a6 100644 --- a/src/logger/messages/error.ts +++ b/src/logger/messages/error.ts @@ -30,5 +30,6 @@ export const codesError: [number, string][] = [ [c.ERROR_EMPTY_ARRAY, '%s: %s must be a non-empty array.'], // initialization / settings validation [c.ERROR_INVALID_IMPRESSIONS_MODE, c.LOG_PREFIX_SETTINGS + ': you passed an invalid "impressionsMode". It should be one of the following values: %s. Defaulting to "%s" mode.'], - [c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.'] + [c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.'], + [c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid. Fallbacking into default MEMORY storage%s'], ]; diff --git a/src/logger/messages/warn.ts b/src/logger/messages/warn.ts index 350eeb43..8c35e311 100644 --- a/src/logger/messages/warn.ts +++ b/src/logger/messages/warn.ts @@ -29,7 +29,6 @@ export const codesWarn: [number, string][] = codesError.concat([ [c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+': split filters have been configured but will have no effect if mode is not "%s", since synchronization is being deferred to an external tool.'], [c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS+': split filter at position %s is invalid. It must be an object with a valid filter type ("byName" or "byPrefix") and a list of "values".'], [c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS+': splitFilters configuration must be a non-empty array of filter objects.'], - [c.WARN_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid. Fallbacking into default MEMORY storage'], [c.WARN_API_KEY, c.LOG_PREFIX_SETTINGS+': You already have %s. We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing it throughout your application'], [c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'], diff --git a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts index c2dbc9eb..6d270946 100644 --- a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts +++ b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts @@ -11,40 +11,42 @@ mockCustomStorageFactory.type = 'CUSTOM'; describe('storage validator for pluggable storage (client-side)', () => { afterEach(() => { - log.warn.mockClear(); + log.error.mockClear(); }); // Check different types, since `storage` param is defined by the user test('fallbacks to default InMemory storage if the storage is invalid or not provided', () => { expect(validateStorageCS({ log, mode: 'standalone' })).toBe(InMemoryStorageCSFactory); expect(validateStorageCS({ log, mode: 'standalone', storage: undefined })).toBe(InMemoryStorageCSFactory); - expect(log.warn).not.toBeCalled(); + expect(log.error).not.toBeCalled(); expect(validateStorageCS({ log, mode: 'standalone', storage: {} })).toBe(InMemoryStorageCSFactory); expect(validateStorageCS({ log, mode: 'standalone', storage: 'invalid storage' })).toBe(InMemoryStorageCSFactory); expect(validateStorageCS({ log, mode: 'standalone', storage: () => { } })).toBe(InMemoryStorageCSFactory); expect(validateStorageCS({ log, mode: 'localhost', storage: () => { } })).toBe(InMemoryStorageCSFactory); - expect(log.warn).toBeCalledTimes(4); + expect(log.error).toBeCalledTimes(4); }); test('returns the provided storage factory if it is valid', () => { expect(validateStorageCS({ log, mode: 'standalone', storage: mockInLocalStorageFactory })).toBe(mockInLocalStorageFactory); - expect(log.warn).not.toBeCalled(); + expect(log.error).not.toBeCalled(); }); test('fallbacks to mock InLocalStorage storage if the storage is InLocalStorage and the mode localhost', () => { expect(validateStorageCS({ log, mode: 'localhost', storage: mockInLocalStorageFactory })).toBe(__InLocalStorageMockFactory); - expect(log.warn).not.toBeCalled(); + expect(log.error).not.toBeCalled(); }); test('throws error if the provided storage factory is not compatible with the mode', () => { - expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer modes'); - expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer modes'); + expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer mode'); + expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer mode'); - expect(() => { validateStorageCS({ log, mode: 'standalone', storage: mockCustomStorageFactory }); }).toThrow('A CustomStorage instance cannot be used on standalone and localhost modes'); - expect(() => { validateStorageCS({ log, mode: 'localhost', storage: mockCustomStorageFactory }); }).toThrow('A CustomStorage instance cannot be used on standalone and localhost modes'); + expect(log.error).not.toBeCalled(); - expect(log.warn).not.toBeCalled(); + expect(validateStorageCS({ log, mode: 'standalone', storage: mockCustomStorageFactory })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'localhost', storage: mockCustomStorageFactory })).toBe(InMemoryStorageCSFactory); + + expect(log.error).toBeCalledTimes(2); }); }); diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index ad5d42fe..dc47fb6a 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -1,7 +1,7 @@ import { InMemoryStorageCSFactory } from '../../../storages/inMemory/InMemoryStorageCS'; import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; -import { WARN_STORAGE_INVALID } from '../../../logger/constants'; +import { ERROR_STORAGE_INVALID } from '../../../logger/constants'; import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; @@ -17,7 +17,9 @@ __InLocalStorageMockFactory.type = STORAGE_MEMORY; * * @param {any} settings config object provided by the user to initialize the sdk * - * @returns {Object} valid storage factory. It might be the default `InMemoryStorageCSFactory` if the provided storage is invalid. + * @returns {Object} valid storage factory. Default to `InMemoryStorageCSFactory` if the provided storage is invalid or not compatible with the sdk mode if mode is standalone or localhost + * + * @throws error if mode is consumer and the provided storage is not compatible */ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: SDKMode }): ISettings['storage'] { let { storage = InMemoryStorageCSFactory, log, mode } = settings; @@ -25,7 +27,7 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: // If an invalid storage is provided, fallback into MEMORY if (typeof storage !== 'function' || [STORAGE_MEMORY, STORAGE_LOCALSTORAGE, STORAGE_CUSTOM].indexOf(storage.type) === -1) { storage = InMemoryStorageCSFactory; - log.warn(WARN_STORAGE_INVALID); + log.error(ERROR_STORAGE_INVALID); } // In localhost mode with InLocalStorage, fallback to a mock InLocalStorage to emit SDK_READY_FROM_CACHE @@ -33,13 +35,15 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: return __InLocalStorageMockFactory; } - // @TODO check behaviour if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) { // Consumer modes require an async storage - if (storage.type !== STORAGE_CUSTOM) throw new Error('A CustomStorage instance is required on consumer modes'); + if (storage.type !== STORAGE_CUSTOM) throw new Error('A CustomStorage instance is required on consumer mode'); } else { // Standalone and localhost modes require a sync storage - if (storage.type === STORAGE_CUSTOM) throw new Error('A CustomStorage instance cannot be used on standalone and localhost modes'); + if (storage.type === STORAGE_CUSTOM) { + storage = InMemoryStorageCSFactory; + log.error(ERROR_STORAGE_INVALID, ['. The provided storage requires to set consumer mode']); + } } // return default InMemory storage if provided one is not valid From e655d718c163dd417aafb062f3a5c97763c4ea8e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 12 Nov 2021 16:21:58 -0300 Subject: [PATCH 16/34] polishing --- src/logger/messages/error.ts | 2 +- src/utils/settingsValidation/storage/storageCS.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logger/messages/error.ts b/src/logger/messages/error.ts index 2ce2d3a6..fee6c423 100644 --- a/src/logger/messages/error.ts +++ b/src/logger/messages/error.ts @@ -31,5 +31,5 @@ export const codesError: [number, string][] = [ // initialization / settings validation [c.ERROR_INVALID_IMPRESSIONS_MODE, c.LOG_PREFIX_SETTINGS + ': you passed an invalid "impressionsMode". It should be one of the following values: %s. Defaulting to "%s" mode.'], [c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.'], - [c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid. Fallbacking into default MEMORY storage%s'], + [c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid.%s Fallbacking into default MEMORY storage'], ]; diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index dc47fb6a..3dadb31a 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -42,7 +42,7 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: // Standalone and localhost modes require a sync storage if (storage.type === STORAGE_CUSTOM) { storage = InMemoryStorageCSFactory; - log.error(ERROR_STORAGE_INVALID, ['. The provided storage requires to set consumer mode']); + log.error(ERROR_STORAGE_INVALID, [' It requires consumer mode.']); } } From 510b03a7937dd04a8001081c4e1cd9b95fda5a5f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 17 Nov 2021 11:33:59 -0300 Subject: [PATCH 17/34] polishing --- src/sdkFactory/index.ts | 2 +- src/services/splitApi.ts | 5 ++++- src/storages/inRedis/index.ts | 2 +- src/storages/pluggable/index.ts | 2 +- src/storages/types.ts | 2 +- src/trackers/impressionObserver/ImpressionObserver.ts | 2 +- src/types.ts | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 55359017..136cc064 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -40,7 +40,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. matchingKey: getMatching(settings.core.key), splitFiltersValidation: settings.sync.__splitFiltersValidation, - // ATM, only used by CustomStorage. true for partial consumer mode + // ATM, only used by CustomStorage mode: settings.mode, // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined diff --git a/src/services/splitApi.ts b/src/services/splitApi.ts index a080be33..a1856a8e 100644 --- a/src/services/splitApi.ts +++ b/src/services/splitApi.ts @@ -16,7 +16,10 @@ function userKeyToQueryParam(userKey: string) { * @param settings validated settings object * @param platform object containing environment-specific `getFetch` and `getOptions` dependencies */ -export function splitApiFactory(settings: ISettings, platform: Pick): ISplitApi { +export function splitApiFactory( + settings: Pick, + platform: Pick +): ISplitApi { const urls = settings.urls; const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString; diff --git a/src/storages/inRedis/index.ts b/src/storages/inRedis/index.ts index b13413ee..68b0a85b 100644 --- a/src/storages/inRedis/index.ts +++ b/src/storages/inRedis/index.ts @@ -30,7 +30,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy // subscription to Redis connect event in order to emit SDK_READY event on consumer mode redisClient.on('connect', () => { - if (onReadyCb) onReadyCb(); + onReadyCb(); }); return { diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 9f4c3b30..e1ddaf67 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -68,7 +68,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn const wrapper = wrapperAdapter(log, options.wrapper); const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE; - // emit SDK_READY event on main client + // Connects to wrapper and emits SDK_READY event on main client wrapperConnect(wrapper, onReadyCb); return { diff --git a/src/storages/types.ts b/src/storages/types.ts index 05c6d651..5fdb3d31 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -430,7 +430,7 @@ export interface IStorageFactoryParams { matchingKey?: string, /* undefined on server-side SDKs */ splitFiltersValidation?: ISplitFiltersValidation, - // ATM, only used by CustomStorage. True for partial consumer mode + // ATM, only used by CustomStorage mode?: SDKMode, // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, diff --git a/src/trackers/impressionObserver/ImpressionObserver.ts b/src/trackers/impressionObserver/ImpressionObserver.ts index 666fa463..8ec9d260 100644 --- a/src/trackers/impressionObserver/ImpressionObserver.ts +++ b/src/trackers/impressionObserver/ImpressionObserver.ts @@ -2,7 +2,7 @@ import { ImpressionDTO } from '../../types'; import LRUCache from '../../utils/LRUCache'; import { IImpressionObserver } from './types'; -export default class ImpressionObserver implements IImpressionObserver { +export default class ImpressionObserver implements IImpressionObserver { private cache: LRUCache; private hasher: (impression: ImpressionDTO) => K; diff --git a/src/types.ts b/src/types.ts index 94e6d461..c7fbc62b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -93,7 +93,7 @@ export interface ISettings { auth: string, streaming: string }, - readonly debug: boolean | LogLevel, + readonly debug: boolean | LogLevel | ILogger, readonly version: string, features: SplitIO.MockedFeaturesFilePath | SplitIO.MockedFeaturesMap, readonly streamingEnabled: boolean, From 133d0573745bc3f0e4d60ff3e5c07957322cc659 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 17 Nov 2021 18:00:58 -0300 Subject: [PATCH 18/34] renamed wrapper.close to wrapper.disconnect --- src/storages/pluggable/__tests__/index.spec.ts | 4 ++-- src/storages/pluggable/__tests__/wrapper.mock.ts | 6 +++--- src/storages/pluggable/__tests__/wrapperAdapter.spec.ts | 4 ++-- src/storages/pluggable/inMemoryWrapper.ts | 4 ++-- src/storages/pluggable/index.ts | 6 +++--- src/storages/pluggable/wrapperAdapter.ts | 2 +- src/storages/types.ts | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 2ee8bc70..34a95fca 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -46,7 +46,7 @@ describe('PLUGGABLE STORAGE', () => { await storage.destroy(); await sharedStorage.destroy(); - expect(wrapperMock.close).toBeCalledTimes(1); // wrapper close method should be called once when storage is destroyed + expect(wrapperMock.disconnect).toBeCalledTimes(1); // wrapper disconnect method should be called once when storage is destroyed expect(internalSdkParams.onReadyCb).toBeCalledTimes(1); // onReady callback should be called when the wrapper connect resolved with true expect(sharedOnReadyCb).toBeCalledTimes(1); @@ -65,7 +65,7 @@ describe('PLUGGABLE STORAGE', () => { // Throws exception if the given object is not a valid wrapper, informing which methods are missing const invalidWrapper = wrapperMockFactory(); invalidWrapper.connect = undefined; - invalidWrapper.close = 'invalid function'; + invalidWrapper.disconnect = 'invalid function'; const errorNoValidWrapperInterface = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; expect(() => PluggableStorage({ wrapper: invalidWrapper })).toThrow(errorNoValidWrapperInterface); expect(() => PluggableStorage({ wrapper: {} })).toThrow(errorNoValidWrapperInterface); diff --git a/src/storages/pluggable/__tests__/wrapper.mock.ts b/src/storages/pluggable/__tests__/wrapper.mock.ts index da4b2122..3301ef5c 100644 --- a/src/storages/pluggable/__tests__/wrapper.mock.ts +++ b/src/storages/pluggable/__tests__/wrapper.mock.ts @@ -103,9 +103,9 @@ export function wrapperMockFactory() { return Promise.reject('key is not a set'); }), - // always connects and close + // always connects and disconnects connect: jest.fn(() => Promise.resolve()), - close: jest.fn(() => Promise.resolve()), + disconnect: jest.fn(() => Promise.resolve()), mockClear() { this._cache = {}; @@ -117,7 +117,7 @@ export function wrapperMockFactory() { this.decr.mockClear(); this.getMany.mockClear(); this.connect.mockClear(); - this.close.mockClear(); + this.disconnect.mockClear(); this.getAndSet.mockClear(); this.pushItems.mockClear(); this.popItems.mockClear(); diff --git a/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts b/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts index dd081b6f..366ad1e4 100644 --- a/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts +++ b/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts @@ -28,7 +28,7 @@ export const wrapperWithIssues = { decr: 'invalid value', getMany: throwsException, connect: rejectedPromise, - close: invalidThenable, + disconnect: invalidThenable, getAndSet: 'invalid value', pushItems: rejectedPromise, popItems: invalidThenable, @@ -48,7 +48,7 @@ const VALID_METHOD_CALLS = { 'decr': ['some_key'], 'getMany': [['some_key_1', 'some_key_2']], 'connect': [], - 'close': [], + 'disconnect': [], 'getAndSet': ['some_key', 'some_value'], 'pushItems': ['some_key_list', ['item1', 'item2']], 'popItems': ['some_key_list'], diff --git a/src/storages/pluggable/inMemoryWrapper.ts b/src/storages/pluggable/inMemoryWrapper.ts index b72de2ff..3f050a16 100644 --- a/src/storages/pluggable/inMemoryWrapper.ts +++ b/src/storages/pluggable/inMemoryWrapper.ts @@ -112,7 +112,7 @@ export function inMemoryWrapperFactory(connDelay?: number): ICustomStorageWrappe return Promise.reject('key is not a set'); }, - // always connects and close + // always connects and disconnects connect() { if (typeof _connDelay === 'number') { return new Promise(res => setTimeout(res, _connDelay)); @@ -120,7 +120,7 @@ export function inMemoryWrapperFactory(connDelay?: number): ICustomStorageWrappe return Promise.resolve(); } }, - close() { return Promise.resolve(); }, + disconnect() { return Promise.resolve(); }, // for testing _setConnDelay(connDelay: number) { diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index e1ddaf67..f0d202f1 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -79,9 +79,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata), // @TODO add telemetry cache when required - // Disconnect the underlying storage, to release its resources (such as open files, database connections, etc). + // Disconnect the underlying storage destroy() { - return wrapper.close(); + return wrapper.disconnect(); }, // emits SDK_READY event on shared clients and returns a reference to the storage @@ -89,7 +89,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn wrapperConnect(wrapper, onReadyCb); return { ...this, - // no-op destroy, to close the wrapper only when the main client is destroyed + // no-op destroy, to disconnect the wrapper only when the main client is destroyed destroy() { } }; } diff --git a/src/storages/pluggable/wrapperAdapter.ts b/src/storages/pluggable/wrapperAdapter.ts index 0b93f494..3b530d21 100644 --- a/src/storages/pluggable/wrapperAdapter.ts +++ b/src/storages/pluggable/wrapperAdapter.ts @@ -19,7 +19,7 @@ export const METHODS_TO_PROMISE_WRAP: string[] = [ 'removeItems', 'getItems', 'connect', - 'close' + 'disconnect' ]; /** diff --git a/src/storages/types.ts b/src/storages/types.ts index 5fdb3d31..bbd1c2c7 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -177,15 +177,15 @@ export interface ICustomStorageWrapper { */ connect: () => Promise /** - * Disconnects the underlying storage. + * Disconnects from the underlying storage. * It is meant for storages that requires to be closed, in order to release resources. Otherwise it can just return a resolved promise. * Note: will be called once on SplitFactory main client destroy. * - * @function close + * @function disconnect * @returns {Promise} A promise that resolves when the operation ends. * The promise never rejects. */ - close: () => Promise + disconnect: () => Promise } /** Splits cache */ From 1b81254d8c57d9b676603f316b2d4eb3d120a595 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 22 Nov 2021 17:51:17 -0300 Subject: [PATCH 19/34] renamed CustomStorage to PluggableStorage --- package-lock.json | 2 +- package.json | 2 +- src/sdkFactory/index.ts | 2 +- src/storages/pluggable/EventsCachePluggable.ts | 6 +++--- src/storages/pluggable/ImpressionsCachePluggable.ts | 6 +++--- src/storages/pluggable/SegmentsCachePluggable.ts | 6 +++--- src/storages/pluggable/SplitsCachePluggable.ts | 8 ++++---- src/storages/pluggable/__tests__/index.spec.ts | 2 +- src/storages/pluggable/__tests__/wrapper.mock.ts | 2 +- src/storages/pluggable/inMemoryWrapper.ts | 6 +++--- src/storages/pluggable/index.ts | 12 ++++++------ src/storages/pluggable/wrapperAdapter.ts | 8 ++++---- src/storages/types.ts | 8 ++++---- src/sync/polling/updaters/mySegmentsUpdater.ts | 2 +- .../streaming/UpdateWorkers/SplitsUpdateWorker.ts | 1 - src/utils/constants/index.ts | 2 +- .../storage/__tests__/storageCS.spec.ts | 12 ++++++------ src/utils/settingsValidation/storage/storageCS.ts | 8 ++++---- 18 files changed, 47 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f35f186..da27bd8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.1", + "version": "1.0.1-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 75501e66..921c8745 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.1", + "version": "1.0.1-rc.2", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 136cc064..2d2b89cd 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -40,7 +40,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. matchingKey: getMatching(settings.core.key), splitFiltersValidation: settings.sync.__splitFiltersValidation, - // ATM, only used by CustomStorage + // ATM, only used by PluggableStorage mode: settings.mode, // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined diff --git a/src/storages/pluggable/EventsCachePluggable.ts b/src/storages/pluggable/EventsCachePluggable.ts index ff0b19dc..d30d43b7 100644 --- a/src/storages/pluggable/EventsCachePluggable.ts +++ b/src/storages/pluggable/EventsCachePluggable.ts @@ -1,4 +1,4 @@ -import { ICustomStorageWrapper, IEventsCacheAsync } from '../types'; +import { IPluggableStorageWrapper, IEventsCacheAsync } from '../types'; import { IMetadata } from '../../dtos/types'; import { SplitIO } from '../../types'; import { ILogger } from '../../logger/types'; @@ -8,11 +8,11 @@ import { StoredEventWithMetadata } from '../../sync/submitters/types'; export class EventsCachePluggable implements IEventsCacheAsync { private readonly log: ILogger; - private readonly wrapper: ICustomStorageWrapper; + private readonly wrapper: IPluggableStorageWrapper; private readonly key: string; private readonly metadata: IMetadata; - constructor(log: ILogger, key: string, wrapper: ICustomStorageWrapper, metadata: IMetadata) { + constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, metadata: IMetadata) { this.log = log; this.key = key; this.wrapper = wrapper; diff --git a/src/storages/pluggable/ImpressionsCachePluggable.ts b/src/storages/pluggable/ImpressionsCachePluggable.ts index debe30ed..4de3e33e 100644 --- a/src/storages/pluggable/ImpressionsCachePluggable.ts +++ b/src/storages/pluggable/ImpressionsCachePluggable.ts @@ -1,4 +1,4 @@ -import { ICustomStorageWrapper, IImpressionsCacheAsync } from '../types'; +import { IPluggableStorageWrapper, IImpressionsCacheAsync } from '../types'; import { IMetadata } from '../../dtos/types'; import { ImpressionDTO } from '../../types'; import { ILogger } from '../../logger/types'; @@ -8,10 +8,10 @@ export class ImpressionsCachePluggable implements IImpressionsCacheAsync { private readonly log: ILogger; private readonly key: string; - private readonly wrapper: ICustomStorageWrapper; + private readonly wrapper: IPluggableStorageWrapper; private readonly metadata: IMetadata; - constructor(log: ILogger, key: string, wrapper: ICustomStorageWrapper, metadata: IMetadata) { + constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, metadata: IMetadata) { this.log = log; this.key = key; this.wrapper = wrapper; diff --git a/src/storages/pluggable/SegmentsCachePluggable.ts b/src/storages/pluggable/SegmentsCachePluggable.ts index ec4c3fc3..7b19633a 100644 --- a/src/storages/pluggable/SegmentsCachePluggable.ts +++ b/src/storages/pluggable/SegmentsCachePluggable.ts @@ -2,7 +2,7 @@ /* eslint-disable no-unused-vars */ import { isNaNNumber } from '../../utils/lang'; import KeyBuilderSS from '../KeyBuilderSS'; -import { ICustomStorageWrapper, ISegmentsCacheAsync } from '../types'; +import { IPluggableStorageWrapper, ISegmentsCacheAsync } from '../types'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; import { _Set } from '../../utils/lang/sets'; @@ -14,9 +14,9 @@ export class SegmentsCachePluggable implements ISegmentsCacheAsync { private readonly log: ILogger; private readonly keys: KeyBuilderSS; - private readonly wrapper: ICustomStorageWrapper; + private readonly wrapper: IPluggableStorageWrapper; - constructor(log: ILogger, keys: KeyBuilderSS, wrapper: ICustomStorageWrapper) { + constructor(log: ILogger, keys: KeyBuilderSS, wrapper: IPluggableStorageWrapper) { this.log = log; this.keys = keys; this.wrapper = wrapper; diff --git a/src/storages/pluggable/SplitsCachePluggable.ts b/src/storages/pluggable/SplitsCachePluggable.ts index e4e29ce4..95bbcc0e 100644 --- a/src/storages/pluggable/SplitsCachePluggable.ts +++ b/src/storages/pluggable/SplitsCachePluggable.ts @@ -1,6 +1,6 @@ import { isFiniteNumber, isNaNNumber } from '../../utils/lang'; import KeyBuilder from '../KeyBuilder'; -import { ICustomStorageWrapper } from '../types'; +import { IPluggableStorageWrapper } from '../types'; import { ILogger } from '../../logger/types'; import { ISplit } from '../../dtos/types'; import { LOG_PREFIX } from './constants'; @@ -13,15 +13,15 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync { private readonly log: ILogger; private readonly keys: KeyBuilder; - private readonly wrapper: ICustomStorageWrapper; + private readonly wrapper: IPluggableStorageWrapper; /** - * Create a SplitsCache that uses a custom storage wrapper. + * Create a SplitsCache that uses a storage wrapper. * @param log Logger instance. * @param keys Key builder. * @param wrapper Adapted wrapper storage. */ - constructor(log: ILogger, keys: KeyBuilder, wrapper: ICustomStorageWrapper) { + constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper) { super(); this.log = log; this.keys = keys; diff --git a/src/storages/pluggable/__tests__/index.spec.ts b/src/storages/pluggable/__tests__/index.spec.ts index 34a95fca..6f37f4f1 100644 --- a/src/storages/pluggable/__tests__/index.spec.ts +++ b/src/storages/pluggable/__tests__/index.spec.ts @@ -57,7 +57,7 @@ describe('PLUGGABLE STORAGE', () => { expect(() => PluggableStorage({ wrapper: wrapperMock })).not.toThrow(); // not prefix but valid wrapper is OK // Throws exception if no object is passed as wrapper - const errorNoValidWrapper = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; + const errorNoValidWrapper = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.'; expect(() => PluggableStorage()).toThrow(errorNoValidWrapper); expect(() => PluggableStorage({ wrapper: undefined })).toThrow(errorNoValidWrapper); expect(() => PluggableStorage({ wrapper: 'invalid wrapper' })).toThrow(errorNoValidWrapper); diff --git a/src/storages/pluggable/__tests__/wrapper.mock.ts b/src/storages/pluggable/__tests__/wrapper.mock.ts index 3301ef5c..594ecbcf 100644 --- a/src/storages/pluggable/__tests__/wrapper.mock.ts +++ b/src/storages/pluggable/__tests__/wrapper.mock.ts @@ -1,6 +1,6 @@ import { startsWith, toNumber } from '../../../utils/lang'; -// Creates an in memory ICustomStorageWrapper implementation with Jest mocks +// Creates an in memory IPluggableStorageWrapper implementation with Jest mocks export function wrapperMockFactory() { /** Holds items and list of items */ diff --git a/src/storages/pluggable/inMemoryWrapper.ts b/src/storages/pluggable/inMemoryWrapper.ts index 3f050a16..cb3a77e8 100644 --- a/src/storages/pluggable/inMemoryWrapper.ts +++ b/src/storages/pluggable/inMemoryWrapper.ts @@ -1,15 +1,15 @@ -import { ICustomStorageWrapper } from '../types'; +import { IPluggableStorageWrapper } from '../types'; import { startsWith, toNumber } from '../../utils/lang'; import { ISet, setToArray, _Set } from '../../utils/lang/sets'; /** - * Creates a ICustomStorageWrapper implementation that stores items in memory. + * Creates a IPluggableStorageWrapper implementation that stores items in memory. * The `_cache` property is the object were items are stored. * Intended for testing purposes. * * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves inmediatelly. */ -export function inMemoryWrapperFactory(connDelay?: number): ICustomStorageWrapper & { _cache: Record>, _setConnDelay(connDelay: number): void } { +export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record>, _setConnDelay(connDelay: number): void } { let _cache: Record> = {}; let _connDelay = connDelay; diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index f0d202f1..9dd9f592 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -1,4 +1,4 @@ -import { ICustomStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types'; +import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types'; import KeyBuilderSS from '../KeyBuilderSS'; import { SplitsCachePluggable } from './SplitsCachePluggable'; @@ -8,17 +8,17 @@ import { EventsCachePluggable } from './EventsCachePluggable'; import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { validatePrefix } from '../KeyBuilder'; -import { CONSUMER_PARTIAL_MODE, STORAGE_CUSTOM } from '../../utils/constants'; +import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'; import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; -const NO_VALID_WRAPPER = 'Expecting custom storage `wrapper` in options, but no valid wrapper instance was provided.'; +const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; export interface PluggableStorageOptions { prefix?: string - wrapper: ICustomStorageWrapper + wrapper: IPluggableStorageWrapper } /** @@ -36,7 +36,7 @@ function validatePluggableStorageOptions(options: any) { } // subscription to wrapper connect event in order to emit SDK_READY event -function wrapperConnect(wrapper: ICustomStorageWrapper, onReadyCb: (error?: any) => void) { +function wrapperConnect(wrapper: IPluggableStorageWrapper, onReadyCb: (error?: any) => void) { wrapper.connect().then(() => { onReadyCb(); }).catch((e) => { @@ -96,6 +96,6 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn }; } - PluggableStorageFactory.type = STORAGE_CUSTOM; + PluggableStorageFactory.type = STORAGE_PLUGGABLE; return PluggableStorageFactory; } diff --git a/src/storages/pluggable/wrapperAdapter.ts b/src/storages/pluggable/wrapperAdapter.ts index 3b530d21..c47a5d8b 100644 --- a/src/storages/pluggable/wrapperAdapter.ts +++ b/src/storages/pluggable/wrapperAdapter.ts @@ -1,5 +1,5 @@ import { ILogger } from '../../logger/types'; -import { ICustomStorageWrapper } from '../types'; +import { IPluggableStorageWrapper } from '../types'; import { LOG_PREFIX } from './constants'; export const METHODS_TO_PROMISE_WRAP: string[] = [ @@ -23,14 +23,14 @@ export const METHODS_TO_PROMISE_WRAP: string[] = [ ]; /** - * Adapter of the Custom Storage Wrapper. + * Adapter of the Pluggable Storage Wrapper. * Used to handle exceptions as rejected promises, in order to simplify the error handling on storages. * * @param log logger instance - * @param wrapper custom storage wrapper to adapt + * @param wrapper storage wrapper to adapt * @returns an adapted version of the given storage wrapper */ -export function wrapperAdapter(log: ILogger, wrapper: ICustomStorageWrapper): ICustomStorageWrapper { +export function wrapperAdapter(log: ILogger, wrapper: IPluggableStorageWrapper): IPluggableStorageWrapper { const wrapperAdapter: Record = {}; diff --git a/src/storages/types.ts b/src/storages/types.ts index bbd1c2c7..5ce0d1e9 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -4,9 +4,9 @@ import { StoredEventWithMetadata, StoredImpressionWithMetadata } from '../sync/s import { SplitIO, ImpressionDTO, SDKMode } from '../types'; /** - * Interface of a custom storage wrapper. + * Interface of a pluggable storage wrapper. */ -export interface ICustomStorageWrapper { +export interface IPluggableStorageWrapper { /** Key-Value operations */ @@ -430,7 +430,7 @@ export interface IStorageFactoryParams { matchingKey?: string, /* undefined on server-side SDKs */ splitFiltersValidation?: ISplitFiltersValidation, - // ATM, only used by CustomStorage + // ATM, only used by PluggableStorage mode?: SDKMode, // This callback is invoked when the storage is ready to be used. Error-first callback style: if an error is passed, @@ -440,7 +440,7 @@ export interface IStorageFactoryParams { metadata: IMetadata, } -export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'CUSTOM'; +export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE'; export type IStorageSyncFactory = { type: StorageType, diff --git a/src/sync/polling/updaters/mySegmentsUpdater.ts b/src/sync/polling/updaters/mySegmentsUpdater.ts index 4ceb4ab3..17a5b762 100644 --- a/src/sync/polling/updaters/mySegmentsUpdater.ts +++ b/src/sync/polling/updaters/mySegmentsUpdater.ts @@ -38,7 +38,7 @@ export function mySegmentsUpdaterFactory( // mySegmentsPromise = tracker.start(tracker.TaskNames.MY_SEGMENTS_FETCH, startingUp ? metricCollectors : false, mySegmentsPromise); } - // @TODO if allowing custom storages, handle async execution + // @TODO if allowing pluggable storages, handle async execution function updateSegments(segmentsData: SegmentsData) { let shouldNotifyUpdate; diff --git a/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts index 944d5776..8542d0a9 100644 --- a/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts @@ -83,7 +83,6 @@ export default class SplitsUpdateWorker implements IUpdateWorker { * @param {string} defaultTreatment default treatment value */ killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) { - // @TODO handle retry due to errors in storage, once we allow the definition of custom async storages if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) { // trigger an SDK_UPDATE if Split was killed locally this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true); diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index e632938f..4a4d8303 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -31,4 +31,4 @@ export const CONSUMER_PARTIAL_MODE: SDKMode = 'consumer_partial'; export const STORAGE_MEMORY: StorageType = 'MEMORY'; export const STORAGE_LOCALSTORAGE: StorageType = 'LOCALSTORAGE'; export const STORAGE_REDIS: StorageType = 'REDIS'; -export const STORAGE_CUSTOM: StorageType = 'CUSTOM'; +export const STORAGE_PLUGGABLE: StorageType = 'PLUGGABLE'; diff --git a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts index 6d270946..5bd7c389 100644 --- a/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts +++ b/src/utils/settingsValidation/storage/__tests__/storageCS.spec.ts @@ -5,8 +5,8 @@ import { loggerMock as log } from '../../../../logger/__tests__/sdkLogger.mock'; const mockInLocalStorageFactory = () => { }; mockInLocalStorageFactory.type = 'LOCALSTORAGE'; -const mockCustomStorageFactory = () => { }; -mockCustomStorageFactory.type = 'CUSTOM'; +const mockPluggableStorageFactory = () => { }; +mockPluggableStorageFactory.type = 'PLUGGABLE'; describe('storage validator for pluggable storage (client-side)', () => { @@ -38,13 +38,13 @@ describe('storage validator for pluggable storage (client-side)', () => { }); test('throws error if the provided storage factory is not compatible with the mode', () => { - expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer mode'); - expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A CustomStorage instance is required on consumer mode'); + expect(() => { validateStorageCS({ log, mode: 'consumer', storage: mockInLocalStorageFactory }); }).toThrow('A PluggableStorage instance is required on consumer mode'); + expect(() => { validateStorageCS({ log, mode: 'consumer_partial', storage: mockInLocalStorageFactory }); }).toThrow('A PluggableStorage instance is required on consumer mode'); expect(log.error).not.toBeCalled(); - expect(validateStorageCS({ log, mode: 'standalone', storage: mockCustomStorageFactory })).toBe(InMemoryStorageCSFactory); - expect(validateStorageCS({ log, mode: 'localhost', storage: mockCustomStorageFactory })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'standalone', storage: mockPluggableStorageFactory })).toBe(InMemoryStorageCSFactory); + expect(validateStorageCS({ log, mode: 'localhost', storage: mockPluggableStorageFactory })).toBe(InMemoryStorageCSFactory); expect(log.error).toBeCalledTimes(2); }); diff --git a/src/utils/settingsValidation/storage/storageCS.ts b/src/utils/settingsValidation/storage/storageCS.ts index 3dadb31a..097ce95d 100644 --- a/src/utils/settingsValidation/storage/storageCS.ts +++ b/src/utils/settingsValidation/storage/storageCS.ts @@ -2,7 +2,7 @@ import { InMemoryStorageCSFactory } from '../../../storages/inMemory/InMemorySto import { ISettings, SDKMode } from '../../../types'; import { ILogger } from '../../../logger/types'; import { ERROR_STORAGE_INVALID } from '../../../logger/constants'; -import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_CUSTOM, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; +import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants'; import { IStorageFactoryParams, IStorageSync } from '../../../storages/types'; export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync { @@ -25,7 +25,7 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: let { storage = InMemoryStorageCSFactory, log, mode } = settings; // If an invalid storage is provided, fallback into MEMORY - if (typeof storage !== 'function' || [STORAGE_MEMORY, STORAGE_LOCALSTORAGE, STORAGE_CUSTOM].indexOf(storage.type) === -1) { + if (typeof storage !== 'function' || [STORAGE_MEMORY, STORAGE_LOCALSTORAGE, STORAGE_PLUGGABLE].indexOf(storage.type) === -1) { storage = InMemoryStorageCSFactory; log.error(ERROR_STORAGE_INVALID); } @@ -37,10 +37,10 @@ export function validateStorageCS(settings: { log: ILogger, storage?: any, mode: if ([LOCALHOST_MODE, STANDALONE_MODE].indexOf(mode) === -1) { // Consumer modes require an async storage - if (storage.type !== STORAGE_CUSTOM) throw new Error('A CustomStorage instance is required on consumer mode'); + if (storage.type !== STORAGE_PLUGGABLE) throw new Error('A PluggableStorage instance is required on consumer mode'); } else { // Standalone and localhost modes require a sync storage - if (storage.type === STORAGE_CUSTOM) { + if (storage.type === STORAGE_PLUGGABLE) { storage = InMemoryStorageCSFactory; log.error(ERROR_STORAGE_INVALID, [' It requires consumer mode.']); } From f68fe6b49c576940207db6733919e5ec85b10ab8 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 1 Dec 2021 15:27:48 -0300 Subject: [PATCH 20/34] upgraded ioredis for vulnerability fix --- package-lock.json | 25 ++++++++++++++++--------- package.json | 4 ++-- src/utils/env/isNode.ts | 5 ++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index da27bd8c..c0eb61f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1053,9 +1053,9 @@ } }, "@types/ioredis": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.17.4.tgz", - "integrity": "sha512-kb5+thmQJ7HHyOAnCOeqRJlF2fyvadHghnLLLKZzCNyShStJeIQtNGGDjA30gWqj6UFSDAWBfGEMKrFDrGfvzQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.1.tgz", + "integrity": "sha512-raYHPqRWrfnEoym94BY28mG1+tcZqh3dsp2q7x5IyMAAEvIdu+H0X8diASMpncIm+oHyH9dalOeOnGOL/YnuOA==", "dev": true, "requires": { "@types/node": "*" @@ -1967,9 +1967,9 @@ "dev": true }, "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", "dev": true }, "detect-newline": { @@ -2894,9 +2894,9 @@ "dev": true }, "ioredis": { - "version": "4.27.2", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.2.tgz", - "integrity": "sha512-7OpYymIthonkC2Jne5uGWXswdhlua1S1rWGAERaotn0hGJWTSURvxdHA9G6wNbT/qKCloCja/FHsfKXW8lpTmg==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.2.tgz", + "integrity": "sha512-kQ+Iv7+c6HsDdPP2XUHaMv8DhnSeAeKEwMbaoqsXYbO+03dItXt7+5jGQDRyjdRUV2rFJbzg7P4Qt1iX2tqkOg==", "dev": true, "requires": { "cluster-key-slot": "^1.1.0", @@ -2904,6 +2904,7 @@ "denque": "^1.1.0", "lodash.defaults": "^4.2.0", "lodash.flatten": "^4.4.0", + "lodash.isarguments": "^3.1.0", "p-map": "^2.1.0", "redis-commands": "1.7.0", "redis-errors": "^1.2.0", @@ -4480,6 +4481,12 @@ "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", "dev": true }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", diff --git a/package.json b/package.json index 921c8745..4fcd3c70 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@types/google.analytics": "0.0.40", - "@types/ioredis": "^4.14.1", + "@types/ioredis": "^4.28.0", "@types/jest": "^27.0.0", "@types/lodash": "^4.14.162", "@types/node": "^14.14.7", @@ -61,7 +61,7 @@ "eslint": "^7.32.0", "eslint-plugin-compat": "3.7.0", "fetch-mock": "^9.10.7", - "ioredis": "^4.26.0", + "ioredis": "^4.28.0", "jest": "^27.2.3", "jest-localstorage-mock": "^2.4.3", "js-yaml": "^3.14.0", diff --git a/src/utils/env/isNode.ts b/src/utils/env/isNode.ts index f189c5b6..64c6200a 100644 --- a/src/utils/env/isNode.ts +++ b/src/utils/env/isNode.ts @@ -1,3 +1,6 @@ -// We check for version truthiness since most shims will have that as empty string. // eslint-disable-next-line no-undef +/** + * 'true' if running in Node.js, or 'false' otherwise. + * We check for version truthiness since most shims will have that as empty string. + */ export const isNode: boolean = typeof process !== 'undefined' && typeof process.version !== 'undefined' && !!process.version ? true : false; From 4ada05afb3eb39858ed419dbdcdb8f767a6f014a Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 2 Dec 2021 16:55:47 -0300 Subject: [PATCH 21/34] removed NodeJS types --- .eslintrc | 1 - package.json | 1 - src/storages/pluggable/constants.ts | 2 +- src/types.ts | 17 +++++++++-------- src/utils/MinEventEmitter.ts | 20 ++++++++++---------- src/utils/env/isNode.ts | 2 +- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.eslintrc b/.eslintrc index 94782cad..fb21ecc8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,7 +18,6 @@ "EventSource": "readonly", // @TODO remove. Configure as a type declaration "MessageEvent": "readonly", // @TODO remove. Configure as a type declaration "Event": "readonly", // @TODO remove. Configure as a type declaration - "NodeJS": "readonly", "UniversalAnalytics": "readonly" // @TODO remove when moving GA integrations to Browser-SDK }, diff --git a/package.json b/package.json index 4fcd3c70..b6fa3516 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "@types/ioredis": "^4.28.0", "@types/jest": "^27.0.0", "@types/lodash": "^4.14.162", - "@types/node": "^14.14.7", "@types/object-assign": "^4.0.30", "@typescript-eslint/eslint-plugin": "^4.2.0", "@typescript-eslint/parser": "^4.2.0", diff --git a/src/storages/pluggable/constants.ts b/src/storages/pluggable/constants.ts index 1f845f45..f28d60b8 100644 --- a/src/storages/pluggable/constants.ts +++ b/src/storages/pluggable/constants.ts @@ -1 +1 @@ -export const LOG_PREFIX = 'storage:pluggable:'; +export const LOG_PREFIX = 'storage:pluggable: '; diff --git a/src/types.ts b/src/types.ts index c7fbc62b..d4351696 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,15 +7,16 @@ import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory import { ISyncManagerFactoryParams, ISyncManagerCS } from './sync/types'; /** - * EventEmitter interface with the minimal methods used by the SDK + * Reduced version of NodeJS.EventEmitter interface with the minimal methods used by the SDK + * @see {@link https://nodejs.org/api/events.html} */ -export interface IEventEmitter extends Pick { - addListener(event: string, listener: (...args: any[]) => void): any; - on(event: string, listener: (...args: any[]) => void): any - once(event: string, listener: (...args: any[]) => void): any - removeListener(event: string, listener: (...args: any[]) => void): any; - off(event: string, listener: (...args: any[]) => void): any; - removeAllListeners(event?: string): any +export interface IEventEmitter { + addListener(event: string, listener: (...args: any[]) => void): this; + on(event: string, listener: (...args: any[]) => void): this + once(event: string, listener: (...args: any[]) => void): this + removeListener(event: string, listener: (...args: any[]) => void): this; + off(event: string, listener: (...args: any[]) => void): this; + removeAllListeners(event?: string): this emit(event: string, ...args: any[]): boolean } diff --git a/src/utils/MinEventEmitter.ts b/src/utils/MinEventEmitter.ts index 2bddb0d6..1ff39d4e 100644 --- a/src/utils/MinEventEmitter.ts +++ b/src/utils/MinEventEmitter.ts @@ -18,7 +18,7 @@ export default class EventEmitter implements IEventEmitter { boolean // whether it is a one-time listener or not ]>> = {}; - private registerListener(type: string, listener: (...args: any[]) => void, oneTime: boolean): EventEmitter { + private registerListener(type: string, listener: (...args: any[]) => void, oneTime: boolean) { checkListener(listener); // To avoid recursion in the case that type === "newListener" before @@ -33,27 +33,27 @@ export default class EventEmitter implements IEventEmitter { return this; } - addListener(type: string, listener: (...args: any[]) => void): EventEmitter { + addListener(type: string, listener: (...args: any[]) => void) { return this.registerListener(type, listener, false); } // alias of addListener - on(type: string, listener: (...args: any[]) => void): EventEmitter { + on(type: string, listener: (...args: any[]) => void) { return this.addListener(type, listener); } - once(type: string, listener: (...args: any[]) => void): EventEmitter { + once(type: string, listener: (...args: any[]) => void) { return this.registerListener(type, listener, true); } - // eslint-disable-next-line - removeListener(type: string, listener: (...args: any[]) => void): EventEmitter { + // @ts-ignore + removeListener(/* type: string, listener: (...args: any[]) => void */) { throw new Error('Method not implemented.'); } - // alias of removeListener - off(type: string, listener: (...args: any[]) => void): EventEmitter { - return this.removeListener(type, listener); + // @ts-ignore alias of removeListener + off(/* type: string, listener: (...args: any[]) => void */) { + return this.removeListener(/* type, listener */); } emit(type: string, ...args: any[]): boolean { @@ -68,7 +68,7 @@ export default class EventEmitter implements IEventEmitter { return true; } - removeAllListeners(type?: string): EventEmitter { + removeAllListeners(type?: string) { if (!this.listeners[REMOVE_LISTENER_EVENT]) { // if not listening for `removeListener`, no need to emit if (type) { diff --git a/src/utils/env/isNode.ts b/src/utils/env/isNode.ts index 64c6200a..8c28623c 100644 --- a/src/utils/env/isNode.ts +++ b/src/utils/env/isNode.ts @@ -1,6 +1,6 @@ -// eslint-disable-next-line no-undef /** * 'true' if running in Node.js, or 'false' otherwise. * We check for version truthiness since most shims will have that as empty string. */ +// eslint-disable-next-line no-undef export const isNode: boolean = typeof process !== 'undefined' && typeof process.version !== 'undefined' && !!process.version ? true : false; From 4de6898e7dd83ecf79a3c523c455b14adcd71ccd Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 2 Dec 2021 16:57:51 -0300 Subject: [PATCH 22/34] removed EventSource DOM type --- .eslintrc | 1 - package-lock.json | 2 +- package.json | 2 +- src/sdkFactory/types.ts | 4 ++-- src/services/types.ts | 18 ++++++++++++++++-- src/sync/streaming/SSEClient/index.ts | 10 +++++----- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.eslintrc b/.eslintrc index fb21ecc8..affa34c4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,7 +15,6 @@ "globals": { // global TS types - "EventSource": "readonly", // @TODO remove. Configure as a type declaration "MessageEvent": "readonly", // @TODO remove. Configure as a type declaration "Event": "readonly", // @TODO remove. Configure as a type declaration "UniversalAnalytics": "readonly" // @TODO remove when moving GA integrations to Browser-SDK diff --git a/package-lock.json b/package-lock.json index c0eb61f5..b2c91c99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.2", + "version": "1.0.1-rc.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b6fa3516..cf0cc5ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.2", + "version": "1.0.1-rc.3", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sdkFactory/types.ts b/src/sdkFactory/types.ts index b0d87343..d907d2d0 100644 --- a/src/sdkFactory/types.ts +++ b/src/sdkFactory/types.ts @@ -3,7 +3,7 @@ import { ISignalListener } from '../listeners/types'; import { ILogger } from '../logger/types'; import { ISdkReadinessManager } from '../readiness/types'; import { ISdkClientFactoryParams } from '../sdkClient/types'; -import { IFetch, ISplitApi } from '../services/types'; +import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types'; import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types'; import { ISyncManager, ISyncManagerFactoryParams } from '../sync/types'; import { IImpressionObserver } from '../trackers/impressionObserver/types'; @@ -16,7 +16,7 @@ import { SplitIO, ISettings, IEventEmitter } from '../types'; export interface IPlatform { getOptions?: () => object getFetch?: () => (IFetch | undefined) - getEventSource?: () => (typeof EventSource | undefined) + getEventSource?: () => (IEventSourceConstructor | undefined) EventEmitter: new () => IEventEmitter } diff --git a/src/services/types.ts b/src/services/types.ts index b1bdf954..d7af2d59 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -50,8 +50,8 @@ export type IPostMetricsCounters = (body: string) => Promise export type IPostMetricsTimes = (body: string) => Promise export interface ISplitApi { - getSdkAPIHealthCheck: IHealthCheckAPI - getEventsAPIHealthCheck: IHealthCheckAPI + getSdkAPIHealthCheck: IHealthCheckAPI + getEventsAPIHealthCheck: IHealthCheckAPI fetchAuth: IFetchAuth fetchSplitChanges: IFetchSplitChanges fetchSegmentChanges: IFetchSegmentChanges @@ -62,3 +62,17 @@ export interface ISplitApi { postMetricsCounters: IPostMetricsCounters postMetricsTimes: IPostMetricsTimes } + +// Minimal version of EventSource API used by the SDK +interface EventSourceEventMap { + 'error': Event + 'message': MessageEvent + 'open': Event +} + +interface IEventSource { + addEventListener(type: K, listener: (this: IEventSource, ev: EventSourceEventMap[K]) => any): void + close(): void +} + +export type IEventSourceConstructor = new (url: string, eventSourceInitDict?: { headers?: object }) => IEventSource diff --git a/src/sync/streaming/SSEClient/index.ts b/src/sync/streaming/SSEClient/index.ts index 7e3cc4bb..fbd09261 100644 --- a/src/sync/streaming/SSEClient/index.ts +++ b/src/sync/streaming/SSEClient/index.ts @@ -1,3 +1,4 @@ +import { IEventSourceConstructor } from '../../../services/types'; import { ISettings } from '../../../types'; import { IAuthTokenPushEnabled } from '../AuthClient/types'; import { ISSEClient, ISseEventHandler } from './types'; @@ -31,9 +32,9 @@ function buildSSEHeaders(settings: ISettings) { */ export default class SSEClient implements ISSEClient { // Instance properties: - eventSource: typeof EventSource; + eventSource?: IEventSourceConstructor; streamingUrl: string; - connection?: InstanceType; + connection?: InstanceType; handler?: ISseEventHandler; useHeaders?: boolean; headers: Record; @@ -46,8 +47,7 @@ export default class SSEClient implements ISSEClient { * @param getEventSource Function to get the EventSource constructor. * @throws 'EventSource API is not available. ' if EventSource is not available. */ - constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => (typeof EventSource | undefined)) { - // @ts-expect-error + constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => (IEventSourceConstructor | undefined)) { this.eventSource = getEventSource && getEventSource(); // if eventSource is not available, throw an exception if (!this.eventSource) throw new Error('EventSource API is not available. '); @@ -79,7 +79,7 @@ export default class SSEClient implements ISSEClient { ).join(','); const url = `${this.streamingUrl}?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${VERSION}&heartbeats=true`; // same results using `&heartbeats=false` - this.connection = new this.eventSource( + this.connection = new this.eventSource!( // For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params, // because native EventSource implementations for browser doesn't support headers. this.useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`, From c1ebd257b518cbbddeae709a4b0d23d66b471d81 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 9 Dec 2021 20:14:12 -0300 Subject: [PATCH 23/34] fixed type --- package-lock.json | 2 +- package.json | 2 +- src/services/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2c91c99..cdecc850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.3", + "version": "1.0.1-rc.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index cf0cc5ac..68610a2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.3", + "version": "1.0.1-rc.4", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/services/types.ts b/src/services/types.ts index d7af2d59..22650c5b 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -75,4 +75,4 @@ interface IEventSource { close(): void } -export type IEventSourceConstructor = new (url: string, eventSourceInitDict?: { headers?: object }) => IEventSource +export type IEventSourceConstructor = new (url: string, eventSourceInitDict?: any) => IEventSource From 8be0a3c3373452072499aec04b1eb5ec3059808f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 20 Dec 2021 15:12:13 -0300 Subject: [PATCH 24/34] fix: prevent returning null dynamic config if treatment name contains a . --- src/evaluator/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index 3c12c9b3..df9d5ef8 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -1,7 +1,6 @@ import Engine from './Engine'; import thenable from '../utils/promise/thenable'; import * as LabelsConstants from '../utils/labels'; -import { get } from '../utils/lang'; import { CONTROL } from '../utils/constants'; import { ISplit, MaybeThenable } from '../dtos/types'; import { IStorageAsync, IStorageSync } from '../storages/types'; @@ -106,17 +105,17 @@ function getEvaluation( const split = Engine.parse(log, splitJSON, storage); evaluation = split.getTreatment(key, attributes, evaluateFeature); - // If the storage is async, evaluation and changeNumber will return a thenable + // If the storage is async and the evaluated split uses segment, evaluation is thenable if (thenable(evaluation)) { return evaluation.then(result => { result.changeNumber = split.getChangeNumber(); - result.config = get(splitJSON, `configurations.${result.treatment}`, null); + result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null; return result; }); } else { evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional - evaluation.config = get(splitJSON, `configurations.${evaluation.treatment}`, null); + evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null; } } From b40d1fee08c596b619b83c0b1737319c3b923833 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 20 Dec 2021 15:13:18 -0300 Subject: [PATCH 25/34] updated MinEvents default export to prevent issue with Webpack --- package-lock.json | 2 +- package.json | 2 +- src/utils/MinEvents.ts | 9 +-------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index cdecc850..43488ed1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 68610a2a..b2483879 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/utils/MinEvents.ts b/src/utils/MinEvents.ts index e32e255b..5b24373c 100644 --- a/src/utils/MinEvents.ts +++ b/src/utils/MinEvents.ts @@ -30,8 +30,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -import { IEventEmitter } from '../types'; - var R = typeof Reflect === 'object' ? Reflect : null; var ReflectApply = R && typeof R.apply === 'function' ? R.apply @@ -39,15 +37,10 @@ var ReflectApply = R && typeof R.apply === 'function' return Function.prototype.apply.call(target, receiver, args); }; -function EventEmitter() { +export default function EventEmitter() { EventEmitter.init.call(this); } -export default EventEmitter as new () => IEventEmitter; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; - EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; From 642665ea045f957622873f60a4194c6a28bf43ea Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 20 Dec 2021 16:37:55 -0300 Subject: [PATCH 26/34] fixed type check --- src/__tests__/testUtils/eventSourceMock.ts | 2 +- src/readiness/__tests__/readinessManager.spec.ts | 2 +- src/sdkFactory/__tests__/index.spec.ts | 2 +- .../polling/updaters/__tests__/splitChangesUpdater.spec.ts | 2 +- src/sync/streaming/AuthClient/__tests__/index.spec.ts | 2 +- src/sync/streaming/__tests__/pushManager.spec.ts | 2 +- src/utils/MinEvents.ts | 6 ++++-- 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/__tests__/testUtils/eventSourceMock.ts b/src/__tests__/testUtils/eventSourceMock.ts index 281421b3..f47615ed 100644 --- a/src/__tests__/testUtils/eventSourceMock.ts +++ b/src/__tests__/testUtils/eventSourceMock.ts @@ -12,7 +12,7 @@ * */ -import EventEmitter from '../../utils/MinEvents'; +import { EventEmitter } from '../../utils/MinEvents'; import { IEventEmitter } from '../../types'; type ReadyStateType = 0 | 1 | 2; diff --git a/src/readiness/__tests__/readinessManager.spec.ts b/src/readiness/__tests__/readinessManager.spec.ts index ae30b7cb..0a6726c5 100644 --- a/src/readiness/__tests__/readinessManager.spec.ts +++ b/src/readiness/__tests__/readinessManager.spec.ts @@ -1,5 +1,5 @@ import { readinessManagerFactory } from '../readinessManager'; -import EventEmitter from '../../utils/MinEvents'; +import { EventEmitter } from '../../utils/MinEvents'; import { IReadinessManager } from '../types'; import { SDK_READY, SDK_UPDATE, SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_READY_FROM_CACHE, SDK_SPLITS_CACHE_LOADED, SDK_READY_TIMED_OUT } from '../constants'; diff --git a/src/sdkFactory/__tests__/index.spec.ts b/src/sdkFactory/__tests__/index.spec.ts index 3e758f68..58a1144f 100644 --- a/src/sdkFactory/__tests__/index.spec.ts +++ b/src/sdkFactory/__tests__/index.spec.ts @@ -2,7 +2,7 @@ import { ISdkFactoryParams } from '../types'; import { sdkFactory } from '../index'; import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; import { SplitIO } from '../../types'; -import EventEmitter from '../../utils/MinEvents'; +import { EventEmitter } from '../../utils/MinEvents'; /** Mocks */ diff --git a/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts b/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts index cfe8dac6..bc68243c 100644 --- a/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts +++ b/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts @@ -8,7 +8,7 @@ import { splitChangesUpdaterFactory, parseSegments, computeSplitsMutation } from import splitChangesMock1 from '../../../../__tests__/mocks/splitchanges.since.-1.json'; import fetchMock from '../../../../__tests__/testUtils/fetchMock'; import { settingsSplitApi } from '../../../../utils/settingsValidation/__tests__/settings.mocks'; -import EventEmitter from '../../../../utils/MinEvents'; +import { EventEmitter } from '../../../../utils/MinEvents'; import { loggerMock } from '../../../../logger/__tests__/sdkLogger.mock'; const activeSplitWithSegments = { diff --git a/src/sync/streaming/AuthClient/__tests__/index.spec.ts b/src/sync/streaming/AuthClient/__tests__/index.spec.ts index afaf5f73..70604400 100644 --- a/src/sync/streaming/AuthClient/__tests__/index.spec.ts +++ b/src/sync/streaming/AuthClient/__tests__/index.spec.ts @@ -3,7 +3,7 @@ import { splitApiFactory } from '../../../../services/splitApi'; import { authDataResponseSample, authDataSample, jwtSampleInvalid, jwtSampleNoChannels, jwtSampleNoIat, userKeySample, userKeyBase64HashSample } from '../../__tests__/dataMocks'; import fetchMock from '../../../../__tests__/testUtils/fetchMock'; import { settingsSplitApi } from '../../../../utils/settingsValidation/__tests__/settings.mocks'; -import EventEmitter from '../../../../utils/MinEvents'; +import { EventEmitter } from '../../../../utils/MinEvents'; // module to test import { authenticateFactory, hashUserKey } from '../index'; diff --git a/src/sync/streaming/__tests__/pushManager.spec.ts b/src/sync/streaming/__tests__/pushManager.spec.ts index 9b6fb784..02161150 100644 --- a/src/sync/streaming/__tests__/pushManager.spec.ts +++ b/src/sync/streaming/__tests__/pushManager.spec.ts @@ -1,5 +1,5 @@ import pushManagerFactory from '../pushManager'; -import EventEmitter from '../../../utils/MinEvents'; +import { EventEmitter } from '../../../utils/MinEvents'; import { fullSettings } from '../../../utils/settingsValidation/__tests__/settings.mocks'; import { IPushManagerCS } from '../types'; diff --git a/src/utils/MinEvents.ts b/src/utils/MinEvents.ts index 5b24373c..71aa3626 100644 --- a/src/utils/MinEvents.ts +++ b/src/utils/MinEvents.ts @@ -30,6 +30,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +import { IEventEmitter } from '../types'; + var R = typeof Reflect === 'object' ? Reflect : null; var ReflectApply = R && typeof R.apply === 'function' ? R.apply @@ -37,9 +39,9 @@ var ReflectApply = R && typeof R.apply === 'function' return Function.prototype.apply.call(target, receiver, args); }; -export default function EventEmitter() { +export const EventEmitter: { new(): IEventEmitter } = function EventEmitter() { EventEmitter.init.call(this); -} +}; EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; From e5651b81288a9e38109296a86ae0080aed00e682 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 21 Dec 2021 14:58:54 -0300 Subject: [PATCH 27/34] polishing --- src/logger/messages/warn.ts | 4 ++-- src/sdkFactory/index.ts | 2 +- src/storages/types.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/logger/messages/warn.ts b/src/logger/messages/warn.ts index 8c35e311..f6a66eec 100644 --- a/src/logger/messages/warn.ts +++ b/src/logger/messages/warn.ts @@ -21,9 +21,9 @@ export const codesWarn: [number, string][] = codesError.concat([ [c.WARN_TRIMMING_PROPERTIES, '%s: Event has more than 300 properties. Some of them will be trimmed when processed.'], [c.WARN_CONVERTING, '%s: %s "%s" is not of type string, converting.'], [c.WARN_TRIMMING, '%s: %s "%s" has extra whitespace, trimming.'], - [c.WARN_NOT_EXISTENT_SPLIT, '%s: split "%s" does not exist in this environment, please double check what splits exist in the web console.'], + [c.WARN_NOT_EXISTENT_SPLIT, '%s: split "%s" does not exist in this environment, please double check what splits exist in the Split web console.'], [c.WARN_LOWERCASE_TRAFFIC_TYPE, '%s: traffic_type_name should be all lowercase - converting string to lowercase.'], - [c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding split in this environment, make sure you\'re tracking your events to a valid traffic type defined in the web console.'], + [c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding split in this environment, make sure you\'re tracking your events to a valid traffic type defined in the Split web console.'], // initialization / settings validation [c.WARN_INTEGRATION_INVALID, c.LOG_PREFIX_SETTINGS+': %s integration item(s) at settings is invalid. %s'], [c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+': split filters have been configured but will have no effect if mode is not "%s", since synchronization is being deferred to an external tool.'], diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 2d2b89cd..f074f387 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -46,7 +46,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined // or only instantiates submitters, and therefore it is not able to emit readiness events. onReadyCb: (error) => { - if (error) return; // don't emit SDK_READY if storage failed to connect. + if (error) return; // Don't emit SDK_READY if storage failed to connect. Error message is logged by wrapperAdapter readinessManager.splits.emit(SDK_SPLITS_ARRIVED); readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED); }, diff --git a/src/storages/types.ts b/src/storages/types.ts index 5ce0d1e9..0e3a6001 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -70,7 +70,7 @@ export interface IPluggableStorageWrapper { /** Integer operations */ /** - * Increments in 1 the given `key` value or set it in 1 if the value doesn't exist. + * Increments in 1 the given `key` value or set it to 1 if the value doesn't exist. * * @function incr * @param {string} key Key to increment @@ -79,7 +79,7 @@ export interface IPluggableStorageWrapper { */ incr: (key: string) => Promise /** - * Decrements in 1 the given `key` value or set it in -1 if the value doesn't exist. + * Decrements in 1 the given `key` value or set it to -1 if the value doesn't exist. * * @function decr * @param {string} key Key to decrement From dfe59366366b361255fb077911157f283138cdd0 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 21 Dec 2021 17:44:08 -0300 Subject: [PATCH 28/34] prepare release --- CHANGES.txt | 20 +++++++++++++------- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 429ad003..845f2030 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,17 @@ +1.1.0 (December 22, 2021) + - Added support for client-side SDK instances to run in "consumer" and "partial consumer" modes, with a custom implementation of it's internal storage modules, enabling customers to implement this caching in any storage technology of choice and connect it to the SDK instance itself which will use it instead of in-memory storage. + - Updated multiple modules due to general polishing and improvements. + - Updated ioredis dependency for vulnerability fixes. + - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot ("."). + 1.0.0 (October 20, 2021) -- BREAKING CHANGE on multiple modules due to general polishing, improvements and bug fixes. In most cases the change is to use named exports. This affected mostly modules related with synchronization and storages. - - Updated streaming logic to use the newest version of our streaming service, including: - - Integration with Auth service V2, connecting to the new channels and applying the received connection delay. - - Implemented handling of the new MySegmentsV2 notification types (SegmentRemoval, KeyList, Bounded and Unbounded) - - New control notification for environment scoped streaming reset. -- Updated localhost mode to emit SDK_READY_FROM_CACHE event in Browser when using localStorage (Related to issue https://github.com/splitio/react-client/issues/34). -- Updated dependencies for vulnerability fixes. + - BREAKING CHANGE on multiple modules due to general polishing, improvements and bug fixes. In most cases the change is to use named exports. This affected mostly modules related with synchronization and storages. + - Updated streaming logic to use the newest version of our streaming service, including: + - Integration with Auth service V2, connecting to the new channels and applying the received connection delay. + - Implemented handling of the new MySegmentsV2 notification types (SegmentRemoval, KeyList, Bounded and Unbounded) + - New control notification for environment scoped streaming reset. + - Updated localhost mode to emit SDK_READY_FROM_CACHE event in Browser when using localStorage (Related to issue https://github.com/splitio/react-client/issues/34). + - Updated dependencies for vulnerability fixes. 0.1.0 (March 30, 2021) - Initial public release. It includes common modules to be consumed by the different Split implementations written in JavaScript. Based on the original JS SDK in the `javascript-client` repository. diff --git a/package-lock.json b/package-lock.json index 43488ed1..0cdd8424 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.5", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b2483879..1f058127 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.5", + "version": "1.1.0", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", From a95573df4a2c178268c89affcfc84a663c1428b3 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 21 Dec 2021 19:11:13 -0300 Subject: [PATCH 29/34] updated README --- CHANGES.txt | 3 ++- README.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 845f2030..ad8321b2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ 1.1.0 (December 22, 2021) - - Added support for client-side SDK instances to run in "consumer" and "partial consumer" modes, with a custom implementation of it's internal storage modules, enabling customers to implement this caching in any storage technology of choice and connect it to the SDK instance itself which will use it instead of in-memory storage. + - Added support for the SDK to run in "consumer" and "partial consumer" modes, with a pluggable implementation of it's internal storage, enabling + customers to implement this caching with any storage technology of choice and connect it to the SDK instance to use instead of its default in-memory storage. - Updated multiple modules due to general polishing and improvements. - Updated ioredis dependency for vulnerability fixes. - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot ("."). diff --git a/README.md b/README.md index 5ebcc78a..7d6d7a13 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Split Javascript SDK common components -[![npm version](https://badge.fury.io/js/%40splitsoftware%2Fsplitio-commons.svg)](https://badge.fury.io/js/%40splitsoftware%2Fsplitio) [![Build Status](https://travis-ci.com/splitio/javascript-commons.svg?branch=main)](https://travis-ci.com/splitio/javascript-commons) +[![npm version](https://badge.fury.io/js/%40splitsoftware%2Fsplitio-commons.svg)](https://badge.fury.io/js/%40splitsoftware%2Fsplitio-commons) [![Build Status](https://github.com/splitio/javascript-commons/actions/workflows/ci.yml/badge.svg)](https://github.com/splitio/javascript-commons/actions/workflows/ci.yml) ## Overview This library is designed to work with Split, the platform for controlled rollouts, which serves features to your users via a Split feature flag to manage your complete customer experience. From 53b3a887d86c8851f032c5cbc3a737b2d68c4b32 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 3 Jan 2022 16:31:22 -0300 Subject: [PATCH 30/34] replaced default exports with named exports, and replaced object-assign dependency with inline ES module --- .eslintrc | 7 +- package-lock.json | 528 +++++++++++++++++- package.json | 3 +- src/evaluator/Engine.ts | 6 +- src/evaluator/combiners/__tests__/and.spec.ts | 6 +- .../combiners/__tests__/ifelseif.spec.ts | 8 +- src/evaluator/combiners/and.ts | 4 +- src/evaluator/combiners/ifelseif.ts | 4 +- .../condition/__tests__/engineUtils.spec.ts | 2 +- src/evaluator/condition/index.ts | 4 +- src/evaluator/index.ts | 4 +- src/evaluator/matchers/__tests__/all.spec.ts | 2 +- .../matchers/__tests__/between.spec.ts | 2 +- .../matchers/__tests__/boolean.spec.ts | 2 +- .../matchers/__tests__/cont_all.spec.ts | 2 +- .../matchers/__tests__/cont_any.spec.ts | 2 +- .../matchers/__tests__/cont_str.spec.ts | 2 +- .../matchers/__tests__/dependency.spec.ts | 2 +- src/evaluator/matchers/__tests__/eq.spec.ts | 2 +- .../matchers/__tests__/eq_set.spec.ts | 2 +- src/evaluator/matchers/__tests__/ew.spec.ts | 2 +- src/evaluator/matchers/__tests__/gte.spec.ts | 2 +- src/evaluator/matchers/__tests__/lte.spec.ts | 2 +- .../matchers/__tests__/part_of.spec.ts | 2 +- .../matchers/__tests__/regex.spec.ts | 2 +- .../__tests__/segment/client_side.spec.ts | 2 +- .../__tests__/segment/server_side.spec.ts | 2 +- src/evaluator/matchers/__tests__/sw.spec.ts | 2 +- .../matchers/__tests__/whitelist.spec.ts | 2 +- src/evaluator/matchers/all.ts | 2 +- src/evaluator/matchers/between.ts | 2 +- src/evaluator/matchers/boolean.ts | 2 +- src/evaluator/matchers/cont_all.ts | 2 +- src/evaluator/matchers/cont_any.ts | 2 +- src/evaluator/matchers/cont_str.ts | 2 +- src/evaluator/matchers/dependency.ts | 4 +- src/evaluator/matchers/eq.ts | 2 +- src/evaluator/matchers/eq_set.ts | 2 +- src/evaluator/matchers/ew.ts | 2 +- src/evaluator/matchers/gte.ts | 2 +- src/evaluator/matchers/index.ts | 70 +-- src/evaluator/matchers/lte.ts | 2 +- src/evaluator/matchers/part_of.ts | 2 +- src/evaluator/matchers/segment.ts | 4 +- src/evaluator/matchers/string.ts | 2 +- src/evaluator/matchers/sw.ts | 2 +- src/evaluator/matchers/whitelist.ts | 2 +- .../__tests__/segment.spec.ts | 6 +- .../__tests__/whitelist.spec.ts | 6 +- src/evaluator/matchersTransform/index.ts | 10 +- src/evaluator/matchersTransform/segment.ts | 2 +- src/evaluator/matchersTransform/set.ts | 2 +- .../matchersTransform/unaryNumeric.ts | 2 +- src/evaluator/matchersTransform/whitelist.ts | 2 +- .../parser/__tests__/boolean.spec.ts | 2 +- src/evaluator/parser/__tests__/index.spec.ts | 2 +- .../parser/__tests__/invalidMatcher.spec.ts | 2 +- src/evaluator/parser/__tests__/regex.spec.ts | 2 +- src/evaluator/parser/__tests__/set.spec.ts | 2 +- src/evaluator/parser/__tests__/string.spec.ts | 2 +- .../__tests__/trafficAllocation.spec.ts | 2 +- src/evaluator/parser/index.ts | 24 +- .../treatments/__tests__/index.spec.ts | 2 +- src/evaluator/treatments/index.ts | 2 +- src/evaluator/value/index.ts | 6 +- src/evaluator/value/sanitize.ts | 2 +- src/integrations/__tests__/browser.spec.ts | 2 +- src/integrations/browser.ts | 4 +- src/integrations/ga/GaToSplit.ts | 2 +- src/integrations/pluggable.ts | 2 +- src/listeners/__tests__/browser.spec.ts | 2 +- src/listeners/__tests__/node.spec.ts | 2 +- src/listeners/browser.ts | 4 +- src/listeners/node.ts | 4 +- src/logger/index.ts | 2 +- .../__tests__/sdkReadinessManager.spec.ts | 2 +- src/readiness/readinessManager.ts | 4 +- src/readiness/sdkReadinessManager.ts | 6 +- .../__tests__/sdkClientMethodCS.spec.ts | 2 +- src/sdkClient/client.ts | 4 +- src/sdkClient/clientCS.ts | 4 +- src/sdkClient/clientInputValidation.ts | 4 +- src/sdkClient/sdkClient.ts | 6 +- src/sdkClient/sdkClientMethodCS.ts | 4 +- src/sdkClient/sdkClientMethodCSWithTT.ts | 4 +- src/sdkFactory/index.ts | 6 +- .../__tests__/index.asyncCache.spec.ts | 4 +- .../__tests__/index.syncCache.spec.ts | 2 +- src/sdkManager/index.ts | 4 +- src/services/splitApi.ts | 2 +- src/services/splitHttpClient.ts | 2 +- src/storages/AbstractSegmentsCacheSync.ts | 2 +- src/storages/AbstractSplitsCacheAsync.ts | 2 +- src/storages/AbstractSplitsCacheSync.ts | 2 +- src/storages/KeyBuilder.ts | 2 +- src/storages/KeyBuilderCS.ts | 4 +- src/storages/KeyBuilderSS.ts | 4 +- src/storages/__tests__/KeyBuilder.spec.ts | 4 +- src/storages/findLatencyIndex.ts | 2 +- .../inLocalStorage/MySegmentsCacheInLocal.ts | 6 +- .../inLocalStorage/SplitsCacheInLocal.ts | 6 +- .../__tests__/MySegmentsCacheInLocal.spec.ts | 4 +- .../__tests__/SplitsCacheInLocal.spec.ts | 4 +- src/storages/inLocalStorage/index.ts | 16 +- src/storages/inMemory/CountsCacheInMemory.ts | 2 +- src/storages/inMemory/EventsCacheInMemory.ts | 2 +- .../inMemory/ImpressionCountsCacheInMemory.ts | 2 +- .../inMemory/ImpressionsCacheInMemory.ts | 2 +- src/storages/inMemory/InMemoryStorage.ts | 10 +- src/storages/inMemory/InMemoryStorageCS.ts | 10 +- .../inMemory/LatenciesCacheInMemory.ts | 4 +- .../inMemory/MySegmentsCacheInMemory.ts | 4 +- .../inMemory/SegmentsCacheInMemory.ts | 4 +- src/storages/inMemory/SplitsCacheInMemory.ts | 4 +- .../__tests__/CountsCacheInMemory.spec.ts | 4 +- .../__tests__/EventsCacheInMemory.spec.ts | 18 +- .../ImpressionCountsCacheInMemory.spec.ts | 6 +- .../ImpressionsCacheInMemory.spec.ts | 4 +- .../__tests__/LatenciesCacheInMemory.spec.ts | 6 +- .../__tests__/MySegmentsCacheInMemory.spec.ts | 4 +- .../__tests__/SegmentsCacheInMemory.spec.ts | 6 +- .../__tests__/SplitsCacheInMemory.spec.ts | 10 +- src/storages/inRedis/CountsCacheInRedis.ts | 4 +- src/storages/inRedis/EventsCacheInRedis.ts | 2 +- .../inRedis/ImpressionsCacheInRedis.ts | 2 +- src/storages/inRedis/LatenciesCacheInRedis.ts | 6 +- src/storages/inRedis/RedisAdapter.ts | 6 +- src/storages/inRedis/SegmentsCacheInRedis.ts | 4 +- src/storages/inRedis/SplitsCacheInRedis.ts | 6 +- .../__tests__/CountsCacheInRedis.spec.ts | 4 +- .../__tests__/EventsCacheInRedis.spec.ts | 2 +- .../__tests__/ImpressionsCacheInRedis.spec.ts | 8 +- .../__tests__/LatenciesCacheInRedis.spec.ts | 4 +- .../inRedis/__tests__/RedisAdapter.spec.ts | 4 +- .../__tests__/SegmentsCacheInRedis.spec.ts | 4 +- .../__tests__/SplitsCacheInRedis.spec.ts | 4 +- src/storages/inRedis/index.ts | 16 +- .../pluggable/SegmentsCachePluggable.ts | 2 +- .../pluggable/SplitsCachePluggable.ts | 4 +- .../__tests__/SegmentsCachePluggable.spec.ts | 2 +- .../__tests__/SplitsCachePluggable.spec.ts | 2 +- .../__tests__/wrapperAdapter.spec.ts | 2 +- src/storages/pluggable/index.ts | 8 +- src/sync/__tests__/syncTask.spec.ts | 2 +- .../offline/splitsParser/parseCondition.ts | 2 +- .../splitsParser/splitsParserFromFile.ts | 2 +- .../splitsParser/splitsParserFromSettings.ts | 2 +- src/sync/offline/syncManagerOffline.ts | 4 +- .../offline/syncTasks/fromObjectSyncTask.ts | 4 +- .../polling/fetchers/mySegmentsFetcher.ts | 2 +- .../polling/fetchers/segmentChangesFetcher.ts | 2 +- .../polling/fetchers/splitChangesFetcher.ts | 2 +- src/sync/polling/pollingManagerCS.ts | 6 +- src/sync/polling/pollingManagerSS.ts | 8 +- .../polling/syncTasks/mySegmentsSyncTask.ts | 6 +- .../polling/syncTasks/segmentsSyncTask.ts | 6 +- src/sync/polling/syncTasks/splitsSyncTask.ts | 6 +- .../__tests__/splitChangesUpdater.spec.ts | 6 +- .../polling/updaters/mySegmentsUpdater.ts | 2 +- .../polling/updaters/segmentChangesUpdater.ts | 2 +- .../polling/updaters/splitChangesUpdater.ts | 2 +- src/sync/streaming/AuthClient/index.ts | 2 +- .../SSEClient/__tests__/index.spec.ts | 16 +- src/sync/streaming/SSEClient/index.ts | 2 +- .../SSEHandler/NotificationKeeper.ts | 2 +- .../SSEHandler/__tests__/index.spec.ts | 2 +- src/sync/streaming/SSEHandler/index.ts | 4 +- .../UpdateWorkers/MySegmentsUpdateWorker.ts | 4 +- .../UpdateWorkers/SegmentsUpdateWorker.ts | 4 +- .../UpdateWorkers/SplitsUpdateWorker.ts | 4 +- .../__tests__/MySegmentsUpdateWorker.spec.ts | 2 +- .../__tests__/SegmentsUpdateWorker.spec.ts | 4 +- .../__tests__/SplitsUpdateWorker.spec.ts | 4 +- src/sync/streaming/UpdateWorkers/types.ts | 2 +- .../streaming/__tests__/pushManager.spec.ts | 2 +- src/sync/streaming/pushManager.ts | 16 +- src/sync/submitters/submitterSyncTask.ts | 2 +- src/sync/syncTask.ts | 2 +- src/trackers/__tests__/eventTracker.spec.ts | 2 +- .../__tests__/impressionsTracker.spec.ts | 4 +- src/trackers/eventTracker.ts | 6 +- .../impressionObserver/ImpressionObserver.ts | 4 +- .../impressionObserverCS.ts | 2 +- .../impressionObserverSS.ts | 2 +- src/trackers/impressionsTracker.ts | 6 +- src/utils/Backoff.ts | 2 +- src/utils/LRUCache/__tests__/index.spec.ts | 2 +- src/utils/LRUCache/index.ts | 2 +- src/utils/MinEventEmitter.ts | 2 +- .../__tests__/trafficTypeExistance.spec.ts | 2 +- .../inputValidation/trafficTypeExistance.ts | 2 +- src/utils/lang/binarySearch.ts | 2 +- src/utils/lang/objectAssign.ts | 104 ++++ src/utils/promise/thenable.ts | 4 +- src/utils/promise/timeout.ts | 2 +- src/utils/promise/wrapper.ts | 2 +- .../settingsValidation/impressionsMode.ts | 2 +- src/utils/settingsValidation/index.ts | 4 +- src/utils/settingsValidation/mode.ts | 2 +- src/utils/timeTracker/__tests__/index.spec.ts | 26 +- src/utils/timeTracker/index.ts | 10 +- src/utils/timeTracker/now/browser.ts | 4 +- src/utils/timeTracker/now/node.ts | 2 +- src/utils/timeTracker/timer.ts | 2 +- 204 files changed, 1048 insertions(+), 436 deletions(-) create mode 100644 src/utils/lang/objectAssign.ts diff --git a/.eslintrc b/.eslintrc index affa34c4..6457f1b6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,7 +4,8 @@ ], "parser": "@typescript-eslint/parser", "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "import" ], "env": { @@ -79,7 +80,9 @@ } ], "compat/compat": ["error", "defaults, not ie < 10, not node < 6"], - "no-throw-literal": "error" + "no-throw-literal": "error", + "import/no-default-export": "error", + "import/no-self-import": "error" }, "parserOptions": { "ecmaVersion": 2015, diff --git a/package-lock.json b/package-lock.json index 43488ed1..979c9292 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1101,6 +1101,12 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/lodash": { "version": "4.14.162", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.162.tgz", @@ -1113,12 +1119,6 @@ "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==", "dev": true }, - "@types/object-assign": { - "version": "4.0.30", - "resolved": "https://registry.npmjs.org/@types/object-assign/-/object-assign-4.0.30.tgz", - "integrity": "sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI=", - "dev": true - }, "@types/prettier": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz", @@ -1487,12 +1487,36 @@ "sprintf-js": "~1.0.2" } }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, "ast-metadata-inferer": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.2.0.tgz", @@ -1725,6 +1749,16 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1960,6 +1994,15 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2046,6 +2089,45 @@ "ansi-colors": "^4.1.1" } }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2262,6 +2344,107 @@ } } }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz", + "integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, "eslint-plugin-compat": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-3.7.0.tgz", @@ -2285,6 +2468,71 @@ } } }, + "eslint-plugin-import": { + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz", + "integrity": "sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.1", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -2696,6 +2944,17 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -2708,6 +2967,16 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -2780,12 +3049,33 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -2893,6 +3183,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "ioredis": { "version": "4.28.2", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.28.2.tgz", @@ -2912,6 +3213,31 @@ "standard-as-callback": "^2.1.0" } }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, "is-ci": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", @@ -2930,6 +3256,15 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2957,36 +3292,94 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-subset": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", "dev": true }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4682,10 +5075,40 @@ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } }, "once": { "version": "1.4.0", @@ -5053,6 +5476,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", @@ -5185,6 +5619,26 @@ } } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5428,6 +5882,35 @@ } } }, + "tsconfig-paths": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", + "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "tslib": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", @@ -5486,6 +5969,18 @@ "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -5600,6 +6095,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index b2483879..38d9443c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "bugs": "https://github.com/splitio/javascript-commons/issues", "homepage": "https://github.com/splitio/javascript-commons#readme", "dependencies": { - "object-assign": "^4.1.1", "tslib": "^2.1.0" }, "devDependencies": { @@ -52,13 +51,13 @@ "@types/ioredis": "^4.28.0", "@types/jest": "^27.0.0", "@types/lodash": "^4.14.162", - "@types/object-assign": "^4.0.30", "@typescript-eslint/eslint-plugin": "^4.2.0", "@typescript-eslint/parser": "^4.2.0", "cross-env": "^7.0.2", "csv-streamify": "^4.0.0", "eslint": "^7.32.0", "eslint-plugin-compat": "3.7.0", + "eslint-plugin-import": "^2.25.3", "fetch-mock": "^9.10.7", "ioredis": "^4.28.0", "jest": "^27.2.3", diff --git a/src/evaluator/Engine.ts b/src/evaluator/Engine.ts index d7466caa..af65a87f 100644 --- a/src/evaluator/Engine.ts +++ b/src/evaluator/Engine.ts @@ -1,7 +1,7 @@ import { get } from '../utils/lang'; -import parser from './parser'; +import { parser } from './parser'; import { keyParser } from '../utils/key'; -import thenable from '../utils/promise/thenable'; +import { thenable } from '../utils/promise/thenable'; import * as LabelsConstants from '../utils/labels'; import { CONTROL } from '../utils/constants'; import { ISplit, MaybeThenable } from '../dtos/types'; @@ -17,7 +17,7 @@ function evaluationResult(result: IEvaluation | undefined, defaultTreatment: str }; } -export default class Engine { +export class Engine { constructor(private baseInfo: ISplit, private evaluator: IEvaluator) { diff --git a/src/evaluator/combiners/__tests__/and.spec.ts b/src/evaluator/combiners/__tests__/and.spec.ts index 1990546e..8d31a9d4 100644 --- a/src/evaluator/combiners/__tests__/and.spec.ts +++ b/src/evaluator/combiners/__tests__/and.spec.ts @@ -1,16 +1,16 @@ -import andCombiner from '../and'; +import { andCombinerContext } from '../and'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; test('COMBINER AND / should always return true', async function () { - let AND = andCombiner(loggerMock, [() => true, () => true, () => true]); + let AND = andCombinerContext(loggerMock, [() => true, () => true, () => true]); expect(await AND('always true')).toBe(true); // should always return true }); test('COMBINER AND / should always return false', async function () { - let AND = andCombiner(loggerMock, [() => true, () => true, () => false]); + let AND = andCombinerContext(loggerMock, [() => true, () => true, () => false]); expect(await AND('always false')).toBe(false); // should always return false }); diff --git a/src/evaluator/combiners/__tests__/ifelseif.spec.ts b/src/evaluator/combiners/__tests__/ifelseif.spec.ts index eb874ebc..983b21a1 100644 --- a/src/evaluator/combiners/__tests__/ifelseif.spec.ts +++ b/src/evaluator/combiners/__tests__/ifelseif.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import ifElseIfCombinerFactory from '../ifelseif'; +import { ifElseIfCombinerContext } from '../ifelseif'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; test('IF ELSE IF COMBINER / should correctly propagate context parameters and predicates returns value', async function () { @@ -17,7 +17,7 @@ test('IF ELSE IF COMBINER / should correctly propagate context parameters and pr } let predicates = [evaluator]; - let ifElseIfEvaluator = ifElseIfCombinerFactory(loggerMock, predicates); + let ifElseIfEvaluator = ifElseIfCombinerContext(loggerMock, predicates); expect(await ifElseIfEvaluator(inputKey, inputSeed, inputAttributes) === evaluationResult).toBe(true); console.log(`evaluator should return ${evaluationResult}`); @@ -36,7 +36,7 @@ test('IF ELSE IF COMBINER / should stop evaluating when one matcher return a tre } ]; - let ifElseIfEvaluator = ifElseIfCombinerFactory(loggerMock, predicates); + let ifElseIfEvaluator = ifElseIfCombinerContext(loggerMock, predicates); expect(await ifElseIfEvaluator()).toBe('exclude'); // exclude treatment found }); @@ -54,7 +54,7 @@ test('IF ELSE IF COMBINER / should return undefined if there is none matching ru } ]; - const ifElseIfEvaluator = ifElseIfCombinerFactory(loggerMock, predicates); + const ifElseIfEvaluator = ifElseIfCombinerContext(loggerMock, predicates); expect(await ifElseIfEvaluator() === undefined).toBe(true); }); diff --git a/src/evaluator/combiners/and.ts b/src/evaluator/combiners/and.ts index 271801dd..b229a22b 100644 --- a/src/evaluator/combiners/and.ts +++ b/src/evaluator/combiners/and.ts @@ -1,11 +1,11 @@ import { findIndex } from '../../utils/lang'; import { ILogger } from '../../logger/types'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import { MaybeThenable } from '../../dtos/types'; import { IMatcher } from '../types'; import { ENGINE_COMBINER_AND } from '../../logger/constants'; -export default function andCombinerContext(log: ILogger, matchers: IMatcher[]) { +export function andCombinerContext(log: ILogger, matchers: IMatcher[]) { function andResults(results: boolean[]): boolean { // Array.prototype.every is supported by target environments diff --git a/src/evaluator/combiners/ifelseif.ts b/src/evaluator/combiners/ifelseif.ts index 9aa162f7..2ecb95ff 100644 --- a/src/evaluator/combiners/ifelseif.ts +++ b/src/evaluator/combiners/ifelseif.ts @@ -1,13 +1,13 @@ import { findIndex } from '../../utils/lang'; import { ILogger } from '../../logger/types'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import * as LabelsConstants from '../../utils/labels'; import { CONTROL } from '../../utils/constants'; import { SplitIO } from '../../types'; import { IEvaluation, IEvaluator, ISplitEvaluator } from '../types'; import { ENGINE_COMBINER_IFELSEIF, ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, ERROR_ENGINE_COMBINER_IFELSEIF } from '../../logger/constants'; -export default function ifElseIfCombinerContext(log: ILogger, predicates: IEvaluator[]): IEvaluator { +export function ifElseIfCombinerContext(log: ILogger, predicates: IEvaluator[]): IEvaluator { function unexpectedInputHandler() { log.error(ERROR_ENGINE_COMBINER_IFELSEIF); diff --git a/src/evaluator/condition/__tests__/engineUtils.spec.ts b/src/evaluator/condition/__tests__/engineUtils.spec.ts index 0f1a6d87..8682c851 100644 --- a/src/evaluator/condition/__tests__/engineUtils.spec.ts +++ b/src/evaluator/condition/__tests__/engineUtils.spec.ts @@ -1,5 +1,5 @@ import * as engineUtils from '../engineUtils'; -import Treatments from '../../treatments'; +import { Treatments } from '../../treatments'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; const treatmentsMock = Treatments.parse([{ diff --git a/src/evaluator/condition/index.ts b/src/evaluator/condition/index.ts index 542617ea..5cf1cac8 100644 --- a/src/evaluator/condition/index.ts +++ b/src/evaluator/condition/index.ts @@ -1,5 +1,5 @@ import { getTreatment, shouldApplyRollout } from './engineUtils'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import * as LabelsConstants from '../../utils/labels'; import { MaybeThenable } from '../../dtos/types'; import { IEvaluation, IEvaluator, ISplitEvaluator } from '../types'; @@ -22,7 +22,7 @@ function match(log: ILogger, matchingResult: boolean, bucketingKey: string | und } // Condition factory -export default function conditionContext(log: ILogger, matcherEvaluator: (...args: any) => MaybeThenable, treatments: { getTreatmentFor: (x: number) => string }, label: string, conditionType: 'ROLLOUT' | 'WHITELIST'): IEvaluator { +export function conditionContext(log: ILogger, matcherEvaluator: (...args: any) => MaybeThenable, treatments: { getTreatmentFor: (x: number) => string }, label: string, conditionType: 'ROLLOUT' | 'WHITELIST'): IEvaluator { return function conditionEvaluator(key: SplitIO.SplitKey, seed: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) { diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index df9d5ef8..9f6cccf1 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -1,5 +1,5 @@ -import Engine from './Engine'; -import thenable from '../utils/promise/thenable'; +import { Engine } from './Engine'; +import { thenable } from '../utils/promise/thenable'; import * as LabelsConstants from '../utils/labels'; import { CONTROL } from '../utils/constants'; import { ISplit, MaybeThenable } from '../dtos/types'; diff --git a/src/evaluator/matchers/__tests__/all.spec.ts b/src/evaluator/matchers/__tests__/all.spec.ts index d4a47b09..aea0063d 100644 --- a/src/evaluator/matchers/__tests__/all.spec.ts +++ b/src/evaluator/matchers/__tests__/all.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/between.spec.ts b/src/evaluator/matchers/__tests__/between.spec.ts index ca95ba16..9e7308f1 100644 --- a/src/evaluator/matchers/__tests__/between.spec.ts +++ b/src/evaluator/matchers/__tests__/between.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/boolean.spec.ts b/src/evaluator/matchers/__tests__/boolean.spec.ts index 515b46d5..8f33893c 100644 --- a/src/evaluator/matchers/__tests__/boolean.spec.ts +++ b/src/evaluator/matchers/__tests__/boolean.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/cont_all.spec.ts b/src/evaluator/matchers/__tests__/cont_all.spec.ts index 8ea23697..2b11df6f 100644 --- a/src/evaluator/matchers/__tests__/cont_all.spec.ts +++ b/src/evaluator/matchers/__tests__/cont_all.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/cont_any.spec.ts b/src/evaluator/matchers/__tests__/cont_any.spec.ts index 13896900..cc4481a6 100644 --- a/src/evaluator/matchers/__tests__/cont_any.spec.ts +++ b/src/evaluator/matchers/__tests__/cont_any.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/cont_str.spec.ts b/src/evaluator/matchers/__tests__/cont_str.spec.ts index f8c56d4e..527ab0f7 100644 --- a/src/evaluator/matchers/__tests__/cont_str.spec.ts +++ b/src/evaluator/matchers/__tests__/cont_str.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/dependency.spec.ts b/src/evaluator/matchers/__tests__/dependency.spec.ts index 6f4f62ec..0e41a6cf 100644 --- a/src/evaluator/matchers/__tests__/dependency.spec.ts +++ b/src/evaluator/matchers/__tests__/dependency.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { evaluateFeature } from '../../index'; import { IMatcher, IMatcherDto } from '../../types'; import { IStorageSync } from '../../../storages/types'; diff --git a/src/evaluator/matchers/__tests__/eq.spec.ts b/src/evaluator/matchers/__tests__/eq.spec.ts index 40b40f8d..57ffb394 100644 --- a/src/evaluator/matchers/__tests__/eq.spec.ts +++ b/src/evaluator/matchers/__tests__/eq.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/eq_set.spec.ts b/src/evaluator/matchers/__tests__/eq_set.spec.ts index 17450cf6..9acf6412 100644 --- a/src/evaluator/matchers/__tests__/eq_set.spec.ts +++ b/src/evaluator/matchers/__tests__/eq_set.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/ew.spec.ts b/src/evaluator/matchers/__tests__/ew.spec.ts index c5980d0b..7d071c5b 100644 --- a/src/evaluator/matchers/__tests__/ew.spec.ts +++ b/src/evaluator/matchers/__tests__/ew.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/gte.spec.ts b/src/evaluator/matchers/__tests__/gte.spec.ts index f7a4ec54..0c6046b4 100644 --- a/src/evaluator/matchers/__tests__/gte.spec.ts +++ b/src/evaluator/matchers/__tests__/gte.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/lte.spec.ts b/src/evaluator/matchers/__tests__/lte.spec.ts index ad920020..a461269f 100644 --- a/src/evaluator/matchers/__tests__/lte.spec.ts +++ b/src/evaluator/matchers/__tests__/lte.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/part_of.spec.ts b/src/evaluator/matchers/__tests__/part_of.spec.ts index 9472d389..1b48f093 100644 --- a/src/evaluator/matchers/__tests__/part_of.spec.ts +++ b/src/evaluator/matchers/__tests__/part_of.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/regex.spec.ts b/src/evaluator/matchers/__tests__/regex.spec.ts index 3465d623..e93164c0 100644 --- a/src/evaluator/matchers/__tests__/regex.spec.ts +++ b/src/evaluator/matchers/__tests__/regex.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import fs from 'fs'; import rl from 'readline'; import { IMatcher, IMatcherDto } from '../../types'; diff --git a/src/evaluator/matchers/__tests__/segment/client_side.spec.ts b/src/evaluator/matchers/__tests__/segment/client_side.spec.ts index 7514a862..3bace2ca 100644 --- a/src/evaluator/matchers/__tests__/segment/client_side.spec.ts +++ b/src/evaluator/matchers/__tests__/segment/client_side.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../../matcherTypes'; -import matcherFactory from '../..'; +import { matcherFactory } from '../..'; import { IMatcher, IMatcherDto } from '../../../types'; import { IStorageSync } from '../../../../storages/types'; import { loggerMock } from '../../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/segment/server_side.spec.ts b/src/evaluator/matchers/__tests__/segment/server_side.spec.ts index 97d9dcc7..906aec62 100644 --- a/src/evaluator/matchers/__tests__/segment/server_side.spec.ts +++ b/src/evaluator/matchers/__tests__/segment/server_side.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../../matcherTypes'; -import matcherFactory from '../..'; +import { matcherFactory } from '../..'; import { IMatcher, IMatcherDto } from '../../../types'; import { IStorageSync } from '../../../../storages/types'; import { loggerMock } from '../../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/sw.spec.ts b/src/evaluator/matchers/__tests__/sw.spec.ts index ec8cdb5b..3bf6ca3f 100644 --- a/src/evaluator/matchers/__tests__/sw.spec.ts +++ b/src/evaluator/matchers/__tests__/sw.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/__tests__/whitelist.spec.ts b/src/evaluator/matchers/__tests__/whitelist.spec.ts index 4c7aa723..64e1f2b2 100644 --- a/src/evaluator/matchers/__tests__/whitelist.spec.ts +++ b/src/evaluator/matchers/__tests__/whitelist.spec.ts @@ -1,5 +1,5 @@ import { matcherTypes } from '../matcherTypes'; -import matcherFactory from '..'; +import { matcherFactory } from '..'; import { _Set } from '../../../utils/lang/sets'; import { IMatcher, IMatcherDto } from '../../types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/matchers/all.ts b/src/evaluator/matchers/all.ts index 8b8c64fb..51326a91 100644 --- a/src/evaluator/matchers/all.ts +++ b/src/evaluator/matchers/all.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_ALL } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function allMatcherContext(log: ILogger) { +export function allMatcherContext(log: ILogger) { return function allMatcher(runtimeAttr: string): boolean { log.debug(ENGINE_MATCHER_ALL); diff --git a/src/evaluator/matchers/between.ts b/src/evaluator/matchers/between.ts index f56709b3..063e2a59 100644 --- a/src/evaluator/matchers/between.ts +++ b/src/evaluator/matchers/between.ts @@ -2,7 +2,7 @@ import { IBetweenMatcherData } from '../../dtos/types'; import { ENGINE_MATCHER_BETWEEN } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function betweenMatcherContext(log: ILogger, ruleVO: IBetweenMatcherData) /*: Function */ { +export function betweenMatcherContext(log: ILogger, ruleVO: IBetweenMatcherData) /*: Function */ { return function betweenMatcher(runtimeAttr: number): boolean { let isBetween = runtimeAttr >= ruleVO.start && runtimeAttr <= ruleVO.end; diff --git a/src/evaluator/matchers/boolean.ts b/src/evaluator/matchers/boolean.ts index 6e64a268..821d8f98 100644 --- a/src/evaluator/matchers/boolean.ts +++ b/src/evaluator/matchers/boolean.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_BOOLEAN } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function booleanMatcherContext(log: ILogger, ruleAttr: boolean) /*: Function */ { +export function booleanMatcherContext(log: ILogger, ruleAttr: boolean) /*: Function */ { return function booleanMatcher(runtimeAttr: boolean): boolean { let booleanMatches = ruleAttr === runtimeAttr; diff --git a/src/evaluator/matchers/cont_all.ts b/src/evaluator/matchers/cont_all.ts index 02bc35e1..e52a9e85 100644 --- a/src/evaluator/matchers/cont_all.ts +++ b/src/evaluator/matchers/cont_all.ts @@ -2,7 +2,7 @@ import { ENGINE_MATCHER_CONTAINS_ALL } from '../../logger/constants'; import { ILogger } from '../../logger/types'; import { findIndex } from '../../utils/lang'; -export default function containsAllMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function containsAllSetMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function containsAllMatcher(runtimeAttr: string[]): boolean { let containsAll = true; diff --git a/src/evaluator/matchers/cont_any.ts b/src/evaluator/matchers/cont_any.ts index 69fcdb3e..6ab0b08c 100644 --- a/src/evaluator/matchers/cont_any.ts +++ b/src/evaluator/matchers/cont_any.ts @@ -2,7 +2,7 @@ import { ENGINE_MATCHER_CONTAINS_ANY } from '../../logger/constants'; import { ILogger } from '../../logger/types'; import { findIndex } from '../../utils/lang'; -export default function containsAnyMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function containsAnySetMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function containsAnyMatcher(runtimeAttr: string[]): boolean { let containsAny = false; diff --git a/src/evaluator/matchers/cont_str.ts b/src/evaluator/matchers/cont_str.ts index 0a15d13e..b3dfe506 100644 --- a/src/evaluator/matchers/cont_str.ts +++ b/src/evaluator/matchers/cont_str.ts @@ -2,7 +2,7 @@ import { isString } from '../../utils/lang'; import { ILogger } from '../../logger/types'; import { ENGINE_MATCHER_CONTAINS_STRING } from '../../logger/constants'; -export default function containsStringMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function containsStringMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function containsStringMatcher(runtimeAttr: string): boolean { let contains = ruleAttr.some(e => isString(runtimeAttr) && runtimeAttr.indexOf(e) > -1); diff --git a/src/evaluator/matchers/dependency.ts b/src/evaluator/matchers/dependency.ts index e3b8723b..42040328 100644 --- a/src/evaluator/matchers/dependency.ts +++ b/src/evaluator/matchers/dependency.ts @@ -1,11 +1,11 @@ import { IDependencyMatcherData, MaybeThenable } from '../../dtos/types'; import { IStorageAsync, IStorageSync } from '../../storages/types'; import { ILogger } from '../../logger/types'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import { IDependencyMatcherValue, IEvaluation, ISplitEvaluator } from '../types'; import { ENGINE_MATCHER_DEPENDENCY, ENGINE_MATCHER_DEPENDENCY_PRE } from '../../logger/constants'; -export default function dependencyMatcherContext(log: ILogger, { split, treatments }: IDependencyMatcherData, storage: IStorageSync | IStorageAsync) { +export function dependencyMatcherContext(log: ILogger, { split, treatments }: IDependencyMatcherData, storage: IStorageSync | IStorageAsync) { function checkTreatment(evaluation: IEvaluation, acceptableTreatments: string[], parentName: string) { let matches = false; diff --git a/src/evaluator/matchers/eq.ts b/src/evaluator/matchers/eq.ts index ddccc264..019b1a19 100644 --- a/src/evaluator/matchers/eq.ts +++ b/src/evaluator/matchers/eq.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_EQUAL } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function equalToMatcherContext(log: ILogger, ruleAttr: number) /*: Function */ { +export function equalToMatcherContext(log: ILogger, ruleAttr: number) /*: Function */ { return function equalToMatcher(runtimeAttr: number): boolean { let isEqual = runtimeAttr === ruleAttr; diff --git a/src/evaluator/matchers/eq_set.ts b/src/evaluator/matchers/eq_set.ts index f0fddd27..b36b1103 100644 --- a/src/evaluator/matchers/eq_set.ts +++ b/src/evaluator/matchers/eq_set.ts @@ -2,7 +2,7 @@ import { ENGINE_MATCHER_EQUAL_TO_SET } from '../../logger/constants'; import { ILogger } from '../../logger/types'; import { findIndex } from '../../utils/lang'; -export default function equalToSetMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function equalToSetMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function equalToSetMatcher(runtimeAttr: string[]): boolean { // Length being the same is the first condition. let isEqual = runtimeAttr.length === ruleAttr.length; diff --git a/src/evaluator/matchers/ew.ts b/src/evaluator/matchers/ew.ts index 20d46c63..851e0e2c 100644 --- a/src/evaluator/matchers/ew.ts +++ b/src/evaluator/matchers/ew.ts @@ -2,7 +2,7 @@ import { ENGINE_MATCHER_ENDS_WITH } from '../../logger/constants'; import { ILogger } from '../../logger/types'; import { endsWith as strEndsWith } from '../../utils/lang'; -export default function endsWithMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function endsWithMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function endsWithMatcher(runtimeAttr: string): boolean { let endsWith = ruleAttr.some(e => strEndsWith(runtimeAttr, e)); diff --git a/src/evaluator/matchers/gte.ts b/src/evaluator/matchers/gte.ts index a08f55f5..83d83e4e 100644 --- a/src/evaluator/matchers/gte.ts +++ b/src/evaluator/matchers/gte.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_GREATER } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function greaterThanEqualMatcherContext(log: ILogger, ruleAttr: number) /*: Function */ { +export function greaterThanEqualMatcherContext(log: ILogger, ruleAttr: number) /*: Function */ { return function greaterThanEqualMatcher(runtimeAttr: number): boolean { let isGreaterEqualThan = runtimeAttr >= ruleAttr; diff --git a/src/evaluator/matchers/index.ts b/src/evaluator/matchers/index.ts index 8cf50779..ecb195b4 100644 --- a/src/evaluator/matchers/index.ts +++ b/src/evaluator/matchers/index.ts @@ -1,49 +1,49 @@ -import allMatcher from './all'; -import segmentMatcher from './segment'; -import whitelistMatcher from './whitelist'; -import eqMatcher from './eq'; -import gteMatcher from './gte'; -import lteMatcher from './lte'; -import betweenMatcher from './between'; -import equalToSetMatcher from './eq_set'; -import containsAllSetMatcher from './cont_all'; -import containsAnySetMatcher from './cont_any'; -import partOfSetMatcher from './part_of'; -import swMatcher from './sw'; -import ewMatcher from './ew'; -import containsStrMatcher from './cont_str'; -import dependencyMatcher from './dependency'; -import booleanMatcher from './boolean'; -import stringMatcher from './string'; +import { allMatcherContext } from './all'; +import { segmentMatcherContext } from './segment'; +import { whitelistMatcherContext } from './whitelist'; +import { equalToMatcherContext } from './eq'; +import { greaterThanEqualMatcherContext } from './gte'; +import { lessThanEqualMatcherContext } from './lte'; +import { betweenMatcherContext } from './between'; +import { equalToSetMatcherContext } from './eq_set'; +import { containsAnySetMatcherContext } from './cont_any'; +import { containsAllSetMatcherContext } from './cont_all'; +import { partOfSetMatcherContext } from './part_of'; +import { endsWithMatcherContext } from './ew'; +import { startsWithMatcherContext } from './sw'; +import { containsStringMatcherContext } from './cont_str'; +import { dependencyMatcherContext } from './dependency'; +import { booleanMatcherContext } from './boolean'; +import { stringMatcherContext } from './string'; import { IStorageAsync, IStorageSync } from '../../storages/types'; import { IMatcher, IMatcherDto } from '../types'; import { ILogger } from '../../logger/types'; const matchers = [ undefined, // UNDEFINED: 0, - allMatcher, // ALL_KEYS: 1, - segmentMatcher, // IN_SEGMENT: 2, - whitelistMatcher, // WHITELIST: 3, - eqMatcher, // EQUAL_TO: 4, - gteMatcher, // GREATER_THAN_OR_EQUAL_TO: 5, - lteMatcher, // LESS_THAN_OR_EQUAL_TO: 6, - betweenMatcher, // BETWEEN: 7, - equalToSetMatcher, // EQUAL_TO_SET: 8, - containsAnySetMatcher, // CONTAINS_ANY_OF_SET: 9, - containsAllSetMatcher, // CONTAINS_ALL_OF_SET: 10, - partOfSetMatcher, // PART_OF_SET: 11, - ewMatcher, // ENDS_WITH: 12, - swMatcher, // STARTS_WITH: 13, - containsStrMatcher, // CONTAINS_STRING: 14, - dependencyMatcher, // IN_SPLIT_TREATMENT: 15, - booleanMatcher, // EQUAL_TO_BOOLEAN: 16, - stringMatcher // MATCHES_STRING: 17 + allMatcherContext, // ALL_KEYS: 1, + segmentMatcherContext, // IN_SEGMENT: 2, + whitelistMatcherContext, // WHITELIST: 3, + equalToMatcherContext, // EQUAL_TO: 4, + greaterThanEqualMatcherContext, // GREATER_THAN_OR_EQUAL_TO: 5, + lessThanEqualMatcherContext, // LESS_THAN_OR_EQUAL_TO: 6, + betweenMatcherContext, // BETWEEN: 7, + equalToSetMatcherContext, // EQUAL_TO_SET: 8, + containsAnySetMatcherContext, // CONTAINS_ANY_OF_SET: 9, + containsAllSetMatcherContext, // CONTAINS_ALL_OF_SET: 10, + partOfSetMatcherContext, // PART_OF_SET: 11, + endsWithMatcherContext, // ENDS_WITH: 12, + startsWithMatcherContext, // STARTS_WITH: 13, + containsStringMatcherContext, // CONTAINS_STRING: 14, + dependencyMatcherContext, // IN_SPLIT_TREATMENT: 15, + booleanMatcherContext, // EQUAL_TO_BOOLEAN: 16, + stringMatcherContext // MATCHES_STRING: 17 ]; /** * Matcher factory. */ -export default function matcherFactory(log: ILogger, matcherDto: IMatcherDto, storage?: IStorageSync | IStorageAsync): IMatcher | undefined { +export function matcherFactory(log: ILogger, matcherDto: IMatcherDto, storage?: IStorageSync | IStorageAsync): IMatcher | undefined { let { type, value diff --git a/src/evaluator/matchers/lte.ts b/src/evaluator/matchers/lte.ts index 06883b94..b2ebeeb6 100644 --- a/src/evaluator/matchers/lte.ts +++ b/src/evaluator/matchers/lte.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_LESS } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function lessThanEqualMatcherContext(log: ILogger, ruleAttr: number) /*: function */ { +export function lessThanEqualMatcherContext(log: ILogger, ruleAttr: number) /*: function */ { return function lessThanEqualMatcher(runtimeAttr: number): boolean { let isLessEqualThan = runtimeAttr <= ruleAttr; diff --git a/src/evaluator/matchers/part_of.ts b/src/evaluator/matchers/part_of.ts index ccabf3e4..75385731 100644 --- a/src/evaluator/matchers/part_of.ts +++ b/src/evaluator/matchers/part_of.ts @@ -2,7 +2,7 @@ import { findIndex } from '../../utils/lang'; import { ILogger } from '../../logger/types'; import { ENGINE_MATCHER_PART_OF } from '../../logger/constants'; -export default function partOfMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function partOfSetMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function partOfMatcher(runtimeAttr: string[]): boolean { // To be part of the length should be minor or equal. let isPartOf = runtimeAttr.length <= ruleAttr.length; diff --git a/src/evaluator/matchers/segment.ts b/src/evaluator/matchers/segment.ts index 3e9db7d5..ad5ec30a 100644 --- a/src/evaluator/matchers/segment.ts +++ b/src/evaluator/matchers/segment.ts @@ -1,10 +1,10 @@ import { MaybeThenable } from '../../dtos/types'; import { ISegmentsCacheBase } from '../../storages/types'; import { ILogger } from '../../logger/types'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import { ENGINE_MATCHER_SEGMENT } from '../../logger/constants'; -export default function matcherSegmentContext(log: ILogger, segmentName: string, storage: { segments: ISegmentsCacheBase }) { +export function segmentMatcherContext(log: ILogger, segmentName: string, storage: { segments: ISegmentsCacheBase }) { return function segmentMatcher(key: string): MaybeThenable { const isInSegment = storage.segments.isInSegment(segmentName, key); diff --git a/src/evaluator/matchers/string.ts b/src/evaluator/matchers/string.ts index f0e0512f..81e8bc48 100644 --- a/src/evaluator/matchers/string.ts +++ b/src/evaluator/matchers/string.ts @@ -1,7 +1,7 @@ import { ENGINE_MATCHER_STRING_INVALID, ENGINE_MATCHER_STRING } from '../../logger/constants'; import { ILogger } from '../../logger/types'; -export default function stringMatcherContext(log: ILogger, ruleAttr: string) /*: Function */ { +export function stringMatcherContext(log: ILogger, ruleAttr: string) /*: Function */ { return function stringMatcher(runtimeAttr: string): boolean { let re; diff --git a/src/evaluator/matchers/sw.ts b/src/evaluator/matchers/sw.ts index e2bfc03e..7f014b20 100644 --- a/src/evaluator/matchers/sw.ts +++ b/src/evaluator/matchers/sw.ts @@ -2,7 +2,7 @@ import { ENGINE_MATCHER_STARTS_WITH } from '../../logger/constants'; import { ILogger } from '../../logger/types'; import { startsWith } from '../../utils/lang'; -export default function startsWithMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { +export function startsWithMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ { return function startsWithMatcher(runtimeAttr: string): boolean { let matches = ruleAttr.some(e => startsWith(runtimeAttr, e)); diff --git a/src/evaluator/matchers/whitelist.ts b/src/evaluator/matchers/whitelist.ts index b84cb129..54fb35c8 100644 --- a/src/evaluator/matchers/whitelist.ts +++ b/src/evaluator/matchers/whitelist.ts @@ -2,7 +2,7 @@ import { setToArray, ISet } from '../../utils/lang/sets'; import { ILogger } from '../../logger/types'; import { ENGINE_MATCHER_WHITELIST } from '../../logger/constants'; -export default function whitelistMatcherContext(log: ILogger, ruleAttr: ISet) /*: Function */ { +export function whitelistMatcherContext(log: ILogger, ruleAttr: ISet) /*: Function */ { return function whitelistMatcher(runtimeAttr: string): boolean { let isInWhitelist = ruleAttr.has(runtimeAttr); diff --git a/src/evaluator/matchersTransform/__tests__/segment.spec.ts b/src/evaluator/matchersTransform/__tests__/segment.spec.ts index bfc9c2a8..1d25811e 100644 --- a/src/evaluator/matchersTransform/__tests__/segment.spec.ts +++ b/src/evaluator/matchersTransform/__tests__/segment.spec.ts @@ -1,4 +1,4 @@ -import transform from '../segment'; +import { segmentTransform } from '../segment'; test('TRANSFORMS / a segment object should be flatten to a string', function () { const segmentName = 'employees'; @@ -6,14 +6,14 @@ test('TRANSFORMS / a segment object should be flatten to a string', function () segmentName }; - const plainSegmentName = transform(sample); + const plainSegmentName = segmentTransform(sample); expect(segmentName).toBe(plainSegmentName); // extracted segmentName matches }); test('TRANSFORMS / if there is none segmentName entry, returns undefined', function () { const sample = undefined; - const undefinedSegmentName = transform(sample); + const undefinedSegmentName = segmentTransform(sample); expect(undefinedSegmentName).toBe(undefined); // expected to be undefined }); diff --git a/src/evaluator/matchersTransform/__tests__/whitelist.spec.ts b/src/evaluator/matchersTransform/__tests__/whitelist.spec.ts index c171631b..5f846390 100644 --- a/src/evaluator/matchersTransform/__tests__/whitelist.spec.ts +++ b/src/evaluator/matchersTransform/__tests__/whitelist.spec.ts @@ -1,4 +1,4 @@ -import transform from '../whitelist'; +import { whitelistTransform } from '../whitelist'; test('TRANSFORMS / a whitelist Array should be casted into a Set', function () { let sample = { @@ -9,7 +9,7 @@ test('TRANSFORMS / a whitelist Array should be casted into a Set', function () { ] }; - let sampleSet = transform(sample); + let sampleSet = whitelistTransform(sample); sample.whitelist.forEach(item => { if (!sampleSet.has(item)) { @@ -18,7 +18,7 @@ test('TRANSFORMS / a whitelist Array should be casted into a Set', function () { }); // @ts-ignore - sampleSet = transform({}); + sampleSet = whitelistTransform({}); expect(sampleSet.size).toBe(0); // Empty Set if passed an object without a whitelist expect(true).toBe(true); // Everything looks fine diff --git a/src/evaluator/matchersTransform/index.ts b/src/evaluator/matchersTransform/index.ts index abb1c8b0..b63b47f7 100644 --- a/src/evaluator/matchersTransform/index.ts +++ b/src/evaluator/matchersTransform/index.ts @@ -1,9 +1,9 @@ import { findIndex } from '../../utils/lang'; import { matcherTypes, matcherTypesMapper, matcherDataTypes } from '../matchers/matcherTypes'; -import segmentTransform from './segment'; -import whitelistTransform from './whitelist'; -import setTransform from './set'; -import numericTransform from './unaryNumeric'; +import { segmentTransform } from './segment'; +import { whitelistTransform } from './whitelist'; +import { setTransform } from './set'; +import { numericTransform } from './unaryNumeric'; import { zeroSinceHH, zeroSinceSS } from '../convertions'; import { IBetweenMatcherData, IInSegmentMatcherData, ISplitMatcher, IUnaryNumericMatcherData, IWhitelistMatcherData } from '../../dtos/types'; import { IMatcherDto } from '../types'; @@ -11,7 +11,7 @@ import { IMatcherDto } from '../types'; /** * Flat the complex matcherGroup structure into something handy. */ -export default function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] { +export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] { let parsedMatchers = matchers.map(matcher => { let { diff --git a/src/evaluator/matchersTransform/segment.ts b/src/evaluator/matchersTransform/segment.ts index 40783b8f..00674cf2 100644 --- a/src/evaluator/matchersTransform/segment.ts +++ b/src/evaluator/matchersTransform/segment.ts @@ -3,6 +3,6 @@ import { IInSegmentMatcherData } from '../../dtos/types'; /** * Extract segment name as a plain string. */ -export default function transform(segment?: IInSegmentMatcherData) { +export function segmentTransform(segment?: IInSegmentMatcherData) { return segment ? segment.segmentName : undefined; } diff --git a/src/evaluator/matchersTransform/set.ts b/src/evaluator/matchersTransform/set.ts index bc130fa1..aefbe612 100644 --- a/src/evaluator/matchersTransform/set.ts +++ b/src/evaluator/matchersTransform/set.ts @@ -3,6 +3,6 @@ import { IWhitelistMatcherData } from '../../dtos/types'; /** * Extract whitelist array. Used by set and string matchers */ -export default function transform(whitelistObject: IWhitelistMatcherData) { +export function setTransform(whitelistObject: IWhitelistMatcherData) { return whitelistObject.whitelist; } diff --git a/src/evaluator/matchersTransform/unaryNumeric.ts b/src/evaluator/matchersTransform/unaryNumeric.ts index b8df02f6..701e9925 100644 --- a/src/evaluator/matchersTransform/unaryNumeric.ts +++ b/src/evaluator/matchersTransform/unaryNumeric.ts @@ -3,6 +3,6 @@ import { IUnaryNumericMatcherData } from '../../dtos/types'; /** * Extract value from unary matcher data. */ -export default function transform(unaryNumericObject: IUnaryNumericMatcherData) { +export function numericTransform(unaryNumericObject: IUnaryNumericMatcherData) { return unaryNumericObject.value; } diff --git a/src/evaluator/matchersTransform/whitelist.ts b/src/evaluator/matchersTransform/whitelist.ts index 635bf935..8b74870d 100644 --- a/src/evaluator/matchersTransform/whitelist.ts +++ b/src/evaluator/matchersTransform/whitelist.ts @@ -4,6 +4,6 @@ import { _Set } from '../../utils/lang/sets'; /** * Extract whitelist as a set. Used by 'WHITELIST' matcher. */ -export default function transform(whitelistObject: IWhitelistMatcherData) { +export function whitelistTransform(whitelistObject: IWhitelistMatcherData) { return new _Set(whitelistObject.whitelist); } diff --git a/src/evaluator/parser/__tests__/boolean.spec.ts b/src/evaluator/parser/__tests__/boolean.spec.ts index 81f8a30b..7d304ec9 100644 --- a/src/evaluator/parser/__tests__/boolean.spec.ts +++ b/src/evaluator/parser/__tests__/boolean.spec.ts @@ -1,4 +1,4 @@ -import parser from '..'; +import { parser } from '..'; import { keyParser } from '../../../utils/key'; import { ISplitCondition } from '../../../dtos/types'; import { IEvaluation } from '../../types'; diff --git a/src/evaluator/parser/__tests__/index.spec.ts b/src/evaluator/parser/__tests__/index.spec.ts index a27fecc7..41176fbd 100644 --- a/src/evaluator/parser/__tests__/index.spec.ts +++ b/src/evaluator/parser/__tests__/index.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import parser from '..'; +import { parser } from '..'; import { keyParser } from '../../../utils/key'; import { ISplitCondition } from '../../../dtos/types'; import { bucket } from '../../../utils/murmur3/murmur3'; diff --git a/src/evaluator/parser/__tests__/invalidMatcher.spec.ts b/src/evaluator/parser/__tests__/invalidMatcher.spec.ts index 3ff988bd..1920bf91 100644 --- a/src/evaluator/parser/__tests__/invalidMatcher.spec.ts +++ b/src/evaluator/parser/__tests__/invalidMatcher.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import parser from '..'; +import { parser } from '..'; import { ISplitCondition } from '../../../dtos/types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/parser/__tests__/regex.spec.ts b/src/evaluator/parser/__tests__/regex.spec.ts index ee2b0904..176c961a 100644 --- a/src/evaluator/parser/__tests__/regex.spec.ts +++ b/src/evaluator/parser/__tests__/regex.spec.ts @@ -1,4 +1,4 @@ -import parser from '..'; +import { parser } from '..'; import { keyParser } from '../../../utils/key'; import { ISplitCondition } from '../../../dtos/types'; import { IEvaluation } from '../../types'; diff --git a/src/evaluator/parser/__tests__/set.spec.ts b/src/evaluator/parser/__tests__/set.spec.ts index de652ed3..5d46abb8 100644 --- a/src/evaluator/parser/__tests__/set.spec.ts +++ b/src/evaluator/parser/__tests__/set.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import parser from '..'; +import { parser } from '..'; import { keyParser } from '../../../utils/key'; import { ISplitCondition } from '../../../dtos/types'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/parser/__tests__/string.spec.ts b/src/evaluator/parser/__tests__/string.spec.ts index 326ade21..dc10f3c6 100644 --- a/src/evaluator/parser/__tests__/string.spec.ts +++ b/src/evaluator/parser/__tests__/string.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import parser from '..'; +import { parser } from '..'; import { ISplitCondition } from '../../../dtos/types'; import { keyParser } from '../../../utils/key'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/evaluator/parser/__tests__/trafficAllocation.spec.ts b/src/evaluator/parser/__tests__/trafficAllocation.spec.ts index ddf5afae..d9af5ca9 100644 --- a/src/evaluator/parser/__tests__/trafficAllocation.spec.ts +++ b/src/evaluator/parser/__tests__/trafficAllocation.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import parser from '..'; +import { parser } from '..'; import { keyParser } from '../../../utils/key'; import { ISplitCondition } from '../../../dtos/types'; import { IEvaluation } from '../../types'; diff --git a/src/evaluator/parser/index.ts b/src/evaluator/parser/index.ts index 4fdc6f0f..8e53a321 100644 --- a/src/evaluator/parser/index.ts +++ b/src/evaluator/parser/index.ts @@ -1,18 +1,18 @@ -import matchersTransform from '../matchersTransform'; -import Treatments from '../treatments'; -import matcherFactory from '../matchers'; -import sanitizeValue from '../value'; -import conditionFactory from '../condition'; -import ifElseIfCombiner from '../combiners/ifelseif'; -import andCombiner from '../combiners/and'; -import thenable from '../../utils/promise/thenable'; +import { matchersTransform } from '../matchersTransform'; +import { Treatments } from '../treatments'; +import { matcherFactory } from '../matchers'; +import { sanitizeValue } from '../value'; +import { conditionContext } from '../condition'; +import { ifElseIfCombinerContext } from '../combiners/ifelseif'; +import { andCombinerContext } from '../combiners/and'; +import { thenable } from '../../utils/promise/thenable'; import { IEvaluator, IMatcherDto, ISplitEvaluator } from '../types'; import { ISplitCondition } from '../../dtos/types'; import { IStorageAsync, IStorageSync } from '../../storages/types'; import { SplitIO } from '../../types'; import { ILogger } from '../../logger/types'; -export default function parser(log: ILogger, conditions: ISplitCondition[], storage: IStorageSync | IStorageAsync): IEvaluator { +export function parser(log: ILogger, conditions: ISplitCondition[], storage: IStorageSync | IStorageAsync): IEvaluator { let predicates = []; for (let i = 0; i < conditions.length; i++) { @@ -53,9 +53,9 @@ export default function parser(log: ILogger, conditions: ISplitCondition[], stor break; } - predicates.push(conditionFactory( + predicates.push(conditionContext( log, - andCombiner(log, expressions), + andCombinerContext(log, expressions), Treatments.parse(partitions), label, conditionType @@ -63,5 +63,5 @@ export default function parser(log: ILogger, conditions: ISplitCondition[], stor } // Instanciate evaluator given the set of conditions using if else if logic - return ifElseIfCombiner(log, predicates); + return ifElseIfCombinerContext(log, predicates); } diff --git a/src/evaluator/treatments/__tests__/index.spec.ts b/src/evaluator/treatments/__tests__/index.spec.ts index 453fb224..771127f1 100644 --- a/src/evaluator/treatments/__tests__/index.spec.ts +++ b/src/evaluator/treatments/__tests__/index.spec.ts @@ -1,4 +1,4 @@ -import Treatments from '..'; +import { Treatments } from '..'; test('TREATMENTS / parse 2 treatments', () => { let t = Treatments.parse([{ diff --git a/src/evaluator/treatments/index.ts b/src/evaluator/treatments/index.ts index 665bfd33..35f7da28 100644 --- a/src/evaluator/treatments/index.ts +++ b/src/evaluator/treatments/index.ts @@ -1,7 +1,7 @@ import { ISplitPartition } from '../../dtos/types'; import { findIndex } from '../../utils/lang'; -export default class Treatments { +export class Treatments { private _ranges: number[]; private _treatments: string[]; diff --git a/src/evaluator/value/index.ts b/src/evaluator/value/index.ts index 228d7d0a..319e05e2 100644 --- a/src/evaluator/value/index.ts +++ b/src/evaluator/value/index.ts @@ -1,7 +1,7 @@ import { SplitIO } from '../../types'; import { IMatcherDto } from '../types'; import { ILogger } from '../../logger/types'; -import sanitizeValue from './sanitize'; +import { sanitize } from './sanitize'; import { ENGINE_VALUE, ENGINE_VALUE_NO_ATTRIBUTES, ENGINE_VALUE_INVALID } from '../../logger/constants'; function parseValue(log: ILogger, key: string, attributeName: string | null, attributes: SplitIO.Attributes) { @@ -23,10 +23,10 @@ function parseValue(log: ILogger, key: string, attributeName: string | null, att /** * Defines value to be matched (key / attribute). */ -export default function value(log: ILogger, key: string, matcherDto: IMatcherDto, attributes: SplitIO.Attributes) { +export function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes: SplitIO.Attributes) { const attributeName = matcherDto.attribute; const valueToMatch = parseValue(log, key, attributeName, attributes); - const sanitizedValue = sanitizeValue(log, matcherDto.type, valueToMatch, matcherDto.dataType, attributes); + const sanitizedValue = sanitize(log, matcherDto.type, valueToMatch, matcherDto.dataType, attributes); if (sanitizedValue !== undefined) { return sanitizedValue; diff --git a/src/evaluator/value/sanitize.ts b/src/evaluator/value/sanitize.ts index 7ca7b5cd..6a36a59d 100644 --- a/src/evaluator/value/sanitize.ts +++ b/src/evaluator/value/sanitize.ts @@ -69,7 +69,7 @@ function getProcessingFunction(matcherTypeID: number, dataType: string) { /** * Sanitize matcher value */ -export default function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array | undefined, dataType: string, attributes: SplitIO.Attributes) { +export function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array | undefined, dataType: string, attributes: SplitIO.Attributes) { const processor = getProcessingFunction(matcherTypeID, dataType); let sanitizedValue: string | number | boolean | Array | IDependencyMatcherValue | undefined; diff --git a/src/integrations/__tests__/browser.spec.ts b/src/integrations/__tests__/browser.spec.ts index 1b79d8bb..40316654 100644 --- a/src/integrations/__tests__/browser.spec.ts +++ b/src/integrations/__tests__/browser.spec.ts @@ -33,7 +33,7 @@ function clearMocks() { } // Test target -import browserIMF from '../browser'; +import { integrationsManagerFactory as browserIMF } from '../browser'; import { BrowserIntegration } from '../ga/types'; describe('IntegrationsManagerFactory for browser', () => { diff --git a/src/integrations/browser.ts b/src/integrations/browser.ts index f3a17713..d4ad8de8 100644 --- a/src/integrations/browser.ts +++ b/src/integrations/browser.ts @@ -1,7 +1,7 @@ import { GOOGLE_ANALYTICS_TO_SPLIT, SPLIT_TO_GOOGLE_ANALYTICS } from '../utils/constants/browser'; import { IIntegration, IIntegrationManager, IIntegrationFactoryParams } from './types'; import { BrowserIntegration } from './ga/types'; -import pluggableIntegrationsManagerFactory from './pluggable'; +import { pluggableIntegrationsManagerFactory } from './pluggable'; import { GoogleAnalyticsToSplit } from './ga/GoogleAnalyticsToSplit'; import { SplitToGoogleAnalytics } from './ga/SplitToGoogleAnalytics'; @@ -14,7 +14,7 @@ import { SplitToGoogleAnalytics } from './ga/SplitToGoogleAnalytics'; * * @returns integration manager or undefined if `integrations` are not present in settings. */ -export default function integrationsManagerFactory( +export function integrationsManagerFactory( integrations: BrowserIntegration[], params: IIntegrationFactoryParams ): IIntegrationManager | undefined { diff --git a/src/integrations/ga/GaToSplit.ts b/src/integrations/ga/GaToSplit.ts index 360c93a1..334f6baa 100644 --- a/src/integrations/ga/GaToSplit.ts +++ b/src/integrations/ga/GaToSplit.ts @@ -1,5 +1,5 @@ /* eslint-disable no-undef */ -import objectAssign from 'object-assign'; +import { objectAssign } from '../../utils/lang/objectAssign'; import { isString, isFiniteNumber, uniqAsStrings } from '../../utils/lang'; import { validateEvent, diff --git a/src/integrations/pluggable.ts b/src/integrations/pluggable.ts index 14544f73..aed6806c 100644 --- a/src/integrations/pluggable.ts +++ b/src/integrations/pluggable.ts @@ -11,7 +11,7 @@ import { IIntegration, IIntegrationManager, IIntegrationFactoryParams } from './ * * @returns integration manager or undefined if `integrations` are not present in settings. */ -export default function integrationsManagerFactory( +export function pluggableIntegrationsManagerFactory( integrations: Array<(params: IIntegrationFactoryParams) => IIntegration | void>, params: IIntegrationFactoryParams ): IIntegrationManager | undefined { diff --git a/src/listeners/__tests__/browser.spec.ts b/src/listeners/__tests__/browser.spec.ts index f232b482..8af13e8b 100644 --- a/src/listeners/__tests__/browser.spec.ts +++ b/src/listeners/__tests__/browser.spec.ts @@ -1,4 +1,4 @@ -import BrowserSignalListener from '../browser'; +import { BrowserSignalListener } from '../browser'; import { IEventsCacheSync, IImpressionCountsCacheSync, IImpressionsCacheSync, IStorageSync } from '../../storages/types'; import { ISplitApi } from '../../services/types'; import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; diff --git a/src/listeners/__tests__/node.spec.ts b/src/listeners/__tests__/node.spec.ts index 1755fa7d..bc594df9 100644 --- a/src/listeners/__tests__/node.spec.ts +++ b/src/listeners/__tests__/node.spec.ts @@ -1,4 +1,4 @@ -import NodeSignalListener from '../node'; +import { NodeSignalListener } from '../node'; import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; const processOnSpy = jest.spyOn(process, 'on'); diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index 4ac90bfc..9705af2c 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -8,7 +8,7 @@ import { IResponse, ISplitApi } from '../services/types'; import { ImpressionDTO, ISettings } from '../types'; import { ImpressionsPayload } from '../sync/submitters/types'; import { OPTIMIZED, DEBUG } from '../utils/constants'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants'; import { ISyncManager } from '../sync/types'; @@ -19,7 +19,7 @@ const EVENT_NAME = 'for unload page event.'; /** * We'll listen for 'unload' event over the window object, since it's the standard way to listen page reload and close. */ -export default class BrowserSignalListener implements ISignalListener { +export class BrowserSignalListener implements ISignalListener { private fromImpressionsCollector: (data: ImpressionDTO[]) => ImpressionsPayload; diff --git a/src/listeners/node.ts b/src/listeners/node.ts index 9d8a22e9..f57557be 100644 --- a/src/listeners/node.ts +++ b/src/listeners/node.ts @@ -1,6 +1,6 @@ // @TODO eventually migrate to JS-Node-SDK package. import { ISignalListener } from './types'; -import thenable from '../utils/promise/thenable'; +import { thenable } from '../utils/promise/thenable'; import { MaybeThenable } from '../dtos/types'; import { ISettings } from '../types'; import { LOG_PREFIX_CLEANUP, CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants'; @@ -16,7 +16,7 @@ const EVENT_NAME = 'for SIGTERM signal.'; * you should call the cleanup logic yourself, since we cannot ensure the data is sent after * the process is already exiting. */ -export default class NodeSignalListener implements ISignalListener { +export class NodeSignalListener implements ISignalListener { private handler: () => MaybeThenable; private settings: ISettings; diff --git a/src/logger/index.ts b/src/logger/index.ts index ed2e5032..4fba156a 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -1,4 +1,4 @@ -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { ILoggerOptions, ILogger } from './types'; import { find } from '../utils/lang'; import { LogLevel } from '../types'; diff --git a/src/readiness/__tests__/sdkReadinessManager.spec.ts b/src/readiness/__tests__/sdkReadinessManager.spec.ts index 5bfda13e..c1929bdc 100644 --- a/src/readiness/__tests__/sdkReadinessManager.spec.ts +++ b/src/readiness/__tests__/sdkReadinessManager.spec.ts @@ -2,7 +2,7 @@ import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; import { IEventEmitter } from '../../types'; import { SDK_READY, SDK_READY_FROM_CACHE, SDK_READY_TIMED_OUT, SDK_UPDATE } from '../constants'; -import sdkReadinessManagerFactory from '../sdkReadinessManager'; +import { sdkReadinessManagerFactory } from '../sdkReadinessManager'; import { IReadinessManager } from '../types'; import { ERROR_CLIENT_LISTENER, CLIENT_READY_FROM_CACHE, CLIENT_READY, CLIENT_NO_LISTENER } from '../../logger/constants'; diff --git a/src/readiness/readinessManager.ts b/src/readiness/readinessManager.ts index 394ba056..7e0c0431 100644 --- a/src/readiness/readinessManager.ts +++ b/src/readiness/readinessManager.ts @@ -1,4 +1,4 @@ -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { IEventEmitter } from '../types'; import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants'; import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types'; @@ -12,7 +12,7 @@ function splitsEventEmitterFactory(EventEmitter: new () => IEventEmitter): ISpli // `isSplitKill` condition avoids an edge-case of wrongly emitting SDK_READY if: // - `/mySegments` fetch and SPLIT_KILL occurs before `/splitChanges` fetch, and // - storage has cached splits (for which case `splitsStorage.killLocally` can return true) - splitsEventEmitter.on(SDK_SPLITS_ARRIVED, (isSplitKill) => { if (!isSplitKill) splitsEventEmitter.splitsArrived = true; }); + splitsEventEmitter.on(SDK_SPLITS_ARRIVED, (isSplitKill: boolean) => { if (!isSplitKill) splitsEventEmitter.splitsArrived = true; }); splitsEventEmitter.once(SDK_SPLITS_CACHE_LOADED, () => { splitsEventEmitter.splitsCacheLoaded = true; }); return splitsEventEmitter; diff --git a/src/readiness/sdkReadinessManager.ts b/src/readiness/sdkReadinessManager.ts index 360527c2..52f519ad 100644 --- a/src/readiness/sdkReadinessManager.ts +++ b/src/readiness/sdkReadinessManager.ts @@ -1,5 +1,5 @@ -import objectAssign from 'object-assign'; -import promiseWrapper from '../utils/promise/wrapper'; +import { objectAssign } from '../utils/lang/objectAssign'; +import { promiseWrapper } from '../utils/promise/wrapper'; import { readinessManagerFactory } from './readinessManager'; import { ISdkReadinessManager } from './types'; import { IEventEmitter } from '../types'; @@ -19,7 +19,7 @@ const REMOVE_LISTENER_EVENT = 'removeListener'; * by the SDK. It is required to properly log the warning 'No listeners for SDK Readiness detected' * @param readinessManager optional readinessManager to use. only used internally for `shared` method */ -export default function sdkReadinessManagerFactory( +export function sdkReadinessManagerFactory( log: ILogger, EventEmitter: new () => IEventEmitter, readyTimeout = 0, diff --git a/src/sdkClient/__tests__/sdkClientMethodCS.spec.ts b/src/sdkClient/__tests__/sdkClientMethodCS.spec.ts index 345909f9..0a5c8e59 100644 --- a/src/sdkClient/__tests__/sdkClientMethodCS.spec.ts +++ b/src/sdkClient/__tests__/sdkClientMethodCS.spec.ts @@ -4,7 +4,7 @@ import { assertClientApi } from './testUtils'; /** Mocks */ import * as clientCS from '../clientCS'; -const clientCSDecoratorSpy = jest.spyOn(clientCS, 'default'); +const clientCSDecoratorSpy = jest.spyOn(clientCS, 'clientCSDecorator'); import { settingsWithKey, settingsWithKeyAndTT, settingsWithKeyObject } from '../../utils/settingsValidation/__tests__/settings.mocks'; diff --git a/src/sdkClient/client.ts b/src/sdkClient/client.ts index c6889678..a572047e 100644 --- a/src/sdkClient/client.ts +++ b/src/sdkClient/client.ts @@ -1,5 +1,5 @@ import { evaluateFeature, evaluateFeatures } from '../evaluator'; -import thenable from '../utils/promise/thenable'; +import { thenable } from '../utils/promise/thenable'; import { getMatching, getBucketing } from '../utils/key'; import { validateSplitExistance } from '../utils/inputValidation/splitExistance'; import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance'; @@ -15,7 +15,7 @@ import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants'; * Creator of base client with getTreatments and track methods. */ // @TODO missing time tracking to collect telemetry -export default function clientFactory(params: IClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient { +export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient { const { sdkReadinessManager: { readinessManager }, storage, settings: { log, mode }, impressionsTracker, eventTracker } = params; function getTreatment(key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, withConfig = false) { diff --git a/src/sdkClient/clientCS.ts b/src/sdkClient/clientCS.ts index 50a4ba2e..dee6a6af 100644 --- a/src/sdkClient/clientCS.ts +++ b/src/sdkClient/clientCS.ts @@ -1,4 +1,4 @@ -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { SplitIO } from '../types'; @@ -9,7 +9,7 @@ import { SplitIO } from '../types'; * @param key validated split key * @param trafficType validated traffic type */ -export default function clientCSDecorator(client: SplitIO.IClient, key: SplitIO.SplitKey, trafficType?: string): SplitIO.ICsClient { +export function clientCSDecorator(client: SplitIO.IClient, key: SplitIO.SplitKey, trafficType?: string): SplitIO.ICsClient { return objectAssign(client, { // In the client-side API, we bind a key to the client `getTreatment*` methods getTreatment: client.getTreatment.bind(client, key), diff --git a/src/sdkClient/clientInputValidation.ts b/src/sdkClient/clientInputValidation.ts index c0fc26bf..df0eaebc 100644 --- a/src/sdkClient/clientInputValidation.ts +++ b/src/sdkClient/clientInputValidation.ts @@ -1,4 +1,4 @@ -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { validateAttributes, validateEvent, @@ -22,7 +22,7 @@ import { ILogger } from '../logger/types'; * Decorator that validates the input before actually executing the client methods. * We should "guard" the client here, while not polluting the "real" implementation of those methods. */ -export default function clientInputValidationDecorator(log: ILogger, client: TClient, readinessManager: IReadinessManager, isStorageSync = false): TClient { +export function clientInputValidationDecorator(log: ILogger, client: TClient, readinessManager: IReadinessManager, isStorageSync = false): TClient { /** * Avoid repeating this validations code diff --git a/src/sdkClient/sdkClient.ts b/src/sdkClient/sdkClient.ts index e0401347..6630cd94 100644 --- a/src/sdkClient/sdkClient.ts +++ b/src/sdkClient/sdkClient.ts @@ -1,9 +1,9 @@ -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { IStatusInterface, SplitIO } from '../types'; import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../utils/constants'; import { releaseApiKey } from '../utils/inputValidation/apiKey'; -import clientFactory from './client'; -import clientInputValidationDecorator from './clientInputValidation'; +import { clientFactory } from './client'; +import { clientInputValidationDecorator } from './clientInputValidation'; import { ISdkClientFactoryParams } from './types'; /** diff --git a/src/sdkClient/sdkClientMethodCS.ts b/src/sdkClient/sdkClientMethodCS.ts index 22254aaf..c0d28f60 100644 --- a/src/sdkClient/sdkClientMethodCS.ts +++ b/src/sdkClient/sdkClientMethodCS.ts @@ -1,11 +1,11 @@ -import clientCSDecorator from './clientCS'; +import { clientCSDecorator } from './clientCS'; import { ISdkClientFactoryParams } from './types'; import { SplitIO } from '../types'; import { validateKey } from '../utils/inputValidation/key'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; import { ISyncManagerCS } from '../sync/types'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants'; diff --git a/src/sdkClient/sdkClientMethodCSWithTT.ts b/src/sdkClient/sdkClientMethodCSWithTT.ts index 6b8fa9ef..1c844296 100644 --- a/src/sdkClient/sdkClientMethodCSWithTT.ts +++ b/src/sdkClient/sdkClientMethodCSWithTT.ts @@ -1,4 +1,4 @@ -import clientCSDecorator from './clientCS'; +import { clientCSDecorator } from './clientCS'; import { ISdkClientFactoryParams } from './types'; import { SplitIO } from '../types'; import { validateKey } from '../utils/inputValidation/key'; @@ -6,7 +6,7 @@ import { validateTrafficType } from '../utils/inputValidation/trafficType'; import { getMatching, keyParser } from '../utils/key'; import { sdkClientFactory } from './sdkClient'; import { ISyncManagerCS } from '../sync/types'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants'; import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants'; diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index f074f387..5fe1f10f 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -1,7 +1,7 @@ import { ISdkFactoryParams } from './types'; -import sdkReadinessManagerFactory from '../readiness/sdkReadinessManager'; -import impressionsTrackerFactory from '../trackers/impressionsTracker'; -import eventTrackerFactory from '../trackers/eventTracker'; +import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager'; +import { impressionsTrackerFactory } from '../trackers/impressionsTracker'; +import { eventTrackerFactory } from '../trackers/eventTracker'; import { IStorageFactoryParams, IStorageSync } from '../storages/types'; import { SplitIO } from '../types'; import { ISplitApi } from '../services/types'; diff --git a/src/sdkManager/__tests__/index.asyncCache.spec.ts b/src/sdkManager/__tests__/index.asyncCache.spec.ts index 9829f9e2..202c0016 100644 --- a/src/sdkManager/__tests__/index.asyncCache.spec.ts +++ b/src/sdkManager/__tests__/index.asyncCache.spec.ts @@ -2,10 +2,10 @@ import Redis from 'ioredis'; import splitObject from './mocks/input.json'; import splitView from './mocks/output.json'; import { sdkManagerFactory } from '../index'; -import SplitsCacheInRedis from '../../storages/inRedis/SplitsCacheInRedis'; +import { SplitsCacheInRedis } from '../../storages/inRedis/SplitsCacheInRedis'; import { SplitsCachePluggable } from '../../storages/pluggable/SplitsCachePluggable'; import { wrapperAdapter } from '../../storages/pluggable/wrapperAdapter'; -import KeyBuilderSS from '../../storages/KeyBuilderSS'; +import { KeyBuilderSS } from '../../storages/KeyBuilderSS'; import { ISdkReadinessManager } from '../../readiness/types'; import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; diff --git a/src/sdkManager/__tests__/index.syncCache.spec.ts b/src/sdkManager/__tests__/index.syncCache.spec.ts index 4da65cd4..96238c1b 100644 --- a/src/sdkManager/__tests__/index.syncCache.spec.ts +++ b/src/sdkManager/__tests__/index.syncCache.spec.ts @@ -1,7 +1,7 @@ import splitObject from './mocks/input.json'; import splitView from './mocks/output.json'; import { sdkManagerFactory } from '../index'; -import SplitsCacheInMemory from '../../storages/inMemory/SplitsCacheInMemory'; +import { SplitsCacheInMemory } from '../../storages/inMemory/SplitsCacheInMemory'; import { ISdkReadinessManager } from '../../readiness/types'; import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; diff --git a/src/sdkManager/index.ts b/src/sdkManager/index.ts index 2536f121..e0ec5024 100644 --- a/src/sdkManager/index.ts +++ b/src/sdkManager/index.ts @@ -1,5 +1,5 @@ -import objectAssign from 'object-assign'; -import thenable from '../utils/promise/thenable'; +import { objectAssign } from '../utils/lang/objectAssign'; +import { thenable } from '../utils/promise/thenable'; import { find } from '../utils/lang'; import { validateSplit, validateSplitExistance, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation'; import { ISplitsCacheAsync, ISplitsCacheSync } from '../storages/types'; diff --git a/src/services/splitApi.ts b/src/services/splitApi.ts index a1856a8e..dbbcb953 100644 --- a/src/services/splitApi.ts +++ b/src/services/splitApi.ts @@ -2,7 +2,7 @@ import { IPlatform } from '../sdkFactory/types'; import { ISettings } from '../types'; import { splitHttpClientFactory } from './splitHttpClient'; import { ISplitApi } from './types'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } }; diff --git a/src/services/splitHttpClient.ts b/src/services/splitHttpClient.ts index 8f413f5e..ee031149 100644 --- a/src/services/splitHttpClient.ts +++ b/src/services/splitHttpClient.ts @@ -1,5 +1,5 @@ import { IFetch, IRequestOptions, IResponse, ISplitHttpClient } from './types'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../utils/lang/objectAssign'; import { ERROR_HTTP, ERROR_CLIENT_CANNOT_GET_READY } from '../logger/constants'; import { ISettings } from '../types'; diff --git a/src/storages/AbstractSegmentsCacheSync.ts b/src/storages/AbstractSegmentsCacheSync.ts index 6b2799dc..ad371912 100644 --- a/src/storages/AbstractSegmentsCacheSync.ts +++ b/src/storages/AbstractSegmentsCacheSync.ts @@ -6,7 +6,7 @@ import { ISegmentsCacheSync } from './types'; * This class provides a skeletal implementation of the ISegmentsCacheSync interface * to minimize the effort required to implement this interface. */ -export default abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync { +export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync { /** * For server-side synchronizer: add `segmentKeys` list of keys to `name` segment. * For client-side synchronizer: add `name` segment to the cache. `segmentKeys` is undefined. diff --git a/src/storages/AbstractSplitsCacheAsync.ts b/src/storages/AbstractSplitsCacheAsync.ts index e213b9a3..6c44ffcd 100644 --- a/src/storages/AbstractSplitsCacheAsync.ts +++ b/src/storages/AbstractSplitsCacheAsync.ts @@ -5,7 +5,7 @@ import { ISplit } from '../dtos/types'; * This class provides a skeletal implementation of the ISplitsCacheAsync interface * to minimize the effort required to implement this interface. */ -export default abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync { +export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync { abstract addSplit(name: string, split: string): Promise abstract addSplits(entries: [string, string][]): Promise diff --git a/src/storages/AbstractSplitsCacheSync.ts b/src/storages/AbstractSplitsCacheSync.ts index 9bc31467..e07ee914 100644 --- a/src/storages/AbstractSplitsCacheSync.ts +++ b/src/storages/AbstractSplitsCacheSync.ts @@ -5,7 +5,7 @@ import { ISplit } from '../dtos/types'; * This class provides a skeletal implementation of the ISplitsCacheSync interface * to minimize the effort required to implement this interface. */ -export default abstract class AbstractSplitsCacheSync implements ISplitsCacheSync { +export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync { abstract addSplit(name: string, split: string): boolean; diff --git a/src/storages/KeyBuilder.ts b/src/storages/KeyBuilder.ts index b761b7dd..080ed3d9 100644 --- a/src/storages/KeyBuilder.ts +++ b/src/storages/KeyBuilder.ts @@ -12,7 +12,7 @@ export function validatePrefix(prefix: unknown) { DEFAULT_PREFIX; // use default prefix if none is provided } -export default class KeyBuilder { +export class KeyBuilder { protected readonly prefix: string; diff --git a/src/storages/KeyBuilderCS.ts b/src/storages/KeyBuilderCS.ts index 6bfe7adf..d4f9e902 100644 --- a/src/storages/KeyBuilderCS.ts +++ b/src/storages/KeyBuilderCS.ts @@ -1,7 +1,7 @@ import { startsWith } from '../utils/lang'; -import KeyBuilder from './KeyBuilder'; +import { KeyBuilder } from './KeyBuilder'; -export default class KeyBuilderCS extends KeyBuilder { +export class KeyBuilderCS extends KeyBuilder { protected readonly regexSplitsCacheKey: RegExp; protected readonly matchingKey: string; diff --git a/src/storages/KeyBuilderSS.ts b/src/storages/KeyBuilderSS.ts index 8944eecb..2f8898c1 100644 --- a/src/storages/KeyBuilderSS.ts +++ b/src/storages/KeyBuilderSS.ts @@ -1,11 +1,11 @@ -import KeyBuilder from './KeyBuilder'; +import { KeyBuilder } from './KeyBuilder'; import { IMetadata } from '../dtos/types'; // NOT USED // const everythingAfterCount = /count\.([^/]+)$/; // const latencyMetricNameAndBucket = /latency\.([^/]+)\.bucket\.([0-9]+)$/; -export default class KeyBuilderSS extends KeyBuilder { +export class KeyBuilderSS extends KeyBuilder { protected readonly metadata: IMetadata; diff --git a/src/storages/__tests__/KeyBuilder.spec.ts b/src/storages/__tests__/KeyBuilder.spec.ts index 6016d169..e0b82ebe 100644 --- a/src/storages/__tests__/KeyBuilder.spec.ts +++ b/src/storages/__tests__/KeyBuilder.spec.ts @@ -1,5 +1,5 @@ -import KeyBuilder from '../KeyBuilder'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilder } from '../KeyBuilder'; +import { KeyBuilderSS } from '../KeyBuilderSS'; test('KEYS / splits keys', () => { const builder = new KeyBuilder(); diff --git a/src/storages/findLatencyIndex.ts b/src/storages/findLatencyIndex.ts index b060abd6..908752d9 100644 --- a/src/storages/findLatencyIndex.ts +++ b/src/storages/findLatencyIndex.ts @@ -1,7 +1,7 @@ import { isNaNNumber } from '../utils/lang'; // @TODO add unit tests -export default function findLatencyIndex(latency: number, min = 0, max = 23, base = 1.5): number { +export function findLatencyIndex(latency: number, min = 0, max = 23, base = 1.5): number { const index = Math.min(max, Math.max(min, Math.floor(Math.log(latency) / Math.log(base)))); return isNaNNumber(index) ? 0 : index; // index is NaN if latency is not a positive number } diff --git a/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts b/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts index 12289470..7bc6690a 100644 --- a/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +++ b/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts @@ -1,9 +1,9 @@ import { ILogger } from '../../logger/types'; -import AbstractSegmentsCacheSync from '../AbstractSegmentsCacheSync'; -import KeyBuilderCS from '../KeyBuilderCS'; +import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync'; +import { KeyBuilderCS } from '../KeyBuilderCS'; import { LOG_PREFIX, DEFINED } from './constants'; -export default class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync { +export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync { private readonly keys: KeyBuilderCS; private readonly log: ILogger; diff --git a/src/storages/inLocalStorage/SplitsCacheInLocal.ts b/src/storages/inLocalStorage/SplitsCacheInLocal.ts index 68565b55..c35cc15b 100644 --- a/src/storages/inLocalStorage/SplitsCacheInLocal.ts +++ b/src/storages/inLocalStorage/SplitsCacheInLocal.ts @@ -1,14 +1,14 @@ import { ISplit, ISplitFiltersValidation } from '../../dtos/types'; -import AbstractSplitsCacheSync, { usesSegments } from '../AbstractSplitsCacheSync'; +import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync'; import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang'; -import KeyBuilderCS from '../KeyBuilderCS'; +import { KeyBuilderCS } from '../KeyBuilderCS'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; /** * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage. */ -export default class SplitsCacheInLocal extends AbstractSplitsCacheSync { +export class SplitsCacheInLocal extends AbstractSplitsCacheSync { private readonly keys: KeyBuilderCS; private readonly splitFiltersValidation: ISplitFiltersValidation; diff --git a/src/storages/inLocalStorage/__tests__/MySegmentsCacheInLocal.spec.ts b/src/storages/inLocalStorage/__tests__/MySegmentsCacheInLocal.spec.ts index 158f8c2c..36072550 100644 --- a/src/storages/inLocalStorage/__tests__/MySegmentsCacheInLocal.spec.ts +++ b/src/storages/inLocalStorage/__tests__/MySegmentsCacheInLocal.spec.ts @@ -1,5 +1,5 @@ -import MySegmentsCacheInLocal from '../MySegmentsCacheInLocal'; -import KeyBuilderCS from '../../KeyBuilderCS'; +import { MySegmentsCacheInLocal } from '../MySegmentsCacheInLocal'; +import { KeyBuilderCS } from '../../KeyBuilderCS'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; test('SEGMENT CACHE / in LocalStorage', () => { diff --git a/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts b/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts index 0f3172f7..d43bb222 100644 --- a/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts +++ b/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts @@ -1,5 +1,5 @@ -import SplitsCacheInLocal from '../SplitsCacheInLocal'; -import KeyBuilderCS from '../../KeyBuilderCS'; +import { SplitsCacheInLocal } from '../SplitsCacheInLocal'; +import { KeyBuilderCS } from '../../KeyBuilderCS'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { splitWithUserTT, splitWithAccountTT, splitWithAccountTTAndUsesSegments } from '../../__tests__/testUtils'; diff --git a/src/storages/inLocalStorage/index.ts b/src/storages/inLocalStorage/index.ts index 795fd95b..0d35341b 100644 --- a/src/storages/inLocalStorage/index.ts +++ b/src/storages/inLocalStorage/index.ts @@ -1,14 +1,14 @@ -import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; -import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; -import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; +import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory'; +import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory'; +import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory'; import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types'; import { validatePrefix } from '../KeyBuilder'; -import KeyBuilderCS from '../KeyBuilderCS'; +import { KeyBuilderCS } from '../KeyBuilderCS'; import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable'; -import SplitsCacheInLocal from './SplitsCacheInLocal'; -import MySegmentsCacheInLocal from './MySegmentsCacheInLocal'; -import MySegmentsCacheInMemory from '../inMemory/MySegmentsCacheInMemory'; -import SplitsCacheInMemory from '../inMemory/SplitsCacheInMemory'; +import { SplitsCacheInLocal } from './SplitsCacheInLocal'; +import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal'; +import { MySegmentsCacheInMemory } from '../inMemory/MySegmentsCacheInMemory'; +import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory'; import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser'; import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS'; import { LOG_PREFIX } from './constants'; diff --git a/src/storages/inMemory/CountsCacheInMemory.ts b/src/storages/inMemory/CountsCacheInMemory.ts index 8f5dd1fd..ff7cd591 100644 --- a/src/storages/inMemory/CountsCacheInMemory.ts +++ b/src/storages/inMemory/CountsCacheInMemory.ts @@ -1,6 +1,6 @@ import { ICountsCacheSync } from '../types'; -export default class CountsCacheInMemory implements ICountsCacheSync { +export class CountsCacheInMemory implements ICountsCacheSync { private counters: Record = {}; diff --git a/src/storages/inMemory/EventsCacheInMemory.ts b/src/storages/inMemory/EventsCacheInMemory.ts index e60d00d6..d209cba6 100644 --- a/src/storages/inMemory/EventsCacheInMemory.ts +++ b/src/storages/inMemory/EventsCacheInMemory.ts @@ -3,7 +3,7 @@ import { IEventsCacheSync } from '../types'; const MAX_QUEUE_BYTE_SIZE = 5 * 1024 * 1024; // 5M -export default class EventsCacheInMemory implements IEventsCacheSync { +export class EventsCacheInMemory implements IEventsCacheSync { private onFullQueue?: () => void; private readonly maxQueue: number; diff --git a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts index db6b31c6..f7598d2a 100644 --- a/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionCountsCacheInMemory.ts @@ -1,7 +1,7 @@ import { truncateTimeFrame } from '../../utils/time'; import { IImpressionCountsCacheSync } from '../types'; -export default class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync { +export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync { private cache: Record = {}; /** diff --git a/src/storages/inMemory/ImpressionsCacheInMemory.ts b/src/storages/inMemory/ImpressionsCacheInMemory.ts index 9b9e3faa..dcbf6b7a 100644 --- a/src/storages/inMemory/ImpressionsCacheInMemory.ts +++ b/src/storages/inMemory/ImpressionsCacheInMemory.ts @@ -1,7 +1,7 @@ import { IImpressionsCacheSync } from '../types'; import { ImpressionDTO } from '../../types'; -export default class ImpressionsCacheInMemory implements IImpressionsCacheSync { +export class ImpressionsCacheInMemory implements IImpressionsCacheSync { private queue: ImpressionDTO[] = []; diff --git a/src/storages/inMemory/InMemoryStorage.ts b/src/storages/inMemory/InMemoryStorage.ts index 173fbf3a..00d666fc 100644 --- a/src/storages/inMemory/InMemoryStorage.ts +++ b/src/storages/inMemory/InMemoryStorage.ts @@ -1,9 +1,9 @@ -import SplitsCacheInMemory from './SplitsCacheInMemory'; -import SegmentsCacheInMemory from './SegmentsCacheInMemory'; -import ImpressionsCacheInMemory from './ImpressionsCacheInMemory'; -import EventsCacheInMemory from './EventsCacheInMemory'; +import { SplitsCacheInMemory } from './SplitsCacheInMemory'; +import { SegmentsCacheInMemory } from './SegmentsCacheInMemory'; +import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory'; +import { EventsCacheInMemory } from './EventsCacheInMemory'; import { IStorageFactoryParams, IStorageSync } from '../types'; -import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory'; +import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory'; import { STORAGE_MEMORY } from '../../utils/constants'; /** diff --git a/src/storages/inMemory/InMemoryStorageCS.ts b/src/storages/inMemory/InMemoryStorageCS.ts index 8f0eff19..7ac34ea8 100644 --- a/src/storages/inMemory/InMemoryStorageCS.ts +++ b/src/storages/inMemory/InMemoryStorageCS.ts @@ -1,9 +1,9 @@ -import SplitsCacheInMemory from './SplitsCacheInMemory'; -import MySegmentsCacheInMemory from './MySegmentsCacheInMemory'; -import ImpressionsCacheInMemory from './ImpressionsCacheInMemory'; -import EventsCacheInMemory from './EventsCacheInMemory'; +import { SplitsCacheInMemory } from './SplitsCacheInMemory'; +import { MySegmentsCacheInMemory } from './MySegmentsCacheInMemory'; +import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory'; +import { EventsCacheInMemory } from './EventsCacheInMemory'; import { IStorageSync, IStorageFactoryParams } from '../types'; -import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory'; +import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory'; import { STORAGE_MEMORY } from '../../utils/constants'; /** diff --git a/src/storages/inMemory/LatenciesCacheInMemory.ts b/src/storages/inMemory/LatenciesCacheInMemory.ts index 2ec6f054..240b5a8b 100644 --- a/src/storages/inMemory/LatenciesCacheInMemory.ts +++ b/src/storages/inMemory/LatenciesCacheInMemory.ts @@ -1,7 +1,7 @@ import { ILatenciesCacheSync } from '../types'; -import findLatencyIndex from '../findLatencyIndex'; +import { findLatencyIndex } from '../findLatencyIndex'; -export default class LatenciesCacheInMemory implements ILatenciesCacheSync { +export class LatenciesCacheInMemory implements ILatenciesCacheSync { private counters: Record = {}; diff --git a/src/storages/inMemory/MySegmentsCacheInMemory.ts b/src/storages/inMemory/MySegmentsCacheInMemory.ts index 041e31cb..02519d18 100644 --- a/src/storages/inMemory/MySegmentsCacheInMemory.ts +++ b/src/storages/inMemory/MySegmentsCacheInMemory.ts @@ -1,10 +1,10 @@ -import AbstractSegmentsCacheSync from '../AbstractSegmentsCacheSync'; +import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync'; /** * Default MySegmentsCacheInMemory implementation that stores MySegments in memory. * Supported by all JS runtimes. */ -export default class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync { +export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync { private segmentCache: Record = {}; diff --git a/src/storages/inMemory/SegmentsCacheInMemory.ts b/src/storages/inMemory/SegmentsCacheInMemory.ts index 70f4d691..ca86c86d 100644 --- a/src/storages/inMemory/SegmentsCacheInMemory.ts +++ b/src/storages/inMemory/SegmentsCacheInMemory.ts @@ -1,4 +1,4 @@ -import AbstractSegmentsCacheSync from '../AbstractSegmentsCacheSync'; +import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync'; import { ISet, _Set } from '../../utils/lang/sets'; import { isIntegerNumber } from '../../utils/lang'; @@ -6,7 +6,7 @@ import { isIntegerNumber } from '../../utils/lang'; * Default ISplitsCacheSync implementation that stores split definitions in memory. * Supported by all JS runtimes. */ -export default class SegmentsCacheInMemory extends AbstractSegmentsCacheSync { +export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync { private segmentCache: Record> = {}; private segmentChangeNumber: Record = {}; diff --git a/src/storages/inMemory/SplitsCacheInMemory.ts b/src/storages/inMemory/SplitsCacheInMemory.ts index 39eefdcf..32b0ed32 100644 --- a/src/storages/inMemory/SplitsCacheInMemory.ts +++ b/src/storages/inMemory/SplitsCacheInMemory.ts @@ -1,12 +1,12 @@ import { ISplit } from '../../dtos/types'; -import AbstractSplitsCacheSync, { usesSegments } from '../AbstractSplitsCacheSync'; +import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync'; import { isFiniteNumber } from '../../utils/lang'; /** * Default ISplitsCacheSync implementation that stores split definitions in memory. * Supported by all JS runtimes. */ -export default class SplitsCacheInMemory extends AbstractSplitsCacheSync { +export class SplitsCacheInMemory extends AbstractSplitsCacheSync { private splitsCache: Record = {}; private ttCache: Record = {}; diff --git a/src/storages/inMemory/__tests__/CountsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/CountsCacheInMemory.spec.ts index e4f411d8..bb8ebcc0 100644 --- a/src/storages/inMemory/__tests__/CountsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/CountsCacheInMemory.spec.ts @@ -1,7 +1,7 @@ -import CountsCache from '../CountsCacheInMemory'; +import { CountsCacheInMemory } from '../CountsCacheInMemory'; test('COUNT CACHE IN MEMORY / should count metric names incrementatly', () => { - const cache = new CountsCache(); + const cache = new CountsCacheInMemory(); cache.track('counted-metric-one'); cache.track('counted-metric-one'); diff --git a/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts index a4d73884..37a6a4ad 100644 --- a/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/EventsCacheInMemory.spec.ts @@ -1,16 +1,16 @@ // @ts-nocheck -import EventsCache from '../EventsCacheInMemory'; +import { EventsCacheInMemory } from '../EventsCacheInMemory'; test('EVENTS CACHE / Should be able to instantiate and start with an empty queue', () => { let cache; - const createInstance = () => cache = new EventsCache(500); // 500 as eventsQueueSize + const createInstance = () => cache = new EventsCacheInMemory(500); // 500 as eventsQueueSize expect(createInstance).not.toThrow(); // Creation should not throw. expect(cache.state()).toEqual([]); // The queue starts empty. }); test('EVENTS CACHE / Should be able to add items sequentially and retrieve the queue', () => { - const cache = new EventsCache(500); + const cache = new EventsCacheInMemory(500); const queueValues = [1, '2', { p: 3 }, ['4']]; expect(cache.track.bind(cache, queueValues[0])).not.toThrow(); // Calling track should not throw @@ -26,7 +26,7 @@ test('EVENTS CACHE / Should be able to add items sequentially and retrieve the q }); test('EVENTS CACHE / Should be able to clear the queue and accumulated byte size', () => { - const cache = new EventsCache(500); + const cache = new EventsCacheInMemory(500); cache.track('test1', 2019); cache.clear(); @@ -36,7 +36,7 @@ test('EVENTS CACHE / Should be able to clear the queue and accumulated byte size }); test('EVENTS CACHE / Should be able to tell if the queue is empty', () => { - const cache = new EventsCache(500); + const cache = new EventsCacheInMemory(500); expect(cache.state().length === 0).toBe(true); // The queue is empty, expect(cache.isEmpty()).toBe(true); // so if it is empty, it returns true. @@ -48,7 +48,7 @@ test('EVENTS CACHE / Should be able to tell if the queue is empty', () => { }); test('EVENTS CACHE / Should be able to return the DTO we will send to BE', () => { - const cache = new EventsCache(500); + const cache = new EventsCacheInMemory(500); const queueValues = [1, '2', { p: 3 }, ['4']]; cache.track(queueValues[0]); @@ -63,7 +63,7 @@ test('EVENTS CACHE / Should be able to return the DTO we will send to BE', () => test('EVENTS CACHE / Should call "onFullQueueCb" when the queue is full (count wise).', () => { let cbCalled = 0; - const cache = new EventsCache(3); // small eventsQueueSize to be reached + const cache = new EventsCacheInMemory(3); // small eventsQueueSize to be reached cache.setOnFullQueueCb(() => { cbCalled++; cache.clear(); }); cache.track(0); @@ -89,7 +89,7 @@ test('EVENTS CACHE / Should call "onFullQueueCb" when the queue is full (count w test('EVENTS CACHE / Should call "onFullQueueCb" when the queue is full (size wise).', () => { let cbCalled = 0; - const cache = new EventsCache(99999999); // big eventsQueueSize to never be reached + const cache = new EventsCacheInMemory(99999999); // big eventsQueueSize to never be reached cache.setOnFullQueueCb(() => { cbCalled++; cache.clear(); }); // The track method receives the size, which calculation is validated elsewhere. @@ -108,7 +108,7 @@ test('EVENTS CACHE / Should call "onFullQueueCb" when the queue is full (size wi test('EVENTS CACHE / Should not throw if the "onFullQueueCb" callback was not provided.', () => { - const cache = new EventsCache(3); // small eventsQueueSize to be reached + const cache = new EventsCacheInMemory(3); // small eventsQueueSize to be reached cache.track(0, 2048); cache.track(1, 1024); // Cache still not full, diff --git a/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts index 68288808..00d878c9 100644 --- a/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/ImpressionCountsCacheInMemory.spec.ts @@ -1,9 +1,9 @@ // @ts-nocheck -import ImpressionCountsCache from '../ImpressionCountsCacheInMemory'; +import { ImpressionCountsCacheInMemory } from '../ImpressionCountsCacheInMemory'; test('IMPRESSION COUNTS CACHE / Impression Counter Test makeKey', () => { const timestamp = new Date(2020, 9, 2, 10, 0, 0).getTime(); - const counter = new ImpressionCountsCache(); + const counter = new ImpressionCountsCacheInMemory(); expect(counter._makeKey('someFeature', new Date(2020, 9, 2, 10, 53, 12).getTime())).toBe(`someFeature::${timestamp}`); expect(counter._makeKey('', new Date(2020, 9, 2, 10, 53, 12).getTime())).toBe(`::${timestamp}`); @@ -13,7 +13,7 @@ test('IMPRESSION COUNTS CACHE / Impression Counter Test makeKey', () => { test('IMPRESSION COUNTS CACHE / Impression Counter Test BasicUsage', () => { const timestamp = new Date(2020, 9, 2, 10, 10, 12).getTime(); - const counter = new ImpressionCountsCache(); + const counter = new ImpressionCountsCacheInMemory(); counter.track('feature1', timestamp, 1); counter.track('feature1', timestamp + 1, 1); counter.track('feature1', timestamp + 2, 1); diff --git a/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts index f98644c6..e5f7ed1b 100644 --- a/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/ImpressionsCacheInMemory.spec.ts @@ -1,8 +1,8 @@ -import ImpressionsCache from '../ImpressionsCacheInMemory'; +import { ImpressionsCacheInMemory } from '../ImpressionsCacheInMemory'; test('IMPRESSIONS CACHE IN MEMORY / should incrementally store values', () => { - const c = new ImpressionsCache(); + const c = new ImpressionsCacheInMemory(); // @ts-expect-error c.track([0]); // @ts-expect-error diff --git a/src/storages/inMemory/__tests__/LatenciesCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/LatenciesCacheInMemory.spec.ts index 9e43668e..18ad0a3a 100644 --- a/src/storages/inMemory/__tests__/LatenciesCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/LatenciesCacheInMemory.spec.ts @@ -1,8 +1,8 @@ -import LatenciesCache from '../LatenciesCacheInMemory'; +import { LatenciesCacheInMemory } from '../LatenciesCacheInMemory'; test('METRICS (LATENCIES) CACHE IN MEMORY / should count based on ranges', () => { const metricName = 'testing'; - const c1 = new LatenciesCache(); + const c1 = new LatenciesCacheInMemory(); c1.track(metricName, 1); c1.track(metricName, 1.2); @@ -25,7 +25,7 @@ test('METRICS (LATENCIES) CACHE IN MEMORY / should count based on ranges', () => test('METRICS (LATENCIES) CACHE IN MEMORY / clear', () => { const metricName = 'testing'; - const c1 = new LatenciesCache(); + const c1 = new LatenciesCacheInMemory(); c1.track(metricName, 1); c1.track(metricName, 1000); diff --git a/src/storages/inMemory/__tests__/MySegmentsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/MySegmentsCacheInMemory.spec.ts index d3ad6c13..3ac0717c 100644 --- a/src/storages/inMemory/__tests__/MySegmentsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/MySegmentsCacheInMemory.spec.ts @@ -1,7 +1,7 @@ -import MySegmentsCache from '../MySegmentsCacheInMemory'; +import { MySegmentsCacheInMemory } from '../MySegmentsCacheInMemory'; test('MY SEGMENTS CACHE / in memory', () => { - const cache = new MySegmentsCache(); + const cache = new MySegmentsCacheInMemory(); cache.addToSegment('mocked-segment'); diff --git a/src/storages/inMemory/__tests__/SegmentsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/SegmentsCacheInMemory.spec.ts index 70ca6b12..4d868849 100644 --- a/src/storages/inMemory/__tests__/SegmentsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/SegmentsCacheInMemory.spec.ts @@ -1,9 +1,9 @@ -import SegmentsCache from '../SegmentsCacheInMemory'; +import { SegmentsCacheInMemory } from '../SegmentsCacheInMemory'; describe('SEGMENTS CACHE IN MEMORY', () => { test('isInSegment, set/getChangeNumber, add/removeFromSegment', () => { - const cache = new SegmentsCache(); + const cache = new SegmentsCacheInMemory(); cache.addToSegment('mocked-segment', [ 'a', 'b', 'c' @@ -29,7 +29,7 @@ describe('SEGMENTS CACHE IN MEMORY', () => { }); test('registerSegment / getRegisteredSegments', async () => { - const cache = new SegmentsCache(); + const cache = new SegmentsCacheInMemory(); await cache.registerSegments(['s1']); await cache.registerSegments(['s2']); diff --git a/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts index 0aa93ede..7d20655d 100644 --- a/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts @@ -1,7 +1,7 @@ -import SplitsCache from '../SplitsCacheInMemory'; +import { SplitsCacheInMemory } from '../SplitsCacheInMemory'; test('SPLITS CACHE / In Memory', () => { - const cache = new SplitsCache(); + const cache = new SplitsCacheInMemory(); cache.addSplit('lol1', '{ "name": "something"}'); cache.addSplit('lol2', '{ "name": "something else"}'); @@ -31,7 +31,7 @@ test('SPLITS CACHE / In Memory', () => { }); test('SPLITS CACHE / In Memory / Get Keys', () => { - const cache = new SplitsCache(); + const cache = new SplitsCacheInMemory(); cache.addSplit('lol1', '{ "name": "something"}'); cache.addSplit('lol2', '{ "name": "something else"}'); @@ -43,7 +43,7 @@ test('SPLITS CACHE / In Memory / Get Keys', () => { }); test('SPLITS CACHE / In Memory / trafficTypeExists and ttcache tests', () => { - const cache = new SplitsCache(); + const cache = new SplitsCacheInMemory(); cache.addSplits([ // loop of addSplit ['split1', '{ "trafficTypeName": "user_tt" }'], @@ -82,7 +82,7 @@ test('SPLITS CACHE / In Memory / trafficTypeExists and ttcache tests', () => { }); test('SPLITS CACHE / In Memory / killLocally', () => { - const cache = new SplitsCache(); + const cache = new SplitsCacheInMemory(); cache.addSplit('lol1', '{ "name": "something"}'); cache.addSplit('lol2', '{ "name": "something else"}'); const initialChangeNumber = cache.getChangeNumber(); diff --git a/src/storages/inRedis/CountsCacheInRedis.ts b/src/storages/inRedis/CountsCacheInRedis.ts index b48b9280..cb551fa2 100644 --- a/src/storages/inRedis/CountsCacheInRedis.ts +++ b/src/storages/inRedis/CountsCacheInRedis.ts @@ -1,8 +1,8 @@ import { ICountsCacheAsync } from '../types'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilderSS } from '../KeyBuilderSS'; import { Redis } from 'ioredis'; -export default class CountsCacheInRedis implements ICountsCacheAsync { +export class CountsCacheInRedis implements ICountsCacheAsync { private readonly redis: Redis; private readonly keys: KeyBuilderSS; diff --git a/src/storages/inRedis/EventsCacheInRedis.ts b/src/storages/inRedis/EventsCacheInRedis.ts index f4bc133b..e2702ab8 100644 --- a/src/storages/inRedis/EventsCacheInRedis.ts +++ b/src/storages/inRedis/EventsCacheInRedis.ts @@ -6,7 +6,7 @@ import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; import { StoredEventWithMetadata } from '../../sync/submitters/types'; -export default class EventsCacheInRedis implements IEventsCacheAsync { +export class EventsCacheInRedis implements IEventsCacheAsync { private readonly log: ILogger; private readonly key: string; diff --git a/src/storages/inRedis/ImpressionsCacheInRedis.ts b/src/storages/inRedis/ImpressionsCacheInRedis.ts index 7f399e64..d4bd2d96 100644 --- a/src/storages/inRedis/ImpressionsCacheInRedis.ts +++ b/src/storages/inRedis/ImpressionsCacheInRedis.ts @@ -7,7 +7,7 @@ import { ILogger } from '../../logger/types'; const IMPRESSIONS_TTL_REFRESH = 3600; // 1 hr -export default class ImpressionsCacheInRedis implements IImpressionsCacheAsync { +export class ImpressionsCacheInRedis implements IImpressionsCacheAsync { private readonly log: ILogger; private readonly key: string; diff --git a/src/storages/inRedis/LatenciesCacheInRedis.ts b/src/storages/inRedis/LatenciesCacheInRedis.ts index 143a759b..f69b7244 100644 --- a/src/storages/inRedis/LatenciesCacheInRedis.ts +++ b/src/storages/inRedis/LatenciesCacheInRedis.ts @@ -1,9 +1,9 @@ import { ILatenciesCacheAsync } from '../types'; -import KeyBuilderSS from '../KeyBuilderSS'; -import findLatencyIndex from '../findLatencyIndex'; +import { KeyBuilderSS } from '../KeyBuilderSS'; +import { findLatencyIndex } from '../findLatencyIndex'; import { Redis } from 'ioredis'; -export default class LatenciesCacheInRedis implements ILatenciesCacheAsync { +export class LatenciesCacheInRedis implements ILatenciesCacheAsync { private readonly redis: Redis; private readonly keys: KeyBuilderSS; diff --git a/src/storages/inRedis/RedisAdapter.ts b/src/storages/inRedis/RedisAdapter.ts index 99dccd45..05ef94f7 100644 --- a/src/storages/inRedis/RedisAdapter.ts +++ b/src/storages/inRedis/RedisAdapter.ts @@ -2,8 +2,8 @@ import ioredis from 'ioredis'; import { ILogger } from '../../logger/types'; import { merge, isString } from '../../utils/lang'; import { _Set, setToArray, ISet } from '../../utils/lang/sets'; -import thenable from '../../utils/promise/thenable'; -import timeout from '../../utils/promise/timeout'; +import { thenable } from '../../utils/promise/thenable'; +import { timeout } from '../../utils/promise/timeout'; const LOG_PREFIX = 'storage:redis-adapter: '; @@ -32,7 +32,7 @@ interface IRedisCommand { /** * Redis adapter on top of the library of choice (written with ioredis) for some extra control. */ -export default class RedisAdapter extends ioredis { +export class RedisAdapter extends ioredis { private readonly log: ILogger private _options: object; private _notReadyCommandsQueue?: IRedisCommand[]; diff --git a/src/storages/inRedis/SegmentsCacheInRedis.ts b/src/storages/inRedis/SegmentsCacheInRedis.ts index daee13a5..a144e2b7 100644 --- a/src/storages/inRedis/SegmentsCacheInRedis.ts +++ b/src/storages/inRedis/SegmentsCacheInRedis.ts @@ -2,10 +2,10 @@ import { Redis } from 'ioredis'; import { ILogger } from '../../logger/types'; import { isNaNNumber } from '../../utils/lang'; import { LOG_PREFIX } from '../inLocalStorage/constants'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilderSS } from '../KeyBuilderSS'; import { ISegmentsCacheAsync } from '../types'; -export default class SegmentsCacheInRedis implements ISegmentsCacheAsync { +export class SegmentsCacheInRedis implements ISegmentsCacheAsync { private readonly log: ILogger; private readonly redis: Redis; diff --git a/src/storages/inRedis/SplitsCacheInRedis.ts b/src/storages/inRedis/SplitsCacheInRedis.ts index 92e5c09c..28996d06 100644 --- a/src/storages/inRedis/SplitsCacheInRedis.ts +++ b/src/storages/inRedis/SplitsCacheInRedis.ts @@ -1,10 +1,10 @@ import { isFiniteNumber, isNaNNumber } from '../../utils/lang'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilderSS } from '../KeyBuilderSS'; import { Redis } from 'ioredis'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; import { ISplit } from '../../dtos/types'; -import AbstractSplitsCacheAsync from '../AbstractSplitsCacheAsync'; +import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync'; /** * Discard errors for an answer of multiple operations. @@ -20,7 +20,7 @@ function processPipelineAnswer(results: Array<[Error | null, string]>): string[] * ISplitsCacheAsync implementation that stores split definitions in Redis. * Supported by Node. */ -export default class SplitsCacheInRedis extends AbstractSplitsCacheAsync { +export class SplitsCacheInRedis extends AbstractSplitsCacheAsync { private readonly log: ILogger; private readonly redis: Redis; diff --git a/src/storages/inRedis/__tests__/CountsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/CountsCacheInRedis.spec.ts index 8220a3ce..b8032ac2 100644 --- a/src/storages/inRedis/__tests__/CountsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/CountsCacheInRedis.spec.ts @@ -1,6 +1,6 @@ import Redis from 'ioredis'; -import KeyBuilderSS from '../../KeyBuilderSS'; -import CountsCacheInRedis from '../CountsCacheInRedis'; +import { KeyBuilderSS } from '../../KeyBuilderSS'; +import { CountsCacheInRedis } from '../CountsCacheInRedis'; const prefix = 'counts_cache_ut'; const metadata = { s: 'version', i: 'ip', n: 'hostname' }; diff --git a/src/storages/inRedis/__tests__/EventsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/EventsCacheInRedis.spec.ts index 2ed55687..9d648feb 100644 --- a/src/storages/inRedis/__tests__/EventsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/EventsCacheInRedis.spec.ts @@ -1,6 +1,6 @@ import Redis from 'ioredis'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; -import EventsCacheInRedis from '../EventsCacheInRedis'; +import { EventsCacheInRedis } from '../EventsCacheInRedis'; import { fakeMetadata, fakeEvent1, fakeEvent1stored, fakeEvent2, fakeEvent2stored, fakeEvent3, fakeEvent3stored } from '../../pluggable/__tests__/EventsCachePluggable.spec'; const prefix = 'events_cache_ut'; diff --git a/src/storages/inRedis/__tests__/ImpressionsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/ImpressionsCacheInRedis.spec.ts index 0701bd7b..8dcd1398 100644 --- a/src/storages/inRedis/__tests__/ImpressionsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/ImpressionsCacheInRedis.spec.ts @@ -1,5 +1,5 @@ -import Redis from '../RedisAdapter'; -import ImpressionsCacheInRedis from '../ImpressionsCacheInRedis'; +import { RedisAdapter } from '../RedisAdapter'; +import { ImpressionsCacheInRedis } from '../ImpressionsCacheInRedis'; import IORedis, { BooleanResponse } from 'ioredis'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { fakeMetadata, o1, o2, o3, o1stored, o2stored, o3stored } from '../../pluggable/__tests__/ImpressionsCachePluggable.spec'; @@ -7,7 +7,7 @@ import { fakeMetadata, o1, o2, o3, o1stored, o2stored, o3stored } from '../../pl describe('IMPRESSIONS CACHE IN REDIS', () => { test('`track`, `count`, `popNWithMetadata` and `drop` methods', async () => { - const connection = new Redis(loggerMock, {}); + const connection = new RedisAdapter(loggerMock, {}); const impressionsKey = 'impr_cache_ut.impressions'; const c = new ImpressionsCacheInRedis(loggerMock, impressionsKey, connection, fakeMetadata); @@ -49,7 +49,7 @@ describe('IMPRESSIONS CACHE IN REDIS', () => { test('`track` should not resolve before calling expire', async () => { const impressionsKey = 'impr_cache_ut_2.impressions'; - const connection = new Redis(loggerMock, {}); + const connection = new RedisAdapter(loggerMock, {}); const c = new ImpressionsCacheInRedis(loggerMock, impressionsKey, connection, fakeMetadata); diff --git a/src/storages/inRedis/__tests__/LatenciesCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/LatenciesCacheInRedis.spec.ts index e592bdd9..7567b969 100644 --- a/src/storages/inRedis/__tests__/LatenciesCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/LatenciesCacheInRedis.spec.ts @@ -1,6 +1,6 @@ import Redis from 'ioredis'; -import KeyBuilderSS from '../../KeyBuilderSS'; -import LatenciesCacheInRedis from '../LatenciesCacheInRedis'; +import { KeyBuilderSS } from '../../KeyBuilderSS'; +import { LatenciesCacheInRedis } from '../LatenciesCacheInRedis'; const prefix = 'latencies_cache_UT'; const metadata = { s: 'js_someversion', i: 'some_ip', n: 'some_hostname' }; diff --git a/src/storages/inRedis/__tests__/RedisAdapter.spec.ts b/src/storages/inRedis/__tests__/RedisAdapter.spec.ts index 9557162d..c9e789f6 100644 --- a/src/storages/inRedis/__tests__/RedisAdapter.spec.ts +++ b/src/storages/inRedis/__tests__/RedisAdapter.spec.ts @@ -49,11 +49,11 @@ const timeoutMock = jest.fn(function timeout(ms, originalPromise) { }); jest.mock('../../../utils/promise/timeout'); -import timeout from '../../../utils/promise/timeout'; +import { timeout } from '../../../utils/promise/timeout'; (timeout as jest.Mock).mockImplementation(timeoutMock); // Test target -import RedisAdapter from '../RedisAdapter'; +import { RedisAdapter } from '../RedisAdapter'; function clearAllMocks() { loggerMock.mockClear(); diff --git a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts index dc3ea4b8..e230fbc0 100644 --- a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts @@ -1,6 +1,6 @@ import Redis from 'ioredis'; -import SegmentsCacheInRedis from '../SegmentsCacheInRedis'; -import KeyBuilderSS from '../../KeyBuilderSS'; +import { SegmentsCacheInRedis } from '../SegmentsCacheInRedis'; +import { KeyBuilderSS } from '../../KeyBuilderSS'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; // @ts-expect-error. Doesn't require metadata diff --git a/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts index 7ab8384b..fdb4777a 100644 --- a/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts @@ -1,6 +1,6 @@ import Redis from 'ioredis'; -import SplitsCacheInRedis from '../SplitsCacheInRedis'; -import KeyBuilderSS from '../../KeyBuilderSS'; +import { SplitsCacheInRedis } from '../SplitsCacheInRedis'; +import { KeyBuilderSS } from '../../KeyBuilderSS'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { splitWithUserTT, splitWithAccountTT } from '../../__tests__/testUtils'; diff --git a/src/storages/inRedis/index.ts b/src/storages/inRedis/index.ts index 68b0a85b..de72a220 100644 --- a/src/storages/inRedis/index.ts +++ b/src/storages/inRedis/index.ts @@ -1,13 +1,13 @@ -import RedisAdapter from './RedisAdapter'; +import { RedisAdapter } from './RedisAdapter'; import { IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types'; import { validatePrefix } from '../KeyBuilder'; -import KeyBuilderSS from '../KeyBuilderSS'; -import SplitsCacheInRedis from './SplitsCacheInRedis'; -import SegmentsCacheInRedis from './SegmentsCacheInRedis'; -import ImpressionsCacheInRedis from './ImpressionsCacheInRedis'; -import EventsCacheInRedis from './EventsCacheInRedis'; -import LatenciesCacheInRedis from './LatenciesCacheInRedis'; -import CountsCacheInRedis from './CountsCacheInRedis'; +import { KeyBuilderSS } from '../KeyBuilderSS'; +import { SplitsCacheInRedis } from './SplitsCacheInRedis'; +import { SegmentsCacheInRedis } from './SegmentsCacheInRedis'; +import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis'; +import { EventsCacheInRedis } from './EventsCacheInRedis'; +import { LatenciesCacheInRedis } from './LatenciesCacheInRedis'; +import { CountsCacheInRedis } from './CountsCacheInRedis'; import { STORAGE_REDIS } from '../../utils/constants'; export interface InRedisStorageOptions { diff --git a/src/storages/pluggable/SegmentsCachePluggable.ts b/src/storages/pluggable/SegmentsCachePluggable.ts index 7b19633a..995c66df 100644 --- a/src/storages/pluggable/SegmentsCachePluggable.ts +++ b/src/storages/pluggable/SegmentsCachePluggable.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-unused-vars */ import { isNaNNumber } from '../../utils/lang'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilderSS } from '../KeyBuilderSS'; import { IPluggableStorageWrapper, ISegmentsCacheAsync } from '../types'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; diff --git a/src/storages/pluggable/SplitsCachePluggable.ts b/src/storages/pluggable/SplitsCachePluggable.ts index 95bbcc0e..6fec1420 100644 --- a/src/storages/pluggable/SplitsCachePluggable.ts +++ b/src/storages/pluggable/SplitsCachePluggable.ts @@ -1,10 +1,10 @@ import { isFiniteNumber, isNaNNumber } from '../../utils/lang'; -import KeyBuilder from '../KeyBuilder'; +import { KeyBuilder } from '../KeyBuilder'; import { IPluggableStorageWrapper } from '../types'; import { ILogger } from '../../logger/types'; import { ISplit } from '../../dtos/types'; import { LOG_PREFIX } from './constants'; -import AbstractSplitsCacheAsync from '../AbstractSplitsCacheAsync'; +import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync'; /** * ISplitsCacheAsync implementation for pluggable storages. diff --git a/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts b/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts index d0ec3a56..0ab833c8 100644 --- a/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts +++ b/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts @@ -1,5 +1,5 @@ import { SegmentsCachePluggable } from '../SegmentsCachePluggable'; -import KeyBuilderSS from '../../KeyBuilderSS'; +import { KeyBuilderSS } from '../../KeyBuilderSS'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { wrapperMock } from './wrapper.mock'; diff --git a/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts b/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts index 99069585..652fb07a 100644 --- a/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts +++ b/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts @@ -1,5 +1,5 @@ import { SplitsCachePluggable } from '../SplitsCachePluggable'; -import KeyBuilder from '../../KeyBuilder'; +import { KeyBuilder } from '../../KeyBuilder'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { wrapperMockFactory } from './wrapper.mock'; import { splitWithUserTT, splitWithAccountTT } from '../../__tests__/testUtils'; diff --git a/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts b/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts index 366ad1e4..90805b90 100644 --- a/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts +++ b/src/storages/pluggable/__tests__/wrapperAdapter.spec.ts @@ -1,6 +1,6 @@ // @ts-nocheck import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; -import thenable from '../../../utils/promise/thenable'; +import { thenable } from '../../../utils/promise/thenable'; import { LOG_PREFIX } from '../constants'; /** Mocks */ diff --git a/src/storages/pluggable/index.ts b/src/storages/pluggable/index.ts index 9dd9f592..fef04aa6 100644 --- a/src/storages/pluggable/index.ts +++ b/src/storages/pluggable/index.ts @@ -1,6 +1,6 @@ import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types'; -import KeyBuilderSS from '../KeyBuilderSS'; +import { KeyBuilderSS } from '../KeyBuilderSS'; import { SplitsCachePluggable } from './SplitsCachePluggable'; import { SegmentsCachePluggable } from './SegmentsCachePluggable'; import { ImpressionsCachePluggable } from './ImpressionsCachePluggable'; @@ -9,9 +9,9 @@ import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter'; import { isObject } from '../../utils/lang'; import { validatePrefix } from '../KeyBuilder'; import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'; -import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory'; -import EventsCacheInMemory from '../inMemory/EventsCacheInMemory'; -import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory'; +import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory'; +import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory'; +import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory'; const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.'; const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.'; diff --git a/src/sync/__tests__/syncTask.spec.ts b/src/sync/__tests__/syncTask.spec.ts index b947e0a3..d4a7502b 100644 --- a/src/sync/__tests__/syncTask.spec.ts +++ b/src/sync/__tests__/syncTask.spec.ts @@ -1,4 +1,4 @@ -import syncTaskFactory from '../syncTask'; +import { syncTaskFactory } from '../syncTask'; import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; const period = 30; diff --git a/src/sync/offline/splitsParser/parseCondition.ts b/src/sync/offline/splitsParser/parseCondition.ts index 671fbed1..a2223bec 100644 --- a/src/sync/offline/splitsParser/parseCondition.ts +++ b/src/sync/offline/splitsParser/parseCondition.ts @@ -7,7 +7,7 @@ export interface IMockSplitEntry { config?: string } -export default function parseCondition(data: IMockSplitEntry): ISplitCondition { +export function parseCondition(data: IMockSplitEntry): ISplitCondition { const treatment = data.treatment; if (data.keys) { diff --git a/src/sync/offline/splitsParser/splitsParserFromFile.ts b/src/sync/offline/splitsParser/splitsParserFromFile.ts index 95ea8d14..d65d6a55 100644 --- a/src/sync/offline/splitsParser/splitsParserFromFile.ts +++ b/src/sync/offline/splitsParser/splitsParserFromFile.ts @@ -5,7 +5,7 @@ import path from 'path'; // @ts-ignore import yaml from 'js-yaml'; import { isString, endsWith, find, forOwn, uniq, } from '../../../utils/lang'; -import parseCondition, { IMockSplitEntry } from './parseCondition'; +import { parseCondition, IMockSplitEntry } from './parseCondition'; import { ISplitPartial } from '../../../dtos/types'; import { ISettings, SplitIO } from '../../../types'; import { ILogger } from '../../../logger/types'; diff --git a/src/sync/offline/splitsParser/splitsParserFromSettings.ts b/src/sync/offline/splitsParser/splitsParserFromSettings.ts index f9cf9175..8e892969 100644 --- a/src/sync/offline/splitsParser/splitsParserFromSettings.ts +++ b/src/sync/offline/splitsParser/splitsParserFromSettings.ts @@ -1,7 +1,7 @@ import { ISplitPartial } from '../../../dtos/types'; import { ISettings, SplitIO } from '../../../types'; import { isObject, forOwn } from '../../../utils/lang'; -import parseCondition from './parseCondition'; +import { parseCondition } from './parseCondition'; function hasTreatmentChanged(prev: string | SplitIO.TreatmentWithConfig, curr: string | SplitIO.TreatmentWithConfig) { if (typeof prev !== typeof curr) return true; diff --git a/src/sync/offline/syncManagerOffline.ts b/src/sync/offline/syncManagerOffline.ts index 58a75b6f..253493f2 100644 --- a/src/sync/offline/syncManagerOffline.ts +++ b/src/sync/offline/syncManagerOffline.ts @@ -1,6 +1,6 @@ import { ISyncManager, ISyncManagerCS, ISyncManagerFactoryParams } from '../types'; -import fromObjectSyncTaskFactory from './syncTasks/fromObjectSyncTask'; -import objectAssign from 'object-assign'; +import { fromObjectSyncTaskFactory } from './syncTasks/fromObjectSyncTask'; +import { objectAssign } from '../../utils/lang/objectAssign'; import { ISplitsParser } from './splitsParser/types'; import { IReadinessManager } from '../../readiness/types'; import { SDK_SEGMENTS_ARRIVED } from '../../readiness/constants'; diff --git a/src/sync/offline/syncTasks/fromObjectSyncTask.ts b/src/sync/offline/syncTasks/fromObjectSyncTask.ts index 2eccc27c..8cef1c7c 100644 --- a/src/sync/offline/syncTasks/fromObjectSyncTask.ts +++ b/src/sync/offline/syncTasks/fromObjectSyncTask.ts @@ -3,7 +3,7 @@ import { IReadinessManager } from '../../../readiness/types'; import { ISplitsCacheSync } from '../../../storages/types'; import { ISplitsParser } from '../splitsParser/types'; import { ISplitPartial } from '../../../dtos/types'; -import syncTaskFactory from '../../syncTask'; +import { syncTaskFactory } from '../../syncTask'; import { ISyncTask } from '../../types'; import { ISettings } from '../../../types'; import { CONTROL } from '../../../utils/constants'; @@ -79,7 +79,7 @@ export function fromObjectUpdaterFactory( /** * PollingManager in Offline mode */ -export default function fromObjectSyncTaskFactory( +export function fromObjectSyncTaskFactory( splitsParser: ISplitsParser, storage: { splits: ISplitsCacheSync }, readiness: IReadinessManager, diff --git a/src/sync/polling/fetchers/mySegmentsFetcher.ts b/src/sync/polling/fetchers/mySegmentsFetcher.ts index 298ef558..8ea7a5f4 100644 --- a/src/sync/polling/fetchers/mySegmentsFetcher.ts +++ b/src/sync/polling/fetchers/mySegmentsFetcher.ts @@ -6,7 +6,7 @@ import { IMySegmentsFetcher } from './types'; * Factory of MySegments fetcher. * MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors. */ -export default function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments, userMatchingKey: string): IMySegmentsFetcher { +export function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments, userMatchingKey: string): IMySegmentsFetcher { return function mySegmentsFetcher( noCache?: boolean, diff --git a/src/sync/polling/fetchers/segmentChangesFetcher.ts b/src/sync/polling/fetchers/segmentChangesFetcher.ts index 0bf14400..e28e0aa8 100644 --- a/src/sync/polling/fetchers/segmentChangesFetcher.ts +++ b/src/sync/polling/fetchers/segmentChangesFetcher.ts @@ -21,7 +21,7 @@ function greedyFetch(fetchSegmentChanges: IFetchSegmentChanges, since: number, s * Factory of SegmentChanges fetcher. * SegmentChanges fetcher is a wrapper around `segmentChanges` API service that parses the response and handle errors and retries. */ -export default function segmentChangesFetcherFactory(fetchSegmentChanges: IFetchSegmentChanges): ISegmentChangesFetcher { +export function segmentChangesFetcherFactory(fetchSegmentChanges: IFetchSegmentChanges): ISegmentChangesFetcher { return function segmentChangesFetcher( since: number, diff --git a/src/sync/polling/fetchers/splitChangesFetcher.ts b/src/sync/polling/fetchers/splitChangesFetcher.ts index 47de07ea..42e45463 100644 --- a/src/sync/polling/fetchers/splitChangesFetcher.ts +++ b/src/sync/polling/fetchers/splitChangesFetcher.ts @@ -5,7 +5,7 @@ import { ISplitChangesFetcher } from './types'; * Factory of SplitChanges fetcher. * SplitChanges fetcher is a wrapper around `splitChanges` API service that parses the response and handle errors. */ -export default function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges): ISplitChangesFetcher { +export function splitChangesFetcherFactory(fetchSplitChanges: IFetchSplitChanges): ISplitChangesFetcher { return function splitChangesFetcher( since: number, diff --git a/src/sync/polling/pollingManagerCS.ts b/src/sync/polling/pollingManagerCS.ts index 7fbbc747..138ed003 100644 --- a/src/sync/polling/pollingManagerCS.ts +++ b/src/sync/polling/pollingManagerCS.ts @@ -3,8 +3,8 @@ import { forOwn } from '../../utils/lang'; import { IReadinessManager } from '../../readiness/types'; import { ISplitApi } from '../../services/types'; import { IStorageSync } from '../../storages/types'; -import mySegmentsSyncTaskFactory from './syncTasks/mySegmentsSyncTask'; -import splitsSyncTaskFactory from './syncTasks/splitsSyncTask'; +import { mySegmentsSyncTaskFactory } from './syncTasks/mySegmentsSyncTask'; +import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask'; import { ISettings } from '../../types'; import { getMatching } from '../../utils/key'; import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants'; @@ -14,7 +14,7 @@ import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger * Expose start / stop mechanism for polling data from services. * For client-side API with multiple clients. */ -export default function pollingManagerCSFactory( +export function pollingManagerCSFactory( splitApi: ISplitApi, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/polling/pollingManagerSS.ts b/src/sync/polling/pollingManagerSS.ts index 773da265..c1e7edad 100644 --- a/src/sync/polling/pollingManagerSS.ts +++ b/src/sync/polling/pollingManagerSS.ts @@ -1,17 +1,17 @@ -import splitsSyncTaskFactory from './syncTasks/splitsSyncTask'; -import segmentsSyncTaskFactory from './syncTasks/segmentsSyncTask'; +import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask'; +import { segmentsSyncTaskFactory } from './syncTasks/segmentsSyncTask'; import { IStorageSync } from '../../storages/types'; import { IReadinessManager } from '../../readiness/types'; import { ISplitApi } from '../../services/types'; import { ISettings } from '../../types'; import { IPollingManager, ISegmentsSyncTask, ISplitsSyncTask } from './types'; -import thenable from '../../utils/promise/thenable'; +import { thenable } from '../../utils/promise/thenable'; import { POLLING_START, POLLING_STOP, LOG_PREFIX_SYNC_POLLING } from '../../logger/constants'; /** * Expose start / stop mechanism for pulling data from services. */ -export default function pollingManagerSSFactory( +export function pollingManagerSSFactory( splitApi: ISplitApi, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/polling/syncTasks/mySegmentsSyncTask.ts b/src/sync/polling/syncTasks/mySegmentsSyncTask.ts index 6547c3c6..7b830d21 100644 --- a/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +++ b/src/sync/polling/syncTasks/mySegmentsSyncTask.ts @@ -1,16 +1,16 @@ import { IStorageSync } from '../../../storages/types'; import { IReadinessManager } from '../../../readiness/types'; -import syncTaskFactory from '../../syncTask'; +import { syncTaskFactory } from '../../syncTask'; import { ISegmentsSyncTask } from '../types'; import { IFetchMySegments } from '../../../services/types'; -import mySegmentsFetcherFactory from '../fetchers/mySegmentsFetcher'; +import { mySegmentsFetcherFactory } from '../fetchers/mySegmentsFetcher'; import { ISettings } from '../../../types'; import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater'; /** * Creates a sync task that periodically executes a `mySegmentsUpdater` task */ -export default function mySegmentsSyncTaskFactory( +export function mySegmentsSyncTaskFactory( fetchMySegments: IFetchMySegments, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/polling/syncTasks/segmentsSyncTask.ts b/src/sync/polling/syncTasks/segmentsSyncTask.ts index 69cda5a3..f5a93711 100644 --- a/src/sync/polling/syncTasks/segmentsSyncTask.ts +++ b/src/sync/polling/syncTasks/segmentsSyncTask.ts @@ -1,8 +1,8 @@ import { IStorageSync } from '../../../storages/types'; import { IReadinessManager } from '../../../readiness/types'; -import syncTaskFactory from '../../syncTask'; +import { syncTaskFactory } from '../../syncTask'; import { ISegmentsSyncTask } from '../types'; -import segmentChangesFetcherFactory from '../fetchers/segmentChangesFetcher'; +import { segmentChangesFetcherFactory } from '../fetchers/segmentChangesFetcher'; import { IFetchSegmentChanges } from '../../../services/types'; import { ISettings } from '../../../types'; import { segmentChangesUpdaterFactory } from '../updaters/segmentChangesUpdater'; @@ -10,7 +10,7 @@ import { segmentChangesUpdaterFactory } from '../updaters/segmentChangesUpdater' /** * Creates a sync task that periodically executes a `segmentChangesUpdater` task */ -export default function segmentsSyncTaskFactory( +export function segmentsSyncTaskFactory( fetchSegmentChanges: IFetchSegmentChanges, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/polling/syncTasks/splitsSyncTask.ts b/src/sync/polling/syncTasks/splitsSyncTask.ts index 04544c02..6807a0b4 100644 --- a/src/sync/polling/syncTasks/splitsSyncTask.ts +++ b/src/sync/polling/syncTasks/splitsSyncTask.ts @@ -1,8 +1,8 @@ import { IStorageSync } from '../../../storages/types'; import { IReadinessManager } from '../../../readiness/types'; -import syncTaskFactory from '../../syncTask'; +import { syncTaskFactory } from '../../syncTask'; import { ISplitsSyncTask } from '../types'; -import splitChangesFetcherFactory from '../fetchers/splitChangesFetcher'; +import { splitChangesFetcherFactory } from '../fetchers/splitChangesFetcher'; import { IFetchSplitChanges } from '../../../services/types'; import { ISettings } from '../../../types'; import { splitChangesUpdaterFactory } from '../updaters/splitChangesUpdater'; @@ -10,7 +10,7 @@ import { splitChangesUpdaterFactory } from '../updaters/splitChangesUpdater'; /** * Creates a sync task that periodically executes a `splitChangesUpdater` task */ -export default function splitsSyncTaskFactory( +export function splitsSyncTaskFactory( fetchSplitChanges: IFetchSplitChanges, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts b/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts index bc68243c..60cc9b52 100644 --- a/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts +++ b/src/sync/polling/updaters/__tests__/splitChangesUpdater.spec.ts @@ -1,9 +1,9 @@ import { ISplit } from '../../../../dtos/types'; import { readinessManagerFactory } from '../../../../readiness/readinessManager'; import { splitApiFactory } from '../../../../services/splitApi'; -import SegmentsCacheInMemory from '../../../../storages/inMemory/SegmentsCacheInMemory'; -import SplitsCacheInMemory from '../../../../storages/inMemory/SplitsCacheInMemory'; -import splitChangesFetcherFactory from '../../fetchers/splitChangesFetcher'; +import { SegmentsCacheInMemory } from '../../../../storages/inMemory/SegmentsCacheInMemory'; +import { SplitsCacheInMemory } from '../../../../storages/inMemory/SplitsCacheInMemory'; +import { splitChangesFetcherFactory } from '../../fetchers/splitChangesFetcher'; import { splitChangesUpdaterFactory, parseSegments, computeSplitsMutation } from '../splitChangesUpdater'; import splitChangesMock1 from '../../../../__tests__/mocks/splitchanges.since.-1.json'; import fetchMock from '../../../../__tests__/testUtils/fetchMock'; diff --git a/src/sync/polling/updaters/mySegmentsUpdater.ts b/src/sync/polling/updaters/mySegmentsUpdater.ts index 17a5b762..a72ee54b 100644 --- a/src/sync/polling/updaters/mySegmentsUpdater.ts +++ b/src/sync/polling/updaters/mySegmentsUpdater.ts @@ -1,7 +1,7 @@ import { IMySegmentsFetcher } from '../fetchers/types'; import { ISegmentsCacheSync, ISplitsCacheSync } from '../../../storages/types'; import { ISegmentsEventEmitter } from '../../../readiness/types'; -import timeout from '../../../utils/promise/timeout'; +import { timeout } from '../../../utils/promise/timeout'; import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants'; import { ILogger } from '../../../logger/types'; import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants'; diff --git a/src/sync/polling/updaters/segmentChangesUpdater.ts b/src/sync/polling/updaters/segmentChangesUpdater.ts index eefedf88..90a1bc06 100644 --- a/src/sync/polling/updaters/segmentChangesUpdater.ts +++ b/src/sync/polling/updaters/segmentChangesUpdater.ts @@ -6,7 +6,7 @@ import { findIndex } from '../../../utils/lang'; import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants'; import { ILogger } from '../../../logger/types'; import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants'; -import thenable from '../../../utils/promise/thenable'; +import { thenable } from '../../../utils/promise/thenable'; type ISegmentChangesUpdater = (segmentNames?: string[], noCache?: boolean, fetchOnlyNew?: boolean) => Promise diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index ce490771..754ceed9 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -3,7 +3,7 @@ import { ISegmentsCacheBase, ISplitsCacheBase } from '../../../storages/types'; import { ISplitChangesFetcher } from '../fetchers/types'; import { ISplit, ISplitChangesResponse } from '../../../dtos/types'; import { ISplitsEventEmitter } from '../../../readiness/types'; -import timeout from '../../../utils/promise/timeout'; +import { timeout } from '../../../utils/promise/timeout'; import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants'; import { ILogger } from '../../../logger/types'; import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants'; diff --git a/src/sync/streaming/AuthClient/index.ts b/src/sync/streaming/AuthClient/index.ts index 8f19c34e..96ae9ca8 100644 --- a/src/sync/streaming/AuthClient/index.ts +++ b/src/sync/streaming/AuthClient/index.ts @@ -1,6 +1,6 @@ import { IFetchAuth } from '../../../services/types'; import { IAuthenticate, IAuthToken } from './types'; -import objectAssign from 'object-assign'; +import { objectAssign } from '../../../utils/lang/objectAssign'; import { encodeToBase64 } from '../../../utils/base64'; import { decodeJWTtoken } from '../../../utils/jwt'; import { hash } from '../../../utils/murmur3/murmur3'; diff --git a/src/sync/streaming/SSEClient/__tests__/index.spec.ts b/src/sync/streaming/SSEClient/__tests__/index.spec.ts index 6155224b..9564f1b5 100644 --- a/src/sync/streaming/SSEClient/__tests__/index.spec.ts +++ b/src/sync/streaming/SSEClient/__tests__/index.spec.ts @@ -4,7 +4,7 @@ import { authDataSample, channelsQueryParamSample } from '../../__tests__/dataMo import { fullSettings as settings } from '../../../../utils/settingsValidation/__tests__/settings.mocks'; import { url } from '../../../../utils/settingsValidation/url'; -import SSClient from '../index'; +import { SSEClient } from '../index'; const EXPECTED_URL = url(settings, '/sse') + '?channels=' + channelsQueryParamSample + @@ -17,12 +17,12 @@ const EXPECTED_HEADERS = { }; test('SSClient / instance creation throws error if EventSource is not provided', () => { - expect(() => { new SSClient(settings); }).toThrow(Error); - expect(() => { new SSClient(settings, false, () => undefined); }).toThrow(Error); + expect(() => { new SSEClient(settings); }).toThrow(Error); + expect(() => { new SSEClient(settings, false, () => undefined); }).toThrow(Error); }); test('SSClient / instance creation success if EventSource is provided', () => { - const instance = new SSClient(settings, false, () => EventSourceMock); + const instance = new SSEClient(settings, false, () => EventSourceMock); expect(instance.eventSource).toBe(EventSourceMock); }); @@ -35,7 +35,7 @@ test('SSClient / setEventHandler, open and close methods', () => { }; // instance SSEClient - const instance = new SSClient(settings, false, () => EventSourceMock); + const instance = new SSEClient(settings, false, () => EventSourceMock); instance.setEventHandler(handler); // open connection @@ -81,7 +81,7 @@ test('SSClient / setEventHandler, open and close methods', () => { test('SSClient / open method: URL with metadata query params', () => { - const instance = new SSClient(settings, false, () => EventSourceMock); + const instance = new SSEClient(settings, false, () => EventSourceMock); instance.open(authDataSample); const EXPECTED_BROWSER_URL = EXPECTED_URL + `&SplitSDKVersion=${settings.version}&SplitSDKClientKey=${EXPECTED_HEADERS.SplitSDKClientKey}`; @@ -99,7 +99,7 @@ test('SSClient / open method: URL and metadata headers with IP and Hostname', () hostname: 'some hostname' } }; - const instance = new SSClient(settingsWithRuntime, true, () => EventSourceMock); + const instance = new SSEClient(settingsWithRuntime, true, () => EventSourceMock); instance.open(authDataSample); expect(instance.connection.url).toBe(EXPECTED_URL); // URL is properly set for streaming connection @@ -114,7 +114,7 @@ test('SSClient / open method: URL and metadata headers with IP and Hostname', () test('SSClient / open method: URL and metadata headers without IP and Hostname', () => { - const instance = new SSClient(settings, true, () => EventSourceMock); + const instance = new SSEClient(settings, true, () => EventSourceMock); instance.open(authDataSample); expect(instance.connection.url).toBe(EXPECTED_URL); // URL is properly set for streaming connection diff --git a/src/sync/streaming/SSEClient/index.ts b/src/sync/streaming/SSEClient/index.ts index fbd09261..378ac9c0 100644 --- a/src/sync/streaming/SSEClient/index.ts +++ b/src/sync/streaming/SSEClient/index.ts @@ -30,7 +30,7 @@ function buildSSEHeaders(settings: ISettings) { /** * Handles streaming connections with EventSource API */ -export default class SSEClient implements ISSEClient { +export class SSEClient implements ISSEClient { // Instance properties: eventSource?: IEventSourceConstructor; streamingUrl: string; diff --git a/src/sync/streaming/SSEHandler/NotificationKeeper.ts b/src/sync/streaming/SSEHandler/NotificationKeeper.ts index 1df88250..03dae152 100644 --- a/src/sync/streaming/SSEHandler/NotificationKeeper.ts +++ b/src/sync/streaming/SSEHandler/NotificationKeeper.ts @@ -9,7 +9,7 @@ const CONTROL_CHANNEL_REGEXS = [/control_pri$/, /control_sec$/]; * @param pushEmitter emitter for events related to streaming support */ // @TODO update logic to handle OCCUPANCY for any region and rename according to new spec (e.g.: PUSH_SUBSYSTEM_UP --> PUSH_SUBSYSTEM_UP) -export default function notificationKeeperFactory(pushEmitter: IPushEventEmitter) { +export function notificationKeeperFactory(pushEmitter: IPushEventEmitter) { let channels = CONTROL_CHANNEL_REGEXS.map(regex => ({ regex, diff --git a/src/sync/streaming/SSEHandler/__tests__/index.spec.ts b/src/sync/streaming/SSEHandler/__tests__/index.spec.ts index 79f8c573..fea55027 100644 --- a/src/sync/streaming/SSEHandler/__tests__/index.spec.ts +++ b/src/sync/streaming/SSEHandler/__tests__/index.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import SSEHandlerFactory from '..'; +import { SSEHandlerFactory } from '..'; import { PUSH_SUBSYSTEM_UP, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, PUSH_RETRYABLE_ERROR, MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MY_SEGMENTS_UPDATE_V2, ControlType } from '../../constants'; import { loggerMock } from '../../../../logger/__tests__/sdkLogger.mock'; diff --git a/src/sync/streaming/SSEHandler/index.ts b/src/sync/streaming/SSEHandler/index.ts index b0cd7069..b0b46767 100644 --- a/src/sync/streaming/SSEHandler/index.ts +++ b/src/sync/streaming/SSEHandler/index.ts @@ -1,5 +1,5 @@ import { errorParser, messageParser } from './NotificationParser'; -import notificationKeeperFactory from './NotificationKeeper'; +import { notificationKeeperFactory } from './NotificationKeeper'; import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants'; import { IPushEventEmitter } from '../types'; import { ISseEventHandler } from '../SSEClient/types'; @@ -25,7 +25,7 @@ function isRetryableError(error: INotificationError) { * @param log factory logger * @param pushEmitter emitter for events related to streaming support */ -export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter): ISseEventHandler { +export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter): ISseEventHandler { const notificationKeeper = notificationKeeperFactory(pushEmitter); diff --git a/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts index 192e81fa..26bc5514 100644 --- a/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts @@ -1,12 +1,12 @@ import { ISegmentsSyncTask } from '../../polling/types'; -import Backoff from '../../../utils/Backoff'; +import { Backoff } from '../../../utils/Backoff'; import { IUpdateWorker } from './types'; import { SegmentsData } from '../SSEHandler/types'; /** * MySegmentsUpdateWorker class */ -export default class MySegmentsUpdateWorker implements IUpdateWorker { +export class MySegmentsUpdateWorker implements IUpdateWorker { private readonly mySegmentsSyncTask: ISegmentsSyncTask; private maxChangeNumber: number; diff --git a/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts index 3a299024..5c52872d 100644 --- a/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts @@ -1,5 +1,5 @@ import { ISegmentsCacheSync } from '../../../storages/types'; -import Backoff from '../../../utils/Backoff'; +import { Backoff } from '../../../utils/Backoff'; import { ISegmentsSyncTask } from '../../polling/types'; import { ISegmentUpdateData } from '../SSEHandler/types'; import { IUpdateWorker } from './types'; @@ -7,7 +7,7 @@ import { IUpdateWorker } from './types'; /** * SegmentUpdateWorker class */ -export default class SegmentsUpdateWorker implements IUpdateWorker { +export class SegmentsUpdateWorker implements IUpdateWorker { private readonly segmentsCache: ISegmentsCacheSync; private readonly segmentsSyncTask: ISegmentsSyncTask; diff --git a/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts index 8542d0a9..c9d12f65 100644 --- a/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts @@ -1,7 +1,7 @@ import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants'; import { ISplitsEventEmitter } from '../../../readiness/types'; import { ISplitsCacheSync } from '../../../storages/types'; -import Backoff from '../../../utils/Backoff'; +import { Backoff } from '../../../utils/Backoff'; import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types'; import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types'; import { IUpdateWorker } from './types'; @@ -9,7 +9,7 @@ import { IUpdateWorker } from './types'; /** * SplitsUpdateWorker class */ -export default class SplitsUpdateWorker implements IUpdateWorker { +export class SplitsUpdateWorker implements IUpdateWorker { private readonly splitsCache: ISplitsCacheSync; private readonly splitsSyncTask: ISplitsSyncTask; diff --git a/src/sync/streaming/UpdateWorkers/__tests__/MySegmentsUpdateWorker.spec.ts b/src/sync/streaming/UpdateWorkers/__tests__/MySegmentsUpdateWorker.spec.ts index 182bc3ad..1e4129e3 100644 --- a/src/sync/streaming/UpdateWorkers/__tests__/MySegmentsUpdateWorker.spec.ts +++ b/src/sync/streaming/UpdateWorkers/__tests__/MySegmentsUpdateWorker.spec.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import MySegmentsUpdateWorker from '../MySegmentsUpdateWorker'; +import { MySegmentsUpdateWorker } from '../MySegmentsUpdateWorker'; function mySegmentsSyncTaskMock() { diff --git a/src/sync/streaming/UpdateWorkers/__tests__/SegmentsUpdateWorker.spec.ts b/src/sync/streaming/UpdateWorkers/__tests__/SegmentsUpdateWorker.spec.ts index 5e6acb6a..4a494657 100644 --- a/src/sync/streaming/UpdateWorkers/__tests__/SegmentsUpdateWorker.spec.ts +++ b/src/sync/streaming/UpdateWorkers/__tests__/SegmentsUpdateWorker.spec.ts @@ -1,6 +1,6 @@ // @ts-nocheck -import SegmentsCacheInMemory from '../../../../storages/inMemory/SegmentsCacheInMemory'; -import SegmentsUpdateWorker from '../SegmentsUpdateWorker'; +import { SegmentsCacheInMemory } from '../../../../storages/inMemory/SegmentsCacheInMemory'; +import { SegmentsUpdateWorker } from '../SegmentsUpdateWorker'; function segmentsSyncTaskMock(segmentsStorage) { diff --git a/src/sync/streaming/UpdateWorkers/__tests__/SplitsUpdateWorker.spec.ts b/src/sync/streaming/UpdateWorkers/__tests__/SplitsUpdateWorker.spec.ts index bf28fa2e..74fd9c1f 100644 --- a/src/sync/streaming/UpdateWorkers/__tests__/SplitsUpdateWorker.spec.ts +++ b/src/sync/streaming/UpdateWorkers/__tests__/SplitsUpdateWorker.spec.ts @@ -1,7 +1,7 @@ // @ts-nocheck import { SDK_SPLITS_ARRIVED } from '../../../../readiness/constants'; -import SplitsCacheInMemory from '../../../../storages/inMemory/SplitsCacheInMemory'; -import SplitsUpdateWorker from '../SplitsUpdateWorker'; +import { SplitsCacheInMemory } from '../../../../storages/inMemory/SplitsCacheInMemory'; +import { SplitsUpdateWorker } from '../SplitsUpdateWorker'; function splitsSyncTaskMock(splitStorage) { diff --git a/src/sync/streaming/UpdateWorkers/types.ts b/src/sync/streaming/UpdateWorkers/types.ts index ea91a154..de70407e 100644 --- a/src/sync/streaming/UpdateWorkers/types.ts +++ b/src/sync/streaming/UpdateWorkers/types.ts @@ -1,4 +1,4 @@ -import Backoff from '../../../utils/Backoff'; +import { Backoff } from '../../../utils/Backoff'; export interface IUpdateWorker { readonly backoff: Backoff, diff --git a/src/sync/streaming/__tests__/pushManager.spec.ts b/src/sync/streaming/__tests__/pushManager.spec.ts index 02161150..26b63f7d 100644 --- a/src/sync/streaming/__tests__/pushManager.spec.ts +++ b/src/sync/streaming/__tests__/pushManager.spec.ts @@ -1,4 +1,4 @@ -import pushManagerFactory from '../pushManager'; +import { pushManagerFactory } from '../pushManager'; import { EventEmitter } from '../../../utils/MinEvents'; import { fullSettings } from '../../../utils/settingsValidation/__tests__/settings.mocks'; import { IPushManagerCS } from '../types'; diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index e7b7caac..2ba45e92 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -3,15 +3,15 @@ import { ISSEClient } from './SSEClient/types'; import { IStorageSync } from '../../storages/types'; import { IReadinessManager } from '../../readiness/types'; import { ISegmentsSyncTask, IPollingManager } from '../polling/types'; -import objectAssign from 'object-assign'; -import Backoff from '../../utils/Backoff'; -import SSEHandlerFactory from './SSEHandler'; -import MySegmentsUpdateWorker from './UpdateWorkers/MySegmentsUpdateWorker'; -import SegmentsUpdateWorker from './UpdateWorkers/SegmentsUpdateWorker'; -import SplitsUpdateWorker from './UpdateWorkers/SplitsUpdateWorker'; +import { objectAssign } from '../../utils/lang/objectAssign'; +import { Backoff } from '../../utils/Backoff'; +import { SSEHandlerFactory } from './SSEHandler'; +import { MySegmentsUpdateWorker } from './UpdateWorkers/MySegmentsUpdateWorker'; +import { SegmentsUpdateWorker } from './UpdateWorkers/SegmentsUpdateWorker'; +import { SplitsUpdateWorker } from './UpdateWorkers/SplitsUpdateWorker'; import { authenticateFactory, hashUserKey } from './AuthClient'; import { forOwn } from '../../utils/lang'; -import SSEClient from './SSEClient'; +import { SSEClient } from './SSEClient'; import { IFetchAuth } from '../../services/types'; import { ISettings } from '../../types'; import { getMatching } from '../../utils/key'; @@ -29,7 +29,7 @@ import { IAuthTokenPushEnabled } from './AuthClient/types'; * - for server-side if key is not provided in settings. * - for client-side, with support for multiple clients, if key is provided in settings */ -export default function pushManagerFactory( +export function pushManagerFactory( pollingManager: IPollingManager, storage: IStorageSync, readiness: IReadinessManager, diff --git a/src/sync/submitters/submitterSyncTask.ts b/src/sync/submitters/submitterSyncTask.ts index bf92b2e9..a17df77d 100644 --- a/src/sync/submitters/submitterSyncTask.ts +++ b/src/sync/submitters/submitterSyncTask.ts @@ -1,4 +1,4 @@ -import syncTaskFactory from '../syncTask'; +import { syncTaskFactory } from '../syncTask'; import { ISyncTask, ITimeTracker } from '../types'; import { IRecorderCacheProducerSync } from '../../storages/types'; import { ILogger } from '../../logger/types'; diff --git a/src/sync/syncTask.ts b/src/sync/syncTask.ts index d86cb9f4..eb3a2c30 100644 --- a/src/sync/syncTask.ts +++ b/src/sync/syncTask.ts @@ -13,7 +13,7 @@ import { ISyncTask } from './types'; * @param taskName Optional task name for logging. * @returns A sync task that wraps the given task. */ -export default function syncTaskFactory(log: ILogger, task: (...args: Input) => Promise, period: number, taskName = 'task'): ISyncTask { +export function syncTaskFactory(log: ILogger, task: (...args: Input) => Promise, period: number, taskName = 'task'): ISyncTask { // Flag that indicates if the task is being executed let executing = false; diff --git a/src/trackers/__tests__/eventTracker.spec.ts b/src/trackers/__tests__/eventTracker.spec.ts index 0edb462e..aa2cd234 100644 --- a/src/trackers/__tests__/eventTracker.spec.ts +++ b/src/trackers/__tests__/eventTracker.spec.ts @@ -1,6 +1,6 @@ import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; import { SplitIO } from '../../types'; -import eventTrackerFactory from '../eventTracker'; +import { eventTrackerFactory } from '../eventTracker'; /* Mocks */ diff --git a/src/trackers/__tests__/impressionsTracker.spec.ts b/src/trackers/__tests__/impressionsTracker.spec.ts index b85135f1..fe763d67 100644 --- a/src/trackers/__tests__/impressionsTracker.spec.ts +++ b/src/trackers/__tests__/impressionsTracker.spec.ts @@ -1,5 +1,5 @@ -import impressionsTrackerFactory from '../impressionsTracker'; -import ImpressionCountsCacheInMemory from '../../storages/inMemory/ImpressionCountsCacheInMemory'; +import { impressionsTrackerFactory } from '../impressionsTracker'; +import { ImpressionCountsCacheInMemory } from '../../storages/inMemory/ImpressionCountsCacheInMemory'; import { impressionObserverSSFactory } from '../impressionObserver/impressionObserverSS'; import { impressionObserverCSFactory } from '../impressionObserver/impressionObserverCS'; import { ImpressionDTO } from '../../types'; diff --git a/src/trackers/eventTracker.ts b/src/trackers/eventTracker.ts index b18c0546..b3d63f6e 100644 --- a/src/trackers/eventTracker.ts +++ b/src/trackers/eventTracker.ts @@ -1,5 +1,5 @@ -import objectAssign from 'object-assign'; -import thenable from '../utils/promise/thenable'; +import { objectAssign } from '../utils/lang/objectAssign'; +import { thenable } from '../utils/promise/thenable'; import { IEventsCacheBase } from '../storages/types'; import { IEventsHandler, IEventTracker } from './types'; import { SplitIO } from '../types'; @@ -12,7 +12,7 @@ import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constant * @param eventsCache cache to save events * @param integrationsManager optional event handler used for integrations */ -export default function eventTrackerFactory( +export function eventTrackerFactory( log: ILogger, eventsCache: IEventsCacheBase, integrationsManager?: IEventsHandler diff --git a/src/trackers/impressionObserver/ImpressionObserver.ts b/src/trackers/impressionObserver/ImpressionObserver.ts index 8ec9d260..377a4f08 100644 --- a/src/trackers/impressionObserver/ImpressionObserver.ts +++ b/src/trackers/impressionObserver/ImpressionObserver.ts @@ -1,8 +1,8 @@ import { ImpressionDTO } from '../../types'; -import LRUCache from '../../utils/LRUCache'; +import { LRUCache } from '../../utils/LRUCache'; import { IImpressionObserver } from './types'; -export default class ImpressionObserver implements IImpressionObserver { +export class ImpressionObserver implements IImpressionObserver { private cache: LRUCache; private hasher: (impression: ImpressionDTO) => K; diff --git a/src/trackers/impressionObserver/impressionObserverCS.ts b/src/trackers/impressionObserver/impressionObserverCS.ts index bf86409e..712d8738 100644 --- a/src/trackers/impressionObserver/impressionObserverCS.ts +++ b/src/trackers/impressionObserver/impressionObserverCS.ts @@ -1,4 +1,4 @@ -import ImpressionObserver from './ImpressionObserver'; +import { ImpressionObserver } from './ImpressionObserver'; import { hash } from '../../utils/murmur3/murmur3'; import { buildKey } from './buildKey'; import { ImpressionDTO } from '../../types'; diff --git a/src/trackers/impressionObserver/impressionObserverSS.ts b/src/trackers/impressionObserver/impressionObserverSS.ts index 828cfc21..7a81279f 100644 --- a/src/trackers/impressionObserver/impressionObserverSS.ts +++ b/src/trackers/impressionObserver/impressionObserverSS.ts @@ -1,4 +1,4 @@ -import ImpressionObserver from './ImpressionObserver'; +import { ImpressionObserver } from './ImpressionObserver'; import { hash128 } from '../../utils/murmur3/murmur3_128_x86'; import { buildKey } from './buildKey'; import { ImpressionDTO } from '../../types'; diff --git a/src/trackers/impressionsTracker.ts b/src/trackers/impressionsTracker.ts index 1c43f7fb..c7c18e5b 100644 --- a/src/trackers/impressionsTracker.ts +++ b/src/trackers/impressionsTracker.ts @@ -1,5 +1,5 @@ -import objectAssign from 'object-assign'; -import thenable from '../utils/promise/thenable'; +import { objectAssign } from '../utils/lang/objectAssign'; +import { thenable } from '../utils/promise/thenable'; import { truncateTimeFrame } from '../utils/time'; import { IImpressionCountsCacheSync, IImpressionsCacheBase } from '../storages/types'; import { IImpressionsHandler, IImpressionsTracker } from './types'; @@ -18,7 +18,7 @@ import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIO * @param observer optional impression observer. If provided, previous time (pt property) is included in impression instances * @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode) */ -export default function impressionsTrackerFactory( +export function impressionsTrackerFactory( log: ILogger, impressionsCache: IImpressionsCacheBase, diff --git a/src/utils/Backoff.ts b/src/utils/Backoff.ts index 3e39c83a..0e710b4d 100644 --- a/src/utils/Backoff.ts +++ b/src/utils/Backoff.ts @@ -1,4 +1,4 @@ -export default class Backoff { +export class Backoff { static DEFAULT_BASE_MILLIS = 1000; // 1 second static DEFAULT_MAX_MILLIS = 1800000; // 30 minutes diff --git a/src/utils/LRUCache/__tests__/index.spec.ts b/src/utils/LRUCache/__tests__/index.spec.ts index 3e59b0e2..7d35c027 100644 --- a/src/utils/LRUCache/__tests__/index.spec.ts +++ b/src/utils/LRUCache/__tests__/index.spec.ts @@ -1,4 +1,4 @@ -import LRUCache from '..'; +import { LRUCache } from '..'; test('Check Cache', () => { const cache = new LRUCache(5); diff --git a/src/utils/LRUCache/index.ts b/src/utils/LRUCache/index.ts index bad7267f..edf1b59b 100644 --- a/src/utils/LRUCache/index.ts +++ b/src/utils/LRUCache/index.ts @@ -1,7 +1,7 @@ import { IMap, _Map } from '../lang/maps'; import { LinkedList, Node } from './LinkedList'; -export default class LRUCache { +export class LRUCache { maxLen: number; items: IMap>; lru: LinkedList<{ key: K, value: V }>; diff --git a/src/utils/MinEventEmitter.ts b/src/utils/MinEventEmitter.ts index 1ff39d4e..a081f1e7 100644 --- a/src/utils/MinEventEmitter.ts +++ b/src/utils/MinEventEmitter.ts @@ -11,7 +11,7 @@ function checkListener(listener: unknown) { } // @TODO implement missing methods, check spec and add UTs -export default class EventEmitter implements IEventEmitter { +export class EventEmitter implements IEventEmitter { private listeners: Record void, // the event listener diff --git a/src/utils/inputValidation/__tests__/trafficTypeExistance.spec.ts b/src/utils/inputValidation/__tests__/trafficTypeExistance.spec.ts index 293ccb84..ad19302f 100644 --- a/src/utils/inputValidation/__tests__/trafficTypeExistance.spec.ts +++ b/src/utils/inputValidation/__tests__/trafficTypeExistance.spec.ts @@ -1,7 +1,7 @@ import { IReadinessManager } from '../../../readiness/types'; import { ISplitsCacheBase } from '../../../storages/types'; import { LOCALHOST_MODE, STANDALONE_MODE } from '../../constants'; -import thenable from '../../promise/thenable'; +import { thenable } from '../../promise/thenable'; import { WARN_NOT_EXISTENT_TT } from '../../../logger/constants'; /** Mocks */ diff --git a/src/utils/inputValidation/trafficTypeExistance.ts b/src/utils/inputValidation/trafficTypeExistance.ts index 99d9f705..743ceb8f 100644 --- a/src/utils/inputValidation/trafficTypeExistance.ts +++ b/src/utils/inputValidation/trafficTypeExistance.ts @@ -1,4 +1,4 @@ -import thenable from '../promise/thenable'; +import { thenable } from '../promise/thenable'; import { LOCALHOST_MODE } from '../constants'; import { ISplitsCacheBase } from '../../storages/types'; import { IReadinessManager } from '../../readiness/types'; diff --git a/src/utils/lang/binarySearch.ts b/src/utils/lang/binarySearch.ts index 4cf34852..7ff83fa0 100644 --- a/src/utils/lang/binarySearch.ts +++ b/src/utils/lang/binarySearch.ts @@ -6,7 +6,7 @@ * @returns integer number between 0 and `items.length`. This value is the index of the search value, * if it is contained in the array, or the index at which the value should be inserted to keep the array ordered. */ -export default function binarySearch(items: number[], value: number): number { +export function binarySearch(items: number[], value: number): number { let startIndex = 0; let stopIndex = items.length - 1; let middle = Math.floor((stopIndex + startIndex) / 2); diff --git a/src/utils/lang/objectAssign.ts b/src/utils/lang/objectAssign.ts new file mode 100644 index 00000000..23fcbd9a --- /dev/null +++ b/src/utils/lang/objectAssign.ts @@ -0,0 +1,104 @@ +/* +Adaptation of "object-assign" library (https://www.npmjs.com/package/object-assign) +exported as an ES module instead of CommonJS, to avoid extra configuration steps when using +the ESM build of the SDK with tools that doesn't support CommonJS by default (e.g. Rollup). + +object-assign +(c) Sindre Sorhus +@license MIT +*/ + +/* eslint-disable */ +// @ts-nocheck + +var getOwnPropertySymbols = Object.getOwnPropertySymbols; +var hasOwnProperty = Object.prototype.hasOwnProperty; +var propIsEnumerable = Object.prototype.propertyIsEnumerable; + +function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError('Object.assign cannot be called with null or undefined'); + } + + return Object(val); +} + +function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } + + // Detect buggy property enumeration order in older V8 versions. + + // https://bugs.chromium.org/p/v8/issues/detail?id=4118 + var test1 = new String('abc'); + test1[5] = 'de'; + if (Object.getOwnPropertyNames(test1)[0] === '5') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2['_' + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function (n) { + return test2[n]; + }); + if (order2.join('') !== '0123456789') { + return false; + } + + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test3 = {}; + 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { + test3[letter] = letter; + }); + if (Object.keys(Object.assign({}, test3)).join('') !== + 'abcdefghijklmnopqrst') { + return false; + } + + return true; + } catch (err) { + // We don't expect any of the above to throw, but better to be safe. + return false; + } +} + +// https://www.npmjs.com/package/@types/object-assign +type ObjectAssign = ((target: T, source: U) => T & U) & + ((target: T, source1: U, source2: V) => T & U & V) & + ((target: T, source1: U, source2: V, source3: W) => T & U & V & W) & + ((target: T, source1: U, source2: V, source3: W, source4: Q) => T & U & V & W & Q) & + ((target: T, source1: U, source2: V, source3: W, source4: Q, source5: R) => T & U & V & W & Q & R) & + ((target: any, ...sources: any[]) => any); + +export const objectAssign: ObjectAssign = shouldUseNative() ? Object.assign : function (target, source) { + var from; + var to = toObject(target); + var symbols; + + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); + + // eslint-disable-next-line no-restricted-syntax + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } + + return to; +}; diff --git a/src/utils/promise/thenable.ts b/src/utils/promise/thenable.ts index d63d489b..6770eba1 100644 --- a/src/utils/promise/thenable.ts +++ b/src/utils/promise/thenable.ts @@ -1,2 +1,4 @@ // returns true if the given value is a thenable object -export default (o: any): o is Promise => o !== undefined && o !== null && typeof o.then === 'function'; +export function thenable(o: any): o is Promise { + return o !== undefined && o !== null && typeof o.then === 'function'; +} diff --git a/src/utils/promise/timeout.ts b/src/utils/promise/timeout.ts index 3ca70b8c..9d3c12ee 100644 --- a/src/utils/promise/timeout.ts +++ b/src/utils/promise/timeout.ts @@ -1,4 +1,4 @@ -export default function timeout(ms: number, promise: Promise): Promise { +export function timeout(ms: number, promise: Promise): Promise { if (ms < 1) return promise; return new Promise((resolve, reject) => { diff --git a/src/utils/promise/wrapper.ts b/src/utils/promise/wrapper.ts index 7c7779ec..d0100fd6 100644 --- a/src/utils/promise/wrapper.ts +++ b/src/utils/promise/wrapper.ts @@ -16,7 +16,7 @@ * @returns a promise that doesn't need to be handled for rejection (except when using async/await syntax) and * includes a method named `hasOnFulfilled` that returns true if the promise has attached an onFulfilled handler. */ -export default function promiseWrapper(customPromise: Promise, defaultOnRejected: (_: any) => any): Promise & { hasOnFulfilled: () => boolean } { +export function promiseWrapper(customPromise: Promise, defaultOnRejected: (_: any) => any): Promise & { hasOnFulfilled: () => boolean } { let hasOnFulfilled = false; let hasOnRejected = false; diff --git a/src/utils/settingsValidation/impressionsMode.ts b/src/utils/settingsValidation/impressionsMode.ts index 84ae2f14..ddcda23d 100644 --- a/src/utils/settingsValidation/impressionsMode.ts +++ b/src/utils/settingsValidation/impressionsMode.ts @@ -3,7 +3,7 @@ import { ILogger } from '../../logger/types'; import { SplitIO } from '../../types'; import { DEBUG, OPTIMIZED } from '../constants'; -export default function validImpressionsMode(log: ILogger, impressionsMode: string): SplitIO.ImpressionsMode { +export function validImpressionsMode(log: ILogger, impressionsMode: string): SplitIO.ImpressionsMode { impressionsMode = impressionsMode.toUpperCase(); if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) === -1) { log.error(ERROR_INVALID_IMPRESSIONS_MODE, [[DEBUG, OPTIMIZED], OPTIMIZED]); diff --git a/src/utils/settingsValidation/index.ts b/src/utils/settingsValidation/index.ts index 1840fdb5..321c2375 100644 --- a/src/utils/settingsValidation/index.ts +++ b/src/utils/settingsValidation/index.ts @@ -1,8 +1,8 @@ import { merge } from '../lang'; -import mode from './mode'; +import { mode } from './mode'; import { validateSplitFilters } from './splitFilters'; import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE } from '../constants'; -import validImpressionsMode from './impressionsMode'; +import { validImpressionsMode } from './impressionsMode'; import { ISettingsValidationParams } from './types'; import { ISettings } from '../../types'; diff --git a/src/utils/settingsValidation/mode.ts b/src/utils/settingsValidation/mode.ts index dd11fe06..8480a24b 100644 --- a/src/utils/settingsValidation/mode.ts +++ b/src/utils/settingsValidation/mode.ts @@ -1,6 +1,6 @@ import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants'; -export default function mode(key: string, mode: string) { +export function mode(key: string, mode: string) { // Leaving the comparison as is, in case we change the mode name but not the setting. if (key === 'localhost') return LOCALHOST_MODE; diff --git a/src/utils/timeTracker/__tests__/index.spec.ts b/src/utils/timeTracker/__tests__/index.spec.ts index e95def88..e4036fd8 100644 --- a/src/utils/timeTracker/__tests__/index.spec.ts +++ b/src/utils/timeTracker/__tests__/index.spec.ts @@ -1,5 +1,5 @@ -import timer from '../timer'; -import tracker from '../index'; +import { timer } from '../timer'; +import { TrackerAPI } from '../index'; import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { IResponse } from '../../../services/types'; @@ -19,18 +19,18 @@ test('TIMER / should count the time between two tasks', (done) => { describe('TIME TRACKER', () => { test('should have the correct API', () => { - expect(typeof tracker.start).toBe('function'); // It should have the correct API. - expect(typeof tracker.stop).toBe('function'); // It should have the correct API. - expect(typeof tracker.TaskNames).toBe('object'); // It should have the correct API. + expect(typeof TrackerAPI.start).toBe('function'); // It should have the correct API. + expect(typeof TrackerAPI.stop).toBe('function'); // It should have the correct API. + expect(typeof TrackerAPI.TaskNames).toBe('object'); // It should have the correct API. }); test('start() / should return the correct type', () => { const promise = new Promise(res => { setTimeout(res, 1000); }); - const startNormal = tracker.start(loggerMock, tracker.TaskNames.SDK_READY); - const startNormalFake = tracker.start(loggerMock, 'fakeTask3'); - const startWithPromise = tracker.start(loggerMock, 'fakeTask4', undefined, promise); + const startNormal = TrackerAPI.start(loggerMock, TrackerAPI.TaskNames.SDK_READY); + const startNormalFake = TrackerAPI.start(loggerMock, 'fakeTask3'); + const startWithPromise = TrackerAPI.start(loggerMock, 'fakeTask4', undefined, promise); expect(typeof startNormal).toBe('function'); // If we call start without a promise, it will return the stop function, // @ts-expect-error @@ -42,14 +42,14 @@ describe('TIME TRACKER', () => { }); test('stop() / should stop the timer and return the time, if any', () => { - tracker.start(loggerMock, 'test_task'); + TrackerAPI.start(loggerMock, 'test_task'); // creating two tasks with the same task name - const stopFromStart = tracker.start(loggerMock, 'fakeTask5') as () => number; - const stopFromStart2 = tracker.start(loggerMock, 'fakeTask5') as () => number; + const stopFromStart = TrackerAPI.start(loggerMock, 'fakeTask5') as () => number; + const stopFromStart2 = TrackerAPI.start(loggerMock, 'fakeTask5') as () => number; - const stopNotExistentTask = tracker.stop(loggerMock, 'not_existent'); - const stopNotExistentTaskAndModifier = tracker.stop(loggerMock, 'test_task', 'mod'); + const stopNotExistentTask = TrackerAPI.stop(loggerMock, 'not_existent'); + const stopNotExistentTaskAndModifier = TrackerAPI.stop(loggerMock, 'test_task', 'mod'); expect(typeof stopNotExistentTask).toBe('undefined'); // If we try to stop a timer that does not exist, we get undefined. expect(typeof stopNotExistentTaskAndModifier).toBe('undefined'); // If we try to stop a timer that does not exist, we get undefined. diff --git a/src/utils/timeTracker/index.ts b/src/utils/timeTracker/index.ts index c6899659..a09289b8 100644 --- a/src/utils/timeTracker/index.ts +++ b/src/utils/timeTracker/index.ts @@ -1,6 +1,6 @@ import { uniqueId } from '../lang'; -import timer from './timer'; -import thenable from '../promise/thenable'; +import { timer } from './timer'; +import { thenable } from '../promise/thenable'; import { ILogger } from '../../logger/types'; import { IResponse } from '../../services/types'; @@ -112,7 +112,8 @@ function getCallbackForTask(task: string, collector: MetricsCollector | false): return false; } -const TrackerAPI = { +// Our "time tracker" API +export const TrackerAPI = { /** * "Private" method, used to attach count/countException and stop callbacks to a promise. * @@ -223,6 +224,3 @@ const TrackerAPI = { */ TaskNames: CONSTANTS }; - -// Our "time tracker" API -export default TrackerAPI; diff --git a/src/utils/timeTracker/now/browser.ts b/src/utils/timeTracker/now/browser.ts index 314ab2e4..5c939606 100644 --- a/src/utils/timeTracker/now/browser.ts +++ b/src/utils/timeTracker/now/browser.ts @@ -9,6 +9,4 @@ function nowFactory() { } } -const now = nowFactory(); - -export default now; +export const now = nowFactory(); diff --git a/src/utils/timeTracker/now/node.ts b/src/utils/timeTracker/now/node.ts index dbe3366d..fe5c5f27 100644 --- a/src/utils/timeTracker/now/node.ts +++ b/src/utils/timeTracker/now/node.ts @@ -1,5 +1,5 @@ // @TODO migrate to Node SDK package eventually -export default function now() { +export function now() { // eslint-disable-next-line no-undef let time = process.hrtime(); diff --git a/src/utils/timeTracker/timer.ts b/src/utils/timeTracker/timer.ts index 29b99405..425f993a 100644 --- a/src/utils/timeTracker/timer.ts +++ b/src/utils/timeTracker/timer.ts @@ -1,4 +1,4 @@ -export default function start(now?: () => number) { +export function timer(now?: () => number) { const st = now ? now() : Date.now(); return function stop() { From 605b119ce6f936e3f7e723a172a21d00cb3fb66d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 3 Jan 2022 19:58:34 -0300 Subject: [PATCH 31/34] prepare rc --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 979c9292..b6c2312b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 38d9443c..7a34192c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", From 9875f6bff7c9652583d189eb924c77ea21efc7f3 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 5 Jan 2022 16:52:22 -0300 Subject: [PATCH 32/34] polish log message 'Connecting to streaming' --- src/logger/messages/info.ts | 2 +- src/sync/streaming/pushManager.ts | 3 ++- src/utils/settingsValidation/index.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/logger/messages/info.ts b/src/logger/messages/info.ts index 22083bf6..42838345 100644 --- a/src/logger/messages/info.ts +++ b/src/logger/messages/info.ts @@ -24,7 +24,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([ [c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s %s.'], [c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'], [c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect in %s seconds.'], - [c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + '%sConnecting to streaming.'], + [c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting to streaming.'], [c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given Api key. Switching to polling mode.'], [c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting from streaming.'], [c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'], diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index 2ba45e92..f5130447 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -115,7 +115,8 @@ export function pushManagerFactory( function connectPush() { // Guard condition in case `stop/disconnectPush` has been called (e.g., calling SDK destroy, or app signal close/background) if (disconnected) return; - log.info(STREAMING_CONNECTING, [disconnected === undefined ? '' : 'Re-']); + // @TODO distinguish log for 'Connecting' (1st time) and 'Re-connecting' + log.info(STREAMING_CONNECTING); disconnected = false; const userKeys = userKey ? Object.keys(clients) : undefined; diff --git a/src/utils/settingsValidation/index.ts b/src/utils/settingsValidation/index.ts index 321c2375..e10965a0 100644 --- a/src/utils/settingsValidation/index.ts +++ b/src/utils/settingsValidation/index.ts @@ -122,7 +122,7 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV // Startup periods startup.requestTimeoutBeforeReady = fromSecondsToMillis(startup.requestTimeoutBeforeReady); startup.readyTimeout = fromSecondsToMillis(startup.readyTimeout); - startup.eventsFirstPushWindow = fromSecondsToMillis(withDefaults.startup.eventsFirstPushWindow); + startup.eventsFirstPushWindow = fromSecondsToMillis(startup.eventsFirstPushWindow); // ensure a valid SDK mode // @ts-ignore, modify readonly prop From 537aae859cd1aeb3bbcaeae9e93a49bcc4bf781e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 7 Jan 2022 20:23:26 -0300 Subject: [PATCH 33/34] updated readme --- CHANGES.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ad8321b2..8ba93e81 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,16 +1,16 @@ 1.1.0 (December 22, 2021) - Added support for the SDK to run in "consumer" and "partial consumer" modes, with a pluggable implementation of it's internal storage, enabling customers to implement this caching with any storage technology of choice and connect it to the SDK instance to use instead of its default in-memory storage. - - Updated multiple modules due to general polishing and improvements. + - Updated multiple modules due to general polishing and improvements, including the replacement of default exports with named exports, to avoid runtime errors with some particular configurations of Webpack projects. - Updated ioredis dependency for vulnerability fixes. - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot ("."). 1.0.0 (October 20, 2021) - BREAKING CHANGE on multiple modules due to general polishing, improvements and bug fixes. In most cases the change is to use named exports. This affected mostly modules related with synchronization and storages. - - Updated streaming logic to use the newest version of our streaming service, including: - - Integration with Auth service V2, connecting to the new channels and applying the received connection delay. - - Implemented handling of the new MySegmentsV2 notification types (SegmentRemoval, KeyList, Bounded and Unbounded) - - New control notification for environment scoped streaming reset. + - Updated streaming logic to use the newest version of our streaming service, including: + - Integration with Auth service V2, connecting to the new channels and applying the received connection delay. + - Implemented handling of the new MySegmentsV2 notification types (SegmentRemoval, KeyList, Bounded and Unbounded) + - New control notification for environment scoped streaming reset. - Updated localhost mode to emit SDK_READY_FROM_CACHE event in Browser when using localStorage (Related to issue https://github.com/splitio/react-client/issues/34). - Updated dependencies for vulnerability fixes. From c660e8179384394c9266e566282d0f2bdb971d5f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 11 Jan 2022 12:25:30 -0300 Subject: [PATCH 34/34] updated release date and licence.txt --- CHANGES.txt | 4 ++-- LICENSE | 2 +- src/sdkFactory/index.ts | 4 ++-- src/sync/polling/updaters/splitChangesUpdater.ts | 7 +++++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8ba93e81..b6952027 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,6 @@ -1.1.0 (December 22, 2021) +1.1.0 (January 11, 2022) - Added support for the SDK to run in "consumer" and "partial consumer" modes, with a pluggable implementation of it's internal storage, enabling - customers to implement this caching with any storage technology of choice and connect it to the SDK instance to use instead of its default in-memory storage. + customers to implement this caching with any storage technology of choice and connect it to the SDK instance to be used instead of its default in-memory storage. - Updated multiple modules due to general polishing and improvements, including the replacement of default exports with named exports, to avoid runtime errors with some particular configurations of Webpack projects. - Updated ioredis dependency for vulnerability fixes. - Bugfixing - Fixed issue returning dynamic configs if treatment name contains a dot ("."). diff --git a/LICENSE b/LICENSE index 18dec1b3..051b5fd9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2021 Split Software, Inc. +Copyright © 2022 Split Software, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/sdkFactory/index.ts b/src/sdkFactory/index.ts index 5fe1f10f..61433a5e 100644 --- a/src/sdkFactory/index.ts +++ b/src/sdkFactory/index.ts @@ -43,8 +43,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO. // ATM, only used by PluggableStorage mode: settings.mode, - // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined - // or only instantiates submitters, and therefore it is not able to emit readiness events. + // Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined, + // or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events. onReadyCb: (error) => { if (error) return; // Don't emit SDK_READY if storage failed to connect. Error message is logged by wrapperAdapter readinessManager.splits.emit(SDK_SPLITS_ARRIVED); diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index 754ceed9..822dbf13 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -168,9 +168,12 @@ export function splitChangesUpdaterFactory( return false; }); - // After triggering the requests, if we have cached splits information let's notify that. + // After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE. + // Wrapping in a promise since checkCache can be async. if (splitsEventEmitter && startingUp) { - Promise.resolve(splits.checkCache()).then(cacheReady => { if (cacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED); }); + Promise.resolve(splits.checkCache()).then(isCacheReady => { + if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED); + }); } return fetcherPromise; }