react-native-nitro-amplitude 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. package/.watchmanconfig +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +139 -0
  4. package/android/CMakeLists.txt +34 -0
  5. package/android/build.gradle +85 -0
  6. package/android/consumer-rules.pro +35 -0
  7. package/android/gradle.properties +4 -0
  8. package/android/src/main/cpp/AndroidAmplitudeAdapterCpp.cpp +126 -0
  9. package/android/src/main/cpp/AndroidAmplitudeAdapterCpp.hpp +48 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +9 -0
  11. package/android/src/main/java/com/nitroamplitude/AndroidAmplitudeAdapter.kt +147 -0
  12. package/android/src/main/java/com/nitroamplitude/NitroAmplitudePackage.kt +24 -0
  13. package/app.plugin.js +36 -0
  14. package/cpp/bindings/HybridAmplitudeContext.cpp +83 -0
  15. package/cpp/bindings/HybridAmplitudeContext.hpp +29 -0
  16. package/cpp/bindings/HybridAmplitudeStorage.cpp +163 -0
  17. package/cpp/bindings/HybridAmplitudeStorage.hpp +38 -0
  18. package/cpp/bindings/HybridAmplitudeWorker.cpp +162 -0
  19. package/cpp/bindings/HybridAmplitudeWorker.hpp +74 -0
  20. package/cpp/core/NativeAmplitudeAdapter.hpp +44 -0
  21. package/ios/IOSAmplitudeAdapterCpp.hpp +37 -0
  22. package/ios/IOSAmplitudeAdapterCpp.mm +155 -0
  23. package/lib/commonjs/AmplitudeContext.nitro.js +6 -0
  24. package/lib/commonjs/AmplitudeContext.nitro.js.map +1 -0
  25. package/lib/commonjs/AmplitudeStorage.nitro.js +6 -0
  26. package/lib/commonjs/AmplitudeStorage.nitro.js.map +1 -0
  27. package/lib/commonjs/AmplitudeWorker.nitro.js +6 -0
  28. package/lib/commonjs/AmplitudeWorker.nitro.js.map +1 -0
  29. package/lib/commonjs/analytics/campaign/campaign-tracker.js +105 -0
  30. package/lib/commonjs/analytics/campaign/campaign-tracker.js.map +1 -0
  31. package/lib/commonjs/analytics/campaign/types.js +6 -0
  32. package/lib/commonjs/analytics/campaign/types.js.map +1 -0
  33. package/lib/commonjs/analytics/config.js +283 -0
  34. package/lib/commonjs/analytics/config.js.map +1 -0
  35. package/lib/commonjs/analytics/cookie-migration/index.js +59 -0
  36. package/lib/commonjs/analytics/cookie-migration/index.js.map +1 -0
  37. package/lib/commonjs/analytics/index.js +95 -0
  38. package/lib/commonjs/analytics/index.js.map +1 -0
  39. package/lib/commonjs/analytics/migration/remnant-data-migration.js +177 -0
  40. package/lib/commonjs/analytics/migration/remnant-data-migration.js.map +1 -0
  41. package/lib/commonjs/analytics/nitro-transport.js +31 -0
  42. package/lib/commonjs/analytics/nitro-transport.js.map +1 -0
  43. package/lib/commonjs/analytics/plugins/context.js +140 -0
  44. package/lib/commonjs/analytics/plugins/context.js.map +1 -0
  45. package/lib/commonjs/analytics/react-native-client.js +352 -0
  46. package/lib/commonjs/analytics/react-native-client.js.map +1 -0
  47. package/lib/commonjs/analytics/storage/local-storage.js +73 -0
  48. package/lib/commonjs/analytics/storage/local-storage.js.map +1 -0
  49. package/lib/commonjs/analytics/types.js +175 -0
  50. package/lib/commonjs/analytics/types.js.map +1 -0
  51. package/lib/commonjs/analytics/typings/browser-snippet.d.js +6 -0
  52. package/lib/commonjs/analytics/typings/browser-snippet.d.js.map +1 -0
  53. package/lib/commonjs/analytics/typings/ua-parser.d.js +2 -0
  54. package/lib/commonjs/analytics/typings/ua-parser.d.js.map +1 -0
  55. package/lib/commonjs/analytics/utils/platform.js +16 -0
  56. package/lib/commonjs/analytics/utils/platform.js.map +1 -0
  57. package/lib/commonjs/analytics/version.js +8 -0
  58. package/lib/commonjs/analytics/version.js.map +1 -0
  59. package/lib/commonjs/experiment/experimentClient.js +792 -0
  60. package/lib/commonjs/experiment/experimentClient.js.map +1 -0
  61. package/lib/commonjs/experiment/factory.js +77 -0
  62. package/lib/commonjs/experiment/factory.js.map +1 -0
  63. package/lib/commonjs/experiment/gen/version.js +9 -0
  64. package/lib/commonjs/experiment/gen/version.js.map +1 -0
  65. package/lib/commonjs/experiment/index.js +143 -0
  66. package/lib/commonjs/experiment/index.js.map +1 -0
  67. package/lib/commonjs/experiment/integration/NativeExperimentReactNativeClient.js +11 -0
  68. package/lib/commonjs/experiment/integration/NativeExperimentReactNativeClient.js.map +1 -0
  69. package/lib/commonjs/experiment/integration/connector.js +67 -0
  70. package/lib/commonjs/experiment/integration/connector.js.map +1 -0
  71. package/lib/commonjs/experiment/integration/default.js +92 -0
  72. package/lib/commonjs/experiment/integration/default.js.map +1 -0
  73. package/lib/commonjs/experiment/logger/ampLogger.js +80 -0
  74. package/lib/commonjs/experiment/logger/ampLogger.js.map +1 -0
  75. package/lib/commonjs/experiment/logger/consoleLogger.js +60 -0
  76. package/lib/commonjs/experiment/logger/consoleLogger.js.map +1 -0
  77. package/lib/commonjs/experiment/storage/cache.js +197 -0
  78. package/lib/commonjs/experiment/storage/cache.js.map +1 -0
  79. package/lib/commonjs/experiment/storage/local-storage.js +25 -0
  80. package/lib/commonjs/experiment/storage/local-storage.js.map +1 -0
  81. package/lib/commonjs/experiment/stubClient.js +46 -0
  82. package/lib/commonjs/experiment/stubClient.js.map +1 -0
  83. package/lib/commonjs/experiment/transport/http.js +72 -0
  84. package/lib/commonjs/experiment/transport/http.js.map +1 -0
  85. package/lib/commonjs/experiment/types/client.js +6 -0
  86. package/lib/commonjs/experiment/types/client.js.map +1 -0
  87. package/lib/commonjs/experiment/types/config.js +67 -0
  88. package/lib/commonjs/experiment/types/config.js.map +1 -0
  89. package/lib/commonjs/experiment/types/exposure.js +2 -0
  90. package/lib/commonjs/experiment/types/exposure.js.map +1 -0
  91. package/lib/commonjs/experiment/types/logger.js +42 -0
  92. package/lib/commonjs/experiment/types/logger.js.map +1 -0
  93. package/lib/commonjs/experiment/types/source.js +54 -0
  94. package/lib/commonjs/experiment/types/source.js.map +1 -0
  95. package/lib/commonjs/experiment/types/storage.js +2 -0
  96. package/lib/commonjs/experiment/types/storage.js.map +1 -0
  97. package/lib/commonjs/experiment/types/transport.js +2 -0
  98. package/lib/commonjs/experiment/types/transport.js.map +1 -0
  99. package/lib/commonjs/experiment/types/user.js +2 -0
  100. package/lib/commonjs/experiment/types/user.js.map +1 -0
  101. package/lib/commonjs/experiment/types/variant.js +2 -0
  102. package/lib/commonjs/experiment/types/variant.js.map +1 -0
  103. package/lib/commonjs/experiment/util/backoff.js +54 -0
  104. package/lib/commonjs/experiment/util/backoff.js.map +1 -0
  105. package/lib/commonjs/experiment/util/base64.js +23 -0
  106. package/lib/commonjs/experiment/util/base64.js.map +1 -0
  107. package/lib/commonjs/experiment/util/convert.js +78 -0
  108. package/lib/commonjs/experiment/util/convert.js.map +1 -0
  109. package/lib/commonjs/experiment/util/index.js +25 -0
  110. package/lib/commonjs/experiment/util/index.js.map +1 -0
  111. package/lib/commonjs/experiment/util/platform.js +16 -0
  112. package/lib/commonjs/experiment/util/platform.js.map +1 -0
  113. package/lib/commonjs/experiment/util/randomstring.js +16 -0
  114. package/lib/commonjs/experiment/util/randomstring.js.map +1 -0
  115. package/lib/commonjs/experiment/util/userSessionExposureTracker.js +40 -0
  116. package/lib/commonjs/experiment/util/userSessionExposureTracker.js.map +1 -0
  117. package/lib/commonjs/index.js +298 -0
  118. package/lib/commonjs/index.js.map +1 -0
  119. package/lib/commonjs/index.web.js +22 -0
  120. package/lib/commonjs/index.web.js.map +1 -0
  121. package/lib/commonjs/native/context.js +43 -0
  122. package/lib/commonjs/native/context.js.map +1 -0
  123. package/lib/commonjs/native/http.js +62 -0
  124. package/lib/commonjs/native/http.js.map +1 -0
  125. package/lib/commonjs/native/hybrid.js +31 -0
  126. package/lib/commonjs/native/hybrid.js.map +1 -0
  127. package/lib/commonjs/native/storage.js +93 -0
  128. package/lib/commonjs/native/storage.js.map +1 -0
  129. package/lib/commonjs/package.json +1 -0
  130. package/lib/module/AmplitudeContext.nitro.js +4 -0
  131. package/lib/module/AmplitudeContext.nitro.js.map +1 -0
  132. package/lib/module/AmplitudeStorage.nitro.js +4 -0
  133. package/lib/module/AmplitudeStorage.nitro.js.map +1 -0
  134. package/lib/module/AmplitudeWorker.nitro.js +4 -0
  135. package/lib/module/AmplitudeWorker.nitro.js.map +1 -0
  136. package/lib/module/analytics/campaign/campaign-tracker.js +100 -0
  137. package/lib/module/analytics/campaign/campaign-tracker.js.map +1 -0
  138. package/lib/module/analytics/campaign/types.js +4 -0
  139. package/lib/module/analytics/campaign/types.js.map +1 -0
  140. package/lib/module/analytics/config.js +272 -0
  141. package/lib/module/analytics/config.js.map +1 -0
  142. package/lib/module/analytics/cookie-migration/index.js +51 -0
  143. package/lib/module/analytics/cookie-migration/index.js.map +1 -0
  144. package/lib/module/analytics/index.js +34 -0
  145. package/lib/module/analytics/index.js.map +1 -0
  146. package/lib/module/analytics/migration/remnant-data-migration.js +172 -0
  147. package/lib/module/analytics/migration/remnant-data-migration.js.map +1 -0
  148. package/lib/module/analytics/nitro-transport.js +26 -0
  149. package/lib/module/analytics/nitro-transport.js.map +1 -0
  150. package/lib/module/analytics/plugins/context.js +134 -0
  151. package/lib/module/analytics/plugins/context.js.map +1 -0
  152. package/lib/module/analytics/react-native-client.js +346 -0
  153. package/lib/module/analytics/react-native-client.js.map +1 -0
  154. package/lib/module/analytics/storage/local-storage.js +68 -0
  155. package/lib/module/analytics/storage/local-storage.js.map +1 -0
  156. package/lib/module/analytics/types.js +4 -0
  157. package/lib/module/analytics/types.js.map +1 -0
  158. package/lib/module/analytics/typings/browser-snippet.d.js +4 -0
  159. package/lib/module/analytics/typings/browser-snippet.d.js.map +1 -0
  160. package/lib/module/analytics/typings/ua-parser.d.js +2 -0
  161. package/lib/module/analytics/typings/ua-parser.d.js.map +1 -0
  162. package/lib/module/analytics/utils/platform.js +10 -0
  163. package/lib/module/analytics/utils/platform.js.map +1 -0
  164. package/lib/module/analytics/version.js +4 -0
  165. package/lib/module/analytics/version.js.map +1 -0
  166. package/lib/module/experiment/experimentClient.js +788 -0
  167. package/lib/module/experiment/experimentClient.js.map +1 -0
  168. package/lib/module/experiment/factory.js +73 -0
  169. package/lib/module/experiment/factory.js.map +1 -0
  170. package/lib/module/experiment/gen/version.js +5 -0
  171. package/lib/module/experiment/gen/version.js.map +1 -0
  172. package/lib/module/experiment/index.js +16 -0
  173. package/lib/module/experiment/index.js.map +1 -0
  174. package/lib/module/experiment/integration/NativeExperimentReactNativeClient.js +7 -0
  175. package/lib/module/experiment/integration/NativeExperimentReactNativeClient.js.map +1 -0
  176. package/lib/module/experiment/integration/connector.js +61 -0
  177. package/lib/module/experiment/integration/connector.js.map +1 -0
  178. package/lib/module/experiment/integration/default.js +87 -0
  179. package/lib/module/experiment/integration/default.js.map +1 -0
  180. package/lib/module/experiment/logger/ampLogger.js +76 -0
  181. package/lib/module/experiment/logger/ampLogger.js.map +1 -0
  182. package/lib/module/experiment/logger/consoleLogger.js +55 -0
  183. package/lib/module/experiment/logger/consoleLogger.js.map +1 -0
  184. package/lib/module/experiment/storage/cache.js +187 -0
  185. package/lib/module/experiment/storage/cache.js.map +1 -0
  186. package/lib/module/experiment/storage/local-storage.js +19 -0
  187. package/lib/module/experiment/storage/local-storage.js.map +1 -0
  188. package/lib/module/experiment/stubClient.js +41 -0
  189. package/lib/module/experiment/stubClient.js.map +1 -0
  190. package/lib/module/experiment/transport/http.js +66 -0
  191. package/lib/module/experiment/transport/http.js.map +1 -0
  192. package/lib/module/experiment/types/client.js +4 -0
  193. package/lib/module/experiment/types/client.js.map +1 -0
  194. package/lib/module/experiment/types/config.js +64 -0
  195. package/lib/module/experiment/types/config.js.map +1 -0
  196. package/lib/module/experiment/types/exposure.js +2 -0
  197. package/lib/module/experiment/types/exposure.js.map +1 -0
  198. package/lib/module/experiment/types/logger.js +39 -0
  199. package/lib/module/experiment/types/logger.js.map +1 -0
  200. package/lib/module/experiment/types/source.js +51 -0
  201. package/lib/module/experiment/types/source.js.map +1 -0
  202. package/lib/module/experiment/types/storage.js +2 -0
  203. package/lib/module/experiment/types/storage.js.map +1 -0
  204. package/lib/module/experiment/types/transport.js +2 -0
  205. package/lib/module/experiment/types/transport.js.map +1 -0
  206. package/lib/module/experiment/types/user.js +2 -0
  207. package/lib/module/experiment/types/user.js.map +1 -0
  208. package/lib/module/experiment/types/variant.js +2 -0
  209. package/lib/module/experiment/types/variant.js.map +1 -0
  210. package/lib/module/experiment/util/backoff.js +49 -0
  211. package/lib/module/experiment/util/backoff.js.map +1 -0
  212. package/lib/module/experiment/util/base64.js +16 -0
  213. package/lib/module/experiment/util/base64.js.map +1 -0
  214. package/lib/module/experiment/util/convert.js +71 -0
  215. package/lib/module/experiment/util/convert.js.map +1 -0
  216. package/lib/module/experiment/util/index.js +17 -0
  217. package/lib/module/experiment/util/index.js.map +1 -0
  218. package/lib/module/experiment/util/platform.js +10 -0
  219. package/lib/module/experiment/util/platform.js.map +1 -0
  220. package/lib/module/experiment/util/randomstring.js +11 -0
  221. package/lib/module/experiment/util/randomstring.js.map +1 -0
  222. package/lib/module/experiment/util/userSessionExposureTracker.js +35 -0
  223. package/lib/module/experiment/util/userSessionExposureTracker.js.map +1 -0
  224. package/lib/module/index.js +50 -0
  225. package/lib/module/index.js.map +1 -0
  226. package/lib/module/index.web.js +14 -0
  227. package/lib/module/index.web.js.map +1 -0
  228. package/lib/module/native/context.js +35 -0
  229. package/lib/module/native/context.js.map +1 -0
  230. package/lib/module/native/http.js +57 -0
  231. package/lib/module/native/http.js.map +1 -0
  232. package/lib/module/native/hybrid.js +24 -0
  233. package/lib/module/native/hybrid.js.map +1 -0
  234. package/lib/module/native/storage.js +86 -0
  235. package/lib/module/native/storage.js.map +1 -0
  236. package/lib/typescript/AmplitudeContext.nitro.d.ts +17 -0
  237. package/lib/typescript/AmplitudeContext.nitro.d.ts.map +1 -0
  238. package/lib/typescript/AmplitudeStorage.nitro.d.ts +17 -0
  239. package/lib/typescript/AmplitudeStorage.nitro.d.ts.map +1 -0
  240. package/lib/typescript/AmplitudeWorker.nitro.d.ts +11 -0
  241. package/lib/typescript/AmplitudeWorker.nitro.d.ts.map +1 -0
  242. package/lib/typescript/analytics/campaign/campaign-tracker.d.ts +85 -0
  243. package/lib/typescript/analytics/campaign/campaign-tracker.d.ts.map +1 -0
  244. package/lib/typescript/analytics/campaign/types.d.ts +11 -0
  245. package/lib/typescript/analytics/campaign/types.d.ts.map +1 -0
  246. package/lib/typescript/analytics/config.d.ts +68 -0
  247. package/lib/typescript/analytics/config.d.ts.map +1 -0
  248. package/lib/typescript/analytics/cookie-migration/index.d.ts +6 -0
  249. package/lib/typescript/analytics/cookie-migration/index.d.ts.map +1 -0
  250. package/lib/typescript/analytics/index.d.ts +7 -0
  251. package/lib/typescript/analytics/index.d.ts.map +1 -0
  252. package/lib/typescript/analytics/migration/remnant-data-migration.d.ts +20 -0
  253. package/lib/typescript/analytics/migration/remnant-data-migration.d.ts.map +1 -0
  254. package/lib/typescript/analytics/nitro-transport.d.ts +9 -0
  255. package/lib/typescript/analytics/nitro-transport.d.ts.map +1 -0
  256. package/lib/typescript/analytics/plugins/context.d.ts +14 -0
  257. package/lib/typescript/analytics/plugins/context.d.ts.map +1 -0
  258. package/lib/typescript/analytics/react-native-client.d.ts +55 -0
  259. package/lib/typescript/analytics/react-native-client.d.ts.map +1 -0
  260. package/lib/typescript/analytics/storage/local-storage.d.ts +14 -0
  261. package/lib/typescript/analytics/storage/local-storage.d.ts.map +1 -0
  262. package/lib/typescript/analytics/types.d.ts +2 -0
  263. package/lib/typescript/analytics/types.d.ts.map +1 -0
  264. package/lib/typescript/analytics/utils/platform.d.ts +3 -0
  265. package/lib/typescript/analytics/utils/platform.d.ts.map +1 -0
  266. package/lib/typescript/analytics/version.d.ts +2 -0
  267. package/lib/typescript/analytics/version.d.ts.map +1 -0
  268. package/lib/typescript/experiment/experimentClient.d.ts +234 -0
  269. package/lib/typescript/experiment/experimentClient.d.ts.map +1 -0
  270. package/lib/typescript/experiment/factory.d.ts +11 -0
  271. package/lib/typescript/experiment/factory.d.ts.map +1 -0
  272. package/lib/typescript/experiment/gen/version.d.ts +2 -0
  273. package/lib/typescript/experiment/gen/version.d.ts.map +1 -0
  274. package/lib/typescript/experiment/index.d.ts +15 -0
  275. package/lib/typescript/experiment/index.d.ts.map +1 -0
  276. package/lib/typescript/experiment/integration/NativeExperimentReactNativeClient.d.ts +16 -0
  277. package/lib/typescript/experiment/integration/NativeExperimentReactNativeClient.d.ts.map +1 -0
  278. package/lib/typescript/experiment/integration/connector.d.ts +16 -0
  279. package/lib/typescript/experiment/integration/connector.d.ts.map +1 -0
  280. package/lib/typescript/experiment/integration/default.d.ts +20 -0
  281. package/lib/typescript/experiment/integration/default.d.ts.map +1 -0
  282. package/lib/typescript/experiment/logger/ampLogger.d.ts +47 -0
  283. package/lib/typescript/experiment/logger/ampLogger.d.ts.map +1 -0
  284. package/lib/typescript/experiment/logger/consoleLogger.d.ts +40 -0
  285. package/lib/typescript/experiment/logger/consoleLogger.d.ts.map +1 -0
  286. package/lib/typescript/experiment/storage/cache.d.ts +35 -0
  287. package/lib/typescript/experiment/storage/cache.d.ts.map +1 -0
  288. package/lib/typescript/experiment/storage/local-storage.d.ts +11 -0
  289. package/lib/typescript/experiment/storage/local-storage.d.ts.map +1 -0
  290. package/lib/typescript/experiment/stubClient.d.ts +21 -0
  291. package/lib/typescript/experiment/stubClient.d.ts.map +1 -0
  292. package/lib/typescript/experiment/transport/http.d.ts +17 -0
  293. package/lib/typescript/experiment/transport/http.d.ts.map +1 -0
  294. package/lib/typescript/experiment/types/client.d.ts +36 -0
  295. package/lib/typescript/experiment/types/client.d.ts.map +1 -0
  296. package/lib/typescript/experiment/types/config.d.ts +164 -0
  297. package/lib/typescript/experiment/types/config.d.ts.map +1 -0
  298. package/lib/typescript/experiment/types/exposure.d.ts +107 -0
  299. package/lib/typescript/experiment/types/exposure.d.ts.map +1 -0
  300. package/lib/typescript/experiment/types/logger.d.ts +67 -0
  301. package/lib/typescript/experiment/types/logger.d.ts.map +1 -0
  302. package/lib/typescript/experiment/types/source.d.ts +43 -0
  303. package/lib/typescript/experiment/types/source.d.ts.map +1 -0
  304. package/lib/typescript/experiment/types/storage.d.ts +7 -0
  305. package/lib/typescript/experiment/types/storage.d.ts.map +1 -0
  306. package/lib/typescript/experiment/types/transport.d.ts +8 -0
  307. package/lib/typescript/experiment/types/transport.d.ts.map +1 -0
  308. package/lib/typescript/experiment/types/user.d.ts +106 -0
  309. package/lib/typescript/experiment/types/user.d.ts.map +1 -0
  310. package/lib/typescript/experiment/types/variant.d.ts +33 -0
  311. package/lib/typescript/experiment/types/variant.d.ts.map +1 -0
  312. package/lib/typescript/experiment/util/backoff.d.ts +14 -0
  313. package/lib/typescript/experiment/util/backoff.d.ts.map +1 -0
  314. package/lib/typescript/experiment/util/base64.d.ts +3 -0
  315. package/lib/typescript/experiment/util/base64.d.ts.map +1 -0
  316. package/lib/typescript/experiment/util/convert.d.ts +7 -0
  317. package/lib/typescript/experiment/util/convert.d.ts.map +1 -0
  318. package/lib/typescript/experiment/util/index.d.ts +6 -0
  319. package/lib/typescript/experiment/util/index.d.ts.map +1 -0
  320. package/lib/typescript/experiment/util/platform.d.ts +3 -0
  321. package/lib/typescript/experiment/util/platform.d.ts.map +1 -0
  322. package/lib/typescript/experiment/util/randomstring.d.ts +2 -0
  323. package/lib/typescript/experiment/util/randomstring.d.ts.map +1 -0
  324. package/lib/typescript/experiment/util/userSessionExposureTracker.d.ts +16 -0
  325. package/lib/typescript/experiment/util/userSessionExposureTracker.d.ts.map +1 -0
  326. package/lib/typescript/index.d.ts +31 -0
  327. package/lib/typescript/index.d.ts.map +1 -0
  328. package/lib/typescript/index.web.d.ts +12 -0
  329. package/lib/typescript/index.web.d.ts.map +1 -0
  330. package/lib/typescript/native/context.d.ts +29 -0
  331. package/lib/typescript/native/context.d.ts.map +1 -0
  332. package/lib/typescript/native/http.d.ts +6 -0
  333. package/lib/typescript/native/http.d.ts.map +1 -0
  334. package/lib/typescript/native/hybrid.d.ts +8 -0
  335. package/lib/typescript/native/hybrid.d.ts.map +1 -0
  336. package/lib/typescript/native/storage.d.ts +29 -0
  337. package/lib/typescript/native/storage.d.ts.map +1 -0
  338. package/nitro.json +33 -0
  339. package/nitrogen/generated/.gitattributes +1 -0
  340. package/nitrogen/generated/android/NitroAmplitude+autolinking.cmake +83 -0
  341. package/nitrogen/generated/android/NitroAmplitude+autolinking.gradle +27 -0
  342. package/nitrogen/generated/android/NitroAmplitudeOnLoad.cpp +69 -0
  343. package/nitrogen/generated/android/NitroAmplitudeOnLoad.hpp +34 -0
  344. package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitroamplitude/NitroAmplitudeOnLoad.kt +35 -0
  345. package/nitrogen/generated/ios/NitroAmplitude+autolinking.rb +62 -0
  346. package/nitrogen/generated/ios/NitroAmplitude-Swift-Cxx-Bridge.cpp +17 -0
  347. package/nitrogen/generated/ios/NitroAmplitude-Swift-Cxx-Bridge.hpp +27 -0
  348. package/nitrogen/generated/ios/NitroAmplitude-Swift-Cxx-Umbrella.hpp +38 -0
  349. package/nitrogen/generated/ios/NitroAmplitudeAutolinking.mm +55 -0
  350. package/nitrogen/generated/ios/NitroAmplitudeAutolinking.swift +16 -0
  351. package/nitrogen/generated/shared/c++/HybridAmplitudeContextSpec.cpp +25 -0
  352. package/nitrogen/generated/shared/c++/HybridAmplitudeContextSpec.hpp +67 -0
  353. package/nitrogen/generated/shared/c++/HybridAmplitudeStorageSpec.cpp +30 -0
  354. package/nitrogen/generated/shared/c++/HybridAmplitudeStorageSpec.hpp +73 -0
  355. package/nitrogen/generated/shared/c++/HybridAmplitudeWorkerSpec.cpp +24 -0
  356. package/nitrogen/generated/shared/c++/HybridAmplitudeWorkerSpec.hpp +66 -0
  357. package/package.json +130 -0
  358. package/react-native-nitro-amplitude.podspec +36 -0
  359. package/src/AmplitudeContext.nitro.ts +21 -0
  360. package/src/AmplitudeStorage.nitro.ts +17 -0
  361. package/src/AmplitudeWorker.nitro.ts +25 -0
  362. package/src/analytics/campaign/campaign-tracker.ts +162 -0
  363. package/src/analytics/campaign/types.ts +18 -0
  364. package/src/analytics/config.ts +373 -0
  365. package/src/analytics/cookie-migration/index.ts +69 -0
  366. package/src/analytics/index.ts +36 -0
  367. package/src/analytics/migration/remnant-data-migration.ts +206 -0
  368. package/src/analytics/nitro-transport.ts +35 -0
  369. package/src/analytics/plugins/context.ts +166 -0
  370. package/src/analytics/react-native-client.ts +573 -0
  371. package/src/analytics/storage/local-storage.ts +76 -0
  372. package/src/analytics/types.ts +30 -0
  373. package/src/analytics/typings/browser-snippet.d.ts +7 -0
  374. package/src/analytics/typings/ua-parser.d.ts +4 -0
  375. package/src/analytics/utils/platform.ts +9 -0
  376. package/src/analytics/version.ts +1 -0
  377. package/src/experiment/experimentClient.ts +987 -0
  378. package/src/experiment/factory.ts +86 -0
  379. package/src/experiment/gen/version.ts +2 -0
  380. package/src/experiment/index.ts +14 -0
  381. package/src/experiment/integration/NativeExperimentReactNativeClient.ts +25 -0
  382. package/src/experiment/integration/connector.ts +82 -0
  383. package/src/experiment/integration/default.ts +107 -0
  384. package/src/experiment/logger/ampLogger.ts +76 -0
  385. package/src/experiment/logger/consoleLogger.ts +54 -0
  386. package/src/experiment/storage/cache.ts +271 -0
  387. package/src/experiment/storage/local-storage.ts +23 -0
  388. package/src/experiment/stubClient.ts +61 -0
  389. package/src/experiment/transport/http.ts +105 -0
  390. package/src/experiment/types/client.ts +38 -0
  391. package/src/experiment/types/config.ts +210 -0
  392. package/src/experiment/types/exposure.ts +107 -0
  393. package/src/experiment/types/logger.ts +71 -0
  394. package/src/experiment/types/source.ts +52 -0
  395. package/src/experiment/types/storage.ts +6 -0
  396. package/src/experiment/types/transport.ts +14 -0
  397. package/src/experiment/types/user.ts +132 -0
  398. package/src/experiment/types/variant.ts +36 -0
  399. package/src/experiment/util/backoff.ts +67 -0
  400. package/src/experiment/util/base64.ts +20 -0
  401. package/src/experiment/util/convert.ts +78 -0
  402. package/src/experiment/util/index.ts +23 -0
  403. package/src/experiment/util/platform.ts +9 -0
  404. package/src/experiment/util/randomstring.ts +12 -0
  405. package/src/experiment/util/userSessionExposureTracker.ts +47 -0
  406. package/src/index.ts +66 -0
  407. package/src/index.web.ts +19 -0
  408. package/src/native/context.ts +73 -0
  409. package/src/native/http.ts +77 -0
  410. package/src/native/hybrid.ts +32 -0
  411. package/src/native/storage.ts +107 -0
