diff --git a/.eslintrc.js b/.eslintrc.js index 798635b..695bae4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,6 +17,7 @@ module.exports = { 'plugins': [ 'react', '@typescript-eslint', + 'eslint-plugin-tsdoc', 'import' ], 'rules': { @@ -56,5 +57,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'], + 'rules': { + 'tsdoc/syntax': 'warn' + } }], }; diff --git a/.gitignore b/.gitignore index 3c9ac65..d53333b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ .DS_Store -node_modules -lib -es -types -coverage -examples -.vscode -.scannerwork +/node_modules +/cjs +/esm +/types +/coverage +/examples +/.vscode +/.scannerwork diff --git a/CHANGES.txt b/CHANGES.txt index 08c1acc..cbf8f19 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,14 @@ +2.0.0 (November 14, 2024) + - Added support for targeting rules based on large segments. + - Updated @splitsoftware/splitio package to version 11.0.1 that includes major updates, and updated some transitive dependencies for vulnerability fixes. + - Updated `getTreatments` action creator to not dispatch an action when called while the SDK is not ready or ready from cache, to avoid unnecessary updates in the state. + - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for ECMAScript Modules build. + - BREAKING CHANGES: + - Removed the `core.trafficType` option from the `config` object accepted by the `initSplitSdk` action creator, and made the `trafficType` argument of the `track` helper function mandatory. + This is because traffic types can no longer be bound to SDK clients since JavaScript SDK v11.0.0, so the traffic type must now be provided as an argument in `track` function calls. + Refer to ./MIGRATION-GUIDE.md for more details. + - Updated peer dependencies to drop support for Redux library below v3.0.0. + 1.14.1 (October 15, 2024) - Bugfixing - Fixed error in `splitReducer` when handling actions with a `null` payload, preventing crashes caused by accessing undefined payload properties (Related to https://github.com/splitio/redux-client/issues/121). @@ -55,7 +66,7 @@ - Updated linter dependencies and rules. The deprecated TSLint package was replaced by ESLint. - Updated some transitive dependencies for vulnerability fixes. - Updated @splitsoftware/splitio package to version 10.22.4 that includes minor improvements. - - Bugfixing - Fixed error when using the SDK in localhost mode for testing with NodeJS test runners such as Jest (See https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#localhost-mode). + - Bugfixing - Fixed error when using the SDK in localhost mode for testing with Node.js test runners such as Jest (See https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#localhost-mode). 1.7.1 (November 15, 2022) - Updated React Redux peer dependency range to include React-redux@8.x.x and React@18.x.x. diff --git a/MIGRATION-GUIDE.md b/MIGRATION-GUIDE.md new file mode 100644 index 0000000..944b7fc --- /dev/null +++ b/MIGRATION-GUIDE.md @@ -0,0 +1,40 @@ + +# Migrating to Redux SDK v2.0.0 + +Redux SDK v2.0.0 introduces a breaking change that you should consider when migrating from a previous version. + +If you were passing the `core.trafficType` option to the SDK configuration object, you should remove it since it is no longer supported. +The `trafficType` must be passed as an argument of the `track` helper function. For example: + +```js +import { initSplitSdk, track } from '@splitsoftware/splitio-redux' + +const CONFIG = { + core: { + authorizationKey: YOUR_CLIENT_SIDE_SDK_KEY, + key: USER_KEY, + trafficType: 'user' + } +} + +store.dispatch(initSplitSdk({ config: CONFIG })) + +track({ eventType: 'my_event' }); +``` + +should be refactored to: + +```js +import { initSplitSdk, track } from '@splitsoftware/splitio-redux' + +const CONFIG = { + core: { + authorizationKey: YOUR_CLIENT_SIDE_SDK_KEY, + key: USER_KEY + } +} + +store.dispatch(initSplitSdk({ config: CONFIG })) + +track({ eventType: 'my_event', trafficType: 'user' }); +``` diff --git a/README.md b/README.md index 9041027..78d0a7c 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Split has built and maintains SDKs for: * Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK) * JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK) * JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK) -* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK) +* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK) * PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK) * PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK) * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK) diff --git a/package-lock.json b/package-lock.json index b932fcd..f176b5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-redux", - "version": "1.14.1", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-redux", - "version": "1.14.1", + "version": "2.0.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "10.28.0", + "@splitsoftware/splitio": "11.0.1", "tslib": "^2.3.1" }, "devDependencies": { @@ -24,6 +24,7 @@ "eslint-plugin-compat": "^4.1.2", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", + "eslint-plugin-tsdoc": "^0.3.0", "husky": "^3.1.0", "jest": "^27.2.3", "react": "^18.0.0", @@ -39,7 +40,7 @@ }, "peerDependencies": { "react-redux": ">=4.0.0", - "redux": ">=2.0.0", + "redux": ">=3.0.0", "redux-thunk": ">=2.0.0" }, "peerDependenciesMeta": { @@ -1448,6 +1449,46 @@ "integrity": "sha512-vC+UDAsQti7Cv2oBahPfgnTXT7n0XZk8e7UFucNMmkauszdiiEsNFI0elmMMrh2u+IaMOvAAHo3DDzMx7y80Cw==", "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", @@ -1502,13 +1543,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "10.28.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.28.0.tgz", - "integrity": "sha512-hzBnBZHmUTXvyMBbVTDUYtspLHjyjb/YqKtetNh7pAvkmj37vOXyXfF50Of5jr3Qmvdo0YFbKvMIOEXlXSGWaQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.1.tgz", + "integrity": "sha512-s6DF8h/ftP+vj2mpe46cDWDMy2DeEWlHLDmBGP3Rw4yOr57hdWwstg7b5mti2NLivObgVs8gKj1eoSxJK5Eb0A==", "dependencies": { - "@splitsoftware/splitio-commons": "1.17.0", - "@types/google.analytics": "0.0.40", - "@types/ioredis": "^4.28.0", + "@splitsoftware/splitio-commons": "2.0.0", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -1517,15 +1556,15 @@ "unfetch": "^4.2.0" }, "engines": { - "node": ">=6", - "npm": ">=3" + "node": ">=14.0.0" } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.0.tgz", - "integrity": "sha512-rvP+0LGUN92bcTytiqyVxq9UzBG5kTkIYjU7b7AU2awBUYgM0bqT3xhQ9/MJ/2fsBbqC6QIsxoKDOz9pMgbAQw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", + "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", "dependencies": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" }, "peerDependencies": { @@ -1789,11 +1828,6 @@ "@babel/types": "^7.3.0" } }, - "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", @@ -3795,6 +3829,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": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -4443,10 +4487,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", @@ -4734,6 +4781,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", @@ -5095,12 +5154,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" @@ -7412,6 +7474,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-sdsl": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", @@ -8681,6 +8749,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", @@ -8694,12 +8771,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" }, @@ -10998,6 +11075,44 @@ "integrity": "sha512-vC+UDAsQti7Cv2oBahPfgnTXT7n0XZk8e7UFucNMmkauszdiiEsNFI0elmMMrh2u+IaMOvAAHo3DDzMx7y80Cw==", "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", @@ -11043,13 +11158,11 @@ } }, "@splitsoftware/splitio": { - "version": "10.28.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.28.0.tgz", - "integrity": "sha512-hzBnBZHmUTXvyMBbVTDUYtspLHjyjb/YqKtetNh7pAvkmj37vOXyXfF50Of5jr3Qmvdo0YFbKvMIOEXlXSGWaQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.0.1.tgz", + "integrity": "sha512-s6DF8h/ftP+vj2mpe46cDWDMy2DeEWlHLDmBGP3Rw4yOr57hdWwstg7b5mti2NLivObgVs8gKj1eoSxJK5Eb0A==", "requires": { - "@splitsoftware/splitio-commons": "1.17.0", - "@types/google.analytics": "0.0.40", - "@types/ioredis": "^4.28.0", + "@splitsoftware/splitio-commons": "2.0.0", "bloom-filters": "^3.0.0", "ioredis": "^4.28.0", "js-yaml": "^3.13.1", @@ -11059,10 +11172,11 @@ } }, "@splitsoftware/splitio-commons": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.0.tgz", - "integrity": "sha512-rvP+0LGUN92bcTytiqyVxq9UzBG5kTkIYjU7b7AU2awBUYgM0bqT3xhQ9/MJ/2fsBbqC6QIsxoKDOz9pMgbAQw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", + "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", "requires": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" } }, @@ -11265,11 +11379,6 @@ "@babel/types": "^7.3.0" } }, - "@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", @@ -13032,6 +13141,16 @@ } } }, + "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": "7.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", @@ -13275,9 +13394,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": { @@ -13476,6 +13595,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", @@ -13736,12 +13864,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": { @@ -15442,6 +15570,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-sdsl": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", @@ -16402,6 +16536,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", @@ -16415,12 +16555,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 cb9b2fb..154503f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "@splitsoftware/splitio-redux", - "version": "1.14.1", + "version": "2.0.0", "description": "A library to easily use Split JS SDK with Redux and React Redux", - "main": "lib/index.js", - "module": "es/index.js", + "main": "cjs/index.js", + "module": "esm/index.js", "types": "types/index.d.ts", "files": [ "README.md", @@ -11,17 +11,17 @@ "LICENSE", "CHANGES.txt", "src", - "lib", - "es", + "cjs", + "esm", "types" ], "scripts": { - "build": "rimraf lib/* es/* types/* && tsc && tsc -m commonjs --outDir lib -d true --declarationDir types", + "build": "rimraf cjs/* esm/* types/* && tsc && tsc -m commonjs --outDir cjs -d true --declarationDir types", "postbuild": "./version_replace.sh", "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", @@ -59,7 +59,7 @@ }, "homepage": "https://github.com/splitio/redux-client#readme", "dependencies": { - "@splitsoftware/splitio": "10.28.0", + "@splitsoftware/splitio": "11.0.1", "tslib": "^2.3.1" }, "devDependencies": { @@ -74,6 +74,7 @@ "eslint-plugin-compat": "^4.1.2", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", + "eslint-plugin-tsdoc": "^0.3.0", "husky": "^3.1.0", "jest": "^27.2.3", "react": "^18.0.0", @@ -89,7 +90,7 @@ }, "peerDependencies": { "react-redux": ">=4.0.0", - "redux": ">=2.0.0", + "redux": ">=3.0.0", "redux-thunk": ">=2.0.0" }, "peerDependenciesMeta": { diff --git a/src/__tests__/asyncActions.browser.test.ts b/src/__tests__/asyncActions.browser.test.ts index 4d23117..6f004a3 100644 --- a/src/__tests__/asyncActions.browser.test.ts +++ b/src/__tests__/asyncActions.browser.test.ts @@ -13,7 +13,7 @@ import { sdkBrowserConfig } from './utils/sdkConfigs'; import { SPLIT_READY, SPLIT_READY_WITH_EVALUATIONS, SPLIT_READY_FROM_CACHE, SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_UPDATE, SPLIT_UPDATE_WITH_EVALUATIONS, SPLIT_TIMEDOUT, SPLIT_DESTROY, ADD_TREATMENTS, - ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, getControlTreatmentsWithConfig, ERROR_GETT_NO_PARAM_OBJECT, + ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, ERROR_GETT_NO_PARAM_OBJECT, } from '../constants'; /** Test targets */ @@ -323,7 +323,7 @@ describe('getTreatments', () => { } }); - it('stores control treatments (without calling SDK client) and registers pending evaluations if Split SDK is not operational, to dispatch it when ready (Using action result promise)', (done) => { + it('registers evaluations if Split SDK is not operational, to dispatch it when ready (Using action result promise)', (done) => { const store = mockStore(STATE_INITIAL); const actionResult = store.dispatch(initSplitSdk({ config: sdkBrowserConfig, onReadyFromCache: onReadyFromCacheCb })); @@ -332,10 +332,7 @@ describe('getTreatments', () => { // If SDK is not operational, ADD_TREATMENTS actions are dispatched, with control treatments for provided feature flag names, and no treatments for provided flag sets. - expect(store.getActions()).toEqual([ - { type: ADD_TREATMENTS, payload: { key: sdkBrowserConfig.core.key, treatments: getControlTreatmentsWithConfig(['split2']) } }, - { type: ADD_TREATMENTS, payload: { key: sdkBrowserConfig.core.key, treatments: {} } }, - ]); + expect(store.getActions().length).toBe(0); // SDK client is not called, but items are added to 'evalOnReady' list. expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(0); expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledTimes(0); @@ -345,8 +342,8 @@ describe('getTreatments', () => { // When the SDK is ready from cache, the SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS action is not dispatched if the `getTreatments` action was dispatched with `evalOnReadyFromCache` false (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE); function onReadyFromCacheCb() { - expect(store.getActions().length).toBe(3); - const action = store.getActions()[2]; + expect(store.getActions().length).toBe(1); + const action = store.getActions()[0]; expect(action).toEqual({ type: SPLIT_READY_FROM_CACHE, payload: { @@ -359,7 +356,7 @@ describe('getTreatments', () => { actionResult.then(() => { // The SPLIT_READY_WITH_EVALUATIONS action is dispatched if the SDK is ready and there are pending evaluations. - const action = store.getActions()[3]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_READY_WITH_EVALUATIONS, payload: { @@ -392,7 +389,7 @@ describe('getTreatments', () => { }); }); - it('stores control treatments (without calling SDK client) and registers pending evaluations if Split SDK is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when SDK timeout)', (done) => { + it('registers pending evaluations if Split SDK is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when SDK timeout)', (done) => { const store = mockStore(STATE_INITIAL); store.dispatch(initSplitSdk({ config: sdkBrowserConfig, onTimedout: onTimedoutCb, onReadyFromCache: onReadyFromCacheCb, onReady: onReadyCb })); @@ -401,15 +398,7 @@ describe('getTreatments', () => { store.dispatch(getTreatments({ splitNames: 'split3', attributes, evalOnUpdate: true, evalOnReadyFromCache: true })); // If SDK is not ready, an ADD_TREATMENTS action is dispatched with control treatments without calling SDK client - expect(store.getActions().length).toBe(1); - let action = store.getActions()[0]; - expect(action).toEqual({ - type: ADD_TREATMENTS, - payload: { - key: sdkBrowserConfig.core.key, - treatments: getControlTreatmentsWithConfig(['split3']) - } - }); + expect(store.getActions().length).toBe(0); expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(0); // the item is added for evaluation on SDK_READY, and also on SDK_READY_FROM_CACHE and SDK_UPDATE events @@ -421,7 +410,7 @@ describe('getTreatments', () => { // When the SDK has timedout, the SPLIT_TIMEDOUT action is dispatched. It doesn't affect registered evaluations for other SDK events. (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT); function onTimedoutCb() { - action = store.getActions()[1]; + const action = store.getActions()[0]; expect(action).toEqual({ type: SPLIT_TIMEDOUT, payload: { @@ -434,7 +423,7 @@ describe('getTreatments', () => { // SPLIT_READY_FROM_CACHE, because of the `evalOnReadyFromCache` param in `getTreatments` action (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE); function onReadyFromCacheCb() { - action = store.getActions()[2]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, payload: { @@ -456,7 +445,7 @@ describe('getTreatments', () => { // Using cb for ready event, because action result is rejected due to SDK timeout function onReadyCb() { // The SPLIT_READY_WITH_EVALUATIONS action is dispatched if the SDK is ready and there are pending evaluations. - action = store.getActions()[3]; + let action = store.getActions()[2]; expect(action).toEqual({ type: SPLIT_READY_WITH_EVALUATIONS, payload: { @@ -476,7 +465,7 @@ describe('getTreatments', () => { // Triggering an update dispatches SPLIT_UPDATE_WITH_EVALUATIONS (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_UPDATE); - action = store.getActions()[4]; + action = store.getActions()[3]; expect(action).toEqual({ type: SPLIT_UPDATE_WITH_EVALUATIONS, payload: { @@ -496,7 +485,7 @@ describe('getTreatments', () => { // We deregister the item from evalOnUpdate. store.dispatch(getTreatments({ splitNames: 'split3', evalOnUpdate: false, key: { matchingKey: sdkBrowserConfig.core.key as string, bucketingKey: 'bucket' } })); - action = store.getActions()[5]; + action = store.getActions()[4]; expect(action).toEqual({ type: ADD_TREATMENTS, payload: { @@ -508,7 +497,7 @@ describe('getTreatments', () => { // Now, SDK_UPDATE events do not trigger SPLIT_UPDATE_WITH_EVALUATIONS but SPLIT_UPDATE instead (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_UPDATE); - action = store.getActions()[6]; + action = store.getActions()[5]; expect(action).toEqual({ type: SPLIT_UPDATE, payload: { @@ -516,14 +505,14 @@ describe('getTreatments', () => { } }); - expect(store.getActions().length).toBe(7); // control assertion - no more actions after the update. + expect(store.getActions().length).toBe(6); // control assertion - no more actions after the update. expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(4); // control assertion - called 4 times, in actions SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_READY_WITH_EVALUATIONS, SPLIT_UPDATE_WITH_EVALUATIONS and ADD_TREATMENTS. done(); } }); - it('for non-default clients, it stores control treatments (without calling SDK client) and registers pending evaluations if the client is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when the client timeouts)', (done) => { + it('for non-default clients, registers pending evaluations if the client is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when the client timeouts)', (done) => { // Init SDK and set ready const store = mockStore(STATE_INITIAL); @@ -676,7 +665,7 @@ describe('destroySplitSdk', () => { const actionResult = store.dispatch(destroySplitSdk()); actionResult.then(() => { - const action = store.getActions()[3]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_DESTROY, payload: { diff --git a/src/__tests__/helpers.browser.test.ts b/src/__tests__/helpers.browser.test.ts index 0cbdd09..b0bd0f9 100644 --- a/src/__tests__/helpers.browser.test.ts +++ b/src/__tests__/helpers.browser.test.ts @@ -122,11 +122,11 @@ describe('track', () => { it('logs error and returns false if the SDK was not initialized', () => { const errorSpy = jest.spyOn(console, 'error'); - expect(track({ eventType: 'event' })).toBe(false); + expect(track({ eventType: 'event', trafficType: 'user' })).toBe(false); expect(errorSpy).toBeCalledWith(ERROR_TRACK_NO_INITSPLITSDK); }); - it('should invoke the track method of the main client (no traffic type in config)', () => { + it('should invoke the track method of the main client', () => { initSplitSdk({ config: sdkBrowserConfig }); expect(track({ eventType: 'event', trafficType: 'user' })).toBe(true); @@ -134,22 +134,11 @@ describe('track', () => { expect((splitSdk.factory as any).client().track.mock.calls[0][0]).toBe('user'); expect((splitSdk.factory as any).client().track.mock.calls[0][1]).toBe('event'); - // TT must be provided if not included in the config + // @ts-expect-error TT must be provided expect(track({ eventType: 'event' })).toBe(false); }); - it('should invoke the track method of the main client (traffic type in config)', () => { - initSplitSdk({ config: { ...sdkBrowserConfig, core: { ...sdkBrowserConfig.core, trafficType: 'user' } } }); - - expect(track({ eventType: 'event' })).toBe(true); - expect((splitSdk.factory as any).client().track.mock.calls.length).toBe(1); - expect((splitSdk.factory as any).client().track.mock.calls[0][0]).toBe('event'); - - // TT is ignored if included in the config - expect(track({ eventType: 'event', trafficType: 'user' })).toBe(true); - }); - - it('should invoke the track method of a shared client (no traffic type in config)', () => { + it('should invoke the track method of a shared client', () => { initSplitSdk({ config: sdkBrowserConfig }); expect(track({ eventType: 'event', key: 'user1', trafficType: 'user' })).toBe(true); @@ -157,19 +146,7 @@ describe('track', () => { expect((splitSdk.factory as any).client('user1').track.mock.calls[0][0]).toBe('user'); expect((splitSdk.factory as any).client('user1').track.mock.calls[0][1]).toBe('event'); - // TT must be provided if key is provided - expect(track({ eventType: 'event', key: 'user1' })).toBe(false); - }); - - it('should invoke the track method of a shared client (traffic type in config)', () => { - initSplitSdk({ config: { ...sdkBrowserConfig, core: { ...sdkBrowserConfig.core, trafficType: 'user' } } }); - - expect(track({ eventType: 'event', key: 'user1', trafficType: 'user' })).toBe(true); - expect((splitSdk.factory as any).client('user1').track.mock.calls.length).toBe(1); - expect((splitSdk.factory as any).client('user1').track.mock.calls[0][0]).toBe('user'); - expect((splitSdk.factory as any).client('user1').track.mock.calls[0][1]).toBe('event'); - - // TT must be provided if key is provided, no matter if present in the config, since that TT is for main client + // @ts-expect-error TT must be provided expect(track({ eventType: 'event', key: 'user1' })).toBe(false); }); diff --git a/src/__tests__/reducer.test.ts b/src/__tests__/reducer.test.ts index 10ef362..6f44306 100644 --- a/src/__tests__/reducer.test.ts +++ b/src/__tests__/reducer.test.ts @@ -2,7 +2,7 @@ import { initialStatus, splitReducer } from '../reducer'; import { splitReady, splitReadyWithEvaluations, splitReadyFromCache, splitReadyFromCacheWithEvaluations, splitTimedout, splitUpdate, splitUpdateWithEvaluations, splitDestroy, addTreatments } from '../actions'; import { ISplitState } from '../types'; import SplitIO from '@splitsoftware/splitio/types/splitio'; -import { AnyAction } from 'redux'; +import { Action } from 'redux'; const initialState: ISplitState = { isReady: false, @@ -179,7 +179,7 @@ describe('Split reducer', () => { }); }); - const actionCreatorsWithEvaluations: Array<[string, (key: SplitIO.SplitKey, treatments: SplitIO.TreatmentsWithConfig, timestamp: number, nonDefaultKey?: boolean) => AnyAction, boolean, boolean]> = [ + const actionCreatorsWithEvaluations: Array<[string, (key: SplitIO.SplitKey, treatments: SplitIO.TreatmentsWithConfig, timestamp: number, nonDefaultKey?: boolean) => Action, boolean, boolean]> = [ ['ADD_TREATMENTS', addTreatments, false, false], ['SPLIT_READY_WITH_EVALUATIONS', splitReadyWithEvaluations, true, false], ['SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS', splitReadyFromCacheWithEvaluations, false, true], diff --git a/src/__tests__/utils/mockBrowserSplitSdk.ts b/src/__tests__/utils/mockBrowserSplitSdk.ts index 37c6107..46b628f 100644 --- a/src/__tests__/utils/mockBrowserSplitSdk.ts +++ b/src/__tests__/utils/mockBrowserSplitSdk.ts @@ -22,7 +22,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 : ''}`; } @@ -33,7 +33,7 @@ export function mockSdk() { // ATM, isReadyFromCache is shared among clients let isReadyFromCache = false; - function mockClient(key?: SplitIO.SplitKey) { + function mockClient(_key?: SplitIO.SplitKey) { // Readiness let isReady = false; let hasTimedout = false; @@ -51,14 +51,10 @@ export function mockSdk() { __emitter__.once(Event.SDK_READY_TIMED_OUT, () => { hasTimedout = true; syncLastUpdate(); }); __emitter__.on(Event.SDK_UPDATE, () => { syncLastUpdate(); }); + let attributesCache = {}; + // Client methods const track: jest.Mock = jest.fn((tt, et, v, p) => { - if (!(key || !config.core.trafficType)) { - p = v; - v = et; - et = tt; - tt = config.core.trafficType; - } return typeof tt === 'string' && typeof et === 'string' && (typeof v === 'number' || typeof v === 'undefined') && @@ -76,14 +72,16 @@ export function mockSdk() { return acc; }, {}); }); - const setAttributes: jest.Mock = jest.fn(() => { + const setAttributes: jest.Mock = jest.fn((attributes) => { + attributesCache = Object.assign(attributesCache, attributes); return true; }); const clearAttributes: jest.Mock = jest.fn(() => { + attributesCache = {}; return true; }); const getAttributes: jest.Mock = jest.fn(() => { - return true; + return attributesCache; }); const ready: jest.Mock = jest.fn(() => { return promiseWrapper(new Promise((res, rej) => { @@ -130,14 +128,18 @@ export function mockSdk() { const manager: jest.Mock = jest.fn().mockReturnValue({ names, split, splits }); // Cache of clients - const __clients__: { [key: string]: any } = {}; + const __clients__: { [instanceId: string]: any } = {}; const client = jest.fn((key?: SplitIO.SplitKey) => { const clientKey = key || parseKey(config.core.key); - const clientTT = key ? undefined : config.core.trafficType; - const instanceId = buildInstanceId(clientKey, clientTT); + const instanceId = buildInstanceId(clientKey); return __clients__[instanceId] || (__clients__[instanceId] = mockClient(key)); }); + // Factory destroy + const destroy = jest.fn(() => { + return Promise.all(Object.keys(__clients__).map(instanceId => __clients__[instanceId].destroy())); + }); + const modules = { settings: { version: 'javascript-10.18.0' } }; if (__updateModules) __updateModules(modules); @@ -145,6 +147,7 @@ export function mockSdk() { const factory = { client, manager, + destroy, settings: modules.settings, __names__: names, __split__: split, diff --git a/src/__tests__/utils/mockNodeSplitSdk.ts b/src/__tests__/utils/mockNodeSplitSdk.ts index 874c32a..97f4ec1 100644 --- a/src/__tests__/utils/mockNodeSplitSdk.ts +++ b/src/__tests__/utils/mockNodeSplitSdk.ts @@ -26,8 +26,12 @@ function mockClient() { __emitter__.on(Event.SDK_UPDATE, () => { syncLastUpdate(); }); // Client methods - const track: jest.Mock = jest.fn(() => { - return true; + const track: jest.Mock = jest.fn((key, tt, et, v, p) => { + return typeof key === 'string' && + typeof tt === 'string' && + typeof et === 'string' && + (typeof v === 'number' || typeof v === 'undefined') && + (typeof p === 'object' || typeof p === 'undefined'); }); const getTreatmentsWithConfig: jest.Mock = jest.fn((key, featureFlagNames) => { return featureFlagNames.reduce((acc: SplitIO.TreatmentsWithConfig, featureFlagName: string) => { @@ -86,12 +90,17 @@ export function mockSdk() { const splits: jest.Mock = jest.fn().mockReturnValue([]); const manager: jest.Mock = jest.fn().mockReturnValue({ names, split, splits }); - // Client (only one client on Node SDK) + // Client (only one client on Node.js) const __client__ = mockClient(); const client = jest.fn(() => { return __client__; }); + // Factory destroy + const destroy = jest.fn(() => { + return __client__.destroy(); + }); + const modules = { settings: { version: 'nodejs-10.18.0' } }; if (__updateModules) __updateModules(modules); @@ -99,6 +108,7 @@ export function mockSdk() { const factory = { client, manager, + destroy, settings: modules.settings, __names__: names, __split__: split, diff --git a/src/__tests__/utils/promiseWrapper.ts b/src/__tests__/utils/promiseWrapper.ts index ee36a2f..3c4a3da 100644 --- a/src/__tests__/utils/promiseWrapper.ts +++ b/src/__tests__/utils/promiseWrapper.ts @@ -8,8 +8,8 @@ * - setting an `onFinally` handler as the first handler (e.g. `promiseWrapper(Promise.reject()).finally(...)`) * - setting more than one handler with at least one of them being an onRejected handler * - * @param customPromise promise to wrap - * @param defaultOnRejected default onRejected function + * @param customPromise - Promise to wrap + * @param defaultOnRejected - Default onRejected function * @returns a promise that doesn't need to be handled for rejection (except when using async/await syntax). */ export default function promiseWrapper(customPromise: Promise, defaultOnRejected: (_: any) => any): Promise { diff --git a/src/asyncActions.ts b/src/asyncActions.ts index 6092f03..0bab016 100644 --- a/src/asyncActions.ts +++ b/src/asyncActions.ts @@ -3,7 +3,7 @@ import { SplitFactory as SplitFactoryForLocalhost } from '@splitsoftware/splitio import { Dispatch, Action } from 'redux'; import { IInitSplitSdkParams, IGetTreatmentsParams, IDestroySplitSdkParams, ISplitFactoryBuilder } from './types'; import { splitReady, splitReadyWithEvaluations, splitReadyFromCache, splitReadyFromCacheWithEvaluations, splitTimedout, splitUpdate, splitUpdateWithEvaluations, splitDestroy, addTreatments } from './actions'; -import { VERSION, ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, getControlTreatmentsWithConfig } from './constants'; +import { VERSION, ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK } from './constants'; import { matching, __getStatus, validateGetTreatmentsParams, isMainClient } from './utils'; /** @@ -14,8 +14,8 @@ import { matching, __getStatus, validateGetTreatmentsParams, isMainClient } from export interface ISplitSdk { config: SplitIO.IBrowserSettings | SplitIO.INodeSettings; splitio: ISplitFactoryBuilder; - factory: SplitIO.ISDK; - sharedClients: { [stringKey: string]: SplitIO.IClient }; + factory: SplitIO.IBrowserSDK | SplitIO.ISDK; + sharedClients: { [stringKey: string]: SplitIO.IBrowserClient }; isDetached: boolean; // true: server-side, false: client-side (i.e., client with bound key) dispatch: Dispatch; } @@ -32,14 +32,14 @@ export const splitSdk: ISplitSdk = { /** * This action creator initializes the Split SDK. It dispatches a Thunk (async) action. * - * @param {IInitSplitSdkParams} params + * @param params - Parameter object to initialize the SDK. */ export function initSplitSdk(params: IInitSplitSdkParams): (dispatch: Dispatch) => Promise { splitSdk.config = params.config; splitSdk.splitio = params.splitio || - // For client-side localhost mode, we need to use the client-side SDK, to support test runners that execute in NodeJS + // For client-side localhost mode, we need to use the client-side SDK, to support test runners that execute in Node.js (splitSdk.config?.core?.authorizationKey === 'localhost' && typeof splitSdk.config?.features === 'object' ? SplitFactoryForLocalhost : SplitFactory) as ISplitFactoryBuilder; @@ -86,8 +86,8 @@ export function initSplitSdk(params: IInitSplitSdkParams): (dispatch: Dispatch { @@ -102,9 +102,9 @@ function __getTreatments(client: IClientNotDetached, evalParams: IGetTreatmentsP } /** - * This action creator performs a treatment evaluation, i.e., it invokes the actual `client.getTreatment*` methods. + * This action creator performs a feature flag evaluation, i.e., it invokes the actual `client.getTreatment*` methods. * - * @param {IGetTreatmentsParams} params + * @param params - Parameter object to evaluate feature flags. */ export function getTreatments(params: IGetTreatmentsParams): Action | (() => void) { @@ -164,16 +164,13 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi splitReadyFromCacheWithEvaluations(params.key, treatments, status.lastUpdate, true) : addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, treatments); } else { - // Otherwise, it adds control treatments to the store, without calling the SDK (no impressions sent) - // With flag sets, an empty object is passed since we don't know their feature flag names - // @TODO remove eventually to minimize state changes - return addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, splitNames ? getControlTreatmentsWithConfig(splitNames) : {}); + return () => { }; } - } else { // Split SDK running in Node + } else { // Split SDK running in Node.js // Evaluate Split and return redux action. - const client = splitSdk.factory.client(); + const client = splitSdk.factory.client() as SplitIO.IClient; const treatments = splitNames ? client.getTreatmentsWithConfig(params.key, splitNames, params.attributes) : client.getTreatmentsWithConfigByFlagSets(params.key, flagSets, params.attributes); @@ -185,7 +182,7 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi /** * Interface of SDK client for not detached execution (browser). */ -interface IClientNotDetached extends SplitIO.IClient { +interface IClientNotDetached extends SplitIO.IBrowserClient { _trackingStatus?: boolean; /** * stored evaluations to execute on SDK update. It is an object because we might @@ -208,8 +205,8 @@ interface IClientNotDetached extends SplitIO.IClient { * These lists are used by `getTreatments` action creator to schedule evaluation of feature flags on SDK_UPDATE, SDK_READY and SDK_READY_FROM_CACHE events. * It is exported for testing purposes only. * - * @param splitSdk it contains the Split factory, the store dispatch function, and other internal properties - * @param key optional user key + * @param splitSdk - It contains the Split factory, the store dispatch function, and other internal properties + * @param key - Optional user key * @returns SDK client with `evalOnUpdate`, `evalOnReady` and `evalOnReadyFromCache` action lists. */ export function getClient(splitSdk: ISplitSdk, key?: SplitIO.SplitKey): IClientNotDetached { @@ -294,15 +291,12 @@ export function destroySplitSdk(params: IDestroySplitSdkParams = {}): (dispatch: // Destroy the client(s) outside the thunk action, since on server-side the action is not dispatched // because stores have a life-span per session/request and there may not be one when server shuts down. const mainClient = splitSdk.factory.client(); - // in node, `splitSdk.sharedClients` is an empty object - const sharedClients = splitSdk.sharedClients; - const destroyPromises = Object.keys(sharedClients).map((clientKey) => sharedClients[clientKey].destroy()); - destroyPromises.push(mainClient.destroy()); + const destroyPromise = splitSdk.factory.destroy(); // Add onDestroy callback listener. It is important for server-side, where the thunk action is not dispatched // and so the user cannot access the promise as follows: `store.dispatch(destroySplitSdk()).then(...)` let dispatched = false; - if (params.onDestroy) Promise.all(destroyPromises).then(() => { + if (params.onDestroy) destroyPromise.then(() => { // condition to avoid calling the callback twice, since it should be called preferably after the action has been dispatched if (!dispatched) params.onDestroy(); }); @@ -310,7 +304,7 @@ export function destroySplitSdk(params: IDestroySplitSdkParams = {}): (dispatch: // Return Thunk (async) action return (dispatch: Dispatch): Promise => { dispatched = true; - return Promise.all(destroyPromises).then(function () { + return destroyPromise.then(function () { dispatch(splitDestroy(__getStatus(mainClient).lastUpdate)); if (params.onDestroy) params.onDestroy(); }); diff --git a/src/constants.ts b/src/constants.ts index a2d00cb..f0e9eaa 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,13 +15,6 @@ export const CONTROL_WITH_CONFIG: SplitIO.TreatmentWithConfig = { config: null, }; -export const getControlTreatmentsWithConfig = (featureFlagNames: string[]): SplitIO.TreatmentsWithConfig => { - return featureFlagNames.reduce((pValue: SplitIO.TreatmentsWithConfig, cValue: string) => { - pValue[cValue] = CONTROL_WITH_CONFIG; - return pValue; - }, {}); -}; - // Action types export const SPLIT_READY = 'SPLIT_READY'; diff --git a/src/helpers.ts b/src/helpers.ts index 4b658ac..d9a1c09 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -8,7 +8,7 @@ import { initialStatus } from './reducer'; * This function track events, i.e., it invokes the actual `client.track*` methods. * This function is not an action creator, but rather a simple access to `client.track()`. * - * @param {ITrackParams} params + * @param params - Parameter object to track an event. * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#track} */ @@ -17,30 +17,26 @@ export function track(params: ITrackParams): boolean { console.error(ERROR_TRACK_NO_INITSPLITSDK); return false; } - const trackParams = [params.eventType, params.value, params.properties]; - let client; // Client getting variates depending on browser or node. - if (splitSdk.isDetached) { // Node - // In node, user must always provide key and TT as params - client = splitSdk.factory.client(); - trackParams.unshift(params.key, params.trafficType); + const { key, trafficType, eventType, value, properties } = params; + + if (splitSdk.isDetached) { // Node.js + // In Node.js, user must always provide key and TT as params + const client = splitSdk.factory.client() as SplitIO.IClient; + + return client.track(key, trafficType, eventType, value, properties); } else { // Browser // client is a shared or main client whether or not the key is provided - client = getClient(splitSdk, params.key); + const client = getClient(splitSdk, params.key); - // TT is required if the key is provided (shared client) or if not present in config (main client) - if (params.key || !(splitSdk.config.core as SplitIO.IBrowserSettings['core']).trafficType) { - trackParams.unshift(params.trafficType); - } + return client.track(trafficType, eventType, value, properties); } - - return client.track(...trackParams as [string, any]); } /** * Gets the array of feature flag names. * - * @returns {string[]} The list of feature flag names. The list might be empty if the SDK was not initialized or if it's not ready yet. + * @returns The list of feature flag names. The list might be empty if the SDK was not initialized or if it's not ready yet. * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#manager} */ @@ -56,8 +52,8 @@ export function getSplitNames(): string[] { /** * Gets the data of a split in SplitView format. * - * @param {string} featureFlagName The name of the split we wan't to get info of. - * @returns {SplitView} The SplitIO.SplitView of the given split, or null if split does not exist or the SDK was not initialized or is not ready. + * @param featureFlagName - The name of the feature flag we wan't to get info of. + * @returns The SplitIO.SplitView of the given split, or null if split does not exist or the SDK was not initialized or is not ready. * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#manager} */ @@ -73,7 +69,7 @@ export function getSplit(featureFlagName: string): SplitIO.SplitView { /** * Gets the array of feature flags data in SplitView format. * - * @returns {SplitViews} The list of SplitIO.SplitView. The list might be empty if the SDK was not initialized or if it's not ready yet + * @returns The list of SplitIO.SplitView. The list might be empty if the SDK was not initialized or if it's not ready yet * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#manager} */ @@ -92,10 +88,10 @@ export function getSplits(): SplitIO.SplitViews { * This function is similar to the `selectStatus` selector, but it does not require the Split state as a parameter since it uses the global `splitSdk` object. * Consider using the `selectStatus` selector instead for a more Redux-friendly approach. * - * @param {SplitIO.SplitKey} key To use only on client-side. Ignored in server-side. If a key is provided and a client associated to that key has been used, the status of that client is returned. + * @param key - To use only on client-side. Ignored in server-side. If a key is provided and a client associated to that key has been used, the status of that client is returned. * If no key is provided, the status of the main client and manager is returned (the main client shares the status with the manager). * - * @returns {IStatus} The status of the SDK client or manager. + * @returns The status of the SDK client or manager. * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#subscribe-to-events} */ diff --git a/src/react-redux/connectSplit.ts b/src/react-redux/connectSplit.ts index b0d48e1..3398c1a 100644 --- a/src/react-redux/connectSplit.ts +++ b/src/react-redux/connectSplit.ts @@ -8,7 +8,7 @@ import { defaultGetSplitState } from '../selectors'; * - The Split state at Redux, under the prop key `split`. * - The action creator `getTreatments`, bound to the `dispatch` of your store. * - * @param {IGetSplitState} getSplitState optional function that takes the entire Redux state and returns + * @param getSplitState - Optional function that takes the entire Redux state and returns * the state slice which corresponds to where the Split reducer was mounted. This functionality is rarely * needed, and defaults to assuming that the reducer is mounted under the `splitio` key. */ diff --git a/src/react-redux/connectToggler.ts b/src/react-redux/connectToggler.ts index e0997ab..3c072fa 100644 --- a/src/react-redux/connectToggler.ts +++ b/src/react-redux/connectToggler.ts @@ -8,11 +8,7 @@ const NullRenderComponent: React.ComponentType = () => null; /** * To avoid passing down dispatch property, merge props override default - * behaviour from connect. Here dispatchProps are not passing down. - * - * @param {any} stateProps - * @param {any} dispatchProps - * @param {any} ownProps + * behavior from connect. Here dispatchProps are not passing down. */ const mergeProps = (stateProps: any, dispatchProps: any, ownProps: any) => ({ ...stateProps, @@ -20,11 +16,8 @@ const mergeProps = (stateProps: any, dispatchProps: any, ownProps: any) => ({ }); /** - * This toggler just returns a react component that decides which component to render + * This toggler just returns a react component that decides which component to render * regarding it props. - * - * @param {React.ComponentType} ComponentOn - * @param {React.ComponentType} ComponentDefault */ const toggler = (ComponentOn: React.ComponentType, ComponentDefault: React.ComponentType = NullRenderComponent) => ({ isFeatureOn, ...props }: { isFeatureOn: boolean }) => @@ -34,9 +27,9 @@ const toggler = (ComponentOn: React.ComponentType, ComponentDefault: React.Compo * Looks on the features of the Split piece of state, and maps to isFeatureOn * depending if this feature is ON or not * - * @param {string} featureFlagName feature flag name - * @param {SplitIO.SplitKey} key user key - * @param {IGetSplitState} getSplitState function that extract the Split piece of state from the Redux state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param getSplitState - Function that extract the Split piece of state from the Redux state. */ export function mapIsFeatureOnToProps(featureFlagName: string, key?: SplitIO.SplitKey, getSplitState: IGetSplitState = defaultGetSplitState) { return (state: any) => { @@ -51,9 +44,9 @@ export function mapIsFeatureOnToProps(featureFlagName: string, key?: SplitIO.Spl * Looks on the features of the Split piece of state, and maps to feature * the value of this feature * - * @param {string} featureFlagName feature flag name - * @param {SplitIO.SplitKey} key user key - * @param {IGetSplitState} getSplitState function that extract the Split piece of state from the Redux state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param getSplitState - Function that extract the Split piece of state from the Redux state. */ export function mapTreatmentToProps(featureFlagName: string, key?: SplitIO.SplitKey, getSplitState: IGetSplitState = defaultGetSplitState): (state: any) => { feature: string } { return (state: any) => { @@ -71,9 +64,9 @@ export function mapTreatmentToProps(featureFlagName: string, key?: SplitIO.Split * * So connect send the global state and the toggler decide which to render * - * @param {string} featureFlagtName feature flag name - * @param {SplitIO.SplitKey} key user key - * @param {IGetSplitState} getSplitState function that extract the Split piece of state from the Redux state. + * @param featureFlagtName - The feature flag name. + * @param key - The user key. + * @param getSplitState - Function that extract the Split piece of state from the Redux state. */ export function connectToggler(featureFlagName: string, key?: SplitIO.SplitKey, getSplitState: IGetSplitState = defaultGetSplitState) { return (ComponentOn: React.ComponentType, ComponentDefault?: React.ComponentType) => diff --git a/src/selectors.ts b/src/selectors.ts index ed1411e..1f19da6 100644 --- a/src/selectors.ts +++ b/src/selectors.ts @@ -12,10 +12,10 @@ export const defaultGetSplitState = getStateSlice(DEFAULT_SPLIT_STATE_SLICE); * If a treatment is not found, it returns the default value, which is `'control'` if not specified. * A treatment is not found if an invalid Split state is passed or if a `getTreatments` action has not been dispatched for the provided feature flag name and key. * - * @param {ISplitState} splitState - * @param {string} featureFlagName - * @param {SplitIO.SplitKey} key - * @param {string} defaultValue + * @param splitState - The Split piece of state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param defaultValue - The default value to return if the treatment is not found. */ export function selectTreatmentValue(splitState: ISplitState, featureFlagName: string, key?: SplitIO.SplitKey, defaultValue: string = CONTROL): string { return selectTreatmentWithConfig(splitState, featureFlagName, key, { treatment: defaultValue, config: null }).treatment; @@ -26,10 +26,10 @@ export function selectTreatmentValue(splitState: ISplitState, featureFlagName: s * If a treatment is not found, it returns the default value, which is `{ treatment: 'control', configuration: null }` if not specified. * A treatment is not found if an invalid Split state is passed or if a `getTreatments` action has not been dispatched for the provided feature flag name and key. * - * @param {ISplitState} splitState - * @param {string} featureFlagName - * @param {SplitIO.SplitKey} key - * @param {SplitIO.TreatmentWithConfig} defaultValue + * @param splitState - The Split piece of state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param defaultValue - The default value to return if the treatment is not found. */ export function selectTreatmentWithConfig(splitState: ISplitState, featureFlagName: string, key?: SplitIO.SplitKey, defaultValue: SplitIO.TreatmentWithConfig = CONTROL_WITH_CONFIG): SplitIO.TreatmentWithConfig { const splitTreatments = splitState && splitState.treatments ? splitState.treatments[featureFlagName] : console.error(ERROR_SELECTOR_NO_SPLITSTATE); @@ -48,10 +48,10 @@ export function selectTreatmentWithConfig(splitState: ISplitState, featureFlagNa * If a treatment is not found, it returns the default value, which is `'control'` if not specified. * A treatment is not found if an invalid Split state is passed or if a `getTreatments` action has not been dispatched for the provided feature flag name and key. * - * @param {ISplitState} splitState - * @param {string} featureFlagName - * @param {SplitIO.SplitKey} key - * @param {string} defaultValue + * @param splitState - The Split piece of state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param defaultValue - The default value to return if the treatment is not found. */ export function selectTreatmentAndStatus(splitState: ISplitState, featureFlagName: string, key?: SplitIO.SplitKey, defaultValue: string = CONTROL): { treatment: string @@ -66,10 +66,10 @@ export function selectTreatmentAndStatus(splitState: ISplitState, featureFlagNam * If a treatment is not found, it returns the default value as treatment, which is `{ treatment: 'control', configuration: null }` if not specified. * A treatment is not found if an invalid Split state is passed or if a `getTreatments` action has not been dispatched for the provided feature flag name and key. * - * @param {ISplitState} splitState - * @param {string} featureFlagName - * @param {SplitIO.SplitKey} key - * @param {SplitIO.TreatmentWithConfig} defaultValue + * @param splitState - The Split piece of state. + * @param featureFlagName - The feature flag name. + * @param key - The user key. + * @param defaultValue - The default value to return if the treatment is not found. */ export function selectTreatmentWithConfigAndStatus(splitState: ISplitState, featureFlagName: string, key?: SplitIO.SplitKey, defaultValue: SplitIO.TreatmentWithConfig = CONTROL_WITH_CONFIG): { treatment: SplitIO.TreatmentWithConfig @@ -87,11 +87,11 @@ export function selectTreatmentWithConfigAndStatus(splitState: ISplitState, feat /** * Extracts an object with the status properties of the SDK client or manager from the Split state. * - * @param {ISplitState} splitState - * @param {SplitIO.SplitKey} key To use only on client-side. Ignored in server-side. If a key is provided and a client associated to that key has been used, the status of that client is returned. + * @param splitState - The Split piece of state. + * @param key - To use only on client-side. Ignored in server-side. If a key is provided and a client associated to that key has been used, the status of that client is returned. * If no key is provided, the status of the main client and manager is returned (the main client shares the status with the manager). * - * @returns {IStatus} The status of the SDK client or manager. + * @returns The status of the SDK client or manager. * * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#subscribe-to-events} */ diff --git a/src/types.ts b/src/types.ts index 4b2d6ab..7c10731 100644 --- a/src/types.ts +++ b/src/types.ts @@ -77,7 +77,7 @@ export interface IInitSplitSdkParams { config: SplitIO.IBrowserSettings | SplitIO.INodeSettings; /** - * Optional param to provide a Split factory initializer to use instead of SplitFactory from '@splitsoftware/splitio'. + * Optional param to provide a Split factory initializer to use instead of SplitFactory from `'@splitsoftware/splitio'`. * It can be useful when the Split factory is imported from the UMD bundle in a HTML script. */ splitio?: ISplitFactoryBuilder; @@ -109,7 +109,7 @@ export interface IInitSplitSdkParams { export type IGetTreatmentsParams = { /** - * user key used to evaluate. It is mandatory for node but optional for browser. If not provided in browser, + * user key used to evaluate. It is mandatory for Node.js but optional for browser. If not provided in browser, * it defaults to the key defined in the SDK config, i.e., the config object passed to `initSplitSdk`. */ key?: SplitIO.SplitKey; @@ -122,17 +122,19 @@ export type IGetTreatmentsParams = { /** * This param indicates to re-evaluate the feature flags if the SDK is updated. For example, a `true` value might be - * the desired behaviour for permission toggles or operation toggles, such as a kill switch, that you want to - * inmediately reflect in your app. A `false` value might be useful for experiment or release toggles, where - * you want to keep the treatment unchanged during the sesion of the user. - * @default false + * the desired behavior for permission toggles or operation toggles, such as a kill switch, that you want to + * immediately reflect in your app. A `false` value might be useful for experiment or release toggles, where + * you want to keep the treatment unchanged during the session of the user. + * + * @defaultValue `false` */ evalOnUpdate?: boolean; /** * This param indicates to evaluate the feature flags if the SDK is ready from cache (i.e., it emits SDK_READY_FROM_CACHE event). * This params is only relevant when using 'LOCALSTORAGE' as storage type, since otherwise the event is never emitted. - * @default false + * + * @defaultValue `false` */ evalOnReadyFromCache?: boolean; } & ({ @@ -167,16 +169,15 @@ export interface IDestroySplitSdkParams { export interface ITrackParams { /** - * user key used to track event. It is mandatory for node but optional for browser. If not provided in browser, + * user key used to track event. It is mandatory for Node.js but optional for browser. If not provided in browser, * it defaults to the key defined in the SDK config object. */ key?: SplitIO.SplitKey; /** - * the traffic type of the key in the track call. If not provided, it defaults to the traffic type defined in the SDK - * config object. If not provided either in the SDK setting, the function logs an error message and returns false. + * the traffic type of the key in the track call. If not provided, the function logs an error message and returns false. */ - trafficType?: string; + trafficType: string; /** * The event type that this event should correspond to. The expected data type is String. @@ -194,7 +195,7 @@ export interface ITrackParams { properties?: SplitIO.Properties; } -export type ISplitFactoryBuilder = (settings: SplitIO.IBrowserSettings | SplitIO.INodeSettings) => SplitIO.ISDK; +export type ISplitFactoryBuilder = ((settings: SplitIO.IBrowserSettings) => SplitIO.IBrowserSDK) | ((settings: SplitIO.INodeSettings) => SplitIO.ISDK); export type ISplitAction = { type: string; diff --git a/src/utils.ts b/src/utils.ts index ca53411..9e09fbc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -52,7 +52,7 @@ export interface IClientStatus { } // The following util might be removed in the future, if the JS SDK extends its public API with a "getStatus" method -export function __getStatus(client: SplitIO.IClient): IClientStatus { +export function __getStatus(client: SplitIO.IBasicClient): IClientStatus { // @ts-expect-error, function exists but it is not part of JS SDK type definitions return client.__getStatus(); } diff --git a/tsconfig.json b/tsconfig.json index 52a0e77..147dcc1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "sourceMap": false, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "es", /* Redirect output structure to the directory. */ + "outDir": "esm", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ diff --git a/version_replace.sh b/version_replace.sh index fc9d0db..f801b9c 100755 --- a/version_replace.sh +++ b/version_replace.sh @@ -2,7 +2,7 @@ VERSION=$(node -e "(function () { console.log(require('./package.json').version) })()") -replace 'REDUX_SDK_VERSION_NUMBER' $VERSION ./lib/constants.js ./es/constants.js +replace 'REDUX_SDK_VERSION_NUMBER' $VERSION ./cjs/constants.js ./esm/constants.js if [ $? -eq 0 ] then