diff --git a/CHANGES.txt b/CHANGES.txt index 4e2cadd..4c02a5a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ -2.0.1 (November XX, 2024) +2.0.1 (December 4, 2024) - Updated internal handling of the `updateOnSdkTimedout` param to remove the wrong log "[ERROR] A listener was added for SDK_READY_TIMED_OUT on the SDK, which has already fired and won't be emitted again". + - Updated implementation of `SplitFactoryProvider` component to support React Strict Mode (Related to https://github.com/splitio/react-client/issues/221). - Bugfixing - Fixed an issue with the `updateOn***` object parameters of the `useSplitClient` and `useSplitTreatments` hooks, and their components and HOCs alternatives, which were not defaulting to `true` when a non-boolean value was provided. 2.0.0 (November 1, 2024) diff --git a/package-lock.json b/package-lock.json index 957d359..a0caaca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-react", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-react", - "version": "2.0.0", + "version": "2.0.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "11.0.0", + "@splitsoftware/splitio": "11.0.3", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" @@ -1589,12 +1589,12 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0.tgz", - "integrity": "sha512-9ZEhbpsgjvg41FQaahaG+JouzTQ91mX3xgzSClPMPL55kryjXAnQk9jawapnlkI1cKqE4VDvrznkmfr2/4qHRA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.3.tgz", + "integrity": "sha512-UtoixGfICCj52FfaVdI186Czw0qCvvEyCw/OtJVTsgM4Zq0k2mY8yKzQ7tSB/HJtzbUVnuPoxkwEcj0479stmg==", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0", - "bloom-filters": "^3.0.0", + "@splitsoftware/splitio-commons": "2.0.2", + "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", "node-fetch": "^2.7.0", @@ -1606,9 +1606,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.2.tgz", + "integrity": "sha512-r2m3kwWnSuROT+7zTzhWBrM0DMRBGJNQcTyvXw8zLPPmBs/PnmAnxCy7uRpfMHOGbP9Q3Iju0bU/H5dG8svyiw==", "dependencies": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -1998,6 +1998,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "node_modules/@types/seedrandom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.8.tgz", + "integrity": "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==" + }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -2953,15 +2958,14 @@ } }, "node_modules/bloom-filters": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.0.tgz", - "integrity": "sha512-DBDgLkYokKS5NA5y8P9fuTavKQCkleAP39yqpW/5Nab/vwzHv+wOPRM/yDAStghARDleyRI4orW91uuxj48LKQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.4.tgz", + "integrity": "sha512-BdnPWo2OpYhlvuP2fRzJBdioMCkm7Zp0HCf8NJgF5Mbyqy7VQ/CnTiVWMMyq4EZCBHwj0Kq6098gW2/3RsZsrA==", "dependencies": { + "@types/seedrandom": "^3.0.8", "base64-arraybuffer": "^1.0.2", "is-buffer": "^2.0.5", - "lodash": "^4.17.15", - "lodash.eq": "^4.0.0", - "lodash.indexof": "^4.0.5", + "lodash": "^4.17.21", "long": "^5.2.0", "reflect-metadata": "^0.1.13", "seedrandom": "^3.0.5", @@ -3308,9 +3312,9 @@ } }, "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -4326,9 +4330,9 @@ "dev": true }, "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -6009,9 +6013,9 @@ } }, "node_modules/jest-changed-files/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -7217,9 +7221,9 @@ "dev": true }, "node_modules/jest-runtime/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -8144,21 +8148,11 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, - "node_modules/lodash.eq": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.eq/-/lodash.eq-4.0.0.tgz", - "integrity": "sha512-vbrJpXL6kQNG6TkInxX12DZRfuYVllSxhwYqjYB78g2zF3UI15nFO/0AgmZnZRnaQ38sZtjCiVjGr2rnKt4v0g==" - }, "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" }, - "node_modules/lodash.indexof": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lodash.indexof/-/lodash.indexof-4.0.5.tgz", - "integrity": "sha512-t9wLWMQsawdVmf6/IcAgVGqAJkNzYVcn4BHYZKTPW//l7N5Oq7Bq138BaVk19agcsPZePcidSgTTw4NqS1nUAw==" - }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -8177,9 +8171,9 @@ "dev": true }, "node_modules/long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -9113,9 +9107,9 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" }, "node_modules/regenerator-runtime": { "version": "0.13.11", @@ -10519,9 +10513,9 @@ } }, "node_modules/webpack-cli/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -12150,12 +12144,12 @@ } }, "@splitsoftware/splitio": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.0.tgz", - "integrity": "sha512-9ZEhbpsgjvg41FQaahaG+JouzTQ91mX3xgzSClPMPL55kryjXAnQk9jawapnlkI1cKqE4VDvrznkmfr2/4qHRA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.3.tgz", + "integrity": "sha512-UtoixGfICCj52FfaVdI186Czw0qCvvEyCw/OtJVTsgM4Zq0k2mY8yKzQ7tSB/HJtzbUVnuPoxkwEcj0479stmg==", "requires": { - "@splitsoftware/splitio-commons": "2.0.0", - "bloom-filters": "^3.0.0", + "@splitsoftware/splitio-commons": "2.0.2", + "bloom-filters": "^3.0.4", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", "node-fetch": "^2.7.0", @@ -12164,9 +12158,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.2.tgz", + "integrity": "sha512-r2m3kwWnSuROT+7zTzhWBrM0DMRBGJNQcTyvXw8zLPPmBs/PnmAnxCy7uRpfMHOGbP9Q3Iju0bU/H5dG8svyiw==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -12495,6 +12489,11 @@ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "dev": true }, + "@types/seedrandom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.8.tgz", + "integrity": "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ==" + }, "@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -13215,15 +13214,14 @@ "dev": true }, "bloom-filters": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.0.tgz", - "integrity": "sha512-DBDgLkYokKS5NA5y8P9fuTavKQCkleAP39yqpW/5Nab/vwzHv+wOPRM/yDAStghARDleyRI4orW91uuxj48LKQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/bloom-filters/-/bloom-filters-3.0.4.tgz", + "integrity": "sha512-BdnPWo2OpYhlvuP2fRzJBdioMCkm7Zp0HCf8NJgF5Mbyqy7VQ/CnTiVWMMyq4EZCBHwj0Kq6098gW2/3RsZsrA==", "requires": { + "@types/seedrandom": "^3.0.8", "base64-arraybuffer": "^1.0.2", "is-buffer": "^2.0.5", - "lodash": "^4.17.15", - "lodash.eq": "^4.0.0", - "lodash.indexof": "^4.0.5", + "lodash": "^4.17.21", "long": "^5.2.0", "reflect-metadata": "^0.1.13", "seedrandom": "^3.0.5", @@ -13489,9 +13487,9 @@ } }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -14030,9 +14028,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -15560,9 +15558,9 @@ }, "dependencies": { "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -16470,9 +16468,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -17086,21 +17084,11 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, - "lodash.eq": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lodash.eq/-/lodash.eq-4.0.0.tgz", - "integrity": "sha512-vbrJpXL6kQNG6TkInxX12DZRfuYVllSxhwYqjYB78g2zF3UI15nFO/0AgmZnZRnaQ38sZtjCiVjGr2rnKt4v0g==" - }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" }, - "lodash.indexof": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lodash.indexof/-/lodash.indexof-4.0.5.tgz", - "integrity": "sha512-t9wLWMQsawdVmf6/IcAgVGqAJkNzYVcn4BHYZKTPW//l7N5Oq7Bq138BaVk19agcsPZePcidSgTTw4NqS1nUAw==" - }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -17119,9 +17107,9 @@ "dev": true }, "long": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", - "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "loose-envify": { "version": "1.4.0", @@ -17841,9 +17829,9 @@ } }, "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" }, "regenerator-runtime": { "version": "0.13.11", @@ -18885,9 +18873,9 @@ "dev": true }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", diff --git a/package.json b/package.json index 9e8b856..3eb460e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-react", - "version": "2.0.0", + "version": "2.0.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", + "@splitsoftware/splitio": "11.0.3", "memoize-one": "^5.1.1", "shallowequal": "^1.1.0", "tslib": "^2.3.1" diff --git a/src/SplitFactoryProvider.tsx b/src/SplitFactoryProvider.tsx index 02e36b0..38b2502 100644 --- a/src/SplitFactoryProvider.tsx +++ b/src/SplitFactoryProvider.tsx @@ -1,9 +1,21 @@ import React from 'react'; import { ISplitFactoryProviderProps } from './types'; -import { WARN_SF_CONFIG_AND_FACTORY } from './constants'; -import { getSplitFactory, destroySplitFactory, getSplitClient, getStatus, initAttributes } from './utils'; +import { VERSION, WARN_SF_CONFIG_AND_FACTORY } from './constants'; +import { getSplitClient, getStatus, initAttributes } from './utils'; import { SplitContext } from './SplitContext'; +import { SplitFactory } from '@splitsoftware/splitio/client'; + +/** + * Implementation rationale: + * - Follows React rules: pure components & hooks, with side effects managed in `useEffect`. + * - The `factory` and `client` properties in the context are available from the initial render, rather than being set lazily in a `useEffect`, so that: + * - Hooks retrieve the correct values from the start; for example, `useTrack` accesses the client's `track` method rather than a no-op function (related to https://github.com/splitio/react-client/issues/198). + * - Hooks can support Suspense and Server components where `useEffect` is not called (related to https://github.com/splitio/react-client/issues/192). + * - Re-renders are avoided for child components that do not depend on the factory being ready (e.g., tracking events, updating attributes, or managing consent). + * - `SplitFactoryProvider` updates the context only when props change (`config` or `factory`) but not the state (e.g., client status), preventing unnecessary updates to child components and allowing them to control when to update independently. + * - For these reasons, and to reduce component tree depth, `SplitFactoryProvider` no longer wraps the child component in a `SplitClient` component and thus does not accept a child as a function or `updateOn` props anymore. + */ /** * The SplitFactoryProvider is the top level component that provides the Split SDK factory to all child components via the Split Context. @@ -17,13 +29,22 @@ import { SplitContext } from './SplitContext'; export function SplitFactoryProvider(props: ISplitFactoryProviderProps) { const { config, factory: propFactory, attributes } = props; - const factory = React.useMemo(() => { - const factory = propFactory || (config ? getSplitFactory(config) : undefined); - initAttributes(factory && factory.client(), attributes); - return factory; - }, [config, propFactory, attributes]); + const factory = React.useMemo void }>(() => { + return propFactory ? + propFactory : + config ? + // @ts-expect-error. 2nd param is not part of type definitions. Used to overwrite the SDK version and enable lazy init + SplitFactory(config, (modules) => { + modules.settings.version = VERSION; + modules.lazyInit = true; + }) : + undefined; + }, [config, propFactory]); + const client = factory ? getSplitClient(factory) : undefined; + initAttributes(client, attributes); + // Effect to initialize and destroy the factory when config is provided React.useEffect(() => { if (propFactory) { @@ -31,15 +52,14 @@ export function SplitFactoryProvider(props: ISplitFactoryProviderProps) { return; } - if (config) { - const factory = getSplitFactory(config); + if (factory) { factory.init && factory.init(); return () => { - destroySplitFactory(factory); + factory.destroy(); } } - }, [config, propFactory]); + }, [config, propFactory, factory]); return ( diff --git a/src/__tests__/SplitClient.test.tsx b/src/__tests__/SplitClient.test.tsx index 70d37e1..4943ffd 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 { getStatus } 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: getStatus(outerFactory.client()).lastUpdate }); return null; diff --git a/src/__tests__/SplitFactoryProvider.test.tsx b/src/__tests__/SplitFactoryProvider.test.tsx index bb6c2f5..5a6ff96 100644 --- a/src/__tests__/SplitFactoryProvider.test.tsx +++ b/src/__tests__/SplitFactoryProvider.test.tsx @@ -13,14 +13,30 @@ const logSpy = jest.spyOn(console, 'log'); /** Test target */ import { SplitFactoryProvider } from '../SplitFactoryProvider'; import { SplitContext, useSplitContext } from '../SplitContext'; -import { __factories, IClientWithContext } from '../utils'; +import { getStatus } from '../utils'; import { WARN_SF_CONFIG_AND_FACTORY } from '../constants'; import { INITIAL_STATUS } from './testUtils/utils'; import { useSplitClient } from '../useSplitClient'; describe('SplitFactoryProvider', () => { - test('passes no-ready props to the child if initialized with a config.', () => { + test('passes no-ready properties, no factory and no client to the context if initialized without a config and factory props.', () => { + render( + + {React.createElement(() => { + const context = useSplitContext(); + expect(context).toEqual({ + ...INITIAL_STATUS, + factory: undefined, + client: undefined, + }); + return null; + })} + + ); + }); + + test('passes no-ready properties to the context if initialized with a config.', () => { render( {React.createElement(() => { @@ -36,7 +52,7 @@ describe('SplitFactoryProvider', () => { ); }); - test('passes ready props to the child if initialized with a ready factory.', async () => { + test('passes ready properties to the context if initialized with a ready factory.', async () => { const outerFactory = SplitFactory(sdkBrowser); (outerFactory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE); (outerFactory as any).client().__emitter__.emit(Event.SDK_READY); @@ -54,7 +70,7 @@ describe('SplitFactoryProvider', () => { client: outerFactory.client(), isReady: true, isReadyFromCache: true, - lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate + lastUpdate: getStatus(outerFactory.client()).lastUpdate }); return null; })} @@ -183,8 +199,7 @@ describe('SplitFactoryProvider', () => { wrapper.unmount(); - // Created factories are removed from `factories` cache and `destroy` method is called - expect(__factories.size).toBe(0); + // factory `destroy` methods are called expect(createdFactories.size).toBe(2); expect(factoryDestroySpies.length).toBe(2); factoryDestroySpies.forEach(spy => expect(spy).toBeCalledTimes(1)); @@ -197,8 +212,6 @@ describe('SplitFactoryProvider', () => { {React.createElement(() => { const { factory } = useSplitClient(); - // if factory is provided as a prop, `factories` cache is not modified - expect(__factories.size).toBe(0); destroySpy = jest.spyOn(factory!, 'destroy'); return null; })} diff --git a/src/__tests__/SplitTreatments.test.tsx b/src/__tests__/SplitTreatments.test.tsx index b94b477..ce33a96 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 } from '../utils'; import { newSplitFactoryLocalhostInstance } from './testUtils/utils'; import { CONTROL_WITH_CONFIG, EXCEPTION_NO_SFP } from '../constants'; @@ -73,7 +73,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, getStatus(outerFactory.client()).lastUpdate]); return null; }} diff --git a/src/useSplitClient.ts b/src/useSplitClient.ts index 56d7fc9..f2808a3 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, getStatus } from './utils'; import { ISplitContextValues, IUseSplitClientOptions } from './types'; export const DEFAULT_UPDATE_OPTIONS = { @@ -33,7 +33,7 @@ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextV // @TODO Move `getSplitClient` side effects // @TODO Once `SplitClient` is removed, which updates the context, simplify next line as `const client = factory ? getSplitClient(factory, splitKey) : undefined;` - const client = factory && splitKey ? getSplitClient(factory, splitKey) : contextClient as IClientWithContext; + const client = factory && splitKey ? getSplitClient(factory, splitKey) : contextClient; initAttributes(client, attributes); @@ -44,7 +44,7 @@ export function useSplitClient(options?: IUseSplitClientOptions): ISplitContextV React.useEffect(() => { if (!client) return; - const update = () => setLastUpdate(client.__getStatus().lastUpdate); + const update = () => setLastUpdate(getStatus(client).lastUpdate); // Clients are created on the hook's call, so the status may have changed const statusOnEffect = getStatus(client); diff --git a/src/useTrack.ts b/src/useTrack.ts index 2af449b..a71058f 100644 --- a/src/useTrack.ts +++ b/src/useTrack.ts @@ -13,5 +13,7 @@ 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, updateOnSdkTimedout: false, updateOnSdkUpdate: false }); + + // Retrieve the client `track` rather than a bound version of it, as there is no need to bind the function, and can be used as a reactive dependency that only changes if the underlying client changes. return client ? client.track : noOpFalse; } diff --git a/src/utils.ts b/src/utils.ts index f7a8f33..26b5f90 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,6 @@ import memoizeOne from 'memoize-one'; import shallowEqual from 'shallowequal'; -import { SplitFactory } from '@splitsoftware/splitio/client'; -import { CONTROL_WITH_CONFIG, VERSION, WARN_NAMES_AND_FLAGSETS } from './constants'; +import { CONTROL_WITH_CONFIG, WARN_NAMES_AND_FLAGSETS } from './constants'; import { ISplitStatus } from './types'; // Utils used to access singleton instances of Split factories and clients, and to gracefully shutdown all clients together. @@ -9,7 +8,7 @@ import { ISplitStatus } from './types'; /** * ClientWithContext interface. */ -export interface IClientWithContext extends SplitIO.IBrowserClient { +interface IClientWithContext extends SplitIO.IBrowserClient { __getStatus(): { isReady: boolean; isReadyFromCache: boolean; @@ -26,24 +25,6 @@ export interface IFactoryWithLazyInit extends SplitIO.IBrowserSDK { init(): void; } -// exported for testing purposes -export const __factories: Map = new Map(); - -// idempotent operation -export function getSplitFactory(config: SplitIO.IBrowserSettings) { - if (!__factories.has(config)) { - // SplitFactory is not an idempotent operation - // @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 IFactoryWithLazyInit; - newFactory.config = config; - __factories.set(config, newFactory); - } - return __factories.get(config) as IFactoryWithLazyInit; -} - // idempotent operation export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.SplitKey): IClientWithContext { // factory.client is an idempotent operation @@ -56,11 +37,6 @@ export function getSplitClient(factory: SplitIO.IBrowserSDK, key?: SplitIO.Split return client; } -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.IBrowserClient): ISplitStatus { @@ -79,6 +55,7 @@ export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus { /** * Manage client attributes binding */ +// @TODO should reset attributes rather than set/merge them, to keep SFP and hooks pure. export function initAttributes(client?: SplitIO.IBrowserClient, attributes?: SplitIO.Attributes) { if (client && attributes) client.setAttributes(attributes); }