From 628b1855e72dc9f4dcafc35d6d9933e9d19cc090 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Oct 2024 19:10:07 -0300 Subject: [PATCH 01/15] Make factory and client context properties available in first render when a config prop is provided --- src/SplitFactoryProvider.tsx | 64 ++++----------------- src/__tests__/SplitClient.test.tsx | 14 +++-- src/__tests__/SplitContext.test.tsx | 8 ++- src/__tests__/SplitFactoryProvider.test.tsx | 36 +++++++----- src/__tests__/testUtils/mockSplitFactory.ts | 4 ++ src/__tests__/testUtils/utils.tsx | 6 +- src/__tests__/useSplitManager.test.tsx | 8 +-- src/__tests__/useTrack.test.tsx | 23 ++++---- src/useTrack.ts | 2 +- src/utils.ts | 2 + 10 files changed, 71 insertions(+), 96 deletions(-) diff --git a/src/SplitFactoryProvider.tsx b/src/SplitFactoryProvider.tsx index 1046d48..f0ff09b 100644 --- a/src/SplitFactoryProvider.tsx +++ b/src/SplitFactoryProvider.tsx @@ -3,8 +3,7 @@ import React from 'react'; import { SplitClient } from './SplitClient'; import { ISplitFactoryProviderProps } from './types'; import { WARN_SF_CONFIG_AND_FACTORY } from './constants'; -import { getSplitFactory, destroySplitFactory, IFactoryWithClients, getSplitClient, getStatus } from './utils'; -import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient'; +import { getSplitFactory, destroySplitFactory, getSplitClient, getStatus } from './utils'; import { SplitContext } from './SplitContext'; /** @@ -18,70 +17,29 @@ import { SplitContext } from './SplitContext'; * @see {@link https://help.split.io/hc/en-us/articles/360038825091-React-SDK#2-instantiate-the-sdk-and-create-a-new-split-client} */ export function SplitFactoryProvider(props: ISplitFactoryProviderProps) { - let { - config, factory: propFactory, - updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate - } = { ...DEFAULT_UPDATE_OPTIONS, ...props }; + const { config, factory: propFactory } = props; - if (config && propFactory) { - console.log(WARN_SF_CONFIG_AND_FACTORY); - config = undefined; - } - - const [configFactory, setConfigFactory] = React.useState(); const factory = React.useMemo(() => { - return propFactory || (configFactory && config === configFactory.config ? configFactory : undefined); - }, [config, propFactory, configFactory]); + return propFactory || (config ? getSplitFactory(config) : undefined); + }, [config, propFactory]); const client = factory ? getSplitClient(factory) : undefined; // Effect to initialize and destroy the factory React.useEffect(() => { + if (propFactory) { + if (config) console.log(WARN_SF_CONFIG_AND_FACTORY); + return; + } + if (config) { const factory = getSplitFactory(config); + factory.init && factory.init(); return () => { destroySplitFactory(factory); } } - }, [config]); - - // Effect to subscribe/unsubscribe to events - React.useEffect(() => { - const factory = config && getSplitFactory(config); - if (factory) { - const client = getSplitClient(factory); - const status = getStatus(client); - - // Unsubscribe from events and update state when first event is emitted - const update = () => { // eslint-disable-next-line no-use-before-define - unsubscribe(); - setConfigFactory(factory); - } - - const unsubscribe = () => { - client.off(client.Event.SDK_READY, update); - client.off(client.Event.SDK_READY_FROM_CACHE, update); - client.off(client.Event.SDK_READY_TIMED_OUT, update); - client.off(client.Event.SDK_UPDATE, update); - } - - if (updateOnSdkReady) { - if (status.isReady) update(); - else client.once(client.Event.SDK_READY, update); - } - if (updateOnSdkReadyFromCache) { - if (status.isReadyFromCache) update(); - else client.once(client.Event.SDK_READY_FROM_CACHE, update); - } - if (updateOnSdkTimedout) { - if (status.hasTimedout) update(); - else client.once(client.Event.SDK_READY_TIMED_OUT, update); - } - if (updateOnSdkUpdate) client.on(client.Event.SDK_UPDATE, update); - - return unsubscribe; - } - }, [config, updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate]); + }, [config, propFactory]); return ( { return { SplitFactory: mockSdk() }; }); @@ -14,7 +14,7 @@ import { ISplitClientChildProps } from '../types'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; -import { INITIAL_CONTEXT, testAttributesBinding, TestComponentProps } from './testUtils/utils'; +import { INITIAL_STATUS, testAttributesBinding, TestComponentProps } from './testUtils/utils'; import { IClientWithContext } from '../utils'; import { EXCEPTION_NO_SFP } from '../constants'; @@ -25,7 +25,11 @@ describe('SplitClient', () => { {(childProps: ISplitClientChildProps) => { - expect(childProps).toEqual(INITIAL_CONTEXT); + expect(childProps).toEqual({ + ...INITIAL_STATUS, + factory: getLastInstance(SplitFactory), + client: getLastInstance(SplitFactory).client('user1'), + }); return null; }} @@ -47,7 +51,7 @@ describe('SplitClient', () => { {(childProps: ISplitClientChildProps) => { expect(childProps).toEqual({ - ...INITIAL_CONTEXT, + ...INITIAL_STATUS, factory : outerFactory, client: outerFactory.client(), isReady: true, @@ -232,7 +236,7 @@ describe('SplitClient', () => { {(value) => { expect(value).toEqual({ - ...INITIAL_CONTEXT, + ...INITIAL_STATUS, factory: outerFactory, client: outerFactory.client('user2'), }); diff --git a/src/__tests__/SplitContext.test.tsx b/src/__tests__/SplitContext.test.tsx index 4ffb11d..5388adb 100644 --- a/src/__tests__/SplitContext.test.tsx +++ b/src/__tests__/SplitContext.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { SplitContext } from '../SplitContext'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; -import { INITIAL_CONTEXT } from './testUtils/utils'; +import { INITIAL_STATUS } from './testUtils/utils'; /** * Test default SplitContext value @@ -23,7 +23,11 @@ test('SplitContext.Consumer shows value when wrapped in a SplitFactoryProvider', {(value) => { - expect(value).toEqual(INITIAL_CONTEXT); + expect(value).toEqual({ + ...INITIAL_STATUS, + factory: undefined, + client: undefined + }); return null; }} diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index f2c5039..f212393 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, act } from '@testing-library/react'; /** Mocks */ -import { mockSdk, Event } from './testUtils/mockSplitFactory'; +import { mockSdk, Event, getLastInstance } from './testUtils/mockSplitFactory'; jest.mock('@splitsoftware/splitio/client', () => { return { SplitFactory: mockSdk() }; }); @@ -17,7 +17,7 @@ import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; import { __factories, IClientWithContext } from '../utils'; import { WARN_SF_CONFIG_AND_FACTORY } from '../constants'; -import { INITIAL_CONTEXT } from './testUtils/utils'; +import { INITIAL_STATUS } from './testUtils/utils'; describe('SplitFactoryProvider', () => { @@ -25,7 +25,11 @@ describe('SplitFactoryProvider', () => { render( {(childProps: ISplitFactoryProviderChildProps) => { - expect(childProps).toEqual(INITIAL_CONTEXT); + expect(childProps).toEqual({ + ...INITIAL_STATUS, + factory: getLastInstance(SplitFactory), + client: getLastInstance(SplitFactory).client(), + }); return null; }} @@ -43,7 +47,7 @@ describe('SplitFactoryProvider', () => { {(childProps: ISplitFactoryProviderChildProps) => { expect(childProps).toEqual({ - ...INITIAL_CONTEXT, + ...INITIAL_STATUS, factory: outerFactory, client: outerFactory.client(), isReady: true, @@ -84,7 +88,7 @@ describe('SplitFactoryProvider', () => { default: fail('Child must not be rerendered'); } // eslint-disable-next-line no-use-before-define - if (factory) expect(factory).toBe(innerFactory); + expect(factory).toBe(innerFactory || getLastInstance(SplitFactory)); expect(lastUpdate).toBeGreaterThan(previousLastUpdate); renderTimes++; previousLastUpdate = lastUpdate; @@ -93,7 +97,7 @@ describe('SplitFactoryProvider', () => { ); - const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value; + const innerFactory = getLastInstance(SplitFactory); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY)); @@ -168,7 +172,7 @@ describe('SplitFactoryProvider', () => { default: fail('Child must not be rerendered'); } // eslint-disable-next-line no-use-before-define - if (factory) expect(factory).toBe(innerFactory); + expect(factory).toBe(innerFactory || getLastInstance(SplitFactory)); expect(lastUpdate).toBeGreaterThan(previousLastUpdate); renderTimes++; previousLastUpdate = lastUpdate; @@ -177,7 +181,7 @@ describe('SplitFactoryProvider', () => { ); - const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value; + const innerFactory = getLastInstance(SplitFactory); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE)); @@ -241,7 +245,7 @@ describe('SplitFactoryProvider', () => { default: fail('Child must not be rerendered'); } // eslint-disable-next-line no-use-before-define - if (factory) expect(factory).toBe(innerFactory); + expect(factory).toBe(innerFactory || getLastInstance(SplitFactory)); expect(lastUpdate).toBeGreaterThan(previousLastUpdate); renderTimes++; previousLastUpdate = lastUpdate; @@ -250,7 +254,7 @@ describe('SplitFactoryProvider', () => { ); - const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value; + const innerFactory = getLastInstance(SplitFactory); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY)); act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE)); @@ -296,7 +300,11 @@ describe('SplitFactoryProvider', () => { return ( {(value) => { - expect(value).toEqual(INITIAL_CONTEXT); + expect(value).toEqual({ + ...INITIAL_STATUS, + factory: getLastInstance(SplitFactory), + client: getLastInstance(SplitFactory).client(), + }); done(); return null; }} @@ -344,14 +352,14 @@ describe('SplitFactoryProvider', () => { case 5: expect(isReady).toBe(false); expect(hasTimedout).toBe(false); - expect(factory).toBe(undefined); + expect(factory).toBe(getLastInstance(SplitFactory)); return null; case 3: case 4: case 6: expect(isReady).toBe(true); expect(hasTimedout).toBe(true); - expect(factory).not.toBe(undefined); + expect(factory).toBe(getLastInstance(SplitFactory)); createdFactories.add(factory!); clientDestroySpies.push(jest.spyOn(factory!.client(), 'destroy')); return ( @@ -368,7 +376,7 @@ describe('SplitFactoryProvider', () => { }; const emitSdkEvents = () => { - const factory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value; + const factory = getLastInstance(SplitFactory); factory.client().__emitter__.emit(Event.SDK_READY_TIMED_OUT) factory.client().__emitter__.emit(Event.SDK_READY) }; diff --git a/src/__tests__/testUtils/mockSplitFactory.ts b/src/__tests__/testUtils/mockSplitFactory.ts index 379b747..c8f4959 100644 --- a/src/__tests__/testUtils/mockSplitFactory.ts +++ b/src/__tests__/testUtils/mockSplitFactory.ts @@ -164,3 +164,7 @@ export function mockSdk() { }); } + +export function getLastInstance(SplitFactoryMock: any) { + return SplitFactoryMock.mock.results.slice(-1)[0].value; +} diff --git a/src/__tests__/testUtils/utils.tsx b/src/__tests__/testUtils/utils.tsx index 6324bde..36a0c5f 100644 --- a/src/__tests__/testUtils/utils.tsx +++ b/src/__tests__/testUtils/utils.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { ISplitContextValues } from '../../types'; +import { ISplitStatus } from '../../types'; const { SplitFactory: originalSplitFactory } = jest.requireActual('@splitsoftware/splitio/client'); export interface TestComponentProps { @@ -116,9 +116,7 @@ export function testAttributesBinding(Component: React.FunctionComponent); } -export const INITIAL_CONTEXT: ISplitContextValues = { - client: undefined, - factory: undefined, +export const INITIAL_STATUS: ISplitStatus = { isReady: false, isReadyFromCache: false, isTimedout: false, diff --git a/src/__tests__/useSplitManager.test.tsx b/src/__tests__/useSplitManager.test.tsx index c8f9dff..97fe026 100644 --- a/src/__tests__/useSplitManager.test.tsx +++ b/src/__tests__/useSplitManager.test.tsx @@ -14,6 +14,7 @@ import { getStatus } from '../utils'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { useSplitManager } from '../useSplitManager'; import { EXCEPTION_NO_SFP } from '../constants'; +import { INITIAL_STATUS } from './testUtils/utils'; describe('useSplitManager', () => { @@ -33,12 +34,7 @@ describe('useSplitManager', () => { manager: outerFactory.manager(), client: outerFactory.client(), factory: outerFactory, - hasTimedout: false, - isDestroyed: false, - isReady: false, - isReadyFromCache: false, - isTimedout: false, - lastUpdate: 0, + ...INITIAL_STATUS, }); act(() => (outerFactory.client() as any).__emitter__.emit(Event.SDK_READY)); diff --git a/src/__tests__/useTrack.test.tsx b/src/__tests__/useTrack.test.tsx index 33706c1..bb047c6 100644 --- a/src/__tests__/useTrack.test.tsx +++ b/src/__tests__/useTrack.test.tsx @@ -22,36 +22,37 @@ describe('useTrack', () => { const value = 10; const properties = { prop1: 'prop1' }; - test('returns the track method bound to the client at Split context updated by SplitFactoryProvider.', () => { + test('returns the track method of the client at Split context updated by SplitFactoryProvider.', () => { const outerFactory = SplitFactory(sdkBrowser); - let boundTrack; + let clientTrack; let trackResult; render( {React.createElement(() => { - boundTrack = useTrack(); - trackResult = boundTrack(tt, eventType, value, properties); + clientTrack = useTrack(); + trackResult = clientTrack(tt, eventType, value, properties); return null; })} , ); const track = outerFactory.client().track as jest.Mock; + expect(track).toBe(clientTrack); expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toHaveReturnedWith(trackResult); }); - test('returns the track method bound to the client at Split context updated by SplitClient.', () => { + test('returns the track method of the client at Split context updated by SplitClient.', () => { const outerFactory = SplitFactory(sdkBrowser); - let boundTrack; + let clientTrack; let trackResult; render( {React.createElement(() => { - boundTrack = useTrack(); - trackResult = boundTrack(tt, eventType, value, properties); + clientTrack = useTrack(); + trackResult = clientTrack(tt, eventType, value, properties); return null; })} @@ -62,15 +63,15 @@ describe('useTrack', () => { expect(track).toHaveReturnedWith(trackResult); }); - test('returns the track method bound to a new client given a splitKey.', () => { + test('returns the track method of a new client given a splitKey.', () => { const outerFactory = SplitFactory(sdkBrowser); let trackResult; render( {React.createElement(() => { - const boundTrack = useTrack('user2'); - trackResult = boundTrack(tt, eventType, value, properties); + const clientTrack = useTrack('user2'); + trackResult = clientTrack(tt, eventType, value, properties); return null; })} , diff --git a/src/useTrack.ts b/src/useTrack.ts index d9394ef..a737f20 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -14,5 +14,5 @@ const noOpFalse = () => false; export function useTrack(splitKey?: SplitIO.SplitKey): SplitIO.IBrowserClient['track'] { // All update options are false to avoid re-renders. The track method doesn't need the client to be operational. const { client } = useSplitClient({ splitKey, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); - return client ? client.track.bind(client) : noOpFalse; + return client ? client.track : noOpFalse; } diff --git a/src/utils.ts b/src/utils.ts index a4023c0..3cee655 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -26,6 +26,7 @@ export interface IClientWithContext extends SplitIO.IBrowserClient { */ export interface IFactoryWithClients extends SplitIO.IBrowserSDK { config: SplitIO.IBrowserSettings; + init(): void; } // exported for testing purposes @@ -38,6 +39,7 @@ export function getSplitFactory(config: SplitIO.IBrowserSettings) { // @ts-expect-error. 2nd param is not part of type definitions. Used to overwrite the SDK version const newFactory = SplitFactory(config, (modules) => { modules.settings.version = VERSION; + modules.lazyInit = true; }) as IFactoryWithClients; newFactory.config = config; __factories.set(config, newFactory); From 833d6c92c83d7a49d801c7d1c23c41fb167df2b7 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Oct 2024 23:13:47 -0300 Subject: [PATCH 02/15] Added test to validate bugfix --- CHANGES.txt | 1 + package.json | 2 +- src/__tests__/SplitClient.test.tsx | 3 +-- src/__tests__/useTrack.test.tsx | 36 ++++++++++++++++++++++++++---- src/useTrack.ts | 7 +++--- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 000f2c0..3dd15d4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ - Added support for targeting rules based on large segments for browsers. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for EcmaScript Modules build. + - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event. This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render but rather a no-op function. - BREAKING CHANGES: - Updated error handling: using the library modules without wrapping them in a `SplitFactoryProvider` component will now throw an error instead of logging it, as the modules requires the `SplitContext` to work properly. - Removed the `core.trafficType` configuration option and the `trafficType` parameter from the SDK `client()` method, `useSplitClient`, `useTrack`, and `SplitClient` component. This is because traffic types can no longer be bound to SDK clients in JavaScript SDK v11.0.0, and so the traffic type must be provided as first argument in the `track` method calls. diff --git a/package.json b/package.json index d41e870..239db6d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "check": "npm run check:lint && npm run check:types", "check:lint": "eslint 'src/**/*.ts*'", "check:types": "tsc --noEmit", - "test": "jest src", + "test": "jest src --silent", "test:watch": "npm test -- --watch", "test:coverage": "jest src --coverage", "test:debug": "node --inspect node_modules/.bin/jest --runInBand", diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index 2c4ece3..d2bdade 100644 --- a/src/__tests__/SplitClient.test.tsx +++ b/src/__tests__/SplitClient.test.tsx @@ -214,8 +214,7 @@ describe('SplitClient', () => { count++; // side effect in the render phase - if (!(client as IClientWithContext).__getStatus().isReady) { - console.log('emit'); + if (!(client as any).__getStatus().isReady) { (client as any).__emitter__.emit(Event.SDK_READY); } diff --git a/src/__tests__/useTrack.test.tsx b/src/__tests__/useTrack.test.tsx index bb047c6..b77d850 100644 --- a/src/__tests__/useTrack.test.tsx +++ b/src/__tests__/useTrack.test.tsx @@ -1,8 +1,8 @@ -import React from 'react'; -import { render } from '@testing-library/react'; +import React, { useEffect } from 'react'; +import { render, act } from '@testing-library/react'; /** Mocks */ -import { mockSdk } from './testUtils/mockSplitFactory'; +import { mockSdk, Event, getLastInstance } from './testUtils/mockSplitFactory'; jest.mock('@splitsoftware/splitio/client', () => { return { SplitFactory: mockSdk() }; }); @@ -13,6 +13,7 @@ import { sdkBrowser } from './testUtils/sdkConfigs'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { useTrack } from '../useTrack'; +import { useSplitClient } from '../useSplitClient'; import { EXCEPTION_NO_SFP } from '../constants'; describe('useTrack', () => { @@ -22,7 +23,34 @@ describe('useTrack', () => { const value = 10; const properties = { prop1: 'prop1' }; - test('returns the track method of the client at Split context updated by SplitFactoryProvider.', () => { + test('returns the track method of the client at Split context updated by SplitFactoryProvider (config prop).', () => { + render( + + {React.createElement(() => { + const clientTrack = useTrack(); + + const { client } = useSplitClient(); + expect(clientTrack).toBe(client!.track); + + clientTrack(tt, eventType, value, properties); + + useEffect(() => { + clientTrack(tt, eventType, value, properties); + }, [clientTrack]); + return null; + })} + , + ); + + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE)); + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY)); + + const track = getLastInstance(SplitFactory).client().track as jest.Mock; + expect(track).toBeCalledWith(tt, eventType, value, properties); + expect(track).toBeCalledTimes(4); // 3 from render + 1 from useEffect (`clientTrack` dependency doesn't change) + }); + + test('returns the track method of the client at Split context updated by SplitFactoryProvider (factory prop).', () => { const outerFactory = SplitFactory(sdkBrowser); let clientTrack; let trackResult; diff --git a/src/useTrack.ts b/src/useTrack.ts index a737f20..4e7fc73 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -4,10 +4,11 @@ import { useSplitClient } from './useSplitClient'; const noOpFalse = () => false; /** - * 'useTrack' is a hook that returns the track method from a Split client. - * It uses the 'useContext' hook to access the client from the Split context. + * 'useTrack' is a hook that retrieves the track method from a Split client. + * It uses the 'useSplitClient' hook to access the client from the Split context. + * Basically, it is a shortcut for `const track = useSplitClient().client?.track || (() => false);`. * - * @returns A track function bound to a Split client. If the client is not available, the result is a no-op function that returns false. + * @returns A track function of the Split client. If the client is not available, the result is a no-op function that returns false. * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#track} */ From 6bfb15be1382b91907487c5343e566e61489dc6d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 24 Oct 2024 00:00:06 -0300 Subject: [PATCH 03/15] Polishing --- CHANGES.txt | 2 +- src/__tests__/useTrack.test.tsx | 63 ++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3dd15d4..2b23b9b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ - Added support for targeting rules based on large segments for browsers. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for EcmaScript Modules build. - - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event. This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render but rather a no-op function. + - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event (Related to https://github.com/splitio/react-client/issues/198). This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render. - BREAKING CHANGES: - Updated error handling: using the library modules without wrapping them in a `SplitFactoryProvider` component will now throw an error instead of logging it, as the modules requires the `SplitContext` to work properly. - Removed the `core.trafficType` configuration option and the `trafficType` parameter from the SDK `client()` method, `useSplitClient`, `useTrack`, and `SplitClient` component. This is because traffic types can no longer be bound to SDK clients in JavaScript SDK v11.0.0, and so the traffic type must be provided as first argument in the `track` method calls. diff --git a/src/__tests__/useTrack.test.tsx b/src/__tests__/useTrack.test.tsx index b77d850..6a1d2bd 100644 --- a/src/__tests__/useTrack.test.tsx +++ b/src/__tests__/useTrack.test.tsx @@ -23,34 +23,7 @@ describe('useTrack', () => { const value = 10; const properties = { prop1: 'prop1' }; - test('returns the track method of the client at Split context updated by SplitFactoryProvider (config prop).', () => { - render( - - {React.createElement(() => { - const clientTrack = useTrack(); - - const { client } = useSplitClient(); - expect(clientTrack).toBe(client!.track); - - clientTrack(tt, eventType, value, properties); - - useEffect(() => { - clientTrack(tt, eventType, value, properties); - }, [clientTrack]); - return null; - })} - , - ); - - act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE)); - act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY)); - - const track = getLastInstance(SplitFactory).client().track as jest.Mock; - expect(track).toBeCalledWith(tt, eventType, value, properties); - expect(track).toBeCalledTimes(4); // 3 from render + 1 from useEffect (`clientTrack` dependency doesn't change) - }); - - test('returns the track method of the client at Split context updated by SplitFactoryProvider (factory prop).', () => { + test('returns the track method of the client at Split context updated by SplitFactoryProvider.', () => { const outerFactory = SplitFactory(sdkBrowser); let clientTrack; let trackResult; @@ -121,4 +94,38 @@ describe('useTrack', () => { }).toThrow(EXCEPTION_NO_SFP); }); + test('returns the track method of the client at Split context updated by SplitFactoryProvider (config prop).', () => { + let splitKey: string | undefined = undefined; + render( + + {React.createElement(() => { + const clientTrack = useTrack(splitKey); + + const { client } = useSplitClient({ splitKey }); + expect(clientTrack).toBe(client!.track); + + clientTrack(tt, eventType, value, properties); + + useEffect(() => { + clientTrack(tt, eventType, value, properties); + }, [clientTrack]); + return null; + })} + , + ); + + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE)); + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY)); + splitKey = 'user2'; // `clientTrack` dependency changed + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_UPDATE)); + + let track = getLastInstance(SplitFactory).client().track as jest.Mock; + expect(track).toBeCalledWith(tt, eventType, value, properties); + expect(track).toBeCalledTimes(4); // 3 from render + 1 from useEffect + + track = getLastInstance(SplitFactory).client('user2').track as jest.Mock; + expect(track).toBeCalledWith(tt, eventType, value, properties); + expect(track).toBeCalledTimes(2); // 1 from render + 1 from useEffect (`clientTrack` dependency changed) + }); + }); From e1bad0a130191614f8c25e491a1b8fdc615a30dd Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 24 Oct 2024 16:18:41 -0300 Subject: [PATCH 04/15] Added test to validate useTrack does not re-render --- src/__tests__/useTrack.test.tsx | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/__tests__/useTrack.test.tsx b/src/__tests__/useTrack.test.tsx index 6a1d2bd..2cdf7c4 100644 --- a/src/__tests__/useTrack.test.tsx +++ b/src/__tests__/useTrack.test.tsx @@ -37,7 +37,7 @@ describe('useTrack', () => { })} , ); - const track = outerFactory.client().track as jest.Mock; + const track = outerFactory.client().track; expect(track).toBe(clientTrack); expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toHaveReturnedWith(trackResult); @@ -59,7 +59,7 @@ describe('useTrack', () => { ); - const track = outerFactory.client('user2').track as jest.Mock; + const track = outerFactory.client('user2').track; expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toHaveReturnedWith(trackResult); }); @@ -77,7 +77,7 @@ describe('useTrack', () => { })} , ); - const track = outerFactory.client('user2').track as jest.Mock; + const track = outerFactory.client('user2').track; expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toHaveReturnedWith(trackResult); }); @@ -119,13 +119,33 @@ describe('useTrack', () => { splitKey = 'user2'; // `clientTrack` dependency changed act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_UPDATE)); - let track = getLastInstance(SplitFactory).client().track as jest.Mock; + let track = getLastInstance(SplitFactory).client().track; expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toBeCalledTimes(4); // 3 from render + 1 from useEffect - track = getLastInstance(SplitFactory).client('user2').track as jest.Mock; + track = getLastInstance(SplitFactory).client('user2').track; expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toBeCalledTimes(2); // 1 from render + 1 from useEffect (`clientTrack` dependency changed) }); + test('does not re-render on SDK events', () => { + render( + + {React.createElement(() => { + const clientTrack = useTrack(); + clientTrack(tt, eventType, value, properties); + + return null; + })} + , + ); + + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT)); + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE)); + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_READY)); + act(() => getLastInstance(SplitFactory).client().__emitter__.emit(Event.SDK_UPDATE)); + + expect(getLastInstance(SplitFactory).client().track).toBeCalledTimes(1); + }); + }); From e26260057e26b91960f1602f5cc27173ef06b482 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 24 Oct 2024 17:43:53 -0300 Subject: [PATCH 05/15] Polishing --- package-lock.json | 2 +- package.json | 2 +- src/SplitTreatments.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 063dd91..a010821 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "webpack-merge": "^5.8.0" }, "peerDependencies": { - "react": ">=16.3.0" + "react": ">=16.8.0" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index e2a7661..814624a 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "check": "npm run check:lint && npm run check:types", "check:lint": "eslint 'src/**/*.ts*'", "check:types": "tsc --noEmit", - "test": "jest src", + "test": "jest src --silent", "test:watch": "npm test -- --watch", "test:coverage": "jest src --coverage", "test:debug": "node --inspect node_modules/.bin/jest --runInBand", diff --git a/src/SplitTreatments.tsx b/src/SplitTreatments.tsx index f9c0940..5a6bc68 100644 --- a/src/SplitTreatments.tsx +++ b/src/SplitTreatments.tsx @@ -14,7 +14,7 @@ import { useSplitTreatments } from './useSplitTreatments'; export function SplitTreatments(props: ISplitTreatmentsProps) { const { children } = props; // SplitTreatments doesn't update on SDK events, since it is inside SplitFactory and/or SplitClient. - const context = useSplitTreatments({ updateOnSdkReady: false, updateOnSdkTimedout: false, ...props }); + const context = useSplitTreatments({ updateOnSdkReady: false, updateOnSdkReadyFromCache: false, ...props }); return ( From fec84b32dd89e9513e4b84c2d4a36ca34624c63a Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 24 Oct 2024 17:51:31 -0300 Subject: [PATCH 06/15] Polishing --- src/SplitTreatments.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SplitTreatments.tsx b/src/SplitTreatments.tsx index 5a6bc68..43142af 100644 --- a/src/SplitTreatments.tsx +++ b/src/SplitTreatments.tsx @@ -14,7 +14,7 @@ import { useSplitTreatments } from './useSplitTreatments'; export function SplitTreatments(props: ISplitTreatmentsProps) { const { children } = props; // SplitTreatments doesn't update on SDK events, since it is inside SplitFactory and/or SplitClient. - const context = useSplitTreatments({ updateOnSdkReady: false, updateOnSdkReadyFromCache: false, ...props }); + const context = useSplitTreatments({ ...props, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); return ( From bafcfd9998fcb1d067d6106bb559d06270c74cc2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 25 Oct 2024 18:34:52 -0300 Subject: [PATCH 07/15] Update JS SDK to use updated Type declaration files --- package-lock.json | 38 ++++++++++----------- package.json | 4 +-- src/__tests__/SplitFactoryProvider.test.tsx | 4 +-- src/__tests__/testUtils/mockSplitFactory.ts | 2 +- src/__tests__/testUtils/utils.tsx | 2 +- src/__tests__/utils.test.ts | 2 +- src/types.ts | 8 ++--- src/useTrack.ts | 2 +- src/utils.ts | 13 +++---- 9 files changed, 38 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8f6e2c..0d74ecb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-react", - "version": "1.13.1-rc.0", + "version": "2.0.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-react", - "version": "1.13.1-rc.0", + "version": "2.0.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.1", + "@splitsoftware/splitio": "11.0.0-rc.3", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -1548,12 +1548,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "11.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.1.tgz", - "integrity": "sha512-wBr7i+EUlB7h9ccNgbS0k8aEA3ZEzNrL9dcd5OAATflCnAC5sp33KGgi/anbTCErs344JvRXdzoqAMKV84B3Hw==", + "version": "11.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.3.tgz", + "integrity": "sha512-7wPuhIPjAj7Sywwz1TNY3OpM3WuP07r+qKtBZg+ccU3PorQOzPhacvHQRWMu8VcrULc34LW4YR8wkyaIyCFB5g==", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0-rc.2", - "@types/ioredis": "^4.28.0", + "@splitsoftware/splitio-commons": "2.0.0-rc.4", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -1566,10 +1565,11 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.2.tgz", - "integrity": "sha512-QfCg2D5hjg+iuQiEYpsMEjtJ/dcYUsbcXJbvj7BH9t9DfVEplCv09oJcsC6CTFTSeA958y7yqe2EvTy/HVSM5g==", + "version": "2.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.4.tgz", + "integrity": "sha512-z0rm4X9oh7LdIcHeEkh6Ca3JzfdhJ0IH3r7nZ6ghL0AXmPzbezoDswxjKOe/WYBbTW3utsgLqTTjr9Ww/b/hUw==", "dependencies": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" }, "peerDependencies": { @@ -12028,12 +12028,11 @@ } }, "@splitsoftware/splitio": { - "version": "11.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.1.tgz", - "integrity": "sha512-wBr7i+EUlB7h9ccNgbS0k8aEA3ZEzNrL9dcd5OAATflCnAC5sp33KGgi/anbTCErs344JvRXdzoqAMKV84B3Hw==", + "version": "11.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.3.tgz", + "integrity": "sha512-7wPuhIPjAj7Sywwz1TNY3OpM3WuP07r+qKtBZg+ccU3PorQOzPhacvHQRWMu8VcrULc34LW4YR8wkyaIyCFB5g==", "requires": { - "@splitsoftware/splitio-commons": "2.0.0-rc.2", - "@types/ioredis": "^4.28.0", + "@splitsoftware/splitio-commons": "2.0.0-rc.4", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -12043,10 +12042,11 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.2.tgz", - "integrity": "sha512-QfCg2D5hjg+iuQiEYpsMEjtJ/dcYUsbcXJbvj7BH9t9DfVEplCv09oJcsC6CTFTSeA958y7yqe2EvTy/HVSM5g==", + "version": "2.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.4.tgz", + "integrity": "sha512-z0rm4X9oh7LdIcHeEkh6Ca3JzfdhJ0IH3r7nZ6ghL0AXmPzbezoDswxjKOe/WYBbTW3utsgLqTTjr9Ww/b/hUw==", "requires": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" } }, diff --git a/package.json b/package.json index 239db6d..a3ecf5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-react", - "version": "1.13.1-rc.0", + "version": "2.0.0-rc.1", "description": "A React library to easily integrate and use Split JS SDK", "main": "cjs/index.js", "module": "esm/index.js", @@ -63,7 +63,7 @@ }, "homepage": "https://github.com/splitio/react-client#readme", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.1", + "@splitsoftware/splitio": "11.0.0-rc.3", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index f212393..def463b 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -54,7 +54,7 @@ describe('SplitFactoryProvider', () => { isReadyFromCache: true, lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate }); - expect((childProps.factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version); + expect((childProps.factory as SplitIO.ISDK).settings.version).toBe(outerFactory.settings.version); return null; }} @@ -337,7 +337,7 @@ describe('SplitFactoryProvider', () => { test('cleans up on update and unmount if config prop is provided.', () => { let renderTimes = 0; - const createdFactories = new Set(); + const createdFactories = new Set(); const clientDestroySpies: jest.SpyInstance[] = []; const outerFactory = SplitFactory(sdkBrowser); diff --git a/src/__tests__/testUtils/mockSplitFactory.ts b/src/__tests__/testUtils/mockSplitFactory.ts index c8f4959..a399bcf 100644 --- a/src/__tests__/testUtils/mockSplitFactory.ts +++ b/src/__tests__/testUtils/mockSplitFactory.ts @@ -1,5 +1,5 @@ import { EventEmitter } from 'events'; -import SplitIO from '@splitsoftware/splitio/types/splitio'; +import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import jsSdkPackageJson from '@splitsoftware/splitio/package.json'; import reactSdkPackageJson from '../../../package.json'; diff --git a/src/__tests__/testUtils/utils.tsx b/src/__tests__/testUtils/utils.tsx index 36a0c5f..9c3171e 100644 --- a/src/__tests__/testUtils/utils.tsx +++ b/src/__tests__/testUtils/utils.tsx @@ -8,7 +8,7 @@ export interface TestComponentProps { attributesClient: SplitIO.Attributes, splitKey: SplitIO.SplitKey, testSwitch: (done: jest.DoneCallback, splitKey?: SplitIO.SplitKey) => void, - factory: SplitIO.IBrowserSDK + factory: SplitIO.ISDK } export function newSplitFactoryLocalhostInstance() { diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index b5ef021..ce17a3c 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,6 +1,6 @@ import { CONTROL_WITH_CONFIG } from '../constants'; import { getControlTreatmentsWithConfig } from '../utils'; -import SplitIO from '@splitsoftware/splitio/types/splitio'; +import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; describe('getControlTreatmentsWithConfig', () => { diff --git a/src/types.ts b/src/types.ts index dc59bb9..7183f65 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import SplitIO from '@splitsoftware/splitio/types/splitio'; +import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import type { ReactNode } from 'react'; /** @@ -50,7 +50,7 @@ export interface ISplitContextValues extends ISplitStatus { * * NOTE: This property is not recommended for direct use, as better alternatives are available. */ - factory?: SplitIO.IBrowserSDK; + factory?: SplitIO.ISDK; /** * Split client instance. @@ -59,7 +59,7 @@ export interface ISplitContextValues extends ISplitStatus { * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#2-instantiate-the-sdk-and-create-a-new-split-client} */ - client?: SplitIO.IBrowserClient; + client?: SplitIO.IClient; } /** @@ -121,7 +121,7 @@ export interface ISplitFactoryProviderProps extends IUpdateProps { * * If both `factory` and `config` are provided, the `config` option is ignored. */ - factory?: SplitIO.IBrowserSDK; + factory?: SplitIO.ISDK; /** * An object of type Attributes used to evaluate the feature flags. diff --git a/src/useTrack.ts b/src/useTrack.ts index 4e7fc73..965601b 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -12,7 +12,7 @@ const noOpFalse = () => false; * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#track} */ -export function useTrack(splitKey?: SplitIO.SplitKey): SplitIO.IBrowserClient['track'] { +export function useTrack(splitKey?: SplitIO.SplitKey): SplitIO.IClient['track'] { // All update options are false to avoid re-renders. The track method doesn't need the client to be operational. const { client } = useSplitClient({ splitKey, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); return client ? client.track : noOpFalse; diff --git a/src/utils.ts b/src/utils.ts index 3cee655..d7274bd 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,13 +3,14 @@ import shallowEqual from 'shallowequal'; import { SplitFactory } from '@splitsoftware/splitio/client'; import { CONTROL_WITH_CONFIG, VERSION, WARN_NAMES_AND_FLAGSETS } from './constants'; import { ISplitStatus } from './types'; +import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; // Utils used to access singleton instances of Split factories and clients, and to gracefully shutdown all clients together. /** * ClientWithContext interface. */ -export interface IClientWithContext extends SplitIO.IBrowserClient { +export interface IClientWithContext extends SplitIO.IClient { __getStatus(): { isReady: boolean; isReadyFromCache: boolean; @@ -24,7 +25,7 @@ export interface IClientWithContext extends SplitIO.IBrowserClient { /** * FactoryWithClientInstances interface. */ -export interface IFactoryWithClients extends SplitIO.IBrowserSDK { +export interface IFactoryWithClients extends SplitIO.ISDK { config: SplitIO.IBrowserSettings; init(): void; } @@ -48,7 +49,7 @@ export function getSplitFactory(config: SplitIO.IBrowserSettings) { } // idempotent operation -export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IClientWithContext { +export function getSplitClient(factory: SplitIO.ISDK, key?: SplitIO.SplitKey): IClientWithContext { // factory.client is an idempotent operation const client = (key !== undefined ? factory.client(key) : factory.client()) as IClientWithContext; @@ -66,7 +67,7 @@ export function destroySplitFactory(factory: IFactoryWithClients): Promise // Util used to get client status. // It might be removed in the future, if the JS SDK extends its public API with a `getStatus` method -export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus { +export function getStatus(client?: SplitIO.IClient): ISplitStatus { const status = client && (client as IClientWithContext).__getStatus(); return { @@ -82,7 +83,7 @@ export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus { /** * Manage client attributes binding */ -export function initAttributes(client?: SplitIO.IBrowserClient, attributes?: SplitIO.Attributes) { +export function initAttributes(client?: SplitIO.IClient, attributes?: SplitIO.Attributes) { if (client && attributes) client.setAttributes(attributes); } @@ -176,7 +177,7 @@ function argsAreEqual(newArgs: any[], lastArgs: any[]): boolean { shallowEqual(newArgs[5], lastArgs[5]); // flagSets } -function evaluateFeatureFlags(client: SplitIO.IBrowserClient | undefined, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) { +function evaluateFeatureFlags(client: SplitIO.IClient | undefined, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) { if (names && flagSets) console.log(WARN_NAMES_AND_FLAGSETS); return client && (client as IClientWithContext).__getStatus().isOperational && (names || flagSets) ? From 465af415cd7926ed1fb084cde49eabe1d94d236f Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 25 Oct 2024 18:39:52 -0300 Subject: [PATCH 08/15] Fix import --- src/__tests__/testUtils/sdkConfigs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/testUtils/sdkConfigs.ts b/src/__tests__/testUtils/sdkConfigs.ts index a3421a5..1c20ef6 100644 --- a/src/__tests__/testUtils/sdkConfigs.ts +++ b/src/__tests__/testUtils/sdkConfigs.ts @@ -1,4 +1,4 @@ -import SplitIO from '@splitsoftware/splitio/types/splitio'; +import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; export const sdkBrowser: SplitIO.IBrowserSettings = { core: { From 2d1a0d01a972780dd010e102deae232157ab32c5 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Sat, 26 Oct 2024 11:27:51 -0300 Subject: [PATCH 09/15] test namespace implicit import --- src/__tests__/testUtils/mockSplitFactory.ts | 1 - src/__tests__/testUtils/sdkConfigs.ts | 2 -- src/__tests__/utils.test.ts | 1 - src/types.ts | 1 - src/utils.ts | 1 - 5 files changed, 6 deletions(-) diff --git a/src/__tests__/testUtils/mockSplitFactory.ts b/src/__tests__/testUtils/mockSplitFactory.ts index a399bcf..09dca47 100644 --- a/src/__tests__/testUtils/mockSplitFactory.ts +++ b/src/__tests__/testUtils/mockSplitFactory.ts @@ -1,5 +1,4 @@ import { EventEmitter } from 'events'; -import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import jsSdkPackageJson from '@splitsoftware/splitio/package.json'; import reactSdkPackageJson from '../../../package.json'; diff --git a/src/__tests__/testUtils/sdkConfigs.ts b/src/__tests__/testUtils/sdkConfigs.ts index 1c20ef6..0ec97f5 100644 --- a/src/__tests__/testUtils/sdkConfigs.ts +++ b/src/__tests__/testUtils/sdkConfigs.ts @@ -1,5 +1,3 @@ -import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; - export const sdkBrowser: SplitIO.IBrowserSettings = { core: { authorizationKey: 'sdk-key', diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index ce17a3c..3d7e32e 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,6 +1,5 @@ import { CONTROL_WITH_CONFIG } from '../constants'; import { getControlTreatmentsWithConfig } from '../utils'; -import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; describe('getControlTreatmentsWithConfig', () => { diff --git a/src/types.ts b/src/types.ts index 7183f65..730758f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,3 @@ -import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import type { ReactNode } from 'react'; /** diff --git a/src/utils.ts b/src/utils.ts index d7274bd..e22b5aa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,7 +3,6 @@ import shallowEqual from 'shallowequal'; import { SplitFactory } from '@splitsoftware/splitio/client'; import { CONTROL_WITH_CONFIG, VERSION, WARN_NAMES_AND_FLAGSETS } from './constants'; import { ISplitStatus } from './types'; -import SplitIO from '@splitsoftware/splitio-commons/types/splitio'; // Utils used to access singleton instances of Split factories and clients, and to gracefully shutdown all clients together. From dff8b1fd7ea209cc6185d5a081cd249dbe212c4e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Sat, 26 Oct 2024 13:55:50 -0300 Subject: [PATCH 10/15] Update changelog entry --- CHANGES.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2b23b9b..40f8762 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ 2.0.0 (October XX, 2024) - - Added support for targeting rules based on large segments for browsers. + - Added support for targeting rules based on large segments. + - Added support for passing factory instances to the `factory` prop of the `SplitFactoryProvider` component from other SDK packages that extends the `SplitIO.ISDK` interface, such as `@splitsoftware/splitio-react-native`, `@splitsoftware/splitio-browserjs` and `@splitsoftware/browser-suite` packages. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for EcmaScript Modules build. - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event (Related to https://github.com/splitio/react-client/issues/198). This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render. @@ -7,7 +8,7 @@ - Updated error handling: using the library modules without wrapping them in a `SplitFactoryProvider` component will now throw an error instead of logging it, as the modules requires the `SplitContext` to work properly. - Removed the `core.trafficType` configuration option and the `trafficType` parameter from the SDK `client()` method, `useSplitClient`, `useTrack`, and `SplitClient` component. This is because traffic types can no longer be bound to SDK clients in JavaScript SDK v11.0.0, and so the traffic type must be provided as first argument in the `track` method calls. - Removed deprecated modules: `SplitFactory` component, `useClient`, `useTreatments` and `useManager` hooks, and `withSplitFactory`, `withSplitClient` and `withSplitTreatments` high-order components. Refer to ./MIGRATION-GUIDE.md for instructions on how to migrate to the new alternatives. - - Renamed TypeScript interfaces `ISplitFactoryProps` to `ISplitFactoryProviderProps`, and `ISplitFactoryChildProps` to `ISplitFactoryProviderChildProps`. + - Renamed some TypeScript interfaces: `SplitIO.IBrowserSDK` to `SplitIO.ISDK`, `ISplitFactoryProps` to `ISplitFactoryProviderProps`, and `ISplitFactoryChildProps` to `ISplitFactoryProviderChildProps`. - Renamed `SplitSdk` to `SplitFactory` function, which is the underlying Split SDK factory, i.e., `import { SplitFactory } from '@splitsoftware/splitio'`. - Dropped support for React below 16.8.0, as the library components where rewritten using the React Hooks API available in React v16.8.0 and above. This refactor simplifies code maintenance and reduces bundle size. From 2c311b9254d25721ed15db31abea7400d68cea06 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 29 Oct 2024 14:25:46 -0300 Subject: [PATCH 11/15] Add TSDoc linter rules --- .eslintrc.js | 7 ++ package-lock.json | 229 ++++++++++++++++++++++++++++++++++++++-------- package.json | 3 +- 3 files changed, 200 insertions(+), 39 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9eba935..bbebf16 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { 'plugins': [ 'react', '@typescript-eslint', + 'eslint-plugin-tsdoc', 'import' ], 'rules': { @@ -63,5 +64,11 @@ module.exports = { 'import/no-self-import': 'error', 'import/no-default-export': 'error', } + }, { + // Enable TSDoc rules for TypeScript files, allowing the use of JSDoc in JS files. + 'files': ['**/*.ts', '**/*.tsx'], + 'rules': { + 'tsdoc/syntax': 'warn' + } }] } diff --git a/package-lock.json b/package-lock.json index 0d74ecb..8ea51aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.3", + "@splitsoftware/splitio": "11.0.0-rc.4", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -30,6 +30,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tsdoc": "^0.3.0", "husky": "^3.1.0", "jest": "^27.2.3", "react": "^18.0.0", @@ -1494,6 +1495,46 @@ "integrity": "sha512-h/luqw9oAmMF1C/GuUY/PAgZlF4wx71q2bdH+ct8vmjcvseCY32au8XmYy7xZ8l5VJiY/3ltFpr5YiO55v0mzg==", "dev": true }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1548,11 +1589,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "11.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.3.tgz", - "integrity": "sha512-7wPuhIPjAj7Sywwz1TNY3OpM3WuP07r+qKtBZg+ccU3PorQOzPhacvHQRWMu8VcrULc34LW4YR8wkyaIyCFB5g==", + "version": "11.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.4.tgz", + "integrity": "sha512-hQmeTf6tEWglbZwHvfdC4j0aSzGppfizcIKs+bOhXQf2Z52SVflO0cSuu6hxnLzDFtIbnDiOses12GhUceFBkg==", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0-rc.4", + "@splitsoftware/splitio-commons": "2.0.0-rc.5", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -1565,9 +1606,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.4.tgz", - "integrity": "sha512-z0rm4X9oh7LdIcHeEkh6Ca3JzfdhJ0IH3r7nZ6ghL0AXmPzbezoDswxjKOe/WYBbTW3utsgLqTTjr9Ww/b/hUw==", + "version": "2.0.0-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.5.tgz", + "integrity": "sha512-hNLA3cfVj5yGSHpOyTQVzcU2kIceJtJOdatcuue2ENOesjwDHfpvEy/YkIgLcLwXpvUxTFKpZd1BRej8gSbWoA==", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -4194,6 +4235,16 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz", + "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.15.0", + "@microsoft/tsdoc-config": "0.17.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -4869,10 +4920,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -5166,6 +5220,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -5506,12 +5572,15 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7841,6 +7910,12 @@ "node": ">=8" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9185,6 +9260,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -9198,12 +9282,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -11983,6 +12067,44 @@ "integrity": "sha512-h/luqw9oAmMF1C/GuUY/PAgZlF4wx71q2bdH+ct8vmjcvseCY32au8XmYy7xZ8l5VJiY/3ltFpr5YiO55v0mzg==", "dev": true }, + "@microsoft/tsdoc": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12028,11 +12150,11 @@ } }, "@splitsoftware/splitio": { - "version": "11.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.3.tgz", - "integrity": "sha512-7wPuhIPjAj7Sywwz1TNY3OpM3WuP07r+qKtBZg+ccU3PorQOzPhacvHQRWMu8VcrULc34LW4YR8wkyaIyCFB5g==", + "version": "11.0.0-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.4.tgz", + "integrity": "sha512-hQmeTf6tEWglbZwHvfdC4j0aSzGppfizcIKs+bOhXQf2Z52SVflO0cSuu6hxnLzDFtIbnDiOses12GhUceFBkg==", "requires": { - "@splitsoftware/splitio-commons": "2.0.0-rc.4", + "@splitsoftware/splitio-commons": "2.0.0-rc.5", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -12042,9 +12164,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.4.tgz", - "integrity": "sha512-z0rm4X9oh7LdIcHeEkh6Ca3JzfdhJ0IH3r7nZ6ghL0AXmPzbezoDswxjKOe/WYBbTW3utsgLqTTjr9Ww/b/hUw==", + "version": "2.0.0-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.5.tgz", + "integrity": "sha512-hNLA3cfVj5yGSHpOyTQVzcU2kIceJtJOdatcuue2ENOesjwDHfpvEy/YkIgLcLwXpvUxTFKpZd1BRej8gSbWoA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -14332,6 +14454,16 @@ "dev": true, "requires": {} }, + "eslint-plugin-tsdoc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.3.0.tgz", + "integrity": "sha512-0MuFdBrrJVBjT/gyhkP2BqpD0np1NxNLfQ38xXDlSs/KVVpKI2A6vN7jx2Rve/CyUsvOsMGwp9KKrinv7q9g3A==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.15.0", + "@microsoft/tsdoc-config": "0.17.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -14597,9 +14729,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "function.prototype.name": { @@ -14804,6 +14936,15 @@ "has-symbols": "^1.0.2" } }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -15044,12 +15185,12 @@ } }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.2" } }, "is-date-object": { @@ -16762,6 +16903,12 @@ } } }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -17809,6 +17956,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -17822,12 +17975,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } diff --git a/package.json b/package.json index a3ecf5b..d2c9bea 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ }, "homepage": "https://github.com/splitio/react-client#readme", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.3", + "@splitsoftware/splitio": "11.0.0-rc.4", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -84,6 +84,7 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tsdoc": "^0.3.0", "husky": "^3.1.0", "jest": "^27.2.3", "react": "^18.0.0", From d26572e7aa35484131e17e5499c01ea24d89e396 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 30 Oct 2024 13:18:15 -0300 Subject: [PATCH 12/15] Update changelog entry --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 40f8762..ce3ba6c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ - Added support for targeting rules based on large segments. - Added support for passing factory instances to the `factory` prop of the `SplitFactoryProvider` component from other SDK packages that extends the `SplitIO.ISDK` interface, such as `@splitsoftware/splitio-react-native`, `@splitsoftware/splitio-browserjs` and `@splitsoftware/browser-suite` packages. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. - - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for EcmaScript Modules build. + - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for ECMAScript Modules build. - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event (Related to https://github.com/splitio/react-client/issues/198). This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render. - BREAKING CHANGES: - Updated error handling: using the library modules without wrapping them in a `SplitFactoryProvider` component will now throw an error instead of logging it, as the modules requires the `SplitContext` to work properly. From eb475cd8fd4800c41d7074cfd7ebdada5bdf25ea Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 31 Oct 2024 14:22:56 -0300 Subject: [PATCH 13/15] Upgrade JS SDK --- .github/workflows/ci-cd.yml | 6 ++-- CHANGES.txt | 4 +-- package-lock.json | 34 ++++++++++----------- package.json | 4 +-- src/__tests__/SplitClient.test.tsx | 4 +-- src/__tests__/SplitFactoryProvider.test.tsx | 8 ++--- src/__tests__/SplitTreatments.test.tsx | 4 +-- src/__tests__/testUtils/utils.tsx | 2 +- src/types.ts | 6 ++-- src/useSplitClient.ts | 4 +-- src/useTrack.ts | 2 +- src/utils.ts | 26 ++++++++-------- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3870a1f..a281849 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -49,7 +49,7 @@ jobs: run: echo "VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_ENV - name: SonarQube Scan (Push) - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development') + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/refactor_type_definitions') uses: SonarSource/sonarcloud-github-action@v1.9 env: SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} @@ -77,7 +77,7 @@ jobs: -Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }} - name: Store assets - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development') + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/refactor_type_definitions') uses: actions/upload-artifact@v3 with: name: assets @@ -88,7 +88,7 @@ jobs: name: Upload assets runs-on: ubuntu-latest needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/development' + if: github.event_name == 'push' && github.ref == 'refs/heads/refactor_type_definitions' strategy: matrix: environment: diff --git a/CHANGES.txt b/CHANGES.txt index ce3ba6c..f4c99ca 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,6 @@ 2.0.0 (October XX, 2024) - Added support for targeting rules based on large segments. - - Added support for passing factory instances to the `factory` prop of the `SplitFactoryProvider` component from other SDK packages that extends the `SplitIO.ISDK` interface, such as `@splitsoftware/splitio-react-native`, `@splitsoftware/splitio-browserjs` and `@splitsoftware/browser-suite` packages. + - Added support for passing factory instances to the `factory` prop of the `SplitFactoryProvider` component from other SDK packages that extends the `SplitIO.IBrowserSDK` interface, such as `@splitsoftware/splitio-react-native`, `@splitsoftware/splitio-browserjs` and `@splitsoftware/browser-suite` packages. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for ECMAScript Modules build. - Bugfixing - When the `config` prop is provided, the `SplitFactoryProvider` now makes the SDK factory and client instances available in the context immediately during the initial render, instead of waiting for the first SDK event (Related to https://github.com/splitio/react-client/issues/198). This change fixes a bug in the `useTrack` hook, which was not retrieving the client's `track` method during the initial render. @@ -8,7 +8,7 @@ - Updated error handling: using the library modules without wrapping them in a `SplitFactoryProvider` component will now throw an error instead of logging it, as the modules requires the `SplitContext` to work properly. - Removed the `core.trafficType` configuration option and the `trafficType` parameter from the SDK `client()` method, `useSplitClient`, `useTrack`, and `SplitClient` component. This is because traffic types can no longer be bound to SDK clients in JavaScript SDK v11.0.0, and so the traffic type must be provided as first argument in the `track` method calls. - Removed deprecated modules: `SplitFactory` component, `useClient`, `useTreatments` and `useManager` hooks, and `withSplitFactory`, `withSplitClient` and `withSplitTreatments` high-order components. Refer to ./MIGRATION-GUIDE.md for instructions on how to migrate to the new alternatives. - - Renamed some TypeScript interfaces: `SplitIO.IBrowserSDK` to `SplitIO.ISDK`, `ISplitFactoryProps` to `ISplitFactoryProviderProps`, and `ISplitFactoryChildProps` to `ISplitFactoryProviderChildProps`. + - Renamed some TypeScript interfaces: `ISplitFactoryProps` to `ISplitFactoryProviderProps`, and `ISplitFactoryChildProps` to `ISplitFactoryProviderChildProps`. - Renamed `SplitSdk` to `SplitFactory` function, which is the underlying Split SDK factory, i.e., `import { SplitFactory } from '@splitsoftware/splitio'`. - Dropped support for React below 16.8.0, as the library components where rewritten using the React Hooks API available in React v16.8.0 and above. This refactor simplifies code maintenance and reduces bundle size. diff --git a/package-lock.json b/package-lock.json index 8ea51aa..daceee8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-react", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-react", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.4", + "@splitsoftware/splitio": "11.0.0-rc.5", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -1589,11 +1589,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "11.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.4.tgz", - "integrity": "sha512-hQmeTf6tEWglbZwHvfdC4j0aSzGppfizcIKs+bOhXQf2Z52SVflO0cSuu6hxnLzDFtIbnDiOses12GhUceFBkg==", + "version": "11.0.0-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.5.tgz", + "integrity": "sha512-n8nDEKtGir4sb5n0vYSoORnK+nFXYCYvGFH55aBNEpUhjMlqu9ocEPtdvtBeuz30yIb18eiiSFAr74WgnPfZHA==", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0-rc.5", + "@splitsoftware/splitio-commons": "2.0.0-rc.6", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -1606,9 +1606,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.5", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.5.tgz", - "integrity": "sha512-hNLA3cfVj5yGSHpOyTQVzcU2kIceJtJOdatcuue2ENOesjwDHfpvEy/YkIgLcLwXpvUxTFKpZd1BRej8gSbWoA==", + "version": "2.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.6.tgz", + "integrity": "sha512-A4Dk02ShJBjXqtPro6ylBAPc344Unnyk7YmEmvQqv1x4VUKloLw76PI7FgUgG7bM2UH079jSIeUg8HePW2PL1g==", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -12150,11 +12150,11 @@ } }, "@splitsoftware/splitio": { - "version": "11.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.4.tgz", - "integrity": "sha512-hQmeTf6tEWglbZwHvfdC4j0aSzGppfizcIKs+bOhXQf2Z52SVflO0cSuu6hxnLzDFtIbnDiOses12GhUceFBkg==", + "version": "11.0.0-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0-rc.5.tgz", + "integrity": "sha512-n8nDEKtGir4sb5n0vYSoORnK+nFXYCYvGFH55aBNEpUhjMlqu9ocEPtdvtBeuz30yIb18eiiSFAr74WgnPfZHA==", "requires": { - "@splitsoftware/splitio-commons": "2.0.0-rc.5", + "@splitsoftware/splitio-commons": "2.0.0-rc.6", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -12164,9 +12164,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.0.0-rc.5", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.5.tgz", - "integrity": "sha512-hNLA3cfVj5yGSHpOyTQVzcU2kIceJtJOdatcuue2ENOesjwDHfpvEy/YkIgLcLwXpvUxTFKpZd1BRej8gSbWoA==", + "version": "2.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0-rc.6.tgz", + "integrity": "sha512-A4Dk02ShJBjXqtPro6ylBAPc344Unnyk7YmEmvQqv1x4VUKloLw76PI7FgUgG7bM2UH079jSIeUg8HePW2PL1g==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index d2c9bea..2aae76b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-react", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "description": "A React library to easily integrate and use Split JS SDK", "main": "cjs/index.js", "module": "esm/index.js", @@ -63,7 +63,7 @@ }, "homepage": "https://github.com/splitio/react-client#readme", "dependencies": { - "@splitsoftware/splitio": "11.0.0-rc.4", + "@splitsoftware/splitio": "11.0.0-rc.5", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index d2bdade..74078d9 100644 --- a/src/__tests__/SplitClient.test.tsx +++ b/src/__tests__/SplitClient.test.tsx @@ -15,7 +15,7 @@ import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; import { INITIAL_STATUS, testAttributesBinding, TestComponentProps } from './testUtils/utils'; -import { IClientWithContext } from '../utils'; +import { IBrowserClientWithContext } from '../utils'; import { EXCEPTION_NO_SFP } from '../constants'; describe('SplitClient', () => { @@ -56,7 +56,7 @@ describe('SplitClient', () => { client: outerFactory.client(), isReady: true, isReadyFromCache: true, - lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate + lastUpdate: (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate }); return null; diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index def463b..cae3c84 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -15,7 +15,7 @@ import { ISplitFactoryProviderChildProps } from '../types'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; -import { __factories, IClientWithContext } from '../utils'; +import { __factories, IBrowserClientWithContext } from '../utils'; import { WARN_SF_CONFIG_AND_FACTORY } from '../constants'; import { INITIAL_STATUS } from './testUtils/utils'; @@ -52,9 +52,9 @@ describe('SplitFactoryProvider', () => { client: outerFactory.client(), isReady: true, isReadyFromCache: true, - lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate + lastUpdate: (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate }); - expect((childProps.factory as SplitIO.ISDK).settings.version).toBe(outerFactory.settings.version); + expect((childProps.factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version); return null; }} @@ -337,7 +337,7 @@ describe('SplitFactoryProvider', () => { test('cleans up on update and unmount if config prop is provided.', () => { let renderTimes = 0; - const createdFactories = new Set(); + const createdFactories = new Set(); const clientDestroySpies: jest.SpyInstance[] = []; const outerFactory = SplitFactory(sdkBrowser); diff --git a/src/__tests__/SplitTreatments.test.tsx b/src/__tests__/SplitTreatments.test.tsx index 9193b15..54d786b 100644 --- a/src/__tests__/SplitTreatments.test.tsx +++ b/src/__tests__/SplitTreatments.test.tsx @@ -8,7 +8,7 @@ jest.mock('@splitsoftware/splitio/client', () => { }); import { SplitFactory } from '@splitsoftware/splitio/client'; import { sdkBrowser } from './testUtils/sdkConfigs'; -import { getStatus, IClientWithContext } from '../utils'; +import { getStatus, IBrowserClientWithContext } from '../utils'; import { newSplitFactoryLocalhostInstance } from './testUtils/utils'; import { CONTROL_WITH_CONFIG, EXCEPTION_NO_SFP } from '../constants'; @@ -70,7 +70,7 @@ describe('SplitTreatments', () => { expect(clientMock.getTreatmentsWithConfig.mock.calls.length).toBe(1); expect(treatments).toBe(clientMock.getTreatmentsWithConfig.mock.results[0].value); expect(featureFlagNames).toBe(clientMock.getTreatmentsWithConfig.mock.calls[0][0]); - expect([isReady2, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate]).toStrictEqual([true, false, false, false, false, (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate]); + expect([isReady2, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate]).toStrictEqual([true, false, false, false, false, (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate]); return null; }} diff --git a/src/__tests__/testUtils/utils.tsx b/src/__tests__/testUtils/utils.tsx index 9c3171e..36a0c5f 100644 --- a/src/__tests__/testUtils/utils.tsx +++ b/src/__tests__/testUtils/utils.tsx @@ -8,7 +8,7 @@ export interface TestComponentProps { attributesClient: SplitIO.Attributes, splitKey: SplitIO.SplitKey, testSwitch: (done: jest.DoneCallback, splitKey?: SplitIO.SplitKey) => void, - factory: SplitIO.ISDK + factory: SplitIO.IBrowserSDK } export function newSplitFactoryLocalhostInstance() { diff --git a/src/types.ts b/src/types.ts index 730758f..b77f29f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,7 +49,7 @@ export interface ISplitContextValues extends ISplitStatus { * * NOTE: This property is not recommended for direct use, as better alternatives are available. */ - factory?: SplitIO.ISDK; + factory?: SplitIO.IBrowserSDK; /** * Split client instance. @@ -58,7 +58,7 @@ export interface ISplitContextValues extends ISplitStatus { * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#2-instantiate-the-sdk-and-create-a-new-split-client} */ - client?: SplitIO.IClient; + client?: SplitIO.IBrowserClient; } /** @@ -120,7 +120,7 @@ export interface ISplitFactoryProviderProps extends IUpdateProps { * * If both `factory` and `config` are provided, the `config` option is ignored. */ - factory?: SplitIO.ISDK; + factory?: SplitIO.IBrowserSDK; /** * An object of type Attributes used to evaluate the feature flags. diff --git a/src/useSplitClient.ts b/src/useSplitClient.ts index 2f75aef..8b1b66a 100644 --- a/src/useSplitClient.ts +++ b/src/useSplitClient.ts @@ -1,6 +1,6 @@ import React from 'react'; import { useSplitContext } from './SplitContext'; -import { getSplitClient, initAttributes, IClientWithContext, getStatus } from './utils'; +import { getSplitClient, initAttributes, IBrowserClientWithContext, getStatus } from './utils'; import { ISplitContextValues, IUseSplitClientOptions } from './types'; export const DEFAULT_UPDATE_OPTIONS = { @@ -31,7 +31,7 @@ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextV const context = useSplitContext(); const { client: contextClient, factory } = context; - let client = contextClient as IClientWithContext; + let client = contextClient as IBrowserClientWithContext; if (splitKey && factory) { // @TODO `getSplitClient` starts client sync. Move side effects to useEffect client = getSplitClient(factory, splitKey); diff --git a/src/useTrack.ts b/src/useTrack.ts index 965601b..4e7fc73 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -12,7 +12,7 @@ const noOpFalse = () => false; * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#track} */ -export function useTrack(splitKey?: SplitIO.SplitKey): SplitIO.IClient['track'] { +export function useTrack(splitKey?: SplitIO.SplitKey): SplitIO.IBrowserClient['track'] { // All update options are false to avoid re-renders. The track method doesn't need the client to be operational. const { client } = useSplitClient({ splitKey, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); return client ? client.track : noOpFalse; diff --git a/src/utils.ts b/src/utils.ts index e22b5aa..a43a2f8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -9,7 +9,7 @@ import { ISplitStatus } from './types'; /** * ClientWithContext interface. */ -export interface IClientWithContext extends SplitIO.IClient { +export interface IBrowserClientWithContext extends SplitIO.IBrowserClient { __getStatus(): { isReady: boolean; isReadyFromCache: boolean; @@ -24,13 +24,13 @@ export interface IClientWithContext extends SplitIO.IClient { /** * FactoryWithClientInstances interface. */ -export interface IFactoryWithClients extends SplitIO.ISDK { +export interface IFactoryWithLazyInit extends SplitIO.IBrowserSDK { config: SplitIO.IBrowserSettings; init(): void; } // exported for testing purposes -export const __factories: Map = new Map(); +export const __factories: Map = new Map(); // idempotent operation export function getSplitFactory(config: SplitIO.IBrowserSettings) { @@ -40,17 +40,17 @@ export function getSplitFactory(config: SplitIO.IBrowserSettings) { const newFactory = SplitFactory(config, (modules) => { modules.settings.version = VERSION; modules.lazyInit = true; - }) as IFactoryWithClients; + }) as IFactoryWithLazyInit; newFactory.config = config; __factories.set(config, newFactory); } - return __factories.get(config) as IFactoryWithClients; + return __factories.get(config) as IFactoryWithLazyInit; } // idempotent operation -export function getSplitClient(factory: SplitIO.ISDK, key?: SplitIO.SplitKey): IClientWithContext { +export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IBrowserClientWithContext { // factory.client is an idempotent operation - const client = (key !== undefined ? factory.client(key) : factory.client()) as IClientWithContext; + const client = (key !== undefined ? factory.client(key) : factory.client()) as IBrowserClientWithContext; // Remove EventEmitter warning emitted when using multiple SDK hooks or components. // Unlike JS SDK, users don't need to access the client directly, making the warning irrelevant. @@ -59,15 +59,15 @@ export function getSplitClient(factory: SplitIO.ISDK, key?: SplitIO.SplitKey): I return client; } -export function destroySplitFactory(factory: IFactoryWithClients): Promise | undefined { +export function destroySplitFactory(factory: IFactoryWithLazyInit): Promise | undefined { __factories.delete(factory.config); return factory.destroy(); } // Util used to get client status. // It might be removed in the future, if the JS SDK extends its public API with a `getStatus` method -export function getStatus(client?: SplitIO.IClient): ISplitStatus { - const status = client && (client as IClientWithContext).__getStatus(); +export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus { + const status = client && (client as IBrowserClientWithContext).__getStatus(); return { isReady: status ? status.isReady : false, @@ -82,7 +82,7 @@ export function getStatus(client?: SplitIO.IClient): ISplitStatus { /** * Manage client attributes binding */ -export function initAttributes(client?: SplitIO.IClient, attributes?: SplitIO.Attributes) { +export function initAttributes(client?: SplitIO.IBrowserClient, attributes?: SplitIO.Attributes) { if (client && attributes) client.setAttributes(attributes); } @@ -176,10 +176,10 @@ function argsAreEqual(newArgs: any[], lastArgs: any[]): boolean { shallowEqual(newArgs[5], lastArgs[5]); // flagSets } -function evaluateFeatureFlags(client: SplitIO.IClient | undefined, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) { +function evaluateFeatureFlags(client: SplitIO.IBrowserClient | undefined, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) { if (names && flagSets) console.log(WARN_NAMES_AND_FLAGSETS); - return client && (client as IClientWithContext).__getStatus().isOperational && (names || flagSets) ? + return client && (client as IBrowserClientWithContext).__getStatus().isOperational && (names || flagSets) ? names ? client.getTreatmentsWithConfig(names, attributes) : client.getTreatmentsWithConfigByFlagSets(flagSets!, attributes) : From 54b13cae28aa1c0b2ebafad34f7b7be37581abe8 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 31 Oct 2024 14:27:50 -0300 Subject: [PATCH 14/15] Polishing --- CHANGES.txt | 2 +- src/__tests__/SplitClient.test.tsx | 4 ++-- src/__tests__/SplitFactoryProvider.test.tsx | 4 ++-- src/__tests__/SplitTreatments.test.tsx | 4 ++-- src/useSplitClient.ts | 4 ++-- src/utils.ts | 10 +++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f4c99ca..03167d5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -2.0.0 (October XX, 2024) +2.0.0 (November XX, 2024) - Added support for targeting rules based on large segments. - Added support for passing factory instances to the `factory` prop of the `SplitFactoryProvider` component from other SDK packages that extends the `SplitIO.IBrowserSDK` interface, such as `@splitsoftware/splitio-react-native`, `@splitsoftware/splitio-browserjs` and `@splitsoftware/browser-suite` packages. - Updated @splitsoftware/splitio package to version 11.0.0 that includes major updates, and updated some transitive dependencies for vulnerability fixes. diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index 74078d9..d2bdade 100644 --- a/src/__tests__/SplitClient.test.tsx +++ b/src/__tests__/SplitClient.test.tsx @@ -15,7 +15,7 @@ import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; import { INITIAL_STATUS, testAttributesBinding, TestComponentProps } from './testUtils/utils'; -import { IBrowserClientWithContext } from '../utils'; +import { IClientWithContext } from '../utils'; import { EXCEPTION_NO_SFP } from '../constants'; describe('SplitClient', () => { @@ -56,7 +56,7 @@ describe('SplitClient', () => { client: outerFactory.client(), isReady: true, isReadyFromCache: true, - lastUpdate: (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate + lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate }); return null; diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index cae3c84..f212393 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -15,7 +15,7 @@ import { ISplitFactoryProviderChildProps } from '../types'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; -import { __factories, IBrowserClientWithContext } from '../utils'; +import { __factories, IClientWithContext } from '../utils'; import { WARN_SF_CONFIG_AND_FACTORY } from '../constants'; import { INITIAL_STATUS } from './testUtils/utils'; @@ -52,7 +52,7 @@ describe('SplitFactoryProvider', () => { client: outerFactory.client(), isReady: true, isReadyFromCache: true, - lastUpdate: (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate + lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate }); expect((childProps.factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version); return null; diff --git a/src/__tests__/SplitTreatments.test.tsx b/src/__tests__/SplitTreatments.test.tsx index 54d786b..9193b15 100644 --- a/src/__tests__/SplitTreatments.test.tsx +++ b/src/__tests__/SplitTreatments.test.tsx @@ -8,7 +8,7 @@ jest.mock('@splitsoftware/splitio/client', () => { }); import { SplitFactory } from '@splitsoftware/splitio/client'; import { sdkBrowser } from './testUtils/sdkConfigs'; -import { getStatus, IBrowserClientWithContext } from '../utils'; +import { getStatus, IClientWithContext } from '../utils'; import { newSplitFactoryLocalhostInstance } from './testUtils/utils'; import { CONTROL_WITH_CONFIG, EXCEPTION_NO_SFP } from '../constants'; @@ -70,7 +70,7 @@ describe('SplitTreatments', () => { expect(clientMock.getTreatmentsWithConfig.mock.calls.length).toBe(1); expect(treatments).toBe(clientMock.getTreatmentsWithConfig.mock.results[0].value); expect(featureFlagNames).toBe(clientMock.getTreatmentsWithConfig.mock.calls[0][0]); - expect([isReady2, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate]).toStrictEqual([true, false, false, false, false, (outerFactory.client() as IBrowserClientWithContext).__getStatus().lastUpdate]); + expect([isReady2, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate]).toStrictEqual([true, false, false, false, false, (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate]); return null; }} diff --git a/src/useSplitClient.ts b/src/useSplitClient.ts index 8b1b66a..2f75aef 100644 --- a/src/useSplitClient.ts +++ b/src/useSplitClient.ts @@ -1,6 +1,6 @@ import React from 'react'; import { useSplitContext } from './SplitContext'; -import { getSplitClient, initAttributes, IBrowserClientWithContext, getStatus } from './utils'; +import { getSplitClient, initAttributes, IClientWithContext, getStatus } from './utils'; import { ISplitContextValues, IUseSplitClientOptions } from './types'; export const DEFAULT_UPDATE_OPTIONS = { @@ -31,7 +31,7 @@ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextV const context = useSplitContext(); const { client: contextClient, factory } = context; - let client = contextClient as IBrowserClientWithContext; + let client = contextClient as IClientWithContext; if (splitKey && factory) { // @TODO `getSplitClient` starts client sync. Move side effects to useEffect client = getSplitClient(factory, splitKey); diff --git a/src/utils.ts b/src/utils.ts index a43a2f8..06495e5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -9,7 +9,7 @@ import { ISplitStatus } from './types'; /** * ClientWithContext interface. */ -export interface IBrowserClientWithContext extends SplitIO.IBrowserClient { +export interface IClientWithContext extends SplitIO.IBrowserClient { __getStatus(): { isReady: boolean; isReadyFromCache: boolean; @@ -48,9 +48,9 @@ export function getSplitFactory(config: SplitIO.IBrowserSettings) { } // idempotent operation -export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IBrowserClientWithContext { +export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IClientWithContext { // factory.client is an idempotent operation - const client = (key !== undefined ? factory.client(key) : factory.client()) as IBrowserClientWithContext; + const client = (key !== undefined ? factory.client(key) : factory.client()) as IClientWithContext; // Remove EventEmitter warning emitted when using multiple SDK hooks or components. // Unlike JS SDK, users don't need to access the client directly, making the warning irrelevant. @@ -67,7 +67,7 @@ export function destroySplitFactory(factory: IFactoryWithLazyInit): Promise Date: Thu, 31 Oct 2024 14:32:51 -0300 Subject: [PATCH 15/15] rollback ci-cd --- .github/workflows/ci-cd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a281849..3870a1f 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -49,7 +49,7 @@ jobs: run: echo "VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_ENV - name: SonarQube Scan (Push) - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/refactor_type_definitions') + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development') uses: SonarSource/sonarcloud-github-action@v1.9 env: SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} @@ -77,7 +77,7 @@ jobs: -Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }} - name: Store assets - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/refactor_type_definitions') + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development') uses: actions/upload-artifact@v3 with: name: assets @@ -88,7 +88,7 @@ jobs: name: Upload assets runs-on: ubuntu-latest needs: build - if: github.event_name == 'push' && github.ref == 'refs/heads/refactor_type_definitions' + if: github.event_name == 'push' && github.ref == 'refs/heads/development' strategy: matrix: environment: