@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
@@ -9,8 +9,18 @@ import {
9
9
  import { canonicalizeAppVersion } from "@hot-updater/plugin-core";
10
10
  import { Platform } from "react-native";
11
11
 
12
+ import {
13
+ emitAfterAppReady,
14
+ getPreviousProcessExit,
15
+ getRunningReleaseId,
16
+ } from "./appReady";
17
+ import type {
18
+ ReleaseTransitionKind,
19
+ UpdateErrorResource,
20
+ UpdateErrorStage,
21
+ } from "./clientPlugin";
12
22
  import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
13
- import type { HotUpdaterHttpClient, HotUpdaterHttpSession } from "./httpClient";
23
+ import type { HotUpdaterHttpClient } from "./httpClient";
14
24
  import {
15
25
  acceptReleaseCatalog,
16
26
  commitReleaseSelection,
@@ -21,20 +31,20 @@ import {
21
31
  getCohort,
22
32
  getDefaultChannel,
23
33
  getFingerprintHash,
24
- getInstallId,
25
34
  getMinBundleId,
26
35
  getCrashHistory,
27
- getPersistedUserIdentity,
28
36
  isReleaseSelectionCurrent,
29
37
  isChannelSwitched,
30
- updateBundle,
38
+ stageBundle,
31
39
  } from "./native";
32
- import { reportBundleDownloaded } from "./notifyAppReadyInsights";
33
40
  import {
34
41
  hasExpectedReleaseCatalogScope,
35
42
  type ExpectedReleaseCatalogScope,
36
43
  } from "./releaseCatalogCache";
37
44
  import { hotUpdaterStore } from "./store";
45
+ import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
46
+
47
+ export type { ReleaseTransitionKind };
38
48
 
39
49
  export interface CheckForUpdateOptions {
40
50
  /**
@@ -76,14 +86,7 @@ export type CheckForUpdateResult = {
76
86
  updateBundle: () => Promise<boolean>;
77
87
  };
78
88
 
79
- export type ReleaseTransitionKind =
80
- | "INSTALL"
81
- | "ADOPT_RELEASE"
82
- | "USE_EMBEDDED"
83
- | "USE_BUILTIN";
84
-
85
89
  export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
86
- insights?: boolean;
87
90
  client: HotUpdaterHttpClient;
88
91
  }
89
92
 
@@ -113,7 +116,7 @@ const validateCatalog = (
113
116
  !/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
114
117
  !hasExpectedReleaseCatalogScope(catalog, expectedScope)
115
118
  ) {
116
- throw new HotUpdaterError("Received an invalid Release catalog");
119
+ throw new InvalidUpdateResponseError("Received an invalid Release catalog");
117
120
  }
118
121
  };
119
122
 
@@ -126,40 +129,45 @@ const resetProgress = () => {
126
129
  });
127
130
  };
128
131
 
129
- const notifyUnchangedSelection = async (input: {
130
- readonly active: PersistedSelectionReceipt | null;
131
- readonly desired: PersistedSelectionReceipt;
132
- readonly appVersion: string;
133
- readonly cohort: string;
134
- readonly fingerprintHash: string | null;
135
- readonly platform: "ios" | "android";
136
- readonly session: HotUpdaterHttpSession;
137
- readonly requestHeaders?: Record<string, string>;
138
- readonly requestTimeout?: number;
139
- }): Promise<void> => {
140
- const { userId, username } = getPersistedUserIdentity();
141
- try {
142
- await input.session.sendInsightsEvent({
143
- appVersion: input.appVersion,
144
- channel: input.desired.channel,
145
- cohort: input.cohort,
146
- fingerprintHash: input.fingerprintHash,
147
- fromBundleId: null,
148
- fromReleaseId: input.active?.releaseId ?? null,
149
- installId: getInstallId(),
150
- platform: input.platform,
151
- requestHeaders: input.requestHeaders,
152
- requestTimeout: input.requestTimeout,
153
- toBundleId: input.desired.bundleId,
154
- toReleaseId: input.desired.releaseId,
155
- type: "UNCHANGED",
156
- updateStrategy: null,
157
- ...(userId === undefined ? {} : { userId }),
158
- ...(username === undefined ? {} : { username }),
159
- });
160
- } catch (error) {
161
- console.warn("[HotUpdater] Unchanged selection insights failed:", error);
162
- }
132
+ /** The running bundle, the channel checked, and the update's target. */
133
+ type UpdateErrorScope = {
134
+ readonly channel: string;
135
+ readonly bundleId: string;
136
+ readonly updateStrategy: "fingerprint" | "appVersion";
137
+ readonly targetBundleId?: string;
138
+ readonly targetReleaseId?: string | null;
139
+ };
140
+
141
+ /**
142
+ * Tells plugins why an update failed, when it failed as an update. An error
143
+ * the SDK's JavaScript raised happened in `stage`, fetching `resource`.
144
+ */
145
+ export const reportUpdateError = (
146
+ error: unknown,
147
+ stage: UpdateErrorStage,
148
+ resource: UpdateErrorResource | undefined,
149
+ scope: UpdateErrorScope,
150
+ ): void => {
151
+ emitAfterAppReady("onUpdateError", () => {
152
+ const classification = classifyUpdateError(error, stage, resource);
153
+ if (classification === null) return null;
154
+ const previousProcessExit = getPreviousProcessExit();
155
+ return {
156
+ ...classification,
157
+ ...(previousProcessExit === null ? {} : { previousProcessExit }),
158
+ channel: scope.channel,
159
+ bundleId: scope.bundleId,
160
+ releaseId: getRunningReleaseId(scope.bundleId, getChannel()),
161
+ updateStrategy: scope.updateStrategy,
162
+ ...(scope.targetBundleId === undefined
163
+ ? {}
164
+ : { targetBundleId: scope.targetBundleId }),
165
+ ...(scope.targetReleaseId == null
166
+ ? {}
167
+ : { targetReleaseId: scope.targetReleaseId }),
168
+ cause: error,
169
+ };
170
+ });
163
171
  };
164
172
 
165
173
  async function checkForReleaseCatalogUpdate(input: {
@@ -210,6 +218,8 @@ async function checkForReleaseCatalogUpdate(input: {
210
218
  requestTimeout: options.requestTimeout,
211
219
  updateStrategy: options.updateStrategy,
212
220
  });
221
+ // The server says this scope has no catalog yet: no update, not a failure.
222
+ if (catalog === null) return null;
213
223
  validateCatalog(catalog, expectedScope);
214
224
  const catalogId = catalog.catalogId;
215
225
  const scopeKey = catalog.scopeKey;
@@ -263,7 +273,7 @@ async function checkForReleaseCatalogUpdate(input: {
263
273
  scopeKey,
264
274
  })
265
275
  ) {
266
- throw new HotUpdaterError(
276
+ throw new InvalidUpdateResponseError(
267
277
  "Rejected a stale or inconsistent Release catalog",
268
278
  );
269
279
  }
@@ -327,7 +337,20 @@ async function checkForReleaseCatalogUpdate(input: {
327
337
  scopeKey,
328
338
  selectionContextHash,
329
339
  };
330
- const updateBundleForSelection = async (): Promise<boolean> => {
340
+ const fromReleaseId =
341
+ active?.bundleId === input.currentBundleId
342
+ ? active.releaseId
343
+ : activeState.stableSelection?.bundleId === input.currentBundleId
344
+ ? activeState.stableSelection.releaseId
345
+ : null;
346
+ const errorScope = {
347
+ bundleId: input.currentBundleId,
348
+ channel: input.targetChannel,
349
+ targetBundleId: desired.bundleId,
350
+ targetReleaseId: desired.releaseId,
351
+ updateStrategy: options.updateStrategy,
352
+ };
353
+ const installSelection = async (): Promise<boolean> => {
331
354
  resetProgress();
332
355
  if (!isReleaseSelectionCurrent(guard)) {
333
356
  throw new StaleReleaseCatalogError();
@@ -338,18 +361,14 @@ async function checkForReleaseCatalogUpdate(input: {
338
361
  guard,
339
362
  selection: receipt,
340
363
  });
341
- if (committed && transitionKind === "ADOPT_RELEASE" && options.insights) {
342
- await notifyUnchangedSelection({
343
- active,
344
- appVersion: input.currentAppVersion,
345
- cohort: input.cohort,
346
- desired: receipt,
347
- fingerprintHash: input.fingerprintHash,
348
- platform: input.platform,
349
- requestHeaders: options.requestHeaders,
350
- requestTimeout: options.requestTimeout,
351
- session,
352
- });
364
+ if (committed && transitionKind === "ADOPT_RELEASE") {
365
+ emitAfterAppReady("onUpdateCheck", () => ({
366
+ status: "UNCHANGED",
367
+ channel: receipt.channel,
368
+ bundleId: receipt.bundleId,
369
+ releaseId: receipt.releaseId,
370
+ previousReleaseId: active?.releaseId ?? null,
371
+ }));
353
372
  }
354
373
  return committed;
355
374
  }
@@ -363,7 +382,7 @@ async function checkForReleaseCatalogUpdate(input: {
363
382
  if (!isReleaseSelectionCurrent(guard)) {
364
383
  throw new StaleReleaseCatalogError();
365
384
  }
366
- const downloaded = await updateBundle({
385
+ const delivery = await stageBundle({
367
386
  assets: artifact.assets,
368
387
  bundleId: desired.bundleId,
369
388
  channel: input.targetChannel,
@@ -374,35 +393,53 @@ async function checkForReleaseCatalogUpdate(input: {
374
393
  shouldSkipCurrentBundleIdCheck: true,
375
394
  status: desired.status,
376
395
  });
377
- if (downloaded) {
378
- await reportBundleDownloaded(options, {
396
+ if (delivery !== null && desired.bundleId !== input.currentBundleId) {
397
+ emitAfterAppReady("onBundleDownloaded", () => ({
398
+ channel: input.targetChannel,
379
399
  fromBundleId: input.currentBundleId,
380
- fromReleaseId:
381
- active?.bundleId === input.currentBundleId
382
- ? active.releaseId
383
- : activeState.stableSelection?.bundleId === input.currentBundleId
384
- ? activeState.stableSelection.releaseId
385
- : null,
400
+ fromReleaseId,
386
401
  toBundleId: desired.bundleId,
387
402
  toReleaseId: desired.releaseId,
388
- channel: input.targetChannel,
389
403
  updateStrategy: options.updateStrategy,
390
- });
404
+ ...delivery,
405
+ }));
406
+ }
407
+ return true;
408
+ };
409
+ const updateBundleForSelection = async (): Promise<boolean> => {
410
+ try {
411
+ return await installSelection();
412
+ } catch (error) {
413
+ reportUpdateError(error, "download", "artifact", errorScope);
414
+ throw error;
391
415
  }
392
- return downloaded;
393
416
  };
394
417
 
418
+ const shouldForceUpdate =
419
+ transitionKind === "ADOPT_RELEASE"
420
+ ? false
421
+ : desired.status === "ROLLBACK"
422
+ ? true
423
+ : (release?.shouldForceUpdate ?? false);
424
+ emitAfterAppReady("onUpdateCheck", () => ({
425
+ status: "UPDATE_AVAILABLE",
426
+ channel: input.targetChannel,
427
+ fromBundleId: input.currentBundleId,
428
+ fromReleaseId,
429
+ toBundleId: desired.bundleId,
430
+ toReleaseId: desired.releaseId,
431
+ transitionKind,
432
+ updateStatus: desired.status,
433
+ shouldForceUpdate,
434
+ updateStrategy: options.updateStrategy,
435
+ }));
436
+
395
437
  return {
396
438
  id: desired.releaseId ?? desired.bundleId,
397
439
  message: release?.message ?? null,
398
440
  releaseId: desired.releaseId,
399
441
  rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
400
- shouldForceUpdate:
401
- transitionKind === "ADOPT_RELEASE"
402
- ? false
403
- : desired.status === "ROLLBACK"
404
- ? true
405
- : (release?.shouldForceUpdate ?? false),
442
+ shouldForceUpdate,
406
443
  status: desired.status,
407
444
  targetCohorts: release?.targetCohorts ? [...release.targetCohorts] : [],
408
445
  transitionKind,
@@ -451,7 +488,7 @@ export async function checkForUpdate(
451
488
  const fingerprintHash = getFingerprintHash();
452
489
 
453
490
  try {
454
- return await checkForReleaseCatalogUpdate({
491
+ const result = await checkForReleaseCatalogUpdate({
455
492
  cohort,
456
493
  currentAppVersion,
457
494
  currentBundleId,
@@ -465,7 +502,25 @@ export async function checkForUpdate(
465
502
  platform,
466
503
  targetChannel,
467
504
  });
505
+ if (result === null) {
506
+ emitAfterAppReady("onUpdateCheck", () => {
507
+ const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
508
+ return {
509
+ status: "UNCHANGED",
510
+ channel: currentChannel,
511
+ bundleId: currentBundleId,
512
+ releaseId,
513
+ previousReleaseId: releaseId,
514
+ };
515
+ });
516
+ }
517
+ return result;
468
518
  } catch (error) {
519
+ reportUpdateError(error, "check", "catalog", {
520
+ bundleId: currentBundleId,
521
+ channel: targetChannel,
522
+ updateStrategy: options.updateStrategy,
523
+ });
469
524
  options.onError?.(error as Error);
470
525
  return null;
471
526
  }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * The client plugin contract. Built-in plugins, such as
3
+ * `@hot-updater/react-native/plugins/insights`, and third-party plugins use
4
+ * the same one, as server plugins do.
5
+ */
6
+
7
+ export type UpdateStrategy = "fingerprint" | "appVersion";
8
+
9
+ /** How an update moves the app's selection. */
10
+ export type ReleaseTransitionKind =
11
+ | "INSTALL"
12
+ | "ADOPT_RELEASE"
13
+ | "USE_EMBEDDED"
14
+ | "USE_BUILTIN";
15
+
16
+ /** The launch outcome native reports once per JavaScript runtime. */
17
+ export type AppReadyResult = (
18
+ | {
19
+ /** No update was applied or recovered at this launch. */
20
+ readonly status: "UNCHANGED";
21
+ readonly channel: string;
22
+ /** The running bundle. */
23
+ readonly bundleId: string;
24
+ /** The Release the running bundle was selected as, when known. */
25
+ readonly releaseId: string | null;
26
+ }
27
+ | {
28
+ /**
29
+ * `UPDATE_APPLIED`: a staged update became active at this launch.
30
+ * `RECOVERED`: a failed update was rolled back to `toBundleId`.
31
+ */
32
+ readonly status: "UPDATE_APPLIED" | "RECOVERED";
33
+ readonly channel: string;
34
+ readonly fromBundleId: string;
35
+ readonly fromReleaseId: string | null;
36
+ readonly toBundleId: string;
37
+ readonly toReleaseId: string | null;
38
+ readonly updateStrategy: UpdateStrategy;
39
+ }
40
+ ) & {
41
+ /**
42
+ * Android 11+: why the app's previous main process exited, the
43
+ * `ApplicationExitInfo` reason without its `REASON_` prefix, such as
44
+ * `CRASH`, `ANR`, `LOW_MEMORY`, or `USER_REQUESTED`. Null on iOS and on
45
+ * older Android. It tells a crash from the system or the user closing the
46
+ * app.
47
+ */
48
+ readonly previousProcessExit: string | null;
49
+ };
50
+
51
+ /** What an update check found. */
52
+ export type UpdateCheckResult =
53
+ | {
54
+ /**
55
+ * The app keeps running its bundle: the check found nothing to
56
+ * install, or the app adopted a newer Release of the running bundle.
57
+ */
58
+ readonly status: "UNCHANGED";
59
+ readonly channel: string;
60
+ readonly bundleId: string;
61
+ /** The Release the running bundle is selected as after the check. */
62
+ readonly releaseId: string | null;
63
+ /** The Release before the check; it differs after an adoption. */
64
+ readonly previousReleaseId: string | null;
65
+ }
66
+ | {
67
+ /** The check found a Release to install, adopt, or roll back to. */
68
+ readonly status: "UPDATE_AVAILABLE";
69
+ readonly channel: string;
70
+ readonly fromBundleId: string;
71
+ readonly fromReleaseId: string | null;
72
+ readonly toBundleId: string;
73
+ readonly toReleaseId: string | null;
74
+ readonly transitionKind: ReleaseTransitionKind;
75
+ readonly updateStatus: "UPDATE" | "ROLLBACK";
76
+ readonly shouldForceUpdate: boolean;
77
+ readonly updateStrategy: UpdateStrategy;
78
+ };
79
+
80
+ /** A bundle the SDK downloaded, verified, and staged for the next launch. */
81
+ export interface BundleDownloadedInfo {
82
+ readonly channel: string;
83
+ readonly fromBundleId: string;
84
+ readonly fromReleaseId: string | null;
85
+ readonly toBundleId: string;
86
+ readonly toReleaseId: string | null;
87
+ readonly updateStrategy: UpdateStrategy;
88
+ /**
89
+ * How the bundle arrived: `patch` when a bsdiff patch produced a file,
90
+ * `manifest` when only the changed files were downloaded, `archive` for
91
+ * the full archive.
92
+ */
93
+ readonly delivery: "patch" | "manifest" | "archive";
94
+ /** A patch was tried, but the file or the archive was downloaded instead. */
95
+ readonly patchFallback: boolean;
96
+ }
97
+
98
+ /**
99
+ * Where an update failed: `check` covers the Release catalog request,
100
+ * `download` the transfer and verification of the update's files, and
101
+ * `install` patching, extracting, and moving them into place.
102
+ */
103
+ export type UpdateErrorStage = "check" | "download" | "install";
104
+
105
+ /** What the SDK was fetching or applying when an update failed. */
106
+ export type UpdateErrorResource =
107
+ | "catalog"
108
+ | "artifact"
109
+ | "manifest"
110
+ | "file"
111
+ | "patch"
112
+ | "archive";
113
+
114
+ /** Why no response arrived, for a `network` failure. */
115
+ export type UpdateErrorTransport =
116
+ | "timeout"
117
+ | "dns"
118
+ | "tls"
119
+ | "connection"
120
+ | "offline"
121
+ | "cancelled";
122
+
123
+ export type UpdateErrorReason =
124
+ | "network"
125
+ | "http"
126
+ | "invalid_response"
127
+ | "hash_mismatch"
128
+ | "signature"
129
+ | "patch"
130
+ | "extract"
131
+ | "storage"
132
+ | "unknown";
133
+
134
+ /** An update check, download, or install that failed. */
135
+ export interface UpdateError {
136
+ readonly stage: UpdateErrorStage;
137
+ readonly reason: UpdateErrorReason;
138
+ /** What was being fetched or applied, when the SDK knows. */
139
+ readonly resource?: UpdateErrorResource;
140
+ /** The response status when `reason` is `"http"`. */
141
+ readonly httpStatus?: number;
142
+ /**
143
+ * The storage origin's XML error `<Code>`, such as `AccessDenied`,
144
+ * `NoSuchKey`, or `ExpiredToken`, when an `http` failure's body names one.
145
+ * It tells an expired signed URL from a missing file.
146
+ */
147
+ readonly originCode?: string;
148
+ /** Why no response arrived, for a `network` failure, when the SDK knows. */
149
+ readonly transport?: UpdateErrorTransport;
150
+ /** Android 11+: why the previous main process exited (see `AppReadyResult`). */
151
+ readonly previousProcessExit?: string;
152
+ /** The bundle the update targeted; absent for the check stage. */
153
+ readonly targetBundleId?: string;
154
+ /** The Release the update targeted, when it names one. */
155
+ readonly targetReleaseId?: string;
156
+ readonly channel: string;
157
+ /** The running bundle and the Release it was selected as. */
158
+ readonly bundleId: string;
159
+ readonly releaseId: string | null;
160
+ readonly updateStrategy: UpdateStrategy;
161
+ /** The error the SDK caught. */
162
+ readonly cause: unknown;
163
+ }
164
+
165
+ /**
166
+ * Lifecycle hooks a plugin returns from `setup`. Hooks observe: the SDK
167
+ * never waits for one and ignores what it returns, and a hook that throws or
168
+ * rejects is reported through `onError`, or a console warning without one,
169
+ * so a plugin can never delay or change an update.
170
+ */
171
+ export interface HotUpdaterClientHooks {
172
+ /** Called once per JavaScript runtime, after native reports the launch. */
173
+ onAppReady?(result: AppReadyResult): void | Promise<void>;
174
+ /** Called after each update check, and after a Release adoption commits. */
175
+ onUpdateCheck?(result: UpdateCheckResult): void | Promise<void>;
176
+ /** Called after the SDK stages a downloaded bundle. */
177
+ onBundleDownloaded?(info: BundleDownloadedInfo): void | Promise<void>;
178
+ /** Called when an update check, download, or install fails. */
179
+ onUpdateError?(error: UpdateError): void | Promise<void>;
180
+ }
181
+
182
+ /**
183
+ * A plugin's persistent key-value store. Native keeps it on the device,
184
+ * outside device backups, and the SDK scopes its keys to the plugin's id.
185
+ * It is meant for small values, up to 64 KB each.
186
+ */
187
+ export interface HotUpdaterClientStorage {
188
+ get(key: string): string | null;
189
+ /** Writes the value; `null` removes the key. */
190
+ set(key: string, value: string | null): void;
191
+ }
192
+
193
+ export interface HotUpdaterClientContext {
194
+ /**
195
+ * Requests a path relative to the configured `baseURL`, with the SDK's
196
+ * `requestHeaders` and `requestTimeout` applied. Rejects with
197
+ * `Request timed out` when the timeout passes before a response arrives.
198
+ */
199
+ fetch(path: string, init?: RequestInit): Promise<Response>;
200
+ /** A random id native creates once per installation. */
201
+ readonly installId: string;
202
+ readonly platform: "ios" | "android";
203
+ /** The native app version, or null when the native build has none. */
204
+ readonly appVersion: string | null;
205
+ readonly sdkVersion: string;
206
+ /** Whether this is a debug build (`__DEV__`). */
207
+ readonly isDebugBuild: boolean;
208
+ /** The bundle native reports as current. */
209
+ getBundleId(): string;
210
+ getChannel(): string;
211
+ getCohort(): string;
212
+ getFingerprintHash(): string | null;
213
+ readonly storage: HotUpdaterClientStorage;
214
+ /** The current time in milliseconds since the epoch. */
215
+ now(): number;
216
+ }
217
+
218
+ export interface HotUpdaterClientPlugin {
219
+ /** Unique among an app's plugins; two plugins with one id throw at init. */
220
+ readonly id: string;
221
+ /**
222
+ * Called once, when `HotUpdater.init` or `HotUpdater.wrap` first
223
+ * configures the runtime with this plugin.
224
+ */
225
+ setup(context: HotUpdaterClientContext): HotUpdaterClientHooks | void;
226
+ }
227
+
228
+ /** Declares a client plugin; built-in and third-party plugins use the same contract. */
229
+ export const defineClientPlugin = <const P extends HotUpdaterClientPlugin>(
230
+ plugin: P,
231
+ ): P => plugin;
@@ -0,0 +1,65 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, relative, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ const sourceRoot = dirname(fileURLToPath(import.meta.url));
8
+
9
+ /** Every relative module a file imports, statically or dynamically. */
10
+ const readImports = (file: string): string[] => {
11
+ const source = readFileSync(file, "utf8");
12
+ const specifiers = [
13
+ ...source.matchAll(/\b(?:import|export)\s[^;]*?\bfrom\s*["']([^"']+)["']/g),
14
+ ...source.matchAll(/\bimport\s*["']([^"']+)["']/g),
15
+ ...source.matchAll(/\bimport\(\s*["']([^"']+)["']\s*\)/g),
16
+ ].map((match) => match[1]!);
17
+ return specifiers.filter((specifier) => specifier.startsWith("."));
18
+ };
19
+
20
+ const resolveModule = (from: string, specifier: string): string => {
21
+ const base = resolve(dirname(from), specifier);
22
+ const file = [
23
+ `${base}.ts`,
24
+ `${base}.tsx`,
25
+ join(base, "index.ts"),
26
+ join(base, "index.tsx"),
27
+ ].find((path) => existsSync(path));
28
+ if (file === undefined) {
29
+ throw new Error(`Cannot resolve ${specifier} from ${from}`);
30
+ }
31
+ return file;
32
+ };
33
+
34
+ const collectGraph = (entry: string): Set<string> => {
35
+ const seen = new Set<string>();
36
+ const pending = [entry];
37
+ while (pending.length > 0) {
38
+ const file = pending.pop()!;
39
+ if (seen.has(file)) continue;
40
+ seen.add(file);
41
+ for (const specifier of readImports(file)) {
42
+ pending.push(resolveModule(file, specifier));
43
+ }
44
+ }
45
+ return seen;
46
+ };
47
+
48
+ describe("@hot-updater/react-native root entry", () => {
49
+ const graph = [...collectGraph(join(sourceRoot, "index.ts"))].map((file) =>
50
+ relative(sourceRoot, file),
51
+ );
52
+
53
+ it("bundles no plugin, since Metro does not tree-shake", () => {
54
+ expect(graph).toContain("pluginHost.ts");
55
+ expect(graph.filter((file) => file.startsWith("plugins/"))).toEqual([]);
56
+ });
57
+
58
+ it("sends no Insights request", () => {
59
+ const insightsCode = graph.filter((file) => {
60
+ const source = readFileSync(join(sourceRoot, file), "utf8");
61
+ return /["'`/]events["'`]/.test(source);
62
+ });
63
+ expect(insightsCode).toEqual([]);
64
+ });
65
+ });
package/src/fetchJSON.ts CHANGED
@@ -1,13 +1,6 @@
1
- export class FetchJSONResponseError extends Error {
2
- constructor(
3
- readonly status: number,
4
- statusText: string,
5
- ) {
6
- super(
7
- `Request failed with HTTP ${status}${statusText ? ` ${statusText}` : ""}`,
8
- );
9
- }
10
- }
1
+ import { UpdateHttpError } from "./updateError";
2
+
3
+ export class FetchJSONResponseError extends UpdateHttpError {}
11
4
 
12
5
  export const fetchJSON = async <T>({
13
6
  url,