@@ -0,0 +1,987 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * @module experiment-react-native-client
4
+ */
5
+
6
+ import {
7
+ topologicalSort,
8
+ EvaluationApi,
9
+ EvaluationEngine,
10
+ EvaluationFlag,
11
+ FetchError,
12
+ FlagApi,
13
+ Poller,
14
+ SdkFlagApi,
15
+ SdkEvaluationApi,
16
+ GetVariantsOptions,
17
+ } from "@amplitude/experiment-core";
18
+
19
+ import { version as PACKAGE_VERSION } from "./gen/version";
20
+ import { ConnectorUserProvider } from "./integration/connector";
21
+ import { DefaultUserProvider } from "./integration/default";
22
+ import { AmpLogger } from "./logger/ampLogger";
23
+ import { ConsoleLogger } from "./logger/consoleLogger";
24
+ import {
25
+ getFlagStorage,
26
+ getVariantsOptionsStorage,
27
+ getVariantStorage,
28
+ LoadStoreCache,
29
+ SingleValueStoreCache,
30
+ } from "./storage/cache";
31
+ import { MemoryStorage } from "./storage/local-storage";
32
+ import { FetchHttpClient, WrapperClient } from "./transport/http";
33
+ import { Client, FetchOptions } from "./types/client";
34
+ import { ExperimentConfig, Defaults } from "./types/config";
35
+ import { Exposure } from "./types/exposure";
36
+ import { LogLevel } from "./types/logger";
37
+ import { isFallback, Source, VariantSource } from "./types/source";
38
+ import { ExperimentUser, ExperimentUserProvider } from "./types/user";
39
+ import { Variant, Variants } from "./types/variant";
40
+ import {
41
+ isLocalEvaluationMode,
42
+ isNullOrUndefined,
43
+ isNullUndefinedOrEmpty,
44
+ } from "./util";
45
+ import { Backoff } from "./util/backoff";
46
+ import {
47
+ convertEvaluationVariantToVariant,
48
+ convertUserToContext,
49
+ convertVariant,
50
+ } from "./util/convert";
51
+ import { UserSessionExposureTracker } from "./util/userSessionExposureTracker";
52
+
53
+ // Configs which have been removed from the public API.
54
+ // May be added back in the future.
55
+ const fetchBackoffTimeout = 10000;
56
+ const fetchBackoffAttempts = 8;
57
+ const fetchBackoffMinMillis = 500;
58
+ const fetchBackoffMaxMillis = 10000;
59
+ const fetchBackoffScalar = 1.5;
60
+ const flagPollerIntervalMillis = 60000;
61
+
62
+ const euServerUrl = "https://api.lab.eu.amplitude.com";
63
+ const euFlagsServerUrl = "https://flag.lab.eu.amplitude.com";
64
+
65
+ type ResolvedExperimentConfig = ExperimentConfig & {
66
+ debug: boolean;
67
+ logLevel: LogLevel;
68
+ loggerProvider: NonNullable<ExperimentConfig["loggerProvider"]> | null;
69
+ instanceName: string;
70
+ fallbackVariant: Variant;
71
+ initialVariants: Variants;
72
+ source: Source;
73
+ serverUrl: string;
74
+ flagsServerUrl: string;
75
+ serverZone: "US" | "EU";
76
+ fetchTimeoutMillis: number;
77
+ retryFetchOnFailure: boolean;
78
+ automaticExposureTracking: boolean;
79
+ pollOnStart: boolean;
80
+ fetchOnStart: boolean;
81
+ automaticFetchOnAmplitudeIdentityChange: boolean;
82
+ userProvider: ExperimentUserProvider | null;
83
+ exposureTrackingProvider: NonNullable<
84
+ ExperimentConfig["exposureTrackingProvider"]
85
+ > | null;
86
+ httpClient: NonNullable<ExperimentConfig["httpClient"]>;
87
+ storage: NonNullable<ExperimentConfig["storage"]> | null;
88
+ };
89
+
90
+ /**
91
+ * The default {@link Client} used to fetch variations from Experiment's
92
+ * servers.
93
+ *
94
+ * @category Core Usage
95
+ */
96
+ export class ExperimentClient implements Client {
97
+ private readonly apiKey: string;
98
+ private readonly config: ResolvedExperimentConfig;
99
+ private readonly logger: AmpLogger;
100
+ private readonly variants: LoadStoreCache<Variant>;
101
+ private readonly flags: LoadStoreCache<EvaluationFlag>;
102
+ private readonly flagApi: FlagApi;
103
+ private readonly evaluationApi: EvaluationApi;
104
+ private readonly engine: EvaluationEngine = new EvaluationEngine();
105
+ private user: ExperimentUser = {};
106
+ private readonly defaultUserProvider: DefaultUserProvider;
107
+ private readonly userSessionExposureTracker:
108
+ | UserSessionExposureTracker
109
+ | undefined;
110
+ private retriesBackoff: Backoff | undefined;
111
+ private readonly poller: Poller = new Poller(
112
+ () => this.pollFlags(),
113
+ flagPollerIntervalMillis,
114
+ );
115
+ private isRunning = false;
116
+ private readonly flagsAndVariantsLoadedPromise: Promise<void>[];
117
+ private readonly initialFlags: EvaluationFlag[] | undefined;
118
+ private fetchSequenceNumber = 0;
119
+ private storedFetchSequenceNumber = 0;
120
+ private readonly fetchVariantsOptions: SingleValueStoreCache<GetVariantsOptions>;
121
+ private readonly stopCallbacks = new Set<() => void>();
122
+
123
+ /**
124
+ * Creates a new ExperimentClient instance.
125
+ *
126
+ * In most cases you will want to use the `initialize` factory method in
127
+ * {@link Experiment}.
128
+ *
129
+ * @param apiKey The Client key for the Experiment project
130
+ * @param config See {@link ExperimentConfig} for config options
131
+ */
132
+ public constructor(apiKey: string, config: ExperimentConfig) {
133
+ this.apiKey = apiKey;
134
+ const serverZone = config?.serverZone ?? Defaults.serverZone ?? "US";
135
+ this.config = {
136
+ ...Defaults,
137
+ ...config,
138
+ debug: config?.debug ?? Defaults.debug ?? false,
139
+ logLevel: config?.logLevel ?? Defaults.logLevel ?? LogLevel.Error,
140
+ loggerProvider: config?.loggerProvider ?? Defaults.loggerProvider ?? null,
141
+ instanceName:
142
+ config?.instanceName ?? Defaults.instanceName ?? "$default_instance",
143
+ fallbackVariant:
144
+ config?.fallbackVariant ?? Defaults.fallbackVariant ?? {},
145
+ initialVariants:
146
+ config?.initialVariants ?? Defaults.initialVariants ?? {},
147
+ source: config?.source ?? Defaults.source ?? Source.LocalStorage,
148
+ serverZone,
149
+ serverUrl:
150
+ config?.serverUrl ??
151
+ (serverZone === "EU"
152
+ ? euServerUrl
153
+ : (Defaults.serverUrl ?? "https://api.lab.amplitude.com")),
154
+ flagsServerUrl:
155
+ config?.flagsServerUrl ??
156
+ (serverZone === "EU"
157
+ ? euFlagsServerUrl
158
+ : (Defaults.flagsServerUrl ?? "https://flag.lab.amplitude.com")),
159
+ fetchTimeoutMillis:
160
+ config?.fetchTimeoutMillis ?? Defaults.fetchTimeoutMillis ?? 10000,
161
+ retryFetchOnFailure:
162
+ config?.retryFetchOnFailure ?? Defaults.retryFetchOnFailure ?? true,
163
+ automaticExposureTracking:
164
+ config?.automaticExposureTracking ??
165
+ Defaults.automaticExposureTracking ??
166
+ true,
167
+ pollOnStart: config?.pollOnStart ?? Defaults.pollOnStart ?? true,
168
+ fetchOnStart: config?.fetchOnStart ?? Defaults.fetchOnStart ?? true,
169
+ automaticFetchOnAmplitudeIdentityChange:
170
+ config?.automaticFetchOnAmplitudeIdentityChange ??
171
+ Defaults.automaticFetchOnAmplitudeIdentityChange ??
172
+ false,
173
+ userProvider: config?.userProvider ?? Defaults.userProvider ?? null,
174
+ exposureTrackingProvider:
175
+ config?.exposureTrackingProvider ??
176
+ Defaults.exposureTrackingProvider ??
177
+ null,
178
+ httpClient: config?.httpClient ?? Defaults.httpClient ?? FetchHttpClient,
179
+ storage: config?.storage ?? Defaults.storage ?? null,
180
+ };
181
+ this.logger = new AmpLogger(
182
+ this.config.loggerProvider || new ConsoleLogger(),
183
+ ExperimentClient.getLogLevel(this.config),
184
+ );
185
+ this.defaultUserProvider = new DefaultUserProvider(
186
+ this.config.userProvider,
187
+ );
188
+ if (this.config.exposureTrackingProvider) {
189
+ this.userSessionExposureTracker = new UserSessionExposureTracker(
190
+ this.config.exposureTrackingProvider,
191
+ );
192
+ }
193
+ // Setup Remote APIs
194
+ const httpClient = new WrapperClient(
195
+ this.config.httpClient || FetchHttpClient,
196
+ );
197
+ this.flagApi = new SdkFlagApi(
198
+ this.apiKey,
199
+ this.config.flagsServerUrl,
200
+ httpClient,
201
+ );
202
+ this.evaluationApi = new SdkEvaluationApi(
203
+ this.apiKey,
204
+ this.config.serverUrl,
205
+ httpClient,
206
+ );
207
+ // Storage & Caching
208
+ const storage = this.config.storage || new MemoryStorage();
209
+ this.variants = getVariantStorage(
210
+ this.apiKey,
211
+ this.config.instanceName,
212
+ storage,
213
+ );
214
+ this.flags = getFlagStorage(this.apiKey, this.config.instanceName, storage);
215
+ if (this.config.initialFlags) {
216
+ try {
217
+ this.initialFlags = JSON.parse(this.config.initialFlags);
218
+ } catch (error) {
219
+ this.logger.warn(error);
220
+ }
221
+ }
222
+ this.fetchVariantsOptions = getVariantsOptionsStorage(
223
+ this.apiKey,
224
+ this.config.instanceName,
225
+ storage,
226
+ );
227
+ this.flagsAndVariantsLoadedPromise = [
228
+ this.flags.load(this.convertInitialFlagsForStorage()),
229
+ this.variants.load(),
230
+ this.fetchVariantsOptions.load(),
231
+ ];
232
+ }
233
+
234
+ /**
235
+ * Call to ensure the completion of the loading variants and flags from localStorage upon initialization.
236
+ */
237
+ public async cacheReady(): Promise<ExperimentClient> {
238
+ await Promise.all(this.flagsAndVariantsLoadedPromise);
239
+ return this;
240
+ }
241
+
242
+ /**
243
+ * Start the SDK by getting flag configurations from the server and fetching
244
+ * variants for the user. The promise returned by this function resolves when
245
+ * local flag configurations have been updated, and the {@link fetch()}
246
+ * result has been received (if the request was made).
247
+ *
248
+ * To force this function not to fetch variants, set the {@link fetchOnStart}
249
+ * configuration option to `false` when initializing the SDK.
250
+ *
251
+ * Finally, this function will start polling for flag configurations at a
252
+ * fixed interval. To disable polling, set the {@link pollOnStart}
253
+ * configuration option to `false` on initialization.
254
+ *
255
+ * @param user The user to set in the SDK.
256
+ * @see fetchOnStart
257
+ * @see pollOnStart
258
+ * @see fetch
259
+ * @see variant
260
+ */
261
+ public async start(user?: ExperimentUser): Promise<void> {
262
+ if (this.isRunning) {
263
+ return;
264
+ } else {
265
+ this.isRunning = true;
266
+ }
267
+ try {
268
+ this.defaultUserProvider.start();
269
+ this.setUser(user ?? {});
270
+ const flagsReadyPromise = this.doFlags();
271
+ const fetchOnStart = this.config.fetchOnStart ?? true;
272
+ if (fetchOnStart) {
273
+ await Promise.all([this.fetch(user), flagsReadyPromise]);
274
+ } else {
275
+ await flagsReadyPromise;
276
+ }
277
+ if (this.config.pollOnStart) {
278
+ this.poller.start();
279
+ }
280
+ } catch (e) {
281
+ this.stop();
282
+ throw e;
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Stop background polling and retry work started by the client.
288
+ */
289
+ public stop(): void {
290
+ this.stopRetries();
291
+ this.defaultUserProvider.stop();
292
+ for (const callback of this.stopCallbacks) {
293
+ try {
294
+ callback();
295
+ } catch (e) {
296
+ this.logger.warn(e);
297
+ }
298
+ }
299
+ this.stopCallbacks.clear();
300
+ if (!this.isRunning) {
301
+ return;
302
+ }
303
+ this.poller.stop();
304
+ this.isRunning = false;
305
+ }
306
+
307
+ /**
308
+ * Assign the given user to the SDK and asynchronously fetch all variants
309
+ * from the server. Subsequent calls may omit the user from the argument to
310
+ * use the user from the previous call.
311
+ *
312
+ * If an {@link ExperimentUserProvider} has been set, the argument user will
313
+ * be merged with the provider user, preferring user fields from the argument
314
+ * user and falling back on the provider for fields which are null or
315
+ * undefined.
316
+ *
317
+ * If configured, fetch retries the request in the background on failure.
318
+ * Variants received from a successful retry are stored in local storage for
319
+ * access.
320
+ *
321
+ * If you are using the `initialVariants` config option to preload this SDK
322
+ * from the server, you generally do not need to call `fetch`.
323
+ *
324
+ * @param user The user to fetch variants for.
325
+ * @param options Options for this specific fetch call.
326
+ * @returns Promise that resolves when the request for variants completes.
327
+ * @see ExperimentUser
328
+ * @see ExperimentUserProvider
329
+ */
330
+ public async fetch(
331
+ user: ExperimentUser = this.user,
332
+ options?: FetchOptions,
333
+ ): Promise<ExperimentClient> {
334
+ const fetchUser = user ?? this.user;
335
+ this.setUser(fetchUser);
336
+ try {
337
+ await this.fetchWithRetries(fetchUser, options);
338
+ } catch (e) {
339
+ this.logger.warn(e);
340
+ }
341
+ return this;
342
+ }
343
+
344
+ public async fetchOrThrow(
345
+ user: ExperimentUser = this.user,
346
+ options?: FetchOptions,
347
+ ): Promise<ExperimentClient> {
348
+ const fetchUser = user ?? this.user;
349
+ this.setUser(fetchUser);
350
+ await this.fetchWithRetries(fetchUser, options);
351
+ return this;
352
+ }
353
+
354
+ /**
355
+ * Returns the variant for the provided key.
356
+ *
357
+ * Access the variant from {@link Source}, falling back on the given
358
+ * fallback, then the configured fallbackVariant.
359
+ *
360
+ * If an {@link ExposureTrackingProvider} is configured and trackExposure is
361
+ * true, this function will call the provider with an {@link Exposure}.
362
+ * The exposure event does not count towards your event volume within Amplitude.
363
+ *
364
+ * @param key The key to get the variant for.
365
+ * @param fallback The highest priority fallback.
366
+ * @see ExperimentConfig
367
+ * @see ExposureTrackingProvider
368
+ */
369
+ public variant(key: string, fallback?: string | Variant): Variant {
370
+ if (!this.apiKey) {
371
+ return { value: undefined };
372
+ }
373
+ const sourceVariant = this.variantAndSource(key, fallback);
374
+ if (this.config.automaticExposureTracking) {
375
+ this.exposureInternal(key, sourceVariant);
376
+ }
377
+ this.logger.debug(
378
+ `[Experiment] variant for ${key} is ${sourceVariant.variant?.value}`,
379
+ );
380
+ return sourceVariant.variant || {};
381
+ }
382
+
383
+ /**
384
+ * Track an exposure event for the variant associated with the flag/experiment
385
+ * {@link key}.
386
+ *
387
+ * This method requires that an {@link ExposureTrackingProvider} be
388
+ * configured when this client is initialized, either manually, or through the
389
+ * Amplitude Analytics SDK integration from set up using
390
+ * {@link Experiment.initializeWithAmplitudeAnalytics}.
391
+ *
392
+ * @param key The flag/experiment key to track an exposure for.
393
+ */
394
+ public exposure(key: string): void {
395
+ const sourceVariant = this.variantAndSource(key);
396
+ this.exposureInternal(key, sourceVariant);
397
+ }
398
+
399
+ /**
400
+ * Returns all variants for the user.
401
+ *
402
+ * The primary source of variants is based on the
403
+ * {@link Source} configured in the {@link ExperimentConfig}.
404
+ *
405
+ * @see Source
406
+ * @see ExperimentConfig
407
+ */
408
+ public all(): Variants {
409
+ if (!this.apiKey) {
410
+ return {};
411
+ }
412
+ const evaluatedVariants = this.evaluate();
413
+ for (const flagKey in evaluatedVariants) {
414
+ const flag = this.flags.get(flagKey);
415
+ if (!isLocalEvaluationMode(flag)) {
416
+ delete evaluatedVariants[flagKey];
417
+ }
418
+ }
419
+ return {
420
+ ...this.secondaryVariants(),
421
+ ...this.sourceVariants(),
422
+ ...evaluatedVariants,
423
+ };
424
+ }
425
+
426
+ /**
427
+ * Clear all variants in the cache and storage.
428
+ */
429
+ public clear(): void {
430
+ this.variants.clear();
431
+ void this.variants.store().catch((e) => this.logger.warn(e));
432
+ }
433
+
434
+ /**
435
+ * Get a copy of the internal {@link ExperimentUser} object if it is set.
436
+ *
437
+ * @returns a copy of the internal user object if set.
438
+ */
439
+ public getUser(): ExperimentUser {
440
+ if (!this.user) {
441
+ return {};
442
+ }
443
+ if (this.user?.user_properties) {
444
+ const userPropertiesCopy = { ...this.user.user_properties };
445
+ return { ...this.user, user_properties: userPropertiesCopy };
446
+ } else {
447
+ return { ...this.user };
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Copy in and set the user within the experiment client.
453
+ *
454
+ * @param user the user to set within the experiment client.
455
+ */
456
+ public setUser(user: ExperimentUser): void {
457
+ if (!user) {
458
+ this.user = {};
459
+ return;
460
+ }
461
+ if (this.user?.user_properties) {
462
+ const userPropertiesCopy = { ...user.user_properties };
463
+ this.user = { ...user, user_properties: userPropertiesCopy };
464
+ } else {
465
+ this.user = { ...user };
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Get the user provider set by {@link setUserProvider} or null if the user
471
+ * provider has not been set.
472
+ *
473
+ * @returns The user provider set by {@link setUserProvider} or null.
474
+ * @deprecated use ExperimentConfig.userProvider instead
475
+ */
476
+ public getUserProvider(): ExperimentUserProvider {
477
+ return this.defaultUserProvider;
478
+ }
479
+
480
+ /**
481
+ * Sets a user provider that will inject identity information into the user
482
+ * for {@link fetch()} requests. The user provider will only set user fields
483
+ * in outgoing requests which are null or undefined.
484
+ *
485
+ * See {@link ExperimentUserProvider} for more details
486
+ * @param userProvider
487
+ * @deprecated use ExperimentConfig.userProvider instead
488
+ */
489
+ public setUserProvider(userProvider: ExperimentUserProvider): Client {
490
+ this.defaultUserProvider.baseProvider = userProvider;
491
+ return this;
492
+ }
493
+
494
+ /**
495
+ * Enables or disables tracking of assignment events when fetching variants.
496
+ * @param doTrack Whether to track assignment events.
497
+ */
498
+ public async setTracksAssignment(doTrack: boolean): Promise<void> {
499
+ this.fetchVariantsOptions.put({
500
+ ...this.fetchVariantsOptions.get(),
501
+ trackingOption: doTrack ? "track" : "no-track",
502
+ });
503
+ await this.fetchVariantsOptions.store();
504
+ }
505
+
506
+ public addStopCallback(callback: () => void): void {
507
+ this.stopCallbacks.add(callback);
508
+ }
509
+
510
+ public fetchOnIdentityChange(): void {
511
+ void this.fetch().catch((e) => this.logger.warn(e));
512
+ }
513
+
514
+ private convertInitialFlagsForStorage(): Record<string, EvaluationFlag> {
515
+ if (this.initialFlags) {
516
+ const flagsMap: Record<string, EvaluationFlag> = {};
517
+ this.initialFlags.forEach((flag: EvaluationFlag) => {
518
+ flagsMap[flag.key] = flag;
519
+ });
520
+ return flagsMap;
521
+ }
522
+ return {};
523
+ }
524
+
525
+ private mergeInitialFlagsWithStorage(): void {
526
+ if (this.initialFlags) {
527
+ this.initialFlags.forEach((flag: EvaluationFlag) => {
528
+ if (!this.flags.get(flag.key)) {
529
+ this.flags.put(flag.key, flag);
530
+ }
531
+ });
532
+ }
533
+ }
534
+
535
+ private evaluate(flagKeys?: string[]): Variants {
536
+ const user = this.addContextSync(this.user);
537
+ const flags = topologicalSort(this.flags.getAll(), flagKeys);
538
+ const context = convertUserToContext(user);
539
+ const evaluationVariants = this.engine.evaluate(context, flags);
540
+ const variants: Variants = {};
541
+ for (const flagKey of Object.keys(evaluationVariants)) {
542
+ variants[flagKey] = convertEvaluationVariantToVariant(
543
+ evaluationVariants[flagKey],
544
+ );
545
+ }
546
+ return variants;
547
+ }
548
+
549
+ private variantAndSource(
550
+ key: string,
551
+ fallback?: string | Variant,
552
+ ): SourceVariant {
553
+ let sourceVariant: SourceVariant = {};
554
+ if (this.config.source === Source.LocalStorage) {
555
+ sourceVariant = this.localStorageVariantAndSource(key, fallback);
556
+ } else if (this.config.source === Source.InitialVariants) {
557
+ sourceVariant = this.initialVariantsVariantAndSource(key, fallback);
558
+ }
559
+ const flag = this.flags.get(key);
560
+ if (flag && (isLocalEvaluationMode(flag) || !sourceVariant.variant)) {
561
+ sourceVariant = this.localEvaluationVariantAndSource(key, flag, fallback);
562
+ }
563
+ return sourceVariant;
564
+ }
565
+
566
+ /**
567
+ * This function assumes the flag exists and is local evaluation mode. For
568
+ * local evaluation, fallback order goes:
569
+ *
570
+ * 1. Local evaluation
571
+ * 2. Inline function fallback
572
+ * 3. Initial variants
573
+ * 4. Config fallback
574
+ *
575
+ * If there is a default variant and no fallback, return the default variant.
576
+ */
577
+ private localEvaluationVariantAndSource(
578
+ key: string,
579
+ flag: EvaluationFlag,
580
+ fallback?: string | Variant,
581
+ ): SourceVariant {
582
+ let defaultSourceVariant: SourceVariant = {};
583
+ // Local evaluation
584
+ const variant = this.evaluate([flag.key])[key];
585
+ const source = VariantSource.LocalEvaluation;
586
+ const isLocalEvaluationDefault = variant?.metadata?.default as boolean;
587
+ if (!isNullOrUndefined(variant) && !isLocalEvaluationDefault) {
588
+ return {
589
+ variant: convertVariant(variant),
590
+ source: source,
591
+ hasDefaultVariant: false,
592
+ };
593
+ } else if (isLocalEvaluationDefault) {
594
+ defaultSourceVariant = {
595
+ variant: convertVariant(variant),
596
+ source: source,
597
+ hasDefaultVariant: true,
598
+ };
599
+ }
600
+ // Inline fallback
601
+ if (!isNullOrUndefined(fallback)) {
602
+ return {
603
+ variant: convertVariant(fallback),
604
+ source: VariantSource.FallbackInline,
605
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
606
+ };
607
+ }
608
+ // Initial variants
609
+ const initialVariant = this.config.initialVariants[key];
610
+ if (!isNullOrUndefined(initialVariant)) {
611
+ return {
612
+ variant: convertVariant(initialVariant),
613
+ source: VariantSource.SecondaryInitialVariants,
614
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
615
+ };
616
+ }
617
+ // Configured fallback, or default variant
618
+ const fallbackVariant = convertVariant(this.config.fallbackVariant);
619
+ const fallbackSourceVariant = {
620
+ variant: fallbackVariant,
621
+ source: VariantSource.FallbackConfig,
622
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
623
+ };
624
+ if (!isNullUndefinedOrEmpty(fallbackVariant)) {
625
+ return fallbackSourceVariant;
626
+ }
627
+ return defaultSourceVariant;
628
+ }
629
+
630
+ /**
631
+ * For Source.LocalStorage, fallback order goes:
632
+ *
633
+ * 1. Local Storage
634
+ * 2. Inline function fallback
635
+ * 3. InitialFlags
636
+ * 4. Config fallback
637
+ *
638
+ * If there is a default variant and no fallback, return the default variant.
639
+ */
640
+ private localStorageVariantAndSource(
641
+ key: string,
642
+ fallback?: string | Variant,
643
+ ): SourceVariant {
644
+ let defaultSourceVariant: SourceVariant = {};
645
+ // Local storage
646
+ const localStorageVariant = this.variants.get(key);
647
+ const isLocalStorageDefault = localStorageVariant?.metadata
648
+ ?.default as boolean;
649
+ if (!isNullOrUndefined(localStorageVariant) && !isLocalStorageDefault) {
650
+ return {
651
+ variant: convertVariant(localStorageVariant),
652
+ source: VariantSource.LocalStorage,
653
+ hasDefaultVariant: false,
654
+ };
655
+ } else if (isLocalStorageDefault) {
656
+ defaultSourceVariant = {
657
+ variant: convertVariant(localStorageVariant),
658
+ source: VariantSource.LocalStorage,
659
+ hasDefaultVariant: true,
660
+ };
661
+ }
662
+ // Inline fallback
663
+ if (!isNullOrUndefined(fallback)) {
664
+ return {
665
+ variant: convertVariant(fallback),
666
+ source: VariantSource.FallbackInline,
667
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
668
+ };
669
+ }
670
+ // Initial variants
671
+ const initialVariant = this.config.initialVariants[key];
672
+ if (!isNullOrUndefined(initialVariant)) {
673
+ return {
674
+ variant: convertVariant(initialVariant),
675
+ source: VariantSource.SecondaryInitialVariants,
676
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
677
+ };
678
+ }
679
+ // Configured fallback, or default variant
680
+ const fallbackVariant = convertVariant(this.config.fallbackVariant);
681
+ const fallbackSourceVariant = {
682
+ variant: fallbackVariant,
683
+ source: VariantSource.FallbackConfig,
684
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
685
+ };
686
+ if (!isNullUndefinedOrEmpty(fallbackVariant)) {
687
+ return fallbackSourceVariant;
688
+ }
689
+ return defaultSourceVariant;
690
+ }
691
+
692
+ /**
693
+ * For Source.InitialVariants, fallback order goes:
694
+ *
695
+ * 1. Initial variants
696
+ * 2. Local storage
697
+ * 3. Inline function fallback
698
+ * 4. Config fallback
699
+ *
700
+ * If there is a default variant and no fallback, return the default variant.
701
+ */
702
+ private initialVariantsVariantAndSource(
703
+ key: string,
704
+ fallback?: string | Variant,
705
+ ): SourceVariant {
706
+ let defaultSourceVariant: SourceVariant = {};
707
+ // Initial variants
708
+ const initialVariantsVariant = this.config.initialVariants[key];
709
+ if (!isNullOrUndefined(initialVariantsVariant)) {
710
+ return {
711
+ variant: convertVariant(initialVariantsVariant),
712
+ source: VariantSource.InitialVariants,
713
+ hasDefaultVariant: false,
714
+ };
715
+ }
716
+ // Local storage
717
+ const localStorageVariant = this.variants.get(key);
718
+ const isLocalStorageDefault = localStorageVariant?.metadata
719
+ ?.default as boolean;
720
+ if (!isNullOrUndefined(localStorageVariant) && !isLocalStorageDefault) {
721
+ return {
722
+ variant: convertVariant(localStorageVariant),
723
+ source: VariantSource.LocalStorage,
724
+ hasDefaultVariant: false,
725
+ };
726
+ } else if (isLocalStorageDefault) {
727
+ defaultSourceVariant = {
728
+ variant: convertVariant(localStorageVariant),
729
+ source: VariantSource.LocalStorage,
730
+ hasDefaultVariant: true,
731
+ };
732
+ }
733
+ // Inline fallback
734
+ if (!isNullOrUndefined(fallback)) {
735
+ return {
736
+ variant: convertVariant(fallback),
737
+ source: VariantSource.FallbackInline,
738
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
739
+ };
740
+ }
741
+ // Configured fallback, or default variant
742
+ const fallbackVariant = convertVariant(this.config.fallbackVariant);
743
+ const fallbackSourceVariant = {
744
+ variant: fallbackVariant,
745
+ source: VariantSource.FallbackConfig,
746
+ hasDefaultVariant: defaultSourceVariant.hasDefaultVariant,
747
+ };
748
+ if (!isNullUndefinedOrEmpty(fallbackVariant)) {
749
+ return fallbackSourceVariant;
750
+ }
751
+ return defaultSourceVariant;
752
+ }
753
+
754
+ private async fetchInternal(
755
+ user: ExperimentUser,
756
+ timeoutMillis: number,
757
+ retry: boolean,
758
+ options?: FetchOptions,
759
+ ): Promise<Variants> {
760
+ // Don't even try to fetch variants if API key is not set
761
+ if (!this.apiKey) {
762
+ throw Error("Experiment API key is empty");
763
+ }
764
+
765
+ this.logger.debug(`[Experiment] Fetch all: retry=${retry}`);
766
+
767
+ // Proactively cancel retries if active in order to avoid unnecessary API
768
+ // requests. A new failure will restart the retries.
769
+ if (retry) {
770
+ this.stopRetries();
771
+ }
772
+
773
+ const sequenceNumber = ++this.fetchSequenceNumber;
774
+
775
+ try {
776
+ const variants = await this.doFetch(user, timeoutMillis, options);
777
+ await this.storeVariants(variants, sequenceNumber, options);
778
+ return variants;
779
+ } catch (e: unknown) {
780
+ if (retry && this.shouldRetryFetch(e)) {
781
+ this.startRetries(user, options);
782
+ }
783
+ throw e;
784
+ }
785
+ }
786
+
787
+ private async fetchWithRetries(
788
+ user: ExperimentUser,
789
+ options?: FetchOptions,
790
+ ): Promise<Variants> {
791
+ return await this.fetchInternal(
792
+ user,
793
+ this.config.fetchTimeoutMillis,
794
+ this.config.retryFetchOnFailure,
795
+ options,
796
+ );
797
+ }
798
+
799
+ private async doFetch(
800
+ user: ExperimentUser,
801
+ timeoutMillis: number,
802
+ options?: FetchOptions,
803
+ ): Promise<Variants> {
804
+ user = await this.addContextOrWait(user, 10000);
805
+ this.logger.debug("[Experiment] Fetch variants for user: ", user);
806
+ const results = await this.evaluationApi.getVariants(user, {
807
+ ...this.fetchVariantsOptions.get(),
808
+ timeoutMillis: timeoutMillis,
809
+ flagKeys: options?.flagKeys,
810
+ });
811
+ const variants: Variants = {};
812
+ for (const key of Object.keys(results)) {
813
+ variants[key] = convertEvaluationVariantToVariant(results[key]);
814
+ }
815
+ this.logger.debug("[Experiment] Received variants: ", variants);
816
+ return variants;
817
+ }
818
+
819
+ private async doFlags(): Promise<void> {
820
+ const flags = await this.flagApi.getFlags({
821
+ libraryName: "experiment-js-client",
822
+ libraryVersion: PACKAGE_VERSION,
823
+ timeoutMillis: this.config.fetchTimeoutMillis,
824
+ });
825
+ this.flags.clear();
826
+ this.flags.putAll(flags);
827
+ await this.flags.store();
828
+ this.mergeInitialFlagsWithStorage();
829
+ }
830
+
831
+ private async pollFlags(): Promise<void> {
832
+ try {
833
+ await this.doFlags();
834
+ } catch (e) {
835
+ this.logger.warn(e);
836
+ }
837
+ }
838
+
839
+ private async storeVariants(
840
+ variants: Variants,
841
+ sequenceNumber: number,
842
+ options?: FetchOptions,
843
+ ): Promise<void> {
844
+ if (sequenceNumber <= this.storedFetchSequenceNumber) {
845
+ this.logger.debug(
846
+ `[Experiment] Ignoring stale fetch response (sequence ${sequenceNumber} <= ${this.storedFetchSequenceNumber})`,
847
+ );
848
+ return;
849
+ }
850
+
851
+ let failedFlagKeys = options?.flagKeys ? options.flagKeys : [];
852
+ if (failedFlagKeys.length === 0) {
853
+ this.variants.clear();
854
+ }
855
+ for (const key in variants) {
856
+ failedFlagKeys = failedFlagKeys.filter((flagKey) => flagKey !== key);
857
+ this.variants.put(key, variants[key]);
858
+ }
859
+
860
+ for (const key in failedFlagKeys) {
861
+ this.variants.remove(key);
862
+ }
863
+ await this.variants.store();
864
+ this.storedFetchSequenceNumber = sequenceNumber;
865
+ this.logger.debug("[Experiment] Stored variants: ", variants);
866
+ }
867
+
868
+ private startRetries(user: ExperimentUser, options?: FetchOptions): void {
869
+ this.logger.debug("[Experiment] Retry fetch");
870
+ this.retriesBackoff = new Backoff(
871
+ fetchBackoffAttempts,
872
+ fetchBackoffMinMillis,
873
+ fetchBackoffMaxMillis,
874
+ fetchBackoffScalar,
875
+ );
876
+ this.retriesBackoff.start(async () => {
877
+ await this.fetchInternal(user, fetchBackoffTimeout, false, options);
878
+ });
879
+ }
880
+
881
+ private stopRetries(): void {
882
+ if (this.retriesBackoff != null) {
883
+ this.retriesBackoff.cancel();
884
+ }
885
+ }
886
+
887
+ private addContextSync(user: ExperimentUser): ExperimentUser {
888
+ const providedUser = this.defaultUserProvider.getUserSync();
889
+ return this.mergeContext(user, providedUser);
890
+ }
891
+
892
+ private async addContext(user: ExperimentUser): Promise<ExperimentUser> {
893
+ const providedUser = await this.defaultUserProvider.getUser();
894
+ return this.mergeContext(user, providedUser);
895
+ }
896
+
897
+ private async addContextOrWait(
898
+ user: ExperimentUser,
899
+ ms: number,
900
+ ): Promise<ExperimentUser> {
901
+ const baseProvider = this.defaultUserProvider.baseProvider;
902
+ if (baseProvider instanceof ConnectorUserProvider) {
903
+ await baseProvider.identityReady(ms);
904
+ }
905
+ return this.addContext(user);
906
+ }
907
+
908
+ private mergeContext(
909
+ user: ExperimentUser,
910
+ providedUser: ExperimentUser,
911
+ ): ExperimentUser {
912
+ const mergedUserProperties = {
913
+ ...user?.user_properties,
914
+ ...providedUser?.user_properties,
915
+ };
916
+ return {
917
+ library: `experiment-react-native-client/${PACKAGE_VERSION}`,
918
+ ...providedUser,
919
+ ...user,
920
+ user_properties: mergedUserProperties,
921
+ };
922
+ }
923
+
924
+ private sourceVariants(): Variants {
925
+ if (this.config.source === Source.LocalStorage) {
926
+ return this.variants.getAll();
927
+ } else if (this.config.source === Source.InitialVariants) {
928
+ return this.config.initialVariants;
929
+ }
930
+ return {};
931
+ }
932
+
933
+ private secondaryVariants(): Variants {
934
+ if (this.config.source === Source.LocalStorage) {
935
+ return this.config.initialVariants;
936
+ } else if (this.config.source === Source.InitialVariants) {
937
+ return this.variants.getAll();
938
+ }
939
+ return {};
940
+ }
941
+
942
+ private exposureInternal(key: string, sourceVariant: SourceVariant): void {
943
+ const exposure: Exposure = { flag_key: key };
944
+ // Do not track exposure for fallback variants that are not associated with
945
+ // a default variant.
946
+ const fallback = isFallback(sourceVariant.source);
947
+ if (fallback && !sourceVariant.hasDefaultVariant) {
948
+ return;
949
+ }
950
+ if (sourceVariant.variant?.expKey) {
951
+ exposure.experiment_key = sourceVariant.variant?.expKey;
952
+ }
953
+ const metadata = sourceVariant.variant?.metadata;
954
+ if (!fallback && !metadata?.default) {
955
+ if (sourceVariant.variant?.key) {
956
+ exposure.variant = sourceVariant.variant.key;
957
+ } else if (sourceVariant.variant?.value) {
958
+ exposure.variant = sourceVariant.variant.value;
959
+ }
960
+ }
961
+ if (metadata) exposure.metadata = metadata;
962
+ const user = this.addContextSync(this.getUser());
963
+ this.userSessionExposureTracker?.track(exposure, user);
964
+ }
965
+
966
+ private static getLogLevel(config: ExperimentConfig): LogLevel {
967
+ // Backwards compatibility: if debug flag is set to true, use Debug level
968
+ if (config.debug === true) {
969
+ return LogLevel.Debug;
970
+ }
971
+ // Otherwise use the configured logLevel or default to Warn
972
+ return config.logLevel ?? LogLevel.Warn;
973
+ }
974
+
975
+ private shouldRetryFetch(e: unknown): boolean {
976
+ if (e instanceof FetchError) {
977
+ return e.statusCode < 400 || e.statusCode >= 500 || e.statusCode === 429;
978
+ }
979
+ return true;
980
+ }
981
+ }
982
+
983
+ type SourceVariant = {
984
+ variant?: Variant | undefined;
985
+ source?: VariantSource | undefined;
986
+ hasDefaultVariant?: boolean | undefined;
987
+ };