diff --git a/CHANGES.txt b/CHANGES.txt index d88d728..4e5b2ce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,9 @@ +1.3.0 (September 22, 2025) + - Added the `InLocalStorage` export to the `@splitsoftware/splitio-react-native` package. This export can be used to configure the SDK to use `AsyncStorage` or another storage implementation to persist the SDK rollout plan data across app restarts and speed up the initialization. + - Added the `initialRolloutPlan` configuration option, to allow preloading the SDK storage with a snapshot of the rollout plan. + - Updated the application state listener to sync feature flag definitions when the app returns to the foreground and has exceeded the SDK’s feature refresh interval. + - Updated @splitsoftware/splitio-commons package to version 2.6.0. + 1.2.0 (June 25, 2025) - Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. - Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. @@ -95,12 +101,12 @@ 0.2.0 (March 31, 2022) - Added support to SDK clients to optionally bind attributes, keeping these loaded within the SDK along with the user ID, for easier usage when requesting flag. - - Added `scheduler.impressionsQueueSize` property to SDK configuration (See https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK#configuration). + - Added `scheduler.impressionsQueueSize` property to SDK configuration (See https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#configuration). - Updated some NPM dependencies for vulnerability fixes. - Bugfix - Fixed validation of SDK configuration, to parse core.key into a string and log a warning when passing a number (Related to issue https://github.com/splitio/react-native-client/issues/19). 0.1.0 (October 20, 2021) - - Added localhost mode support (Read more in our docs here: https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK#localhost-mode). + - Added localhost mode support (Read more in our docs here: https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#localhost-mode). - Updated @splitsoftware/splitio-commons dependency to version 1.0.0, which includes: - Updated localhost mode to emit SDK_READY_FROM_CACHE event in Browser when using localStorage (Related to issue https://github.com/splitio/react-client/issues/34). - Updated streaming logic to use the newest version of our streaming service, including: @@ -111,4 +117,4 @@ - Updated some NPM dependencies for vulnerability fixes. 0.0.1 (July 29, 2021) - - Initial public release. Check the official documentation for details: https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK + - Initial public release. Check the official documentation for details: https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/ diff --git a/README.md b/README.md index fc672e2..79cb9f0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This SDK is designed to work with Split, the platform for controlled rollouts, w [![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware) ## Compatibility -The React Native SDK is a library for React Native applications. The library was build with native modules to support streaming in Android and iOS, and therefore it requires linking the native dependency in order to use streaming. For Expo applications, streaming is not supported by default but a polyfill can be used instead. Check our [public documentation](https://help.split.io/hc/en-us/articles/4406066357901) for installation details. +The React Native SDK is a library for React Native applications. The library was build with native modules to support streaming in Android and iOS, and therefore it requires linking the native dependency in order to use streaming. For Expo applications, streaming is not supported by default but a polyfill can be used instead. Check our [public documentation](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/) for installation details. ## Getting started Below is a simple App.jsx example that describes the instantiation and most basic usage of our SDK: @@ -37,7 +37,7 @@ client.on(client.Event.SDK_READY, function() { }); ``` -Please refer to [React Native SDK](https://help.split.io/hc/en-us/articles/4406066357901) to learn about all the functionality provided by our SDK as well as specifics for configuration options available for tailoring it to your current application setup. +Please refer to [React Native SDK](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/) to learn about all the functionality provided by our SDK as well as specifics for configuration options available for tailoring it to your current application setup. ## Submitting issues The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/react-native-client/issues). We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner. @@ -56,23 +56,24 @@ To learn more about Split, contact hello@split.io, or get started with feature f Split has built and maintains SDKs for: -* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK) -* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK) -* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities) -* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin) -* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK) -* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK) -* 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.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) -* React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK) -* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK) -* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK) -* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK) +* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/net-sdk/) +* Android [Github](https://github.com/splitio/android-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/android-sdk/) +* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/angular-utilities/) +* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/elixir-thin-client-sdk/) +* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/flutter-plugin/) +* GO [Github](https://github.com/splitio/go-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/go-sdk/) +* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/ios-sdk/) +* Java [Github](https://github.com/splitio/java-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/java-sdk/) +* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/) +* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/browser-sdk/) +* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/nodejs-sdk/) +* PHP [Github](https://github.com/splitio/php-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/php-sdk/) +* PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/php-thin-client-sdk/) +* Python [Github](https://github.com/splitio/python-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/python-sdk/) +* React [Github](https://github.com/splitio/react-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-sdk/) +* React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/) +* Redux [Github](https://github.com/splitio/redux-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/redux-sdk/) +* Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/server-side-sdks/ruby-sdk/) For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20). diff --git a/example/package-lock.json b/example/package-lock.json index 4cc84e3..fe981c0 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -4703,9 +4703,10 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4946,22 +4947,52 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10083,9 +10114,10 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15825,9 +15857,9 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { "version": "1.0.2", @@ -15998,17 +16030,29 @@ } }, "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" + }, + "dependencies": { + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "concat-map": { @@ -19909,9 +19953,9 @@ } }, "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==" }, "once": { "version": "1.4.0", diff --git a/package-lock.json b/package-lock.json index 1d845a1..dd19b2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-react-native", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-react-native", - "version": "1.2.0", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.4.1" + "@splitsoftware/splitio-commons": "2.6.0" }, "devDependencies": { "@react-native-community/eslint-config": "^3.2.0", @@ -4887,9 +4887,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.1.tgz", - "integrity": "sha512-VcbWpPykfx19LTJ0yeZbV0u3PUIt8MuiZ2a8zqkNf9KnDnhau/XxS/ctoO5jYrg4Nk2rCi0fpt1TkTstqzbaYA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.6.0.tgz", + "integrity": "sha512-0xODXLciIvHSuMlb8eukIB2epb3ZyGOsrwS0cMuTdxEvCqr7Nuc9pWDdJtRuN1UwL/jIjBnpDYAc8s6mpqLX2g==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -6169,10 +6169,11 @@ "dev": true }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6446,17 +6447,18 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { @@ -6478,6 +6480,37 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -14794,10 +14827,11 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -21625,9 +21659,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.1.tgz", - "integrity": "sha512-VcbWpPykfx19LTJ0yeZbV0u3PUIt8MuiZ2a8zqkNf9KnDnhau/XxS/ctoO5jYrg4Nk2rCi0fpt1TkTstqzbaYA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.6.0.tgz", + "integrity": "sha512-0xODXLciIvHSuMlb8eukIB2epb3ZyGOsrwS0cMuTdxEvCqr7Nuc9pWDdJtRuN1UwL/jIjBnpDYAc8s6mpqLX2g==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" @@ -22585,9 +22619,9 @@ "dev": true }, "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, "call-bind": { @@ -22792,17 +22826,17 @@ } }, "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "dependencies": { @@ -22820,6 +22854,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true + }, + "negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true } } }, @@ -28998,9 +29044,9 @@ } }, "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true }, "once": { diff --git a/package.json b/package.json index 064db0c..1a6dab7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-react-native", - "version": "1.2.0", + "version": "1.3.0", "description": "Split SDK for React Native", "main": "lib/commonjs/index.js", "module": "lib/module/index.js", @@ -61,7 +61,7 @@ }, "homepage": "https://github.com/splitio/react-native-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "2.4.1" + "@splitsoftware/splitio-commons": "2.6.0" }, "devDependencies": { "@react-native-community/eslint-config": "^3.2.0", diff --git a/src/full/index.ts b/src/full/index.ts index d76a207..3ed8556 100644 --- a/src/full/index.ts +++ b/src/full/index.ts @@ -1,4 +1,5 @@ export { SplitFactory } from './splitFactory'; +export { InLocalStorage } from '@splitsoftware/splitio-commons/src/storages/inLocalStorage/index'; export { ErrorLogger } from '@splitsoftware/splitio-commons/src/logger/browser/ErrorLogger'; export { WarnLogger } from '@splitsoftware/splitio-commons/src/logger/browser/WarnLogger'; export { InfoLogger } from '@splitsoftware/splitio-commons/src/logger/browser/InfoLogger'; diff --git a/src/index.ts b/src/index.ts index d76a207..3ed8556 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { SplitFactory } from './splitFactory'; +export { InLocalStorage } from '@splitsoftware/splitio-commons/src/storages/inLocalStorage/index'; export { ErrorLogger } from '@splitsoftware/splitio-commons/src/logger/browser/ErrorLogger'; export { WarnLogger } from '@splitsoftware/splitio-commons/src/logger/browser/WarnLogger'; export { InfoLogger } from '@splitsoftware/splitio-commons/src/logger/browser/InfoLogger'; diff --git a/src/platform/RNSignalListener.ts b/src/platform/RNSignalListener.ts index aea0155..2385288 100644 --- a/src/platform/RNSignalListener.ts +++ b/src/platform/RNSignalListener.ts @@ -20,6 +20,7 @@ const EVENT_NAME = 'for AppState change events.'; export class RNSignalListener implements ISignalListener { private _lastTransition: Transition | undefined; private _appStateSubscription: NativeEventSubscription | undefined; + private _lastBgTimestamp: number | undefined; constructor(private syncManager: ISyncManager, private settings: ISettings & { flushDataOnBackground?: boolean }) {} @@ -39,6 +40,10 @@ export class RNSignalListener implements ISignalListener { return transition; } + private _mustSyncAll() { + return this.settings.sync.enabled && this._lastBgTimestamp && this._lastBgTimestamp < Date.now() - this.settings.scheduler.featuresRefreshRate; + } + private _handleAppStateChange = (nextAppState: AppStateStatus) => { const action = this._getTransition(nextAppState); @@ -51,10 +56,17 @@ export class RNSignalListener implements ISignalListener { // In 2, PushManager is resumed in case it was paused and the SDK is running in push mode. // If running in polling mode, either pushManager is not defined (e.g., streamingEnabled is false) // or calling pushManager.start has no effect because it was disabled (PUSH_NONRETRYABLE_ERROR). - if (this.syncManager.pushManager) this.syncManager.pushManager.start(); + if (this.syncManager.pushManager) { + this.syncManager.pushManager.start(); + + // Sync all if singleSync is disabled and background time exceeds features refresh rate + // For streaming, this compensates the re-connection delay, and for polling, it compensates the suspension of scheduled tasks during background. + if (this._mustSyncAll()) this.syncManager.pollingManager!.syncAll(); + } break; case TO_BACKGROUND: + this._lastBgTimestamp = Date.now(); this.settings.log.debug( `App transition to background${this.syncManager.pushManager ? '. Pausing streaming' : ''}${ this.settings.flushDataOnBackground ? '. Flushing events and impressions' : '' @@ -65,7 +77,7 @@ export class RNSignalListener implements ISignalListener { // Here, PushManager is paused in case the SDK is running in push mode, to close streaming connection for Android. // In iOS it is not strictly required, since connections are automatically closed/resumed by the OS. // The remaining SyncManager components (PollingManager and Submitter) don't need to be stopped, even if running in - // polling mode, because sync tasks are "delayed" during background, since JS timers callbacks are executed only + // polling mode, because sync tasks are suspended during background, since JS timers callbacks are executed only // when the app is in foreground (https://github.com/facebook/react-native/issues/12981#issuecomment-652745831). // Other features like Fetch, AsyncStorage, AppState and NetInfo listeners, can be used in background. if (this.syncManager.pushManager) this.syncManager.pushManager.stop(); diff --git a/src/platform/__tests__/RNSignalListener.spec.ts b/src/platform/__tests__/RNSignalListener.spec.ts index 875909d..bdd1406 100644 --- a/src/platform/__tests__/RNSignalListener.spec.ts +++ b/src/platform/__tests__/RNSignalListener.spec.ts @@ -6,10 +6,13 @@ jest.doMock('react-native/Libraries/AppState/AppState', () => AppStateMock); const syncManagerMockWithPushManager = { flush: jest.fn(), pushManager: { start: jest.fn(), stop: jest.fn() }, + pollingManager: { syncAll: jest.fn() }, }; const settingsMock = { log: { debug: jest.fn() }, flushDataOnBackground: true, + scheduler: { featuresRefreshRate: 100 }, + sync: { enabled: true }, }; describe('RNSignalListener', () => { @@ -20,7 +23,7 @@ describe('RNSignalListener', () => { syncManagerMockWithPushManager.pushManager.start.mockClear(); }); - test('starting in foreground', () => { + test('starting in foreground', async () => { // @ts-expect-error. SyncManager mock partially implemented const signalListener = new RNSignalListener(syncManagerMockWithPushManager, settingsMock); @@ -41,9 +44,14 @@ describe('RNSignalListener', () => { expect(syncManagerMockWithPushManager.pushManager.stop).toBeCalledTimes(1); expect(syncManagerMockWithPushManager.pushManager.stop).toBeCalledTimes(1); + // Wait for features refresh rate to validate that syncAll is called when resuming foreground + expect(syncManagerMockWithPushManager.pollingManager!.syncAll).toBeCalledTimes(0); + await new Promise((resolve) => setTimeout(resolve, settingsMock.scheduler.featuresRefreshRate)); + // Going to foreground should be handled AppStateMock._emitChangeEvent('inactive'); expect(syncManagerMockWithPushManager.pushManager.start).toBeCalledTimes(2); + expect(syncManagerMockWithPushManager.pollingManager!.syncAll).toBeCalledTimes(1); // Handling another foreground event, have no effect AppStateMock._emitChangeEvent('active'); @@ -56,9 +64,14 @@ describe('RNSignalListener', () => { expect(syncManagerMockWithPushManager.flush).toBeCalledTimes(2); expect(syncManagerMockWithPushManager.pushManager.stop).toBeCalledTimes(2); + // Validate that syncAll is not called if singleSync is enabled + settingsMock.sync.enabled = false; + await new Promise((resolve) => setTimeout(resolve, settingsMock.scheduler.featuresRefreshRate)); + // Going to foreground should be handled again AppStateMock._emitChangeEvent('active'); expect(syncManagerMockWithPushManager.pushManager.start).toBeCalledTimes(3); + expect(syncManagerMockWithPushManager.pollingManager!.syncAll).toBeCalledTimes(1); // Stopping RNSignalListener signalListener.stop(); // @ts-ignore access private property diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 601eefd..f4bf3c3 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -1,7 +1,7 @@ import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '1.2.0'; +const packageVersion = '1.3.0'; export const defaults = { startup: { diff --git a/ts-tests/index.ts b/ts-tests/index.ts index 6ec1401..07c5b79 100644 --- a/ts-tests/index.ts +++ b/ts-tests/index.ts @@ -10,7 +10,7 @@ import type * as SplitTypes from '../types/splitio'; -import { SplitFactory, DebugLogger, InfoLogger, WarnLogger, ErrorLogger } from '../types/index'; +import { SplitFactory, DebugLogger, InfoLogger, WarnLogger, ErrorLogger, InLocalStorage } from '../types/index'; // Validate that the SplitIO namespace is available and matches the types when imported explicitly let ambientType: SplitIO.ISDK; @@ -59,7 +59,23 @@ let fullReactNativeSettings: SplitIO.IReactNativeSettings = { feature2: { treatment: 'treatment2', config: "{ 'prop': 'value'}" }, feature3: { treatment: 'treatment3', config: null }, }, - storage: undefined, + initialRolloutPlan: {} as SplitIO.RolloutPlan, + storage: InLocalStorage({ + prefix: 'splitio', + clearOnInit: true, + expirationDays: 1, + wrapper: { + getItem(key: string) { + return Promise.resolve('value'); + }, + setItem(key: string, value: string) { + return Promise.resolve(); + }, + removeItem(key: string) { + return Promise.resolve(); + }, + } as SplitIO.StorageWrapper, + }), impressionListener: { logImpression: (data: SplitIO.ImpressionData) => { let impressionData: SplitIO.ImpressionData = data; diff --git a/types/full/index.d.ts b/types/full/index.d.ts index 4fb3d8c..c97761d 100644 --- a/types/full/index.d.ts +++ b/types/full/index.d.ts @@ -11,35 +11,56 @@ declare module JsSdk { * Split.io SDK factory function. * * The settings parameter should be an object that complies with the SplitIO.IReactNativeSettings. - * For more information read the corresponding article: @see {@link https://help.split.io/hc/en-us/articles/4406066357901#configuration} + * For more information read the corresponding article: @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#configuration} */ export function SplitFactory(settings: SplitIO.IReactNativeSettings): SplitIO.IBrowserSDK; + /** + * Persistent storage. By default, it uses the Web LocalStorage API if available. + * Consider providing a custom storage wrapper, like [AsyncStorage](https://github.com/react-native-async-storage/async-storage), for Android and iOS targets. + * + * Example: + * ``` + * import AsyncStorage from '@react-native-async-storage/async-storage'; + * import { SplitFactory, InLocalStorage } from '@splitsoftware/splitio-react-native'; + * + * const SDK_CONFIG = { + * ... + * storage: InLocalStorage({ + * wrapper: AsyncStorage, + * }), + * } + * ``` + * + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#configuring-persistent-cache-for-the-sdk} + */ + export function InLocalStorage(options?: SplitIO.InLocalStorageOptions): SplitIO.StorageSyncFactory; + /** * Creates a logger instance that enables descriptive log messages with DEBUG log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function DebugLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with INFO log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function InfoLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with WARN log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function WarnLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with ERROR log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function ErrorLogger(): SplitIO.ILogger; } diff --git a/types/index.d.ts b/types/index.d.ts index bd65604..5ccacc6 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -11,35 +11,56 @@ declare module JsSdk { * Split.io SDK factory function. * * The settings parameter should be an object that complies with the SplitIO.IReactNativeSettings. - * For more information read the corresponding article: @see {@link https://help.split.io/hc/en-us/articles/4406066357901#configuration} + * For more information read the corresponding article: @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#configuration} */ export function SplitFactory(settings: SplitIO.IReactNativeSettings): SplitIO.IBrowserSDK; + /** + * Persistent storage. By default, it uses the Web LocalStorage API if available. + * Consider providing a custom storage wrapper, like [AsyncStorage](https://github.com/react-native-async-storage/async-storage), for Android and iOS targets. + * + * Example: + * ``` + * import AsyncStorage from '@react-native-async-storage/async-storage'; + * import { SplitFactory, InLocalStorage } from '@splitsoftware/splitio-react-native'; + * + * const SDK_CONFIG = { + * ... + * storage: InLocalStorage({ + * wrapper: AsyncStorage, + * }), + * } + * ``` + * + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#configuring-persistent-cache-for-the-sdk} + */ + export function InLocalStorage(options?: SplitIO.InLocalStorageOptions): SplitIO.StorageSyncFactory; + /** * Creates a logger instance that enables descriptive log messages with DEBUG log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function DebugLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with INFO log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function InfoLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with WARN log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function WarnLogger(): SplitIO.ILogger; /** * Creates a logger instance that enables descriptive log messages with ERROR log level when passed in the factory settings. * - * @see {@link https://help.split.io/hc/en-us/articles/4406066357901#logging} + * @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/react-native-sdk/#logging} */ export function ErrorLogger(): SplitIO.ILogger; }