@hot-updater/react-native 1.0.0-rc.16 → 1.0.0-rc.17

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 (316) hide show
  1. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +491 -388
  2. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +5 -11
  3. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  4. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +290 -12
  5. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +35 -19
  6. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  7. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  8. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  9. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  10. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +110 -52
  11. package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
  12. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  13. package/android/src/newarch/HotUpdaterModule.kt +74 -69
  14. package/android/src/oldarch/HotUpdaterModule.kt +63 -59
  15. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  16. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +1 -1
  17. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +446 -61
  18. package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +220 -0
  19. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +19 -27
  20. package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +71 -0
  21. package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +106 -0
  22. package/android/src/test/java/com/hotupdater/NoBackupContext.kt +11 -0
  23. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +309 -23
  24. package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +57 -0
  25. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +238 -157
  26. package/ios/HotUpdater/Internal/BundleMetadata.swift +3 -53
  27. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  28. package/ios/HotUpdater/Internal/HotUpdater.mm +8 -18
  29. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +48 -29
  30. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  31. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +108 -1
  32. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  33. package/lib/commonjs/appReady.js +111 -0
  34. package/lib/commonjs/appReady.js.map +1 -0
  35. package/lib/commonjs/appReady.spec.js +360 -0
  36. package/lib/commonjs/appReady.spec.js.map +1 -0
  37. package/lib/commonjs/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
  38. package/lib/commonjs/appReady.test-utils.js.map +1 -0
  39. package/lib/commonjs/baseURL.js +16 -0
  40. package/lib/commonjs/baseURL.js.map +1 -0
  41. package/lib/commonjs/checkForUpdate.js +104 -56
  42. package/lib/commonjs/checkForUpdate.js.map +1 -1
  43. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +337 -74
  44. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  45. package/lib/commonjs/clientPlugin.js +49 -0
  46. package/lib/commonjs/clientPlugin.js.map +1 -0
  47. package/lib/commonjs/entryImportGraph.spec.js +50 -0
  48. package/lib/commonjs/entryImportGraph.spec.js.map +1 -0
  49. package/lib/commonjs/fetchJSON.js +2 -6
  50. package/lib/commonjs/fetchJSON.js.map +1 -1
  51. package/lib/commonjs/httpClient.js +18 -165
  52. package/lib/commonjs/httpClient.js.map +1 -1
  53. package/lib/commonjs/httpClient.spec.js +26 -199
  54. package/lib/commonjs/httpClient.spec.js.map +1 -1
  55. package/lib/commonjs/index.js +49 -36
  56. package/lib/commonjs/index.js.map +1 -1
  57. package/lib/commonjs/index.spec.js +108 -123
  58. package/lib/commonjs/index.spec.js.map +1 -1
  59. package/lib/commonjs/native.js +67 -48
  60. package/lib/commonjs/native.js.map +1 -1
  61. package/lib/commonjs/native.spec.js +80 -58
  62. package/lib/commonjs/native.spec.js.map +1 -1
  63. package/lib/commonjs/pluginHost.js +208 -0
  64. package/lib/commonjs/pluginHost.js.map +1 -0
  65. package/lib/commonjs/pluginHost.spec.js +292 -0
  66. package/lib/commonjs/pluginHost.spec.js.map +1 -0
  67. package/lib/commonjs/plugins/insights/eventId.js +129 -0
  68. package/lib/commonjs/plugins/insights/eventId.js.map +1 -0
  69. package/lib/commonjs/plugins/insights/eventId.spec.js +47 -0
  70. package/lib/commonjs/plugins/insights/eventId.spec.js.map +1 -0
  71. package/lib/commonjs/plugins/insights/index.js +272 -0
  72. package/lib/commonjs/plugins/insights/index.js.map +1 -0
  73. package/lib/commonjs/plugins/insights/insights.spec.js +640 -0
  74. package/lib/commonjs/plugins/insights/insights.spec.js.map +1 -0
  75. package/lib/commonjs/plugins/insights/sender.js +128 -0
  76. package/lib/commonjs/plugins/insights/sender.js.map +1 -0
  77. package/lib/commonjs/plugins/insights/sender.spec.js +265 -0
  78. package/lib/commonjs/plugins/insights/sender.spec.js.map +1 -0
  79. package/lib/commonjs/plugins/insights/state.js +145 -0
  80. package/lib/commonjs/plugins/insights/state.js.map +1 -0
  81. package/lib/commonjs/releaseCatalogCache.js +13 -2
  82. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  83. package/lib/commonjs/releaseCatalogCache.spec.js +18 -0
  84. package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -1
  85. package/lib/commonjs/sdkVersion.js +1 -1
  86. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  87. package/lib/commonjs/updateError.js +132 -0
  88. package/lib/commonjs/updateError.js.map +1 -0
  89. package/lib/commonjs/updateError.spec.js +149 -0
  90. package/lib/commonjs/updateError.spec.js.map +1 -0
  91. package/lib/commonjs/wrap.js +43 -25
  92. package/lib/commonjs/wrap.js.map +1 -1
  93. package/lib/commonjs/wrap.progress.spec.js +195 -0
  94. package/lib/commonjs/wrap.progress.spec.js.map +1 -0
  95. package/lib/commonjs/wrap.spec.js +94 -230
  96. package/lib/commonjs/wrap.spec.js.map +1 -1
  97. package/lib/module/appReady.js +103 -0
  98. package/lib/module/appReady.js.map +1 -0
  99. package/lib/module/appReady.spec.js +359 -0
  100. package/lib/module/appReady.spec.js.map +1 -0
  101. package/lib/module/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
  102. package/lib/module/appReady.test-utils.js.map +1 -0
  103. package/lib/module/baseURL.js +11 -0
  104. package/lib/module/baseURL.js.map +1 -0
  105. package/lib/module/checkForUpdate.js +103 -57
  106. package/lib/module/checkForUpdate.js.map +1 -1
  107. package/lib/module/checkForUpdate.releaseCatalog.spec.js +337 -74
  108. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  109. package/lib/module/clientPlugin.js +44 -0
  110. package/lib/module/clientPlugin.js.map +1 -0
  111. package/lib/module/entryImportGraph.spec.js +50 -0
  112. package/lib/module/entryImportGraph.spec.js.map +1 -0
  113. package/lib/module/fetchJSON.js +2 -6
  114. package/lib/module/fetchJSON.js.map +1 -1
  115. package/lib/module/httpClient.js +18 -165
  116. package/lib/module/httpClient.js.map +1 -1
  117. package/lib/module/httpClient.spec.js +27 -200
  118. package/lib/module/httpClient.spec.js.map +1 -1
  119. package/lib/module/index.js +44 -38
  120. package/lib/module/index.js.map +1 -1
  121. package/lib/module/index.spec.js +108 -123
  122. package/lib/module/index.spec.js.map +1 -1
  123. package/lib/module/native.js +63 -44
  124. package/lib/module/native.js.map +1 -1
  125. package/lib/module/native.spec.js +80 -58
  126. package/lib/module/native.spec.js.map +1 -1
  127. package/lib/module/pluginHost.js +200 -0
  128. package/lib/module/pluginHost.js.map +1 -0
  129. package/lib/module/pluginHost.spec.js +291 -0
  130. package/lib/module/pluginHost.spec.js.map +1 -0
  131. package/lib/module/plugins/insights/eventId.js +122 -0
  132. package/lib/module/plugins/insights/eventId.js.map +1 -0
  133. package/lib/module/plugins/insights/eventId.spec.js +47 -0
  134. package/lib/module/plugins/insights/eventId.spec.js.map +1 -0
  135. package/lib/module/plugins/insights/index.js +268 -0
  136. package/lib/module/plugins/insights/index.js.map +1 -0
  137. package/lib/module/plugins/insights/insights.spec.js +639 -0
  138. package/lib/module/plugins/insights/insights.spec.js.map +1 -0
  139. package/lib/module/plugins/insights/sender.js +123 -0
  140. package/lib/module/plugins/insights/sender.js.map +1 -0
  141. package/lib/module/plugins/insights/sender.spec.js +265 -0
  142. package/lib/module/plugins/insights/sender.spec.js.map +1 -0
  143. package/lib/module/plugins/insights/state.js +139 -0
  144. package/lib/module/plugins/insights/state.js.map +1 -0
  145. package/lib/module/releaseCatalogCache.js +13 -2
  146. package/lib/module/releaseCatalogCache.js.map +1 -1
  147. package/lib/module/releaseCatalogCache.spec.js +18 -0
  148. package/lib/module/releaseCatalogCache.spec.js.map +1 -1
  149. package/lib/module/sdkVersion.js +1 -1
  150. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  151. package/lib/module/updateError.js +126 -0
  152. package/lib/module/updateError.js.map +1 -0
  153. package/lib/module/updateError.spec.js +149 -0
  154. package/lib/module/updateError.spec.js.map +1 -0
  155. package/lib/module/wrap.js +43 -25
  156. package/lib/module/wrap.js.map +1 -1
  157. package/lib/module/wrap.progress.spec.js +196 -0
  158. package/lib/module/wrap.progress.spec.js.map +1 -0
  159. package/lib/module/wrap.spec.js +93 -229
  160. package/lib/module/wrap.spec.js.map +1 -1
  161. package/lib/typescript/commonjs/appReady.d.ts +23 -0
  162. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/appReady.test-utils.d.ts +9 -0
  164. package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/baseURL.d.ts +4 -0
  166. package/lib/typescript/commonjs/baseURL.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/checkForUpdate.d.ts +15 -2
  168. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  169. package/lib/typescript/commonjs/clientPlugin.d.ts +185 -0
  170. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/fetchJSON.d.ts +2 -3
  172. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/httpClient.d.ts +2 -36
  174. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/index.d.ts +3 -6
  176. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  177. package/lib/typescript/commonjs/native.d.ts +29 -15
  178. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/pluginHost.d.ts +29 -0
  180. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  181. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +18 -0
  182. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +1 -0
  183. package/lib/typescript/commonjs/plugins/insights/index.d.ts +40 -0
  184. package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/plugins/insights/sender.d.ts +74 -0
  186. package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/plugins/insights/state.d.ts +46 -0
  188. package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +5 -1
  190. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  191. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  192. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +34 -12
  193. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  195. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  196. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  197. package/lib/typescript/commonjs/wrap.types.d.ts +6 -10
  198. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  199. package/lib/typescript/module/appReady.d.ts +23 -0
  200. package/lib/typescript/module/appReady.d.ts.map +1 -0
  201. package/lib/typescript/module/appReady.test-utils.d.ts +9 -0
  202. package/lib/typescript/module/appReady.test-utils.d.ts.map +1 -0
  203. package/lib/typescript/module/baseURL.d.ts +4 -0
  204. package/lib/typescript/module/baseURL.d.ts.map +1 -0
  205. package/lib/typescript/module/checkForUpdate.d.ts +15 -2
  206. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  207. package/lib/typescript/module/clientPlugin.d.ts +185 -0
  208. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  209. package/lib/typescript/module/fetchJSON.d.ts +2 -3
  210. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  211. package/lib/typescript/module/httpClient.d.ts +2 -36
  212. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  213. package/lib/typescript/module/index.d.ts +3 -6
  214. package/lib/typescript/module/index.d.ts.map +1 -1
  215. package/lib/typescript/module/native.d.ts +29 -15
  216. package/lib/typescript/module/native.d.ts.map +1 -1
  217. package/lib/typescript/module/pluginHost.d.ts +29 -0
  218. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  219. package/lib/typescript/module/plugins/insights/eventId.d.ts +18 -0
  220. package/lib/typescript/module/plugins/insights/eventId.d.ts.map +1 -0
  221. package/lib/typescript/module/plugins/insights/index.d.ts +40 -0
  222. package/lib/typescript/module/plugins/insights/index.d.ts.map +1 -0
  223. package/lib/typescript/module/plugins/insights/sender.d.ts +74 -0
  224. package/lib/typescript/module/plugins/insights/sender.d.ts.map +1 -0
  225. package/lib/typescript/module/plugins/insights/state.d.ts +46 -0
  226. package/lib/typescript/module/plugins/insights/state.d.ts.map +1 -0
  227. package/lib/typescript/module/releaseCatalogCache.d.ts +5 -1
  228. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  229. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  230. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +34 -12
  231. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  232. package/lib/typescript/module/updateError.d.ts +26 -0
  233. package/lib/typescript/module/updateError.d.ts.map +1 -0
  234. package/lib/typescript/module/wrap.d.ts.map +1 -1
  235. package/lib/typescript/module/wrap.types.d.ts +6 -10
  236. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  237. package/package.json +14 -2
  238. package/plugins/insights/package.json +8 -0
  239. package/src/appReady.spec.ts +393 -0
  240. package/src/{notifyAppReadyInsights.test-utils.ts → appReady.test-utils.ts} +7 -7
  241. package/src/appReady.ts +149 -0
  242. package/src/baseURL.ts +15 -0
  243. package/src/checkForUpdate.releaseCatalog.spec.ts +370 -76
  244. package/src/checkForUpdate.ts +135 -80
  245. package/src/clientPlugin.ts +231 -0
  246. package/src/entryImportGraph.spec.ts +65 -0
  247. package/src/fetchJSON.ts +3 -10
  248. package/src/httpClient.spec.ts +30 -227
  249. package/src/httpClient.ts +20 -256
  250. package/src/index.spec.ts +117 -93
  251. package/src/index.ts +67 -52
  252. package/src/native.spec.ts +98 -72
  253. package/src/native.ts +93 -77
  254. package/src/pluginHost.spec.ts +297 -0
  255. package/src/pluginHost.ts +262 -0
  256. package/src/plugins/insights/eventId.spec.ts +60 -0
  257. package/src/plugins/insights/eventId.ts +171 -0
  258. package/src/plugins/insights/index.ts +367 -0
  259. package/src/plugins/insights/insights.spec.ts +732 -0
  260. package/src/plugins/insights/sender.spec.ts +287 -0
  261. package/src/plugins/insights/sender.ts +221 -0
  262. package/src/plugins/insights/state.ts +184 -0
  263. package/src/releaseCatalogCache.spec.ts +22 -0
  264. package/src/releaseCatalogCache.ts +19 -4
  265. package/src/sdkVersion.ts +1 -1
  266. package/src/specs/NativeHotUpdater.ts +34 -13
  267. package/src/updateError.spec.ts +144 -0
  268. package/src/updateError.ts +153 -0
  269. package/src/wrap.progress.spec.ts +232 -0
  270. package/src/wrap.spec.ts +100 -283
  271. package/src/wrap.tsx +57 -26
  272. package/src/wrap.types.ts +6 -12
  273. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -225
  274. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  275. package/lib/commonjs/notifyAppReadyInsights.js +0 -188
  276. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  277. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -378
  278. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  279. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  280. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -171
  281. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  282. package/lib/commonjs/uuidv7.js +0 -41
  283. package/lib/commonjs/uuidv7.js.map +0 -1
  284. package/lib/commonjs/uuidv7.spec.js +0 -30
  285. package/lib/commonjs/uuidv7.spec.js.map +0 -1
  286. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -224
  287. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  288. package/lib/module/notifyAppReadyInsights.js +0 -181
  289. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  290. package/lib/module/notifyAppReadyInsights.spec.js +0 -377
  291. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  292. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  293. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -170
  294. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  295. package/lib/module/uuidv7.js +0 -36
  296. package/lib/module/uuidv7.js.map +0 -1
  297. package/lib/module/uuidv7.spec.js +0 -30
  298. package/lib/module/uuidv7.spec.js.map +0 -1
  299. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -25
  300. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  301. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  302. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  303. package/lib/typescript/commonjs/uuidv7.d.ts +0 -10
  304. package/lib/typescript/commonjs/uuidv7.d.ts.map +0 -1
  305. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -25
  306. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  307. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  308. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  309. package/lib/typescript/module/uuidv7.d.ts +0 -10
  310. package/lib/typescript/module/uuidv7.d.ts.map +0 -1
  311. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -242
  312. package/src/notifyAppReadyInsights.spec.ts +0 -362
  313. package/src/notifyAppReadyInsights.transport.spec.ts +0 -196
  314. package/src/notifyAppReadyInsights.ts +0 -293
  315. package/src/uuidv7.spec.ts +0 -25
  316. package/src/uuidv7.ts +0 -38
