From e8e0d2aabdcd0761ff5ddec53e6917dbc5e999ae Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 21 Oct 2024 14:17:56 -0300 Subject: [PATCH 01/16] Refactor context value to use undefined rather than null for factory and client properties --- src/SplitFactoryProvider.tsx | 6 +-- src/__tests__/SplitClient.test.tsx | 43 ++++++++++----------- src/__tests__/SplitFactoryProvider.test.tsx | 38 ++++++++---------- src/__tests__/testUtils/utils.tsx | 4 +- src/types.ts | 4 +- src/useSplitManager.ts | 6 +-- src/utils.ts | 6 +-- 7 files changed, 49 insertions(+), 58 deletions(-) diff --git a/src/SplitFactoryProvider.tsx b/src/SplitFactoryProvider.tsx index 9e370d1..1046d48 100644 --- a/src/SplitFactoryProvider.tsx +++ b/src/SplitFactoryProvider.tsx @@ -28,11 +28,11 @@ export function SplitFactoryProvider(props: ISplitFactoryProviderProps) { config = undefined; } - const [configFactory, setConfigFactory] = React.useState(null); + const [configFactory, setConfigFactory] = React.useState(); const factory = React.useMemo(() => { - return propFactory || (configFactory && config === configFactory.config ? configFactory : null); + return propFactory || (configFactory && config === configFactory.config ? configFactory : undefined); }, [config, propFactory, configFactory]); - const client = factory ? getSplitClient(factory) : null; + const client = factory ? getSplitClient(factory) : undefined; // Effect to initialize and destroy the factory React.useEffect(() => { diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index 94b95cc..9675710 100644 --- a/src/__tests__/SplitClient.test.tsx +++ b/src/__tests__/SplitClient.test.tsx @@ -14,7 +14,7 @@ import { ISplitClientChildProps } from '../types'; import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitClient } from '../SplitClient'; import { SplitContext } from '../SplitContext'; -import { testAttributesBinding, TestComponentProps } from './testUtils/utils'; +import { INITIAL_CONTEXT, testAttributesBinding, TestComponentProps } from './testUtils/utils'; import { IClientWithContext } from '../utils'; import { EXCEPTION_NO_SFP } from '../constants'; @@ -24,13 +24,8 @@ describe('SplitClient', () => { render( - {({ isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => { - expect(isReady).toBe(false); - expect(isReadyFromCache).toBe(false); - expect(hasTimedout).toBe(false); - expect(isTimedout).toBe(false); - expect(isDestroyed).toBe(false); - expect(lastUpdate).toBe(0); + {(childProps: ISplitClientChildProps) => { + expect(childProps).toEqual(INITIAL_CONTEXT); return null; }} @@ -50,14 +45,15 @@ describe('SplitClient', () => { {/* Equivalent to */} - {({ client, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => { - expect(client).toBe(outerFactory.client()); - expect(isReady).toBe(true); - expect(isReadyFromCache).toBe(true); - expect(hasTimedout).toBe(false); - expect(isTimedout).toBe(false); - expect(isDestroyed).toBe(false); - expect(lastUpdate).toBe((outerFactory.client() as IClientWithContext).__getStatus().lastUpdate); + {(childProps: ISplitClientChildProps) => { + expect(childProps).toEqual({ + ...INITIAL_CONTEXT, + factory : outerFactory, + client: outerFactory.client(), + isReady: true, + isReadyFromCache: true, + lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate + }); return null; }} @@ -162,7 +158,7 @@ describe('SplitClient', () => { expect(renderTimes).toBe(3); }); - test('rerender child only on SDK_READY event, as default behaviour.', async () => { + test('rerender child only on SDK_READY event, as default behavior.', async () => { const outerFactory = SplitFactory(sdkBrowser); (outerFactory as any).client().__emitter__.emit(Event.SDK_READY); (outerFactory.manager().names as jest.Mock).mockReturnValue(['split1']); @@ -228,18 +224,19 @@ describe('SplitClient', () => { expect(count).toEqual(2); }); - test('renders a passed JSX.Element with a new SplitContext value.', (done) => { + test('renders a passed JSX.Element with a new SplitContext value.', () => { const outerFactory = SplitFactory(sdkBrowser); const Component = () => { return ( {(value) => { - expect(value.client).toBe(outerFactory.client('user2')); - expect(value.isReady).toBe(false); - expect(value.isTimedout).toBe(false); - expect(value.lastUpdate).toBe(0); - done(); + expect(value).toEqual({ + ...INITIAL_CONTEXT, + factory: outerFactory, + client: outerFactory.client('user2'), + }); + return null; }} diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index 5efddd4..f2c5039 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -24,15 +24,8 @@ describe('SplitFactoryProvider', () => { test('passes no-ready props to the child if initialized with a config.', () => { render( - {({ factory, client, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitFactoryProviderChildProps) => { - expect(factory).toBe(null); - expect(client).toBe(null); - expect(isReady).toBe(false); - expect(isReadyFromCache).toBe(false); - expect(hasTimedout).toBe(false); - expect(isTimedout).toBe(false); - expect(isDestroyed).toBe(false); - expect(lastUpdate).toBe(0); + {(childProps: ISplitFactoryProviderChildProps) => { + expect(childProps).toEqual(INITIAL_CONTEXT); return null; }} @@ -48,15 +41,16 @@ describe('SplitFactoryProvider', () => { render( - {({ factory, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitFactoryProviderChildProps) => { - expect(factory).toBe(outerFactory); - expect(isReady).toBe(true); - expect(isReadyFromCache).toBe(true); - expect(hasTimedout).toBe(false); - expect(isTimedout).toBe(false); - expect(isDestroyed).toBe(false); - expect(lastUpdate).toBe((outerFactory.client() as IClientWithContext).__getStatus().lastUpdate); - expect((factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version); + {(childProps: ISplitFactoryProviderChildProps) => { + expect(childProps).toEqual({ + ...INITIAL_CONTEXT, + factory: outerFactory, + client: outerFactory.client(), + isReady: true, + isReadyFromCache: true, + lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate + }); + expect((childProps.factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version); return null; }} @@ -229,7 +223,7 @@ describe('SplitFactoryProvider', () => { expect(renderTimes).toBe(3); }); - test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behaviour (config prop)', async () => { + test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behavior (config prop)', async () => { let renderTimes = 0; let previousLastUpdate = -1; @@ -263,7 +257,7 @@ describe('SplitFactoryProvider', () => { expect(renderTimes).toBe(2); }); - test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behaviour (factory prop)', async () => { + test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behavior (factory prop)', async () => { const outerFactory = SplitFactory(sdkBrowser); let renderTimes = 0; let previousLastUpdate = -1; @@ -350,14 +344,14 @@ describe('SplitFactoryProvider', () => { case 5: expect(isReady).toBe(false); expect(hasTimedout).toBe(false); - expect(factory).toBe(null); + expect(factory).toBe(undefined); return null; case 3: case 4: case 6: expect(isReady).toBe(true); expect(hasTimedout).toBe(true); - expect(factory).not.toBe(null); + expect(factory).not.toBe(undefined); createdFactories.add(factory!); clientDestroySpies.push(jest.spyOn(factory!.client(), 'destroy')); return ( diff --git a/src/__tests__/testUtils/utils.tsx b/src/__tests__/testUtils/utils.tsx index d19143a..6324bde 100644 --- a/src/__tests__/testUtils/utils.tsx +++ b/src/__tests__/testUtils/utils.tsx @@ -117,8 +117,8 @@ export function testAttributesBinding(Component: React.FunctionComponent // 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 | null): ISplitStatus { +export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus { const status = client && (client as IClientWithContext).__getStatus(); return { @@ -80,7 +80,7 @@ export function getStatus(client: SplitIO.IBrowserClient | null): ISplitStatus { /** * Manage client attributes binding */ -export function initAttributes(client: SplitIO.IBrowserClient | null, attributes?: SplitIO.Attributes) { +export function initAttributes(client?: SplitIO.IBrowserClient, attributes?: SplitIO.Attributes) { if (client && attributes) client.setAttributes(attributes); } @@ -174,7 +174,7 @@ function argsAreEqual(newArgs: any[], lastArgs: any[]): boolean { shallowEqual(newArgs[5], lastArgs[5]); // flagSets } -function evaluateFeatureFlags(client: SplitIO.IBrowserClient | null, _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) ? From f23f0a42c2d7eaec5b3839b29383430f599b6ba2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 22 Oct 2024 14:49:01 -0300 Subject: [PATCH 02/16] Upgrade JS SDK to v11, which has clients without bound traffic type --- package-lock.json | 47 ++++++++------------- package.json | 2 +- src/SplitClient.tsx | 2 +- src/__tests__/SplitClient.test.tsx | 2 +- src/__tests__/testUtils/mockSplitFactory.ts | 11 +++-- src/__tests__/useSplitClient.test.tsx | 16 +++---- src/__tests__/useTrack.test.tsx | 15 +++---- src/types.ts | 6 --- src/useSplitClient.ts | 6 +-- src/useTrack.ts | 4 +- src/utils.ts | 6 +-- 11 files changed, 48 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index 063dd91..e8f6e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.13.1-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "10.28.1-rc.2", + "@splitsoftware/splitio": "11.0.0-rc.1", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -45,7 +45,7 @@ "webpack-merge": "^5.8.0" }, "peerDependencies": { - "react": ">=16.3.0" + "react": ">=16.8.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1548,12 +1548,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "10.28.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.28.1-rc.2.tgz", - "integrity": "sha512-UwRlu3aBY/e2cDQUxDXZCnLisleOCSUgCQSIN8gGdAKO9QQHG1Vt2cxsxMIGRIIBWUIuuUJ2phVKHM/0M2ZPhw==", + "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==", "dependencies": { - "@splitsoftware/splitio-commons": "1.17.1-rc.1", - "@types/google.analytics": "0.0.40", + "@splitsoftware/splitio-commons": "2.0.0-rc.2", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", @@ -1563,14 +1562,13 @@ "unfetch": "^4.2.0" }, "engines": { - "node": ">=6", - "npm": ">=3" + "node": ">=14.0.0" } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.17.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.1.tgz", - "integrity": "sha512-mmDcWW2iyqQF/FzLgPoRA3KXpvswk/sDIhQGWTg3WPkapnA+e4WXb+U/TSGGB/Ig88NlM76FlxMDkrHnBayDXg==", + "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==", "dependencies": { "tslib": "^2.3.1" }, @@ -1847,11 +1845,6 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, - "node_modules/@types/google.analytics": { - "version": "0.0.40", - "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", - "integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==" - }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -12035,12 +12028,11 @@ } }, "@splitsoftware/splitio": { - "version": "10.28.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.28.1-rc.2.tgz", - "integrity": "sha512-UwRlu3aBY/e2cDQUxDXZCnLisleOCSUgCQSIN8gGdAKO9QQHG1Vt2cxsxMIGRIIBWUIuuUJ2phVKHM/0M2ZPhw==", + "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==", "requires": { - "@splitsoftware/splitio-commons": "1.17.1-rc.1", - "@types/google.analytics": "0.0.40", + "@splitsoftware/splitio-commons": "2.0.0-rc.2", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", @@ -12051,9 +12043,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "1.17.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.1.tgz", - "integrity": "sha512-mmDcWW2iyqQF/FzLgPoRA3KXpvswk/sDIhQGWTg3WPkapnA+e4WXb+U/TSGGB/Ig88NlM76FlxMDkrHnBayDXg==", + "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==", "requires": { "tslib": "^2.3.1" } @@ -12269,11 +12261,6 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, - "@types/google.analytics": { - "version": "0.0.40", - "resolved": "https://registry.npmjs.org/@types/google.analytics/-/google.analytics-0.0.40.tgz", - "integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==" - }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", diff --git a/package.json b/package.json index e2a7661..d41e870 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ }, "homepage": "https://github.com/splitio/react-client#readme", "dependencies": { - "@splitsoftware/splitio": "10.28.1-rc.2", + "@splitsoftware/splitio": "11.0.0-rc.1", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" diff --git a/src/SplitClient.tsx b/src/SplitClient.tsx index 7f5ebbb..ea42e95 100644 --- a/src/SplitClient.tsx +++ b/src/SplitClient.tsx @@ -8,7 +8,7 @@ import { useSplitClient } from './useSplitClient'; * Children components will have access to the new client when accessing Split Context. * * The underlying SDK client can be changed during the component lifecycle - * if the component is updated with a different splitKey or trafficType prop. + * if the component is updated with a different splitKey prop. * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#advanced-instantiate-multiple-sdk-clients} */ diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index 9675710..1ba033f 100644 --- a/src/__tests__/SplitClient.test.tsx +++ b/src/__tests__/SplitClient.test.tsx @@ -344,7 +344,7 @@ describe('SplitClient', () => { function Component({ attributesFactory, attributesClient, splitKey, testSwitch, factory }: TestComponentProps) { return ( - + {() => { testSwitch(done, splitKey); return null; diff --git a/src/__tests__/testUtils/mockSplitFactory.ts b/src/__tests__/testUtils/mockSplitFactory.ts index db5fffe..379b747 100644 --- a/src/__tests__/testUtils/mockSplitFactory.ts +++ b/src/__tests__/testUtils/mockSplitFactory.ts @@ -26,7 +26,7 @@ function parseKey(key: SplitIO.SplitKey): SplitIO.SplitKey { }; } } -function buildInstanceId(key: any, trafficType: string | undefined) { +function buildInstanceId(key: any, trafficType?: string) { return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType !== undefined ? trafficType : ''}`; } @@ -34,7 +34,7 @@ export function mockSdk() { return jest.fn((config: SplitIO.IBrowserSettings, __updateModules) => { - function mockClient(_key: SplitIO.SplitKey, _trafficType?: string) { + function mockClient(_key: SplitIO.SplitKey) { // Readiness let isReady = false; let isReadyFromCache = false; @@ -135,11 +135,10 @@ export function mockSdk() { // Cache of clients const __clients__: { [instanceId: string]: any } = {}; - const client = jest.fn((key?: string, trafficType?: string) => { + const client = jest.fn((key?: string) => { const clientKey = key || parseKey(config.core.key); - const clientTT = trafficType || config.core.trafficType; - const instanceId = buildInstanceId(clientKey, clientTT); - return __clients__[instanceId] || (__clients__[instanceId] = mockClient(clientKey, clientTT)); + const instanceId = buildInstanceId(clientKey); + return __clients__[instanceId] || (__clients__[instanceId] = mockClient(clientKey)); }); // Factory destroy diff --git a/src/__tests__/useSplitClient.test.tsx b/src/__tests__/useSplitClient.test.tsx index de852a8..400dad3 100644 --- a/src/__tests__/useSplitClient.test.tsx +++ b/src/__tests__/useSplitClient.test.tsx @@ -56,12 +56,12 @@ describe('useSplitClient', () => { {React.createElement(() => { (outerFactory.client as jest.Mock).mockClear(); - client = useSplitClient({ splitKey: 'user2', trafficType: 'user' }).client; + client = useSplitClient({ splitKey: 'user2' }).client; return null; })} ); - expect(outerFactory.client as jest.Mock).toBeCalledWith('user2', 'user'); + expect(outerFactory.client as jest.Mock).toBeCalledWith('user2'); expect(outerFactory.client as jest.Mock).toHaveReturnedWith(client); }); @@ -70,7 +70,7 @@ describe('useSplitClient', () => { render( React.createElement(() => { useSplitClient(); - useSplitClient({ splitKey: 'user2', trafficType: 'user' }); + useSplitClient({ splitKey: 'user2' }); return null; }) ); @@ -81,7 +81,7 @@ describe('useSplitClient', () => { // eslint-disable-next-line react/prop-types const InnerComponent = ({ splitKey, attributesClient, testSwitch }) => { - useSplitClient({ splitKey, trafficType: 'user', attributes: attributesClient }); + useSplitClient({ splitKey, attributes: attributesClient }); testSwitch(done, splitKey); return null; }; @@ -112,7 +112,7 @@ describe('useSplitClient', () => { {() => countSplitContext++} - @@ -120,8 +120,8 @@ describe('useSplitClient', () => { {React.createElement(() => { // Equivalent to - // - Using config key and traffic type: `const { client } = useSplitClient(sdkBrowser.core.key, sdkBrowser.core.trafficType, { att1: 'att1' });` - // - Disabling update props, since the wrapping SplitFactoryProvider has them enabled: `const { client } = useSplitClient(undefined, undefined, { att1: 'att1' }, { updateOnSdkReady: false, updateOnSdkReadyFromCache: false });` + // - Using config key: `const { client } = useSplitClient({ splitKey: sdkBrowser.core.key, attributes: { att1: 'att1' } });` + // - Disabling update props, since the wrapping SplitFactoryProvider has them enabled: `const { client } = useSplitClient({ attributes: { att1: 'att1' }, updateOnSdkReady: false, updateOnSdkReadyFromCache: false });` const { client } = useSplitClient({ attributes: { att1: 'att1' } }); expect(client).toBe(mainClient); // Assert that the main client was retrieved. expect(client!.getAttributes()).toEqual({ att1: 'att1' }); // Assert that the client was retrieved with the provided attributes. @@ -141,7 +141,7 @@ describe('useSplitClient', () => { {() => { countSplitClientWithUpdate++; return null }} {React.createElement(() => { - useSplitClient({ splitKey: sdkBrowser.core.key, trafficType: sdkBrowser.core.trafficType, updateOnSdkUpdate: true }).client; + useSplitClient({ splitKey: sdkBrowser.core.key, updateOnSdkUpdate: true }).client; countUseSplitClientWithUpdate++; return null; })} diff --git a/src/__tests__/useTrack.test.tsx b/src/__tests__/useTrack.test.tsx index e4fd2ad..33706c1 100644 --- a/src/__tests__/useTrack.test.tsx +++ b/src/__tests__/useTrack.test.tsx @@ -62,22 +62,21 @@ describe('useTrack', () => { expect(track).toHaveReturnedWith(trackResult); }); - test('returns the track method bound to a new client given a splitKey and optional trafficType.', () => { + test('returns the track method bound to a new client given a splitKey.', () => { const outerFactory = SplitFactory(sdkBrowser); - let boundTrack; let trackResult; render( {React.createElement(() => { - boundTrack = useTrack('user2', tt); - trackResult = boundTrack(eventType, value, properties); + const boundTrack = useTrack('user2'); + trackResult = boundTrack(tt, eventType, value, properties); return null; })} , ); - const track = outerFactory.client('user2', tt).track as jest.Mock; - expect(track).toBeCalledWith(eventType, value, properties); + const track = outerFactory.client('user2').track as jest.Mock; + expect(track).toBeCalledWith(tt, eventType, value, properties); expect(track).toHaveReturnedWith(trackResult); }); @@ -85,8 +84,8 @@ describe('useTrack', () => { expect(() => { render( React.createElement(() => { - const track = useTrack('user2', tt); - track(eventType, value, properties); + const track = useTrack('user2'); + track(tt, eventType, value, properties); return null; }), ); diff --git a/src/types.ts b/src/types.ts index 42db265..dc59bb9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -145,12 +145,6 @@ export interface IUseSplitClientOptions extends IUpdateProps { */ splitKey?: SplitIO.SplitKey; - /** - * Traffic type associated with the customer identifier. - * If no provided here or at the config object, it will be required on the client.track() calls. - */ - trafficType?: string; - /** * An object of type Attributes used to evaluate the feature flags. */ diff --git a/src/useSplitClient.ts b/src/useSplitClient.ts index 94cfb93..2f75aef 100644 --- a/src/useSplitClient.ts +++ b/src/useSplitClient.ts @@ -11,7 +11,7 @@ export const DEFAULT_UPDATE_OPTIONS = { }; /** - * 'useSplitClient' is a hook that returns an Split Context object with the client and its status corresponding to the provided key and trafficType. + * 'useSplitClient' is a hook that returns an Split Context object with the client and its status corresponding to the provided key. * It uses the 'useContext' hook to access the context, which is updated by SplitFactoryProvider and SplitClient components in the hierarchy of components. * * @returns A Split Context object @@ -25,7 +25,7 @@ export const DEFAULT_UPDATE_OPTIONS = { */ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextValues { const { - updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate, splitKey, trafficType, attributes + updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate, splitKey, attributes } = { ...DEFAULT_UPDATE_OPTIONS, ...options }; const context = useSplitContext(); @@ -34,7 +34,7 @@ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextV let client = contextClient as IClientWithContext; if (splitKey && factory) { // @TODO `getSplitClient` starts client sync. Move side effects to useEffect - client = getSplitClient(factory, splitKey, trafficType); + client = getSplitClient(factory, splitKey); } initAttributes(client, attributes); diff --git a/src/useTrack.ts b/src/useTrack.ts index b511018..d9394ef 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -11,8 +11,8 @@ const noOpFalse = () => false; * * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#track} */ -export function useTrack(splitKey?: SplitIO.SplitKey, trafficType?: string): SplitIO.IBrowserClient['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, trafficType, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); + const { client } = useSplitClient({ splitKey, updateOnSdkReady: false, updateOnSdkReadyFromCache: false }); return client ? client.track.bind(client) : noOpFalse; } diff --git a/src/utils.ts b/src/utils.ts index 96e9ab7..a4023c0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -46,13 +46,13 @@ export function getSplitFactory(config: SplitIO.IBrowserSettings) { } // idempotent operation -export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey, trafficType?: string): IClientWithContext { +export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IClientWithContext { // factory.client is an idempotent operation - const client = (key !== undefined ? factory.client(key, trafficType) : factory.client()) as IClientWithContext; + 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. - client.setMaxListeners(0); + client.setMaxListeners && client.setMaxListeners(0); return client; } From 4688ff0388fb16583195dec91e9bea968fe50a0d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 22 Oct 2024 15:51:14 -0300 Subject: [PATCH 03/16] Update changelog entry --- CHANGES.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index b800f1f..000f2c0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,9 +1,10 @@ 2.0.0 (October XX, 2024) - Added support for targeting rules based on large segments for browsers. - - Updated @splitsoftware/splitio package to version 10.29.0 that includes minor updates, and updated some transitive dependencies for vulnerability fixes. + - 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. - 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. - 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 `SplitSdk` to `SplitFactory` function, which is the underlying Split SDK factory, i.e., `import { SplitFactory } from '@splitsoftware/splitio'`. From 628b1855e72dc9f4dcafc35d6d9933e9d19cc090 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Oct 2024 19:10:07 -0300 Subject: [PATCH 04/16] 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 05/16] 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 06/16] 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 07/16] 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 bafcfd9998fcb1d067d6106bb559d06270c74cc2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 25 Oct 2024 18:34:52 -0300 Subject: [PATCH 08/16] 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 09/16] 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 10/16] 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 11/16] 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 12/16] 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 13/16] 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 14/16] 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 15/16] 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 16/16] 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: