diff --git a/CHANGES.txt b/CHANGES.txt index fde5f24..365c02b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,12 @@ +1.10.0 (December 18, 2023) + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): + - Added a new optional `flagSets` property to the param object of the `getTreatments` action creator, to support evaluating flags in given flag set/s. Either `splitNames` or `flagSets` must be provided to the function. If both are provided, `splitNames` will be used. + - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. + - Added `sets` property to the `SplitView` object returned by the `getSplit` and `getSplits` helper functions to expose flag sets on flag views. + - Added `defaultTreatment` property to the `SplitView` object returned by the `getSplit` and `getSplits` helper functions (Related to issue https://github.com/splitio/javascript-commons/issues/225). + - Updated `getTreatments` action creator to validate the provided params object, in order to log a descriptive error when an invalid object is provided rather than throwing a cryptic error. + - Updated @splitsoftware/splitio package to version 10.24.1 that includes flag sets support, vulnerability fixes and other improvements. + 1.9.0 (July 18, 2023) - Updated some transitive dependencies for vulnerability fixes. - Updated @splitsoftware/splitio package to version 10.23.0 that includes: @@ -68,7 +77,7 @@ - Updated Split's SDK dependency to fix vulnerabilities. 1.3.0 (December 9, 2020) - - Added a new parameter to `getTreatments` actions creator: `evalOnReadyFromCache` to evaluate feature flags when the SDK_READY_FROM_CACHE event is emitted. Learn more in our Redux SDK documentation. + - Added a new parameter to `getTreatments` action creator: `evalOnReadyFromCache` to evaluate feature flags when the SDK_READY_FROM_CACHE event is emitted. Learn more in our Redux SDK documentation. - Updated how feature flag evaluations are handled on SDK_READY, SDK_READY_FROM_CACHE and SDK_UPDATE events, to dispatch a single action with evaluations that results in all treatments updates in the state at once, instead of having multiple actions that might lead to multiple store notifications. - Updated some NPM dependencies for vulnerability fixes. diff --git a/package-lock.json b/package-lock.json index e6b0d63..8fb3d00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-redux", - "version": "1.9.0", + "version": "1.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-redux", - "version": "1.9.0", + "version": "1.10.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio": "10.23.0" + "@splitsoftware/splitio": "10.24.1" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", @@ -51,18 +51,19 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", - "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -126,28 +127,20 @@ } }, "node_modules/@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", @@ -175,39 +168,35 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -305,21 +294,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -349,13 +347,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -363,9 +361,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -549,32 +547,33 @@ } }, "node_modules/@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -583,12 +582,13 @@ } }, "node_modules/@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1396,10 +1396,58 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@mdn/browser-compat-data": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.4.tgz", - "integrity": "sha512-zzSR4lk5i9YSSQtQwZpSYK84xrSCPtrtU6F6WkYcchOYwkOiJXezpiaS4GnKFmafFcxCXNI2wPrsCoj2OO0VrA==", + "version": "5.3.28", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.28.tgz", + "integrity": "sha512-vC+UDAsQti7Cv2oBahPfgnTXT7n0XZk8e7UFucNMmkauszdiiEsNFI0elmMMrh2u+IaMOvAAHo3DDzMx7y80Cw==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -1456,11 +1504,11 @@ } }, "node_modules/@splitsoftware/splitio": { - "version": "10.23.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.23.0.tgz", - "integrity": "sha512-b9mn2B8U1DfpDETsaWH4T1jhkn8XWwlAVsHwhgIRhCgBs0B9wm4SsXx+OWHZ5bl5uvEwtFFIAtCU58j/irnqpw==", + "version": "10.24.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.24.1.tgz", + "integrity": "sha512-WzVZrP2IAqzNBywNXgmLxiS60qumkcnu6u1lUPlNgdVek82TzWeqyqW+htKmDMJ/ifsJPWrgT1VLMZJvOnBsVA==", "dependencies": { - "@splitsoftware/splitio-commons": "1.9.0", + "@splitsoftware/splitio-commons": "1.12.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -1478,9 +1526,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.0.tgz", - "integrity": "sha512-2QoWvGOk/LB+q2TglqGD0w/hcUKG4DZwBSt5NtmT1ODGiLyCf2wbcfG/eBR9QlUnLisJ62dj6vOQsVUB2kiHOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1.tgz", + "integrity": "sha512-EkCcqlYvVafazs9c5i+pmhf6rIyj3A70dqQ4U3BKE646t7tf6mxGzqZz1sAl540xNyYI7CA/iIqisEWvDtJc0A==", "dependencies": { "tslib": "^2.3.1" }, @@ -1494,9 +1542,9 @@ } }, "node_modules/@splitsoftware/splitio-commons/node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@testing-library/dom": { "version": "9.0.1", @@ -1703,12 +1751,6 @@ "node": ">= 6" } }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, "node_modules/@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -2716,9 +2758,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -2728,13 +2770,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2835,9 +2881,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001516", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", - "integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", + "version": "1.0.30001559", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", + "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", "dev": true, "funding": [ { @@ -3240,9 +3286,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.330", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.330.tgz", - "integrity": "sha512-PqyefhybrVdjAJ45HaPLtuVaehiSw7C3ya0aad+rvmV53IVyXmYRk3pwIOb2TxTDTnmgQdn46NjMMaysx79/6Q==", + "version": "1.4.574", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.574.tgz", + "integrity": "sha512-bg1m8L0n02xRzx4LsTTMbBPiUd9yIR+74iPtS/Ao65CuXvhVZHP0ym1kSdDG3yHFDXqHQQBKujlN1AQ8qZnyFg==", "dev": true }, "node_modules/emittery": { @@ -3533,19 +3579,18 @@ } }, "node_modules/eslint-plugin-compat": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.1.4.tgz", - "integrity": "sha512-RxySWBmzfIROLFKgeJBJue2BU/6vM2KJWXWAUq+oW4QtrsZXRxbjgxmO1OfF3sHcRuuIenTS/wgo3GyUWZF24w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz", + "integrity": "sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==", "dev": true, "dependencies": { - "@mdn/browser-compat-data": "^5.2.47", - "@tsconfig/node14": "^1.0.3", + "@mdn/browser-compat-data": "^5.3.13", "ast-metadata-inferer": "^0.8.0", - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001473", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001524", "find-up": "^5.0.0", - "lodash.memoize": "4.1.2", - "semver": "7.3.8" + "lodash.memoize": "^4.1.2", + "semver": "^7.5.4" }, "engines": { "node": ">=14.x" @@ -3616,9 +3661,9 @@ } }, "node_modules/eslint-plugin-compat/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7893,9 +7938,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-package-data": { @@ -9564,9 +9609,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -9576,6 +9621,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -9583,7 +9632,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -9963,18 +10012,19 @@ }, "dependencies": { "@adobe/css-tools": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", - "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -10021,22 +10071,15 @@ } }, "@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.15.4", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" } }, "@babel/helper-compilation-targets": { @@ -10059,33 +10102,29 @@ } } }, - "@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" - } + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true }, - "@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -10159,18 +10198,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.15.4" + "@babel/types": "^7.22.5" } }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -10191,20 +10236,20 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz", - "integrity": "sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -10334,40 +10379,42 @@ } }, "@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.15.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", - "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.9", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -10968,10 +11015,49 @@ } } }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@mdn/browser-compat-data": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.4.tgz", - "integrity": "sha512-zzSR4lk5i9YSSQtQwZpSYK84xrSCPtrtU6F6WkYcchOYwkOiJXezpiaS4GnKFmafFcxCXNI2wPrsCoj2OO0VrA==", + "version": "5.3.28", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.28.tgz", + "integrity": "sha512-vC+UDAsQti7Cv2oBahPfgnTXT7n0XZk8e7UFucNMmkauszdiiEsNFI0elmMMrh2u+IaMOvAAHo3DDzMx7y80Cw==", "dev": true }, "@nodelib/fs.scandir": { @@ -11019,11 +11105,11 @@ } }, "@splitsoftware/splitio": { - "version": "10.23.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.23.0.tgz", - "integrity": "sha512-b9mn2B8U1DfpDETsaWH4T1jhkn8XWwlAVsHwhgIRhCgBs0B9wm4SsXx+OWHZ5bl5uvEwtFFIAtCU58j/irnqpw==", + "version": "10.24.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-10.24.1.tgz", + "integrity": "sha512-WzVZrP2IAqzNBywNXgmLxiS60qumkcnu6u1lUPlNgdVek82TzWeqyqW+htKmDMJ/ifsJPWrgT1VLMZJvOnBsVA==", "requires": { - "@splitsoftware/splitio-commons": "1.9.0", + "@splitsoftware/splitio-commons": "1.12.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -11035,17 +11121,17 @@ } }, "@splitsoftware/splitio-commons": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.9.0.tgz", - "integrity": "sha512-2QoWvGOk/LB+q2TglqGD0w/hcUKG4DZwBSt5NtmT1ODGiLyCf2wbcfG/eBR9QlUnLisJ62dj6vOQsVUB2kiHOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1.tgz", + "integrity": "sha512-EkCcqlYvVafazs9c5i+pmhf6rIyj3A70dqQ4U3BKE646t7tf6mxGzqZz1sAl540xNyYI7CA/iIqisEWvDtJc0A==", "requires": { "tslib": "^2.3.1" }, "dependencies": { "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -11201,12 +11287,6 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, "@types/aria-query": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", @@ -11986,15 +12066,15 @@ "dev": true }, "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" } }, "bs-logger": { @@ -12070,9 +12150,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001516", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", - "integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", + "version": "1.0.30001559", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", + "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", "dev": true }, "chalk": { @@ -12392,9 +12472,9 @@ } }, "electron-to-chromium": { - "version": "1.4.330", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.330.tgz", - "integrity": "sha512-PqyefhybrVdjAJ45HaPLtuVaehiSw7C3ya0aad+rvmV53IVyXmYRk3pwIOb2TxTDTnmgQdn46NjMMaysx79/6Q==", + "version": "1.4.574", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.574.tgz", + "integrity": "sha512-bg1m8L0n02xRzx4LsTTMbBPiUd9yIR+74iPtS/Ao65CuXvhVZHP0ym1kSdDG3yHFDXqHQQBKujlN1AQ8qZnyFg==", "dev": true }, "emittery": { @@ -12862,19 +12942,18 @@ } }, "eslint-plugin-compat": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.1.4.tgz", - "integrity": "sha512-RxySWBmzfIROLFKgeJBJue2BU/6vM2KJWXWAUq+oW4QtrsZXRxbjgxmO1OfF3sHcRuuIenTS/wgo3GyUWZF24w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz", + "integrity": "sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==", "dev": true, "requires": { - "@mdn/browser-compat-data": "^5.2.47", - "@tsconfig/node14": "^1.0.3", + "@mdn/browser-compat-data": "^5.3.13", "ast-metadata-inferer": "^0.8.0", - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001473", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001524", "find-up": "^5.0.0", - "lodash.memoize": "4.1.2", - "semver": "7.3.8" + "lodash.memoize": "^4.1.2", + "semver": "^7.5.4" }, "dependencies": { "find-up": { @@ -12915,9 +12994,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -15846,9 +15925,9 @@ "dev": true }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "normalize-package-data": { @@ -17093,9 +17172,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "requires": { "escalade": "^3.1.1", diff --git a/package.json b/package.json index f22f007..a27c6a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-redux", - "version": "1.9.0", + "version": "1.10.0", "description": "A library to easily use Split JS SDK with Redux and React Redux", "main": "lib/index.js", "module": "es/index.js", @@ -59,7 +59,7 @@ }, "homepage": "https://github.com/splitio/redux-client#readme", "dependencies": { - "@splitsoftware/splitio": "10.23.0" + "@splitsoftware/splitio": "10.24.1" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", diff --git a/src/__tests__/actions.browser.test.ts b/src/__tests__/actions.browser.test.ts index 432edd8..1c114cb 100644 --- a/src/__tests__/actions.browser.test.ts +++ b/src/__tests__/actions.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_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, getControlTreatmentsWithConfig, ERROR_GETT_NO_PARAM_OBJECT, } from '../constants'; /** Test targets */ @@ -160,6 +160,24 @@ describe('getTreatments', () => { expect(store.getActions().length).toBe(0); }); + it('logs error and dispatches a no-op async action if the provided param is invalid', async () => { + // Init SDK and set ready + const store = mockStore(STATE_INITIAL); + const actionResult = store.dispatch(initSplitSdk({ config: sdkBrowserConfig })); + (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY); + await actionResult; + + const consoleLogSpy = jest.spyOn(console, 'log'); + store.clearActions(); + + // @ts-expect-error testing invalid input + store.dispatch(getTreatments()); + + expect(store.getActions().length).toBe(0); + expect(consoleLogSpy).toBeCalledWith(ERROR_GETT_NO_PARAM_OBJECT); + consoleLogSpy.mockRestore(); + }); + it('dispatches an ADD_TREATMENTS action if Split SDK is ready', (done) => { // Init SDK and set ready @@ -169,13 +187,20 @@ describe('getTreatments', () => { actionResult.then(() => { store.dispatch(getTreatments({ splitNames: 'split1' })); + store.dispatch(getTreatments({ flagSets: 'set1' })); - const action = store.getActions()[1]; - expect(action.type).toBe(ADD_TREATMENTS); - expect(action.payload.key).toBe(sdkBrowserConfig.core.key); + const actions = [store.getActions()[1], store.getActions()[2]]; + actions.forEach(action => { + expect(action.type).toBe(ADD_TREATMENTS); + expect(action.payload.key).toBe(sdkBrowserConfig.core.key); + }); // getting the evaluation result and validating it matches the results from SDK - expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments); + expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(['split1'], undefined); + expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(actions[0].payload.treatments); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(['set1'], undefined); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveLastReturnedWith(actions[1].payload.treatments); + expect(getClient(splitSdk).evalOnUpdate).toEqual({}); expect(getClient(splitSdk).evalOnReady.length).toEqual(0); @@ -238,10 +263,16 @@ describe('getTreatments', () => { // The first ADD_TREATMENTS actions is dispatched again, but this time is registered for 'evalOnUpdate' store.dispatch(getTreatments({ splitNames: 'split1', evalOnUpdate: true })); + // Dispatch another ADD_TREATMENTS action with flag sets + store.dispatch(getTreatments({ flagSets: 'set1', evalOnUpdate: true })); // Validate action and registered callback expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(5); - expect(Object.values(getClient(splitSdk).evalOnUpdate).length).toBe(1); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledTimes(1); + expect(getClient(splitSdk).evalOnUpdate).toEqual({ + 'flag::split1': { evalOnUpdate: true, flagSets: undefined, splitNames: ['split1'] }, + 'set::set1': { evalOnUpdate: true, flagSets: ['set1'], splitNames: undefined } + }); done(); }); @@ -253,23 +284,25 @@ describe('getTreatments', () => { const store = mockStore(STATE_INITIAL); const actionResult = store.dispatch(initSplitSdk({ config: sdkBrowserConfig, onReadyFromCache: onReadyFromCacheCb })); store.dispatch(getTreatments({ splitNames: 'split2' })); // `evalOnUpdate` and `evalOnReadyFromCache` params are false by default + store.dispatch(getTreatments({ flagSets: 'set2' })); - // If SDK is not operational, an ADD_TREATMENTS action is dispatched with control treatments - // without calling SDK client, but the item is added to 'evalOnReady' list. - expect(store.getActions().length).toBe(1); - expect(getClient(splitSdk).evalOnReady.length).toEqual(1); - expect(getClient(splitSdk).evalOnUpdate).toEqual({}); - let action = store.getActions()[0]; - expect(action.type).toBe(ADD_TREATMENTS); - expect(action.payload.key).toBe(sdkBrowserConfig.core.key); - expect(action.payload.treatments).toEqual(getControlTreatmentsWithConfig(['split2'])); + // 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: {} } }, + ]); + // 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); + expect(getClient(splitSdk).evalOnReady.length).toEqual(2); + expect(getClient(splitSdk).evalOnUpdate).toEqual({}); // 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(2); - action = store.getActions()[1]; + expect(store.getActions().length).toBe(3); + const action = store.getActions()[2]; expect(action.type).toBe(SPLIT_READY_FROM_CACHE); } @@ -277,16 +310,19 @@ describe('getTreatments', () => { actionResult.then(() => { // The SPLIT_READY_WITH_EVALUATIONS action is dispatched if the SDK is ready and there are pending evaluations. - action = store.getActions()[2]; + const action = store.getActions()[3]; expect(action.type).toBe(SPLIT_READY_WITH_EVALUATIONS); expect(action.payload.key).toBe(sdkBrowserConfig.core.key); // getting the evaluation result and validating it matches the results from SDK const treatments = action.payload.treatments; - expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split2'], undefined); - expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(treatments); + expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledWith(['split2'], undefined); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledWith(['set2'], undefined); + expect(treatments).toEqual({ + ...(splitSdk.factory.client().getTreatmentsWithConfig as jest.Mock).mock.results[0].value, + ...(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets as jest.Mock).mock.results[0].value, + }) - expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(1); // control assertion - getTreatmentsWithConfig calls expect(getClient(splitSdk).evalOnUpdate).toEqual({}); // control assertion - cbs scheduled for update // The ADD_TREATMENTS actions is dispatched again, but this time is registered for 'evalOnUpdate' diff --git a/src/__tests__/actions.node.test.ts b/src/__tests__/actions.node.test.ts index 7e7bb3d..3d99a73 100644 --- a/src/__tests__/actions.node.test.ts +++ b/src/__tests__/actions.node.test.ts @@ -167,22 +167,27 @@ describe('getTreatments', () => { // Invoke with a feature flag name string and no attributes store.dispatch(getTreatments({ key: splitKey, splitNames: 'split1' })); + store.dispatch(getTreatments({ key: splitKey, flagSets: ['set1'] })); - let action = store.getActions()[1]; // action 0 is SPLIT_READY - expect(action.type).toBe(ADD_TREATMENTS); - expect(action.payload.key).toBe(splitKey); + const actions = [store.getActions()[1], store.getActions()[2]]; // action 0 is SPLIT_READY + actions.forEach(action => { + expect(action.type).toBe(ADD_TREATMENTS); + expect(action.payload.key).toBe(splitKey); + }); expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(splitKey, ['split1'], undefined); - expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments); + expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(actions[0].payload.treatments); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(splitKey, ['set1'], undefined); + expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveLastReturnedWith(actions[1].payload.treatments); // Invoke with a list of feature flag names and a attributes object const featureFlagNames = ['split1', 'split2']; const attributes = { att1: 'att1' }; - store.dispatch(getTreatments({ key: splitKey, splitNames: featureFlagNames, attributes })); + store.dispatch(getTreatments({ key: 'other_user', splitNames: featureFlagNames, attributes })); - action = store.getActions()[2]; + const action = store.getActions()[3]; expect(action.type).toBe(ADD_TREATMENTS); - expect(action.payload.key).toBe(splitKey); - expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(splitKey, featureFlagNames, attributes); + expect(action.payload.key).toBe('other_user'); + expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith('other_user', featureFlagNames, attributes); expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments); } diff --git a/src/__tests__/helpers.browser.test.ts b/src/__tests__/helpers.browser.test.ts index 5c1267d..64e4f30 100644 --- a/src/__tests__/helpers.browser.test.ts +++ b/src/__tests__/helpers.browser.test.ts @@ -29,6 +29,8 @@ const featureFlagViews: SplitIO.SplitViews = [ treatments: ['on', 'off'], changeNumber: 0, configs: { on: null, off: null }, + sets: [], + defaultTreatment: 'off', }, { name: 'split_2', trafficType: 'user', @@ -36,6 +38,8 @@ const featureFlagViews: SplitIO.SplitViews = [ treatments: ['on', 'off'], changeNumber: 0, configs: { on: null, off: null }, + sets: [], + defaultTreatment: 'off', }, ]; diff --git a/src/__tests__/helpers.node.test.ts b/src/__tests__/helpers.node.test.ts index e5807c8..cd1db86 100644 --- a/src/__tests__/helpers.node.test.ts +++ b/src/__tests__/helpers.node.test.ts @@ -29,6 +29,8 @@ const featureFlagViews: SplitIO.SplitViews = [ treatments: ['on', 'off'], changeNumber: 0, configs: { on: null, off: null }, + sets: [], + defaultTreatment: 'off', }, { name: 'split_2', trafficType: 'user', @@ -36,6 +38,8 @@ const featureFlagViews: SplitIO.SplitViews = [ treatments: ['on', 'off'], changeNumber: 0, configs: { on: null, off: null }, + sets: [], + defaultTreatment: 'off', }, ]; diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts new file mode 100644 index 0000000..a6ff13b --- /dev/null +++ b/src/__tests__/index.test.ts @@ -0,0 +1,46 @@ +import { + splitReducer as exportedSplitReducer, + initSplitSdk as exportedInitSplitSdk, + getTreatments as exportedGetTreatments, + destroySplitSdk as exportedDestroySplitSdk, + splitSdk as exportedSplitSdk, + track as exportedTrack, + getSplitNames as exportedGetSplitNames, + getSplit as exportedGetSplit, + getSplits as exportedGetSplits, + selectTreatmentValue as exportedSelectTreatmentValue, + selectTreatmentWithConfig as exportedSelectTreatmentWithConfig, + connectSplit as exportedConnectSplit, + connectToggler as exportedConnectToggler, + mapTreatmentToProps as exportedMapTreatmentToProps, + mapIsFeatureOnToProps as exportedMapIsFeatureOnToProps, + /* eslint-disable @typescript-eslint/no-unused-vars */ // Checks that types are exported. Otherwise, the test would fail with a TS error. + ISplitState, +} from '../index'; + +import { splitReducer } from '../reducer'; +import { initSplitSdk, getTreatments, destroySplitSdk, splitSdk } from '../asyncActions'; +import { track, getSplitNames, getSplit, getSplits } from '../helpers'; +import { selectTreatmentValue, selectTreatmentWithConfig } from '../selectors'; +import { connectSplit } from '../react-redux/connectSplit'; +import { connectToggler, mapTreatmentToProps, mapIsFeatureOnToProps } from '../react-redux/connectToggler'; + +it('index should export modules', () => { + + expect(exportedSplitReducer).toBe(splitReducer); + expect(exportedInitSplitSdk).toBe(initSplitSdk); + expect(exportedGetTreatments).toBe(getTreatments); + expect(exportedDestroySplitSdk).toBe(destroySplitSdk); + expect(exportedSplitSdk).toBe(splitSdk); + expect(exportedTrack).toBe(track); + expect(exportedGetSplitNames).toBe(getSplitNames); + expect(exportedGetSplit).toBe(getSplit); + expect(exportedGetSplits).toBe(getSplits); + expect(exportedSelectTreatmentValue).toBe(selectTreatmentValue); + expect(exportedSelectTreatmentWithConfig).toBe(selectTreatmentWithConfig); + expect(exportedConnectSplit).toBe(connectSplit); + expect(exportedConnectToggler).toBe(connectToggler); + expect(exportedMapTreatmentToProps).toBe(mapTreatmentToProps); + expect(exportedMapIsFeatureOnToProps).toBe(mapIsFeatureOnToProps); + +}); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 8b8edd6..1748cde 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,5 +1,6 @@ /** Test targets */ -import { matching } from '../utils'; +import { WARN_FEATUREFLAGS_AND_FLAGSETS } from '../constants'; +import { matching, validateGetTreatmentsParams } from '../utils'; describe('matching', () => { @@ -31,3 +32,43 @@ describe('matching', () => { }); }); + +describe('validateGetTreatmentsParams', () => { + + it('should return a sanitized copy of the provided params object', () => { + // String values are converted to arrays + expect(validateGetTreatmentsParams({ splitNames: 'some split' })).toStrictEqual({ splitNames: ['some split'], flagSets: undefined }); + expect(validateGetTreatmentsParams({ flagSets: 'flag set' })).toStrictEqual({ splitNames: undefined, flagSets: ['flag set'] }); + + // Feature flag names are sanitized because they are used by getControlTreatmentsWithConfig function while the SDK is not ready + expect(validateGetTreatmentsParams({ splitNames: ['some split', null] })).toStrictEqual({ splitNames: ['some split'], flagSets: undefined }); + // Flag set names are not sanitized, because they are not used by Redux SDK directly + expect(validateGetTreatmentsParams({ flagSets: ['flag set', null] })).toStrictEqual({ splitNames: undefined, flagSets: ['flag set', null] }); + }); + + it('should ignore flagSets if both splitNames and flagSets are provided', () => { + const consoleSpy = jest.spyOn(console, 'log'); + + expect(validateGetTreatmentsParams({ splitNames: ['some split'], flagSets: ['flag set', null] })).toStrictEqual({ splitNames: ['some split'], flagSets: undefined }); + + expect(consoleSpy.mock.calls).toEqual([[WARN_FEATUREFLAGS_AND_FLAGSETS]]); + consoleSpy.mockRestore(); + }); + + it('should return false if splitNames and flagSets values are invalid', () => { + // Invalid values for splitNames and flagSets are converted to empty arrays + expect(validateGetTreatmentsParams({ splitNames: {} })).toStrictEqual(false); + expect(validateGetTreatmentsParams({ flagSets: {} })).toStrictEqual(false); + expect(validateGetTreatmentsParams({ splitNames: true })).toStrictEqual(false); + expect(validateGetTreatmentsParams({ flagSets: true })).toStrictEqual(false); + expect(validateGetTreatmentsParams({ splitNames: null, flagSets: null })).toStrictEqual(false); + expect(validateGetTreatmentsParams({})).toStrictEqual(false); + }); + + it('should return false if the provided param is not an object', () => { // @ts-expect-error testing invalid input + expect(validateGetTreatmentsParams()).toStrictEqual(false); + expect(validateGetTreatmentsParams([])).toStrictEqual(false); + expect(validateGetTreatmentsParams('invalid')).toStrictEqual(false); + }); + +}); diff --git a/src/__tests__/utils/mockBrowserSplitSdk.ts b/src/__tests__/utils/mockBrowserSplitSdk.ts index 872e3bf..f6c696e 100644 --- a/src/__tests__/utils/mockBrowserSplitSdk.ts +++ b/src/__tests__/utils/mockBrowserSplitSdk.ts @@ -60,6 +60,12 @@ export function mockSdk() { return acc; }, {}); }); + const getTreatmentsWithConfigByFlagSets: jest.Mock = jest.fn((flagSets) => { + return flagSets.reduce((acc: SplitIO.TreatmentsWithConfig, flagSet: string) => { + acc[flagSet + '_feature_flag'] = { treatment: 'fakeTreatment', config: null }; + return acc; + }, {}); + }); const setAttributes: jest.Mock = jest.fn(() => { return true; }); @@ -89,6 +95,7 @@ export function mockSdk() { return Object.assign(Object.create(__emitter__), { getTreatmentsWithConfig, + getTreatmentsWithConfigByFlagSets, track, ready, destroy, diff --git a/src/__tests__/utils/mockNodeSplitSdk.ts b/src/__tests__/utils/mockNodeSplitSdk.ts index a346f20..a5eee41 100644 --- a/src/__tests__/utils/mockNodeSplitSdk.ts +++ b/src/__tests__/utils/mockNodeSplitSdk.ts @@ -27,6 +27,12 @@ function mockClient() { return acc; }, {}); }); + const getTreatmentsWithConfigByFlagSets: jest.Mock = jest.fn((key, flagSets) => { + return flagSets.reduce((acc: SplitIO.TreatmentsWithConfig, flagSet: string) => { + acc[flagSet + '_feature_flag'] = { treatment: 'fakeTreatment', config: null }; + return acc; + }, {}); + }); const ready: jest.Mock = jest.fn(() => { return promiseWrapper(new Promise((res, rej) => { __isReady__ ? res() : __emitter__.on(Event.SDK_READY, res); @@ -47,6 +53,7 @@ function mockClient() { return Object.assign(Object.create(__emitter__), { getTreatmentsWithConfig, + getTreatmentsWithConfigByFlagSets, track, ready, destroy, diff --git a/src/asyncActions.ts b/src/asyncActions.ts index fa60d6c..7a3b828 100644 --- a/src/asyncActions.ts +++ b/src/asyncActions.ts @@ -4,7 +4,7 @@ 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 { matching, getStatus } from './utils'; +import { matching, getStatus, validateGetTreatmentsParams } from './utils'; /** * Internal object SplitSdk. This object should not be accessed or @@ -87,11 +87,17 @@ export function initSplitSdk(params: IInitSplitSdkParams): (dispatch: Dispatch { - return { ...acc, ...client.getTreatmentsWithConfig((params.splitNames as string[]), params.attributes) }; + return { + ...acc, + ...(params.splitNames ? + client.getTreatmentsWithConfig(params.splitNames as string[], params.attributes) : + client.getTreatmentsWithConfigByFlagSets(params.flagSets as string[], params.attributes) + ) + }; }, {}); } @@ -108,10 +114,11 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi return () => { }; } - // Convert string feature flag name to a one item array. - if (typeof params.splitNames === 'string') { - params.splitNames = [params.splitNames]; - } + params = validateGetTreatmentsParams(params) as IGetTreatmentsParams; + if (!params) return () => { }; + + const splitNames = params.splitNames as string[]; + const flagSets = params.flagSets as string[]; if (!splitSdk.isDetached) { // Split SDK running in Browser @@ -119,12 +126,18 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi // Register or unregister the current `getTreatments` action from being re-executed on SDK_UPDATE. if (params.evalOnUpdate) { - params.splitNames.forEach((featureFlagName) => { - client.evalOnUpdate[featureFlagName] = { ...params, splitNames: [featureFlagName] }; + splitNames && splitNames.forEach((featureFlagName) => { + client.evalOnUpdate[`flag::${featureFlagName}`] = { ...params, splitNames: [featureFlagName] } as IGetTreatmentsParams; + }); + flagSets && flagSets.forEach((flagSetName) => { + client.evalOnUpdate[`set::${flagSetName}`] = { ...params, flagSets: [flagSetName] } as IGetTreatmentsParams; }); } else { - params.splitNames.forEach((featureFlagName) => { - delete client.evalOnUpdate[featureFlagName]; + splitNames && splitNames.forEach((featureFlagName) => { + delete client.evalOnUpdate[`flag::${featureFlagName}`]; + }); + flagSets && flagSets.forEach((flagSetName) => { + delete client.evalOnUpdate[`set::${flagSetName}`]; }); } @@ -146,15 +159,18 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi return 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, getControlTreatmentsWithConfig(params.splitNames)); + return addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, splitNames ? getControlTreatmentsWithConfig(splitNames) : {}); } } else { // Split SDK running in Node // Evaluate Split and return redux action. const client = splitSdk.factory.client(); - const treatments = client.getTreatmentsWithConfig(params.key, params.splitNames, params.attributes); + const treatments = splitNames ? + client.getTreatmentsWithConfig(params.key, splitNames, params.attributes) : + client.getTreatmentsWithConfigByFlagSets(params.key, flagSets, params.attributes); return addTreatments(params.key, treatments); } @@ -167,9 +183,9 @@ interface IClientNotDetached extends SplitIO.IClient { _trackingStatus?: boolean; /** * stored evaluations to execute on SDK update. It is an object because we might - * want to change the evaluation parameters (i.e. attributes) per each feature flag name. + * want to change the evaluation parameters (i.e. attributes) per each feature flag name or flag set. */ - evalOnUpdate: { [featureFlagName: string]: IGetTreatmentsParams }; + evalOnUpdate: { [name: string]: IGetTreatmentsParams }; /** * stored evaluations to execute when the SDK is ready. It is an array, so if multiple evaluations * are set with the same feature flag name, the result (i.e. treatment) of the last one is the stored one. diff --git a/src/constants.ts b/src/constants.ts index f9ba036..c62d723 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -42,12 +42,16 @@ export const SPLIT_DESTROY = 'SPLIT_DESTROY'; export const ADD_TREATMENTS = 'ADD_TREATMENTS'; // Warning and error messages -export const ERROR_GETT_NO_INITSPLITSDK = '[Error] To use "getTreatments" the SDK must be first initialized with a "initSplitSdk" action'; +export const ERROR_GETT_NO_INITSPLITSDK = '[ERROR] To use "getTreatments" the SDK must be first initialized with an "initSplitSdk" action'; -export const ERROR_DESTROY_NO_INITSPLITSDK = '[Error] To use "destroySplitSdk" the SDK must be first initialized with a "initSplitSdk" action'; +export const ERROR_DESTROY_NO_INITSPLITSDK = '[ERROR] To use "destroySplitSdk" the SDK must be first initialized with an "initSplitSdk" action'; -export const ERROR_TRACK_NO_INITSPLITSDK = '[Error] To use "track" the SDK must be first initialized with an "initSplitSdk" action'; +export const ERROR_TRACK_NO_INITSPLITSDK = '[ERROR] To use "track" the SDK must be first initialized with an "initSplitSdk" action'; -export const ERROR_MANAGER_NO_INITSPLITSDK = '[Error] To use the manager, the SDK must be first initialized with an "initSplitSdk" action'; +export const ERROR_MANAGER_NO_INITSPLITSDK = '[ERROR] To use the manager, the SDK must be first initialized with an "initSplitSdk" action'; -export const ERROR_SELECTOR_NO_SPLITSTATE = '[Error] When using selectors, "splitState" value must be a proper splitio piece of state'; +export const ERROR_SELECTOR_NO_SPLITSTATE = '[ERROR] When using selectors, "splitState" value must be a proper splitio piece of state'; + +export const ERROR_GETT_NO_PARAM_OBJECT = '[ERROR] "getTreatments" must be called with a param object containing a valid splitNames or flagSets properties'; + +export const WARN_FEATUREFLAGS_AND_FLAGSETS = '[WARN] Both splitNames and flagSets properties were provided. flagSets will be ignored'; diff --git a/src/types.ts b/src/types.ts index 9b38b8f..499354b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -95,7 +95,7 @@ export interface IInitSplitSdkParams { /** * Type of the param object passed to `getTreatments` action creator. */ -export interface IGetTreatmentsParams { +export type IGetTreatmentsParams = { /** * user key used to evaluate. It is mandatory for node but optional for browser. If not provided in browser, @@ -103,11 +103,6 @@ export interface IGetTreatmentsParams { */ key?: SplitIO.SplitKey; - /** - * feature flag name or array of feature flag names to evaluate. - */ - splitNames: string[] | string; - /** * optional map of attributes passed to the actual `client.getTreatment*` methods. * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#attribute-syntax} @@ -129,7 +124,21 @@ export interface IGetTreatmentsParams { * @default false */ evalOnReadyFromCache?: boolean; -} +} & ({ + + /** + * Feature flag name or array of feature flag names to evaluate. Either this or the `flagSets` property must be provided. If both are provided, the `flagSets` option is ignored. + */ + splitNames: string[] | string; + flagSets?: undefined; +} | { + + /** + * Feature flag set or array of feature flag sets to evaluate. Either this or the `splitNames` property must be provided. If both are provided, the `flagSets` option is ignored. + */ + flagSets: string[] | string; + splitNames?: undefined; +}) /** * Type of the param object passed to `destroySplitSdk` action creator. diff --git a/src/utils.ts b/src/utils.ts index dff2aab..0a9b1f7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,6 @@ +import { ERROR_GETT_NO_PARAM_OBJECT, WARN_FEATUREFLAGS_AND_FLAGSETS } from './constants'; +import { IGetTreatmentsParams } from './types'; + /** * Validates if a value is an object. */ @@ -29,3 +32,101 @@ export function getStatus(client: SplitIO.IClient): IClientStatus { // @ts-expect-error, function exists but it is not part of JS SDK type definitions return client.__getStatus(); } + +/** + * Validates and sanitizes the parameters passed to the "getTreatments" action creator. + * The returned object is a copy of the passed one, with the "splitNames" and "flagSets" properties converted to an array of strings. + */ +export function validateGetTreatmentsParams(params: any): IGetTreatmentsParams | false { + if (!isObject(params) || (!params.splitNames && !params.flagSets)) { + console.log(ERROR_GETT_NO_PARAM_OBJECT); + return false; + } + + let { splitNames, flagSets } = params; + + if (splitNames) { + // Feature flag names are sanitized because they are passed to the getControlTreatmentsWithConfig function. + splitNames = validateFeatureFlags(typeof splitNames === 'string' ? [splitNames] : splitNames); + if (!splitNames) { + console.log(ERROR_GETT_NO_PARAM_OBJECT); + return false; + } + + // Ignore flagSets if splitNames are provided + if (flagSets) console.log(WARN_FEATUREFLAGS_AND_FLAGSETS); + flagSets = undefined; + } else { + // Flag set names are not sanitized, because they are not used by Redux SDK directly. We just make sure it is an array. + flagSets = typeof flagSets === 'string' ? [flagSets] : flagSets; + if (!Array.isArray(flagSets)) { + console.log(ERROR_GETT_NO_PARAM_OBJECT); + return false; + } + } + + return { + ...params, + splitNames, + flagSets, + }; +} + +// The following input validation utils are based on the ones in the React SDK. They might be replaced by utils from the JS SDK in the future. + +function validateFeatureFlags(maybeFeatureFlags: unknown, listName = 'feature flag names'): false | string[] { + if (Array.isArray(maybeFeatureFlags) && maybeFeatureFlags.length > 0) { + const validatedArray: string[] = []; + // Remove invalid values + maybeFeatureFlags.forEach((maybeFeatureFlag) => { + const featureFlagName = validateFeatureFlag(maybeFeatureFlag); + if (featureFlagName) validatedArray.push(featureFlagName); + }); + + // Strip off duplicated values if we have valid feature flag names then return + if (validatedArray.length) return uniq(validatedArray); + } + + console.log(`[ERROR] ${listName} must be a non-empty array.`); + return false; +} + +const TRIMMABLE_SPACES_REGEX = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/; + +function validateFeatureFlag(maybeFeatureFlag: unknown, item = 'feature flag name'): false | string { + if (maybeFeatureFlag == undefined) { + console.log(`[ERROR] you passed a null or undefined ${item}, ${item} must be a non-empty string.`); + } else if (!isString(maybeFeatureFlag)) { + console.log(`[ERROR] you passed an invalid ${item}, ${item} must be a non-empty string.`); + } else { + if (TRIMMABLE_SPACES_REGEX.test(maybeFeatureFlag)) { + console.log(`[WARN] ${item} "${maybeFeatureFlag}" has extra whitespace, trimming.`); + maybeFeatureFlag = maybeFeatureFlag.trim(); + } + + if ((maybeFeatureFlag as string).length > 0) { + return maybeFeatureFlag as string; + } else { + console.log(`[ERROR] you passed an empty ${item}, ${item} must be a non-empty string.`); + } + } + + return false; +} + +/** + * Removes duplicate items on an array of strings. + */ +function uniq(arr: string[]): string[] { + const seen: Record = {}; + return arr.filter((item) => { + return Object.prototype.hasOwnProperty.call(seen, item) ? false : seen[item] = true; + }); +} + +/** + * Checks if a given value is a string. + */ +function isString(val: unknown): val is string { + return typeof val === 'string' || val instanceof String; +} diff --git a/types/asyncActions.d.ts b/types/asyncActions.d.ts index 2697703..1498a97 100644 --- a/types/asyncActions.d.ts +++ b/types/asyncActions.d.ts @@ -35,10 +35,10 @@ interface IClientNotDetached extends SplitIO.IClient { _trackingStatus?: boolean; /** * stored evaluations to execute on SDK update. It is an object because we might - * want to change the evaluation parameters (i.e. attributes) per each feature flag name. + * want to change the evaluation parameters (i.e. attributes) per each feature flag name or flag set. */ evalOnUpdate: { - [featureFlagName: string]: IGetTreatmentsParams; + [name: string]: IGetTreatmentsParams; }; /** * stored evaluations to execute when the SDK is ready. It is an array, so if multiple evaluations diff --git a/types/constants.d.ts b/types/constants.d.ts index 25d8ff4..9817bac 100644 --- a/types/constants.d.ts +++ b/types/constants.d.ts @@ -14,8 +14,10 @@ export declare const SPLIT_UPDATE_WITH_EVALUATIONS = "SPLIT_UPDATE_WITH_EVALUATI export declare const SPLIT_TIMEDOUT = "SPLIT_TIMEDOUT"; export declare const SPLIT_DESTROY = "SPLIT_DESTROY"; export declare const ADD_TREATMENTS = "ADD_TREATMENTS"; -export declare const ERROR_GETT_NO_INITSPLITSDK = "[Error] To use \"getTreatments\" the SDK must be first initialized with a \"initSplitSdk\" action"; -export declare const ERROR_DESTROY_NO_INITSPLITSDK = "[Error] To use \"destroySplitSdk\" the SDK must be first initialized with a \"initSplitSdk\" action"; -export declare const ERROR_TRACK_NO_INITSPLITSDK = "[Error] To use \"track\" the SDK must be first initialized with an \"initSplitSdk\" action"; -export declare const ERROR_MANAGER_NO_INITSPLITSDK = "[Error] To use the manager, the SDK must be first initialized with an \"initSplitSdk\" action"; -export declare const ERROR_SELECTOR_NO_SPLITSTATE = "[Error] When using selectors, \"splitState\" value must be a proper splitio piece of state"; +export declare const ERROR_GETT_NO_INITSPLITSDK = "[ERROR] To use \"getTreatments\" the SDK must be first initialized with an \"initSplitSdk\" action"; +export declare const ERROR_DESTROY_NO_INITSPLITSDK = "[ERROR] To use \"destroySplitSdk\" the SDK must be first initialized with an \"initSplitSdk\" action"; +export declare const ERROR_TRACK_NO_INITSPLITSDK = "[ERROR] To use \"track\" the SDK must be first initialized with an \"initSplitSdk\" action"; +export declare const ERROR_MANAGER_NO_INITSPLITSDK = "[ERROR] To use the manager, the SDK must be first initialized with an \"initSplitSdk\" action"; +export declare const ERROR_SELECTOR_NO_SPLITSTATE = "[ERROR] When using selectors, \"splitState\" value must be a proper splitio piece of state"; +export declare const ERROR_GETT_NO_PARAM_OBJECT = "[ERROR] \"getTreatments\" must be called with a param object containing a valid splitNames or flagSets properties"; +export declare const WARN_FEATUREFLAGS_AND_FLAGSETS = "[WARN] Both splitNames and flagSets properties were provided. flagSets will be ignored"; diff --git a/types/types.d.ts b/types/types.d.ts index fdfdb2f..56b5b79 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -79,16 +79,12 @@ export interface IInitSplitSdkParams { /** * Type of the param object passed to `getTreatments` action creator. */ -export interface IGetTreatmentsParams { +export declare type IGetTreatmentsParams = { /** * user key used to evaluate. It is mandatory for node 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; - /** - * feature flag name or array of feature flag names to evaluate. - */ - splitNames: string[] | string; /** * optional map of attributes passed to the actual `client.getTreatment*` methods. * @see {@link https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK#attribute-syntax} @@ -108,7 +104,19 @@ export interface IGetTreatmentsParams { * @default false */ evalOnReadyFromCache?: boolean; -} +} & ({ + /** + * Feature flag name or array of feature flag names to evaluate. Either this or the `flagSets` property must be provided. If both are provided, the `flagSets` option is ignored. + */ + splitNames: string[] | string; + flagSets?: undefined; +} | { + /** + * Feature flag set or array of feature flag sets to evaluate. Either this or the `splitNames` property must be provided. If both are provided, the `flagSets` option is ignored. + */ + flagSets: string[] | string; + splitNames?: undefined; +}); /** * Type of the param object passed to `destroySplitSdk` action creator. */ diff --git a/types/utils.d.ts b/types/utils.d.ts index 8608b7b..f37d930 100644 --- a/types/utils.d.ts +++ b/types/utils.d.ts @@ -1,3 +1,4 @@ +import { IGetTreatmentsParams } from './types'; /** * Validates if a value is an object. */ @@ -17,3 +18,8 @@ export interface IClientStatus { isDestroyed: boolean; } export declare function getStatus(client: SplitIO.IClient): IClientStatus; +/** + * Validates and sanitizes the parameters passed to the "getTreatments" action creator. + * The returned object is a copy of the passed one, with the "splitNames" and "flagSets" properties converted to an array of strings. + */ +export declare function validateGetTreatmentsParams(params: any): IGetTreatmentsParams | false;