@@ -1 +1 @@
1
- {"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../../src/wrap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAOnD,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,iBAAiB,EACjB,gCAAgC,EAChC,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAEtB,wBAAgB,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAEvD;AAED,wBAAgB,IAAI,CAClB,OAAO,EAAE,mBAAmB,GAC3B,CAAC,CAAC,SAAS,MAAM,EAClB,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KACrC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAuH1B"}
1
+ {"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../../../src/wrap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAOnD,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,iBAAiB,EACjB,gCAAgC,EAChC,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAEtB,wBAAgB,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAEvD;AAED,wBAAgB,IAAI,CAClB,OAAO,EAAE,mBAAmB,GAC3B,CAAC,CAAC,SAAS,MAAM,EAClB,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KACrC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAqJ1B"}
@@ -1,4 +1,5 @@
1
1
  import type React from "react";
2
+ import type { HotUpdaterClientPlugin } from "./clientPlugin";
2
3
  import type { HotUpdaterError } from "./error";
3
4
  import type { HotUpdaterHttpClient } from "./httpClient";
4
5
  import type { NotifyAppReadyResult } from "./native";
@@ -20,15 +21,12 @@ export type HotUpdaterFallbackComponentProps = {
20
21
  };
21
22
  interface CommonHotUpdaterOptions {
22
23
  /**
23
- * Sends app-ready transitions and same-Bundle Release adoptions to the
24
- * configured server. Defaults to `true`; set to `false` to disable client
25
- * reporting. Debug builds (`__DEV__`) send nothing unless you pass
26
- * `{ debug: true }`, so development sessions stay out of production data.
24
+ * Client plugins, such as `insights()` from
25
+ * `@hot-updater/react-native/plugins/insights`. Each plugin's `setup` runs
26
+ * once, and its hooks observe launches, update checks, downloads, and
27
+ * update failures. Plugin ids must be unique.
27
28
  */
28
- insights?: boolean | {
29
- /** Also report from debug builds. Defaults to `false`. */
30
- debug?: boolean;
31
- };
29
+ plugins?: readonly HotUpdaterClientPlugin[];
32
30
  /** Base URL of a server exposing the Hot Updater v1 client HTTP protocol. */
33
31
  baseURL: HotUpdaterBaseURL;
34
32
  requestHeaders?: Record<string, string>;
@@ -46,8 +44,6 @@ export type AutoUpdateOptions = CommonHotUpdaterOptions & {
46
44
  export type HotUpdaterInitOptions = CommonHotUpdaterOptions;
47
45
  export type HotUpdaterOptions = AutoUpdateOptions;
48
46
  type InternalCommonOptions = {
49
- /** Whether this runtime reports, with the debug-build default applied. */
50
- insights?: boolean;
51
47
  client: HotUpdaterHttpClient;
52
48
  requestHeaders?: Record<string, string>;
53
49
  requestTimeout?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"wrap.types.d.ts","sourceRoot":"","sources":["../../../src/wrap.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wrap.types.d.ts","sourceRoot":"","sources":["../../../src/wrap.types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/react-native",
3
- "version": "1.0.0-rc.16",
3
+ "version": "1.0.0-rc.17",
4
4
  "description": "React Native OTA solution for self-hosted",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/commonjs/index.js",
@@ -18,11 +18,23 @@
18
18
  "default": "./lib/commonjs/index.js"
19
19
  }
20
20
  },
21
+ "./plugins/insights": {
22
+ "source": "./src/plugins/insights/index.ts",
23
+ "import": {
24
+ "types": "./lib/typescript/module/plugins/insights/index.d.ts",
25
+ "default": "./lib/module/plugins/insights/index.js"
26
+ },
27
+ "require": {
28
+ "types": "./lib/typescript/commonjs/plugins/insights/index.d.ts",
29
+ "default": "./lib/commonjs/plugins/insights/index.js"
30
+ }
31
+ },
21
32
  "./package.json": "./package.json"
22
33
  },
23
34
  "files": [
24
35
  "src",
25
36
  "lib",
37
+ "plugins",
26
38
  "android",
27
39
  "ios",
28
40
  "cpp",
@@ -121,7 +133,7 @@
121
133
  "dependencies": {
122
134
  "use-sync-external-store": "1.5.0",
123
135
  "@hot-updater/core": "1.0.0-rc.15",
124
- "@hot-updater/plugin-core": "1.0.0-rc.16"
136
+ "@hot-updater/plugin-core": "1.0.0-rc.17"
125
137
  },
126
138
  "scripts": {
127
139
  "build": "node ./scripts/sync-sdk-version.mjs run -- bob build",
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@hot-updater/react-native/plugins/insights",
3
+ "private": true,
4
+ "source": "../../src/plugins/insights/index.ts",
5
+ "main": "../../lib/commonjs/plugins/insights/index.js",
6
+ "module": "../../lib/module/plugins/insights/index.js",
7
+ "types": "../../lib/typescript/commonjs/plugins/insights/index.d.ts"
8
+ }
@@ -0,0 +1,393 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ import { createNotifyReadResult, stubNotifyFrame } from "./appReady.test-utils";
4
+ import type {
5
+ AppReadyResult,
6
+ HotUpdaterClientHooks,
7
+ HotUpdaterClientPlugin,
8
+ } from "./clientPlugin";
9
+ import type {
10
+ ActiveUpdateState,
11
+ LaunchTransition,
12
+ NotifyAppReadyResult,
13
+ } from "./native";
14
+
15
+ vi.mock("react-native", () => ({
16
+ Platform: { OS: "ios" },
17
+ }));
18
+
19
+ const mocks = vi.hoisted(() => {
20
+ Reflect.set(globalThis, "HotUpdater", { SDK_VERSION: "test-sdk-version" });
21
+ return {
22
+ getAppVersion: vi.fn(() => "1.0.0"),
23
+ getBundleId: vi.fn(() => "bundle-id"),
24
+ getChannel: vi.fn(() => "production"),
25
+ getActiveUpdateState: vi.fn<() => ActiveUpdateState>(),
26
+ getCohort: vi.fn(() => "123"),
27
+ getFingerprintHash: vi.fn(() => "fingerprint-hash"),
28
+ getInstallId: vi.fn(() => "install-id"),
29
+ getStorageItem: vi.fn<(key: string) => string | null>(() => null),
30
+ setStorageItem: vi.fn(),
31
+ readNotifyAppReady: vi.fn<
32
+ () => {
33
+ transition: LaunchTransition | null;
34
+ pending: boolean;
35
+ result: NotifyAppReadyResult;
36
+ }
37
+ >(() => createNotifyReadResult()),
38
+ };
39
+ });
40
+
41
+ vi.mock("./native", () => mocks);
42
+
43
+ const recordingPlugin = (
44
+ hooks: (events: unknown[]) => HotUpdaterClientHooks = (events) => ({
45
+ onAppReady: (result) => {
46
+ events.push(["onAppReady", result]);
47
+ },
48
+ onBundleDownloaded: (info) => {
49
+ events.push(["onBundleDownloaded", info]);
50
+ },
51
+ }),
52
+ ) => {
53
+ const events: unknown[] = [];
54
+ const plugin: HotUpdaterClientPlugin = {
55
+ id: "recorder",
56
+ setup: () => hooks(events),
57
+ };
58
+ return { events, plugin };
59
+ };
60
+
61
+ const configure = async (
62
+ plugins: HotUpdaterClientPlugin[],
63
+ onError?: (error: unknown) => void,
64
+ ) => {
65
+ const { configurePlugins } = await import("./pluginHost");
66
+ configurePlugins(plugins, {
67
+ baseURL: "https://updates.example.com",
68
+ ...(onError ? { onError } : {}),
69
+ });
70
+ };
71
+
72
+ describe("app-ready reporting to plugins", () => {
73
+ beforeEach(() => {
74
+ vi.resetModules();
75
+ vi.unstubAllGlobals();
76
+ vi.useRealTimers();
77
+ for (const mock of Object.values(mocks)) mock.mockReset();
78
+ mocks.getAppVersion.mockReturnValue("1.0.0");
79
+ mocks.getBundleId.mockReturnValue("bundle-id");
80
+ mocks.getChannel.mockReturnValue("production");
81
+ mocks.getActiveUpdateState.mockReturnValue({
82
+ activeSelection: null,
83
+ stableSelection: null,
84
+ verificationPending: false,
85
+ });
86
+ mocks.getCohort.mockReturnValue("123");
87
+ mocks.getFingerprintHash.mockReturnValue("fingerprint-hash");
88
+ mocks.getInstallId.mockReturnValue("install-id");
89
+ mocks.getStorageItem.mockReturnValue(null);
90
+ mocks.readNotifyAppReady.mockReturnValue(createNotifyReadResult());
91
+ });
92
+
93
+ it.each([
94
+ {
95
+ label: "running selection",
96
+ pending: false,
97
+ channel: "production",
98
+ expected: "release-running",
99
+ },
100
+ {
101
+ label: "staged selection",
102
+ pending: true,
103
+ channel: "production",
104
+ expected: "release-running",
105
+ },
106
+ {
107
+ label: "another channel",
108
+ pending: false,
109
+ channel: "preview",
110
+ expected: null,
111
+ },
112
+ ])(
113
+ "attributes an unchanged launch with $label",
114
+ async ({ pending, channel, expected }) => {
115
+ stubNotifyFrame();
116
+ const running = {
117
+ kind: "BUNDLE" as const,
118
+ releaseId: "release-running",
119
+ bundleId: "bundle-id",
120
+ channel,
121
+ };
122
+ mocks.getActiveUpdateState.mockReturnValue({
123
+ activeSelection: pending
124
+ ? {
125
+ ...running,
126
+ releaseId: "release-pending",
127
+ bundleId: "bundle-pending",
128
+ }
129
+ : running,
130
+ stableSelection: pending ? running : null,
131
+ verificationPending: pending,
132
+ });
133
+ const { events, plugin } = recordingPlugin();
134
+ await configure([plugin]);
135
+ const { handleNotifyAppReady } = await import("./appReady");
136
+
137
+ const readiness = handleNotifyAppReady({});
138
+ await vi.runOnlyPendingTimersAsync();
139
+ await readiness;
140
+
141
+ expect(events).toEqual([
142
+ [
143
+ "onAppReady",
144
+ {
145
+ status: "UNCHANGED",
146
+ channel: "production",
147
+ bundleId: "bundle-id",
148
+ releaseId: expected,
149
+ previousProcessExit: null,
150
+ } satisfies AppReadyResult,
151
+ ],
152
+ ]);
153
+ },
154
+ );
155
+
156
+ it.each(["UPDATE_APPLIED", "RECOVERED"] as const)(
157
+ "reports %s with the persisted transition and calls onNotifyAppReady",
158
+ async (status) => {
159
+ stubNotifyFrame();
160
+ mocks.readNotifyAppReady.mockReturnValue(
161
+ createNotifyReadResult(
162
+ { status, fromBundleId: "bundle-a", toBundleId: "bundle-b" },
163
+ {
164
+ type: status,
165
+ fromBundleId: "bundle-a",
166
+ fromReleaseId: "release-a",
167
+ toBundleId: "bundle-b",
168
+ toReleaseId: "release-b",
169
+ updateStrategy: "fingerprint",
170
+ },
171
+ ),
172
+ );
173
+ const { events, plugin } = recordingPlugin();
174
+ await configure([plugin]);
175
+ const onNotifyAppReady = vi.fn();
176
+ const { handleNotifyAppReady } = await import("./appReady");
177
+
178
+ const readiness = handleNotifyAppReady({ onNotifyAppReady });
179
+ await vi.runOnlyPendingTimersAsync();
180
+ await readiness;
181
+
182
+ expect(events).toEqual([
183
+ [
184
+ "onAppReady",
185
+ {
186
+ status,
187
+ channel: "production",
188
+ fromBundleId: "bundle-a",
189
+ fromReleaseId: "release-a",
190
+ toBundleId: "bundle-b",
191
+ toReleaseId: "release-b",
192
+ updateStrategy: "fingerprint",
193
+ previousProcessExit: null,
194
+ },
195
+ ],
196
+ ]);
197
+ expect(onNotifyAppReady).toHaveBeenCalledWith({
198
+ status,
199
+ fromBundleId: "bundle-a",
200
+ toBundleId: "bundle-b",
201
+ });
202
+ },
203
+ );
204
+
205
+ it("waits for native launch verification before reporting", async () => {
206
+ stubNotifyFrame();
207
+ mocks.readNotifyAppReady
208
+ .mockReturnValueOnce(createNotifyReadResult(undefined, null, true))
209
+ .mockReturnValueOnce(createNotifyReadResult(undefined, null, true))
210
+ .mockReturnValue(createNotifyReadResult());
211
+ const { events, plugin } = recordingPlugin();
212
+ await configure([plugin]);
213
+ const { handleNotifyAppReady } = await import("./appReady");
214
+
215
+ const readiness = handleNotifyAppReady({});
216
+ await vi.runOnlyPendingTimersAsync();
217
+ expect(events).toEqual([]);
218
+ await vi.runAllTimersAsync();
219
+ await readiness;
220
+
221
+ expect(mocks.readNotifyAppReady).toHaveBeenCalledTimes(3);
222
+ expect(events).toHaveLength(1);
223
+ });
224
+
225
+ it("reports the launch once per runtime while every init call gets readiness", async () => {
226
+ stubNotifyFrame();
227
+ const { events, plugin } = recordingPlugin();
228
+ await configure([plugin]);
229
+ const onNotifyAppReady = vi.fn();
230
+ const { handleNotifyAppReady } = await import("./appReady");
231
+
232
+ const first = handleNotifyAppReady({ onNotifyAppReady });
233
+ const second = handleNotifyAppReady({ onNotifyAppReady });
234
+ await vi.runOnlyPendingTimersAsync();
235
+ await Promise.all([first, second]);
236
+
237
+ expect(events).toHaveLength(1);
238
+ expect(onNotifyAppReady).toHaveBeenCalledTimes(2);
239
+ });
240
+
241
+ it("skips onAppReady for an applied launch without its persisted transition", async () => {
242
+ stubNotifyFrame();
243
+ mocks.readNotifyAppReady.mockReturnValue(
244
+ createNotifyReadResult(
245
+ {
246
+ status: "UPDATE_APPLIED",
247
+ fromBundleId: "bundle-a",
248
+ toBundleId: "bundle-b",
249
+ },
250
+ null,
251
+ ),
252
+ );
253
+ const { events, plugin } = recordingPlugin();
254
+ await configure([plugin]);
255
+ const onNotifyAppReady = vi.fn();
256
+ const { handleNotifyAppReady } = await import("./appReady");
257
+
258
+ const readiness = handleNotifyAppReady({ onNotifyAppReady });
259
+ await vi.runOnlyPendingTimersAsync();
260
+ await readiness;
261
+
262
+ expect(events).toEqual([]);
263
+ expect(onNotifyAppReady).toHaveBeenCalledOnce();
264
+ });
265
+
266
+ it("reports a throwing or rejecting hook through onError and keeps readiness", async () => {
267
+ stubNotifyFrame();
268
+ const onError = vi.fn();
269
+ const throwing: HotUpdaterClientPlugin = {
270
+ id: "throwing",
271
+ setup: () => ({
272
+ onAppReady: () => {
273
+ throw new Error("hook failed");
274
+ },
275
+ }),
276
+ };
277
+ const rejecting: HotUpdaterClientPlugin = {
278
+ id: "rejecting",
279
+ setup: () => ({
280
+ onAppReady: async () => {
281
+ throw new Error("hook rejected");
282
+ },
283
+ }),
284
+ };
285
+ await configure([throwing, rejecting], onError);
286
+ const onNotifyAppReady = vi.fn();
287
+ const { handleNotifyAppReady } = await import("./appReady");
288
+
289
+ const readiness = handleNotifyAppReady({ onNotifyAppReady });
290
+ await vi.runOnlyPendingTimersAsync();
291
+ await readiness;
292
+ await vi.runAllTimersAsync();
293
+
294
+ expect(onNotifyAppReady).toHaveBeenCalledWith({ status: "UNCHANGED" });
295
+ expect(onError).toHaveBeenCalledTimes(2);
296
+ expect(onError).toHaveBeenCalledWith(
297
+ expect.objectContaining({
298
+ message: '[HotUpdater] Plugin "throwing" failed in onAppReady',
299
+ cause: expect.objectContaining({ message: "hook failed" }),
300
+ }),
301
+ );
302
+ expect(onError).toHaveBeenCalledWith(
303
+ expect.objectContaining({
304
+ message: '[HotUpdater] Plugin "rejecting" failed in onAppReady',
305
+ }),
306
+ );
307
+ });
308
+
309
+ it("delivers events after the launch, even when they happen first", async () => {
310
+ stubNotifyFrame();
311
+ const { events, plugin } = recordingPlugin();
312
+ await configure([plugin]);
313
+ const { emitAfterAppReady, handleNotifyAppReady } =
314
+ await import("./appReady");
315
+
316
+ const readiness = handleNotifyAppReady({});
317
+ emitAfterAppReady("onBundleDownloaded", () => ({
318
+ channel: "production",
319
+ fromBundleId: "bundle-id",
320
+ fromReleaseId: null,
321
+ toBundleId: "next-bundle",
322
+ toReleaseId: "next-release",
323
+ updateStrategy: "appVersion",
324
+ delivery: "manifest",
325
+ patchFallback: false,
326
+ }));
327
+ expect(events).toEqual([]);
328
+ await vi.runOnlyPendingTimersAsync();
329
+ await readiness;
330
+ await Promise.resolve();
331
+
332
+ expect(events.map((event) => (event as [string])[0])).toEqual([
333
+ "onAppReady",
334
+ "onBundleDownloaded",
335
+ ]);
336
+ });
337
+
338
+ it("passes on why the previous process exited, and keeps it for later events", async () => {
339
+ stubNotifyFrame();
340
+ mocks.readNotifyAppReady.mockReturnValue(
341
+ createNotifyReadResult(
342
+ {
343
+ status: "RECOVERED",
344
+ fromBundleId: "bundle-b",
345
+ toBundleId: "bundle-a",
346
+ },
347
+ {
348
+ type: "RECOVERED",
349
+ fromBundleId: "bundle-b",
350
+ fromReleaseId: null,
351
+ toBundleId: "bundle-a",
352
+ toReleaseId: null,
353
+ updateStrategy: "appVersion",
354
+ },
355
+ false,
356
+ "CRASH_NATIVE",
357
+ ),
358
+ );
359
+ const { events, plugin } = recordingPlugin();
360
+ await configure([plugin]);
361
+ const { getPreviousProcessExit, handleNotifyAppReady } =
362
+ await import("./appReady");
363
+ expect(getPreviousProcessExit()).toBeNull();
364
+
365
+ const readiness = handleNotifyAppReady({});
366
+ await vi.runOnlyPendingTimersAsync();
367
+ await readiness;
368
+
369
+ expect(events).toEqual([
370
+ [
371
+ "onAppReady",
372
+ expect.objectContaining({
373
+ status: "RECOVERED",
374
+ previousProcessExit: "CRASH_NATIVE",
375
+ }),
376
+ ],
377
+ ]);
378
+ expect(getPreviousProcessExit()).toBe("CRASH_NATIVE");
379
+ });
380
+
381
+ it("builds no event when no plugin listens", async () => {
382
+ stubNotifyFrame();
383
+ await configure([]);
384
+ const { handleNotifyAppReady } = await import("./appReady");
385
+
386
+ const readiness = handleNotifyAppReady({});
387
+ await vi.runOnlyPendingTimersAsync();
388
+ await readiness;
389
+
390
+ expect(mocks.getActiveUpdateState).not.toHaveBeenCalled();
391
+ expect(mocks.getBundleId).not.toHaveBeenCalled();
392
+ });
393
+ });
@@ -1,20 +1,20 @@
1
1
  import { vi } from "vitest";
2
2
 
3
- import type {
4
- NotifyAppReadyInsightsEvent,
5
- NotifyAppReadyResult,
6
- } from "./native";
3
+ import type { LaunchTransition, NotifyAppReadyResult } from "./native";
7
4
 
8
5
  export const createNotifyReadResult = (
9
6
  result: NotifyAppReadyResult = { status: "UNCHANGED" },
10
- insightsEvent: NotifyAppReadyInsightsEvent | null = null,
7
+ transition: LaunchTransition | null = null,
11
8
  pending = false,
9
+ previousProcessExit: string | null = null,
12
10
  ): {
13
- insightsEvent: NotifyAppReadyInsightsEvent | null;
11
+ transition: LaunchTransition | null;
12
+ previousProcessExit: string | null;
14
13
  pending: boolean;
15
14
  result: NotifyAppReadyResult;
16
15
  } => ({
17
- insightsEvent,
16
+ transition,
17
+ previousProcessExit,
18
18
  pending,
19
19
  result,
20
20
  });
@@ -0,0 +1,149 @@
1
+ import type { AppReadyResult } from "./clientPlugin";
2
+ import type { HotUpdaterError } from "./error";
3
+ import {
4
+ getActiveUpdateState,
5
+ getBundleId,
6
+ getChannel,
7
+ type LaunchTransition,
8
+ type NotifyAppReadyResult,
9
+ readNotifyAppReady,
10
+ } from "./native";
11
+ import {
12
+ buildPluginEvent,
13
+ dispatchPluginHook,
14
+ emitPluginHook,
15
+ } from "./pluginHost";
16
+
17
+ export type NotifyAppReadyOptions = {
18
+ onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
19
+ onError?: (error: HotUpdaterError | Error | unknown) => void;
20
+ };
21
+
22
+ type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
23
+
24
+ let didEmitAppReady = false;
25
+ /** Settles once this runtime's launch was read, whether it was reported or failed. */
26
+ let appReadyRead: Promise<unknown> | null = null;
27
+ /** Why the previous main process exited, read with this runtime's launch. */
28
+ let previousProcessExit: string | null = null;
29
+
30
+ /** Android 11+: why the app's previous main process exited, once the launch was read. */
31
+ export const getPreviousProcessExit = (): string | null => previousProcessExit;
32
+
33
+ const waitForNextFrame = () =>
34
+ new Promise<void>((resolve) => {
35
+ const requestAnimationFrame = (
36
+ globalThis as typeof globalThis & {
37
+ requestAnimationFrame?: RequestAnimationFrame;
38
+ }
39
+ )?.requestAnimationFrame;
40
+
41
+ if (requestAnimationFrame) {
42
+ requestAnimationFrame(() => resolve());
43
+ return;
44
+ }
45
+
46
+ void Promise.resolve().then(resolve);
47
+ });
48
+
49
+ /**
50
+ * The Release the running bundle was selected as: the active or stable
51
+ * selection that names it on the current channel.
52
+ */
53
+ export const getRunningReleaseId = (
54
+ bundleId: string,
55
+ channel: string,
56
+ ): string | null => {
57
+ const state = getActiveUpdateState();
58
+ return (
59
+ [state.activeSelection, state.stableSelection].find(
60
+ (selection) =>
61
+ selection?.bundleId === bundleId && selection.channel === channel,
62
+ )?.releaseId ?? null
63
+ );
64
+ };
65
+
66
+ const toAppReadyResult = (
67
+ result: NotifyAppReadyResult,
68
+ transition: LaunchTransition | null,
69
+ ): AppReadyResult | null => {
70
+ const channel = getChannel();
71
+ if (result.status === "UNCHANGED") {
72
+ const bundleId = getBundleId();
73
+ return {
74
+ status: "UNCHANGED",
75
+ channel,
76
+ bundleId,
77
+ releaseId: getRunningReleaseId(bundleId, channel),
78
+ previousProcessExit,
79
+ };
80
+ }
81
+ // Native persists the transition with every applied or recovered launch;
82
+ // a report without it cannot say what moved.
83
+ if (transition === null) return null;
84
+ return {
85
+ status: transition.type,
86
+ channel,
87
+ fromBundleId: transition.fromBundleId,
88
+ fromReleaseId: transition.fromReleaseId,
89
+ toBundleId: transition.toBundleId,
90
+ toReleaseId: transition.toReleaseId,
91
+ updateStrategy: transition.updateStrategy,
92
+ previousProcessExit,
93
+ };
94
+ };
95
+
96
+ const notifyAppReady = async (
97
+ options: NotifyAppReadyOptions,
98
+ ): Promise<NotifyAppReadyResult | undefined> => {
99
+ try {
100
+ let nativeReadResult: ReturnType<typeof readNotifyAppReady>;
101
+ do {
102
+ await waitForNextFrame();
103
+ nativeReadResult = readNotifyAppReady();
104
+ } while (nativeReadResult.pending);
105
+
106
+ const { result, transition } = nativeReadResult;
107
+ previousProcessExit = nativeReadResult.previousProcessExit;
108
+ if (!didEmitAppReady) {
109
+ didEmitAppReady = true;
110
+ emitPluginHook("onAppReady", () => toAppReadyResult(result, transition));
111
+ }
112
+
113
+ options.onNotifyAppReady?.(result);
114
+ return result;
115
+ } catch (error) {
116
+ const normalizedError =
117
+ error instanceof Error ? error : new Error(String(error));
118
+ options.onError?.(error);
119
+ console.warn("[HotUpdater] Failed to notify app ready:", normalizedError);
120
+ return undefined;
121
+ }
122
+ };
123
+
124
+ /** Reads this runtime's launch once native finalizes it and reports it to plugins. */
125
+ export const handleNotifyAppReady = (
126
+ options: NotifyAppReadyOptions,
127
+ ): Promise<NotifyAppReadyResult | undefined> => {
128
+ const readiness = notifyAppReady(options);
129
+ appReadyRead ??= readiness;
130
+ return readiness;
131
+ };
132
+
133
+ /**
134
+ * Calls a plugin hook after this runtime's `onAppReady`, so plugins see the
135
+ * launch before anything that followed it. The event is built now, from the
136
+ * state it describes, and only when a plugin listens.
137
+ */
138
+ export const emitAfterAppReady: typeof emitPluginHook = (
139
+ name,
140
+ createPayload,
141
+ ) => {
142
+ const payload = buildPluginEvent(name, createPayload);
143
+ if (payload === null) return;
144
+ if (appReadyRead === null) {
145
+ dispatchPluginHook(name, payload);
146
+ return;
147
+ }
148
+ void appReadyRead.then(() => dispatchPluginHook(name, payload));
149
+ };
package/src/baseURL.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { HotUpdaterBaseURL } from "./types";
2
+
3
+ /** Resolves a functional baseURL and drops trailing slashes. */
4
+ export const resolveBaseURL = async (
5
+ baseURL: HotUpdaterBaseURL,
6
+ ): Promise<string> => {
7
+ const resolvedBaseURL =
8
+ typeof baseURL === "function" ? await baseURL() : baseURL;
9
+
10
+ if (!resolvedBaseURL) {
11
+ throw new Error("baseURL function must return a non-empty string");
12
+ }
13
+
14
+ return resolvedBaseURL.replace(/\/+$/, "");
15
+ };