diff --git a/CHANGES.txt b/CHANGES.txt index b615364a..4a335cf8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +2.0.1 (November 25, 2024) + - Bugfixing - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification. + 2.0.0 (November 1, 2024) - Added support for targeting rules based on large segments. - Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory. @@ -20,7 +23,7 @@ - Updated some transitive dependencies for vulnerability fixes. 1.16.0 (June 13, 2024) - - Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in NodeJS. + - Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in Node.js. - Updated the Redis storage to lazily import the `ioredis` dependency when the storage is created. This prevents errors when the SDK is imported or bundled in a .mjs file, as `ioredis` is a CommonJS module. - Bugfixing - Restored some input validation error logs that were removed in version 1.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name. - Bugfixing - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119). @@ -57,7 +60,7 @@ - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). + - Bugfixing - Fixed SDK key validation in Node.js to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). 1.10.0 (October 20, 2023) - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). @@ -135,7 +138,7 @@ 1.3.0 (April 6, 2022) - Added user consent feature to allow delaying or disabling the data tracking from SDK until user consent is explicitly granted or declined. Read more in our docs. - Added `scheduler.impressionsQueueSize` property to SDK configuration to limit the amount of impressions tracked in memory. Read more in our docs. - - Added support to accept TLS configuration options to the Redis storage in NodeJS. Read more in our docs. + - Added support to accept TLS configuration options to the Redis storage in Node.js. Read more in our docs. - Updated format for MySegments keys in LocalStorage, keeping backwards compatibility (issue https://github.com/splitio/javascript-client/issues/638). - Updated some modules due to general polishing and refactors, including updates in some log messages. - Updated some dependencies for vulnerability fixes. diff --git a/README.md b/README.md index 16ec731b..9b5769b4 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Split has built and maintains SDKs for: * 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) * 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) +* Node.js [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) * PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK) * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK) diff --git a/package-lock.json b/package-lock.json index 40fecca7..65555d17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-commons", - "version": "2.0.0", + "version": "2.0.1", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -2531,9 +2531,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -10011,9 +10011,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", diff --git a/package.json b/package.json index 608c3982..fdf8c86d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "2.0.0", + "version": "2.0.1", "description": "Split JavaScript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/listeners/__tests__/node.spec.ts b/src/listeners/__tests__/node.spec.ts index bc594df9..15fe6253 100644 --- a/src/listeners/__tests__/node.spec.ts +++ b/src/listeners/__tests__/node.spec.ts @@ -5,7 +5,7 @@ const processOnSpy = jest.spyOn(process, 'on'); const processRemoveListenerSpy = jest.spyOn(process, 'removeListener'); const processKillSpy = jest.spyOn(process, 'kill').mockImplementation(() => true); -test('Node JS listener / Signal Listener class methods and start/stop functionality', () => { +test('NodeSignalListener / Signal Listener class methods and start/stop functionality', () => { const syncManagerMock = { flush: jest.fn() }; // @ts-expect-error const listener = new NodeSignalListener(syncManagerMock, fullSettings); @@ -25,7 +25,7 @@ test('Node JS listener / Signal Listener class methods and start/stop functional expect(processRemoveListenerSpy.mock.calls).toEqual([['SIGTERM', listener._sigtermHandler]]); }); -test('Node JS listener / Signal Listener SIGTERM callback with sync handler', () => { +test('NodeSignalListener / Signal Listener SIGTERM callback with sync handler', () => { const syncManagerMock = { flush: jest.fn() }; // @ts-expect-error const listener = new NodeSignalListener(syncManagerMock, fullSettings); @@ -55,7 +55,7 @@ test('Node JS listener / Signal Listener SIGTERM callback with sync handler', () processKillSpy.mockClear(); }); -test('Node JS listener / Signal Listener SIGTERM callback with sync handler that throws an error', () => { +test('NodeSignalListener / Signal Listener SIGTERM callback with sync handler that throws an error', () => { const syncManagerMock = { flush: jest.fn(() => { throw 'some error'; }) }; // @ts-expect-error const listener = new NodeSignalListener(syncManagerMock, fullSettings); @@ -85,7 +85,7 @@ test('Node JS listener / Signal Listener SIGTERM callback with sync handler that processKillSpy.mockClear(); }); -test('Node JS listener / Signal Listener SIGTERM callback with async handler', async () => { +test('NodeSignalListener / Signal Listener SIGTERM callback with async handler', async () => { const fakePromise = new Promise(res => { setTimeout(() => { @@ -125,7 +125,7 @@ test('Node JS listener / Signal Listener SIGTERM callback with async handler', a }); }); -test('Node JS listener / Signal Listener SIGTERM callback with async handler that throws an error', async () => { +test('NodeSignalListener / Signal Listener SIGTERM callback with async handler that throws an error', async () => { const fakePromise = new Promise((res, rej) => { setTimeout(() => { diff --git a/src/listeners/node.ts b/src/listeners/node.ts index f57557be..5089f0b5 100644 --- a/src/listeners/node.ts +++ b/src/listeners/node.ts @@ -1,4 +1,4 @@ -// @TODO eventually migrate to JS-Node-SDK package. +// @TODO eventually migrate to Node.js SDK package. import { ISignalListener } from './types'; import { thenable } from '../utils/promise/thenable'; import { MaybeThenable } from '../dtos/types'; @@ -25,7 +25,7 @@ export class NodeSignalListener implements ISignalListener { syncManager: ISyncManager | undefined, // private handler: () => MaybeThenable, settings: ISettings ) { - // @TODO review handler logic when implementing Node SDK + // @TODO review handler logic when implementing Node.js SDK this.handler = function () { if (syncManager) { // syncManager.stop(); diff --git a/src/sdkClient/sdkClient.ts b/src/sdkClient/sdkClient.ts index fbc4aeb9..cda5ba2e 100644 --- a/src/sdkClient/sdkClient.ts +++ b/src/sdkClient/sdkClient.ts @@ -56,22 +56,18 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo // Mark the SDK as destroyed immediately sdkReadinessManager.readinessManager.destroy(); - // For main client, release the SDK Key and record stat before flushing data + // For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data if (!isSharedClient) { releaseApiKey(settings.core.authorizationKey); telemetryTracker.sessionLength(); + signalListener && signalListener.stop(); + uniqueKeysTracker && uniqueKeysTracker.stop(); } // Stop background jobs syncManager && syncManager.stop(); return __flush().then(() => { - // For main client, cleanup event listeners and scheduled jobs - if (!isSharedClient) { - signalListener && signalListener.stop(); - uniqueKeysTracker && uniqueKeysTracker.stop(); - } - // Cleanup storage return storage.destroy(); }); diff --git a/src/sdkFactory/types.ts b/src/sdkFactory/types.ts index 443b1456..19fa5f10 100644 --- a/src/sdkFactory/types.ts +++ b/src/sdkFactory/types.ts @@ -28,7 +28,7 @@ export interface IPlatform { */ getEventSource?: (settings: ISettings) => (IEventSourceConstructor | undefined) /** - * EventEmitter constructor, like NodeJS.EventEmitter or a polyfill. + * EventEmitter constructor, like Node.js EventEmitter or a polyfill. */ EventEmitter: new () => SplitIO.IEventEmitter, /** @@ -104,7 +104,7 @@ export interface ISdkFactoryParams { filterAdapterFactory?: () => IFilterAdapter // Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed. - // Pass only if `syncManager` (used by Node listener) and `splitApi` (used by Browser listener) are passed. + // Pass only if `syncManager` (used by NodeSignalListener) and `splitApi` (used by Browser listener) are passed. SignalListener?: new ( syncManager: ISyncManager | undefined, // Used by NodeSignalListener to flush data, and by BrowserSignalListener to close streaming connection. settings: ISettings, // Used by BrowserSignalListener diff --git a/src/storages/AbstractMySegmentsCacheSync.ts b/src/storages/AbstractMySegmentsCacheSync.ts index a03fc416..7d3dc304 100644 --- a/src/storages/AbstractMySegmentsCacheSync.ts +++ b/src/storages/AbstractMySegmentsCacheSync.ts @@ -42,7 +42,7 @@ export abstract class AbstractMySegmentsCacheSync implements ISegmentsCacheSync // @TODO for client-side it should be the number of clients, but it requires a refactor of MySegments caches to simplify the code. abstract getKeysCount(): number - abstract getChangeNumber(name: string): number + abstract getChangeNumber(): number /** * For server-side synchronizer: the method is not used. diff --git a/src/storages/inMemory/SegmentsCacheInMemory.ts b/src/storages/inMemory/SegmentsCacheInMemory.ts index 87ca71ce..d25b89d2 100644 --- a/src/storages/inMemory/SegmentsCacheInMemory.ts +++ b/src/storages/inMemory/SegmentsCacheInMemory.ts @@ -65,7 +65,7 @@ export class SegmentsCacheInMemory implements ISegmentsCacheSync { getChangeNumber(name: string) { const value = this.segmentChangeNumber[name]; - return isIntegerNumber(value) ? value : -1; + return isIntegerNumber(value) ? value : undefined; } // No-op. Not used in server-side diff --git a/src/storages/inRedis/SegmentsCacheInRedis.ts b/src/storages/inRedis/SegmentsCacheInRedis.ts index 42ed3b10..d645495f 100644 --- a/src/storages/inRedis/SegmentsCacheInRedis.ts +++ b/src/storages/inRedis/SegmentsCacheInRedis.ts @@ -44,10 +44,10 @@ export class SegmentsCacheInRedis implements ISegmentsCacheAsync { return this.redis.get(this.keys.buildSegmentTillKey(name)).then((value: string | null) => { const i = parseInt(value as string, 10); - return isNaNNumber(i) ? -1 : i; + return isNaNNumber(i) ? undefined : i; }).catch((e) => { this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e); - return -1; + return undefined; }); } diff --git a/src/storages/inRedis/SplitsCacheInRedis.ts b/src/storages/inRedis/SplitsCacheInRedis.ts index c98dca6e..8c1df363 100644 --- a/src/storages/inRedis/SplitsCacheInRedis.ts +++ b/src/storages/inRedis/SplitsCacheInRedis.ts @@ -19,7 +19,7 @@ function processPipelineAnswer(results: Array<[Error | null, string]>): string[] /** * ISplitsCacheAsync implementation that stores split definitions in Redis. - * Supported by Node. + * Supported by Node.js */ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync { diff --git a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts index 62799bab..f31d2c91 100644 --- a/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/SegmentsCacheInRedis.spec.ts @@ -17,7 +17,7 @@ describe('SEGMENTS CACHE IN REDIS', () => { expect(await cache.getChangeNumber('mocked-segment') === 1).toBe(true); - expect(await cache.getChangeNumber('inexistent-segment')).toBe(-1); // -1 if the segment doesn't exist + expect(await cache.getChangeNumber('inexistent-segment')).toBe(undefined); // -1 if the segment doesn't exist await cache.update('mocked-segment', ['d', 'e'], [], 2); diff --git a/src/storages/inRedis/index.ts b/src/storages/inRedis/index.ts index 0971eb51..80474209 100644 --- a/src/storages/inRedis/index.ts +++ b/src/storages/inRedis/index.ts @@ -18,7 +18,7 @@ export interface InRedisStorageOptions { } /** - * InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node. + * InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js * @see {@link https://www.npmjs.com/package/ioredis} */ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsyncFactory { diff --git a/src/storages/pluggable/SegmentsCachePluggable.ts b/src/storages/pluggable/SegmentsCachePluggable.ts index 033b1a49..145953d2 100644 --- a/src/storages/pluggable/SegmentsCachePluggable.ts +++ b/src/storages/pluggable/SegmentsCachePluggable.ts @@ -55,10 +55,10 @@ export class SegmentsCachePluggable implements ISegmentsCacheAsync { return this.wrapper.get(this.keys.buildSegmentTillKey(name)).then((value: string | null) => { const i = parseInt(value as string, 10); - return isNaNNumber(i) ? -1 : i; + return isNaNNumber(i) ? undefined : i; }).catch((e) => { this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from segments storage. Error: ' + e); - return -1; + return undefined; }); } diff --git a/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts b/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts index eedb8f11..459f59eb 100644 --- a/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts +++ b/src/storages/pluggable/__tests__/SegmentsCachePluggable.spec.ts @@ -20,7 +20,7 @@ describe('SEGMENTS CACHE PLUGGABLE', () => { expect(await cache.getChangeNumber('mocked-segment') === 1).toBe(true); - expect(await cache.getChangeNumber('inexistent-segment')).toBe(-1); // -1 if the segment doesn't exist + expect(await cache.getChangeNumber('inexistent-segment')).toBe(undefined); // -1 if the segment doesn't exist await cache.update('mocked-segment', ['d', 'e'], [], 2); diff --git a/src/storages/types.ts b/src/storages/types.ts index 83f388b1..5d7b40b6 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -237,7 +237,7 @@ export interface ISegmentsCacheBase { isInSegment(name: string, key?: string): MaybeThenable // different signature on Server and Client-Side registerSegments(names: string[]): MaybeThenable // only for Server-Side getRegisteredSegments(): MaybeThenable // only for Server-Side - getChangeNumber(name: string): MaybeThenable // only for Server-Side + getChangeNumber(name: string): MaybeThenable // only for Server-Side update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): MaybeThenable // only for Server-Side clear(): MaybeThenable } @@ -248,7 +248,7 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase { registerSegments(names: string[]): boolean getRegisteredSegments(): string[] getKeysCount(): number // only used for telemetry - getChangeNumber(name?: string): number + getChangeNumber(name?: string): number | undefined update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): boolean // only for Server-Side resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side clear(): void @@ -258,7 +258,7 @@ export interface ISegmentsCacheAsync extends ISegmentsCacheBase { isInSegment(name: string, key: string): Promise registerSegments(names: string[]): Promise getRegisteredSegments(): Promise - getChangeNumber(name: string): Promise + getChangeNumber(name: string): Promise update(name: string, addedKeys: string[], removedKeys: string[], changeNumber: number): Promise clear(): Promise } diff --git a/src/sync/polling/updaters/segmentChangesUpdater.ts b/src/sync/polling/updaters/segmentChangesUpdater.ts index 5f9db114..c1009077 100644 --- a/src/sync/polling/updaters/segmentChangesUpdater.ts +++ b/src/sync/polling/updaters/segmentChangesUpdater.ts @@ -33,9 +33,9 @@ export function segmentChangesUpdaterFactory( return sincePromise.then(since => { // if fetchOnlyNew flag, avoid processing already fetched segments - return fetchOnlyNew && since !== -1 ? + return fetchOnlyNew && since !== undefined ? false : - segmentChangesFetcher(since, segmentName, noCache, till).then((changes) => { + segmentChangesFetcher(since || -1, segmentName, noCache, till).then((changes) => { return Promise.all(changes.map(x => { log.debug(`${LOG_PREFIX_SYNC_SEGMENTS}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`); return segments.update(segmentName, x.added, x.removed, x.till); diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index bf8803a2..e8153987 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -19,7 +19,7 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise { let registeredSegments = Promise.resolve(segments.getRegisteredSegments()); return registeredSegments.then(segmentNames => { return Promise.all(segmentNames.map(segmentName => segments.getChangeNumber(segmentName))) - .then(changeNumbers => changeNumbers.every(changeNumber => changeNumber !== -1)); + .then(changeNumbers => changeNumbers.every(changeNumber => changeNumber !== undefined)); }); } diff --git a/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts b/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts index bafdd37d..52cccb22 100644 --- a/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +++ b/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts @@ -3,10 +3,11 @@ import { Backoff } from '../../../utils/Backoff'; import { IUpdateWorker } from './types'; import { ITelemetryTracker } from '../../../trackers/types'; import { MEMBERSHIPS } from '../../../utils/constants'; -import { ISegmentsCacheSync, IStorageSync } from '../../../storages/types'; +import { IStorageSync } from '../../../storages/types'; import { ILogger } from '../../../logger/types'; import { FETCH_BACKOFF_MAX_RETRIES } from './constants'; import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../constants'; +import { AbstractMySegmentsCacheSync } from '../../../storages/AbstractMySegmentsCacheSync'; /** * MySegmentsUpdateWorker factory @@ -16,7 +17,7 @@ export function MySegmentsUpdateWorker(log: ILogger, storage: Pick segmentsCache.getChangeNumber(segment)) { + if (maxChangeNumber > (segmentsCache.getChangeNumber(segment) || -1)) { handleNewEvent = false; // fetch segments revalidating data if cached @@ -32,7 +32,7 @@ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSy } else { const attempts = backoff.attempts + 1; - if (maxChangeNumber <= segmentsCache.getChangeNumber(segment)) { + if (maxChangeNumber <= (segmentsCache.getChangeNumber(segment) || -1)) { log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`); isHandlingEvent = false; return; @@ -60,7 +60,7 @@ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSy return { put(changeNumber: number) { - const currentChangeNumber = segmentsCache.getChangeNumber(segment); + const currentChangeNumber = segmentsCache.getChangeNumber(segment) || -1; if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return; diff --git a/src/sync/syncTask.ts b/src/sync/syncTask.ts index 5f22f6c0..7820c060 100644 --- a/src/sync/syncTask.ts +++ b/src/sync/syncTask.ts @@ -68,8 +68,8 @@ export function syncTaskFactory(log: ILogger, }, stop() { - running = false; - if (timeoutID) { + if (running) { + running = false; log.debug(SYNC_TASK_STOP, [taskName]); clearTimeout(timeoutID); timeoutID = undefined; diff --git a/src/trackers/impressionObserver/__tests__/serverSideObserver.spec.ts b/src/trackers/impressionObserver/__tests__/serverSideObserver.spec.ts index 7f14cfe8..607c9a44 100644 --- a/src/trackers/impressionObserver/__tests__/serverSideObserver.spec.ts +++ b/src/trackers/impressionObserver/__tests__/serverSideObserver.spec.ts @@ -74,7 +74,7 @@ test('Hasher 128 / Impression Hasher Does Not Crash', () => { expect(hashImpression128(imp1)).not.toBe(null); }); -test('Server-side (Node JS) / Impression Observer Basic Functionality', () => { +test('Server-side (Node.js) / Impression Observer Basic Functionality', () => { const observer = impressionObserverSSFactory(); const imp = { diff --git a/src/utils/settingsValidation/index.ts b/src/utils/settingsValidation/index.ts index ce1d1772..17fed4ea 100644 --- a/src/utils/settingsValidation/index.ts +++ b/src/utils/settingsValidation/index.ts @@ -64,7 +64,7 @@ export const base = { // Defines if the logs are enabled, SDK wide. debug: undefined, - // Defines the impression listener, but will only be used on NodeJS. + // Defines the impression listener. impressionListener: undefined, // Instance version. diff --git a/src/utils/settingsValidation/types.ts b/src/utils/settingsValidation/types.ts index 7ced1b33..745360bf 100644 --- a/src/utils/settingsValidation/types.ts +++ b/src/utils/settingsValidation/types.ts @@ -2,7 +2,7 @@ import { ISettings } from '../../types'; /** * Parameters used to specialize the settings validation for each API variant - * (client-side, server-side) and environment (Node server, Browser, etc) + * (client-side, server-side) and environment (Node.js, Browser, etc) */ export interface ISettingsValidationParams { /** diff --git a/types/splitio.d.ts b/types/splitio.d.ts index a87dfc9b..f2faa2df 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -164,7 +164,7 @@ interface INonPluggableSharedSettings { */ interface IServerSideSharedSettings { /** - * SDK Core settings for NodeJS. + * SDK Core settings for Node.js. */ core: { /** @@ -187,7 +187,7 @@ interface IServerSideSharedSettings { IPAddressesEnabled?: boolean; }; /** - * SDK Startup settings for NodeJS. + * SDK Startup settings for Node.js. */ startup?: { /** @@ -450,7 +450,7 @@ interface IClientSideSyncSharedSettings extends IClientSideSharedSettings, ISync declare namespace SplitIO { /** - * EventEmitter interface based on a subset of the NodeJS.EventEmitter methods. + * EventEmitter interface based on a subset of the Node.js EventEmitter methods. */ interface IEventEmitter { addListener(event: string, listener: (...args: any[]) => void): this; @@ -462,7 +462,7 @@ declare namespace SplitIO { emit(event: string, ...args: any[]): boolean; } /** - * NodeJS.EventEmitter interface + * Node.js EventEmitter interface * @see {@link https://nodejs.org/api/events.html} */ interface EventEmitter extends IEventEmitter { @@ -478,7 +478,7 @@ declare namespace SplitIO { listeners(event: string | symbol): Function[]; rawListeners(event: string | symbol): Function[]; listenerCount(type: string | symbol): number; - // Added in Node 6... + // Added in Node.js 6... prependListener(event: string | symbol, listener: (...args: any[]) => void): this; prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; eventNames(): Array; @@ -557,7 +557,7 @@ declare namespace SplitIO { readonly debug: boolean | LogLevel | ILogger; readonly version: string; /** - * Mocked features map if using in client-side, or mocked features file path string if using in server-side (NodeJS). + * Mocked features map if using in client-side, or mocked features file path string if using in server-side (Node.js). */ features: MockedFeaturesMap | MockedFeaturesFilePath; readonly streamingEnabled: boolean; @@ -576,7 +576,7 @@ declare namespace SplitIO { }; readonly impressionListener?: IImpressionListener; /** - * User consent status if using in client-side. Undefined if using in server-side (NodeJS). + * User consent status if using in client-side. Undefined if using in server-side (Node.js). */ readonly userConsent?: ConsentStatus; } @@ -936,11 +936,11 @@ declare namespace SplitIO { wrapper: Object; } /** - * Synchronous storage valid types for NodeJS. + * Synchronous storage valid types for Node.js. */ type NodeSyncStorage = 'MEMORY'; /** - * Asynchronous storages valid types for NodeJS. + * Asynchronous storages valid types for Node.js. */ type NodeAsyncStorage = 'REDIS'; /** @@ -1237,14 +1237,14 @@ declare namespace SplitIO { }; } /** - * Settings interface for JavaScript SDK instances created on NodeJS, with server-side API and synchronous in-memory storage. + * Settings interface for JavaScript SDK instances created on Node.js, with server-side API and synchronous in-memory storage. * If your storage is asynchronous (Redis for example) use SplitIO.INodeAsyncSettings instead. * * @see {@link https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK#configuration} */ interface INodeSettings extends IServerSideSharedSettings, ISyncSharedSettings, INonPluggableSharedSettings { /** - * Defines which kind of storage we can instantiate on NodeJS for 'standalone' mode. + * Defines which kind of storage we can instantiate on Node.js for 'standalone' mode. * The only possible storage type is 'MEMORY', which is the default. */ storage?: { @@ -1263,7 +1263,7 @@ declare namespace SplitIO { }; sync?: ISyncSharedSettings['sync'] & { /** - * Custom options object for HTTP(S) requests in NodeJS. + * Custom options object for HTTP(S) requests in Node.js. * If provided, this object is merged with the options object passed by the SDK for EventSource and Node-Fetch calls. * @see {@link https://www.npmjs.com/package/node-fetch#options} */ @@ -1291,7 +1291,7 @@ declare namespace SplitIO { */ getHeaderOverrides?: (context: { headers: Record }) => Record; /** - * Custom NodeJS HTTP(S) Agent used by the SDK for HTTP(S) requests. + * Custom Node.js HTTP(S) Agent used by the SDK for HTTP(S) requests. * * You can use it, for example, for certificate pinning or setting a network proxy: * @@ -1319,7 +1319,7 @@ declare namespace SplitIO { }; } /** - * Settings interface for JavaScript SDK instances created on NodeJS, with asynchronous storage like Redis. + * Settings interface for JavaScript SDK instances created on Node.js, with asynchronous storage like Redis. * If your storage is synchronous (by default we use memory, which is sync) use SplitIO.INodeSettings instead. * * @see {@link https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK#configuration} @@ -1332,7 +1332,7 @@ declare namespace SplitIO { */ mode: 'consumer'; /** - * Defines which kind of async storage we can instantiate on NodeJS for 'consumer' mode. + * Defines which kind of async storage we can instantiate on Node.js for 'consumer' mode. * The only possible storage type is 'REDIS'. */ storage: { @@ -1501,7 +1501,7 @@ declare namespace SplitIO { } /** * This represents the interface for the Client instance on server-side, where the user key is not bound to the instance and must be provided on each method call. - * This interface is available in NodeJS, or when importing the 'server' sub-package of JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`). + * This interface is available in Node.js, or when importing the 'server' sub-package of JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`). */ interface IClient extends IBasicClient { /** @@ -1592,7 +1592,7 @@ declare namespace SplitIO { /** * This represents the interface for the Client instance on server-side with asynchronous storage, like REDIS. * User key is not bound to the instance and must be provided on each method call, which returns a promise. - * This interface is available in NodeJS, or when importing the 'server' sub-package in JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`). + * This interface is available in Node.js, or when importing the 'server' sub-package in JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`). */ interface IAsyncClient extends IBasicClient { /**